From 1b5681c12b81dd9a570377a99660d15de244b752 Mon Sep 17 00:00:00 2001 From: hongming Date: Wed, 25 Sep 2019 14:07:15 +0800 Subject: [PATCH] refactor: openpitrix module Signed-off-by: hongming --- cmd/controller-manager/app/options/options.go | 9 +- cmd/controller-manager/app/server.go | 16 +- cmd/ks-apiserver/apiserver.go | 1 + go.mod | 43 +- go.sum | 40 + .../doc.go => pkg/apis/openpitrix/group.go | 12 +- pkg/apis/openpitrix/install/install.go | 33 + pkg/apis/openpitrix/v1/register.go | 346 + pkg/apis/resources/v1alpha2/register.go | 57 - pkg/apiserver/openpitrix/applications.go | 273 + pkg/apiserver/openpitrix/apps.go | 544 ++ pkg/apiserver/openpitrix/attachments.go | 54 + pkg/apiserver/openpitrix/categories.go | 171 + pkg/apiserver/openpitrix/repos.go | 222 + pkg/apiserver/resources/application.go | 206 - pkg/apiserver/runtime/runtime.go | 10 +- pkg/constants/constants.go | 1 + .../namespace/namespace_controller.go | 113 +- .../applications.go | 280 +- pkg/models/openpitrix/apps.go | 680 ++ pkg/models/openpitrix/attachments.go | 50 + pkg/models/openpitrix/categories.go | 163 + pkg/models/openpitrix/convert.go | 647 ++ pkg/models/openpitrix/repos.go | 296 + pkg/models/openpitrix/types.go | 833 +++ pkg/models/resources/resources.go | 16 +- pkg/server/config/config.go | 2 +- pkg/server/config/config_test.go | 10 +- pkg/server/errors/errors.go | 16 - pkg/simple/client/factory.go | 9 +- pkg/simple/client/openpitrix/applications.go | 258 - pkg/simple/client/openpitrix/openpitrix.go | 227 - .../client/openpitrix/openpitrixclient.go | 243 + pkg/simple/client/openpitrix/options.go | 88 +- pkg/simple/client/openpitrix/types.go | 117 - tools/cmd/doc-gen/main.go | 2 + vendor/github.com/BurntSushi/toml/.gitignore | 5 + vendor/github.com/BurntSushi/toml/.travis.yml | 15 + vendor/github.com/BurntSushi/toml/COMPATIBLE | 3 + vendor/github.com/BurntSushi/toml/COPYING | 21 + vendor/github.com/BurntSushi/toml/Makefile | 19 + vendor/github.com/BurntSushi/toml/README.md | 218 + vendor/github.com/BurntSushi/toml/decode.go | 509 ++ .../github.com/BurntSushi/toml/decode_meta.go | 121 + vendor/github.com/BurntSushi/toml/doc.go | 27 + vendor/github.com/BurntSushi/toml/encode.go | 568 ++ .../BurntSushi/toml/encoding_types.go | 19 + .../BurntSushi/toml/encoding_types_1.1.go | 18 + vendor/github.com/BurntSushi/toml/lex.go | 953 +++ vendor/github.com/BurntSushi/toml/parse.go | 592 ++ vendor/github.com/BurntSushi/toml/session.vim | 1 + .../github.com/BurntSushi/toml/type_check.go | 91 + .../github.com/BurntSushi/toml/type_fields.go | 242 + vendor/github.com/fatih/camelcase/.travis.yml | 3 + vendor/github.com/fatih/camelcase/LICENSE.md | 20 + vendor/github.com/fatih/camelcase/README.md | 58 + .../github.com/fatih/camelcase/camelcase.go | 90 + vendor/github.com/globalsign/mgo/LICENSE | 25 + vendor/github.com/globalsign/mgo/bson/LICENSE | 25 + .../github.com/globalsign/mgo/bson/README.md | 12 + vendor/github.com/globalsign/mgo/bson/bson.go | 836 +++ .../globalsign/mgo/bson/compatibility.go | 29 + .../github.com/globalsign/mgo/bson/decimal.go | 312 + .../github.com/globalsign/mgo/bson/decode.go | 1055 +++ .../github.com/globalsign/mgo/bson/encode.go | 645 ++ vendor/github.com/globalsign/mgo/bson/json.go | 384 + .../github.com/globalsign/mgo/bson/stream.go | 90 + .../globalsign/mgo/internal/json/LICENSE | 27 + .../globalsign/mgo/internal/json/decode.go | 1685 +++++ .../globalsign/mgo/internal/json/encode.go | 1260 ++++ .../globalsign/mgo/internal/json/extension.go | 95 + .../globalsign/mgo/internal/json/fold.go | 143 + .../globalsign/mgo/internal/json/indent.go | 141 + .../globalsign/mgo/internal/json/scanner.go | 697 ++ .../globalsign/mgo/internal/json/stream.go | 510 ++ .../globalsign/mgo/internal/json/tags.go | 44 + .../github.com/go-openapi/errors/.gitignore | 2 + .../github.com/go-openapi/errors/.travis.yml | 14 + .../go-openapi/errors/CODE_OF_CONDUCT.md | 74 + vendor/github.com/go-openapi/errors/LICENSE | 202 + vendor/github.com/go-openapi/errors/README.md | 8 + vendor/github.com/go-openapi/errors/api.go | 166 + vendor/github.com/go-openapi/errors/auth.go | 20 + vendor/github.com/go-openapi/errors/doc.go | 28 + vendor/github.com/go-openapi/errors/go.mod | 7 + vendor/github.com/go-openapi/errors/go.sum | 5 + .../github.com/go-openapi/errors/headers.go | 85 + .../go-openapi/errors/middleware.go | 51 + .../github.com/go-openapi/errors/parsing.go | 59 + vendor/github.com/go-openapi/errors/schema.go | 562 ++ .../go-openapi/strfmt/.editorconfig | 26 + .../github.com/go-openapi/strfmt/.gitignore | 2 + .../go-openapi/strfmt/.golangci.yml | 29 + .../github.com/go-openapi/strfmt/.travis.yml | 20 + .../go-openapi/strfmt/CODE_OF_CONDUCT.md | 74 + vendor/github.com/go-openapi/strfmt/LICENSE | 202 + vendor/github.com/go-openapi/strfmt/README.md | 73 + vendor/github.com/go-openapi/strfmt/bson.go | 142 + vendor/github.com/go-openapi/strfmt/date.go | 165 + .../github.com/go-openapi/strfmt/default.go | 2244 ++++++ vendor/github.com/go-openapi/strfmt/doc.go | 18 + .../github.com/go-openapi/strfmt/duration.go | 218 + vendor/github.com/go-openapi/strfmt/format.go | 314 + vendor/github.com/go-openapi/strfmt/go.mod | 13 + vendor/github.com/go-openapi/strfmt/go.sum | 25 + vendor/github.com/go-openapi/strfmt/time.go | 210 + .../golang/protobuf/ptypes/empty/empty.pb.go | 83 + .../golang/protobuf/ptypes/empty/empty.proto | 52 + vendor/github.com/google/btree/btree_mem.go | 76 - .../go-grpc-middleware/.gitignore | 202 + .../go-grpc-middleware/.travis.yml | 22 + .../go-grpc-middleware/CONTRIBUTING.md | 20 + .../grpc-ecosystem/go-grpc-middleware/DOC.md | 166 + .../go-grpc-middleware/Gopkg.lock | 123 + .../go-grpc-middleware/Gopkg.toml | 35 + .../grpc-ecosystem/go-grpc-middleware/LICENSE | 201 + .../go-grpc-middleware/README.md | 86 + .../go-grpc-middleware/chain.go | 183 + .../grpc-ecosystem/go-grpc-middleware/doc.go | 69 + .../go-grpc-middleware/makefile | 22 + .../go-grpc-middleware/recovery/DOC.md | 107 + .../go-grpc-middleware/recovery/doc.go | 15 + .../recovery/interceptors.go | 48 + .../go-grpc-middleware/recovery/options.go | 32 + .../go-grpc-middleware/slack.png | Bin 0 -> 5088 bytes .../go-grpc-middleware/validator/DOC.md | 80 + .../go-grpc-middleware/validator/README.md | 73 + .../go-grpc-middleware/validator/doc.go | 45 + .../go-grpc-middleware/validator/validator.go | 57 + .../go-grpc-middleware/wrappers.go | 29 + .../protoc-gen-swagger/options/BUILD.bazel | 37 + .../options/annotations.pb.go | 103 + .../options/annotations.proto | 44 + .../options/openapiv2.pb.go | 1546 ++++ .../options/openapiv2.proto | 373 + .../github.com/klauspost/cpuid/private-gen.go | 476 -- .../github.com/koding/multiconfig/.gitignore | 25 + .../github.com/koding/multiconfig/.travis.yml | 4 + vendor/github.com/koding/multiconfig/LICENSE | 21 + .../github.com/koding/multiconfig/README.md | 92 + vendor/github.com/koding/multiconfig/doc.go | 5 + vendor/github.com/koding/multiconfig/env.go | 129 + vendor/github.com/koding/multiconfig/file.go | 139 + vendor/github.com/koding/multiconfig/flag.go | 207 + .../koding/multiconfig/multiconfig.go | 227 + .../koding/multiconfig/multiloader.go | 27 + .../koding/multiconfig/multivalidator.go | 28 + vendor/github.com/koding/multiconfig/tag.go | 60 + .../koding/multiconfig/validator.go | 73 + .../marten-seemann/qtls/generate_cert.go | 169 - .../miekg/dns/duplicate_generate.go | 144 - vendor/github.com/miekg/dns/msg_generate.go | 328 - vendor/github.com/miekg/dns/types_generate.go | 287 - vendor/golang.org/x/net/html/atom/gen.go | 712 -- vendor/golang.org/x/net/internal/iana/gen.go | 383 - .../x/net/internal/socket/defs_aix.go | 39 - .../x/net/internal/socket/defs_darwin.go | 36 - .../x/net/internal/socket/defs_dragonfly.go | 36 - .../x/net/internal/socket/defs_freebsd.go | 36 - .../x/net/internal/socket/defs_linux.go | 41 - .../x/net/internal/socket/defs_netbsd.go | 39 - .../x/net/internal/socket/defs_openbsd.go | 36 - .../x/net/internal/socket/defs_solaris.go | 36 - vendor/golang.org/x/net/ipv4/defs_aix.go | 39 - vendor/golang.org/x/net/ipv4/defs_darwin.go | 77 - .../golang.org/x/net/ipv4/defs_dragonfly.go | 38 - vendor/golang.org/x/net/ipv4/defs_freebsd.go | 75 - vendor/golang.org/x/net/ipv4/defs_linux.go | 122 - vendor/golang.org/x/net/ipv4/defs_netbsd.go | 37 - vendor/golang.org/x/net/ipv4/defs_openbsd.go | 37 - vendor/golang.org/x/net/ipv4/defs_solaris.go | 84 - vendor/golang.org/x/net/ipv4/gen.go | 199 - vendor/golang.org/x/net/ipv6/defs_aix.go | 82 - vendor/golang.org/x/net/ipv6/defs_darwin.go | 112 - .../golang.org/x/net/ipv6/defs_dragonfly.go | 84 - vendor/golang.org/x/net/ipv6/defs_freebsd.go | 105 - vendor/golang.org/x/net/ipv6/defs_linux.go | 147 - vendor/golang.org/x/net/ipv6/defs_netbsd.go | 80 - vendor/golang.org/x/net/ipv6/defs_openbsd.go | 89 - vendor/golang.org/x/net/ipv6/defs_solaris.go | 114 - vendor/golang.org/x/net/ipv6/gen.go | 199 - vendor/golang.org/x/sys/unix/mkasm_darwin.go | 61 - vendor/golang.org/x/sys/unix/mkpost.go | 106 - vendor/golang.org/x/sys/unix/mksyscall.go | 402 -- .../x/sys/unix/mksyscall_aix_ppc.go | 404 -- .../x/sys/unix/mksyscall_aix_ppc64.go | 602 -- .../x/sys/unix/mksyscall_solaris.go | 335 - vendor/golang.org/x/sys/unix/mksysnum.go | 190 - vendor/golang.org/x/sys/unix/types_aix.go | 236 - vendor/golang.org/x/sys/unix/types_darwin.go | 277 - .../golang.org/x/sys/unix/types_dragonfly.go | 263 - vendor/golang.org/x/sys/unix/types_freebsd.go | 356 - vendor/golang.org/x/sys/unix/types_netbsd.go | 289 - vendor/golang.org/x/sys/unix/types_openbsd.go | 276 - vendor/golang.org/x/sys/unix/types_solaris.go | 266 - .../x/text/encoding/charmap/maketables.go | 556 -- .../x/text/encoding/htmlindex/gen.go | 173 - .../text/encoding/internal/identifier/gen.go | 137 - .../x/text/encoding/japanese/maketables.go | 161 - .../x/text/encoding/korean/maketables.go | 143 - .../encoding/simplifiedchinese/maketables.go | 161 - .../encoding/traditionalchinese/maketables.go | 140 - vendor/golang.org/x/text/language/gen.go | 1712 ----- .../golang.org/x/text/language/gen_common.go | 20 - .../golang.org/x/text/language/gen_index.go | 162 - vendor/golang.org/x/text/unicode/bidi/gen.go | 133 - .../x/text/unicode/bidi/gen_ranges.go | 57 - .../x/text/unicode/bidi/gen_trieval.go | 64 - .../x/text/unicode/norm/maketables.go | 976 --- .../golang.org/x/text/unicode/norm/triegen.go | 117 - vendor/golang.org/x/text/width/gen.go | 115 - vendor/golang.org/x/text/width/gen_common.go | 96 - vendor/golang.org/x/text/width/gen_trieval.go | 34 - .../x/tools/go/gcexportdata/main.go | 99 - .../x/tools/internal/imports/mkindex.go | 173 - .../x/tools/internal/imports/mkstdlib.go | 132 - .../api/annotations/annotations.pb.go | 54 + .../googleapis/api/annotations/client.pb.go | 76 + .../api/annotations/field_behavior.pb.go | 119 + .../googleapis/api/annotations/http.pb.go | 749 ++ .../googleapis/api/annotations/resource.pb.go | 153 + .../grpc/reflection/README.md | 18 + .../grpc_reflection_v1alpha/reflection.pb.go | 939 +++ .../grpc_reflection_v1alpha/reflection.proto | 136 + .../grpc/reflection/serverreflection.go | 454 ++ vendor/k8s.io/kubernetes/pkg/apis/core/BUILD | 60 - vendor/k8s.io/kubernetes/pkg/apis/core/OWNERS | 45 - .../pkg/apis/core/annotation_key_constants.go | 104 - .../pkg/apis/core/field_constants.go | 38 - .../k8s.io/kubernetes/pkg/apis/core/json.go | 28 - .../pkg/apis/core/objectreference.go | 34 - .../kubernetes/pkg/apis/core/register.go | 98 - .../kubernetes/pkg/apis/core/resource.go | 55 - .../k8s.io/kubernetes/pkg/apis/core/taint.go | 36 - .../kubernetes/pkg/apis/core/toleration.go | 30 - .../k8s.io/kubernetes/pkg/apis/core/types.go | 4725 ------------ .../pkg/apis/core/zz_generated.deepcopy.go | 5415 -------------- vendor/modules.txt | 43 +- vendor/openpitrix.io/openpitrix/AUTHORS | 13 + vendor/openpitrix.io/openpitrix/CONTRIBUTORS | 25 + vendor/openpitrix.io/openpitrix/LICENSE | 202 + .../openpitrix/pkg/client/access/client.go | 73 + .../openpitrix/pkg/client/account/client.go | 142 + .../openpitrix/pkg/client/client.go | 35 + .../openpitrix/pkg/config/config.go | 94 + .../openpitrix/pkg/config/env_loader.go | 263 + .../openpitrix/pkg/config/global_config.go | 200 + .../pkg/config/init_global_config.go | 43 + .../openpitrix/pkg/constants/column.go | 240 + .../openpitrix/pkg/constants/common.go | 272 + .../openpitrix/pkg/constants/dlock.go | 10 + .../pkg/constants/email_template.go | 448 ++ .../openpitrix/pkg/constants/frontgate.go | 28 + .../pkg/constants/notify_message.go | 708 ++ .../openpitrix/pkg/constants/table.go | 44 + .../openpitrix/pkg/constants/user.go | 11 + .../openpitrix/pkg/db/Dockerfile | 12 + .../openpitrix/pkg/db/condition.go | 107 + .../openpitrix/pkg/db/connection.go | 72 + vendor/openpitrix.io/openpitrix/pkg/db/db.go | 272 + .../openpitrix.io/openpitrix/pkg/db/errors.go | 22 + .../openpitrix.io/openpitrix/pkg/db/event.go | 49 + .../openpitrix.io/openpitrix/pkg/db/util.go | 31 + .../openpitrix/pkg/gerr/codes.go | 125 + .../openpitrix/pkg/gerr/error.go | 80 + .../openpitrix/pkg/gerr/message.go | 495 ++ .../openpitrix/pkg/logger/logger.go | 201 + .../openpitrix/pkg/manager/checker.go | 132 + .../openpitrix/pkg/manager/common.go | 247 + .../openpitrix/pkg/manager/grpc_client.go | 64 + .../openpitrix/pkg/manager/grpc_server.go | 197 + .../openpitrix.io/openpitrix/pkg/pb/0.pb.go | 49 + .../openpitrix/pkg/pb/account.pb.go | 5165 +++++++++++++ .../openpitrix/pkg/pb/account.pb.gw.go | 1530 ++++ .../openpitrix.io/openpitrix/pkg/pb/app.pb.go | 5203 ++++++++++++++ .../openpitrix/pkg/pb/app.pb.gw.go | 1620 +++++ .../openpitrix/pkg/pb/attachment.pb.go | 1006 +++ .../openpitrix/pkg/pb/attachment.pb.gw.go | 124 + .../openpitrix/pkg/pb/category.pb.go | 865 +++ .../openpitrix/pkg/pb/category.pb.gw.go | 262 + .../openpitrix/pkg/pb/cluster.pb.go | 6362 +++++++++++++++++ .../openpitrix/pkg/pb/cluster.pb.gw.go | 1340 ++++ .../openpitrix.io/openpitrix/pkg/pb/isv.pb.go | 1258 ++++ .../openpitrix/pkg/pb/isv.pb.gw.go | 362 + .../openpitrix.io/openpitrix/pkg/pb/job.pb.go | 766 ++ .../openpitrix/pkg/pb/job.pb.gw.go | 124 + .../openpitrix/pkg/pb/market.pb.go | 1349 ++++ .../openpitrix/pkg/pb/market.pb.gw.go | 404 ++ .../openpitrix/pkg/pb/repo.pb.go | 1438 ++++ .../openpitrix/pkg/pb/repo.pb.gw.go | 312 + .../openpitrix/pkg/pb/repo_indexer.pb.go | 515 ++ .../openpitrix/pkg/pb/repo_indexer.pb.gw.go | 170 + .../openpitrix/pkg/pb/runtime.pb.go | 2407 +++++++ .../openpitrix/pkg/pb/runtime.pb.gw.go | 780 ++ .../openpitrix/pkg/pb/runtime_provider.pb.go | 1619 +++++ .../openpitrix/pkg/pb/service_config.pb.go | 788 ++ .../openpitrix/pkg/pb/service_config.pb.gw.go | 212 + .../openpitrix/pkg/pb/task.pb.go | 864 +++ .../openpitrix/pkg/pb/task.pb.gw.go | 170 + .../openpitrix/pkg/pb/types.pb.go | 179 + .../openpitrix/pkg/sender/owner_path.go | 46 + .../openpitrix/pkg/sender/sender.go | 60 + .../openpitrix/pkg/util/ctxutil/ctx.go | 52 + .../openpitrix/pkg/util/ctxutil/message.go | 35 + .../openpitrix/pkg/util/ctxutil/request.go | 29 + .../openpitrix/pkg/util/ctxutil/sender.go | 45 + .../openpitrix/pkg/util/pbutil/pb.go | 139 + .../pkg/util/reflectutil/reflect.go | 38 + .../openpitrix/pkg/util/stringutil/base64.go | 15 + .../openpitrix/pkg/util/stringutil/string.go | 68 + .../openpitrix/pkg/util/yamlutil/yaml.go | 20 + .../openpitrix/pkg/version/.gitignore | 1 + .../openpitrix/pkg/version/Makefile | 9 + .../openpitrix/pkg/version/version.go | 26 + 314 files changed, 72092 insertions(+), 25762 deletions(-) rename vendor/k8s.io/kubernetes/pkg/apis/core/doc.go => pkg/apis/openpitrix/group.go (52%) create mode 100644 pkg/apis/openpitrix/install/install.go create mode 100644 pkg/apis/openpitrix/v1/register.go create mode 100644 pkg/apiserver/openpitrix/applications.go create mode 100644 pkg/apiserver/openpitrix/apps.go create mode 100644 pkg/apiserver/openpitrix/attachments.go create mode 100644 pkg/apiserver/openpitrix/categories.go create mode 100644 pkg/apiserver/openpitrix/repos.go delete mode 100644 pkg/apiserver/resources/application.go rename pkg/models/{applications => openpitrix}/applications.go (50%) create mode 100644 pkg/models/openpitrix/apps.go create mode 100644 pkg/models/openpitrix/attachments.go create mode 100644 pkg/models/openpitrix/categories.go create mode 100644 pkg/models/openpitrix/convert.go create mode 100644 pkg/models/openpitrix/repos.go create mode 100644 pkg/models/openpitrix/types.go delete mode 100644 pkg/simple/client/openpitrix/applications.go delete mode 100644 pkg/simple/client/openpitrix/openpitrix.go create mode 100644 pkg/simple/client/openpitrix/openpitrixclient.go delete mode 100644 pkg/simple/client/openpitrix/types.go create mode 100644 vendor/github.com/BurntSushi/toml/.gitignore create mode 100644 vendor/github.com/BurntSushi/toml/.travis.yml create mode 100644 vendor/github.com/BurntSushi/toml/COMPATIBLE create mode 100644 vendor/github.com/BurntSushi/toml/COPYING create mode 100644 vendor/github.com/BurntSushi/toml/Makefile create mode 100644 vendor/github.com/BurntSushi/toml/README.md create mode 100644 vendor/github.com/BurntSushi/toml/decode.go create mode 100644 vendor/github.com/BurntSushi/toml/decode_meta.go create mode 100644 vendor/github.com/BurntSushi/toml/doc.go create mode 100644 vendor/github.com/BurntSushi/toml/encode.go create mode 100644 vendor/github.com/BurntSushi/toml/encoding_types.go create mode 100644 vendor/github.com/BurntSushi/toml/encoding_types_1.1.go create mode 100644 vendor/github.com/BurntSushi/toml/lex.go create mode 100644 vendor/github.com/BurntSushi/toml/parse.go create mode 100644 vendor/github.com/BurntSushi/toml/session.vim create mode 100644 vendor/github.com/BurntSushi/toml/type_check.go create mode 100644 vendor/github.com/BurntSushi/toml/type_fields.go create mode 100644 vendor/github.com/fatih/camelcase/.travis.yml create mode 100644 vendor/github.com/fatih/camelcase/LICENSE.md create mode 100644 vendor/github.com/fatih/camelcase/README.md create mode 100644 vendor/github.com/fatih/camelcase/camelcase.go create mode 100644 vendor/github.com/globalsign/mgo/LICENSE create mode 100644 vendor/github.com/globalsign/mgo/bson/LICENSE create mode 100644 vendor/github.com/globalsign/mgo/bson/README.md create mode 100644 vendor/github.com/globalsign/mgo/bson/bson.go create mode 100644 vendor/github.com/globalsign/mgo/bson/compatibility.go create mode 100644 vendor/github.com/globalsign/mgo/bson/decimal.go create mode 100644 vendor/github.com/globalsign/mgo/bson/decode.go create mode 100644 vendor/github.com/globalsign/mgo/bson/encode.go create mode 100644 vendor/github.com/globalsign/mgo/bson/json.go create mode 100644 vendor/github.com/globalsign/mgo/bson/stream.go create mode 100644 vendor/github.com/globalsign/mgo/internal/json/LICENSE create mode 100644 vendor/github.com/globalsign/mgo/internal/json/decode.go create mode 100644 vendor/github.com/globalsign/mgo/internal/json/encode.go create mode 100644 vendor/github.com/globalsign/mgo/internal/json/extension.go create mode 100644 vendor/github.com/globalsign/mgo/internal/json/fold.go create mode 100644 vendor/github.com/globalsign/mgo/internal/json/indent.go create mode 100644 vendor/github.com/globalsign/mgo/internal/json/scanner.go create mode 100644 vendor/github.com/globalsign/mgo/internal/json/stream.go create mode 100644 vendor/github.com/globalsign/mgo/internal/json/tags.go create mode 100644 vendor/github.com/go-openapi/errors/.gitignore create mode 100644 vendor/github.com/go-openapi/errors/.travis.yml create mode 100644 vendor/github.com/go-openapi/errors/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/go-openapi/errors/LICENSE create mode 100644 vendor/github.com/go-openapi/errors/README.md create mode 100644 vendor/github.com/go-openapi/errors/api.go create mode 100644 vendor/github.com/go-openapi/errors/auth.go create mode 100644 vendor/github.com/go-openapi/errors/doc.go create mode 100644 vendor/github.com/go-openapi/errors/go.mod create mode 100644 vendor/github.com/go-openapi/errors/go.sum create mode 100644 vendor/github.com/go-openapi/errors/headers.go create mode 100644 vendor/github.com/go-openapi/errors/middleware.go create mode 100644 vendor/github.com/go-openapi/errors/parsing.go create mode 100644 vendor/github.com/go-openapi/errors/schema.go create mode 100644 vendor/github.com/go-openapi/strfmt/.editorconfig create mode 100644 vendor/github.com/go-openapi/strfmt/.gitignore create mode 100644 vendor/github.com/go-openapi/strfmt/.golangci.yml create mode 100644 vendor/github.com/go-openapi/strfmt/.travis.yml create mode 100644 vendor/github.com/go-openapi/strfmt/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/go-openapi/strfmt/LICENSE create mode 100644 vendor/github.com/go-openapi/strfmt/README.md create mode 100644 vendor/github.com/go-openapi/strfmt/bson.go create mode 100644 vendor/github.com/go-openapi/strfmt/date.go create mode 100644 vendor/github.com/go-openapi/strfmt/default.go create mode 100644 vendor/github.com/go-openapi/strfmt/doc.go create mode 100644 vendor/github.com/go-openapi/strfmt/duration.go create mode 100644 vendor/github.com/go-openapi/strfmt/format.go create mode 100644 vendor/github.com/go-openapi/strfmt/go.mod create mode 100644 vendor/github.com/go-openapi/strfmt/go.sum create mode 100644 vendor/github.com/go-openapi/strfmt/time.go create mode 100644 vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go create mode 100644 vendor/github.com/golang/protobuf/ptypes/empty/empty.proto delete mode 100644 vendor/github.com/google/btree/btree_mem.go create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/.gitignore create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/.travis.yml create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/CONTRIBUTING.md create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/DOC.md create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/Gopkg.lock create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/Gopkg.toml create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/LICENSE create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/README.md create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/chain.go create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/doc.go create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/makefile create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/DOC.md create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/doc.go create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/interceptors.go create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/options.go create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/slack.png create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/validator/DOC.md create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/validator/README.md create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/validator/doc.go create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/validator/validator.go create mode 100644 vendor/github.com/grpc-ecosystem/go-grpc-middleware/wrappers.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options/BUILD.bazel create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options/annotations.pb.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options/annotations.proto create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options/openapiv2.pb.go create mode 100644 vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options/openapiv2.proto delete mode 100644 vendor/github.com/klauspost/cpuid/private-gen.go create mode 100644 vendor/github.com/koding/multiconfig/.gitignore create mode 100644 vendor/github.com/koding/multiconfig/.travis.yml create mode 100644 vendor/github.com/koding/multiconfig/LICENSE create mode 100644 vendor/github.com/koding/multiconfig/README.md create mode 100644 vendor/github.com/koding/multiconfig/doc.go create mode 100644 vendor/github.com/koding/multiconfig/env.go create mode 100644 vendor/github.com/koding/multiconfig/file.go create mode 100644 vendor/github.com/koding/multiconfig/flag.go create mode 100644 vendor/github.com/koding/multiconfig/multiconfig.go create mode 100644 vendor/github.com/koding/multiconfig/multiloader.go create mode 100644 vendor/github.com/koding/multiconfig/multivalidator.go create mode 100644 vendor/github.com/koding/multiconfig/tag.go create mode 100644 vendor/github.com/koding/multiconfig/validator.go delete mode 100644 vendor/github.com/marten-seemann/qtls/generate_cert.go delete mode 100644 vendor/github.com/miekg/dns/duplicate_generate.go delete mode 100644 vendor/github.com/miekg/dns/msg_generate.go delete mode 100644 vendor/github.com/miekg/dns/types_generate.go delete mode 100644 vendor/golang.org/x/net/html/atom/gen.go delete mode 100644 vendor/golang.org/x/net/internal/iana/gen.go delete mode 100644 vendor/golang.org/x/net/internal/socket/defs_aix.go delete mode 100644 vendor/golang.org/x/net/internal/socket/defs_darwin.go delete mode 100644 vendor/golang.org/x/net/internal/socket/defs_dragonfly.go delete mode 100644 vendor/golang.org/x/net/internal/socket/defs_freebsd.go delete mode 100644 vendor/golang.org/x/net/internal/socket/defs_linux.go delete mode 100644 vendor/golang.org/x/net/internal/socket/defs_netbsd.go delete mode 100644 vendor/golang.org/x/net/internal/socket/defs_openbsd.go delete mode 100644 vendor/golang.org/x/net/internal/socket/defs_solaris.go delete mode 100644 vendor/golang.org/x/net/ipv4/defs_aix.go delete mode 100644 vendor/golang.org/x/net/ipv4/defs_darwin.go delete mode 100644 vendor/golang.org/x/net/ipv4/defs_dragonfly.go delete mode 100644 vendor/golang.org/x/net/ipv4/defs_freebsd.go delete mode 100644 vendor/golang.org/x/net/ipv4/defs_linux.go delete mode 100644 vendor/golang.org/x/net/ipv4/defs_netbsd.go delete mode 100644 vendor/golang.org/x/net/ipv4/defs_openbsd.go delete mode 100644 vendor/golang.org/x/net/ipv4/defs_solaris.go delete mode 100644 vendor/golang.org/x/net/ipv4/gen.go delete mode 100644 vendor/golang.org/x/net/ipv6/defs_aix.go delete mode 100644 vendor/golang.org/x/net/ipv6/defs_darwin.go delete mode 100644 vendor/golang.org/x/net/ipv6/defs_dragonfly.go delete mode 100644 vendor/golang.org/x/net/ipv6/defs_freebsd.go delete mode 100644 vendor/golang.org/x/net/ipv6/defs_linux.go delete mode 100644 vendor/golang.org/x/net/ipv6/defs_netbsd.go delete mode 100644 vendor/golang.org/x/net/ipv6/defs_openbsd.go delete mode 100644 vendor/golang.org/x/net/ipv6/defs_solaris.go delete mode 100644 vendor/golang.org/x/net/ipv6/gen.go delete mode 100644 vendor/golang.org/x/sys/unix/mkasm_darwin.go delete mode 100644 vendor/golang.org/x/sys/unix/mkpost.go delete mode 100644 vendor/golang.org/x/sys/unix/mksyscall.go delete mode 100644 vendor/golang.org/x/sys/unix/mksyscall_aix_ppc.go delete mode 100644 vendor/golang.org/x/sys/unix/mksyscall_aix_ppc64.go delete mode 100644 vendor/golang.org/x/sys/unix/mksyscall_solaris.go delete mode 100644 vendor/golang.org/x/sys/unix/mksysnum.go delete mode 100644 vendor/golang.org/x/sys/unix/types_aix.go delete mode 100644 vendor/golang.org/x/sys/unix/types_darwin.go delete mode 100644 vendor/golang.org/x/sys/unix/types_dragonfly.go delete mode 100644 vendor/golang.org/x/sys/unix/types_freebsd.go delete mode 100644 vendor/golang.org/x/sys/unix/types_netbsd.go delete mode 100644 vendor/golang.org/x/sys/unix/types_openbsd.go delete mode 100644 vendor/golang.org/x/sys/unix/types_solaris.go delete mode 100644 vendor/golang.org/x/text/encoding/charmap/maketables.go delete mode 100644 vendor/golang.org/x/text/encoding/htmlindex/gen.go delete mode 100644 vendor/golang.org/x/text/encoding/internal/identifier/gen.go delete mode 100644 vendor/golang.org/x/text/encoding/japanese/maketables.go delete mode 100644 vendor/golang.org/x/text/encoding/korean/maketables.go delete mode 100644 vendor/golang.org/x/text/encoding/simplifiedchinese/maketables.go delete mode 100644 vendor/golang.org/x/text/encoding/traditionalchinese/maketables.go delete mode 100644 vendor/golang.org/x/text/language/gen.go delete mode 100644 vendor/golang.org/x/text/language/gen_common.go delete mode 100644 vendor/golang.org/x/text/language/gen_index.go delete mode 100644 vendor/golang.org/x/text/unicode/bidi/gen.go delete mode 100644 vendor/golang.org/x/text/unicode/bidi/gen_ranges.go delete mode 100644 vendor/golang.org/x/text/unicode/bidi/gen_trieval.go delete mode 100644 vendor/golang.org/x/text/unicode/norm/maketables.go delete mode 100644 vendor/golang.org/x/text/unicode/norm/triegen.go delete mode 100644 vendor/golang.org/x/text/width/gen.go delete mode 100644 vendor/golang.org/x/text/width/gen_common.go delete mode 100644 vendor/golang.org/x/text/width/gen_trieval.go delete mode 100644 vendor/golang.org/x/tools/go/gcexportdata/main.go delete mode 100644 vendor/golang.org/x/tools/internal/imports/mkindex.go delete mode 100644 vendor/golang.org/x/tools/internal/imports/mkstdlib.go create mode 100644 vendor/google.golang.org/genproto/googleapis/api/annotations/annotations.pb.go create mode 100644 vendor/google.golang.org/genproto/googleapis/api/annotations/client.pb.go create mode 100644 vendor/google.golang.org/genproto/googleapis/api/annotations/field_behavior.pb.go create mode 100644 vendor/google.golang.org/genproto/googleapis/api/annotations/http.pb.go create mode 100644 vendor/google.golang.org/genproto/googleapis/api/annotations/resource.pb.go create mode 100644 vendor/google.golang.org/grpc/reflection/README.md create mode 100644 vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection.pb.go create mode 100644 vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection.proto create mode 100644 vendor/google.golang.org/grpc/reflection/serverreflection.go delete mode 100644 vendor/k8s.io/kubernetes/pkg/apis/core/BUILD delete mode 100644 vendor/k8s.io/kubernetes/pkg/apis/core/OWNERS delete mode 100644 vendor/k8s.io/kubernetes/pkg/apis/core/annotation_key_constants.go delete mode 100644 vendor/k8s.io/kubernetes/pkg/apis/core/field_constants.go delete mode 100644 vendor/k8s.io/kubernetes/pkg/apis/core/json.go delete mode 100644 vendor/k8s.io/kubernetes/pkg/apis/core/objectreference.go delete mode 100644 vendor/k8s.io/kubernetes/pkg/apis/core/register.go delete mode 100644 vendor/k8s.io/kubernetes/pkg/apis/core/resource.go delete mode 100644 vendor/k8s.io/kubernetes/pkg/apis/core/taint.go delete mode 100644 vendor/k8s.io/kubernetes/pkg/apis/core/toleration.go delete mode 100644 vendor/k8s.io/kubernetes/pkg/apis/core/types.go delete mode 100644 vendor/k8s.io/kubernetes/pkg/apis/core/zz_generated.deepcopy.go create mode 100644 vendor/openpitrix.io/openpitrix/AUTHORS create mode 100644 vendor/openpitrix.io/openpitrix/CONTRIBUTORS create mode 100644 vendor/openpitrix.io/openpitrix/LICENSE create mode 100644 vendor/openpitrix.io/openpitrix/pkg/client/access/client.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/client/account/client.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/client/client.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/config/config.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/config/env_loader.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/config/global_config.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/config/init_global_config.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/constants/column.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/constants/common.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/constants/dlock.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/constants/email_template.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/constants/frontgate.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/constants/notify_message.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/constants/table.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/constants/user.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/db/Dockerfile create mode 100644 vendor/openpitrix.io/openpitrix/pkg/db/condition.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/db/connection.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/db/db.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/db/errors.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/db/event.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/db/util.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/gerr/codes.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/gerr/error.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/gerr/message.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/logger/logger.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/manager/checker.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/manager/common.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/manager/grpc_client.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/manager/grpc_server.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/0.pb.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/account.pb.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/account.pb.gw.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/app.pb.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/app.pb.gw.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/attachment.pb.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/attachment.pb.gw.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/category.pb.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/category.pb.gw.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/cluster.pb.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/cluster.pb.gw.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/isv.pb.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/isv.pb.gw.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/job.pb.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/job.pb.gw.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/market.pb.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/market.pb.gw.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/repo.pb.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/repo.pb.gw.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/repo_indexer.pb.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/repo_indexer.pb.gw.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/runtime.pb.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/runtime.pb.gw.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/runtime_provider.pb.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/service_config.pb.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/service_config.pb.gw.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/task.pb.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/task.pb.gw.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/pb/types.pb.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/sender/owner_path.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/sender/sender.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/util/ctxutil/ctx.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/util/ctxutil/message.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/util/ctxutil/request.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/util/ctxutil/sender.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/util/pbutil/pb.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/util/reflectutil/reflect.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/util/stringutil/base64.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/util/stringutil/string.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/util/yamlutil/yaml.go create mode 100644 vendor/openpitrix.io/openpitrix/pkg/version/.gitignore create mode 100644 vendor/openpitrix.io/openpitrix/pkg/version/Makefile create mode 100644 vendor/openpitrix.io/openpitrix/pkg/version/version.go diff --git a/cmd/controller-manager/app/options/options.go b/cmd/controller-manager/app/options/options.go index 08f13baae..c0c769065 100644 --- a/cmd/controller-manager/app/options/options.go +++ b/cmd/controller-manager/app/options/options.go @@ -7,6 +7,7 @@ import ( kubesphereconfig "kubesphere.io/kubesphere/pkg/server/config" "kubesphere.io/kubesphere/pkg/simple/client/devops" "kubesphere.io/kubesphere/pkg/simple/client/k8s" + "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" "kubesphere.io/kubesphere/pkg/simple/client/s2is3" "strings" ) @@ -15,6 +16,7 @@ type KubeSphereControllerManagerOptions struct { KubernetesOptions *k8s.KubernetesOptions DevopsOptions *devops.DevopsOptions S3Options *s2is3.S3Options + OpenPitrixOptions *openpitrix.OpenPitrixOptions } func NewKubeSphereControllerManagerOptions() *KubeSphereControllerManagerOptions { @@ -22,6 +24,7 @@ func NewKubeSphereControllerManagerOptions() *KubeSphereControllerManagerOptions KubernetesOptions: k8s.NewKubernetesOptions(), DevopsOptions: devops.NewDevopsOptions(), S3Options: s2is3.NewS3Options(), + OpenPitrixOptions: openpitrix.NewOpenPitrixOptions(), } return s @@ -31,7 +34,7 @@ func (s *KubeSphereControllerManagerOptions) ApplyTo(conf *kubesphereconfig.Conf s.S3Options.ApplyTo(conf.S3Options) s.KubernetesOptions.ApplyTo(conf.KubernetesOptions) s.DevopsOptions.ApplyTo(conf.DevopsOptions) - + s.OpenPitrixOptions.ApplyTo(conf.OpenPitrixOptions) } func (s *KubeSphereControllerManagerOptions) Flags() cliflag.NamedFlagSets { @@ -40,6 +43,7 @@ func (s *KubeSphereControllerManagerOptions) Flags() cliflag.NamedFlagSets { s.KubernetesOptions.AddFlags(fss.FlagSet("kubernetes")) s.DevopsOptions.AddFlags(fss.FlagSet("devops")) s.S3Options.AddFlags(fss.FlagSet("s3")) + s.OpenPitrixOptions.AddFlags(fss.FlagSet("openpitrix")) fs := fss.FlagSet("klog") local := flag.NewFlagSet("klog", flag.ExitOnError) @@ -54,10 +58,9 @@ func (s *KubeSphereControllerManagerOptions) Flags() cliflag.NamedFlagSets { func (s *KubeSphereControllerManagerOptions) Validate() []error { var errs []error - errs = append(errs, s.DevopsOptions.Validate()...) errs = append(errs, s.KubernetesOptions.Validate()...) errs = append(errs, s.S3Options.Validate()...) - + errs = append(errs, s.OpenPitrixOptions.Validate()...) return errs } diff --git a/cmd/controller-manager/app/server.go b/cmd/controller-manager/app/server.go index 4b9a9c5e0..536a94f93 100644 --- a/cmd/controller-manager/app/server.go +++ b/cmd/controller-manager/app/server.go @@ -49,10 +49,7 @@ func NewControllerManagerCommand() *cobra.Command { os.Exit(1) } - err = Complete(s) - if err != nil { - os.Exit(1) - } + s = Complete(s) if errs := s.Validate(); len(errs) != 0 { klog.Error(utilerrors.NewAggregate(errs)) @@ -81,22 +78,24 @@ func NewControllerManagerCommand() *cobra.Command { return cmd } -func Complete(s *options.KubeSphereControllerManagerOptions) error { +func Complete(s *options.KubeSphereControllerManagerOptions) *options.KubeSphereControllerManagerOptions { conf := controllerconfig.Get() conf.Apply(&controllerconfig.Config{ DevopsOptions: s.DevopsOptions, KubernetesOptions: s.KubernetesOptions, S3Options: s.S3Options, + OpenPitrixOptions: s.OpenPitrixOptions, }) - s = &options.KubeSphereControllerManagerOptions{ + out := &options.KubeSphereControllerManagerOptions{ KubernetesOptions: conf.KubernetesOptions, DevopsOptions: conf.DevopsOptions, S3Options: conf.S3Options, + OpenPitrixOptions: conf.OpenPitrixOptions, } - return nil + return out } func CreateClientSet(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{}) error { @@ -104,7 +103,8 @@ func CreateClientSet(s *options.KubeSphereControllerManagerOptions, stopCh <-cha csop.SetKubernetesOptions(s.KubernetesOptions). SetDevopsOptions(s.DevopsOptions). - SetS3Options(s.S3Options) + SetS3Options(s.S3Options). + SetOpenPitrixOptions(s.OpenPitrixOptions) client.NewClientSetFactory(csop, stopCh) return nil diff --git a/cmd/ks-apiserver/apiserver.go b/cmd/ks-apiserver/apiserver.go index 1bb0da9ff..40f21dc79 100644 --- a/cmd/ks-apiserver/apiserver.go +++ b/cmd/ks-apiserver/apiserver.go @@ -24,6 +24,7 @@ import ( _ "kubesphere.io/kubesphere/pkg/apis/devops/install" _ "kubesphere.io/kubesphere/pkg/apis/logging/install" _ "kubesphere.io/kubesphere/pkg/apis/monitoring/install" + _ "kubesphere.io/kubesphere/pkg/apis/openpitrix/install" _ "kubesphere.io/kubesphere/pkg/apis/operations/install" _ "kubesphere.io/kubesphere/pkg/apis/resources/install" _ "kubesphere.io/kubesphere/pkg/apis/servicemesh/metrics/install" diff --git a/go.mod b/go.mod index cfdd2667a..acb7c0a0c 100644 --- a/go.mod +++ b/go.mod @@ -11,11 +11,12 @@ require ( code.cloudfoundry.org/bytefmt v0.0.0-20190710193110-1eb035ffe2b6 github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect github.com/Azure/go-autorest/autorest v0.5.0 // indirect + github.com/Masterminds/semver v1.5.0 // indirect github.com/Microsoft/go-winio v0.4.12 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/PuerkitoBio/goquery v1.5.0 github.com/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30 // indirect - github.com/asaskevich/govalidator v0.0.0-20180315120708-ccb8e960c48f + github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf github.com/aws/aws-sdk-go v1.22.2 github.com/beevik/etree v1.1.0 github.com/beorn7/perks v1.0.0 // indirect @@ -24,7 +25,6 @@ require ( github.com/coreos/etcd v3.3.13+incompatible // indirect github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect - github.com/deckarep/golang-set v1.7.1 // indirect github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/docker/distribution v2.7.1+incompatible github.com/docker/docker v1.4.2-0.20190822205725-ed20165a37b4 @@ -49,6 +49,7 @@ require ( github.com/go-openapi/jsonpointer v0.19.0 // indirect github.com/go-openapi/jsonreference v0.19.0 // indirect github.com/go-openapi/spec v0.19.0 + github.com/go-openapi/strfmt v0.19.0 github.com/go-openapi/swag v0.19.0 // indirect github.com/go-playground/locales v0.12.1 // indirect github.com/go-playground/universal-translator v0.16.0 // indirect @@ -57,6 +58,7 @@ require ( github.com/gobuffalo/flect v0.1.5 // indirect github.com/gocraft/dbr v0.0.0-20180507214907-a0fd650918f6 github.com/golang/example v0.0.0-20170904185048-46695d81d1fa + github.com/golang/protobuf v1.3.1 github.com/google/go-querystring v1.0.0 // indirect github.com/google/gofuzz v1.0.0 // indirect github.com/google/uuid v1.1.1 @@ -97,7 +99,6 @@ require ( github.com/openshift/api v3.9.0+incompatible // indirect github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect - github.com/pkg/errors v0.8.1 // indirect github.com/projectcalico/go-json v0.0.0-20161128004156-6219dc7339ba // indirect github.com/projectcalico/go-yaml v0.0.0-20161201183616-955bc3e451ef // indirect github.com/projectcalico/go-yaml-wrapper v0.0.0-20161127220527-598e54215bee // indirect @@ -120,6 +121,7 @@ require ( golang.org/x/tools v0.0.0-20190710153321-831012c29e42 // indirect google.golang.org/appengine v1.5.0 // indirect google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7 // indirect + google.golang.org/grpc v1.21.0 gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/go-playground/validator.v9 v9.29.1 // indirect @@ -141,6 +143,9 @@ require ( k8s.io/klog v0.4.0 k8s.io/kube-openapi v0.0.0-20181109181836-c59034cc13d5 k8s.io/kubernetes v1.13.6 + kubesphere.io/im v0.1.0 // indirect + openpitrix.io/iam v0.1.0 // indirect + openpitrix.io/openpitrix v0.4.1-0.20190920134345-4d2be6e4965c sigs.k8s.io/application v0.0.0-20190404151855-67ae7f915d4e sigs.k8s.io/controller-runtime v0.1.10 sigs.k8s.io/controller-tools v0.1.12 @@ -161,6 +166,7 @@ replace ( github.com/Azure/go-autorest/logger => github.com/Azure/go-autorest/logger v0.1.0 github.com/Azure/go-autorest/tracing => github.com/Azure/go-autorest/tracing v0.1.0 github.com/BurntSushi/toml => github.com/BurntSushi/toml v0.3.1 + github.com/Masterminds/semver => github.com/Masterminds/semver v1.5.0 github.com/Microsoft/go-winio => github.com/Microsoft/go-winio v0.4.12 github.com/NYTimes/gziphandler => github.com/NYTimes/gziphandler v1.1.1 github.com/PuerkitoBio/goquery => github.com/PuerkitoBio/goquery v1.5.0 @@ -168,6 +174,7 @@ replace ( github.com/PuerkitoBio/urlesc => github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 github.com/Shopify/sarama => github.com/Shopify/sarama v1.19.0 github.com/Shopify/toxiproxy => github.com/Shopify/toxiproxy v2.1.4+incompatible + github.com/StackExchange/wmi => github.com/StackExchange/wmi v0.0.0-20170410192909-ea383cf3ba6e github.com/alcortesm/tgz => github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 github.com/alecthomas/template => github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc github.com/alecthomas/units => github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf @@ -180,7 +187,9 @@ replace ( github.com/aws/aws-sdk-go => github.com/aws/aws-sdk-go v1.22.2 github.com/beevik/etree => github.com/beevik/etree v1.1.0 github.com/beorn7/perks => github.com/beorn7/perks v1.0.0 + github.com/bitly/go-simplejson => github.com/bitly/go-simplejson v0.5.0 github.com/blang/semver => github.com/blang/semver v3.5.0+incompatible + github.com/bmizerany/assert => github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 github.com/cenkalti/backoff => github.com/cenkalti/backoff v2.2.1+incompatible github.com/census-instrumentation/opencensus-proto => github.com/census-instrumentation/opencensus-proto v0.2.0 github.com/cheekybits/genny => github.com/cheekybits/genny v1.0.0 @@ -192,6 +201,7 @@ replace ( github.com/coreos/pkg => github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f github.com/davecgh/go-spew => github.com/davecgh/go-spew v1.1.1 github.com/deckarep/golang-set => github.com/deckarep/golang-set v1.7.1 + github.com/denisenkom/go-mssqldb => github.com/denisenkom/go-mssqldb v0.0.0-20190204142019-df6d76eb9289 github.com/dgrijalva/jwt-go => github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/docker/distribution => github.com/docker/distribution v2.7.1+incompatible github.com/docker/docker => github.com/docker/engine v1.4.2-0.20190822205725-ed20165a37b4 @@ -212,21 +222,27 @@ replace ( github.com/emicklei/go-restful-openapi => github.com/emicklei/go-restful-openapi v1.0.0 github.com/emicklei/go-restful-swagger12 => github.com/emicklei/go-restful-swagger12 v0.0.0-20170926063155-7524189396c6 github.com/emirpasic/gods => github.com/emirpasic/gods v1.12.0 + github.com/erikstmartin/go-testdb => github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 github.com/evanphx/json-patch => github.com/evanphx/json-patch v4.2.0+incompatible + github.com/fatih/camelcase => github.com/fatih/camelcase v1.0.0 github.com/fatih/structs => github.com/fatih/structs v1.1.0 github.com/flynn/go-shlex => github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 github.com/fsnotify/fsnotify => github.com/fsnotify/fsnotify v1.4.7 github.com/ghodss/yaml => github.com/ghodss/yaml v1.0.0 github.com/gliderlabs/ssh => github.com/gliderlabs/ssh v0.1.1 + github.com/globalsign/mgo => github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb github.com/go-acme/lego => github.com/go-acme/lego v2.5.0+incompatible github.com/go-kit/kit => github.com/go-kit/kit v0.8.0 github.com/go-ldap/ldap => github.com/go-ldap/ldap v3.0.3+incompatible github.com/go-logfmt/logfmt => github.com/go-logfmt/logfmt v0.3.0 github.com/go-logr/logr => github.com/go-logr/logr v0.1.0 github.com/go-logr/zapr => github.com/go-logr/zapr v0.1.1 + github.com/go-ole/go-ole => github.com/go-ole/go-ole v1.2.1 + github.com/go-openapi/errors => github.com/go-openapi/errors v0.17.0 github.com/go-openapi/jsonpointer => github.com/go-openapi/jsonpointer v0.19.0 github.com/go-openapi/jsonreference => github.com/go-openapi/jsonreference v0.19.0 github.com/go-openapi/spec => github.com/go-openapi/spec v0.19.0 + github.com/go-openapi/strfmt => github.com/go-openapi/strfmt v0.19.0 github.com/go-openapi/swag => github.com/go-openapi/swag v0.19.0 github.com/go-playground/locales => github.com/go-playground/locales v0.12.1 github.com/go-playground/universal-translator => github.com/go-playground/universal-translator v0.16.0 @@ -235,6 +251,7 @@ replace ( github.com/go-stack/stack => github.com/go-stack/stack v1.8.0 github.com/gobuffalo/flect => github.com/gobuffalo/flect v0.1.5 github.com/gocraft/dbr => github.com/gocraft/dbr v0.0.0-20180507214907-a0fd650918f6 + github.com/gofrs/uuid => github.com/gofrs/uuid v3.2.0+incompatible github.com/gogo/protobuf => github.com/gogo/protobuf v1.2.0 github.com/golang/example => github.com/golang/example v0.0.0-20170904185048-46695d81d1fa github.com/golang/glog => github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b @@ -246,6 +263,7 @@ replace ( github.com/google/go-cmp => github.com/google/go-cmp v0.2.0 github.com/google/go-querystring => github.com/google/go-querystring v1.0.0 github.com/google/gofuzz => github.com/google/gofuzz v1.0.0 + github.com/google/gops => github.com/google/gops v0.3.6 github.com/google/uuid => github.com/google/uuid v1.1.1 github.com/googleapis/gnostic => github.com/googleapis/gnostic v0.2.0 github.com/gophercloud/gophercloud => github.com/gophercloud/gophercloud v0.3.0 @@ -267,15 +285,21 @@ replace ( github.com/jbenet/go-context => github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 github.com/jessevdk/go-flags => github.com/jessevdk/go-flags v1.4.0 github.com/jimstudt/http-authentication => github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a + github.com/jinzhu/gorm => github.com/jinzhu/gorm v1.9.2 + github.com/jinzhu/inflection => github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a + github.com/jinzhu/now => github.com/jinzhu/now v1.0.0 github.com/jmespath/go-jmespath => github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af github.com/jonboulle/clockwork => github.com/jonboulle/clockwork v0.1.0 github.com/json-iterator/go => github.com/json-iterator/go v1.1.6 github.com/julienschmidt/httprouter => github.com/julienschmidt/httprouter v1.2.0 + github.com/kardianos/osext => github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 github.com/kelseyhightower/envconfig => github.com/kelseyhightower/envconfig v1.4.0 github.com/kevinburke/ssh_config => github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e + github.com/keybase/go-ps => github.com/keybase/go-ps v0.0.0-20161005175911-668c8856d999 github.com/kiali/kiali => github.com/kubesphere/kiali v0.15.1-0.20190407071308-6b5b818211c3 github.com/klauspost/cpuid => github.com/klauspost/cpuid v1.2.1 github.com/knative/pkg => github.com/knative/pkg v0.0.0-20190314204845-cd278f2d3394 + github.com/koding/multiconfig => github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7 github.com/konsorten/go-windows-terminal-sequences => github.com/konsorten/go-windows-terminal-sequences v1.0.2 github.com/kr/logfmt => github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 github.com/kr/pretty => github.com/kr/pretty v0.1.0 @@ -331,6 +355,8 @@ replace ( github.com/russross/blackfriday => github.com/russross/blackfriday v1.5.2 github.com/satori/go.uuid => github.com/satori/go.uuid v1.2.0 github.com/sergi/go-diff => github.com/sergi/go-diff v1.0.0 + github.com/shirou/gopsutil => github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7 + github.com/shirou/w32 => github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 github.com/sirupsen/logrus => github.com/sirupsen/logrus v1.2.0 github.com/soheilhy/cmux => github.com/soheilhy/cmux v0.1.4 github.com/sony/sonyflake => github.com/sony/sonyflake v0.0.0-20181109022403-6d5bd6181009 @@ -344,14 +370,12 @@ replace ( github.com/src-d/gcfg => github.com/src-d/gcfg v1.4.0 github.com/stretchr/objx => github.com/stretchr/objx v0.1.1 github.com/stretchr/testify => github.com/stretchr/testify v1.3.0 - github.com/tidwall/gjson => github.com/tidwall/gjson v1.3.2 - github.com/tidwall/match => github.com/tidwall/match v1.0.1 - github.com/tidwall/pretty => github.com/tidwall/pretty v1.0.0 - github.com/tidwall/sjson => github.com/tidwall/sjson v1.0.4 github.com/tmc/grpc-websocket-proxy => github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 github.com/ugorji/go => github.com/ugorji/go v1.1.4 + github.com/urfave/cli => github.com/urfave/cli v1.20.0 github.com/xanzy/ssh-agent => github.com/xanzy/ssh-agent v0.2.1 github.com/xiang90/probing => github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 + github.com/xlab/treeprint => github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6 github.com/xordataexchange/crypt => github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 go.etcd.io/bbolt => go.etcd.io/bbolt v1.3.3 go.opencensus.io => go.opencensus.io v0.20.2 @@ -404,6 +428,11 @@ replace ( k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20181109181836-c59034cc13d5 k8s.io/kubernetes => k8s.io/kubernetes v1.13.6 k8s.io/utils => k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5 + kubesphere.io/im => kubesphere.io/im v0.1.0 + openpitrix.io/iam => openpitrix.io/iam v0.1.0 + openpitrix.io/logger v0.1.0 => github.com/openpitrix/logger v0.1.0 + openpitrix.io/openpitrix => openpitrix.io/openpitrix v0.4.1-0.20190920134345-4d2be6e4965c + rsc.io/goversion => rsc.io/goversion v1.0.0 sigs.k8s.io/application => sigs.k8s.io/application v0.0.0-20190404151855-67ae7f915d4e sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.1.10 sigs.k8s.io/controller-tools => sigs.k8s.io/controller-tools v0.1.12 diff --git a/go.sum b/go.sum index cd7cca3ea..71c0ce207 100644 --- a/go.sum +++ b/go.sum @@ -22,6 +22,8 @@ github.com/Azure/go-autorest/tracing v0.1.0 h1:TRBxC5Pj/fIuh4Qob0ZpkggbfT8RC0Sub github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc= github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= @@ -34,6 +36,7 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/StackExchange/wmi v0.0.0-20170410192909-ea383cf3ba6e/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= @@ -56,7 +59,11 @@ github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4= @@ -78,6 +85,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/denisenkom/go-mssqldb v0.0.0-20190204142019-df6d76eb9289/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= @@ -115,8 +123,11 @@ github.com/emicklei/go-restful-swagger12 v0.0.0-20170926063155-7524189396c6 h1:V github.com/emicklei/go-restful-swagger12 v0.0.0-20170926063155-7524189396c6/go.mod h1:qr0VowGBT4CS4Q8vFF8BSeKz34PuqKGxs/L0IAQA9DQ= github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= +github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= @@ -127,6 +138,8 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1 h1:j3L6gSLQalDETeEg/Jg0mGY0/y/N6zI2xX1978P0Uqw= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb h1:D4uzjWwKYQ5XnAvUbuvHW93esHg7F8N/OYeBBcJoTr0= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-acme/lego v2.5.0+incompatible h1:5fNN9yRQfv8ymH3DSsxla+4aYeQt2IgfZqHKVnK8f0s= github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -137,12 +150,17 @@ github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/zapr v0.1.1 h1:qXBXPDdNncunGs7XeEpsJt8wCjYBygluzfdLO0G5baE= github.com/go-logr/zapr v0.1.1/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= +github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-openapi/errors v0.17.0 h1:g5DzIh94VpuR/dd6Ff8KqyHNnw7yBa2xSHIPPzjRDUo= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= github.com/go-openapi/jsonpointer v0.19.0 h1:FTUMcX77w5rQkClIzDtTxvn6Bsa894CcrzNj2MMfeg8= github.com/go-openapi/jsonpointer v0.19.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonreference v0.19.0 h1:BqWKpV1dFd+AuiKlgtddwVIFQsuMpxfBDBHGfM2yNpk= github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4= github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/strfmt v0.19.0 h1:0Dn9qy1G9+UJfRU7TR8bmdGxb4uifB7HNrJjOnV0yPk= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= github.com/go-openapi/swag v0.19.0 h1:Kg7Wl7LkTPlmc393QZQ/5rQadPhi7pBVEMZxyTi0Ii8= github.com/go-openapi/swag v0.19.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc= @@ -153,11 +171,13 @@ github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDA github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobuffalo/flect v0.1.5 h1:xpKq9ap8MbYfhuPCF0dBH854Gp9CxZjr/IocxELFflo= github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= github.com/gocraft/dbr v0.0.0-20180507214907-a0fd650918f6 h1:kumyNm8Vr8cbVm/aLQYTbDE3SKCbbn5HEVoDp/Dyyfc= github.com/gocraft/dbr v0.0.0-20180507214907-a0fd650918f6/go.mod h1:K/9g3pPouf13kP5K7pdriQEJAy272R9yXuWuDIEWJTM= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/example v0.0.0-20170904185048-46695d81d1fa h1:iqCQC2Z53KkwGgTN9szyL4q0OQHmuNjeoNnMT6lk66k= @@ -179,6 +199,7 @@ github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASu github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gops v0.3.6/go.mod h1:RZ1rH95wsAGX4vMWKmqBOIWynmWisBf4QFdgT/k/xOI= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= @@ -219,6 +240,9 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a h1:BcF8coBl0QFVhe8vAMMlD+CV8EISiu9MGKLoj6ZEyJA= github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8= +github.com/jinzhu/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo= +github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.0.0/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= @@ -226,14 +250,18 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22 github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp/Cjunrr1WlsXSZpqXn+uREuHvUVcK82CV8= github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/keybase/go-ps v0.0.0-20161005175911-668c8856d999/go.mod h1:hY+WOq6m2FpbvyrI93sMaypsttvaIL5nhVR92dTMUcQ= github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/knative/pkg v0.0.0-20190314204845-cd278f2d3394 h1:Lg2DviikLeZmY0rnPpVLMC77h7vqZG5mjh33apZl76o= github.com/knative/pkg v0.0.0-20190314204845-cd278f2d3394/go.mod h1:7Ijfhw7rfB+H9VtosIsDYvZQ+qYTz7auK3fHW/5z4ww= +github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7 h1:SWlt7BoQNASbhTUD0Oy5yysI2seJ7vWuGUp///OM4TM= +github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7/go.mod h1:Y2SaZf2Rzd0pXkLVhLlCiAXFCLSXAIbTKDivVgff/AM= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -297,6 +325,7 @@ github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2i github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/openpitrix/logger v0.1.0/go.mod h1:SV8Btt2cTSmeL9H/1XCkYmQ+WQ2upVY4e0wlr07RP28= github.com/openshift/api v3.9.0+incompatible h1:fJ/KsefYuZAjmrr3+5U9yZIZbTOpVkDDLDLFresAeYs= github.com/openshift/api v3.9.0+incompatible/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= @@ -338,6 +367,8 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= @@ -367,10 +398,12 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -470,6 +503,13 @@ k8s.io/kubernetes v1.13.6 h1:eUAUryzMLFmi4ZY8kMOUtLG5lHp2PUx5WOmy4RVaobk= k8s.io/kubernetes v1.13.6/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5 h1:VBM/0P5TWxwk+Nw6Z+lAw3DKgO76g90ETOiA6rfLV1Y= k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +kubesphere.io/im v0.1.0 h1:Isu/WBOawUb4fzSlQeD1f6Vbq9pqFS0PmDg8v8iFYaY= +kubesphere.io/im v0.1.0/go.mod h1:DHJj/JngMUFyaXecLjBPXj/zk5Oi7ifIixLRp0qJkyA= +openpitrix.io/iam v0.1.0 h1:cb1mCusim7EGeoXEfuaVa1m7Co/pzim3keoxxKdv944= +openpitrix.io/iam v0.1.0/go.mod h1:EcZE8CPBg+1fEKCDEhpsIZ8isWWO7javpu84mSqoVn4= +openpitrix.io/openpitrix v0.4.1-0.20190920134345-4d2be6e4965c h1:r/wQNNzFE0Blbf42Dl8pRNSRc+YiYdgOclNcP3VMeTs= +openpitrix.io/openpitrix v0.4.1-0.20190920134345-4d2be6e4965c/go.mod h1:8rZSFeUp2Np5kfKAXfRL/1HSh6BYywNCALt+ZnxFQ/4= +rsc.io/goversion v1.0.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo= sigs.k8s.io/application v0.0.0-20190404151855-67ae7f915d4e h1:/TWUhUxC+Q5uMFUizxYzNAZjwbjlYXOsfnmSC2WpyuI= sigs.k8s.io/application v0.0.0-20190404151855-67ae7f915d4e/go.mod h1:9C86g0wiFn8jtZjgJepSx188uJeWLGWTbcCycu5p8mU= sigs.k8s.io/controller-runtime v0.1.10 h1:amLOmcekVdnsD1uIpmgRqfTbQWJ2qxvQkcdeFhcotn4= diff --git a/vendor/k8s.io/kubernetes/pkg/apis/core/doc.go b/pkg/apis/openpitrix/group.go similarity index 52% rename from vendor/k8s.io/kubernetes/pkg/apis/core/doc.go rename to pkg/apis/openpitrix/group.go index 6017bfdab..f6b0abd4a 100644 --- a/vendor/k8s.io/kubernetes/pkg/apis/core/doc.go +++ b/pkg/apis/openpitrix/group.go @@ -1,5 +1,5 @@ /* -Copyright 2014 The Kubernetes Authors. +Copyright 2019 The KubeSphere authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,11 +14,5 @@ See the License for the specific language governing permissions and limitations under the License. */ -// +k8s:deepcopy-gen=package - -// Package api contains the latest (or "internal") version of the -// Kubernetes API objects. This is the API objects as represented in memory. -// The contract presented to clients is located in the versioned packages, -// which are sub-directories. The first one is "v1". Those packages -// describe how a particular version is serialized to storage/network. -package core // import "k8s.io/kubernetes/pkg/apis/core" +// Package operations contains operations API versions +package openpitrix diff --git a/pkg/apis/openpitrix/install/install.go b/pkg/apis/openpitrix/install/install.go new file mode 100644 index 000000000..5f436be1e --- /dev/null +++ b/pkg/apis/openpitrix/install/install.go @@ -0,0 +1,33 @@ +/* + + Copyright 2019 The KubeSphere Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +package install + +import ( + "github.com/emicklei/go-restful" + urlruntime "k8s.io/apimachinery/pkg/util/runtime" + openpitrixv1 "kubesphere.io/kubesphere/pkg/apis/openpitrix/v1" + "kubesphere.io/kubesphere/pkg/apiserver/runtime" +) + +func init() { + Install(runtime.Container) +} + +func Install(container *restful.Container) { + urlruntime.Must(openpitrixv1.AddToContainer(container)) +} diff --git a/pkg/apis/openpitrix/v1/register.go b/pkg/apis/openpitrix/v1/register.go new file mode 100644 index 000000000..6464b746e --- /dev/null +++ b/pkg/apis/openpitrix/v1/register.go @@ -0,0 +1,346 @@ +/* + * + * Copyright 2019 The KubeSphere Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ +package v1 + +import ( + "github.com/emicklei/go-restful" + "github.com/emicklei/go-restful-openapi" + "k8s.io/apimachinery/pkg/runtime/schema" + "kubesphere.io/kubesphere/pkg/apiserver/openpitrix" + "kubesphere.io/kubesphere/pkg/apiserver/runtime" + "kubesphere.io/kubesphere/pkg/constants" + "kubesphere.io/kubesphere/pkg/models" + opmodels "kubesphere.io/kubesphere/pkg/models/openpitrix" + "kubesphere.io/kubesphere/pkg/server/errors" + "kubesphere.io/kubesphere/pkg/server/params" + "net/http" +) + +const GroupName = "openpitrix.io" + +var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"} + +var ( + WebServiceBuilder = runtime.NewContainerBuilder(addWebService) + AddToContainer = WebServiceBuilder.AddToContainer +) + +func addWebService(c *restful.Container) error { + + ok := "ok" + mimePatch := []string{restful.MIME_JSON, runtime.MimeMergePatchJson, runtime.MimeJsonPatchJson} + webservice := runtime.NewWebService(GroupVersion) + + webservice.Route(webservice.GET("/applications"). + To(openpitrix.ListApplications). + Returns(http.StatusOK, ok, models.PageableResponse{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). + Doc("List all applications"). + Param(webservice.QueryParameter(params.ConditionsParam, "query conditions, connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). + Required(false). + DataFormat("key=value,key~value"). + DefaultValue("")). + Param(webservice.PathParameter("namespace", "the name of the project")). + Param(webservice.QueryParameter(params.PagingParam, "paging query, e.g. limit=100,page=1"). + Required(false). + DataFormat("limit=%d,page=%d"). + DefaultValue("limit=10,page=1"))) + + webservice.Route(webservice.GET("/namespaces/{namespace}/applications"). + To(openpitrix.ListApplications). + Returns(http.StatusOK, ok, models.PageableResponse{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). + Doc("List all applications within the specified namespace"). + Param(webservice.QueryParameter(params.ConditionsParam, "query conditions, connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). + Required(false). + DataFormat("key=value,key~value"). + DefaultValue("")). + Param(webservice.PathParameter("namespace", "the name of the project")). + Param(webservice.QueryParameter(params.PagingParam, "paging query, e.g. limit=100,page=1"). + Required(false). + DataFormat("limit=%d,page=%d"). + DefaultValue("limit=10,page=1"))) + + webservice.Route(webservice.GET("/namespaces/{namespace}/applications/{application}"). + To(openpitrix.DescribeApplication). + Returns(http.StatusOK, ok, opmodels.Application{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). + Doc("Describe the specified application of the namespace"). + Param(webservice.PathParameter("namespace", "the name of the project")). + Param(webservice.PathParameter("application", "application ID"))) + + webservice.Route(webservice.POST("/namespaces/{namespace}/applications"). + To(openpitrix.CreateApplication). + Doc("Deploy a new application"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). + Reads(opmodels.CreateClusterRequest{}). + Returns(http.StatusOK, ok, errors.Error{}). + Param(webservice.PathParameter("namespace", "the name of the project"))) + + webservice.Route(webservice.PATCH("/namespaces/{namespace}/applications/{application}"). + Consumes(mimePatch...). + To(openpitrix.ModifyApplication). + Doc("Modify application"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). + Reads(opmodels.ModifyClusterAttributesRequest{}). + Returns(http.StatusOK, ok, errors.Error{}). + Param(webservice.PathParameter("namespace", "the name of the project")). + Param(webservice.PathParameter("application", "the id of the application cluster"))) + + webservice.Route(webservice.DELETE("/namespaces/{namespace}/applications/{application}"). + To(openpitrix.DeleteApplication). + Doc("Delete the specified application"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). + Returns(http.StatusOK, ok, errors.Error{}). + Param(webservice.PathParameter("namespace", "the name of the project")). + Param(webservice.PathParameter("application", "the id of the application cluster"))) + + webservice.Route(webservice.POST("/apps/{app}/versions"). + To(openpitrix.CreateAppVersion). + Doc("Create a new app template version"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). + Reads(opmodels.CreateAppVersionRequest{}). + Param(webservice.QueryParameter("validate", "Validate format of package(pack by op tool)")). + Returns(http.StatusOK, ok, opmodels.CreateAppVersionResponse{}). + Param(webservice.PathParameter("app", "app template id"))) + webservice.Route(webservice.DELETE("/apps/{app}/versions/{version}"). + To(openpitrix.DeleteAppVersion). + Doc("Delete the specified app template version"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). + Returns(http.StatusOK, ok, errors.Error{}). + Param(webservice.PathParameter("version", "app template version id")). + Param(webservice.PathParameter("app", "app template id"))) + webservice.Route(webservice.PATCH("/apps/{app}/versions/{version}"). + Consumes(mimePatch...). + To(openpitrix.ModifyAppVersion). + Doc("Patch the specified app template version"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). + Reads(opmodels.ModifyAppVersionRequest{}). + Returns(http.StatusOK, ok, errors.Error{}). + Param(webservice.PathParameter("version", "app template version id")). + Param(webservice.PathParameter("app", "app template id"))) + webservice.Route(webservice.GET("/apps/{app}/versions/{version}"). + To(openpitrix.DescribeAppVersion). + Doc("Describe the specified app template version"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). + Returns(http.StatusOK, ok, opmodels.AppVersion{}). + Param(webservice.PathParameter("version", "app template version id")). + Param(webservice.PathParameter("app", "app template id"))) + webservice.Route(webservice.GET("/apps/{app}/versions"). + To(openpitrix.ListAppVersions). + Doc("Get active versions of app, can filter with these fields(version_id, app_id, name, owner, description, package_name, status, type), default return all active app versions"). + Param(webservice.QueryParameter(params.ConditionsParam, "query conditions,connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). + Required(false). + DataFormat("key=%s,key~%s")). + Param(webservice.QueryParameter(params.PagingParam, "paging query, e.g. limit=100,page=1"). + Required(false). + DataFormat("limit=%d,page=%d"). + DefaultValue("limit=10,page=1")). + Param(webservice.PathParameter("app", "app template id")). + Param(webservice.QueryParameter(params.ReverseParam, "sort parameters, e.g. reverse=true")). + Param(webservice.QueryParameter(params.OrderByParam, "sort parameters, e.g. orderBy=createTime")). + Returns(http.StatusOK, ok, models.PageableResponse{})) + webservice.Route(webservice.GET("/apps/{app}/versions/{version}/audits"). + To(openpitrix.ListAppVersionAudits). + Doc("List audits information of version-specific app template"). + Returns(http.StatusOK, ok, opmodels.AppVersionAudit{}). + Param(webservice.PathParameter("version", "app template version id")). + Param(webservice.PathParameter("app", "app template id"))) + webservice.Route(webservice.GET("/apps/{app}/versions/{version}/package"). + To(openpitrix.GetAppVersionPackage). + Doc("Get packages of version-specific app"). + Returns(http.StatusOK, ok, opmodels.GetAppVersionPackageResponse{}). + Param(webservice.PathParameter("version", "app template version id")). + Param(webservice.PathParameter("app", "app template id"))) + webservice.Route(webservice.POST("/apps/{app}/versions/{version}/action"). + To(openpitrix.DoAppVersionAction). + Doc("Perform submit or other operations on app"). + Returns(http.StatusOK, ok, errors.Error{}). + Param(webservice.PathParameter("version", "app template version id")). + Param(webservice.PathParameter("app", "app template id"))) + webservice.Route(webservice.GET("/apps/{app}/versions/{version}/files"). + To(openpitrix.GetAppVersionFiles). + Doc("Get app template package files"). + Returns(http.StatusOK, ok, opmodels.GetAppVersionPackageFilesResponse{}). + Param(webservice.PathParameter("version", "app template version id")). + Param(webservice.PathParameter("app", "app template id"))) + webservice.Route(webservice.GET("/reviews"). + To(openpitrix.ListReviews). + Doc("Get reviews of version-specific app"). + Param(webservice.QueryParameter(params.ConditionsParam, "query conditions,connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). + Required(false). + DataFormat("key=%s,key~%s")). + Param(webservice.QueryParameter(params.PagingParam, "paging query, e.g. limit=100,page=1"). + Required(false). + DataFormat("limit=%d,page=%d"). + DefaultValue("limit=10,page=1")). + Returns(http.StatusOK, ok, opmodels.AppVersionReview{})) + webservice.Route(webservice.GET("/apps/{app}/audits"). + To(openpitrix.ListAppVersionAudits). + Doc("List audits information of the specific app template"). + Param(webservice.PathParameter("app", "app template id")). + Returns(http.StatusOK, ok, opmodels.AppVersionAudit{})) + webservice.Route(webservice.POST("/apps"). + To(openpitrix.CreateApp). + Doc("Create a new app template"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). + Returns(http.StatusOK, ok, opmodels.CreateAppResponse{}). + Reads(opmodels.CreateAppRequest{}). + Param(webservice.PathParameter("app", "app template id"))) + webservice.Route(webservice.DELETE("/apps/{app}"). + To(openpitrix.DeleteApp). + Doc("Delete the specified app template"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). + Returns(http.StatusOK, ok, errors.Error{}). + Param(webservice.PathParameter("app", "app template id"))) + webservice.Route(webservice.PATCH("/apps/{app}"). + Consumes(mimePatch...). + To(openpitrix.ModifyApp). + Doc("Patch the specified app template"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). + Reads(opmodels.ModifyAppVersionRequest{}). + Returns(http.StatusOK, ok, errors.Error{}). + Param(webservice.PathParameter("app", "app template id"))) + webservice.Route(webservice.GET("/apps/{app}"). + To(openpitrix.DescribeApp). + Doc("Describe the specified app template"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). + Returns(http.StatusOK, ok, opmodels.AppVersion{}). + Param(webservice.PathParameter("app", "app template id"))) + webservice.Route(webservice.POST("/apps/{app}/action"). + To(openpitrix.DoAppAction). + Doc("Perform recover or suspend operation on app"). + Returns(http.StatusOK, ok, errors.Error{}). + Param(webservice.PathParameter("version", "app template version id")). + Param(webservice.PathParameter("app", "app template id"))) + webservice.Route(webservice.GET("/apps"). + To(openpitrix.ListApps). + Doc("List app templates"). + Param(webservice.QueryParameter(params.ConditionsParam, "query conditions,connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). + Required(false). + DataFormat("key=%s,key~%s")). + Param(webservice.QueryParameter(params.PagingParam, "paging query, e.g. limit=100,page=1"). + Required(false). + DataFormat("limit=%d,page=%d"). + DefaultValue("limit=10,page=1")). + Param(webservice.QueryParameter(params.ReverseParam, "sort parameters, e.g. reverse=true")). + Param(webservice.QueryParameter(params.OrderByParam, "sort parameters, e.g. orderBy=createTime")). + Returns(http.StatusOK, ok, models.PageableResponse{})) + webservice.Route(webservice.POST("/categories"). + To(openpitrix.CreateCategory). + Doc("Create app template category"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). + Reads(opmodels.CreateCategoryRequest{}). + Returns(http.StatusOK, ok, opmodels.CreateCategoryResponse{}). + Param(webservice.PathParameter("app", "app template id"))) + webservice.Route(webservice.DELETE("/categories/{category}"). + To(openpitrix.DeleteCategory). + Doc("Delete the specified category"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). + Returns(http.StatusOK, ok, errors.Error{}). + Param(webservice.PathParameter("category", "category id"))) + webservice.Route(webservice.PATCH("/categories/{category}"). + Consumes(mimePatch...). + To(openpitrix.ModifyCategory). + Doc("Patch the specified category"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). + Reads(opmodels.ModifyCategoryRequest{}). + Returns(http.StatusOK, ok, errors.Error{}). + Param(webservice.PathParameter("category", "category id"))) + webservice.Route(webservice.GET("/categories/{category}"). + To(openpitrix.DescribeCategory). + Doc("Describe the specified category"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). + Returns(http.StatusOK, ok, opmodels.Category{}). + Param(webservice.PathParameter("category", "category id"))) + webservice.Route(webservice.GET("/categories"). + To(openpitrix.ListCategories). + Doc("List categories"). + Param(webservice.QueryParameter(params.ConditionsParam, "query conditions,connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). + Required(false). + DataFormat("key=%s,key~%s")). + Param(webservice.QueryParameter(params.PagingParam, "paging query, e.g. limit=100,page=1"). + Required(false). + DataFormat("limit=%d,page=%d"). + DefaultValue("limit=10,page=1")). + Param(webservice.QueryParameter(params.ReverseParam, "sort parameters, e.g. reverse=true")). + Param(webservice.QueryParameter(params.OrderByParam, "sort parameters, e.g. orderBy=createTime")). + Returns(http.StatusOK, ok, models.PageableResponse{})) + + webservice.Route(webservice.GET("/attachments/{attachment}"). + To(openpitrix.DescribeAttachment). + Doc("Get attachment by attachment id"). + Param(webservice.PathParameter("attachment", "attachment id")). + Returns(http.StatusOK, ok, opmodels.Attachment{})) + + webservice.Route(webservice.POST("/repos"). + To(openpitrix.CreateRepo). + Doc("Create repository, repository used to store package of app"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). + Param(webservice.QueryParameter("validate", "Validate repository")). + Returns(http.StatusOK, ok, opmodels.CreateRepoResponse{}). + Reads(opmodels.CreateRepoRequest{})) + webservice.Route(webservice.DELETE("/repos/{repo}"). + To(openpitrix.DeleteRepo). + Doc("Delete the specified repository"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). + Returns(http.StatusOK, ok, errors.Error{}). + Param(webservice.PathParameter("repo", "repo id"))) + webservice.Route(webservice.PATCH("/repos/{repo}"). + Consumes(mimePatch...). + To(openpitrix.ModifyRepo). + Doc("Patch the specified repository"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). + Reads(opmodels.ModifyRepoRequest{}). + Returns(http.StatusOK, ok, errors.Error{}). + Param(webservice.PathParameter("repo", "repo id"))) + webservice.Route(webservice.GET("/repos/{repo}"). + To(openpitrix.DescribeRepo). + Doc("Describe the specified repository"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). + Returns(http.StatusOK, ok, opmodels.Repo{}). + Param(webservice.PathParameter("repo", "repo id"))) + webservice.Route(webservice.GET("/repos"). + To(openpitrix.ListRepos). + Doc("List repositories"). + Param(webservice.QueryParameter(params.ConditionsParam, "query conditions,connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). + Required(false). + DataFormat("key=%s,key~%s")). + Param(webservice.QueryParameter(params.PagingParam, "paging query, e.g. limit=100,page=1"). + Required(false). + DataFormat("limit=%d,page=%d"). + DefaultValue("limit=10,page=1")). + Param(webservice.QueryParameter(params.ReverseParam, "sort parameters, e.g. reverse=true")). + Param(webservice.QueryParameter(params.OrderByParam, "sort parameters, e.g. orderBy=createTime")). + Returns(http.StatusOK, ok, models.PageableResponse{})) + webservice.Route(webservice.POST("/repos/{repo}/action"). + To(openpitrix.DoRepoAction). + Doc("Start index repository event"). + Reads(opmodels.RepoActionRequest{}). + Returns(http.StatusOK, ok, errors.Error{}). + Param(webservice.PathParameter("repo", "repo id"))) + webservice.Route(webservice.GET("/repos/{repo}/events"). + To(openpitrix.ListRepoEvents). + Doc("Get repository events"). + Returns(http.StatusOK, ok, models.PageableResponse{}). + Param(webservice.PathParameter("repo", "repo id"))) + + c.Add(webservice) + + return nil +} diff --git a/pkg/apis/resources/v1alpha2/register.go b/pkg/apis/resources/v1alpha2/register.go index b6bdb5afb..227cd1173 100644 --- a/pkg/apis/resources/v1alpha2/register.go +++ b/pkg/apis/resources/v1alpha2/register.go @@ -35,13 +35,11 @@ import ( "kubesphere.io/kubesphere/pkg/apiserver/workloadstatuses" "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/applications" gitmodel "kubesphere.io/kubesphere/pkg/models/git" registriesmodel "kubesphere.io/kubesphere/pkg/models/registries" "kubesphere.io/kubesphere/pkg/models/status" "kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" "net/http" ) @@ -110,61 +108,6 @@ func addWebService(c *restful.Container) error { Param(webservice.PathParameter("node", "node name")). Returns(http.StatusOK, ok, errors.Error{})) - webservice.Route(webservice.GET("/applications"). - To(resources.ListApplication). - Returns(http.StatusOK, ok, models.PageableResponse{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterResourcesTag}). - Doc("List applications in cluster"). - Param(webservice.QueryParameter(params.ConditionsParam, "query conditions, connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). - Required(false). - DataFormat("key=value,key~value"). - DefaultValue("")). - Param(webservice.QueryParameter("cluster_id", "equivalent to application unique ID")). - Param(webservice.QueryParameter("runtime_id", "runtime id initialization when namespace is created, means which namespace")). - Param(webservice.QueryParameter(params.PagingParam, "paging query, e.g. limit=100,page=1"). - Required(false). - DataFormat("limit=%d,page=%d"). - DefaultValue("limit=10,page=1"))) - - webservice.Route(webservice.GET("/namespaces/{namespace}/applications"). - To(resources.ListNamespacedApplication). - Returns(http.StatusOK, ok, models.PageableResponse{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). - Doc("List all applications within the specified namespace"). - Param(webservice.QueryParameter(params.ConditionsParam, "query conditions, connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). - Required(false). - DataFormat("key=value,key~value"). - DefaultValue("")). - Param(webservice.PathParameter("namespace", "the name of the project")). - Param(webservice.QueryParameter(params.PagingParam, "paging query, e.g. limit=100,page=1"). - Required(false). - DataFormat("limit=%d,page=%d"). - DefaultValue("limit=10,page=1"))) - - webservice.Route(webservice.GET("/namespaces/{namespace}/applications/{application}"). - To(resources.DescribeApplication). - Returns(http.StatusOK, ok, applications.Application{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). - Doc("Describe the specified application of the namespace"). - Param(webservice.PathParameter("namespace", "the name of the project")). - Param(webservice.PathParameter("application", "application ID"))) - - webservice.Route(webservice.POST("/namespaces/{namespace}/applications"). - To(resources.DeployApplication). - Doc("Deploy a new application"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). - Reads(openpitrix.CreateClusterRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). - Param(webservice.PathParameter("namespace", "the name of the project"))) - - webservice.Route(webservice.DELETE("/namespaces/{namespace}/applications/{application}"). - To(resources.DeleteApplication). - Doc("Delete the specified application"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). - Returns(http.StatusOK, ok, errors.Error{}). - Param(webservice.PathParameter("namespace", "the name of the project")). - Param(webservice.PathParameter("application", "application ID"))) - webservice.Route(webservice.GET("/users/{user}/kubectl"). To(resources.GetKubectl). Doc("get user's kubectl pod"). diff --git a/pkg/apiserver/openpitrix/applications.go b/pkg/apiserver/openpitrix/applications.go new file mode 100644 index 000000000..a12239f84 --- /dev/null +++ b/pkg/apiserver/openpitrix/applications.go @@ -0,0 +1,273 @@ +/* + * + * Copyright 2019 The KubeSphere Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package openpitrix + +import ( + "fmt" + "github.com/emicklei/go-restful" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "k8s.io/api/core/v1" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/constants" + "kubesphere.io/kubesphere/pkg/models" + "kubesphere.io/kubesphere/pkg/models/openpitrix" + "kubesphere.io/kubesphere/pkg/models/resources" + "kubesphere.io/kubesphere/pkg/server/errors" + "kubesphere.io/kubesphere/pkg/server/params" + "kubesphere.io/kubesphere/pkg/simple/client" + "net/http" +) + +func ListApplications(req *restful.Request, resp *restful.Response) { + limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) + namespaceName := req.PathParameter("namespace") + conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + if namespaceName != "" { + namespace, err := resources.GetResource("", resources.Namespaces, namespaceName) + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + var runtimeId string + + if ns, ok := namespace.(*v1.Namespace); ok { + runtimeId = ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] + } + + if runtimeId == "" { + resp.WriteAsJson(models.PageableResponse{Items: []interface{}{}, TotalCount: 0}) + return + } else { + conditions.Match["runtime_id"] = runtimeId + } + } + + result, err := openpitrix.ListApplications(conditions, limit, offset) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteAsJson(result) +} + +func DescribeApplication(req *restful.Request, resp *restful.Response) { + clusterId := req.PathParameter("application") + namespaceName := req.PathParameter("namespace") + app, err := openpitrix.DescribeApplication(namespaceName, clusterId) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + if status.Code(err) == codes.NotFound { + resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + return + } + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + namespace, err := resources.GetResource("", resources.Namespaces, namespaceName) + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + var runtimeId string + + if ns, ok := namespace.(*v1.Namespace); ok { + runtimeId = ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] + } + + if runtimeId != app.Cluster.RuntimeId { + err = fmt.Errorf("rumtime not match %s,%s", app.Cluster.RuntimeId, runtimeId) + klog.V(4).Info(err) + resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err)) + return + } + + resp.WriteEntity(app) + return +} + +func CreateApplication(req *restful.Request, resp *restful.Response) { + namespace := req.PathParameter("namespace") + var createClusterRequest openpitrix.CreateClusterRequest + err := req.ReadEntity(&createClusterRequest) + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + createClusterRequest.Username = req.HeaderParameter(constants.UserNameHeader) + + err = openpitrix.CreateApplication(namespace, createClusterRequest) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + resp.WriteEntity(errors.None) +} + +func ModifyApplication(req *restful.Request, resp *restful.Response) { + var modifyClusterAttributesRequest openpitrix.ModifyClusterAttributesRequest + clusterId := req.PathParameter("application") + namespaceName := req.PathParameter("namespace") + err := req.ReadEntity(&modifyClusterAttributesRequest) + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + app, err := openpitrix.DescribeApplication(namespaceName, clusterId) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if status.Code(err) == codes.NotFound { + resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + return + } + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + namespace, err := resources.GetResource("", resources.Namespaces, namespaceName) + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + var runtimeId string + + if ns, ok := namespace.(*v1.Namespace); ok { + runtimeId = ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] + } + + if runtimeId != app.Cluster.RuntimeId { + err = fmt.Errorf("rumtime not match %s,%s", app.Cluster.RuntimeId, runtimeId) + klog.V(4).Info(err) + resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err)) + return + } + + err = openpitrix.PatchApplication(&modifyClusterAttributesRequest) + + if status.Code(err) == codes.NotFound { + resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + return + } + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(errors.None) +} + +func DeleteApplication(req *restful.Request, resp *restful.Response) { + clusterId := req.PathParameter("application") + namespaceName := req.PathParameter("namespace") + app, err := openpitrix.DescribeApplication(namespaceName, clusterId) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if status.Code(err) == codes.NotFound { + resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + return + } + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + namespace, err := resources.GetResource("", resources.Namespaces, namespaceName) + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + var runtimeId string + + if ns, ok := namespace.(*v1.Namespace); ok { + runtimeId = ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] + } + + if runtimeId != app.Cluster.RuntimeId { + err = fmt.Errorf("rumtime not match %s,%s", app.Cluster.RuntimeId, runtimeId) + klog.V(4).Info(err) + resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err)) + return + } + + err = openpitrix.DeleteApplication(clusterId) + + if status.Code(err) == codes.NotFound { + resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + return + } + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(errors.None) +} diff --git a/pkg/apiserver/openpitrix/apps.go b/pkg/apiserver/openpitrix/apps.go new file mode 100644 index 000000000..121761e5d --- /dev/null +++ b/pkg/apiserver/openpitrix/apps.go @@ -0,0 +1,544 @@ +/* + * + * Copyright 2019 The KubeSphere Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package openpitrix + +import ( + "github.com/emicklei/go-restful" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/constants" + "kubesphere.io/kubesphere/pkg/models/openpitrix" + "kubesphere.io/kubesphere/pkg/server/errors" + "kubesphere.io/kubesphere/pkg/server/params" + "kubesphere.io/kubesphere/pkg/simple/client" + "net/http" + "strconv" + "strings" +) + +func GetAppVersionPackage(req *restful.Request, resp *restful.Response) { + appId := req.PathParameter("app") + versionId := req.PathParameter("version") + + result, err := openpitrix.GetAppVersionPackage(appId, versionId) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(result) +} + +func DoAppAction(req *restful.Request, resp *restful.Response) { + var doActionRequest openpitrix.ActionRequest + err := req.ReadEntity(&doActionRequest) + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + appId := req.PathParameter("app") + + err = openpitrix.DoAppAction(appId, &doActionRequest) + if status.Code(err) == codes.NotFound { + resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + return + } + + if status.Code(err) == codes.InvalidArgument { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(errors.None) +} + +func DoAppVersionAction(req *restful.Request, resp *restful.Response) { + var doActionRequest openpitrix.ActionRequest + err := req.ReadEntity(&doActionRequest) + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + doActionRequest.Username = req.HeaderParameter(constants.UserNameHeader) + + versionId := req.PathParameter("version") + + err = openpitrix.DoAppVersionAction(versionId, &doActionRequest) + if status.Code(err) == codes.NotFound { + resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + return + } + + if status.Code(err) == codes.InvalidArgument { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(errors.None) +} + +func GetAppVersionFiles(req *restful.Request, resp *restful.Response) { + versionId := req.PathParameter("version") + getAppVersionFilesRequest := &openpitrix.GetAppVersionFilesRequest{} + if f := req.QueryParameter("files"); f != "" { + getAppVersionFilesRequest.Files = strings.Split(f, ",") + } + + result, err := openpitrix.GetAppVersionFiles(versionId, getAppVersionFilesRequest) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if status.Code(err) == codes.NotFound { + resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + return + } + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(result) +} + +func ListAppVersionAudits(req *restful.Request, resp *restful.Response) { + conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) + orderBy := req.QueryParameter(params.OrderByParam) + limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) + reverse := params.ParseReverse(req) + appId := req.PathParameter("app") + versionId := req.PathParameter("version") + if orderBy == "" { + orderBy = "status_time" + reverse = true + } + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + conditions.Match["app"] = appId + if versionId != "" { + conditions.Match["version"] = versionId + } + + result, err := openpitrix.ListAppVersionAudits(conditions, orderBy, reverse, limit, offset) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(result) +} + +func ListReviews(req *restful.Request, resp *restful.Response) { + conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) + orderBy := req.QueryParameter(params.OrderByParam) + limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) + reverse := params.ParseReverse(req) + if orderBy == "" { + orderBy = "status_time" + reverse = true + } + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + result, err := openpitrix.ListAppVersionReviews(conditions, orderBy, reverse, limit, offset) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(result) +} + +func ListAppVersions(req *restful.Request, resp *restful.Response) { + conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) + orderBy := req.QueryParameter(params.OrderByParam) + limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) + reverse := params.ParseReverse(req) + appId := req.PathParameter("app") + statistics, _ := strconv.ParseBool(req.QueryParameter("statistics")) + if orderBy == "" { + orderBy = "create_time" + reverse = true + } + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + conditions.Match["app"] = appId + + result, err := openpitrix.ListAppVersions(conditions, orderBy, reverse, limit, offset) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + if statistics { + for _, item := range result.Items { + if version, ok := item.(*openpitrix.AppVersion); ok { + statisticsResult, err := openpitrix.ListApplications(¶ms.Conditions{Match: map[string]string{"app_id": version.AppId, "version_id": version.VersionId}}, 0, 0) + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + version.ClusterTotal = &statisticsResult.TotalCount + } + } + } + + resp.WriteEntity(result) +} + +func ListApps(req *restful.Request, resp *restful.Response) { + conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) + orderBy := req.QueryParameter(params.OrderByParam) + limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) + reverse := params.ParseReverse(req) + statistics, _ := strconv.ParseBool(req.QueryParameter("statistics")) + if orderBy == "" { + orderBy = "create_time" + reverse = true + } + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + result, err := openpitrix.ListApps(conditions, orderBy, reverse, limit, offset) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + if statistics { + for _, item := range result.Items { + if app, ok := item.(*openpitrix.App); ok { + statisticsResult, err := openpitrix.ListApplications(¶ms.Conditions{Match: map[string]string{"app_id": app.AppId, "status": "active|used|enabled|stopped"}}, 0, 0) + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + app.ClusterTotal = &statisticsResult.TotalCount + } + } + } + + resp.WriteEntity(result) +} + +func ModifyApp(req *restful.Request, resp *restful.Response) { + + var patchAppRequest openpitrix.ModifyAppRequest + err := req.ReadEntity(&patchAppRequest) + appId := req.PathParameter("app") + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + err = openpitrix.PatchApp(appId, &patchAppRequest) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if status.Code(err) == codes.NotFound { + resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + return + } + + if status.Code(err) == codes.InvalidArgument { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(errors.None) +} + +func DescribeApp(req *restful.Request, resp *restful.Response) { + appId := req.PathParameter("app") + + result, err := openpitrix.DescribeApp(appId) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if status.Code(err) == codes.NotFound { + resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + return + } + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(result) +} + +func DeleteApp(req *restful.Request, resp *restful.Response) { + appId := req.PathParameter("app") + + err := openpitrix.DeleteApp(appId) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if status.Code(err) == codes.NotFound { + resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + return + } + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(errors.None) +} + +func CreateApp(req *restful.Request, resp *restful.Response) { + createAppRequest := &openpitrix.CreateAppRequest{} + err := req.ReadEntity(createAppRequest) + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + validate, _ := strconv.ParseBool(req.QueryParameter("validate")) + + var result interface{} + + if validate { + validatePackageRequest := &openpitrix.ValidatePackageRequest{ + VersionPackage: createAppRequest.VersionPackage, + VersionType: createAppRequest.VersionType, + } + result, err = openpitrix.ValidatePackage(validatePackageRequest) + } else { + result, err = openpitrix.CreateApp(createAppRequest) + } + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if status.Code(err) == codes.InvalidArgument { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(result) +} + +func CreateAppVersion(req *restful.Request, resp *restful.Response) { + var createAppVersionRequest openpitrix.CreateAppVersionRequest + err := req.ReadEntity(&createAppVersionRequest) + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + // override app id + appId := req.PathParameter("app") + createAppVersionRequest.AppId = appId + + validate, _ := strconv.ParseBool(req.QueryParameter("validate")) + + var result interface{} + + if validate { + validatePackageRequest := &openpitrix.ValidatePackageRequest{ + VersionPackage: createAppVersionRequest.Package, + VersionType: createAppVersionRequest.Type, + } + result, err = openpitrix.ValidatePackage(validatePackageRequest) + } else { + result, err = openpitrix.CreateAppVersion(&createAppVersionRequest) + } + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if status.Code(err) == codes.InvalidArgument { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(result) +} + +func ModifyAppVersion(req *restful.Request, resp *restful.Response) { + + var patchAppVersionRequest openpitrix.ModifyAppVersionRequest + err := req.ReadEntity(&patchAppVersionRequest) + versionId := req.PathParameter("version") + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + err = openpitrix.PatchAppVersion(versionId, &patchAppVersionRequest) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if status.Code(err) == codes.InvalidArgument { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(errors.None) +} + +func DeleteAppVersion(req *restful.Request, resp *restful.Response) { + versionId := req.PathParameter("version") + + err := openpitrix.DeleteAppVersion(versionId) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if status.Code(err) == codes.NotFound { + resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + return + } + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(errors.None) +} + +func DescribeAppVersion(req *restful.Request, resp *restful.Response) { + versionId := req.PathParameter("version") + + result, err := openpitrix.DescribeAppVersion(versionId) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if status.Code(err) == codes.NotFound { + resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + return + } + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(result) +} diff --git a/pkg/apiserver/openpitrix/attachments.go b/pkg/apiserver/openpitrix/attachments.go new file mode 100644 index 000000000..87093119b --- /dev/null +++ b/pkg/apiserver/openpitrix/attachments.go @@ -0,0 +1,54 @@ +/* + * + * Copyright 2019 The KubeSphere Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package openpitrix + +import ( + "github.com/emicklei/go-restful" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "kubesphere.io/kubesphere/pkg/models/openpitrix" + "kubesphere.io/kubesphere/pkg/server/errors" + "kubesphere.io/kubesphere/pkg/simple/client" + "net/http" +) + +func DescribeAttachment(req *restful.Request, resp *restful.Response) { + attachmentId := req.PathParameter("attachment") + fileName := req.QueryParameter("filename") + result, err := openpitrix.DescribeAttachment(attachmentId) + // file raw + if fileName != "" { + data := result.AttachmentContent[fileName] + resp.Write(data) + resp.Header().Set("Content-Type", "text/plain") + return + } + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if status.Code(err) == codes.NotFound { + resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + return + } + + resp.WriteEntity(result) +} diff --git a/pkg/apiserver/openpitrix/categories.go b/pkg/apiserver/openpitrix/categories.go new file mode 100644 index 000000000..4bd89ae83 --- /dev/null +++ b/pkg/apiserver/openpitrix/categories.go @@ -0,0 +1,171 @@ +/* + * + * Copyright 2019 The KubeSphere Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package openpitrix + +import ( + "github.com/emicklei/go-restful" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/models/openpitrix" + "kubesphere.io/kubesphere/pkg/server/errors" + "kubesphere.io/kubesphere/pkg/server/params" + "kubesphere.io/kubesphere/pkg/simple/client" + "net/http" + "strconv" +) + +func CreateCategory(req *restful.Request, resp *restful.Response) { + createCategoryRequest := &openpitrix.CreateCategoryRequest{} + err := req.ReadEntity(createCategoryRequest) + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + result, err := openpitrix.CreateCategory(createCategoryRequest) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + if status.Code(err) == codes.InvalidArgument { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(result) +} +func DeleteCategory(req *restful.Request, resp *restful.Response) { + categoryId := req.PathParameter("category") + + err := openpitrix.DeleteCategory(categoryId) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + if status.Code(err) == codes.NotFound { + resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + return + } + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(errors.None) +} +func ModifyCategory(req *restful.Request, resp *restful.Response) { + var modifyCategoryRequest openpitrix.ModifyCategoryRequest + categoryId := req.PathParameter("category") + err := req.ReadEntity(&modifyCategoryRequest) + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + err = openpitrix.PatchCategory(categoryId, &modifyCategoryRequest) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + if status.Code(err) == codes.InvalidArgument { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(errors.None) +} +func DescribeCategory(req *restful.Request, resp *restful.Response) { + categoryId := req.PathParameter("category") + + result, err := openpitrix.DescribeCategory(categoryId) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if status.Code(err) == codes.NotFound { + resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + return + } + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(result) +} +func ListCategories(req *restful.Request, resp *restful.Response) { + conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) + orderBy := req.QueryParameter(params.OrderByParam) + limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) + reverse := params.ParseReverse(req) + if orderBy == "" { + orderBy = "create_time" + reverse = true + } + statistics, _ := strconv.ParseBool(req.QueryParameter("statistics")) + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + result, err := openpitrix.ListCategories(conditions, orderBy, reverse, limit, offset) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + if statistics { + for _, item := range result.Items { + if category, ok := item.(*openpitrix.Category); ok { + statisticsResult, err := openpitrix.ListApps(¶ms.Conditions{Match: map[string]string{"category_id": category.CategoryID, "status": "active"}}, "", false, 0, 0) + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + category.AppTotal = &statisticsResult.TotalCount + } + } + } + + resp.WriteEntity(result) +} diff --git a/pkg/apiserver/openpitrix/repos.go b/pkg/apiserver/openpitrix/repos.go new file mode 100644 index 000000000..38c1704d8 --- /dev/null +++ b/pkg/apiserver/openpitrix/repos.go @@ -0,0 +1,222 @@ +/* + * + * Copyright 2019 The KubeSphere Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package openpitrix + +import ( + "github.com/emicklei/go-restful" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/models/openpitrix" + "kubesphere.io/kubesphere/pkg/server/errors" + "kubesphere.io/kubesphere/pkg/server/params" + "kubesphere.io/kubesphere/pkg/simple/client" + "net/http" + "strconv" +) + +func CreateRepo(req *restful.Request, resp *restful.Response) { + createRepoRequest := &openpitrix.CreateRepoRequest{} + err := req.ReadEntity(createRepoRequest) + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + validate, _ := strconv.ParseBool(req.QueryParameter("validate")) + + var result interface{} + + if validate { + validateRepoRequest := &openpitrix.ValidateRepoRequest{ + Type: createRepoRequest.Type, + Url: createRepoRequest.URL, + Credential: createRepoRequest.Credential, + } + result, err = openpitrix.ValidateRepo(validateRepoRequest) + } else { + result, err = openpitrix.CreateRepo(createRepoRequest) + } + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + if status.Code(err) == codes.InvalidArgument { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(result) +} + +func DoRepoAction(req *restful.Request, resp *restful.Response) { + repoActionRequest := &openpitrix.RepoActionRequest{} + repoId := req.PathParameter("repo") + err := req.ReadEntity(repoActionRequest) + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + err = openpitrix.DoRepoAction(repoId, repoActionRequest) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + if status.Code(err) == codes.InvalidArgument { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(errors.None) +} + +func DeleteRepo(req *restful.Request, resp *restful.Response) { + repoId := req.PathParameter("repo") + + err := openpitrix.DeleteRepo(repoId) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + if status.Code(err) == codes.NotFound { + resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + return + } + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(errors.None) +} + +func ModifyRepo(req *restful.Request, resp *restful.Response) { + var updateRepoRequest openpitrix.ModifyRepoRequest + repoId := req.PathParameter("repo") + err := req.ReadEntity(&updateRepoRequest) + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + err = openpitrix.PatchRepo(repoId, &updateRepoRequest) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + if status.Code(err) == codes.InvalidArgument { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(errors.None) +} + +func DescribeRepo(req *restful.Request, resp *restful.Response) { + repoId := req.PathParameter("repo") + + result, err := openpitrix.DescribeRepo(repoId) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + if status.Code(err) == codes.NotFound { + resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + return + } + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(result) +} +func ListRepos(req *restful.Request, resp *restful.Response) { + conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) + orderBy := req.QueryParameter(params.OrderByParam) + limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) + reverse := params.ParseReverse(req) + if orderBy == "" { + orderBy = "create_time" + reverse = true + } + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + result, err := openpitrix.ListRepos(conditions, orderBy, reverse, limit, offset) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(result) +} + +func ListRepoEvents(req *restful.Request, resp *restful.Response) { + repoId := req.PathParameter("repo") + conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) + limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + result, err := openpitrix.ListRepoEvents(repoId, conditions, limit, offset) + + if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { + resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) + return + } + + if err != nil { + klog.Errorln(err) + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteEntity(result) +} diff --git a/pkg/apiserver/resources/application.go b/pkg/apiserver/resources/application.go deleted file mode 100644 index 437ed8c7e..000000000 --- a/pkg/apiserver/resources/application.go +++ /dev/null @@ -1,206 +0,0 @@ -/* - - Copyright 2019 The KubeSphere Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -package resources - -import ( - "fmt" - "github.com/emicklei/go-restful" - "k8s.io/api/core/v1" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/applications" - "kubesphere.io/kubesphere/pkg/models/resources" - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" - "net/http" -) - -func ListApplication(req *restful.Request, resp *restful.Response) { - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - clusterId := req.QueryParameter("cluster_id") - runtimeId := req.QueryParameter("runtime_id") - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - if err != nil { - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - } - if len(clusterId) > 0 { - app, err := applications.GetApp(clusterId) - if err != nil { - klog.Errorln("get application error", err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - resp.WriteEntity(app) - return - } - - result, err := applications.ListApplication(runtimeId, conditions, limit, offset) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(result) - -} - -func ListNamespacedApplication(req *restful.Request, resp *restful.Response) { - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - namespaceName := req.PathParameter("namespace") - clusterId := req.QueryParameter("cluster_id") - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - if len(clusterId) > 0 { - app, err := applications.GetApp(clusterId) - if err != nil { - klog.Errorln("get app failed", err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - resp.WriteEntity(app) - return - } - - namespace, err := resources.GetResource("", resources.Namespaces, namespaceName) - - if err != nil { - klog.Errorln("get namespace failed", err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - var runtimeId string - - if ns, ok := namespace.(*v1.Namespace); ok { - runtimeId = ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] - } - - if runtimeId == "" { - klog.Errorln("runtime id not found") - resp.WriteAsJson(models.PageableResponse{Items: []interface{}{}, TotalCount: 0}) - return - } - - result, err := applications.ListApplication(runtimeId, conditions, limit, offset) - - if err != nil { - klog.Errorln("list applications failed", err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(result) -} - -func DescribeApplication(req *restful.Request, resp *restful.Response) { - clusterId := req.PathParameter("application") - namespaceName := req.PathParameter("namespace") - app, err := applications.GetApp(clusterId) - if err != nil { - klog.Errorln("get app failed", err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - namespace, err := resources.GetResource("", resources.Namespaces, namespaceName) - - if err != nil { - klog.Errorln("get namespace failed", err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - var runtimeId string - - if ns, ok := namespace.(*v1.Namespace); ok { - runtimeId = ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] - } - - if runtimeId != app.RuntimeId { - klog.Errorln("runtime not match", app.RuntimeId, runtimeId) - resp.WriteHeaderAndEntity(http.StatusForbidden, errors.New(fmt.Sprintf("rumtime not match %s,%s", app.RuntimeId, runtimeId))) - return - } - - resp.WriteEntity(app) - return -} - -func DeployApplication(req *restful.Request, resp *restful.Response) { - namespace := req.PathParameter("namespace") - var app openpitrix.CreateClusterRequest - err := req.ReadEntity(&app) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - err = applications.DeployApplication(namespace, app) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - resp.WriteEntity(errors.None) -} - -func DeleteApplication(req *restful.Request, resp *restful.Response) { - clusterId := req.PathParameter("application") - namespaceName := req.PathParameter("namespace") - app, err := applications.GetApp(clusterId) - if err != nil { - klog.Errorln("get app failed", err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - namespace, err := resources.GetResource("", resources.Namespaces, namespaceName) - - if err != nil { - klog.Errorln("get namespace failed", err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - var runtimeId string - - if ns, ok := namespace.(*v1.Namespace); ok { - runtimeId = ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] - } - - if runtimeId != app.RuntimeId { - klog.Errorln("runtime not match", app.RuntimeId, runtimeId) - resp.WriteHeaderAndEntity(http.StatusForbidden, errors.New(fmt.Sprintf("rumtime not match %s,%s", app.RuntimeId, runtimeId))) - return - } - - err = applications.DeleteApplication(clusterId) - - if err != nil { - klog.Errorln("delete application failed", err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} diff --git a/pkg/apiserver/runtime/runtime.go b/pkg/apiserver/runtime/runtime.go index 5be926b6e..376d2e29a 100644 --- a/pkg/apiserver/runtime/runtime.go +++ b/pkg/apiserver/runtime/runtime.go @@ -31,10 +31,16 @@ var Container = restful.NewContainer() type ContainerBuilder []func(c *restful.Container) error -// +const MimeMergePatchJson = "application/merge-patch+json" +const MimeJsonPatchJson = "application/json-patch+json" + +func init() { + restful.RegisterEntityAccessor(MimeMergePatchJson, restful.NewEntityAccessorJSON(restful.MIME_JSON)) + restful.RegisterEntityAccessor(MimeJsonPatchJson, restful.NewEntityAccessorJSON(restful.MIME_JSON)) +} + func NewWebService(gv schema.GroupVersion) *restful.WebService { webservice := restful.WebService{} - webservice.Path(ApiRootPath + "/" + gv.String()). Consumes(restful.MIME_JSON). Produces(restful.MIME_JSON) diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 8cf790f79..a74b40caf 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -56,6 +56,7 @@ const ( NamespaceResourcesTag = "Namespace Resources" ClusterResourcesTag = "Cluster Resources" ComponentStatusTag = "Component Status" + OpenpitrixTag = "Openpitrix Resources" VerificationTag = "Verification" RegistryTag = "Docker Registry" UserResourcesTag = "User Resources" diff --git a/pkg/controller/namespace/namespace_controller.go b/pkg/controller/namespace/namespace_controller.go index 27271f13d..2dc1a6eaf 100644 --- a/pkg/controller/namespace/namespace_controller.go +++ b/pkg/controller/namespace/namespace_controller.go @@ -21,6 +21,7 @@ package namespace import ( "context" "fmt" + "github.com/golang/protobuf/ptypes/wrappers" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbac "k8s.io/api/rbac/v1" @@ -35,7 +36,7 @@ import ( "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" "kubesphere.io/kubesphere/pkg/utils/k8sutil" "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "math" + "openpitrix.io/openpitrix/pkg/pb" "reflect" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" @@ -44,7 +45,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" - "time" ) const ( @@ -137,12 +137,14 @@ func (r *ReconcileNamespace) Reconcile(request reconcile.Request) (reconcile.Res } else { // The object is being deleted if sliceutil.HasString(instance.ObjectMeta.Finalizers, finalizer) { - if err := r.deleteRouter(instance.Name); err != nil { + if err = r.deleteRouter(instance.Name); err != nil { return reconcile.Result{}, err } - // delete runtime in the background, retry 3 times - go r.deleteRuntime(instance) + // delete runtime + if err = r.deleteRuntime(instance); err != nil { + return reconcile.Result{}, err + } // remove our finalizer from the list and update it. instance.ObjectMeta.Finalizers = sliceutil.RemoveString(instance.ObjectMeta.Finalizers, func(item string) bool { @@ -223,8 +225,7 @@ func (r *ReconcileNamespace) checkAndCreateRoles(namespace *corev1.Namespace) er } if !reflect.DeepEqual(found.Rules, role.Rules) { found.Rules = role.Rules - err := r.Update(context.TODO(), found) - if err != nil { + if err := r.Update(context.TODO(), found); err != nil { klog.Errorf("updating default role namespace: %s, role: %s,error: %s", namespace.Name, role.Name, err) return err } @@ -351,30 +352,73 @@ func (r *ReconcileNamespace) checkAndCreateRoleBindings(namespace *corev1.Namesp // Create openpitrix runtime func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace) error { - openPitrixClient, err := cs.ClientSets().OpenPitrix() - if err != nil { - return err - } if runtimeId := namespace.Annotations[constants.OpenPitrixRuntimeAnnotationKey]; runtimeId != "" { return nil } - cm := &corev1.ConfigMap{} - configName := fmt.Sprintf("kubeconfig-%s", constants.AdminUserName) - err = r.Get(context.TODO(), types.NamespacedName{Namespace: constants.KubeSphereControlNamespace, Name: configName}, cm) + openPitrixClient, err := cs.ClientSets().OpenPitrix() + + if _, notEnabled := err.(cs.ClientSetNotEnabledError); notEnabled { + return nil + } else if err != nil { + klog.Error(fmt.Sprintf("create runtime, namespace: %s, error: %s", namespace.Name, err)) + return err + } + + adminKubeConfigName := fmt.Sprintf("kubeconfig-%s", constants.AdminUserName) + + runtimeCredentials, err := openPitrixClient.Runtime().DescribeRuntimeCredentials(openpitrix.SystemContext(), &pb.DescribeRuntimeCredentialsRequest{SearchWord: &wrappers.StringValue{Value: adminKubeConfigName}, Limit: 1}) if err != nil { + klog.Error(fmt.Sprintf("create runtime, namespace: %s, error: %s", namespace.Name, err)) return err } - runtime := &openpitrix.RunTime{Name: namespace.Name, Zone: namespace.Name, Provider: "kubernetes", RuntimeCredential: cm.Data["config"]} + var kubesphereRuntimeCredentialId string - if err := openPitrixClient.CreateRuntime(runtime); err != nil { - klog.Errorf("creating openpitrix runtime namespace: %s, error: %s", namespace.Name, err) + // runtime credential exist + if len(runtimeCredentials.GetRuntimeCredentialSet()) > 0 { + kubesphereRuntimeCredentialId = runtimeCredentials.GetRuntimeCredentialSet()[0].GetRuntimeCredentialId().GetValue() + } else { + adminKubeConfig := corev1.ConfigMap{} + err := r.Get(context.TODO(), types.NamespacedName{Namespace: constants.KubeSphereControlNamespace, Name: adminKubeConfigName}, &adminKubeConfig) + + if err != nil { + klog.Error(fmt.Sprintf("create runtime, namespace: %s, error: %s", namespace.Name, err)) + return err + } + + resp, err := openPitrixClient.Runtime().CreateRuntimeCredential(openpitrix.SystemContext(), &pb.CreateRuntimeCredentialRequest{ + Name: &wrappers.StringValue{Value: adminKubeConfigName}, + Provider: &wrappers.StringValue{Value: "kubernetes"}, + Description: &wrappers.StringValue{Value: "kubeconfig"}, + RuntimeUrl: &wrappers.StringValue{Value: "kubesphere"}, + RuntimeCredentialContent: &wrappers.StringValue{Value: adminKubeConfig.Data["config"]}, + }) + + if err != nil { + klog.Error(fmt.Sprintf("create runtime, namespace: %s, error: %s", namespace.Name, err)) + return err + } + + kubesphereRuntimeCredentialId = resp.GetRuntimeCredentialId().GetValue() + } + + runtimeId, err := openPitrixClient.Runtime().CreateRuntime(openpitrix.SystemContext(), &pb.CreateRuntimeRequest{ + Name: &wrappers.StringValue{Value: namespace.Name}, + RuntimeCredentialId: &wrappers.StringValue{Value: kubesphereRuntimeCredentialId}, + Provider: &wrappers.StringValue{Value: openpitrix.KubernetesProvider}, + Zone: &wrappers.StringValue{Value: namespace.Name}, + }) + + if err != nil { + klog.Error(fmt.Sprintf("create runtime, namespace: %s, error: %s", namespace.Name, err)) return err } + klog.V(4).Infof("runtime created successfully, namespace: %s, runtime id: %s", namespace.Name, runtimeId) + return nil } @@ -382,22 +426,23 @@ func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace) func (r *ReconcileNamespace) deleteRuntime(namespace *corev1.Namespace) error { if runtimeId := namespace.Annotations[constants.OpenPitrixRuntimeAnnotationKey]; runtimeId != "" { - maxRetries := float64(3) - for i := float64(0); i < maxRetries; i++ { - time.Sleep(time.Duration(i*math.Pow(2, i)) * time.Second) - openPitrixClient, err := cs.ClientSets().OpenPitrix() - if err != nil { - return err - } + openPitrixClient, err := cs.ClientSets().OpenPitrix() - err = openPitrixClient.DeleteRuntime(runtimeId) + if _, notEnabled := err.(cs.ClientSetNotEnabledError); notEnabled { + return nil + } else if err != nil { + klog.Errorf("delete openpitrix runtime: %s, error: %s", runtimeId, err) + return err + } - if err == nil || openpitrix.IsNotFound(err) || openpitrix.IsDeleted(err) { - return nil - } + _, err = openPitrixClient.Runtime().DeleteRuntimes(openpitrix.SystemContext(), &pb.DeleteRuntimesRequest{RuntimeId: []string{runtimeId}, Force: &wrappers.BoolValue{Value: true}}) - klog.Errorf("delete openpitrix runtime: %v times left, error: %s", maxRetries-i-1, err) + if err == nil || openpitrix.IsNotFound(err) || openpitrix.IsDeleted(err) { + return nil + } else { + klog.Errorf("delete openpitrix runtime: %s, error: %s", runtimeId, err) + return err } } @@ -451,12 +496,12 @@ func (r *ReconcileNamespace) deleteRouter(namespace string) error { if errors.IsNotFound(err) { return nil } - klog.V(6).Info("get router service failed", err) + klog.Error(err) } err = r.Delete(context.TODO(), &found) if err != nil { - klog.Error(err, "delete router failed") + klog.Error(err) return err } @@ -467,13 +512,13 @@ func (r *ReconcileNamespace) deleteRouter(namespace string) error { if errors.IsNotFound(err) { return nil } - klog.V(6).Info("get router deployment failed", err) + klog.Error(err) return err } err = r.Delete(context.TODO(), &deploy) if err != nil { - klog.Error(err, "delete router deployment failed") + klog.Error(err) return err } @@ -482,7 +527,7 @@ func (r *ReconcileNamespace) deleteRouter(namespace string) error { } func (r *ReconcileNamespace) deleteRoleBindings(namespace *corev1.Namespace) error { - klog.V(6).Info("deleting role bindings namespace: ", namespace.Name) + klog.V(4).Info("deleting role bindings namespace: ", namespace.Name) adminBinding := &rbac.RoleBinding{} adminBinding.Name = admin.Name adminBinding.Namespace = namespace.Name diff --git a/pkg/models/applications/applications.go b/pkg/models/openpitrix/applications.go similarity index 50% rename from pkg/models/applications/applications.go rename to pkg/models/openpitrix/applications.go index a5e2f50fd..ebcf71382 100644 --- a/pkg/models/applications/applications.go +++ b/pkg/models/openpitrix/applications.go @@ -15,10 +15,13 @@ limitations under the License. */ -package applications +package openpitrix import ( "fmt" + "github.com/golang/protobuf/ptypes/wrappers" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" appsv1 "k8s.io/api/apps/v1" "k8s.io/api/core/v1" "k8s.io/api/extensions/v1beta1" @@ -31,29 +34,20 @@ import ( "kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/models/resources" "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/simple/client" + cs "kubesphere.io/kubesphere/pkg/simple/client" "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" + "openpitrix.io/openpitrix/pkg/pb" "strings" - "time" ) type Application struct { - Name string `json:"name" description:"application name"` - RepoName string `json:"repoName" description:"repo name"` - Runtime string `json:"namespace" description:"namespace name"` - RuntimeId string `json:"runtime_id" description:"It is the application runtime in OpenPitrix and represents k8s namespace by annotating the namespace"` - Version string `json:"version" description:"application version"` - VersionId string `json:"version_id" description:"application version id"` - Status string `json:"status" description:"application status"` - UpdateTime time.Time `json:"updateTime" description:"the last time this application was updated"` - CreateTime time.Time `json:"createTime" description:"application creation time"` - App string `json:"app" description:"application template name"` - AppId string `json:"app_id" description:"application template id"` - Description string `json:"description,omitempty" description:"application description"` - WorkLoads *workLoads `json:"workloads,omitempty" description:"application workloads"` - Services []v1.Service `json:"services,omitempty" description:"application services"` - Ingresses []v1beta1.Ingress `json:"ingresses,omitempty" description:"application ingresses"` - ClusterID string `json:"cluster_id" description:"application id"` + Name string `json:"name" description:"application name"` + Cluster *Cluster `json:"cluster,omitempty" description:"application cluster info"` + Version *AppVersion `json:"version,omitempty" description:"application template version info"` + App *App `json:"app,omitempty" description:"application template info"` + WorkLoads *workLoads `json:"workloads,omitempty" description:"application workloads"` + Services []v1.Service `json:"services,omitempty" description:"application services"` + Ingresses []v1beta1.Ingress `json:"ingresses,omitempty" description:"application ingresses"` } type workLoads struct { @@ -62,92 +56,124 @@ type workLoads struct { Daemonsets []appsv1.DaemonSet `json:"daemonsets,omitempty" description:"daemonset list"` } -func ListApplication(runtimeId string, conditions *params.Conditions, limit, offset int) (*models.PageableResponse, error) { - openPitrixClient, err := client.ClientSets().OpenPitrix() +func ListApplications(conditions *params.Conditions, limit, offset int) (*models.PageableResponse, error) { + client, err := cs.ClientSets().OpenPitrix() if err != nil { + klog.Error(err) return nil, err } - clusterList, err := openPitrixClient.ListClusters(runtimeId, conditions.Match["keyword"], conditions.Match["status"], limit, offset) + describeClustersRequest := &pb.DescribeClustersRequest{ + Limit: uint32(limit), + Offset: uint32(offset)} + if keyword := conditions.Match["keyword"]; keyword != "" { + describeClustersRequest.SearchWord = &wrappers.StringValue{Value: keyword} + } + if runtimeId := conditions.Match["runtime_id"]; runtimeId != "" { + describeClustersRequest.RuntimeId = []string{runtimeId} + } + if appId := conditions.Match["app_id"]; appId != "" { + describeClustersRequest.AppId = []string{appId} + } + if versionId := conditions.Match["version_id"]; versionId != "" { + describeClustersRequest.VersionId = []string{versionId} + } + if status := conditions.Match["status"]; status != "" { + describeClustersRequest.Status = strings.Split(status, "|") + } + resp, err := client.Cluster().DescribeClusters(openpitrix.SystemContext(), describeClustersRequest) if err != nil { + klog.Errorln(err) return nil, err } - result := models.PageableResponse{TotalCount: clusterList.Total} + result := models.PageableResponse{TotalCount: int(resp.TotalCount)} result.Items = make([]interface{}, 0) - for _, item := range clusterList.Clusters { - var app Application - - app.Name = item.Name - app.ClusterID = item.ClusterID - app.UpdateTime = item.UpdateTime - app.Status = item.Status - versionInfo, _ := openPitrixClient.GetVersion(item.VersionID) - app.Version = versionInfo - app.VersionId = item.VersionID - runtimeInfo, _ := openPitrixClient.GetRuntime(item.RunTimeId) - app.Runtime = runtimeInfo - app.RuntimeId = item.RunTimeId - appInfo, _, appId, _ := openPitrixClient.GetAppInfo(item.AppID) - app.App = appInfo - app.AppId = appId - app.Description = item.Description - + for _, cluster := range resp.ClusterSet { + app, err := describeApplication(cluster) + if err != nil { + klog.Errorln(err) + return nil, err + } result.Items = append(result.Items, app) } return &result, nil } -func GetApp(clusterId string) (*Application, error) { - openPitrixClient, err := client.ClientSets().OpenPitrix() - if err != nil { - return nil, err - } - item, err := openPitrixClient.GetCluster(clusterId) - +func describeApplication(cluster *pb.Cluster) (*Application, error) { + op, err := cs.ClientSets().OpenPitrix() if err != nil { klog.Error(err) return nil, err } - var app Application - - app.Name = item.Name - app.ClusterID = item.ClusterID - app.UpdateTime = item.UpdateTime - app.CreateTime = item.CreateTime - app.Status = item.Status - versionInfo, _ := openPitrixClient.GetVersion(item.VersionID) - app.Version = versionInfo - app.VersionId = item.VersionID - - runtimeInfo, _ := openPitrixClient.GetRuntime(item.RunTimeId) - app.Runtime = runtimeInfo - app.RuntimeId = item.RunTimeId - appInfo, repoId, appId, _ := openPitrixClient.GetAppInfo(item.AppID) - app.App = appInfo - app.AppId = appId - app.Description = item.Description - - app.RepoName, _ = openPitrixClient.GetRepo(repoId) - - workloads, err := getWorkLoads(app.Runtime, item.ClusterRoleSets) + app.Name = cluster.Name.Value + app.Cluster = convertCluster(cluster) + versionInfo, err := op.App().DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{VersionId: []string{cluster.GetVersionId().GetValue()}}) if err != nil { - klog.Error(err) + klog.Errorln(err) return nil, err } - app.WorkLoads = workloads - workloadLabels := getLabels(app.Runtime, app.WorkLoads) - app.Services = getSvcs(app.Runtime, workloadLabels) - app.Ingresses = getIng(app.Runtime, app.Services) - + if len(versionInfo.AppVersionSet) > 0 { + app.Version = convertAppVersion(versionInfo.AppVersionSet[0]) + } + appInfo, err := op.App().DescribeApps(openpitrix.SystemContext(), &pb.DescribeAppsRequest{AppId: []string{cluster.GetAppId().GetValue()}, Limit: 1}) + if err != nil { + klog.Errorln(err) + return nil, err + } + if len(appInfo.AppSet) > 0 { + app.App = convertApp(appInfo.GetAppSet()[0]) + } return &app, nil } -func getWorkLoads(namespace string, clusterRoles []openpitrix.ClusterRole) (*workLoads, error) { +func DescribeApplication(namespace string, clusterId string) (*Application, error) { + + client, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return nil, err + } + + clusters, err := client.Cluster().DescribeClusters(openpitrix.SystemContext(), &pb.DescribeClustersRequest{ClusterId: []string{clusterId}, Limit: 1}) + + if err != nil { + klog.Errorln(err) + return nil, err + } + + var cluster *pb.Cluster + if len(clusters.ClusterSet) > 0 { + cluster = clusters.GetClusterSet()[0] + } else { + err := status.New(codes.NotFound, "resource not found").Err() + klog.Errorln(err) + return nil, err + } + app, err := describeApplication(cluster) + if err != nil { + klog.Errorln(err) + return nil, err + } + + workloads, err := getWorkLoads(namespace, cluster.ClusterRoleSet) + + if err != nil { + klog.Errorln(err) + return nil, err + } + app.WorkLoads = workloads + workloadLabels := getLabels(namespace, app.WorkLoads) + app.Services = getSvcs(namespace, workloadLabels) + app.Ingresses = getIng(namespace, app.Services) + return app, nil +} + +func getWorkLoads(namespace string, clusterRoles []*pb.ClusterRole) (*workLoads, error) { var works workLoads for _, clusterRole := range clusterRoles { - workLoadName := clusterRole.Role + workLoadName := clusterRole.Role.Value if len(workLoadName) > 0 { if strings.HasSuffix(workLoadName, openpitrix.DeploySuffix) { name := strings.Split(workLoadName, openpitrix.DeploySuffix)[0] @@ -159,7 +185,7 @@ func getWorkLoads(namespace string, clusterRoles []openpitrix.ClusterRole) (*wor if errors.IsNotFound(err) { continue } - klog.Error(err) + klog.Errorln(err) return nil, err } @@ -175,7 +201,7 @@ func getWorkLoads(namespace string, clusterRoles []openpitrix.ClusterRole) (*wor if errors.IsNotFound(err) { continue } - klog.Error(err) + klog.Errorln(err) return nil, err } works.Daemonsets = append(works.Daemonsets, *item) @@ -190,7 +216,7 @@ func getWorkLoads(namespace string, clusterRoles []openpitrix.ClusterRole) (*wor if errors.IsNotFound(err) { continue } - klog.Error(err) + klog.Errorln(err) return nil, err } works.Statefulsets = append(works.Statefulsets, *item) @@ -202,9 +228,9 @@ func getWorkLoads(namespace string, clusterRoles []openpitrix.ClusterRole) (*wor } func getLabels(namespace string, workloads *workLoads) *[]map[string]string { - k8sClient := client.ClientSets().K8s().Kubernetes() + k8sClient := cs.ClientSets().K8s().Kubernetes() - var workloadLables []map[string]string + var workloadLabels []map[string]string if workloads == nil { return nil } @@ -214,7 +240,7 @@ func getLabels(namespace string, workloads *workLoads) *[]map[string]string { if errors.IsNotFound(err) { continue } - workloadLables = append(workloadLables, deploy.Labels) + workloadLabels = append(workloadLabels, deploy.Labels) } for _, workload := range workloads.Daemonsets { @@ -222,7 +248,7 @@ func getLabels(namespace string, workloads *workLoads) *[]map[string]string { if errors.IsNotFound(err) { continue } - workloadLables = append(workloadLables, daemonset.Labels) + workloadLabels = append(workloadLabels, daemonset.Labels) } for _, workload := range workloads.Statefulsets { @@ -230,10 +256,10 @@ func getLabels(namespace string, workloads *workLoads) *[]map[string]string { if errors.IsNotFound(err) { continue } - workloadLables = append(workloadLables, statefulset.Labels) + workloadLabels = append(workloadLabels, statefulset.Labels) } - return &workloadLables + return &workloadLabels } func isExist(svcs []v1.Service, svc v1.Service) bool { @@ -249,7 +275,7 @@ func getSvcs(namespace string, workLoadLabels *[]map[string]string) []v1.Service if len(*workLoadLabels) == 0 { return nil } - k8sClient := client.ClientSets().K8s().Kubernetes() + k8sClient := cs.ClientSets().K8s().Kubernetes() var services []v1.Service for _, label := range *workLoadLabels { labelSelector := labels.Set(label).AsSelector().String() @@ -276,11 +302,10 @@ func getIng(namespace string, services []v1.Service) []v1beta1.Ingress { for _, svc := range services { result, err := resources.ListResources(namespace, "ingress", ¶ms.Conditions{Fuzzy: map[string]string{"serviceName": svc.Name}}, "", false, -1, 0) if err != nil { - klog.Error(err) + klog.Errorln(err) return nil } - klog.Error(result) for _, i := range result.Items { ingress := i.(*v1beta1.Ingress) @@ -308,31 +333,80 @@ func getIng(namespace string, services []v1.Service) []v1beta1.Ingress { return ings } -func DeployApplication(namespace string, app openpitrix.CreateClusterRequest) error { - openPitrixClient, err := client.ClientSets().OpenPitrix() - if err != nil { - return err - } - +func CreateApplication(namespace string, request CreateClusterRequest) error { ns, err := informers.SharedInformerFactory().Core().V1().Namespaces().Lister().Get(namespace) if err != nil { - klog.Errorf("deploy application failed: %+v", err) + klog.Error(err) return err } if runtimeId := ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey]; runtimeId != "" { - app.RuntimeId = runtimeId + request.RuntimeId = runtimeId } else { return fmt.Errorf("runtime not init: namespace %s", namespace) } - return openPitrixClient.CreateCluster(app) -} -func DeleteApplication(clusterId string) error { - openPitrixClient, err := client.ClientSets().OpenPitrix() + client, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) return err } - return openPitrixClient.DeleteCluster(openpitrix.DeleteClusterRequest{ClusterId: []string{clusterId}}) + _, err = client.Cluster().CreateCluster(openpitrix.ContextWithUsername(request.Username), &pb.CreateClusterRequest{ + AppId: &wrappers.StringValue{Value: request.AppId}, + VersionId: &wrappers.StringValue{Value: request.VersionId}, + RuntimeId: &wrappers.StringValue{Value: request.RuntimeId}, + Conf: &wrappers.StringValue{Value: request.Conf}, + }) + + if err != nil { + klog.Errorln(err) + return err + } + + return nil +} + +func PatchApplication(request *ModifyClusterAttributesRequest) error { + op, err := cs.ClientSets().OpenPitrix() + + if err != nil { + klog.Error(err) + return err + } + + modifyClusterAttributesRequest := &pb.ModifyClusterAttributesRequest{ClusterId: &wrappers.StringValue{Value: request.ClusterID}} + if request.Name != nil { + modifyClusterAttributesRequest.Name = &wrappers.StringValue{Value: *request.Name} + } + if request.Description != nil { + modifyClusterAttributesRequest.Description = &wrappers.StringValue{Value: *request.Description} + } + + _, err = op.Cluster().ModifyClusterAttributes(openpitrix.SystemContext(), modifyClusterAttributesRequest) + + if err != nil { + klog.Errorln(err) + return err + } + return nil +} + +func DeleteApplication(clusterId string) error { + client, err := cs.ClientSets().OpenPitrix() + + if err != nil { + klog.Error(err) + return err + } + + _, err = client.Cluster().DeleteClusters(openpitrix.SystemContext(), &pb.DeleteClustersRequest{ClusterId: []string{clusterId}, Force: &wrappers.BoolValue{Value: true}}) + + if err != nil { + klog.Errorln(err) + return err + } + + return nil } diff --git a/pkg/models/openpitrix/apps.go b/pkg/models/openpitrix/apps.go new file mode 100644 index 000000000..e6110350e --- /dev/null +++ b/pkg/models/openpitrix/apps.go @@ -0,0 +1,680 @@ +/* + * + * Copyright 2019 The KubeSphere Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package openpitrix + +import ( + "github.com/go-openapi/strfmt" + "github.com/golang/protobuf/ptypes/wrappers" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/models" + "kubesphere.io/kubesphere/pkg/server/params" + cs "kubesphere.io/kubesphere/pkg/simple/client" + "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" + "openpitrix.io/openpitrix/pkg/pb" + "strings" +) + +func ListApps(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { + client, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return nil, err + } + + describeAppsRequest := &pb.DescribeAppsRequest{} + if keyword := conditions.Match["keyword"]; keyword != "" { + describeAppsRequest.SearchWord = &wrappers.StringValue{Value: keyword} + } + if appId := conditions.Match["app_id"]; appId != "" { + describeAppsRequest.AppId = strings.Split(appId, "|") + } + if isv := conditions.Match["isv"]; isv != "" { + describeAppsRequest.Isv = strings.Split(isv, "|") + } + if categoryId := conditions.Match["category_id"]; categoryId != "" { + describeAppsRequest.CategoryId = strings.Split(categoryId, "|") + } + if repoId := conditions.Match["repo"]; repoId != "" { + describeAppsRequest.RepoId = strings.Split(repoId, "|") + } + if status := conditions.Match["status"]; status != "" { + describeAppsRequest.Status = strings.Split(status, "|") + } + if orderBy != "" { + describeAppsRequest.SortKey = &wrappers.StringValue{Value: orderBy} + } + describeAppsRequest.Reverse = &wrappers.BoolValue{Value: reverse} + describeAppsRequest.Limit = uint32(limit) + describeAppsRequest.Offset = uint32(offset) + resp, err := client.App().DescribeApps(openpitrix.SystemContext(), describeAppsRequest) + if err != nil { + klog.Error(err) + return nil, err + } + + items := make([]interface{}, 0) + + for _, item := range resp.AppSet { + items = append(items, convertApp(item)) + } + + return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil +} + +func DescribeApp(id string) (*App, error) { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return nil, err + } + resp, err := op.App().DescribeApps(openpitrix.SystemContext(), &pb.DescribeAppsRequest{ + AppId: []string{id}, + Limit: 1, + }) + if err != nil { + klog.Error(err) + return nil, err + } + + var app *App + + if len(resp.AppSet) > 0 { + app = convertApp(resp.AppSet[0]) + return app, nil + } else { + err := status.New(codes.NotFound, "resource not found").Err() + klog.Error(err) + return nil, err + } +} + +func DeleteApp(id string) error { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return err + } + _, err = op.App().DeleteApps(openpitrix.SystemContext(), &pb.DeleteAppsRequest{ + AppId: []string{id}, + }) + if err != nil { + klog.Error(err) + return err + } + return nil +} + +func CreateApp(request *CreateAppRequest) (*CreateAppResponse, error) { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return nil, err + } + createAppRequest := &pb.CreateAppRequest{ + Name: &wrappers.StringValue{Value: request.Name}, + VersionType: &wrappers.StringValue{Value: request.VersionType}, + VersionName: &wrappers.StringValue{Value: request.VersionName}, + } + if request.VersionPackage != nil { + createAppRequest.VersionPackage = &wrappers.BytesValue{Value: request.VersionPackage} + } + if request.Icon != nil { + createAppRequest.Icon = &wrappers.BytesValue{Value: request.Icon} + } + if request.Isv != "" { + createAppRequest.Isv = &wrappers.StringValue{Value: request.Isv} + } + resp, err := op.App().CreateApp(openpitrix.SystemContext(), createAppRequest) + if err != nil { + klog.Error(err) + return nil, err + } + return &CreateAppResponse{ + AppID: resp.GetAppId().GetValue(), + VersionID: resp.GetVersionId().GetValue(), + }, nil +} + +func PatchApp(appId string, request *ModifyAppRequest) error { + client, err := cs.ClientSets().OpenPitrix() + + if err != nil { + klog.Error(err) + return err + } + + // upload app attachment + if request.AttachmentContent != nil { + uploadAttachmentRequest := &pb.UploadAppAttachmentRequest{ + AppId: &wrappers.StringValue{Value: appId}, + AttachmentContent: &wrappers.BytesValue{Value: request.AttachmentContent}, + } + if request.Type != nil { + uploadAttachmentRequest.Type = pb.UploadAppAttachmentRequest_Type(pb.UploadAppAttachmentRequest_Type_value[*request.Type]) + } + if request.Sequence != nil { + uploadAttachmentRequest.Sequence = &wrappers.UInt32Value{Value: uint32(*request.Sequence)} + } + + _, err := client.App().UploadAppAttachment(openpitrix.SystemContext(), uploadAttachmentRequest) + + if err != nil { + klog.Error(err) + return err + } + // patch app + } else { + patchAppRequest := &pb.ModifyAppRequest{ + AppId: &wrappers.StringValue{Value: appId}, + } + + if request.Abstraction != nil { + patchAppRequest.Abstraction = &wrappers.StringValue{Value: *request.Abstraction} + } + if request.CategoryID != nil { + patchAppRequest.CategoryId = &wrappers.StringValue{Value: *request.CategoryID} + } + if request.Description != nil { + patchAppRequest.Description = &wrappers.StringValue{Value: *request.Description} + } + if request.Home != nil { + patchAppRequest.Home = &wrappers.StringValue{Value: *request.Home} + } + if request.Keywords != nil { + patchAppRequest.Keywords = &wrappers.StringValue{Value: *request.Keywords} + } + if request.Maintainers != nil { + patchAppRequest.Maintainers = &wrappers.StringValue{Value: *request.Maintainers} + } + if request.Name != nil { + patchAppRequest.Name = &wrappers.StringValue{Value: *request.Name} + } + if request.Readme != nil { + patchAppRequest.Readme = &wrappers.StringValue{Value: *request.Readme} + } + if request.Sources != nil { + patchAppRequest.Sources = &wrappers.StringValue{Value: *request.Sources} + } + if request.Tos != nil { + patchAppRequest.Tos = &wrappers.StringValue{Value: *request.Tos} + } + + _, err = client.App().ModifyApp(openpitrix.SystemContext(), patchAppRequest) + + if err != nil { + klog.Error(err) + return err + } + } + return nil +} + +func CreateAppVersion(request *CreateAppVersionRequest) (*CreateAppVersionResponse, error) { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return nil, err + } + createAppVersionRequest := &pb.CreateAppVersionRequest{ + AppId: &wrappers.StringValue{Value: request.AppId}, + Name: &wrappers.StringValue{Value: request.Name}, + Description: &wrappers.StringValue{Value: request.Description}, + Type: &wrappers.StringValue{Value: request.Type}, + } + + if request.Package != nil { + createAppVersionRequest.Package = &wrappers.BytesValue{Value: request.Package} + } + + resp, err := op.App().CreateAppVersion(openpitrix.SystemContext(), createAppVersionRequest) + if err != nil { + klog.Error(err) + return nil, err + } + return &CreateAppVersionResponse{ + VersionId: resp.GetVersionId().GetValue(), + }, nil +} + +func ValidatePackage(request *ValidatePackageRequest) (*ValidatePackageResponse, error) { + client, err := cs.ClientSets().OpenPitrix() + + if err != nil { + klog.Error(err) + return nil, err + } + + r := &pb.ValidatePackageRequest{} + + if request.VersionPackage != nil { + r.VersionPackage = request.VersionPackage + } + if request.VersionType != "" { + r.VersionType = request.VersionType + } + + resp, err := client.App().ValidatePackage(openpitrix.SystemContext(), r) + + if err != nil { + klog.Error(err) + return nil, err + } + + result := &ValidatePackageResponse{} + + if resp.Error != nil { + result.Error = resp.Error.Value + } + if resp.Description != nil { + result.Description = resp.Description.Value + } + if resp.Error != nil { + result.Error = resp.Error.Value + } + if resp.ErrorDetails != nil { + result.ErrorDetails = resp.ErrorDetails + } + if resp.Name != nil { + result.Name = resp.Name.Value + } + if resp.Url != nil { + result.URL = resp.Url.Value + } + if resp.VersionName != nil { + result.VersionName = resp.VersionName.Value + } + return result, nil +} + +func DeleteAppVersion(id string) error { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return err + } + _, err = op.App().DeleteAppVersion(openpitrix.SystemContext(), &pb.DeleteAppVersionRequest{ + VersionId: &wrappers.StringValue{Value: id}, + }) + if err != nil { + klog.Error(err) + return err + } + return nil +} + +func PatchAppVersion(id string, request *ModifyAppVersionRequest) error { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return err + } + modifyAppVersionRequest := &pb.ModifyAppVersionRequest{ + VersionId: &wrappers.StringValue{Value: id}, + } + + if request.Name != nil { + modifyAppVersionRequest.Name = &wrappers.StringValue{Value: *request.Name} + } + if request.Description != nil { + modifyAppVersionRequest.Description = &wrappers.StringValue{Value: *request.Description} + } + if request.Package != nil { + modifyAppVersionRequest.Package = &wrappers.BytesValue{Value: request.Package} + } + if request.PackageFiles != nil { + modifyAppVersionRequest.PackageFiles = request.PackageFiles + } + + _, err = op.App().ModifyAppVersion(openpitrix.SystemContext(), modifyAppVersionRequest) + if err != nil { + klog.Error(err) + return err + } + return nil +} + +func DescribeAppVersion(id string) (*AppVersion, error) { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return nil, err + } + resp, err := op.App().DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{ + VersionId: []string{id}, + Limit: 1, + }) + if err != nil { + klog.Error(err) + return nil, err + } + + var app *AppVersion + + if len(resp.AppVersionSet) > 0 { + app = convertAppVersion(resp.AppVersionSet[0]) + return app, nil + } else { + err := status.New(codes.NotFound, "resource not found").Err() + klog.Error(err) + return nil, err + } +} + +func GetAppVersionPackage(appId, versionId string) (*GetAppVersionPackageResponse, error) { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return nil, err + } + resp, err := op.App().GetAppVersionPackage(openpitrix.SystemContext(), &pb.GetAppVersionPackageRequest{ + VersionId: &wrappers.StringValue{Value: versionId}, + }) + if err != nil { + klog.Error(err) + return nil, err + } + + app := &GetAppVersionPackageResponse{ + AppId: appId, + VersionId: versionId, + } + + if resp.Package != nil { + app.Package = resp.Package + } + + return app, nil +} + +func DoAppAction(appId string, request *ActionRequest) error { + op, err := cs.ClientSets().OpenPitrix() + + if err != nil { + klog.Error(err) + return err + } + + switch request.Action { + + case "recover": + // TODO openpitrix need to implement app recover interface + resp, err := op.App().DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{ + AppId: []string{appId}, + Status: []string{"suspended"}, + Limit: 200, + Offset: 0, + }) + if err != nil { + klog.Error(err) + return err + } + for _, version := range resp.AppVersionSet { + + _, err = op.App().RecoverAppVersion(openpitrix.SystemContext(), &pb.RecoverAppVersionRequest{ + VersionId: version.VersionId, + }) + if err != nil { + klog.Error(err) + return err + } + } + + case "suspend": + // TODO openpitrix need to implement app suspend interface + resp, err := op.App().DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{ + AppId: []string{appId}, + Status: []string{"active"}, + Limit: 200, + Offset: 0, + }) + if err != nil { + klog.Error(err) + return err + } + for _, version := range resp.AppVersionSet { + _, err = op.App().SuspendAppVersion(openpitrix.SystemContext(), &pb.SuspendAppVersionRequest{ + VersionId: version.VersionId, + }) + + if err != nil { + klog.Error(err) + return err + } + } + + default: + err = status.New(codes.InvalidArgument, "action not support").Err() + klog.Error(err) + return err + } + + return nil +} + +func DoAppVersionAction(versionId string, request *ActionRequest) error { + op, err := cs.ClientSets().OpenPitrix() + + if err != nil { + klog.Error(err) + return err + } + + switch request.Action { + case "cancel": + _, err = op.App().CancelAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.CancelAppVersionRequest{ + VersionId: &wrappers.StringValue{Value: versionId}, + }) + case "pass": + _, err = op.App().AdminPassAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.PassAppVersionRequest{ + VersionId: &wrappers.StringValue{Value: versionId}, + }) + case "recover": + _, err = op.App().RecoverAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.RecoverAppVersionRequest{ + VersionId: &wrappers.StringValue{Value: versionId}, + }) + case "reject": + _, err = op.App().AdminRejectAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.RejectAppVersionRequest{ + VersionId: &wrappers.StringValue{Value: versionId}, + Message: &wrappers.StringValue{Value: request.Message}, + }) + case "submit": + _, err = op.App().SubmitAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.SubmitAppVersionRequest{ + VersionId: &wrappers.StringValue{Value: versionId}, + }) + case "suspend": + _, err = op.App().SuspendAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.SuspendAppVersionRequest{ + VersionId: &wrappers.StringValue{Value: versionId}, + }) + case "release": + _, err = op.App().ReleaseAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.ReleaseAppVersionRequest{ + VersionId: &wrappers.StringValue{Value: versionId}, + }) + default: + err = status.New(codes.InvalidArgument, "action not support").Err() + } + + if err != nil { + klog.Error(err) + return err + } + + return nil +} + +func GetAppVersionFiles(versionId string, request *GetAppVersionFilesRequest) (*GetAppVersionPackageFilesResponse, error) { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return nil, err + } + getAppVersionPackageFilesRequest := &pb.GetAppVersionPackageFilesRequest{ + VersionId: &wrappers.StringValue{Value: versionId}, + } + if request.Files != nil { + getAppVersionPackageFilesRequest.Files = request.Files + } + + resp, err := op.App().GetAppVersionPackageFiles(openpitrix.SystemContext(), getAppVersionPackageFilesRequest) + if err != nil { + klog.Error(err) + return nil, err + } + + version := &GetAppVersionPackageFilesResponse{ + VersionId: versionId, + } + + if resp.Files != nil { + version.Files = make(map[string]strfmt.Base64) + for k, v := range resp.Files { + version.Files[k] = v + } + } + + return version, nil +} + +func ListAppVersionAudits(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { + client, err := cs.ClientSets().OpenPitrix() + + if err != nil { + klog.Error(err) + return nil, err + } + + describeAppVersionAudits := &pb.DescribeAppVersionAuditsRequest{} + + if keyword := conditions.Match["keyword"]; keyword != "" { + describeAppVersionAudits.SearchWord = &wrappers.StringValue{Value: keyword} + } + if appId := conditions.Match["app"]; appId != "" { + describeAppVersionAudits.AppId = []string{appId} + } + if versionId := conditions.Match["version"]; versionId != "" { + describeAppVersionAudits.VersionId = []string{versionId} + } + if status := conditions.Match["status"]; status != "" { + describeAppVersionAudits.Status = strings.Split(status, "|") + } + if orderBy != "" { + describeAppVersionAudits.SortKey = &wrappers.StringValue{Value: orderBy} + } + describeAppVersionAudits.Reverse = &wrappers.BoolValue{Value: reverse} + describeAppVersionAudits.Limit = uint32(limit) + describeAppVersionAudits.Offset = uint32(offset) + resp, err := client.App().DescribeAppVersionAudits(openpitrix.SystemContext(), describeAppVersionAudits) + + if err != nil { + klog.Error(err) + return nil, err + } + + items := make([]interface{}, 0) + + for _, item := range resp.AppVersionAuditSet { + appVersion := convertAppVersionAudit(item) + items = append(items, appVersion) + } + + return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil +} + +func ListAppVersionReviews(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { + client, err := cs.ClientSets().OpenPitrix() + + if err != nil { + klog.Error(err) + return nil, err + } + + describeAppVersionReviews := &pb.DescribeAppVersionReviewsRequest{} + + if keyword := conditions.Match["keyword"]; keyword != "" { + describeAppVersionReviews.SearchWord = &wrappers.StringValue{Value: keyword} + } + if status := conditions.Match["status"]; status != "" { + describeAppVersionReviews.Status = strings.Split(status, "|") + } + if orderBy != "" { + describeAppVersionReviews.SortKey = &wrappers.StringValue{Value: orderBy} + } + describeAppVersionReviews.Reverse = &wrappers.BoolValue{Value: reverse} + describeAppVersionReviews.Limit = uint32(limit) + describeAppVersionReviews.Offset = uint32(offset) + // TODO icon is needed + resp, err := client.App().DescribeAppVersionReviews(openpitrix.SystemContext(), describeAppVersionReviews) + + if err != nil { + klog.Error(err) + return nil, err + } + + items := make([]interface{}, 0) + + for _, item := range resp.AppVersionReviewSet { + appVersion := convertAppVersionReview(item) + items = append(items, appVersion) + } + + return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil +} + +func ListAppVersions(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { + client, err := cs.ClientSets().OpenPitrix() + + if err != nil { + klog.Error(err) + return nil, err + } + + describeAppVersionsRequest := &pb.DescribeAppVersionsRequest{} + + if keyword := conditions.Match["keyword"]; keyword != "" { + describeAppVersionsRequest.SearchWord = &wrappers.StringValue{Value: keyword} + } + if appId := conditions.Match["app"]; appId != "" { + describeAppVersionsRequest.AppId = []string{appId} + } + if status := conditions.Match["status"]; status != "" { + describeAppVersionsRequest.Status = strings.Split(status, "|") + } + if orderBy != "" { + describeAppVersionsRequest.SortKey = &wrappers.StringValue{Value: orderBy} + } + describeAppVersionsRequest.Reverse = &wrappers.BoolValue{Value: reverse} + describeAppVersionsRequest.Limit = uint32(limit) + describeAppVersionsRequest.Offset = uint32(offset) + resp, err := client.App().DescribeAppVersions(openpitrix.SystemContext(), describeAppVersionsRequest) + + if err != nil { + klog.Error(err) + return nil, err + } + + items := make([]interface{}, 0) + + for _, item := range resp.AppVersionSet { + appVersion := convertAppVersion(item) + items = append(items, appVersion) + } + + return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil +} diff --git a/pkg/models/openpitrix/attachments.go b/pkg/models/openpitrix/attachments.go new file mode 100644 index 000000000..07722b04a --- /dev/null +++ b/pkg/models/openpitrix/attachments.go @@ -0,0 +1,50 @@ +/* + * + * Copyright 2019 The KubeSphere Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package openpitrix + +import ( + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "k8s.io/klog" + cs "kubesphere.io/kubesphere/pkg/simple/client" + "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" + "openpitrix.io/openpitrix/pkg/pb" +) + +func DescribeAttachment(id string) (*Attachment, error) { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return nil, err + } + resp, err := op.Attachment().GetAttachments(openpitrix.SystemContext(), &pb.GetAttachmentsRequest{ + AttachmentId: []string{id}, + }) + if err != nil { + klog.Error(err) + return nil, err + } + if len(resp.Attachments) > 0 { + return convertAttachment(resp.Attachments[id]), nil + } else { + err := status.New(codes.NotFound, "resource not found").Err() + klog.Error(err) + return nil, err + } +} diff --git a/pkg/models/openpitrix/categories.go b/pkg/models/openpitrix/categories.go new file mode 100644 index 000000000..f827a71a8 --- /dev/null +++ b/pkg/models/openpitrix/categories.go @@ -0,0 +1,163 @@ +/* + * + * Copyright 2019 The KubeSphere Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package openpitrix + +import ( + "github.com/golang/protobuf/ptypes/wrappers" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/models" + "kubesphere.io/kubesphere/pkg/server/params" + cs "kubesphere.io/kubesphere/pkg/simple/client" + "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" + "openpitrix.io/openpitrix/pkg/pb" +) + +func CreateCategory(request *CreateCategoryRequest) (*CreateCategoryResponse, error) { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return nil, err + } + r := &pb.CreateCategoryRequest{ + Name: &wrappers.StringValue{Value: request.Name}, + Locale: &wrappers.StringValue{Value: request.Locale}, + Description: &wrappers.StringValue{Value: request.Description}, + } + if request.Icon != nil { + r.Icon = &wrappers.BytesValue{Value: request.Icon} + } + + resp, err := op.Category().CreateCategory(openpitrix.SystemContext(), r) + if err != nil { + klog.Error(err) + return nil, err + } + return &CreateCategoryResponse{ + CategoryId: resp.GetCategoryId().GetValue(), + }, nil +} + +func DeleteCategory(id string) error { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return err + } + _, err = op.Category().DeleteCategories(openpitrix.SystemContext(), &pb.DeleteCategoriesRequest{ + CategoryId: []string{id}, + }) + if err != nil { + klog.Error(err) + return err + } + return nil +} + +func PatchCategory(id string, request *ModifyCategoryRequest) error { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return err + } + modifyCategoryRequest := &pb.ModifyCategoryRequest{ + CategoryId: &wrappers.StringValue{Value: id}, + } + if request.Name != nil { + modifyCategoryRequest.Name = &wrappers.StringValue{Value: *request.Name} + } + if request.Locale != nil { + modifyCategoryRequest.Locale = &wrappers.StringValue{Value: *request.Locale} + } + if request.Description != nil { + modifyCategoryRequest.Description = &wrappers.StringValue{Value: *request.Description} + } + if request.Icon != nil { + modifyCategoryRequest.Icon = &wrappers.BytesValue{Value: request.Icon} + } + + _, err = op.Category().ModifyCategory(openpitrix.SystemContext(), modifyCategoryRequest) + if err != nil { + klog.Error(err) + return err + } + return nil +} + +func DescribeCategory(id string) (*Category, error) { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return nil, err + } + resp, err := op.Category().DescribeCategories(openpitrix.SystemContext(), &pb.DescribeCategoriesRequest{ + CategoryId: []string{id}, + Limit: 1, + }) + if err != nil { + klog.Error(err) + return nil, err + } + + var category *Category + + if len(resp.CategorySet) > 0 { + category = convertCategory(resp.CategorySet[0]) + return category, nil + } else { + err := status.New(codes.NotFound, "resource not found").Err() + klog.Error(err) + return nil, err + } +} + +func ListCategories(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { + client, err := cs.ClientSets().OpenPitrix() + + if err != nil { + klog.Error(err) + return nil, err + } + + req := &pb.DescribeCategoriesRequest{} + + if keyword := conditions.Match["keyword"]; keyword != "" { + req.SearchWord = &wrappers.StringValue{Value: keyword} + } + if orderBy != "" { + req.SortKey = &wrappers.StringValue{Value: orderBy} + } + req.Reverse = &wrappers.BoolValue{Value: reverse} + req.Limit = uint32(limit) + req.Offset = uint32(offset) + resp, err := client.Category().DescribeCategories(openpitrix.SystemContext(), req) + if err != nil { + klog.Error(err) + return nil, err + } + + items := make([]interface{}, 0) + + for _, item := range resp.CategorySet { + items = append(items, convertCategory(item)) + } + + return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil +} diff --git a/pkg/models/openpitrix/convert.go b/pkg/models/openpitrix/convert.go new file mode 100644 index 000000000..d3c8c049d --- /dev/null +++ b/pkg/models/openpitrix/convert.go @@ -0,0 +1,647 @@ +/* + * + * Copyright 2019 The KubeSphere Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package openpitrix + +import ( + "github.com/go-openapi/strfmt" + "openpitrix.io/openpitrix/pkg/pb" + "time" +) + +func convertApp(in *pb.App) *App { + + if in == nil { + return nil + } + + categorySet := make(AppCategorySet, 0) + + for _, item := range in.CategorySet { + category := convertResourceCategory(item) + categorySet = append(categorySet, category) + } + + out := App{ + CategorySet: categorySet, + } + + if in.Abstraction != nil { + out.Abstraction = in.Abstraction.Value + } + if in.Active != nil { + out.Active = in.Active.Value + } + if in.AppId != nil { + out.AppId = in.AppId.Value + } + if in.AppVersionTypes != nil { + out.AppVersionTypes = in.AppVersionTypes.Value + } + if in.ChartName != nil { + out.ChartName = in.ChartName.Value + } + if in.CompanyJoinTime != nil { + date := strfmt.DateTime(time.Unix(in.CompanyJoinTime.Seconds, 0)) + out.CompanyJoinTime = &date + } + if in.CompanyName != nil { + out.CompanyName = in.CompanyName.Value + } + if in.CompanyProfile != nil { + out.CompanyProfile = in.CompanyProfile.Value + } + if in.CompanyWebsite != nil { + out.CompanyWebsite = in.CompanyWebsite.Value + } + if in.CreateTime != nil { + date := strfmt.DateTime(time.Unix(in.CreateTime.Seconds, 0)) + out.CreateTime = &date + } + if in.CompanyWebsite != nil { + out.CompanyWebsite = in.CompanyWebsite.Value + } + if in.Description != nil { + out.Description = in.Description.Value + } + if in.Home != nil { + out.Home = in.Home.Value + } + if in.Icon != nil { + out.Icon = in.Icon.Value + } + if in.Isv != nil { + out.Isv = in.Isv.Value + } + if in.Keywords != nil { + out.Keywords = in.Keywords.Value + } + if in.LatestAppVersion != nil { + out.LatestAppVersion = convertAppVersion(in.LatestAppVersion) + } + if in.Name != nil { + out.Name = in.Name.Value + } + if in.Owner != nil { + out.Owner = in.Owner.Value + } + if in.Readme != nil { + out.Readme = in.Readme.Value + } + if in.RepoId != nil { + out.RepoId = in.RepoId.Value + } + if in.StatusTime != nil { + date := strfmt.DateTime(time.Unix(in.StatusTime.Seconds, 0)) + out.StatusTime = &date + } + if in.Status != nil { + out.Status = in.Status.Value + } + if in.Sources != nil { + out.Sources = in.Sources.Value + } + if in.Screenshots != nil { + out.Screenshots = in.Screenshots.Value + } + if in.Tos != nil { + out.Tos = in.Tos.Value + } + if in.UpdateTime != nil { + date := strfmt.DateTime(time.Unix(in.UpdateTime.Seconds, 0)) + out.UpdateTime = &date + } + + return &out +} + +func convertAppVersion(in *pb.AppVersion) *AppVersion { + if in == nil { + return nil + } + out := AppVersion{} + if in.AppId != nil { + out.AppId = in.AppId.Value + } + if in.Active != nil { + out.Active = in.Active.Value + } + if in.CreateTime != nil { + date := strfmt.DateTime(time.Unix(in.CreateTime.Seconds, 0)) + out.CreateTime = &date + } + if in.Description != nil { + out.Description = in.Description.Value + } + if in.Home != nil { + out.Home = in.Home.Value + } + if in.Icon != nil { + out.Icon = in.Icon.Value + } + if in.Maintainers != nil { + out.Maintainers = in.Maintainers.Value + } + if in.Message != nil { + out.Message = in.Message.Value + } + if in.Keywords != nil { + out.Keywords = in.Keywords.Value + } + if in.Name != nil { + out.Name = in.Name.Value + } + if in.Owner != nil { + out.Owner = in.Owner.Value + } + if in.PackageName != nil { + out.PackageName = in.PackageName.Value + } + if in.Readme != nil { + out.Readme = in.Readme.Value + } + if in.ReviewId != nil { + out.ReviewId = in.ReviewId.Value + } + if in.Screenshots != nil { + out.Screenshots = in.Screenshots.Value + } + if in.Sources != nil { + out.Sources = in.Sources.Value + } + if in.Status != nil { + out.Status = in.Status.Value + } + if in.Sequence != nil { + out.Sequence = int64(in.Sequence.Value) + } + if in.StatusTime != nil { + date := strfmt.DateTime(time.Unix(in.StatusTime.Seconds, 0)) + out.StatusTime = &date + } + if in.Type != nil { + out.Type = in.Type.Value + } + if in.UpdateTime != nil { + date := strfmt.DateTime(time.Unix(in.UpdateTime.Seconds, 0)) + out.UpdateTime = &date + } + if in.VersionId != nil { + out.VersionId = in.VersionId.Value + } + + return &out + +} + +func convertResourceCategory(in *pb.ResourceCategory) *ResourceCategory { + if in == nil { + return nil + } + out := ResourceCategory{} + + if in.CategoryId != nil { + out.CategoryId = in.CategoryId.Value + } + if in.CreateTime != nil { + date := strfmt.DateTime(time.Unix(in.CreateTime.Seconds, 0)) + out.CreateTime = &date + } + if in.Locale != nil { + out.Locale = in.Locale.Value + } + if in.Name != nil { + out.Name = in.Name.Value + } + if in.Status != nil { + out.Status = in.Status.Value + } + if in.StatusTime != nil { + date := strfmt.DateTime(time.Unix(in.StatusTime.Seconds, 0)) + out.StatusTime = &date + } + + return &out +} + +func convertCategory(in *pb.Category) *Category { + if in == nil { + return nil + } + out := Category{} + + if in.CategoryId != nil { + out.CategoryID = in.CategoryId.Value + } + if in.CreateTime != nil { + date := strfmt.DateTime(time.Unix(in.CreateTime.Seconds, 0)) + out.CreateTime = &date + } + if in.Locale != nil { + out.Locale = in.Locale.Value + } + if in.Name != nil { + out.Name = in.Name.Value + } + if in.Description != nil { + out.Description = in.Description.Value + } + if in.Icon != nil { + out.Icon = in.Icon.Value + } + if in.Owner != nil { + out.Owner = in.Owner.Value + } + if in.UpdateTime != nil { + date := strfmt.DateTime(time.Unix(in.UpdateTime.Seconds, 0)) + out.UpdateTime = &date + } + + return &out +} + +func convertAttachment(in *pb.Attachment) *Attachment { + if in == nil { + return nil + } + out := Attachment{} + + out.AttachmentID = in.AttachmentId + + if in.CreateTime != nil { + date := strfmt.DateTime(time.Unix(in.CreateTime.Seconds, 0)) + out.CreateTime = &date + } + if in.AttachmentContent != nil { + out.AttachmentContent = make(map[string]strfmt.Base64) + for k, v := range in.AttachmentContent { + out.AttachmentContent[k] = v + } + } + + return &out +} + +func convertRepo(in *pb.Repo) *Repo { + if in == nil { + return nil + } + out := Repo{} + + if in.RepoId != nil { + out.RepoId = in.RepoId.Value + } + if in.Name != nil { + out.Name = in.Name.Value + } + if in.AppDefaultStatus != nil { + out.AppDefaultStatus = in.AppDefaultStatus.Value + } + if in.Credential != nil { + out.Credential = in.Credential.Value + } + + categorySet := make(RepoCategorySet, 0) + + for _, item := range in.CategorySet { + category := convertResourceCategory(item) + categorySet = append(categorySet, category) + } + + out.CategorySet = categorySet + + if in.Controller != nil { + out.Credential = in.Credential.Value + } + + if in.CreateTime != nil { + date := strfmt.DateTime(time.Unix(in.CreateTime.Seconds, 0)) + out.CreateTime = &date + } + + if in.Description != nil { + out.Description = in.Description.Value + } + + labelSet := make(RepoLabels, 0) + + for _, item := range in.Labels { + label := convertRepoLabel(item) + labelSet = append(labelSet, label) + } + + out.Labels = labelSet + + if in.Owner != nil { + out.Owner = in.Owner.Value + } + if in.Providers != nil { + out.Providers = in.Providers + } + if in.RepoId != nil { + out.RepoId = in.RepoId.Value + } + + selectorSet := make(RepoSelectors, 0) + + for _, item := range in.Selectors { + selector := convertRepoSelector(item) + selectorSet = append(selectorSet, selector) + } + + out.Selectors = selectorSet + + if in.Status != nil { + out.Status = in.Status.Value + } + if in.StatusTime != nil { + out.StatusTime = strfmt.DateTime(time.Unix(in.StatusTime.Seconds, 0)) + } + if in.Type != nil { + out.Type = in.Type.Value + } + if in.Url != nil { + out.URL = in.Url.Value + } + if in.Visibility != nil { + out.Visibility = in.Visibility.Value + } + return &out +} + +func convertRepoLabel(in *pb.RepoLabel) *RepoLabel { + if in == nil { + return nil + } + out := RepoLabel{} + if in.CreateTime != nil { + date := strfmt.DateTime(time.Unix(in.CreateTime.Seconds, 0)) + out.CreateTime = &date + } + if in.LabelKey != nil { + out.LabelKey = in.LabelKey.Value + } + if in.LabelValue != nil { + out.LabelValue = in.LabelValue.Value + } + return &out +} + +func convertRepoSelector(in *pb.RepoSelector) *RepoSelector { + if in == nil { + return nil + } + out := RepoSelector{} + if in.CreateTime != nil { + date := strfmt.DateTime(time.Unix(in.CreateTime.Seconds, 0)) + out.CreateTime = &date + } + if in.SelectorKey != nil { + out.SelectorKey = in.SelectorKey.Value + } + if in.SelectorValue != nil { + out.SelectorValue = in.SelectorValue.Value + } + return &out +} + +func convertAppVersionAudit(in *pb.AppVersionAudit) *AppVersionAudit { + if in == nil { + return nil + } + out := AppVersionAudit{} + if in.AppId != nil { + out.AppId = in.AppId.Value + } + if in.AppName != nil { + out.AppName = in.AppName.Value + } + if in.Message != nil { + out.Message = in.Message.Value + } + if in.Operator != nil { + out.Operator = in.Operator.Value + } + if in.OperatorType != nil { + out.OperatorType = in.OperatorType.Value + } + if in.ReviewId != nil { + out.ReviewId = in.ReviewId.Value + } + if in.Status != nil { + out.Status = in.Status.Value + } + if in.StatusTime != nil { + date := strfmt.DateTime(time.Unix(in.StatusTime.Seconds, 0)) + out.StatusTime = &date + } + if in.VersionId != nil { + out.VersionId = in.VersionId.Value + } + if in.VersionName != nil { + out.VersionName = in.VersionName.Value + } + if in.VersionType != nil { + out.VersionType = in.VersionType.Value + } + return &out +} + +func convertAppVersionReview(in *pb.AppVersionReview) *AppVersionReview { + if in == nil { + return nil + } + out := AppVersionReview{} + if in.AppId != nil { + out.AppId = in.AppId.Value + } + if in.AppName != nil { + out.AppName = in.AppName.Value + } + if in.Phase != nil { + out.Phase = make(AppVersionReviewPhaseOAIGen) + for k, v := range in.Phase { + out.Phase[k] = *convertAppVersionReviewPhase(v) + } + } + if in.ReviewId != nil { + out.ReviewId = in.ReviewId.Value + } + if in.Reviewer != nil { + out.Reviewer = in.Reviewer.Value + } + if in.Status != nil { + out.Status = in.Status.Value + } + if in.StatusTime != nil { + out.StatusTime = strfmt.DateTime(time.Unix(in.StatusTime.Seconds, 0)) + } + if in.VersionId != nil { + out.VersionID = in.VersionId.Value + } + if in.VersionName != nil { + out.VersionName = in.VersionName.Value + } + if in.VersionType != nil { + out.VersionType = in.VersionType.Value + } + return &out +} + +func convertAppVersionReviewPhase(in *pb.AppVersionReviewPhase) *AppVersionReviewPhase { + if in == nil { + return nil + } + out := AppVersionReviewPhase{} + if in.Message != nil { + out.Message = in.Message.Value + } + if in.OperatorType != nil { + out.OperatorType = in.OperatorType.Value + } + if in.ReviewTime != nil { + date := strfmt.DateTime(time.Unix(in.ReviewTime.Seconds, 0)) + out.ReviewTime = &date + } + if in.Operator != nil { + out.Operator = in.Operator.Value + } + if in.Status != nil { + out.Status = in.Status.Value + } + if in.StatusTime != nil { + date := strfmt.DateTime(time.Unix(in.StatusTime.Seconds, 0)) + out.StatusTime = &date + } + return &out +} + +func convertRepoEvent(in *pb.RepoEvent) *RepoEvent { + if in == nil { + return nil + } + out := RepoEvent{} + if in.CreateTime != nil { + date := strfmt.DateTime(time.Unix(in.CreateTime.Seconds, 0)) + out.CreateTime = &date + } + if in.Owner != nil { + out.Owner = in.Owner.Value + } + if in.RepoEventId != nil { + out.RepoEventId = in.RepoEventId.Value + } + if in.RepoId != nil { + out.RepoId = in.RepoId.Value + } + if in.Result != nil { + out.Result = in.Result.Value + } + if in.Status != nil { + out.Status = in.Status.Value + } + if in.StatusTime != nil { + date := strfmt.DateTime(time.Unix(in.StatusTime.Seconds, 0)) + out.StatusTime = &date + } + + return &out +} + +func convertCluster(in *pb.Cluster) *Cluster { + if in == nil { + return nil + } + out := Cluster{} + if in.AdditionalInfo != nil { + out.AdditionalInfo = in.AdditionalInfo.Value + } + if in.AppId != nil { + out.AppId = in.AppId.Value + } + if in.ClusterId != nil { + out.ClusterId = in.ClusterId.Value + } + if in.ClusterType != nil { + out.ClusterType = int64(in.ClusterType.Value) + } + if in.CreateTime != nil { + date := strfmt.DateTime(time.Unix(in.CreateTime.Seconds, 0)) + out.CreateTime = &date + } + if in.Debug != nil { + out.Debug = in.Debug.Value + } + if in.Description != nil { + out.Description = in.Description.Value + } + if in.Endpoints != nil { + out.Endpoints = in.Endpoints.Value + } + if in.Env != nil { + out.Env = in.Env.Value + } + if in.FrontgateId != nil { + out.FrontgateId = in.FrontgateId.Value + } + if in.GlobalUuid != nil { + out.GlobalUUID = in.GlobalUuid.Value + } + if in.MetadataRootAccess != nil { + out.MetadataRootAccess = in.MetadataRootAccess.Value + } + if in.Name != nil { + out.Name = in.Name.Value + } + if in.Owner != nil { + out.Owner = in.Owner.Value + } + if in.RuntimeId != nil { + out.RuntimeId = in.RuntimeId.Value + } + if in.Status != nil { + out.Status = in.Status.Value + } + if in.StatusTime != nil { + date := strfmt.DateTime(time.Unix(in.StatusTime.Seconds, 0)) + out.StatusTime = &date + } + if in.SubnetId != nil { + out.SubnetId = in.SubnetId.Value + } + if in.TransitionStatus != nil { + out.TransitionStatus = in.TransitionStatus.Value + } + if in.UpgradeStatus != nil { + out.UpgradeStatus = in.UpgradeStatus.Value + } + if in.UpgradeTime != nil { + date := strfmt.DateTime(time.Unix(in.UpgradeTime.Seconds, 0)) + out.UpgradeTime = &date + } + if in.VersionId != nil { + out.VersionId = in.VersionId.Value + } + if in.VpcId != nil { + out.VpcId = in.VpcId.Value + } + if in.Zone != nil { + out.Zone = in.Zone.Value + } + return &out +} diff --git a/pkg/models/openpitrix/repos.go b/pkg/models/openpitrix/repos.go new file mode 100644 index 000000000..5954336d4 --- /dev/null +++ b/pkg/models/openpitrix/repos.go @@ -0,0 +1,296 @@ +/* + * + * Copyright 2019 The KubeSphere Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package openpitrix + +import ( + "fmt" + "github.com/golang/protobuf/ptypes/wrappers" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/models" + "kubesphere.io/kubesphere/pkg/server/params" + cs "kubesphere.io/kubesphere/pkg/simple/client" + "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" + "openpitrix.io/openpitrix/pkg/pb" + "strings" +) + +func CreateRepo(request *CreateRepoRequest) (*CreateRepoResponse, error) { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return nil, err + } + createRepoRequest := &pb.CreateRepoRequest{ + Name: &wrappers.StringValue{Value: request.Name}, + Description: &wrappers.StringValue{Value: request.Description}, + Type: &wrappers.StringValue{Value: request.Type}, + Url: &wrappers.StringValue{Value: request.URL}, + Credential: &wrappers.StringValue{Value: request.Credential}, + Visibility: &wrappers.StringValue{Value: request.Visibility}, + CategoryId: &wrappers.StringValue{Value: request.CategoryId}, + AppDefaultStatus: &wrappers.StringValue{Value: request.AppDefaultStatus}, + } + + if request.Providers != nil { + createRepoRequest.Providers = request.Providers + } + if request.Workspace != nil { + createRepoRequest.Labels = &wrappers.StringValue{Value: fmt.Sprintf("workspace=%s", *request.Workspace)} + } + + resp, err := op.Repo().CreateRepo(openpitrix.SystemContext(), createRepoRequest) + if err != nil { + klog.Error(err) + return nil, err + } + return &CreateRepoResponse{ + RepoID: resp.GetRepoId().GetValue(), + }, nil +} + +func DeleteRepo(id string) error { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return err + } + _, err = op.Repo().DeleteRepos(openpitrix.SystemContext(), &pb.DeleteReposRequest{ + RepoId: []string{id}, + }) + if err != nil { + klog.Error(err) + return err + } + return nil +} + +func PatchRepo(id string, request *ModifyRepoRequest) error { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return err + } + modifyRepoRequest := &pb.ModifyRepoRequest{ + RepoId: &wrappers.StringValue{Value: id}, + } + + if request.Name != nil { + modifyRepoRequest.Name = &wrappers.StringValue{Value: *request.Name} + } + if request.Description != nil { + modifyRepoRequest.Description = &wrappers.StringValue{Value: *request.Description} + } + if request.Type != nil { + modifyRepoRequest.Type = &wrappers.StringValue{Value: *request.Type} + } + if request.URL != nil { + modifyRepoRequest.Url = &wrappers.StringValue{Value: *request.URL} + } + if request.Credential != nil { + modifyRepoRequest.Credential = &wrappers.StringValue{Value: *request.Credential} + } + if request.Visibility != nil { + modifyRepoRequest.Visibility = &wrappers.StringValue{Value: *request.Visibility} + } + + if request.CategoryID != nil { + modifyRepoRequest.CategoryId = &wrappers.StringValue{Value: *request.CategoryID} + } + if request.AppDefaultStatus != nil { + modifyRepoRequest.AppDefaultStatus = &wrappers.StringValue{Value: *request.AppDefaultStatus} + } + if request.Providers != nil { + modifyRepoRequest.Providers = request.Providers + } + if request.Workspace != nil { + modifyRepoRequest.Labels = &wrappers.StringValue{Value: fmt.Sprintf("workspace=%s", *request.Workspace)} + } + + _, err = op.Repo().ModifyRepo(openpitrix.SystemContext(), modifyRepoRequest) + if err != nil { + klog.Error(err) + return err + } + return nil +} + +func DescribeRepo(id string) (*Repo, error) { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return nil, err + } + resp, err := op.Repo().DescribeRepos(openpitrix.SystemContext(), &pb.DescribeReposRequest{ + RepoId: []string{id}, + Limit: 1, + }) + if err != nil { + klog.Error(err) + return nil, err + } + + var repo *Repo + + if len(resp.RepoSet) > 0 { + repo = convertRepo(resp.RepoSet[0]) + return repo, nil + } else { + err := status.New(codes.NotFound, "resource not found").Err() + klog.Error(err) + return nil, err + } +} + +func ListRepos(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { + client, err := cs.ClientSets().OpenPitrix() + + if err != nil { + klog.Error(err) + return nil, err + } + + req := &pb.DescribeReposRequest{} + + if keyword := conditions.Match["keyword"]; keyword != "" { + req.SearchWord = &wrappers.StringValue{Value: keyword} + } + if status := conditions.Match["status"]; status != "" { + req.Status = strings.Split(status, "|") + } + if typeStr := conditions.Match["type"]; typeStr != "" { + req.Type = strings.Split(typeStr, "|") + } + if visibility := conditions.Match["visibility"]; visibility != "" { + req.Visibility = strings.Split(visibility, "|") + } + if status := conditions.Match["status"]; status != "" { + req.Status = strings.Split(status, "|") + } + if workspace := conditions.Match["workspace"]; workspace != "" { + req.Label = &wrappers.StringValue{Value: fmt.Sprintf("workspace=%s", workspace)} + } + if orderBy != "" { + req.SortKey = &wrappers.StringValue{Value: orderBy} + } + req.Reverse = &wrappers.BoolValue{Value: reverse} + req.Limit = uint32(limit) + req.Offset = uint32(offset) + resp, err := client.Repo().DescribeRepos(openpitrix.SystemContext(), req) + if err != nil { + klog.Error(err) + return nil, err + } + + items := make([]interface{}, 0) + + for _, item := range resp.RepoSet { + items = append(items, convertRepo(item)) + } + + return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil +} + +func ValidateRepo(request *ValidateRepoRequest) (*ValidateRepoResponse, error) { + client, err := cs.ClientSets().OpenPitrix() + + if err != nil { + klog.Error(err) + return nil, err + } + + resp, err := client.Repo().ValidateRepo(openpitrix.SystemContext(), &pb.ValidateRepoRequest{ + Type: &wrappers.StringValue{Value: request.Type}, + Credential: &wrappers.StringValue{Value: request.Credential}, + Url: &wrappers.StringValue{Value: request.Url}, + }) + + if err != nil { + klog.Error(err) + return nil, err + } + + return &ValidateRepoResponse{ + ErrorCode: int64(resp.ErrorCode), + Ok: resp.Ok.Value, + }, nil +} + +func DoRepoAction(repoId string, request *RepoActionRequest) error { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return err + } + + switch request.Action { + case "index": + indexRepoRequest := &pb.IndexRepoRequest{ + RepoId: &wrappers.StringValue{Value: repoId}, + } + _, err := op.RepoIndexer().IndexRepo(openpitrix.SystemContext(), indexRepoRequest) + + if err != nil { + klog.Error(err) + return err + } + + return nil + default: + err = status.New(codes.InvalidArgument, "action not support").Err() + klog.Error(err) + return err + } +} + +func ListRepoEvents(repoId string, conditions *params.Conditions, limit, offset int) (*models.PageableResponse, error) { + op, err := cs.ClientSets().OpenPitrix() + if err != nil { + klog.Error(err) + return nil, err + } + + describeRepoEventsRequest := &pb.DescribeRepoEventsRequest{ + RepoId: []string{repoId}, + } + if eventId := conditions.Match["repo_event_id"]; eventId != "" { + describeRepoEventsRequest.RepoEventId = strings.Split(eventId, "|") + } + if status := conditions.Match["status"]; status != "" { + describeRepoEventsRequest.Status = strings.Split(status, "|") + } + describeRepoEventsRequest.Limit = uint32(limit) + describeRepoEventsRequest.Offset = uint32(offset) + + resp, err := op.RepoIndexer().DescribeRepoEvents(openpitrix.SystemContext(), describeRepoEventsRequest) + + if err != nil { + klog.Error(err) + return nil, err + } + + items := make([]interface{}, 0) + + for _, item := range resp.RepoEventSet { + items = append(items, convertRepoEvent(item)) + } + + return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil +} diff --git a/pkg/models/openpitrix/types.go b/pkg/models/openpitrix/types.go new file mode 100644 index 000000000..c20897dfa --- /dev/null +++ b/pkg/models/openpitrix/types.go @@ -0,0 +1,833 @@ +package openpitrix + +import ( + "github.com/go-openapi/strfmt" +) + +type ModifyAppRequest struct { + // content of attachment + AttachmentContent []byte `json:"attachment_content,omitempty"` + + // only for screenshot, range: [0, 5] + Sequence *int32 `json:"sequence,omitempty"` + + // optional: icon/screenshot + Type *string `json:"type,omitempty"` + + // abstraction of app + Abstraction *string `json:"abstraction,omitempty"` + + // category id of the app + CategoryID *string `json:"category_id,omitempty"` + + // description of the app + Description *string `json:"description,omitempty"` + + // home page of the app + Home *string `json:"home,omitempty"` + + // key words of the app + Keywords *string `json:"keywords,omitempty"` + + // maintainers who maintainer the app + Maintainers *string `json:"maintainers,omitempty"` + + // name of the app + Name *string `json:"name,omitempty"` + + // instructions of the app + Readme *string `json:"readme,omitempty"` + + // sources of app + Sources *string `json:"sources,omitempty"` + + // tos of app + Tos *string `json:"tos,omitempty"` +} + +type ModifyAppVersionRequest struct { + // app description + Description *string `json:"description,omitempty"` + + // app name + Name *string `json:"name,omitempty"` + + // package of app to replace other + Package []byte `json:"package,omitempty"` + + // filename map to file_content + PackageFiles map[string][]byte `json:"package_files,omitempty"` + + // required, version id of app to modify + VersionID *string `json:"version_id,omitempty"` +} +type AppVersionAudit struct { + + // id of specific version app + AppId string `json:"app_id,omitempty"` + + // name of specific version app + AppName string `json:"app_name,omitempty"` + + // audit message + Message string `json:"message,omitempty"` + + // user of auditer + Operator string `json:"operator,omitempty"` + + // operator of auditer eg.[global_admin|developer|business|technical|isv] + OperatorType string `json:"operator_type,omitempty"` + + // review id + ReviewId string `json:"review_id,omitempty"` + + // audit status, eg.[draft|submitted|passed|rejected|active|in-review|deleted|suspended] + Status string `json:"status,omitempty"` + + // record status changed time + StatusTime *strfmt.DateTime `json:"status_time,omitempty"` + + // id of version to audit + VersionId string `json:"version_id,omitempty"` + + // version name + VersionName string `json:"version_name,omitempty"` + + // version type + VersionType string `json:"version_type,omitempty"` +} + +type ValidatePackageRequest struct { + + // required, version package eg.[the wordpress-0.0.1.tgz will be encoded to bytes] + VersionPackage strfmt.Base64 `json:"version_package,omitempty"` + + // optional, vmbased/helm + VersionType string `json:"version_type,omitempty"` +} +type App struct { + + // abstraction of app + Abstraction string `json:"abstraction,omitempty"` + + // whether there is a released version in the app + Active bool `json:"active,omitempty"` + + // app id + AppId string `json:"app_id,omitempty"` + + // app version types eg.[vmbased|helm] + AppVersionTypes string `json:"app_version_types,omitempty"` + + // category set + CategorySet AppCategorySet `json:"category_set"` + + // chart name of app + ChartName string `json:"chart_name,omitempty"` + + // company join time + CompanyJoinTime *strfmt.DateTime `json:"company_join_time,omitempty"` + + // company name + CompanyName string `json:"company_name,omitempty"` + + // company profile + CompanyProfile string `json:"company_profile,omitempty"` + + // company website + CompanyWebsite string `json:"company_website,omitempty"` + + // the time when app create + CreateTime *strfmt.DateTime `json:"create_time,omitempty"` + + // app description + Description string `json:"description,omitempty"` + + // app home page + Home string `json:"home,omitempty"` + + // app icon + Icon string `json:"icon,omitempty"` + + // the isv user who create the app + Isv string `json:"isv,omitempty"` + + // app key words + Keywords string `json:"keywords,omitempty"` + + // latest version of app + LatestAppVersion *AppVersion `json:"latest_app_version,omitempty"` + + // app maintainers + Maintainers string `json:"maintainers,omitempty"` + + // app name + Name string `json:"name,omitempty"` + + // owner of app + Owner string `json:"owner,omitempty"` + + // app instructions + Readme string `json:"readme,omitempty"` + + // repository(store app package) id + RepoId string `json:"repo_id,omitempty"` + + // app screenshots + Screenshots string `json:"screenshots,omitempty"` + + // sources of app + Sources string `json:"sources,omitempty"` + + // status eg.[modify|submit|review|cancel|release|delete|pass|reject|suspend|recover] + Status string `json:"status,omitempty"` + + // record status changed time + StatusTime *strfmt.DateTime `json:"status_time,omitempty"` + + // tos of app + Tos string `json:"tos,omitempty"` + + // the time when app update + UpdateTime *strfmt.DateTime `json:"update_time,omitempty"` + + ClusterTotal *int `json:"cluster_total,omitempty"` +} +type AppVersionReviewPhase struct { + + // review message + Message string `json:"message,omitempty"` + + // user of reviewer + Operator string `json:"operator,omitempty"` + + // operator type of reviewer eg.[global_admin|developer|business|technical|isv] + OperatorType string `json:"operator_type,omitempty"` + + // app version review time + ReviewTime *strfmt.DateTime `json:"review_time,omitempty"` + + // review status of app version eg.[isv-in-review|isv-passed|isv-rejected|isv-draft|business-in-review|business-passed|business-rejected|develop-draft|develop-in-review|develop-passed|develop-rejected|develop-draft] + Status string `json:"status,omitempty"` + + // record status changed time + StatusTime *strfmt.DateTime `json:"status_time,omitempty"` +} + +type AppVersionReviewPhaseOAIGen map[string]AppVersionReviewPhase + +type GetAppVersionPackageResponse struct { + + // app id of package + AppId string `json:"app_id,omitempty"` + + // package of specific app version + Package strfmt.Base64 `json:"package,omitempty"` + + // version id of package + VersionId string `json:"version_id,omitempty"` +} + +type GetAppVersionPackageFilesResponse struct { + + // filename map to content + Files map[string]strfmt.Base64 `json:"files,omitempty"` + + // version id + VersionId string `json:"version_id,omitempty"` +} +type AppVersionReview struct { + + // app id + AppId string `json:"app_id,omitempty"` + + // app name + AppName string `json:"app_name,omitempty"` + + // phase + Phase AppVersionReviewPhaseOAIGen `json:"phase,omitempty"` + + // review id + ReviewId string `json:"review_id,omitempty"` + + // user who review the app version + Reviewer string `json:"reviewer,omitempty"` + + // review status eg.[isv-in-review|isv-passed|isv-rejected|isv-draft|business-in-review|business-passed|business-rejected|develop-draft|develop-in-review|develop-passed|develop-rejected|develop-draft] + Status string `json:"status,omitempty"` + + // record status changed time + StatusTime strfmt.DateTime `json:"status_time,omitempty"` + + // id of app version + VersionID string `json:"version_id,omitempty"` + + // version name of specific app version + VersionName string `json:"version_name,omitempty"` + + // version type + VersionType string `json:"version_type,omitempty"` +} + +type CreateAppRequest struct { + + // app icon + Icon strfmt.Base64 `json:"icon,omitempty"` + + // isv + Isv string `json:"isv,omitempty"` + + // required, app name + Name string `json:"name,omitempty"` + + // required, version name of the app + VersionName string `json:"version_name,omitempty"` + + // required, version with specific app package + VersionPackage strfmt.Base64 `json:"version_package,omitempty"` + + // optional, vmbased/helm + VersionType string `json:"version_type,omitempty"` +} + +type CreateAppResponse struct { + + // app id + AppID string `json:"app_id,omitempty"` + + // version id of the app + VersionID string `json:"version_id,omitempty"` +} + +type AppVersion struct { + + // active or not + Active bool `json:"active,omitempty"` + + // app id + AppId string `json:"app_id,omitempty"` + + // the time when app version create + CreateTime *strfmt.DateTime `json:"create_time,omitempty"` + + // description of app of specific version + Description string `json:"description,omitempty"` + + // home of app of specific version + Home string `json:"home,omitempty"` + + // icon of app of specific version + Icon string `json:"icon,omitempty"` + + // keywords of app of specific version + Keywords string `json:"keywords,omitempty"` + + // maintainers of app of specific version + Maintainers string `json:"maintainers,omitempty"` + + // message path of app of specific version + Message string `json:"message,omitempty"` + + // version name + Name string `json:"name,omitempty"` + + // owner + Owner string `json:"owner,omitempty"` + + // package name of app of specific version + PackageName string `json:"package_name,omitempty"` + + // readme of app of specific version + Readme string `json:"readme,omitempty"` + + // review id of app of specific version + ReviewId string `json:"review_id,omitempty"` + + // screenshots of app of specific version + Screenshots string `json:"screenshots,omitempty"` + + // sequence of app of specific version + Sequence int64 `json:"sequence,omitempty"` + + // sources of app of specific version + Sources string `json:"sources,omitempty"` + + // status of app of specific version eg.[draft|submitted|passed|rejected|active|in-review|deleted|suspended] + Status string `json:"status,omitempty"` + + // record status changed time + StatusTime *strfmt.DateTime `json:"status_time,omitempty"` + + // type of app of specific version + Type string `json:"type,omitempty"` + + // the time when app version update + UpdateTime *strfmt.DateTime `json:"update_time,omitempty"` + + // version id of app + VersionId string `json:"version_id,omitempty"` + + ClusterTotal *int `json:"cluster_total,omitempty"` +} + +type CreateAppVersionResponse struct { + + // version id + VersionId string `json:"version_id,omitempty"` +} + +type ValidatePackageResponse struct { + + // app description + Description string `json:"description,omitempty"` + + // error eg.[json error] + Error string `json:"error,omitempty"` + + // filename map to detail + ErrorDetails map[string]string `json:"error_details,omitempty"` + + // app name eg.[wordpress|mysql|...] + Name string `json:"name,omitempty"` + + // app url + URL string `json:"url,omitempty"` + + // app version name.eg.[0.1.0] + VersionName string `json:"version_name,omitempty"` +} + +type CreateAppVersionRequest struct { + + // required, id of app to create new version + AppId string `json:"app_id,omitempty"` + + // description of app of specific version + Description string `json:"description,omitempty"` + + // required, version name eg.[0.1.0|0.1.3|...] + Name string `json:"name,omitempty"` + + // package of app of specific version + Package strfmt.Base64 `json:"package,omitempty"` + + // optional: vmbased/helm + Type string `json:"type,omitempty"` +} + +type GetAppVersionFilesRequest struct { + Files []string `json:"files,omitempty"` +} + +type ActionRequest struct { + Action string `json:"action"` + Message string `json:"message,omitempty"` + Username string `json:"-"` +} + +type Attachment struct { + + // filename map to content + AttachmentContent map[string]strfmt.Base64 `json:"attachment_content,omitempty"` + + // attachment id + AttachmentID string `json:"attachment_id,omitempty"` + + // the time attachment create + CreateTime *strfmt.DateTime `json:"create_time,omitempty"` +} + +type CreateCategoryRequest struct { + + // category description + Description string `json:"description,omitempty"` + + // category icon + Icon strfmt.Base64 `json:"icon,omitempty"` + + // the i18n of this category, json format, sample: {"zh_cn": "数据库", "en": "database"} + Locale string `json:"locale,omitempty"` + + // required, category name + Name string `json:"name,omitempty"` +} +type ModifyCategoryRequest struct { + // category description + Description *string `json:"description,omitempty"` + + // category icon + Icon []byte `json:"icon,omitempty"` + + // the i18n of this category, json format, sample: {"zh_cn": "数据库", "en": "database"} + Locale *string `json:"locale,omitempty"` + + // category name + Name *string `json:"name,omitempty"` +} + +type AppCategorySet []*ResourceCategory + +type ResourceCategory struct { + + // category id + CategoryId string `json:"category_id,omitempty"` + + // create time + CreateTime *strfmt.DateTime `json:"create_time,omitempty"` + + // locale + Locale string `json:"locale,omitempty"` + + // name + Name string `json:"name,omitempty"` + + // status + Status string `json:"status,omitempty"` + + // status time + StatusTime *strfmt.DateTime `json:"status_time,omitempty"` +} + +type Category struct { + + // category id + CategoryID string `json:"category_id,omitempty"` + + // the time when category create + CreateTime *strfmt.DateTime `json:"create_time,omitempty"` + + // category description + Description string `json:"description,omitempty"` + + // category icon + Icon string `json:"icon,omitempty"` + + // the i18n of this category, json format, sample: {"zh_cn": "数据库", "en": "database"} + Locale string `json:"locale,omitempty"` + + // category name,app belong to a category,eg.[AI|Firewall|cache|...] + Name string `json:"name,omitempty"` + + // owner + Owner string `json:"owner,omitempty"` + + AppTotal *int `json:"app_total,omitempty"` + + // the time when category update + UpdateTime *strfmt.DateTime `json:"update_time,omitempty"` +} + +type CreateCategoryResponse struct { + + // id of category created + CategoryId string `json:"category_id,omitempty"` +} + +type RepoEvent struct { + // repository event create time + CreateTime *strfmt.DateTime `json:"create_time,omitempty"` + + // owner + Owner string `json:"owner,omitempty"` + + // repository event id + RepoEventId string `json:"repo_event_id,omitempty"` + + // repository id + RepoId string `json:"repo_id,omitempty"` + + // result + Result string `json:"result,omitempty"` + + // repository event status eg.[failed|successful|working|pending] + Status string `json:"status,omitempty"` + + // record status changed time + StatusTime *strfmt.DateTime `json:"status_time,omitempty"` +} + +type CreateRepoRequest struct { + // required app default status.eg:[draft|active] + AppDefaultStatus string `json:"app_default_status,omitempty"` + + // category id + CategoryId string `json:"category_id,omitempty"` + + // required, credential of visiting the repository + Credential string `json:"credential,omitempty"` + + // repository description + Description string `json:"description,omitempty"` + + // workspace + Workspace *string `json:"workspace,omitempty"` + + // required, repository name + Name string `json:"name,omitempty"` + + // required, runtime provider eg.[qingcloud|aliyun|aws|kubernetes] + Providers []string `json:"providers"` + + // repository type + Type string `json:"type,omitempty"` + + // required, url of visiting the repository + URL string `json:"url,omitempty"` + + // required, visibility eg:[public|private] + Visibility string `json:"visibility,omitempty"` +} + +type RepoCategorySet []*ResourceCategory + +type ModifyRepoRequest struct { + // app default status eg:[draft|active] + AppDefaultStatus *string `json:"app_default_status,omitempty"` + + // category id + CategoryID *string `json:"category_id,omitempty"` + + // credential of visiting the repository + Credential *string `json:"credential,omitempty"` + + // repository description + Description *string `json:"description,omitempty"` + + Workspace *string `json:"workspace,omitempty"` + + // repository name + Name *string `json:"name,omitempty"` + + // runtime provider eg.[qingcloud|aliyun|aws|kubernetes] + Providers []string `json:"providers"` + + // repository type + Type *string `json:"type,omitempty"` + + // url of visiting the repository + URL *string `json:"url,omitempty"` + + // visibility eg:[public|private] + Visibility *string `json:"visibility,omitempty"` +} + +type RepoActionRequest struct { + Action string `json:"action"` +} + +type ValidateRepoRequest struct { + Type string `json:"type"` + Credential string `json:"credential"` + Url string `json:"url"` +} + +type RepoSelector struct { + // the time when repository selector create + CreateTime *strfmt.DateTime `json:"create_time,omitempty"` + + // selector key + SelectorKey string `json:"selector_key,omitempty"` + + // selector value + SelectorValue string `json:"selector_value,omitempty"` +} +type RepoLabel struct { + // the time when repository label create + CreateTime *strfmt.DateTime `json:"create_time,omitempty"` + + // label key + LabelKey string `json:"label_key,omitempty"` + + // label value + LabelValue string `json:"label_value,omitempty"` +} + +type RepoLabels []*RepoLabel +type RepoSelectors []*RepoSelector + +type Repo struct { + // app default status eg[active|draft] + AppDefaultStatus string `json:"app_default_status,omitempty"` + + // category set + CategorySet RepoCategorySet `json:"category_set"` + + // controller, value 0 for self resource, value 1 for openpitrix resource + Controller int32 `json:"controller,omitempty"` + + // the time when repository create + CreateTime *strfmt.DateTime `json:"create_time,omitempty"` + + // credential of visiting the repository + Credential string `json:"credential,omitempty"` + + // repository description + Description string `json:"description,omitempty"` + + // labels + Labels RepoLabels `json:"labels"` + + // repository name + Name string `json:"name,omitempty"` + + // owner + Owner string `json:"owner,omitempty"` + + // runtime provider eg.[qingcloud|aliyun|aws|kubernetes] + Providers []string `json:"providers"` + + // repository id + RepoId string `json:"repo_id,omitempty"` + + // selectors + Selectors RepoSelectors `json:"selectors"` + + // status eg.[active|deleted] + Status string `json:"status,omitempty"` + + // record status changed time + StatusTime strfmt.DateTime `json:"status_time,omitempty"` + + // type of repository eg.[http|https|s3] + Type string `json:"type,omitempty"` + + // url of visiting the repository + URL string `json:"url,omitempty"` + + // visibility.eg:[public|private] + Visibility string `json:"visibility,omitempty"` +} + +type CreateRepoResponse struct { + + // id of repository created + RepoID string `json:"repo_id,omitempty"` +} + +type ValidateRepoResponse struct { + + // if validate error,return error code + ErrorCode int64 `json:"errorCode,omitempty"` + + // validate repository ok or not + Ok bool `json:"ok,omitempty"` +} + +type CreateClusterRequest struct { + + // advanced param + AdvancedParam []string `json:"advanced_param"` + + // required, id of app to run in cluster + AppId string `json:"app_id,omitempty"` + + // required, conf a json string, include cpu, memory info of cluster + Conf string `json:"conf,omitempty"` + + // required, id of runtime + RuntimeId string `json:"runtime_id,omitempty"` + + // required, id of app version + VersionId string `json:"version_id,omitempty"` + + Username string `json:"-"` +} + +type Cluster struct { + + // additional info + AdditionalInfo string `json:"additional_info,omitempty"` + + // id of app run in cluster + AppId string `json:"app_id,omitempty"` + + //// cluster common set + //ClusterCommonSet OpenpitrixClusterClusterCommonSet `json:"cluster_common_set"` + + // cluster id + ClusterId string `json:"cluster_id,omitempty"` + + // cluster type, frontgate or normal cluster + ClusterType int64 `json:"cluster_type,omitempty"` + + // the time when cluster create + CreateTime *strfmt.DateTime `json:"create_time,omitempty"` + + // cluster used to debug or not + Debug bool `json:"debug,omitempty"` + + // cluster description + Description string `json:"description,omitempty"` + + // endpoint of cluster + Endpoints string `json:"endpoints,omitempty"` + + // cluster env + Env string `json:"env,omitempty"` + + // frontgate id, a proxy for vpc to communicate + FrontgateId string `json:"frontgate_id,omitempty"` + + // global uuid + GlobalUUID string `json:"global_uuid,omitempty"` + + // metadata root access + MetadataRootAccess bool `json:"metadata_root_access,omitempty"` + + // cluster name + Name string `json:"name,omitempty"` + + // owner + Owner string `json:"owner,omitempty"` + + // cluster runtime id + RuntimeId string `json:"runtime_id,omitempty"` + + // cluster status eg.[active|used|enabled|disabled|deleted|stopped|ceased] + Status string `json:"status,omitempty"` + + // record status changed time + StatusTime *strfmt.DateTime `json:"status_time,omitempty"` + + // subnet id, cluster run in a subnet + SubnetId string `json:"subnet_id,omitempty"` + + // cluster transition status eg.[creating|deleting|upgrading|updating|rollbacking|stopping|starting|recovering|ceasing|resizing|scaling] + TransitionStatus string `json:"transition_status,omitempty"` + + // upgrade status, unused + UpgradeStatus string `json:"upgrade_status,omitempty"` + + // cluster upgraded time + UpgradeTime *strfmt.DateTime `json:"upgrade_time,omitempty"` + + // id of version of app run in cluster + VersionId string `json:"version_id,omitempty"` + + // vpc id, a vpc contain one more subnet + VpcId string `json:"vpc_id,omitempty"` + + // zone of cluster eg.[pek3a|pek3b] + Zone string `json:"zone,omitempty"` +} + +type Runtime struct { + // runtime id + RuntimeId string `protobuf:"bytes,1,opt,name=runtime_id,json=runtimeId,proto3" json:"runtime_id,omitempty"` + // runtime name,create by owner. + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // runtime description + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` +} + +type ModifyClusterAttributesRequest struct { + + // required, id of cluster to modify + ClusterID string `json:"cluster_id,omitempty"` + + // cluster description + Description *string `json:"description,omitempty"` + + // cluster name + Name *string `json:"name,omitempty"` +} diff --git a/pkg/models/resources/resources.go b/pkg/models/resources/resources.go index 85aa9d9cc..05af020fb 100644 --- a/pkg/models/resources/resources.go +++ b/pkg/models/resources/resources.go @@ -121,12 +121,12 @@ func GetResource(namespace, resource, name string) (interface{}, error) { if searcher, ok := resources[resource]; ok { resource, err := searcher.get(namespace, name) if err != nil { - klog.Errorln("get resource", namespace, resource, name, err) + klog.Errorf("resource %s.%s.%s not found: %s", namespace, resource, name, err) return nil, err } return resource, nil } - return nil, fmt.Errorf("resource %s not found", resource) + return nil, fmt.Errorf("resource %s.%s.%s not found", namespace, resource, name) } func ListResources(namespace, resource string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { @@ -136,19 +136,21 @@ func ListResources(namespace, resource string, conditions *params.Conditions, or // none namespace resource if namespace != "" && sliceutil.HasString(clusterResources, resource) { - klog.Errorln("resources not found", resource) - return nil, fmt.Errorf("not found") + err = fmt.Errorf("namespaced resource %s not found", resource) + klog.Errorln(err) + return nil, err } if searcher, ok := resources[resource]; ok { result, err = searcher.search(namespace, conditions, orderBy, reverse) } else { - klog.Errorln("resources not found", resource) - return nil, fmt.Errorf("not found") + err = fmt.Errorf("namespaced resource %s not found", resource) + klog.Errorln(err) + return nil, err } if err != nil { - klog.Errorln("resources search", err) + klog.Errorln(err) return nil, err } diff --git a/pkg/server/config/config.go b/pkg/server/config/config.go index 4e593f278..aabec075b 100644 --- a/pkg/server/config/config.go +++ b/pkg/server/config/config.go @@ -250,7 +250,7 @@ func (c *Config) stripEmptyOptions() { c.LdapOptions = nil } - if c.OpenPitrixOptions != nil && c.OpenPitrixOptions.APIServer == "" { + if c.OpenPitrixOptions != nil && len(c.OpenPitrixOptions.Validate()) > 0 { c.OpenPitrixOptions = nil } diff --git a/pkg/server/config/config_test.go b/pkg/server/config/config_test.go index f5d837232..611236b78 100644 --- a/pkg/server/config/config_test.go +++ b/pkg/server/config/config_test.go @@ -6,7 +6,7 @@ import ( "io/ioutil" "kubesphere.io/kubesphere/pkg/simple/client/alerting" "kubesphere.io/kubesphere/pkg/simple/client/devops" - esclient "kubesphere.io/kubesphere/pkg/simple/client/elasticsearch" + "kubesphere.io/kubesphere/pkg/simple/client/elasticsearch" "kubesphere.io/kubesphere/pkg/simple/client/k8s" "kubesphere.io/kubesphere/pkg/simple/client/kubesphere" "kubesphere.io/kubesphere/pkg/simple/client/ldap" @@ -79,8 +79,12 @@ func newTestConfig() *Config { Bucket: "ssss", }, OpenPitrixOptions: &openpitrix.OpenPitrixOptions{ - APIServer: "http://api-gateway.openpitrix-system.svc", - Token: "ABCDEFGHIJKLMN", + RuntimeManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9103", + ClusterManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9104", + RepoManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9101", + AppManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9102", + CategoryManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9113", + AttachmentManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9122", }, MonitoringOptions: &prometheus.PrometheusOptions{ Endpoint: "http://prometheus.kubesphere-monitoring-system.svc", diff --git a/pkg/server/errors/errors.go b/pkg/server/errors/errors.go index 437bdfb62..80daea632 100644 --- a/pkg/server/errors/errors.go +++ b/pkg/server/errors/errors.go @@ -18,8 +18,6 @@ package errors import ( - "encoding/json" - "errors" "github.com/emicklei/go-restful" "net/http" ) @@ -42,20 +40,6 @@ func New(message string) Error { return Error{Message: message} } -func Parse(data []byte) error { - var j map[string]string - err := json.Unmarshal(data, &j) - if err != nil { - return errors.New(string(data)) - } else if message := j["message"]; message != "" { - return errors.New(message) - } else if message := j["Error"]; message != "" { - return errors.New(message) - } else { - return errors.New(string(data)) - } -} - func ParseSvcErr(err error, resp *restful.Response) { if svcErr, ok := err.(restful.ServiceError); ok { resp.WriteServiceError(svcErr.Code, svcErr) diff --git a/pkg/simple/client/factory.go b/pkg/simple/client/factory.go index a605f29a5..3d9f273a9 100644 --- a/pkg/simple/client/factory.go +++ b/pkg/simple/client/factory.go @@ -302,7 +302,14 @@ func (cs *ClientSet) S3() (*s2is3.S3Client, error) { func (cs *ClientSet) OpenPitrix() (*openpitrix.OpenPitrixClient, error) { var err error - if cs.csoptions.openPitrixOptions == nil || cs.csoptions.openPitrixOptions.APIServer == "" { + if cs.csoptions.openPitrixOptions == nil || + cs.csoptions.openPitrixOptions.RepoManagerEndpoint == "" || + cs.csoptions.openPitrixOptions.RuntimeManagerEndpoint == "" || + cs.csoptions.openPitrixOptions.ClusterManagerEndpoint == "" || + cs.csoptions.openPitrixOptions.AppManagerEndpoint == "" || + cs.csoptions.openPitrixOptions.AttachmentManagerEndpoint == "" || + cs.csoptions.openPitrixOptions.RepoIndexerEndpoint == "" || + cs.csoptions.openPitrixOptions.CategoryManagerEndpoint == "" { return nil, ClientSetNotEnabledError{} } diff --git a/pkg/simple/client/openpitrix/applications.go b/pkg/simple/client/openpitrix/applications.go deleted file mode 100644 index a959aeef9..000000000 --- a/pkg/simple/client/openpitrix/applications.go +++ /dev/null @@ -1,258 +0,0 @@ -/* - - Copyright 2019 The KubeSphere Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -package openpitrix - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "k8s.io/klog" - "net/http" - "strconv" - "strings" -) - -const ( - Unknown = "-" - DeploySuffix = "-Deployment" - DaemonSuffix = "-DaemonSet" - StateSuffix = "-StatefulSet" -) - -func (c *OpenPitrixClient) GetAppInfo(appId string) (string, string, string, error) { - url := fmt.Sprintf("%s/v1/apps?app_id=%s", c.apiServer, appId) - resp, err := c.makeHttpRequest("GET", url, "") - if err != nil { - klog.Error(err) - return Unknown, Unknown, Unknown, err - } - - var apps appList - err = json.Unmarshal(resp, &apps) - if err != nil { - klog.Error(err) - return Unknown, Unknown, Unknown, err - } - - if len(apps.Apps) == 0 { - return Unknown, Unknown, Unknown, err - } - - return apps.Apps[0].ChartName, apps.Apps[0].RepoId, apps.Apps[0].AppId, nil -} - -func (c *OpenPitrixClient) GetCluster(clusterId string) (*Cluster, error) { - url := fmt.Sprintf("%s/v1/clusters?cluster_id=%s", c.apiServer, clusterId) - - resp, err := c.makeHttpRequest("GET", url, "") - if err != nil { - klog.Error(err) - return nil, err - } - - var clusterList ClusterList - err = json.Unmarshal(resp, &clusterList) - - if err != nil { - klog.Error(err) - return nil, err - } - - if len(clusterList.Clusters) == 0 { - return nil, fmt.Errorf("NotFound, clusterId:%s", clusterId) - } - - return &clusterList.Clusters[0], nil -} - -func (c *OpenPitrixClient) ListClusters(runtimeId, searchWord, status string, limit, offset int) (*ClusterList, error) { - - defaultStatus := "status=active&status=stopped&status=pending&status=ceased" - - url := fmt.Sprintf("%s/v1/clusters?limit=%s&offset=%s", c.apiServer, strconv.Itoa(limit), strconv.Itoa(offset)) - - if searchWord != "" { - url = fmt.Sprintf("%s&search_word=%s", url, searchWord) - } - - if status != "" { - url = fmt.Sprintf("%s&status=%s", url, status) - } else { - url = fmt.Sprintf("%s&%s", url, defaultStatus) - } - - if len(runtimeId) > 0 { - url = fmt.Sprintf("%s&runtime_id=%s", url, runtimeId) - } - - resp, err := c.makeHttpRequest("GET", url, "") - if err != nil { - klog.Errorf("request %s failed, reason: %s", url, err) - return nil, err - } - - var clusterList ClusterList - err = json.Unmarshal(resp, &clusterList) - - if err != nil { - return nil, err - } - - return &clusterList, nil -} - -func (c *OpenPitrixClient) GetRepo(repoId string) (string, error) { - url := fmt.Sprintf("%s/v1/repos?repo_id=%s", c.apiServer, repoId) - resp, err := c.makeHttpRequest("GET", url, "") - if err != nil { - klog.Error(err) - return Unknown, err - } - - var repos repoList - err = json.Unmarshal(resp, &repos) - if err != nil { - klog.Error(err) - return Unknown, err - } - - if len(repos.Repos) == 0 { - return Unknown, err - } - - return repos.Repos[0].Name, nil -} - -func (c *OpenPitrixClient) GetVersion(versionId string) (string, error) { - versionUrl := fmt.Sprintf("%s/v1/app_versions?version_id=%s", c.apiServer, versionId) - resp, err := c.makeHttpRequest("GET", versionUrl, "") - if err != nil { - klog.Error(err) - return Unknown, err - } - - var versions VersionList - err = json.Unmarshal(resp, &versions) - if err != nil { - klog.Error(err) - return Unknown, err - } - - if len(versions.Versions) == 0 { - return Unknown, nil - } - return versions.Versions[0].Name, nil -} - -func (c *OpenPitrixClient) GetRuntime(runtimeId string) (string, error) { - - versionUrl := fmt.Sprintf("%s/v1/runtimes?runtime_id=%s", c.apiServer, runtimeId) - resp, err := c.makeHttpRequest("GET", versionUrl, "") - if err != nil { - klog.Error(err) - return Unknown, err - } - - var runtimes runtimeList - err = json.Unmarshal(resp, &runtimes) - if err != nil { - klog.Error(err) - return Unknown, err - } - - if len(runtimes.Runtimes) == 0 { - return Unknown, nil - } - - return runtimes.Runtimes[0].Zone, nil -} - -func (c *OpenPitrixClient) CreateCluster(request CreateClusterRequest) error { - - versionUrl := fmt.Sprintf("%s/v1/clusters/create", c.apiServer) - - data, err := json.Marshal(request) - - if err != nil { - klog.Error(err) - return err - } - - data, err = c.makeHttpRequest("POST", versionUrl, string(data)) - - if err != nil { - klog.Error(err) - return err - } - - return nil -} - -func (c *OpenPitrixClient) DeleteCluster(request DeleteClusterRequest) error { - - versionUrl := fmt.Sprintf("%s/v1/clusters/delete", c.apiServer) - - data, err := json.Marshal(request) - - if err != nil { - klog.Error(err) - return err - } - - data, err = c.makeHttpRequest("POST", versionUrl, string(data)) - - if err != nil { - klog.Error(err) - return err - } - - return nil -} - -func (c *OpenPitrixClient) makeHttpRequest(method, url, data string) ([]byte, error) { - var req *http.Request - - var err error - if method == "GET" { - req, err = http.NewRequest(method, url, nil) - } else { - req, err = http.NewRequest(method, url, strings.NewReader(data)) - } - - req.Header.Add("Authorization", c.token) - - if err != nil { - klog.Error(err) - return nil, err - } - - resp, err := http.DefaultClient.Do(req) - - if err != nil { - err := fmt.Errorf("Request to %s failed, method: %s,token: %s, reason: %s ", url, method, c.apiServer, err) - klog.Error(err) - return nil, err - } - - body, err := ioutil.ReadAll(resp.Body) - defer resp.Body.Close() - if resp.StatusCode >= http.StatusBadRequest { - err = fmt.Errorf(string(body)) - } - return body, err -} diff --git a/pkg/simple/client/openpitrix/openpitrix.go b/pkg/simple/client/openpitrix/openpitrix.go deleted file mode 100644 index 988e7fb77..000000000 --- a/pkg/simple/client/openpitrix/openpitrix.go +++ /dev/null @@ -1,227 +0,0 @@ -/* - - Copyright 2019 The KubeSphere Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -package openpitrix - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "net/http" - "strings" - "time" -) - -func NewOpenPitrixClient(options *OpenPitrixOptions) (*OpenPitrixClient, error) { - return &OpenPitrixClient{ - client: &http.Client{ - Timeout: time.Duration(3) * time.Second, - }, - apiServer: options.APIServer, - token: options.Token, - }, nil -} - -func (c *OpenPitrixClient) CreateRuntime(runtime *RunTime) error { - - data, err := json.Marshal(runtime) - if err != nil { - return err - } - - req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/v1/runtimes", c.apiServer), bytes.NewReader(data)) - - if err != nil { - return err - } - req.Header.Add("Content-Type", "application/json") - req.Header.Add("Authorization", c.token) - - resp, err := c.client.Do(req) - - if err != nil { - klog.Error(err) - return err - } - defer resp.Body.Close() - data, err = ioutil.ReadAll(resp.Body) - - if err != nil { - klog.Error(err) - return err - } - - if resp.StatusCode > http.StatusOK { - err = Error{resp.StatusCode, string(data)} - klog.Error(err) - return err - } - - return nil -} - -func (c *OpenPitrixClient) deleteClusters(clusters []cluster) error { - clusterId := make([]string, 0) - - for _, cluster := range clusters { - if cluster.Status != "deleted" && cluster.Status != "deleting" && !sliceutil.HasString(clusterId, cluster.ClusterId) { - clusterId = append(clusterId, cluster.ClusterId) - } - } - - if len(clusterId) == 0 { - return nil - } - - deleteRequest := struct { - ClusterId []string `json:"cluster_id"` - }{ - ClusterId: clusterId, - } - data, _ := json.Marshal(deleteRequest) - req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/v1/clusters/delete", c.apiServer), bytes.NewReader(data)) - - if err != nil { - return err - } - req.Header.Add("Authorization", c.token) - - resp, err := c.client.Do(req) - if err != nil { - klog.Error(err) - return err - } - - defer resp.Body.Close() - data, err = ioutil.ReadAll(resp.Body) - - if err != nil { - klog.Error(err) - return err - } - - if resp.StatusCode > http.StatusOK { - err = Error{resp.StatusCode, string(data)} - klog.Error(err) - return err - } - - return nil -} - -func (c *OpenPitrixClient) listClusters(runtimeId string) ([]cluster, error) { - limit := 200 - offset := 0 - clusters := make([]cluster, 0) - for { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v1/clusters?runtime_id=%s&limit=%d&offset=%d", c.apiServer, runtimeId, limit, offset), nil) - - if err != nil { - klog.Error(err) - return nil, err - } - - req.Header.Add("Authorization", c.token) - - resp, err := c.client.Do(req) - - if err != nil { - klog.Error(err) - return nil, err - } - - data, err := ioutil.ReadAll(resp.Body) - - if err != nil { - klog.Error(err) - return nil, err - } - - resp.Body.Close() - - if resp.StatusCode > http.StatusOK { - err = Error{resp.StatusCode, string(data)} - klog.Error(err) - return nil, err - } - listClusterResponse := struct { - TotalCount int `json:"total_count"` - ClusterSet []cluster `json:"cluster_set"` - }{} - err = json.Unmarshal(data, &listClusterResponse) - - if err != nil { - klog.Error(err) - return nil, err - } - - clusters = append(clusters, listClusterResponse.ClusterSet...) - - if listClusterResponse.TotalCount <= limit+offset { - break - } - - offset += limit - } - - return clusters, nil -} - -func (c *OpenPitrixClient) DeleteRuntime(runtimeId string) error { - clusters, err := c.listClusters(runtimeId) - - if err != nil { - klog.Error(err) - return err - } - - err = c.deleteClusters(clusters) - - if err != nil { - klog.Error(err) - return err - } - - return nil -} - -func IsNotFound(err error) bool { - if e, ok := err.(Error); ok { - if e.status == http.StatusNotFound { - return true - } - if strings.Contains(e.message, "not exist") { - return true - } - if strings.Contains(e.message, "not found") { - return true - } - } - return false -} - -func IsDeleted(err error) bool { - if e, ok := err.(Error); ok { - if strings.Contains(e.message, "is [deleted]") { - return true - } - } - return false -} diff --git a/pkg/simple/client/openpitrix/openpitrixclient.go b/pkg/simple/client/openpitrix/openpitrixclient.go new file mode 100644 index 000000000..0e8f4a32c --- /dev/null +++ b/pkg/simple/client/openpitrix/openpitrixclient.go @@ -0,0 +1,243 @@ +/* + + Copyright 2019 The KubeSphere Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +package openpitrix + +import ( + "context" + "fmt" + "k8s.io/klog" + "openpitrix.io/openpitrix/pkg/manager" + "openpitrix.io/openpitrix/pkg/pb" + "openpitrix.io/openpitrix/pkg/sender" + "openpitrix.io/openpitrix/pkg/util/ctxutil" + "strconv" + "strings" +) + +const ( + KubernetesProvider = "kubernetes" + Unknown = "-" + DeploySuffix = "-Deployment" + DaemonSuffix = "-DaemonSet" + StateSuffix = "-StatefulSet" + SystemUsername = "system" + SystemUserPath = ":system" +) + +type OpenPitrixClient struct { + runtime pb.RuntimeManagerClient + cluster pb.ClusterManagerClient + app pb.AppManagerClient + repo pb.RepoManagerClient + category pb.CategoryManagerClient + attachment pb.AttachmentManagerClient + repoIndexer pb.RepoIndexerClient +} + +func parseToHostPort(endpoint string) (string, int, error) { + args := strings.Split(endpoint, ":") + if len(args) != 2 { + return "", 0, fmt.Errorf("invalid server host: %s", endpoint) + } + host := args[0] + port, err := strconv.Atoi(args[1]) + if err != nil { + return "", 0, err + } + return host, port, nil +} + +func newRuntimeManagerClient(endpoint string) (pb.RuntimeManagerClient, error) { + host, port, err := parseToHostPort(endpoint) + conn, err := manager.NewClient(host, port) + if err != nil { + return nil, err + } + return pb.NewRuntimeManagerClient(conn), nil +} +func newClusterManagerClient(endpoint string) (pb.ClusterManagerClient, error) { + host, port, err := parseToHostPort(endpoint) + conn, err := manager.NewClient(host, port) + if err != nil { + return nil, err + } + return pb.NewClusterManagerClient(conn), nil +} +func newCategoryManagerClient(endpoint string) (pb.CategoryManagerClient, error) { + host, port, err := parseToHostPort(endpoint) + conn, err := manager.NewClient(host, port) + if err != nil { + return nil, err + } + return pb.NewCategoryManagerClient(conn), nil +} + +func newAttachmentManagerClient(endpoint string) (pb.AttachmentManagerClient, error) { + host, port, err := parseToHostPort(endpoint) + conn, err := manager.NewClient(host, port) + if err != nil { + return nil, err + } + return pb.NewAttachmentManagerClient(conn), nil +} + +func newRepoManagerClient(endpoint string) (pb.RepoManagerClient, error) { + host, port, err := parseToHostPort(endpoint) + conn, err := manager.NewClient(host, port) + if err != nil { + return nil, err + } + return pb.NewRepoManagerClient(conn), nil +} + +func newRepoIndexer(endpoint string) (pb.RepoIndexerClient, error) { + host, port, err := parseToHostPort(endpoint) + conn, err := manager.NewClient(host, port) + if err != nil { + return nil, err + } + return pb.NewRepoIndexerClient(conn), nil +} + +func newAppManagerClient(endpoint string) (pb.AppManagerClient, error) { + host, port, err := parseToHostPort(endpoint) + conn, err := manager.NewClient(host, port) + if err != nil { + return nil, err + } + return pb.NewAppManagerClient(conn), nil +} + +func NewOpenPitrixClient(options *OpenPitrixOptions) (*OpenPitrixClient, error) { + + runtimeMangerClient, err := newRuntimeManagerClient(options.RuntimeManagerEndpoint) + + if err != nil { + klog.Error(err) + return nil, err + } + + clusterManagerClient, err := newClusterManagerClient(options.ClusterManagerEndpoint) + + if err != nil { + klog.Error(err) + return nil, err + } + + repoManagerClient, err := newRepoManagerClient(options.RepoManagerEndpoint) + + if err != nil { + klog.Error(err) + return nil, err + } + + repoIndexerClient, err := newRepoIndexer(options.RepoIndexerEndpoint) + + if err != nil { + klog.Error(err) + return nil, err + } + + appManagerClient, err := newAppManagerClient(options.AppManagerEndpoint) + + if err != nil { + klog.Error(err) + return nil, err + } + + categoryManagerClient, err := newCategoryManagerClient(options.CategoryManagerEndpoint) + + if err != nil { + klog.Error(err) + return nil, err + } + + attachmentManagerClient, err := newAttachmentManagerClient(options.AttachmentManagerEndpoint) + + if err != nil { + klog.Error(err) + return nil, err + } + + client := OpenPitrixClient{ + runtime: runtimeMangerClient, + cluster: clusterManagerClient, + repo: repoManagerClient, + app: appManagerClient, + category: categoryManagerClient, + attachment: attachmentManagerClient, + repoIndexer: repoIndexerClient, + } + + return &client, nil +} +func (c *OpenPitrixClient) Runtime() pb.RuntimeManagerClient { + return c.runtime +} +func (c *OpenPitrixClient) App() pb.AppManagerClient { + return c.app +} +func (c *OpenPitrixClient) Cluster() pb.ClusterManagerClient { + return c.cluster +} +func (c *OpenPitrixClient) Category() pb.CategoryManagerClient { + return c.category +} + +func (c *OpenPitrixClient) Repo() pb.RepoManagerClient { + return c.repo +} + +func (c *OpenPitrixClient) RepoIndexer() pb.RepoIndexerClient { + return c.repoIndexer +} + +func (c *OpenPitrixClient) Attachment() pb.AttachmentManagerClient { + return c.attachment +} + +func SystemContext() context.Context { + ctx := context.Background() + ctx = ctxutil.ContextWithSender(ctx, sender.New(SystemUsername, SystemUserPath, "")) + return ctx +} +func ContextWithUsername(username string) context.Context { + ctx := context.Background() + if username == "" { + username = SystemUsername + } + ctx = ctxutil.ContextWithSender(ctx, sender.New(username, SystemUserPath, "")) + return ctx +} + +func IsNotFound(err error) bool { + if strings.Contains(err.Error(), "not exist") { + return true + } + if strings.Contains(err.Error(), "not found") { + return true + } + return false +} + +func IsDeleted(err error) bool { + if strings.Contains(err.Error(), "is [deleted]") { + return true + } + return false +} diff --git a/pkg/simple/client/openpitrix/options.go b/pkg/simple/client/openpitrix/options.go index 0c321504e..9a6eb20e5 100644 --- a/pkg/simple/client/openpitrix/options.go +++ b/pkg/simple/client/openpitrix/options.go @@ -7,29 +7,72 @@ import ( ) type OpenPitrixOptions struct { - APIServer string `json:"apiServer,omitempty" yaml:"apiServer"` - Token string `json:"token,omitempty" yaml:"token"` + RuntimeManagerEndpoint string `json:"runtimeManagerEndpoint,omitempty" yaml:"runtimeManagerEndpoint,omitempty"` + ClusterManagerEndpoint string `json:"clusterManagerEndpoint,omitempty" yaml:"clusterManagerEndpoint,omitempty"` + RepoManagerEndpoint string `json:"repoManagerEndpoint,omitempty" yaml:"repoManagerEndpoint,omitempty"` + AppManagerEndpoint string `json:"appManagerEndpoint,omitempty" yaml:"appManagerEndpoint,omitempty"` + CategoryManagerEndpoint string `json:"categoryManagerEndpoint,omitempty" yaml:"categoryManagerEndpoint,omitempty"` + AttachmentManagerEndpoint string `json:"attachmentManagerEndpoint,omitempty" yaml:"attachmentManagerEndpoint,omitempty"` + RepoIndexerEndpoint string `json:"repoIndexerEndpoint,omitempty" yaml:"repoIndexerEndpoint,omitempty"` } func NewOpenPitrixOptions() *OpenPitrixOptions { - return &OpenPitrixOptions{ - APIServer: "", - Token: "", - } + return &OpenPitrixOptions{} } func (s *OpenPitrixOptions) ApplyTo(options *OpenPitrixOptions) { - if s.APIServer != "" { + if options == nil { + options = s + return + } + if s.RuntimeManagerEndpoint != "" { reflectutils.Override(options, s) } } func (s *OpenPitrixOptions) Validate() []error { - errs := []error{} + var errs []error - if s.APIServer != "" { - if s.Token == "" { - errs = append(errs, fmt.Errorf("OpenPitrix access token cannot be empty")) + if s.RuntimeManagerEndpoint != "" { + _, _, err := parseToHostPort(s.RuntimeManagerEndpoint) + if err != nil { + errs = append(errs, fmt.Errorf("invalid host port:%s", s.RuntimeManagerEndpoint)) + } + } + if s.ClusterManagerEndpoint != "" { + _, _, err := parseToHostPort(s.ClusterManagerEndpoint) + if err != nil { + errs = append(errs, fmt.Errorf("invalid host port:%s", s.ClusterManagerEndpoint)) + } + } + if s.RepoManagerEndpoint != "" { + _, _, err := parseToHostPort(s.RepoManagerEndpoint) + if err != nil { + errs = append(errs, fmt.Errorf("invalid host port:%s", s.RepoManagerEndpoint)) + } + } + if s.RepoIndexerEndpoint != "" { + _, _, err := parseToHostPort(s.RepoIndexerEndpoint) + if err != nil { + errs = append(errs, fmt.Errorf("invalid host port:%s", s.RepoIndexerEndpoint)) + } + } + if s.AppManagerEndpoint != "" { + _, _, err := parseToHostPort(s.AppManagerEndpoint) + if err != nil { + errs = append(errs, fmt.Errorf("invalid host port:%s", s.AppManagerEndpoint)) + } + } + if s.CategoryManagerEndpoint != "" { + _, _, err := parseToHostPort(s.CategoryManagerEndpoint) + if err != nil { + errs = append(errs, fmt.Errorf("invalid host port:%s", s.CategoryManagerEndpoint)) + } + } + if s.AttachmentManagerEndpoint != "" { + _, _, err := parseToHostPort(s.CategoryManagerEndpoint) + if err != nil { + errs = append(errs, fmt.Errorf("invalid host port:%s", s.CategoryManagerEndpoint)) } } @@ -37,9 +80,24 @@ func (s *OpenPitrixOptions) Validate() []error { } func (s *OpenPitrixOptions) AddFlags(fs *pflag.FlagSet) { - fs.StringVar(&s.APIServer, "openpitrix-apiserver", s.APIServer, ""+ - "OpenPitrix api gateway endpoint, if left blank, following options will be ignored.") + fs.StringVar(&s.RuntimeManagerEndpoint, "openpitrix-runtime-manager-endpoint", s.RuntimeManagerEndpoint, ""+ + "OpenPitrix runtime manager endpoint") - fs.StringVar(&s.Token, "openpitrix-token", s.Token, ""+ - "OpenPitrix api access token.") + fs.StringVar(&s.AppManagerEndpoint, "openpitrix-app-manager-endpoint", s.AppManagerEndpoint, ""+ + "OpenPitrix app manager endpoint") + + fs.StringVar(&s.ClusterManagerEndpoint, "openpitrix-cluster-manager-endpoint", s.ClusterManagerEndpoint, ""+ + "OpenPitrix cluster manager endpoint") + + fs.StringVar(&s.CategoryManagerEndpoint, "openpitrix-category-manager-endpoint", s.CategoryManagerEndpoint, ""+ + "OpenPitrix category manager endpoint") + + fs.StringVar(&s.RepoManagerEndpoint, "openpitrix-repo-manager-endpoint", s.RepoManagerEndpoint, ""+ + "OpenPitrix repo manager endpoint") + + fs.StringVar(&s.RepoIndexerEndpoint, "openpitrix-repo-indexer-endpoint", s.RepoIndexerEndpoint, ""+ + "OpenPitrix repo indexer endpoint") + + fs.StringVar(&s.AttachmentManagerEndpoint, "openpitrix-attachment-manager-endpoint", s.AttachmentManagerEndpoint, ""+ + "OpenPitrix attachment manager endpoint") } diff --git a/pkg/simple/client/openpitrix/types.go b/pkg/simple/client/openpitrix/types.go deleted file mode 100644 index 3e41fa4ff..000000000 --- a/pkg/simple/client/openpitrix/types.go +++ /dev/null @@ -1,117 +0,0 @@ -package openpitrix - -import ( - "fmt" - "net/http" - "time" -) - -type Cluster struct { - ClusterID string `json:"cluster_id"` - Name string `json:"name"` - AppID string `json:"app_id"` - VersionID string `json:"version_id"` - Status string `json:"status"` - UpdateTime time.Time `json:"status_time"` - CreateTime time.Time `json:"create_time"` - RunTimeId string `json:"runtime_id"` - Description string `json:"description"` - ClusterRoleSets []ClusterRole `json:"cluster_role_set"` -} - -type ClusterRole struct { - ClusterID string `json:"cluster_id"` - Role string `json:"role"` -} - -type ClusterList struct { - Total int `json:"total_count"` - Clusters []Cluster `json:"cluster_set"` -} - -type VersionList struct { - Total int `json:"total_count"` - Versions []version `json:"app_version_set"` -} - -type version struct { - Name string `json:"name"` - VersionID string `json:"version_id"` -} - -type runtime struct { - RuntimeID string `json:"runtime_id"` - Zone string `json:"zone"` -} - -type runtimeList struct { - Total int `json:"total_count"` - Runtimes []runtime `json:"runtime_set"` -} - -type app struct { - AppId string `json:"app_id"` - Name string `json:"name"` - ChartName string `json:"chart_name"` - RepoId string `json:"repo_id"` -} - -type repo struct { - RepoId string `json:"repo_id"` - Name string `json:"name"` - Url string `json:"url"` -} - -type appList struct { - Total int `json:"total_count"` - Apps []app `json:"app_set"` -} - -type repoList struct { - Total int `json:"total_count"` - Repos []repo `json:"repo_set"` -} - -type CreateClusterRequest struct { - AppId string `json:"app_id" description:"ID of app to run in cluster, e.g. app-AA3A3y3zEgEM"` - VersionId string `json:"version_id" description:"app version, e.g. appv-154gXYx5RKRp"` - RuntimeId string `json:"runtime_id" description:"ID of runtime, e.g. runtime-wWwXL0LzWqEr"` - Conf string `json:"conf" description:"conf a json string, include cpu, memory info of cluster"` -} - -type DeleteClusterRequest struct { - ClusterId []string `json:"cluster_id" description:"cluster ID"` -} - -type RunTime struct { - RuntimeId string `json:"runtime_id"` - RuntimeUrl string `json:"runtime_url"` - Name string `json:"name"` - Provider string `json:"provider"` - Zone string `json:"zone"` - RuntimeCredential string `json:"runtime_credential"` -} - -type Interface interface { - CreateRuntime(runtime *RunTime) error - DeleteRuntime(runtimeId string) error -} -type cluster struct { - Status string `json:"status"` - ClusterId string `json:"cluster_id"` -} - -type Error struct { - status int - message string -} - -func (e Error) Error() string { - return fmt.Sprintf("status: %d,message: %s", e.status, e.message) -} - -type OpenPitrixClient struct { - client *http.Client - apiServer string - token string -} diff --git a/tools/cmd/doc-gen/main.go b/tools/cmd/doc-gen/main.go index 012577605..fcf1743e0 100644 --- a/tools/cmd/doc-gen/main.go +++ b/tools/cmd/doc-gen/main.go @@ -34,10 +34,12 @@ import ( _ "kubesphere.io/kubesphere/pkg/apis/iam/install" _ "kubesphere.io/kubesphere/pkg/apis/logging/install" _ "kubesphere.io/kubesphere/pkg/apis/monitoring/install" + _ "kubesphere.io/kubesphere/pkg/apis/openpitrix/install" _ "kubesphere.io/kubesphere/pkg/apis/operations/install" _ "kubesphere.io/kubesphere/pkg/apis/resources/install" _ "kubesphere.io/kubesphere/pkg/apis/servicemesh/metrics/install" _ "kubesphere.io/kubesphere/pkg/apis/tenant/install" + _ "kubesphere.io/kubesphere/pkg/apis/terminal/install" ) var output string diff --git a/vendor/github.com/BurntSushi/toml/.gitignore b/vendor/github.com/BurntSushi/toml/.gitignore new file mode 100644 index 000000000..0cd380037 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/.gitignore @@ -0,0 +1,5 @@ +TAGS +tags +.*.swp +tomlcheck/tomlcheck +toml.test diff --git a/vendor/github.com/BurntSushi/toml/.travis.yml b/vendor/github.com/BurntSushi/toml/.travis.yml new file mode 100644 index 000000000..8b8afc4f0 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/.travis.yml @@ -0,0 +1,15 @@ +language: go +go: + - 1.1 + - 1.2 + - 1.3 + - 1.4 + - 1.5 + - 1.6 + - tip +install: + - go install ./... + - go get github.com/BurntSushi/toml-test +script: + - export PATH="$PATH:$HOME/gopath/bin" + - make test diff --git a/vendor/github.com/BurntSushi/toml/COMPATIBLE b/vendor/github.com/BurntSushi/toml/COMPATIBLE new file mode 100644 index 000000000..6efcfd0ce --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/COMPATIBLE @@ -0,0 +1,3 @@ +Compatible with TOML version +[v0.4.0](https://github.com/toml-lang/toml/blob/v0.4.0/versions/en/toml-v0.4.0.md) + diff --git a/vendor/github.com/BurntSushi/toml/COPYING b/vendor/github.com/BurntSushi/toml/COPYING new file mode 100644 index 000000000..01b574320 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/COPYING @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 TOML authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/BurntSushi/toml/Makefile b/vendor/github.com/BurntSushi/toml/Makefile new file mode 100644 index 000000000..3600848d3 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/Makefile @@ -0,0 +1,19 @@ +install: + go install ./... + +test: install + go test -v + toml-test toml-test-decoder + toml-test -encoder toml-test-encoder + +fmt: + gofmt -w *.go */*.go + colcheck *.go */*.go + +tags: + find ./ -name '*.go' -print0 | xargs -0 gotags > TAGS + +push: + git push origin master + git push github master + diff --git a/vendor/github.com/BurntSushi/toml/README.md b/vendor/github.com/BurntSushi/toml/README.md new file mode 100644 index 000000000..7c1b37ecc --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/README.md @@ -0,0 +1,218 @@ +## TOML parser and encoder for Go with reflection + +TOML stands for Tom's Obvious, Minimal Language. This Go package provides a +reflection interface similar to Go's standard library `json` and `xml` +packages. This package also supports the `encoding.TextUnmarshaler` and +`encoding.TextMarshaler` interfaces so that you can define custom data +representations. (There is an example of this below.) + +Spec: https://github.com/toml-lang/toml + +Compatible with TOML version +[v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md) + +Documentation: https://godoc.org/github.com/BurntSushi/toml + +Installation: + +```bash +go get github.com/BurntSushi/toml +``` + +Try the toml validator: + +```bash +go get github.com/BurntSushi/toml/cmd/tomlv +tomlv some-toml-file.toml +``` + +[![Build Status](https://travis-ci.org/BurntSushi/toml.svg?branch=master)](https://travis-ci.org/BurntSushi/toml) [![GoDoc](https://godoc.org/github.com/BurntSushi/toml?status.svg)](https://godoc.org/github.com/BurntSushi/toml) + +### Testing + +This package passes all tests in +[toml-test](https://github.com/BurntSushi/toml-test) for both the decoder +and the encoder. + +### Examples + +This package works similarly to how the Go standard library handles `XML` +and `JSON`. Namely, data is loaded into Go values via reflection. + +For the simplest example, consider some TOML file as just a list of keys +and values: + +```toml +Age = 25 +Cats = [ "Cauchy", "Plato" ] +Pi = 3.14 +Perfection = [ 6, 28, 496, 8128 ] +DOB = 1987-07-05T05:45:00Z +``` + +Which could be defined in Go as: + +```go +type Config struct { + Age int + Cats []string + Pi float64 + Perfection []int + DOB time.Time // requires `import time` +} +``` + +And then decoded with: + +```go +var conf Config +if _, err := toml.Decode(tomlData, &conf); err != nil { + // handle error +} +``` + +You can also use struct tags if your struct field name doesn't map to a TOML +key value directly: + +```toml +some_key_NAME = "wat" +``` + +```go +type TOML struct { + ObscureKey string `toml:"some_key_NAME"` +} +``` + +### Using the `encoding.TextUnmarshaler` interface + +Here's an example that automatically parses duration strings into +`time.Duration` values: + +```toml +[[song]] +name = "Thunder Road" +duration = "4m49s" + +[[song]] +name = "Stairway to Heaven" +duration = "8m03s" +``` + +Which can be decoded with: + +```go +type song struct { + Name string + Duration duration +} +type songs struct { + Song []song +} +var favorites songs +if _, err := toml.Decode(blob, &favorites); err != nil { + log.Fatal(err) +} + +for _, s := range favorites.Song { + fmt.Printf("%s (%s)\n", s.Name, s.Duration) +} +``` + +And you'll also need a `duration` type that satisfies the +`encoding.TextUnmarshaler` interface: + +```go +type duration struct { + time.Duration +} + +func (d *duration) UnmarshalText(text []byte) error { + var err error + d.Duration, err = time.ParseDuration(string(text)) + return err +} +``` + +### More complex usage + +Here's an example of how to load the example from the official spec page: + +```toml +# This is a TOML document. Boom. + +title = "TOML Example" + +[owner] +name = "Tom Preston-Werner" +organization = "GitHub" +bio = "GitHub Cofounder & CEO\nLikes tater tots and beer." +dob = 1979-05-27T07:32:00Z # First class dates? Why not? + +[database] +server = "192.168.1.1" +ports = [ 8001, 8001, 8002 ] +connection_max = 5000 +enabled = true + +[servers] + + # You can indent as you please. Tabs or spaces. TOML don't care. + [servers.alpha] + ip = "10.0.0.1" + dc = "eqdc10" + + [servers.beta] + ip = "10.0.0.2" + dc = "eqdc10" + +[clients] +data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it + +# Line breaks are OK when inside arrays +hosts = [ + "alpha", + "omega" +] +``` + +And the corresponding Go types are: + +```go +type tomlConfig struct { + Title string + Owner ownerInfo + DB database `toml:"database"` + Servers map[string]server + Clients clients +} + +type ownerInfo struct { + Name string + Org string `toml:"organization"` + Bio string + DOB time.Time +} + +type database struct { + Server string + Ports []int + ConnMax int `toml:"connection_max"` + Enabled bool +} + +type server struct { + IP string + DC string +} + +type clients struct { + Data [][]interface{} + Hosts []string +} +``` + +Note that a case insensitive match will be tried if an exact match can't be +found. + +A working example of the above can be found in `_examples/example.{go,toml}`. diff --git a/vendor/github.com/BurntSushi/toml/decode.go b/vendor/github.com/BurntSushi/toml/decode.go new file mode 100644 index 000000000..b0fd51d5b --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/decode.go @@ -0,0 +1,509 @@ +package toml + +import ( + "fmt" + "io" + "io/ioutil" + "math" + "reflect" + "strings" + "time" +) + +func e(format string, args ...interface{}) error { + return fmt.Errorf("toml: "+format, args...) +} + +// Unmarshaler is the interface implemented by objects that can unmarshal a +// TOML description of themselves. +type Unmarshaler interface { + UnmarshalTOML(interface{}) error +} + +// Unmarshal decodes the contents of `p` in TOML format into a pointer `v`. +func Unmarshal(p []byte, v interface{}) error { + _, err := Decode(string(p), v) + return err +} + +// Primitive is a TOML value that hasn't been decoded into a Go value. +// When using the various `Decode*` functions, the type `Primitive` may +// be given to any value, and its decoding will be delayed. +// +// A `Primitive` value can be decoded using the `PrimitiveDecode` function. +// +// The underlying representation of a `Primitive` value is subject to change. +// Do not rely on it. +// +// N.B. Primitive values are still parsed, so using them will only avoid +// the overhead of reflection. They can be useful when you don't know the +// exact type of TOML data until run time. +type Primitive struct { + undecoded interface{} + context Key +} + +// DEPRECATED! +// +// Use MetaData.PrimitiveDecode instead. +func PrimitiveDecode(primValue Primitive, v interface{}) error { + md := MetaData{decoded: make(map[string]bool)} + return md.unify(primValue.undecoded, rvalue(v)) +} + +// PrimitiveDecode is just like the other `Decode*` functions, except it +// decodes a TOML value that has already been parsed. Valid primitive values +// can *only* be obtained from values filled by the decoder functions, +// including this method. (i.e., `v` may contain more `Primitive` +// values.) +// +// Meta data for primitive values is included in the meta data returned by +// the `Decode*` functions with one exception: keys returned by the Undecoded +// method will only reflect keys that were decoded. Namely, any keys hidden +// behind a Primitive will be considered undecoded. Executing this method will +// update the undecoded keys in the meta data. (See the example.) +func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error { + md.context = primValue.context + defer func() { md.context = nil }() + return md.unify(primValue.undecoded, rvalue(v)) +} + +// Decode will decode the contents of `data` in TOML format into a pointer +// `v`. +// +// TOML hashes correspond to Go structs or maps. (Dealer's choice. They can be +// used interchangeably.) +// +// TOML arrays of tables correspond to either a slice of structs or a slice +// of maps. +// +// TOML datetimes correspond to Go `time.Time` values. +// +// All other TOML types (float, string, int, bool and array) correspond +// to the obvious Go types. +// +// An exception to the above rules is if a type implements the +// encoding.TextUnmarshaler interface. In this case, any primitive TOML value +// (floats, strings, integers, booleans and datetimes) will be converted to +// a byte string and given to the value's UnmarshalText method. See the +// Unmarshaler example for a demonstration with time duration strings. +// +// Key mapping +// +// TOML keys can map to either keys in a Go map or field names in a Go +// struct. The special `toml` struct tag may be used to map TOML keys to +// struct fields that don't match the key name exactly. (See the example.) +// A case insensitive match to struct names will be tried if an exact match +// can't be found. +// +// The mapping between TOML values and Go values is loose. That is, there +// may exist TOML values that cannot be placed into your representation, and +// there may be parts of your representation that do not correspond to +// TOML values. This loose mapping can be made stricter by using the IsDefined +// and/or Undecoded methods on the MetaData returned. +// +// This decoder will not handle cyclic types. If a cyclic type is passed, +// `Decode` will not terminate. +func Decode(data string, v interface{}) (MetaData, error) { + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Ptr { + return MetaData{}, e("Decode of non-pointer %s", reflect.TypeOf(v)) + } + if rv.IsNil() { + return MetaData{}, e("Decode of nil %s", reflect.TypeOf(v)) + } + p, err := parse(data) + if err != nil { + return MetaData{}, err + } + md := MetaData{ + p.mapping, p.types, p.ordered, + make(map[string]bool, len(p.ordered)), nil, + } + return md, md.unify(p.mapping, indirect(rv)) +} + +// DecodeFile is just like Decode, except it will automatically read the +// contents of the file at `fpath` and decode it for you. +func DecodeFile(fpath string, v interface{}) (MetaData, error) { + bs, err := ioutil.ReadFile(fpath) + if err != nil { + return MetaData{}, err + } + return Decode(string(bs), v) +} + +// DecodeReader is just like Decode, except it will consume all bytes +// from the reader and decode it for you. +func DecodeReader(r io.Reader, v interface{}) (MetaData, error) { + bs, err := ioutil.ReadAll(r) + if err != nil { + return MetaData{}, err + } + return Decode(string(bs), v) +} + +// unify performs a sort of type unification based on the structure of `rv`, +// which is the client representation. +// +// Any type mismatch produces an error. Finding a type that we don't know +// how to handle produces an unsupported type error. +func (md *MetaData) unify(data interface{}, rv reflect.Value) error { + + // Special case. Look for a `Primitive` value. + if rv.Type() == reflect.TypeOf((*Primitive)(nil)).Elem() { + // Save the undecoded data and the key context into the primitive + // value. + context := make(Key, len(md.context)) + copy(context, md.context) + rv.Set(reflect.ValueOf(Primitive{ + undecoded: data, + context: context, + })) + return nil + } + + // Special case. Unmarshaler Interface support. + if rv.CanAddr() { + if v, ok := rv.Addr().Interface().(Unmarshaler); ok { + return v.UnmarshalTOML(data) + } + } + + // Special case. Handle time.Time values specifically. + // TODO: Remove this code when we decide to drop support for Go 1.1. + // This isn't necessary in Go 1.2 because time.Time satisfies the encoding + // interfaces. + if rv.Type().AssignableTo(rvalue(time.Time{}).Type()) { + return md.unifyDatetime(data, rv) + } + + // Special case. Look for a value satisfying the TextUnmarshaler interface. + if v, ok := rv.Interface().(TextUnmarshaler); ok { + return md.unifyText(data, v) + } + // BUG(burntsushi) + // The behavior here is incorrect whenever a Go type satisfies the + // encoding.TextUnmarshaler interface but also corresponds to a TOML + // hash or array. In particular, the unmarshaler should only be applied + // to primitive TOML values. But at this point, it will be applied to + // all kinds of values and produce an incorrect error whenever those values + // are hashes or arrays (including arrays of tables). + + k := rv.Kind() + + // laziness + if k >= reflect.Int && k <= reflect.Uint64 { + return md.unifyInt(data, rv) + } + switch k { + case reflect.Ptr: + elem := reflect.New(rv.Type().Elem()) + err := md.unify(data, reflect.Indirect(elem)) + if err != nil { + return err + } + rv.Set(elem) + return nil + case reflect.Struct: + return md.unifyStruct(data, rv) + case reflect.Map: + return md.unifyMap(data, rv) + case reflect.Array: + return md.unifyArray(data, rv) + case reflect.Slice: + return md.unifySlice(data, rv) + case reflect.String: + return md.unifyString(data, rv) + case reflect.Bool: + return md.unifyBool(data, rv) + case reflect.Interface: + // we only support empty interfaces. + if rv.NumMethod() > 0 { + return e("unsupported type %s", rv.Type()) + } + return md.unifyAnything(data, rv) + case reflect.Float32: + fallthrough + case reflect.Float64: + return md.unifyFloat64(data, rv) + } + return e("unsupported type %s", rv.Kind()) +} + +func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error { + tmap, ok := mapping.(map[string]interface{}) + if !ok { + if mapping == nil { + return nil + } + return e("type mismatch for %s: expected table but found %T", + rv.Type().String(), mapping) + } + + for key, datum := range tmap { + var f *field + fields := cachedTypeFields(rv.Type()) + for i := range fields { + ff := &fields[i] + if ff.name == key { + f = ff + break + } + if f == nil && strings.EqualFold(ff.name, key) { + f = ff + } + } + if f != nil { + subv := rv + for _, i := range f.index { + subv = indirect(subv.Field(i)) + } + if isUnifiable(subv) { + md.decoded[md.context.add(key).String()] = true + md.context = append(md.context, key) + if err := md.unify(datum, subv); err != nil { + return err + } + md.context = md.context[0 : len(md.context)-1] + } else if f.name != "" { + // Bad user! No soup for you! + return e("cannot write unexported field %s.%s", + rv.Type().String(), f.name) + } + } + } + return nil +} + +func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error { + tmap, ok := mapping.(map[string]interface{}) + if !ok { + if tmap == nil { + return nil + } + return badtype("map", mapping) + } + if rv.IsNil() { + rv.Set(reflect.MakeMap(rv.Type())) + } + for k, v := range tmap { + md.decoded[md.context.add(k).String()] = true + md.context = append(md.context, k) + + rvkey := indirect(reflect.New(rv.Type().Key())) + rvval := reflect.Indirect(reflect.New(rv.Type().Elem())) + if err := md.unify(v, rvval); err != nil { + return err + } + md.context = md.context[0 : len(md.context)-1] + + rvkey.SetString(k) + rv.SetMapIndex(rvkey, rvval) + } + return nil +} + +func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error { + datav := reflect.ValueOf(data) + if datav.Kind() != reflect.Slice { + if !datav.IsValid() { + return nil + } + return badtype("slice", data) + } + sliceLen := datav.Len() + if sliceLen != rv.Len() { + return e("expected array length %d; got TOML array of length %d", + rv.Len(), sliceLen) + } + return md.unifySliceArray(datav, rv) +} + +func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error { + datav := reflect.ValueOf(data) + if datav.Kind() != reflect.Slice { + if !datav.IsValid() { + return nil + } + return badtype("slice", data) + } + n := datav.Len() + if rv.IsNil() || rv.Cap() < n { + rv.Set(reflect.MakeSlice(rv.Type(), n, n)) + } + rv.SetLen(n) + return md.unifySliceArray(datav, rv) +} + +func (md *MetaData) unifySliceArray(data, rv reflect.Value) error { + sliceLen := data.Len() + for i := 0; i < sliceLen; i++ { + v := data.Index(i).Interface() + sliceval := indirect(rv.Index(i)) + if err := md.unify(v, sliceval); err != nil { + return err + } + } + return nil +} + +func (md *MetaData) unifyDatetime(data interface{}, rv reflect.Value) error { + if _, ok := data.(time.Time); ok { + rv.Set(reflect.ValueOf(data)) + return nil + } + return badtype("time.Time", data) +} + +func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error { + if s, ok := data.(string); ok { + rv.SetString(s) + return nil + } + return badtype("string", data) +} + +func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error { + if num, ok := data.(float64); ok { + switch rv.Kind() { + case reflect.Float32: + fallthrough + case reflect.Float64: + rv.SetFloat(num) + default: + panic("bug") + } + return nil + } + return badtype("float", data) +} + +func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error { + if num, ok := data.(int64); ok { + if rv.Kind() >= reflect.Int && rv.Kind() <= reflect.Int64 { + switch rv.Kind() { + case reflect.Int, reflect.Int64: + // No bounds checking necessary. + case reflect.Int8: + if num < math.MinInt8 || num > math.MaxInt8 { + return e("value %d is out of range for int8", num) + } + case reflect.Int16: + if num < math.MinInt16 || num > math.MaxInt16 { + return e("value %d is out of range for int16", num) + } + case reflect.Int32: + if num < math.MinInt32 || num > math.MaxInt32 { + return e("value %d is out of range for int32", num) + } + } + rv.SetInt(num) + } else if rv.Kind() >= reflect.Uint && rv.Kind() <= reflect.Uint64 { + unum := uint64(num) + switch rv.Kind() { + case reflect.Uint, reflect.Uint64: + // No bounds checking necessary. + case reflect.Uint8: + if num < 0 || unum > math.MaxUint8 { + return e("value %d is out of range for uint8", num) + } + case reflect.Uint16: + if num < 0 || unum > math.MaxUint16 { + return e("value %d is out of range for uint16", num) + } + case reflect.Uint32: + if num < 0 || unum > math.MaxUint32 { + return e("value %d is out of range for uint32", num) + } + } + rv.SetUint(unum) + } else { + panic("unreachable") + } + return nil + } + return badtype("integer", data) +} + +func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error { + if b, ok := data.(bool); ok { + rv.SetBool(b) + return nil + } + return badtype("boolean", data) +} + +func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error { + rv.Set(reflect.ValueOf(data)) + return nil +} + +func (md *MetaData) unifyText(data interface{}, v TextUnmarshaler) error { + var s string + switch sdata := data.(type) { + case TextMarshaler: + text, err := sdata.MarshalText() + if err != nil { + return err + } + s = string(text) + case fmt.Stringer: + s = sdata.String() + case string: + s = sdata + case bool: + s = fmt.Sprintf("%v", sdata) + case int64: + s = fmt.Sprintf("%d", sdata) + case float64: + s = fmt.Sprintf("%f", sdata) + default: + return badtype("primitive (string-like)", data) + } + if err := v.UnmarshalText([]byte(s)); err != nil { + return err + } + return nil +} + +// rvalue returns a reflect.Value of `v`. All pointers are resolved. +func rvalue(v interface{}) reflect.Value { + return indirect(reflect.ValueOf(v)) +} + +// indirect returns the value pointed to by a pointer. +// Pointers are followed until the value is not a pointer. +// New values are allocated for each nil pointer. +// +// An exception to this rule is if the value satisfies an interface of +// interest to us (like encoding.TextUnmarshaler). +func indirect(v reflect.Value) reflect.Value { + if v.Kind() != reflect.Ptr { + if v.CanSet() { + pv := v.Addr() + if _, ok := pv.Interface().(TextUnmarshaler); ok { + return pv + } + } + return v + } + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + return indirect(reflect.Indirect(v)) +} + +func isUnifiable(rv reflect.Value) bool { + if rv.CanSet() { + return true + } + if _, ok := rv.Interface().(TextUnmarshaler); ok { + return true + } + return false +} + +func badtype(expected string, data interface{}) error { + return e("cannot load TOML value of type %T into a Go %s", data, expected) +} diff --git a/vendor/github.com/BurntSushi/toml/decode_meta.go b/vendor/github.com/BurntSushi/toml/decode_meta.go new file mode 100644 index 000000000..b9914a679 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/decode_meta.go @@ -0,0 +1,121 @@ +package toml + +import "strings" + +// MetaData allows access to meta information about TOML data that may not +// be inferrable via reflection. In particular, whether a key has been defined +// and the TOML type of a key. +type MetaData struct { + mapping map[string]interface{} + types map[string]tomlType + keys []Key + decoded map[string]bool + context Key // Used only during decoding. +} + +// IsDefined returns true if the key given exists in the TOML data. The key +// should be specified hierarchially. e.g., +// +// // access the TOML key 'a.b.c' +// IsDefined("a", "b", "c") +// +// IsDefined will return false if an empty key given. Keys are case sensitive. +func (md *MetaData) IsDefined(key ...string) bool { + if len(key) == 0 { + return false + } + + var hash map[string]interface{} + var ok bool + var hashOrVal interface{} = md.mapping + for _, k := range key { + if hash, ok = hashOrVal.(map[string]interface{}); !ok { + return false + } + if hashOrVal, ok = hash[k]; !ok { + return false + } + } + return true +} + +// Type returns a string representation of the type of the key specified. +// +// Type will return the empty string if given an empty key or a key that +// does not exist. Keys are case sensitive. +func (md *MetaData) Type(key ...string) string { + fullkey := strings.Join(key, ".") + if typ, ok := md.types[fullkey]; ok { + return typ.typeString() + } + return "" +} + +// Key is the type of any TOML key, including key groups. Use (MetaData).Keys +// to get values of this type. +type Key []string + +func (k Key) String() string { + return strings.Join(k, ".") +} + +func (k Key) maybeQuotedAll() string { + var ss []string + for i := range k { + ss = append(ss, k.maybeQuoted(i)) + } + return strings.Join(ss, ".") +} + +func (k Key) maybeQuoted(i int) string { + quote := false + for _, c := range k[i] { + if !isBareKeyChar(c) { + quote = true + break + } + } + if quote { + return "\"" + strings.Replace(k[i], "\"", "\\\"", -1) + "\"" + } + return k[i] +} + +func (k Key) add(piece string) Key { + newKey := make(Key, len(k)+1) + copy(newKey, k) + newKey[len(k)] = piece + return newKey +} + +// Keys returns a slice of every key in the TOML data, including key groups. +// Each key is itself a slice, where the first element is the top of the +// hierarchy and the last is the most specific. +// +// The list will have the same order as the keys appeared in the TOML data. +// +// All keys returned are non-empty. +func (md *MetaData) Keys() []Key { + return md.keys +} + +// Undecoded returns all keys that have not been decoded in the order in which +// they appear in the original TOML document. +// +// This includes keys that haven't been decoded because of a Primitive value. +// Once the Primitive value is decoded, the keys will be considered decoded. +// +// Also note that decoding into an empty interface will result in no decoding, +// and so no keys will be considered decoded. +// +// In this sense, the Undecoded keys correspond to keys in the TOML document +// that do not have a concrete type in your representation. +func (md *MetaData) Undecoded() []Key { + undecoded := make([]Key, 0, len(md.keys)) + for _, key := range md.keys { + if !md.decoded[key.String()] { + undecoded = append(undecoded, key) + } + } + return undecoded +} diff --git a/vendor/github.com/BurntSushi/toml/doc.go b/vendor/github.com/BurntSushi/toml/doc.go new file mode 100644 index 000000000..b371f396e --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/doc.go @@ -0,0 +1,27 @@ +/* +Package toml provides facilities for decoding and encoding TOML configuration +files via reflection. There is also support for delaying decoding with +the Primitive type, and querying the set of keys in a TOML document with the +MetaData type. + +The specification implemented: https://github.com/toml-lang/toml + +The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify +whether a file is a valid TOML document. It can also be used to print the +type of each key in a TOML document. + +Testing + +There are two important types of tests used for this package. The first is +contained inside '*_test.go' files and uses the standard Go unit testing +framework. These tests are primarily devoted to holistically testing the +decoder and encoder. + +The second type of testing is used to verify the implementation's adherence +to the TOML specification. These tests have been factored into their own +project: https://github.com/BurntSushi/toml-test + +The reason the tests are in a separate project is so that they can be used by +any implementation of TOML. Namely, it is language agnostic. +*/ +package toml diff --git a/vendor/github.com/BurntSushi/toml/encode.go b/vendor/github.com/BurntSushi/toml/encode.go new file mode 100644 index 000000000..d905c21a2 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/encode.go @@ -0,0 +1,568 @@ +package toml + +import ( + "bufio" + "errors" + "fmt" + "io" + "reflect" + "sort" + "strconv" + "strings" + "time" +) + +type tomlEncodeError struct{ error } + +var ( + errArrayMixedElementTypes = errors.New( + "toml: cannot encode array with mixed element types") + errArrayNilElement = errors.New( + "toml: cannot encode array with nil element") + errNonString = errors.New( + "toml: cannot encode a map with non-string key type") + errAnonNonStruct = errors.New( + "toml: cannot encode an anonymous field that is not a struct") + errArrayNoTable = errors.New( + "toml: TOML array element cannot contain a table") + errNoKey = errors.New( + "toml: top-level values must be Go maps or structs") + errAnything = errors.New("") // used in testing +) + +var quotedReplacer = strings.NewReplacer( + "\t", "\\t", + "\n", "\\n", + "\r", "\\r", + "\"", "\\\"", + "\\", "\\\\", +) + +// Encoder controls the encoding of Go values to a TOML document to some +// io.Writer. +// +// The indentation level can be controlled with the Indent field. +type Encoder struct { + // A single indentation level. By default it is two spaces. + Indent string + + // hasWritten is whether we have written any output to w yet. + hasWritten bool + w *bufio.Writer +} + +// NewEncoder returns a TOML encoder that encodes Go values to the io.Writer +// given. By default, a single indentation level is 2 spaces. +func NewEncoder(w io.Writer) *Encoder { + return &Encoder{ + w: bufio.NewWriter(w), + Indent: " ", + } +} + +// Encode writes a TOML representation of the Go value to the underlying +// io.Writer. If the value given cannot be encoded to a valid TOML document, +// then an error is returned. +// +// The mapping between Go values and TOML values should be precisely the same +// as for the Decode* functions. Similarly, the TextMarshaler interface is +// supported by encoding the resulting bytes as strings. (If you want to write +// arbitrary binary data then you will need to use something like base64 since +// TOML does not have any binary types.) +// +// When encoding TOML hashes (i.e., Go maps or structs), keys without any +// sub-hashes are encoded first. +// +// If a Go map is encoded, then its keys are sorted alphabetically for +// deterministic output. More control over this behavior may be provided if +// there is demand for it. +// +// Encoding Go values without a corresponding TOML representation---like map +// types with non-string keys---will cause an error to be returned. Similarly +// for mixed arrays/slices, arrays/slices with nil elements, embedded +// non-struct types and nested slices containing maps or structs. +// (e.g., [][]map[string]string is not allowed but []map[string]string is OK +// and so is []map[string][]string.) +func (enc *Encoder) Encode(v interface{}) error { + rv := eindirect(reflect.ValueOf(v)) + if err := enc.safeEncode(Key([]string{}), rv); err != nil { + return err + } + return enc.w.Flush() +} + +func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) { + defer func() { + if r := recover(); r != nil { + if terr, ok := r.(tomlEncodeError); ok { + err = terr.error + return + } + panic(r) + } + }() + enc.encode(key, rv) + return nil +} + +func (enc *Encoder) encode(key Key, rv reflect.Value) { + // Special case. Time needs to be in ISO8601 format. + // Special case. If we can marshal the type to text, then we used that. + // Basically, this prevents the encoder for handling these types as + // generic structs (or whatever the underlying type of a TextMarshaler is). + switch rv.Interface().(type) { + case time.Time, TextMarshaler: + enc.keyEqElement(key, rv) + return + } + + k := rv.Kind() + switch k { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64, + reflect.Float32, reflect.Float64, reflect.String, reflect.Bool: + enc.keyEqElement(key, rv) + case reflect.Array, reflect.Slice: + if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) { + enc.eArrayOfTables(key, rv) + } else { + enc.keyEqElement(key, rv) + } + case reflect.Interface: + if rv.IsNil() { + return + } + enc.encode(key, rv.Elem()) + case reflect.Map: + if rv.IsNil() { + return + } + enc.eTable(key, rv) + case reflect.Ptr: + if rv.IsNil() { + return + } + enc.encode(key, rv.Elem()) + case reflect.Struct: + enc.eTable(key, rv) + default: + panic(e("unsupported type for key '%s': %s", key, k)) + } +} + +// eElement encodes any value that can be an array element (primitives and +// arrays). +func (enc *Encoder) eElement(rv reflect.Value) { + switch v := rv.Interface().(type) { + case time.Time: + // Special case time.Time as a primitive. Has to come before + // TextMarshaler below because time.Time implements + // encoding.TextMarshaler, but we need to always use UTC. + enc.wf(v.UTC().Format("2006-01-02T15:04:05Z")) + return + case TextMarshaler: + // Special case. Use text marshaler if it's available for this value. + if s, err := v.MarshalText(); err != nil { + encPanic(err) + } else { + enc.writeQuoted(string(s)) + } + return + } + switch rv.Kind() { + case reflect.Bool: + enc.wf(strconv.FormatBool(rv.Bool())) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64: + enc.wf(strconv.FormatInt(rv.Int(), 10)) + case reflect.Uint, reflect.Uint8, reflect.Uint16, + reflect.Uint32, reflect.Uint64: + enc.wf(strconv.FormatUint(rv.Uint(), 10)) + case reflect.Float32: + enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 32))) + case reflect.Float64: + enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 64))) + case reflect.Array, reflect.Slice: + enc.eArrayOrSliceElement(rv) + case reflect.Interface: + enc.eElement(rv.Elem()) + case reflect.String: + enc.writeQuoted(rv.String()) + default: + panic(e("unexpected primitive type: %s", rv.Kind())) + } +} + +// By the TOML spec, all floats must have a decimal with at least one +// number on either side. +func floatAddDecimal(fstr string) string { + if !strings.Contains(fstr, ".") { + return fstr + ".0" + } + return fstr +} + +func (enc *Encoder) writeQuoted(s string) { + enc.wf("\"%s\"", quotedReplacer.Replace(s)) +} + +func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) { + length := rv.Len() + enc.wf("[") + for i := 0; i < length; i++ { + elem := rv.Index(i) + enc.eElement(elem) + if i != length-1 { + enc.wf(", ") + } + } + enc.wf("]") +} + +func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) { + if len(key) == 0 { + encPanic(errNoKey) + } + for i := 0; i < rv.Len(); i++ { + trv := rv.Index(i) + if isNil(trv) { + continue + } + panicIfInvalidKey(key) + enc.newline() + enc.wf("%s[[%s]]", enc.indentStr(key), key.maybeQuotedAll()) + enc.newline() + enc.eMapOrStruct(key, trv) + } +} + +func (enc *Encoder) eTable(key Key, rv reflect.Value) { + panicIfInvalidKey(key) + if len(key) == 1 { + // Output an extra newline between top-level tables. + // (The newline isn't written if nothing else has been written though.) + enc.newline() + } + if len(key) > 0 { + enc.wf("%s[%s]", enc.indentStr(key), key.maybeQuotedAll()) + enc.newline() + } + enc.eMapOrStruct(key, rv) +} + +func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value) { + switch rv := eindirect(rv); rv.Kind() { + case reflect.Map: + enc.eMap(key, rv) + case reflect.Struct: + enc.eStruct(key, rv) + default: + panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String()) + } +} + +func (enc *Encoder) eMap(key Key, rv reflect.Value) { + rt := rv.Type() + if rt.Key().Kind() != reflect.String { + encPanic(errNonString) + } + + // Sort keys so that we have deterministic output. And write keys directly + // underneath this key first, before writing sub-structs or sub-maps. + var mapKeysDirect, mapKeysSub []string + for _, mapKey := range rv.MapKeys() { + k := mapKey.String() + if typeIsHash(tomlTypeOfGo(rv.MapIndex(mapKey))) { + mapKeysSub = append(mapKeysSub, k) + } else { + mapKeysDirect = append(mapKeysDirect, k) + } + } + + var writeMapKeys = func(mapKeys []string) { + sort.Strings(mapKeys) + for _, mapKey := range mapKeys { + mrv := rv.MapIndex(reflect.ValueOf(mapKey)) + if isNil(mrv) { + // Don't write anything for nil fields. + continue + } + enc.encode(key.add(mapKey), mrv) + } + } + writeMapKeys(mapKeysDirect) + writeMapKeys(mapKeysSub) +} + +func (enc *Encoder) eStruct(key Key, rv reflect.Value) { + // Write keys for fields directly under this key first, because if we write + // a field that creates a new table, then all keys under it will be in that + // table (not the one we're writing here). + rt := rv.Type() + var fieldsDirect, fieldsSub [][]int + var addFields func(rt reflect.Type, rv reflect.Value, start []int) + addFields = func(rt reflect.Type, rv reflect.Value, start []int) { + for i := 0; i < rt.NumField(); i++ { + f := rt.Field(i) + // skip unexported fields + if f.PkgPath != "" && !f.Anonymous { + continue + } + frv := rv.Field(i) + if f.Anonymous { + t := f.Type + switch t.Kind() { + case reflect.Struct: + // Treat anonymous struct fields with + // tag names as though they are not + // anonymous, like encoding/json does. + if getOptions(f.Tag).name == "" { + addFields(t, frv, f.Index) + continue + } + case reflect.Ptr: + if t.Elem().Kind() == reflect.Struct && + getOptions(f.Tag).name == "" { + if !frv.IsNil() { + addFields(t.Elem(), frv.Elem(), f.Index) + } + continue + } + // Fall through to the normal field encoding logic below + // for non-struct anonymous fields. + } + } + + if typeIsHash(tomlTypeOfGo(frv)) { + fieldsSub = append(fieldsSub, append(start, f.Index...)) + } else { + fieldsDirect = append(fieldsDirect, append(start, f.Index...)) + } + } + } + addFields(rt, rv, nil) + + var writeFields = func(fields [][]int) { + for _, fieldIndex := range fields { + sft := rt.FieldByIndex(fieldIndex) + sf := rv.FieldByIndex(fieldIndex) + if isNil(sf) { + // Don't write anything for nil fields. + continue + } + + opts := getOptions(sft.Tag) + if opts.skip { + continue + } + keyName := sft.Name + if opts.name != "" { + keyName = opts.name + } + if opts.omitempty && isEmpty(sf) { + continue + } + if opts.omitzero && isZero(sf) { + continue + } + + enc.encode(key.add(keyName), sf) + } + } + writeFields(fieldsDirect) + writeFields(fieldsSub) +} + +// tomlTypeName returns the TOML type name of the Go value's type. It is +// used to determine whether the types of array elements are mixed (which is +// forbidden). If the Go value is nil, then it is illegal for it to be an array +// element, and valueIsNil is returned as true. + +// Returns the TOML type of a Go value. The type may be `nil`, which means +// no concrete TOML type could be found. +func tomlTypeOfGo(rv reflect.Value) tomlType { + if isNil(rv) || !rv.IsValid() { + return nil + } + switch rv.Kind() { + case reflect.Bool: + return tomlBool + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, + reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64: + return tomlInteger + case reflect.Float32, reflect.Float64: + return tomlFloat + case reflect.Array, reflect.Slice: + if typeEqual(tomlHash, tomlArrayType(rv)) { + return tomlArrayHash + } + return tomlArray + case reflect.Ptr, reflect.Interface: + return tomlTypeOfGo(rv.Elem()) + case reflect.String: + return tomlString + case reflect.Map: + return tomlHash + case reflect.Struct: + switch rv.Interface().(type) { + case time.Time: + return tomlDatetime + case TextMarshaler: + return tomlString + default: + return tomlHash + } + default: + panic("unexpected reflect.Kind: " + rv.Kind().String()) + } +} + +// tomlArrayType returns the element type of a TOML array. The type returned +// may be nil if it cannot be determined (e.g., a nil slice or a zero length +// slize). This function may also panic if it finds a type that cannot be +// expressed in TOML (such as nil elements, heterogeneous arrays or directly +// nested arrays of tables). +func tomlArrayType(rv reflect.Value) tomlType { + if isNil(rv) || !rv.IsValid() || rv.Len() == 0 { + return nil + } + firstType := tomlTypeOfGo(rv.Index(0)) + if firstType == nil { + encPanic(errArrayNilElement) + } + + rvlen := rv.Len() + for i := 1; i < rvlen; i++ { + elem := rv.Index(i) + switch elemType := tomlTypeOfGo(elem); { + case elemType == nil: + encPanic(errArrayNilElement) + case !typeEqual(firstType, elemType): + encPanic(errArrayMixedElementTypes) + } + } + // If we have a nested array, then we must make sure that the nested + // array contains ONLY primitives. + // This checks arbitrarily nested arrays. + if typeEqual(firstType, tomlArray) || typeEqual(firstType, tomlArrayHash) { + nest := tomlArrayType(eindirect(rv.Index(0))) + if typeEqual(nest, tomlHash) || typeEqual(nest, tomlArrayHash) { + encPanic(errArrayNoTable) + } + } + return firstType +} + +type tagOptions struct { + skip bool // "-" + name string + omitempty bool + omitzero bool +} + +func getOptions(tag reflect.StructTag) tagOptions { + t := tag.Get("toml") + if t == "-" { + return tagOptions{skip: true} + } + var opts tagOptions + parts := strings.Split(t, ",") + opts.name = parts[0] + for _, s := range parts[1:] { + switch s { + case "omitempty": + opts.omitempty = true + case "omitzero": + opts.omitzero = true + } + } + return opts +} + +func isZero(rv reflect.Value) bool { + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return rv.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return rv.Uint() == 0 + case reflect.Float32, reflect.Float64: + return rv.Float() == 0.0 + } + return false +} + +func isEmpty(rv reflect.Value) bool { + switch rv.Kind() { + case reflect.Array, reflect.Slice, reflect.Map, reflect.String: + return rv.Len() == 0 + case reflect.Bool: + return !rv.Bool() + } + return false +} + +func (enc *Encoder) newline() { + if enc.hasWritten { + enc.wf("\n") + } +} + +func (enc *Encoder) keyEqElement(key Key, val reflect.Value) { + if len(key) == 0 { + encPanic(errNoKey) + } + panicIfInvalidKey(key) + enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1)) + enc.eElement(val) + enc.newline() +} + +func (enc *Encoder) wf(format string, v ...interface{}) { + if _, err := fmt.Fprintf(enc.w, format, v...); err != nil { + encPanic(err) + } + enc.hasWritten = true +} + +func (enc *Encoder) indentStr(key Key) string { + return strings.Repeat(enc.Indent, len(key)-1) +} + +func encPanic(err error) { + panic(tomlEncodeError{err}) +} + +func eindirect(v reflect.Value) reflect.Value { + switch v.Kind() { + case reflect.Ptr, reflect.Interface: + return eindirect(v.Elem()) + default: + return v + } +} + +func isNil(rv reflect.Value) bool { + switch rv.Kind() { + case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return rv.IsNil() + default: + return false + } +} + +func panicIfInvalidKey(key Key) { + for _, k := range key { + if len(k) == 0 { + encPanic(e("Key '%s' is not a valid table name. Key names "+ + "cannot be empty.", key.maybeQuotedAll())) + } + } +} + +func isValidKeyName(s string) bool { + return len(s) != 0 +} diff --git a/vendor/github.com/BurntSushi/toml/encoding_types.go b/vendor/github.com/BurntSushi/toml/encoding_types.go new file mode 100644 index 000000000..d36e1dd60 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/encoding_types.go @@ -0,0 +1,19 @@ +// +build go1.2 + +package toml + +// In order to support Go 1.1, we define our own TextMarshaler and +// TextUnmarshaler types. For Go 1.2+, we just alias them with the +// standard library interfaces. + +import ( + "encoding" +) + +// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here +// so that Go 1.1 can be supported. +type TextMarshaler encoding.TextMarshaler + +// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined +// here so that Go 1.1 can be supported. +type TextUnmarshaler encoding.TextUnmarshaler diff --git a/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go b/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go new file mode 100644 index 000000000..e8d503d04 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go @@ -0,0 +1,18 @@ +// +build !go1.2 + +package toml + +// These interfaces were introduced in Go 1.2, so we add them manually when +// compiling for Go 1.1. + +// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here +// so that Go 1.1 can be supported. +type TextMarshaler interface { + MarshalText() (text []byte, err error) +} + +// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined +// here so that Go 1.1 can be supported. +type TextUnmarshaler interface { + UnmarshalText(text []byte) error +} diff --git a/vendor/github.com/BurntSushi/toml/lex.go b/vendor/github.com/BurntSushi/toml/lex.go new file mode 100644 index 000000000..e0a742a88 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/lex.go @@ -0,0 +1,953 @@ +package toml + +import ( + "fmt" + "strings" + "unicode" + "unicode/utf8" +) + +type itemType int + +const ( + itemError itemType = iota + itemNIL // used in the parser to indicate no type + itemEOF + itemText + itemString + itemRawString + itemMultilineString + itemRawMultilineString + itemBool + itemInteger + itemFloat + itemDatetime + itemArray // the start of an array + itemArrayEnd + itemTableStart + itemTableEnd + itemArrayTableStart + itemArrayTableEnd + itemKeyStart + itemCommentStart + itemInlineTableStart + itemInlineTableEnd +) + +const ( + eof = 0 + comma = ',' + tableStart = '[' + tableEnd = ']' + arrayTableStart = '[' + arrayTableEnd = ']' + tableSep = '.' + keySep = '=' + arrayStart = '[' + arrayEnd = ']' + commentStart = '#' + stringStart = '"' + stringEnd = '"' + rawStringStart = '\'' + rawStringEnd = '\'' + inlineTableStart = '{' + inlineTableEnd = '}' +) + +type stateFn func(lx *lexer) stateFn + +type lexer struct { + input string + start int + pos int + line int + state stateFn + items chan item + + // Allow for backing up up to three runes. + // This is necessary because TOML contains 3-rune tokens (""" and '''). + prevWidths [3]int + nprev int // how many of prevWidths are in use + // If we emit an eof, we can still back up, but it is not OK to call + // next again. + atEOF bool + + // A stack of state functions used to maintain context. + // The idea is to reuse parts of the state machine in various places. + // For example, values can appear at the top level or within arbitrarily + // nested arrays. The last state on the stack is used after a value has + // been lexed. Similarly for comments. + stack []stateFn +} + +type item struct { + typ itemType + val string + line int +} + +func (lx *lexer) nextItem() item { + for { + select { + case item := <-lx.items: + return item + default: + lx.state = lx.state(lx) + } + } +} + +func lex(input string) *lexer { + lx := &lexer{ + input: input, + state: lexTop, + line: 1, + items: make(chan item, 10), + stack: make([]stateFn, 0, 10), + } + return lx +} + +func (lx *lexer) push(state stateFn) { + lx.stack = append(lx.stack, state) +} + +func (lx *lexer) pop() stateFn { + if len(lx.stack) == 0 { + return lx.errorf("BUG in lexer: no states to pop") + } + last := lx.stack[len(lx.stack)-1] + lx.stack = lx.stack[0 : len(lx.stack)-1] + return last +} + +func (lx *lexer) current() string { + return lx.input[lx.start:lx.pos] +} + +func (lx *lexer) emit(typ itemType) { + lx.items <- item{typ, lx.current(), lx.line} + lx.start = lx.pos +} + +func (lx *lexer) emitTrim(typ itemType) { + lx.items <- item{typ, strings.TrimSpace(lx.current()), lx.line} + lx.start = lx.pos +} + +func (lx *lexer) next() (r rune) { + if lx.atEOF { + panic("next called after EOF") + } + if lx.pos >= len(lx.input) { + lx.atEOF = true + return eof + } + + if lx.input[lx.pos] == '\n' { + lx.line++ + } + lx.prevWidths[2] = lx.prevWidths[1] + lx.prevWidths[1] = lx.prevWidths[0] + if lx.nprev < 3 { + lx.nprev++ + } + r, w := utf8.DecodeRuneInString(lx.input[lx.pos:]) + lx.prevWidths[0] = w + lx.pos += w + return r +} + +// ignore skips over the pending input before this point. +func (lx *lexer) ignore() { + lx.start = lx.pos +} + +// backup steps back one rune. Can be called only twice between calls to next. +func (lx *lexer) backup() { + if lx.atEOF { + lx.atEOF = false + return + } + if lx.nprev < 1 { + panic("backed up too far") + } + w := lx.prevWidths[0] + lx.prevWidths[0] = lx.prevWidths[1] + lx.prevWidths[1] = lx.prevWidths[2] + lx.nprev-- + lx.pos -= w + if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' { + lx.line-- + } +} + +// accept consumes the next rune if it's equal to `valid`. +func (lx *lexer) accept(valid rune) bool { + if lx.next() == valid { + return true + } + lx.backup() + return false +} + +// peek returns but does not consume the next rune in the input. +func (lx *lexer) peek() rune { + r := lx.next() + lx.backup() + return r +} + +// skip ignores all input that matches the given predicate. +func (lx *lexer) skip(pred func(rune) bool) { + for { + r := lx.next() + if pred(r) { + continue + } + lx.backup() + lx.ignore() + return + } +} + +// errorf stops all lexing by emitting an error and returning `nil`. +// Note that any value that is a character is escaped if it's a special +// character (newlines, tabs, etc.). +func (lx *lexer) errorf(format string, values ...interface{}) stateFn { + lx.items <- item{ + itemError, + fmt.Sprintf(format, values...), + lx.line, + } + return nil +} + +// lexTop consumes elements at the top level of TOML data. +func lexTop(lx *lexer) stateFn { + r := lx.next() + if isWhitespace(r) || isNL(r) { + return lexSkip(lx, lexTop) + } + switch r { + case commentStart: + lx.push(lexTop) + return lexCommentStart + case tableStart: + return lexTableStart + case eof: + if lx.pos > lx.start { + return lx.errorf("unexpected EOF") + } + lx.emit(itemEOF) + return nil + } + + // At this point, the only valid item can be a key, so we back up + // and let the key lexer do the rest. + lx.backup() + lx.push(lexTopEnd) + return lexKeyStart +} + +// lexTopEnd is entered whenever a top-level item has been consumed. (A value +// or a table.) It must see only whitespace, and will turn back to lexTop +// upon a newline. If it sees EOF, it will quit the lexer successfully. +func lexTopEnd(lx *lexer) stateFn { + r := lx.next() + switch { + case r == commentStart: + // a comment will read to a newline for us. + lx.push(lexTop) + return lexCommentStart + case isWhitespace(r): + return lexTopEnd + case isNL(r): + lx.ignore() + return lexTop + case r == eof: + lx.emit(itemEOF) + return nil + } + return lx.errorf("expected a top-level item to end with a newline, "+ + "comment, or EOF, but got %q instead", r) +} + +// lexTable lexes the beginning of a table. Namely, it makes sure that +// it starts with a character other than '.' and ']'. +// It assumes that '[' has already been consumed. +// It also handles the case that this is an item in an array of tables. +// e.g., '[[name]]'. +func lexTableStart(lx *lexer) stateFn { + if lx.peek() == arrayTableStart { + lx.next() + lx.emit(itemArrayTableStart) + lx.push(lexArrayTableEnd) + } else { + lx.emit(itemTableStart) + lx.push(lexTableEnd) + } + return lexTableNameStart +} + +func lexTableEnd(lx *lexer) stateFn { + lx.emit(itemTableEnd) + return lexTopEnd +} + +func lexArrayTableEnd(lx *lexer) stateFn { + if r := lx.next(); r != arrayTableEnd { + return lx.errorf("expected end of table array name delimiter %q, "+ + "but got %q instead", arrayTableEnd, r) + } + lx.emit(itemArrayTableEnd) + return lexTopEnd +} + +func lexTableNameStart(lx *lexer) stateFn { + lx.skip(isWhitespace) + switch r := lx.peek(); { + case r == tableEnd || r == eof: + return lx.errorf("unexpected end of table name " + + "(table names cannot be empty)") + case r == tableSep: + return lx.errorf("unexpected table separator " + + "(table names cannot be empty)") + case r == stringStart || r == rawStringStart: + lx.ignore() + lx.push(lexTableNameEnd) + return lexValue // reuse string lexing + default: + return lexBareTableName + } +} + +// lexBareTableName lexes the name of a table. It assumes that at least one +// valid character for the table has already been read. +func lexBareTableName(lx *lexer) stateFn { + r := lx.next() + if isBareKeyChar(r) { + return lexBareTableName + } + lx.backup() + lx.emit(itemText) + return lexTableNameEnd +} + +// lexTableNameEnd reads the end of a piece of a table name, optionally +// consuming whitespace. +func lexTableNameEnd(lx *lexer) stateFn { + lx.skip(isWhitespace) + switch r := lx.next(); { + case isWhitespace(r): + return lexTableNameEnd + case r == tableSep: + lx.ignore() + return lexTableNameStart + case r == tableEnd: + return lx.pop() + default: + return lx.errorf("expected '.' or ']' to end table name, "+ + "but got %q instead", r) + } +} + +// lexKeyStart consumes a key name up until the first non-whitespace character. +// lexKeyStart will ignore whitespace. +func lexKeyStart(lx *lexer) stateFn { + r := lx.peek() + switch { + case r == keySep: + return lx.errorf("unexpected key separator %q", keySep) + case isWhitespace(r) || isNL(r): + lx.next() + return lexSkip(lx, lexKeyStart) + case r == stringStart || r == rawStringStart: + lx.ignore() + lx.emit(itemKeyStart) + lx.push(lexKeyEnd) + return lexValue // reuse string lexing + default: + lx.ignore() + lx.emit(itemKeyStart) + return lexBareKey + } +} + +// lexBareKey consumes the text of a bare key. Assumes that the first character +// (which is not whitespace) has not yet been consumed. +func lexBareKey(lx *lexer) stateFn { + switch r := lx.next(); { + case isBareKeyChar(r): + return lexBareKey + case isWhitespace(r): + lx.backup() + lx.emit(itemText) + return lexKeyEnd + case r == keySep: + lx.backup() + lx.emit(itemText) + return lexKeyEnd + default: + return lx.errorf("bare keys cannot contain %q", r) + } +} + +// lexKeyEnd consumes the end of a key and trims whitespace (up to the key +// separator). +func lexKeyEnd(lx *lexer) stateFn { + switch r := lx.next(); { + case r == keySep: + return lexSkip(lx, lexValue) + case isWhitespace(r): + return lexSkip(lx, lexKeyEnd) + default: + return lx.errorf("expected key separator %q, but got %q instead", + keySep, r) + } +} + +// lexValue starts the consumption of a value anywhere a value is expected. +// lexValue will ignore whitespace. +// After a value is lexed, the last state on the next is popped and returned. +func lexValue(lx *lexer) stateFn { + // We allow whitespace to precede a value, but NOT newlines. + // In array syntax, the array states are responsible for ignoring newlines. + r := lx.next() + switch { + case isWhitespace(r): + return lexSkip(lx, lexValue) + case isDigit(r): + lx.backup() // avoid an extra state and use the same as above + return lexNumberOrDateStart + } + switch r { + case arrayStart: + lx.ignore() + lx.emit(itemArray) + return lexArrayValue + case inlineTableStart: + lx.ignore() + lx.emit(itemInlineTableStart) + return lexInlineTableValue + case stringStart: + if lx.accept(stringStart) { + if lx.accept(stringStart) { + lx.ignore() // Ignore """ + return lexMultilineString + } + lx.backup() + } + lx.ignore() // ignore the '"' + return lexString + case rawStringStart: + if lx.accept(rawStringStart) { + if lx.accept(rawStringStart) { + lx.ignore() // Ignore """ + return lexMultilineRawString + } + lx.backup() + } + lx.ignore() // ignore the "'" + return lexRawString + case '+', '-': + return lexNumberStart + case '.': // special error case, be kind to users + return lx.errorf("floats must start with a digit, not '.'") + } + if unicode.IsLetter(r) { + // Be permissive here; lexBool will give a nice error if the + // user wrote something like + // x = foo + // (i.e. not 'true' or 'false' but is something else word-like.) + lx.backup() + return lexBool + } + return lx.errorf("expected value but found %q instead", r) +} + +// lexArrayValue consumes one value in an array. It assumes that '[' or ',' +// have already been consumed. All whitespace and newlines are ignored. +func lexArrayValue(lx *lexer) stateFn { + r := lx.next() + switch { + case isWhitespace(r) || isNL(r): + return lexSkip(lx, lexArrayValue) + case r == commentStart: + lx.push(lexArrayValue) + return lexCommentStart + case r == comma: + return lx.errorf("unexpected comma") + case r == arrayEnd: + // NOTE(caleb): The spec isn't clear about whether you can have + // a trailing comma or not, so we'll allow it. + return lexArrayEnd + } + + lx.backup() + lx.push(lexArrayValueEnd) + return lexValue +} + +// lexArrayValueEnd consumes everything between the end of an array value and +// the next value (or the end of the array): it ignores whitespace and newlines +// and expects either a ',' or a ']'. +func lexArrayValueEnd(lx *lexer) stateFn { + r := lx.next() + switch { + case isWhitespace(r) || isNL(r): + return lexSkip(lx, lexArrayValueEnd) + case r == commentStart: + lx.push(lexArrayValueEnd) + return lexCommentStart + case r == comma: + lx.ignore() + return lexArrayValue // move on to the next value + case r == arrayEnd: + return lexArrayEnd + } + return lx.errorf( + "expected a comma or array terminator %q, but got %q instead", + arrayEnd, r, + ) +} + +// lexArrayEnd finishes the lexing of an array. +// It assumes that a ']' has just been consumed. +func lexArrayEnd(lx *lexer) stateFn { + lx.ignore() + lx.emit(itemArrayEnd) + return lx.pop() +} + +// lexInlineTableValue consumes one key/value pair in an inline table. +// It assumes that '{' or ',' have already been consumed. Whitespace is ignored. +func lexInlineTableValue(lx *lexer) stateFn { + r := lx.next() + switch { + case isWhitespace(r): + return lexSkip(lx, lexInlineTableValue) + case isNL(r): + return lx.errorf("newlines not allowed within inline tables") + case r == commentStart: + lx.push(lexInlineTableValue) + return lexCommentStart + case r == comma: + return lx.errorf("unexpected comma") + case r == inlineTableEnd: + return lexInlineTableEnd + } + lx.backup() + lx.push(lexInlineTableValueEnd) + return lexKeyStart +} + +// lexInlineTableValueEnd consumes everything between the end of an inline table +// key/value pair and the next pair (or the end of the table): +// it ignores whitespace and expects either a ',' or a '}'. +func lexInlineTableValueEnd(lx *lexer) stateFn { + r := lx.next() + switch { + case isWhitespace(r): + return lexSkip(lx, lexInlineTableValueEnd) + case isNL(r): + return lx.errorf("newlines not allowed within inline tables") + case r == commentStart: + lx.push(lexInlineTableValueEnd) + return lexCommentStart + case r == comma: + lx.ignore() + return lexInlineTableValue + case r == inlineTableEnd: + return lexInlineTableEnd + } + return lx.errorf("expected a comma or an inline table terminator %q, "+ + "but got %q instead", inlineTableEnd, r) +} + +// lexInlineTableEnd finishes the lexing of an inline table. +// It assumes that a '}' has just been consumed. +func lexInlineTableEnd(lx *lexer) stateFn { + lx.ignore() + lx.emit(itemInlineTableEnd) + return lx.pop() +} + +// lexString consumes the inner contents of a string. It assumes that the +// beginning '"' has already been consumed and ignored. +func lexString(lx *lexer) stateFn { + r := lx.next() + switch { + case r == eof: + return lx.errorf("unexpected EOF") + case isNL(r): + return lx.errorf("strings cannot contain newlines") + case r == '\\': + lx.push(lexString) + return lexStringEscape + case r == stringEnd: + lx.backup() + lx.emit(itemString) + lx.next() + lx.ignore() + return lx.pop() + } + return lexString +} + +// lexMultilineString consumes the inner contents of a string. It assumes that +// the beginning '"""' has already been consumed and ignored. +func lexMultilineString(lx *lexer) stateFn { + switch lx.next() { + case eof: + return lx.errorf("unexpected EOF") + case '\\': + return lexMultilineStringEscape + case stringEnd: + if lx.accept(stringEnd) { + if lx.accept(stringEnd) { + lx.backup() + lx.backup() + lx.backup() + lx.emit(itemMultilineString) + lx.next() + lx.next() + lx.next() + lx.ignore() + return lx.pop() + } + lx.backup() + } + } + return lexMultilineString +} + +// lexRawString consumes a raw string. Nothing can be escaped in such a string. +// It assumes that the beginning "'" has already been consumed and ignored. +func lexRawString(lx *lexer) stateFn { + r := lx.next() + switch { + case r == eof: + return lx.errorf("unexpected EOF") + case isNL(r): + return lx.errorf("strings cannot contain newlines") + case r == rawStringEnd: + lx.backup() + lx.emit(itemRawString) + lx.next() + lx.ignore() + return lx.pop() + } + return lexRawString +} + +// lexMultilineRawString consumes a raw string. Nothing can be escaped in such +// a string. It assumes that the beginning "'''" has already been consumed and +// ignored. +func lexMultilineRawString(lx *lexer) stateFn { + switch lx.next() { + case eof: + return lx.errorf("unexpected EOF") + case rawStringEnd: + if lx.accept(rawStringEnd) { + if lx.accept(rawStringEnd) { + lx.backup() + lx.backup() + lx.backup() + lx.emit(itemRawMultilineString) + lx.next() + lx.next() + lx.next() + lx.ignore() + return lx.pop() + } + lx.backup() + } + } + return lexMultilineRawString +} + +// lexMultilineStringEscape consumes an escaped character. It assumes that the +// preceding '\\' has already been consumed. +func lexMultilineStringEscape(lx *lexer) stateFn { + // Handle the special case first: + if isNL(lx.next()) { + return lexMultilineString + } + lx.backup() + lx.push(lexMultilineString) + return lexStringEscape(lx) +} + +func lexStringEscape(lx *lexer) stateFn { + r := lx.next() + switch r { + case 'b': + fallthrough + case 't': + fallthrough + case 'n': + fallthrough + case 'f': + fallthrough + case 'r': + fallthrough + case '"': + fallthrough + case '\\': + return lx.pop() + case 'u': + return lexShortUnicodeEscape + case 'U': + return lexLongUnicodeEscape + } + return lx.errorf("invalid escape character %q; only the following "+ + "escape characters are allowed: "+ + `\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX`, r) +} + +func lexShortUnicodeEscape(lx *lexer) stateFn { + var r rune + for i := 0; i < 4; i++ { + r = lx.next() + if !isHexadecimal(r) { + return lx.errorf(`expected four hexadecimal digits after '\u', `+ + "but got %q instead", lx.current()) + } + } + return lx.pop() +} + +func lexLongUnicodeEscape(lx *lexer) stateFn { + var r rune + for i := 0; i < 8; i++ { + r = lx.next() + if !isHexadecimal(r) { + return lx.errorf(`expected eight hexadecimal digits after '\U', `+ + "but got %q instead", lx.current()) + } + } + return lx.pop() +} + +// lexNumberOrDateStart consumes either an integer, a float, or datetime. +func lexNumberOrDateStart(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexNumberOrDate + } + switch r { + case '_': + return lexNumber + case 'e', 'E': + return lexFloat + case '.': + return lx.errorf("floats must start with a digit, not '.'") + } + return lx.errorf("expected a digit but got %q", r) +} + +// lexNumberOrDate consumes either an integer, float or datetime. +func lexNumberOrDate(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexNumberOrDate + } + switch r { + case '-': + return lexDatetime + case '_': + return lexNumber + case '.', 'e', 'E': + return lexFloat + } + + lx.backup() + lx.emit(itemInteger) + return lx.pop() +} + +// lexDatetime consumes a Datetime, to a first approximation. +// The parser validates that it matches one of the accepted formats. +func lexDatetime(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexDatetime + } + switch r { + case '-', 'T', ':', '.', 'Z', '+': + return lexDatetime + } + + lx.backup() + lx.emit(itemDatetime) + return lx.pop() +} + +// lexNumberStart consumes either an integer or a float. It assumes that a sign +// has already been read, but that *no* digits have been consumed. +// lexNumberStart will move to the appropriate integer or float states. +func lexNumberStart(lx *lexer) stateFn { + // We MUST see a digit. Even floats have to start with a digit. + r := lx.next() + if !isDigit(r) { + if r == '.' { + return lx.errorf("floats must start with a digit, not '.'") + } + return lx.errorf("expected a digit but got %q", r) + } + return lexNumber +} + +// lexNumber consumes an integer or a float after seeing the first digit. +func lexNumber(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexNumber + } + switch r { + case '_': + return lexNumber + case '.', 'e', 'E': + return lexFloat + } + + lx.backup() + lx.emit(itemInteger) + return lx.pop() +} + +// lexFloat consumes the elements of a float. It allows any sequence of +// float-like characters, so floats emitted by the lexer are only a first +// approximation and must be validated by the parser. +func lexFloat(lx *lexer) stateFn { + r := lx.next() + if isDigit(r) { + return lexFloat + } + switch r { + case '_', '.', '-', '+', 'e', 'E': + return lexFloat + } + + lx.backup() + lx.emit(itemFloat) + return lx.pop() +} + +// lexBool consumes a bool string: 'true' or 'false. +func lexBool(lx *lexer) stateFn { + var rs []rune + for { + r := lx.next() + if !unicode.IsLetter(r) { + lx.backup() + break + } + rs = append(rs, r) + } + s := string(rs) + switch s { + case "true", "false": + lx.emit(itemBool) + return lx.pop() + } + return lx.errorf("expected value but found %q instead", s) +} + +// lexCommentStart begins the lexing of a comment. It will emit +// itemCommentStart and consume no characters, passing control to lexComment. +func lexCommentStart(lx *lexer) stateFn { + lx.ignore() + lx.emit(itemCommentStart) + return lexComment +} + +// lexComment lexes an entire comment. It assumes that '#' has been consumed. +// It will consume *up to* the first newline character, and pass control +// back to the last state on the stack. +func lexComment(lx *lexer) stateFn { + r := lx.peek() + if isNL(r) || r == eof { + lx.emit(itemText) + return lx.pop() + } + lx.next() + return lexComment +} + +// lexSkip ignores all slurped input and moves on to the next state. +func lexSkip(lx *lexer, nextState stateFn) stateFn { + return func(lx *lexer) stateFn { + lx.ignore() + return nextState + } +} + +// isWhitespace returns true if `r` is a whitespace character according +// to the spec. +func isWhitespace(r rune) bool { + return r == '\t' || r == ' ' +} + +func isNL(r rune) bool { + return r == '\n' || r == '\r' +} + +func isDigit(r rune) bool { + return r >= '0' && r <= '9' +} + +func isHexadecimal(r rune) bool { + return (r >= '0' && r <= '9') || + (r >= 'a' && r <= 'f') || + (r >= 'A' && r <= 'F') +} + +func isBareKeyChar(r rune) bool { + return (r >= 'A' && r <= 'Z') || + (r >= 'a' && r <= 'z') || + (r >= '0' && r <= '9') || + r == '_' || + r == '-' +} + +func (itype itemType) String() string { + switch itype { + case itemError: + return "Error" + case itemNIL: + return "NIL" + case itemEOF: + return "EOF" + case itemText: + return "Text" + case itemString, itemRawString, itemMultilineString, itemRawMultilineString: + return "String" + case itemBool: + return "Bool" + case itemInteger: + return "Integer" + case itemFloat: + return "Float" + case itemDatetime: + return "DateTime" + case itemTableStart: + return "TableStart" + case itemTableEnd: + return "TableEnd" + case itemKeyStart: + return "KeyStart" + case itemArray: + return "Array" + case itemArrayEnd: + return "ArrayEnd" + case itemCommentStart: + return "CommentStart" + } + panic(fmt.Sprintf("BUG: Unknown type '%d'.", int(itype))) +} + +func (item item) String() string { + return fmt.Sprintf("(%s, %s)", item.typ.String(), item.val) +} diff --git a/vendor/github.com/BurntSushi/toml/parse.go b/vendor/github.com/BurntSushi/toml/parse.go new file mode 100644 index 000000000..50869ef92 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/parse.go @@ -0,0 +1,592 @@ +package toml + +import ( + "fmt" + "strconv" + "strings" + "time" + "unicode" + "unicode/utf8" +) + +type parser struct { + mapping map[string]interface{} + types map[string]tomlType + lx *lexer + + // A list of keys in the order that they appear in the TOML data. + ordered []Key + + // the full key for the current hash in scope + context Key + + // the base key name for everything except hashes + currentKey string + + // rough approximation of line number + approxLine int + + // A map of 'key.group.names' to whether they were created implicitly. + implicits map[string]bool +} + +type parseError string + +func (pe parseError) Error() string { + return string(pe) +} + +func parse(data string) (p *parser, err error) { + defer func() { + if r := recover(); r != nil { + var ok bool + if err, ok = r.(parseError); ok { + return + } + panic(r) + } + }() + + p = &parser{ + mapping: make(map[string]interface{}), + types: make(map[string]tomlType), + lx: lex(data), + ordered: make([]Key, 0), + implicits: make(map[string]bool), + } + for { + item := p.next() + if item.typ == itemEOF { + break + } + p.topLevel(item) + } + + return p, nil +} + +func (p *parser) panicf(format string, v ...interface{}) { + msg := fmt.Sprintf("Near line %d (last key parsed '%s'): %s", + p.approxLine, p.current(), fmt.Sprintf(format, v...)) + panic(parseError(msg)) +} + +func (p *parser) next() item { + it := p.lx.nextItem() + if it.typ == itemError { + p.panicf("%s", it.val) + } + return it +} + +func (p *parser) bug(format string, v ...interface{}) { + panic(fmt.Sprintf("BUG: "+format+"\n\n", v...)) +} + +func (p *parser) expect(typ itemType) item { + it := p.next() + p.assertEqual(typ, it.typ) + return it +} + +func (p *parser) assertEqual(expected, got itemType) { + if expected != got { + p.bug("Expected '%s' but got '%s'.", expected, got) + } +} + +func (p *parser) topLevel(item item) { + switch item.typ { + case itemCommentStart: + p.approxLine = item.line + p.expect(itemText) + case itemTableStart: + kg := p.next() + p.approxLine = kg.line + + var key Key + for ; kg.typ != itemTableEnd && kg.typ != itemEOF; kg = p.next() { + key = append(key, p.keyString(kg)) + } + p.assertEqual(itemTableEnd, kg.typ) + + p.establishContext(key, false) + p.setType("", tomlHash) + p.ordered = append(p.ordered, key) + case itemArrayTableStart: + kg := p.next() + p.approxLine = kg.line + + var key Key + for ; kg.typ != itemArrayTableEnd && kg.typ != itemEOF; kg = p.next() { + key = append(key, p.keyString(kg)) + } + p.assertEqual(itemArrayTableEnd, kg.typ) + + p.establishContext(key, true) + p.setType("", tomlArrayHash) + p.ordered = append(p.ordered, key) + case itemKeyStart: + kname := p.next() + p.approxLine = kname.line + p.currentKey = p.keyString(kname) + + val, typ := p.value(p.next()) + p.setValue(p.currentKey, val) + p.setType(p.currentKey, typ) + p.ordered = append(p.ordered, p.context.add(p.currentKey)) + p.currentKey = "" + default: + p.bug("Unexpected type at top level: %s", item.typ) + } +} + +// Gets a string for a key (or part of a key in a table name). +func (p *parser) keyString(it item) string { + switch it.typ { + case itemText: + return it.val + case itemString, itemMultilineString, + itemRawString, itemRawMultilineString: + s, _ := p.value(it) + return s.(string) + default: + p.bug("Unexpected key type: %s", it.typ) + panic("unreachable") + } +} + +// value translates an expected value from the lexer into a Go value wrapped +// as an empty interface. +func (p *parser) value(it item) (interface{}, tomlType) { + switch it.typ { + case itemString: + return p.replaceEscapes(it.val), p.typeOfPrimitive(it) + case itemMultilineString: + trimmed := stripFirstNewline(stripEscapedWhitespace(it.val)) + return p.replaceEscapes(trimmed), p.typeOfPrimitive(it) + case itemRawString: + return it.val, p.typeOfPrimitive(it) + case itemRawMultilineString: + return stripFirstNewline(it.val), p.typeOfPrimitive(it) + case itemBool: + switch it.val { + case "true": + return true, p.typeOfPrimitive(it) + case "false": + return false, p.typeOfPrimitive(it) + } + p.bug("Expected boolean value, but got '%s'.", it.val) + case itemInteger: + if !numUnderscoresOK(it.val) { + p.panicf("Invalid integer %q: underscores must be surrounded by digits", + it.val) + } + val := strings.Replace(it.val, "_", "", -1) + num, err := strconv.ParseInt(val, 10, 64) + if err != nil { + // Distinguish integer values. Normally, it'd be a bug if the lexer + // provides an invalid integer, but it's possible that the number is + // out of range of valid values (which the lexer cannot determine). + // So mark the former as a bug but the latter as a legitimate user + // error. + if e, ok := err.(*strconv.NumError); ok && + e.Err == strconv.ErrRange { + + p.panicf("Integer '%s' is out of the range of 64-bit "+ + "signed integers.", it.val) + } else { + p.bug("Expected integer value, but got '%s'.", it.val) + } + } + return num, p.typeOfPrimitive(it) + case itemFloat: + parts := strings.FieldsFunc(it.val, func(r rune) bool { + switch r { + case '.', 'e', 'E': + return true + } + return false + }) + for _, part := range parts { + if !numUnderscoresOK(part) { + p.panicf("Invalid float %q: underscores must be "+ + "surrounded by digits", it.val) + } + } + if !numPeriodsOK(it.val) { + // As a special case, numbers like '123.' or '1.e2', + // which are valid as far as Go/strconv are concerned, + // must be rejected because TOML says that a fractional + // part consists of '.' followed by 1+ digits. + p.panicf("Invalid float %q: '.' must be followed "+ + "by one or more digits", it.val) + } + val := strings.Replace(it.val, "_", "", -1) + num, err := strconv.ParseFloat(val, 64) + if err != nil { + if e, ok := err.(*strconv.NumError); ok && + e.Err == strconv.ErrRange { + + p.panicf("Float '%s' is out of the range of 64-bit "+ + "IEEE-754 floating-point numbers.", it.val) + } else { + p.panicf("Invalid float value: %q", it.val) + } + } + return num, p.typeOfPrimitive(it) + case itemDatetime: + var t time.Time + var ok bool + var err error + for _, format := range []string{ + "2006-01-02T15:04:05Z07:00", + "2006-01-02T15:04:05", + "2006-01-02", + } { + t, err = time.ParseInLocation(format, it.val, time.Local) + if err == nil { + ok = true + break + } + } + if !ok { + p.panicf("Invalid TOML Datetime: %q.", it.val) + } + return t, p.typeOfPrimitive(it) + case itemArray: + array := make([]interface{}, 0) + types := make([]tomlType, 0) + + for it = p.next(); it.typ != itemArrayEnd; it = p.next() { + if it.typ == itemCommentStart { + p.expect(itemText) + continue + } + + val, typ := p.value(it) + array = append(array, val) + types = append(types, typ) + } + return array, p.typeOfArray(types) + case itemInlineTableStart: + var ( + hash = make(map[string]interface{}) + outerContext = p.context + outerKey = p.currentKey + ) + + p.context = append(p.context, p.currentKey) + p.currentKey = "" + for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() { + if it.typ != itemKeyStart { + p.bug("Expected key start but instead found %q, around line %d", + it.val, p.approxLine) + } + if it.typ == itemCommentStart { + p.expect(itemText) + continue + } + + // retrieve key + k := p.next() + p.approxLine = k.line + kname := p.keyString(k) + + // retrieve value + p.currentKey = kname + val, typ := p.value(p.next()) + // make sure we keep metadata up to date + p.setType(kname, typ) + p.ordered = append(p.ordered, p.context.add(p.currentKey)) + hash[kname] = val + } + p.context = outerContext + p.currentKey = outerKey + return hash, tomlHash + } + p.bug("Unexpected value type: %s", it.typ) + panic("unreachable") +} + +// numUnderscoresOK checks whether each underscore in s is surrounded by +// characters that are not underscores. +func numUnderscoresOK(s string) bool { + accept := false + for _, r := range s { + if r == '_' { + if !accept { + return false + } + accept = false + continue + } + accept = true + } + return accept +} + +// numPeriodsOK checks whether every period in s is followed by a digit. +func numPeriodsOK(s string) bool { + period := false + for _, r := range s { + if period && !isDigit(r) { + return false + } + period = r == '.' + } + return !period +} + +// establishContext sets the current context of the parser, +// where the context is either a hash or an array of hashes. Which one is +// set depends on the value of the `array` parameter. +// +// Establishing the context also makes sure that the key isn't a duplicate, and +// will create implicit hashes automatically. +func (p *parser) establishContext(key Key, array bool) { + var ok bool + + // Always start at the top level and drill down for our context. + hashContext := p.mapping + keyContext := make(Key, 0) + + // We only need implicit hashes for key[0:-1] + for _, k := range key[0 : len(key)-1] { + _, ok = hashContext[k] + keyContext = append(keyContext, k) + + // No key? Make an implicit hash and move on. + if !ok { + p.addImplicit(keyContext) + hashContext[k] = make(map[string]interface{}) + } + + // If the hash context is actually an array of tables, then set + // the hash context to the last element in that array. + // + // Otherwise, it better be a table, since this MUST be a key group (by + // virtue of it not being the last element in a key). + switch t := hashContext[k].(type) { + case []map[string]interface{}: + hashContext = t[len(t)-1] + case map[string]interface{}: + hashContext = t + default: + p.panicf("Key '%s' was already created as a hash.", keyContext) + } + } + + p.context = keyContext + if array { + // If this is the first element for this array, then allocate a new + // list of tables for it. + k := key[len(key)-1] + if _, ok := hashContext[k]; !ok { + hashContext[k] = make([]map[string]interface{}, 0, 5) + } + + // Add a new table. But make sure the key hasn't already been used + // for something else. + if hash, ok := hashContext[k].([]map[string]interface{}); ok { + hashContext[k] = append(hash, make(map[string]interface{})) + } else { + p.panicf("Key '%s' was already created and cannot be used as "+ + "an array.", keyContext) + } + } else { + p.setValue(key[len(key)-1], make(map[string]interface{})) + } + p.context = append(p.context, key[len(key)-1]) +} + +// setValue sets the given key to the given value in the current context. +// It will make sure that the key hasn't already been defined, account for +// implicit key groups. +func (p *parser) setValue(key string, value interface{}) { + var tmpHash interface{} + var ok bool + + hash := p.mapping + keyContext := make(Key, 0) + for _, k := range p.context { + keyContext = append(keyContext, k) + if tmpHash, ok = hash[k]; !ok { + p.bug("Context for key '%s' has not been established.", keyContext) + } + switch t := tmpHash.(type) { + case []map[string]interface{}: + // The context is a table of hashes. Pick the most recent table + // defined as the current hash. + hash = t[len(t)-1] + case map[string]interface{}: + hash = t + default: + p.bug("Expected hash to have type 'map[string]interface{}', but "+ + "it has '%T' instead.", tmpHash) + } + } + keyContext = append(keyContext, key) + + if _, ok := hash[key]; ok { + // Typically, if the given key has already been set, then we have + // to raise an error since duplicate keys are disallowed. However, + // it's possible that a key was previously defined implicitly. In this + // case, it is allowed to be redefined concretely. (See the + // `tests/valid/implicit-and-explicit-after.toml` test in `toml-test`.) + // + // But we have to make sure to stop marking it as an implicit. (So that + // another redefinition provokes an error.) + // + // Note that since it has already been defined (as a hash), we don't + // want to overwrite it. So our business is done. + if p.isImplicit(keyContext) { + p.removeImplicit(keyContext) + return + } + + // Otherwise, we have a concrete key trying to override a previous + // key, which is *always* wrong. + p.panicf("Key '%s' has already been defined.", keyContext) + } + hash[key] = value +} + +// setType sets the type of a particular value at a given key. +// It should be called immediately AFTER setValue. +// +// Note that if `key` is empty, then the type given will be applied to the +// current context (which is either a table or an array of tables). +func (p *parser) setType(key string, typ tomlType) { + keyContext := make(Key, 0, len(p.context)+1) + for _, k := range p.context { + keyContext = append(keyContext, k) + } + if len(key) > 0 { // allow type setting for hashes + keyContext = append(keyContext, key) + } + p.types[keyContext.String()] = typ +} + +// addImplicit sets the given Key as having been created implicitly. +func (p *parser) addImplicit(key Key) { + p.implicits[key.String()] = true +} + +// removeImplicit stops tagging the given key as having been implicitly +// created. +func (p *parser) removeImplicit(key Key) { + p.implicits[key.String()] = false +} + +// isImplicit returns true if the key group pointed to by the key was created +// implicitly. +func (p *parser) isImplicit(key Key) bool { + return p.implicits[key.String()] +} + +// current returns the full key name of the current context. +func (p *parser) current() string { + if len(p.currentKey) == 0 { + return p.context.String() + } + if len(p.context) == 0 { + return p.currentKey + } + return fmt.Sprintf("%s.%s", p.context, p.currentKey) +} + +func stripFirstNewline(s string) string { + if len(s) == 0 || s[0] != '\n' { + return s + } + return s[1:] +} + +func stripEscapedWhitespace(s string) string { + esc := strings.Split(s, "\\\n") + if len(esc) > 1 { + for i := 1; i < len(esc); i++ { + esc[i] = strings.TrimLeftFunc(esc[i], unicode.IsSpace) + } + } + return strings.Join(esc, "") +} + +func (p *parser) replaceEscapes(str string) string { + var replaced []rune + s := []byte(str) + r := 0 + for r < len(s) { + if s[r] != '\\' { + c, size := utf8.DecodeRune(s[r:]) + r += size + replaced = append(replaced, c) + continue + } + r += 1 + if r >= len(s) { + p.bug("Escape sequence at end of string.") + return "" + } + switch s[r] { + default: + p.bug("Expected valid escape code after \\, but got %q.", s[r]) + return "" + case 'b': + replaced = append(replaced, rune(0x0008)) + r += 1 + case 't': + replaced = append(replaced, rune(0x0009)) + r += 1 + case 'n': + replaced = append(replaced, rune(0x000A)) + r += 1 + case 'f': + replaced = append(replaced, rune(0x000C)) + r += 1 + case 'r': + replaced = append(replaced, rune(0x000D)) + r += 1 + case '"': + replaced = append(replaced, rune(0x0022)) + r += 1 + case '\\': + replaced = append(replaced, rune(0x005C)) + r += 1 + case 'u': + // At this point, we know we have a Unicode escape of the form + // `uXXXX` at [r, r+5). (Because the lexer guarantees this + // for us.) + escaped := p.asciiEscapeToUnicode(s[r+1 : r+5]) + replaced = append(replaced, escaped) + r += 5 + case 'U': + // At this point, we know we have a Unicode escape of the form + // `uXXXX` at [r, r+9). (Because the lexer guarantees this + // for us.) + escaped := p.asciiEscapeToUnicode(s[r+1 : r+9]) + replaced = append(replaced, escaped) + r += 9 + } + } + return string(replaced) +} + +func (p *parser) asciiEscapeToUnicode(bs []byte) rune { + s := string(bs) + hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32) + if err != nil { + p.bug("Could not parse '%s' as a hexadecimal number, but the "+ + "lexer claims it's OK: %s", s, err) + } + if !utf8.ValidRune(rune(hex)) { + p.panicf("Escaped character '\\u%s' is not valid UTF-8.", s) + } + return rune(hex) +} + +func isStringType(ty itemType) bool { + return ty == itemString || ty == itemMultilineString || + ty == itemRawString || ty == itemRawMultilineString +} diff --git a/vendor/github.com/BurntSushi/toml/session.vim b/vendor/github.com/BurntSushi/toml/session.vim new file mode 100644 index 000000000..562164be0 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/session.vim @@ -0,0 +1 @@ +au BufWritePost *.go silent!make tags > /dev/null 2>&1 diff --git a/vendor/github.com/BurntSushi/toml/type_check.go b/vendor/github.com/BurntSushi/toml/type_check.go new file mode 100644 index 000000000..c73f8afc1 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/type_check.go @@ -0,0 +1,91 @@ +package toml + +// tomlType represents any Go type that corresponds to a TOML type. +// While the first draft of the TOML spec has a simplistic type system that +// probably doesn't need this level of sophistication, we seem to be militating +// toward adding real composite types. +type tomlType interface { + typeString() string +} + +// typeEqual accepts any two types and returns true if they are equal. +func typeEqual(t1, t2 tomlType) bool { + if t1 == nil || t2 == nil { + return false + } + return t1.typeString() == t2.typeString() +} + +func typeIsHash(t tomlType) bool { + return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash) +} + +type tomlBaseType string + +func (btype tomlBaseType) typeString() string { + return string(btype) +} + +func (btype tomlBaseType) String() string { + return btype.typeString() +} + +var ( + tomlInteger tomlBaseType = "Integer" + tomlFloat tomlBaseType = "Float" + tomlDatetime tomlBaseType = "Datetime" + tomlString tomlBaseType = "String" + tomlBool tomlBaseType = "Bool" + tomlArray tomlBaseType = "Array" + tomlHash tomlBaseType = "Hash" + tomlArrayHash tomlBaseType = "ArrayHash" +) + +// typeOfPrimitive returns a tomlType of any primitive value in TOML. +// Primitive values are: Integer, Float, Datetime, String and Bool. +// +// Passing a lexer item other than the following will cause a BUG message +// to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime. +func (p *parser) typeOfPrimitive(lexItem item) tomlType { + switch lexItem.typ { + case itemInteger: + return tomlInteger + case itemFloat: + return tomlFloat + case itemDatetime: + return tomlDatetime + case itemString: + return tomlString + case itemMultilineString: + return tomlString + case itemRawString: + return tomlString + case itemRawMultilineString: + return tomlString + case itemBool: + return tomlBool + } + p.bug("Cannot infer primitive type of lex item '%s'.", lexItem) + panic("unreachable") +} + +// typeOfArray returns a tomlType for an array given a list of types of its +// values. +// +// In the current spec, if an array is homogeneous, then its type is always +// "Array". If the array is not homogeneous, an error is generated. +func (p *parser) typeOfArray(types []tomlType) tomlType { + // Empty arrays are cool. + if len(types) == 0 { + return tomlArray + } + + theType := types[0] + for _, t := range types[1:] { + if !typeEqual(theType, t) { + p.panicf("Array contains values of type '%s' and '%s', but "+ + "arrays must be homogeneous.", theType, t) + } + } + return tomlArray +} diff --git a/vendor/github.com/BurntSushi/toml/type_fields.go b/vendor/github.com/BurntSushi/toml/type_fields.go new file mode 100644 index 000000000..608997c22 --- /dev/null +++ b/vendor/github.com/BurntSushi/toml/type_fields.go @@ -0,0 +1,242 @@ +package toml + +// Struct field handling is adapted from code in encoding/json: +// +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the Go distribution. + +import ( + "reflect" + "sort" + "sync" +) + +// A field represents a single field found in a struct. +type field struct { + name string // the name of the field (`toml` tag included) + tag bool // whether field has a `toml` tag + index []int // represents the depth of an anonymous field + typ reflect.Type // the type of the field +} + +// byName sorts field by name, breaking ties with depth, +// then breaking ties with "name came from toml tag", then +// breaking ties with index sequence. +type byName []field + +func (x byName) Len() int { return len(x) } + +func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +func (x byName) Less(i, j int) bool { + if x[i].name != x[j].name { + return x[i].name < x[j].name + } + if len(x[i].index) != len(x[j].index) { + return len(x[i].index) < len(x[j].index) + } + if x[i].tag != x[j].tag { + return x[i].tag + } + return byIndex(x).Less(i, j) +} + +// byIndex sorts field by index sequence. +type byIndex []field + +func (x byIndex) Len() int { return len(x) } + +func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +func (x byIndex) Less(i, j int) bool { + for k, xik := range x[i].index { + if k >= len(x[j].index) { + return false + } + if xik != x[j].index[k] { + return xik < x[j].index[k] + } + } + return len(x[i].index) < len(x[j].index) +} + +// typeFields returns a list of fields that TOML should recognize for the given +// type. The algorithm is breadth-first search over the set of structs to +// include - the top struct and then any reachable anonymous structs. +func typeFields(t reflect.Type) []field { + // Anonymous fields to explore at the current level and the next. + current := []field{} + next := []field{{typ: t}} + + // Count of queued names for current level and the next. + count := map[reflect.Type]int{} + nextCount := map[reflect.Type]int{} + + // Types already visited at an earlier level. + visited := map[reflect.Type]bool{} + + // Fields found. + var fields []field + + for len(next) > 0 { + current, next = next, current[:0] + count, nextCount = nextCount, map[reflect.Type]int{} + + for _, f := range current { + if visited[f.typ] { + continue + } + visited[f.typ] = true + + // Scan f.typ for fields to include. + for i := 0; i < f.typ.NumField(); i++ { + sf := f.typ.Field(i) + if sf.PkgPath != "" && !sf.Anonymous { // unexported + continue + } + opts := getOptions(sf.Tag) + if opts.skip { + continue + } + index := make([]int, len(f.index)+1) + copy(index, f.index) + index[len(f.index)] = i + + ft := sf.Type + if ft.Name() == "" && ft.Kind() == reflect.Ptr { + // Follow pointer. + ft = ft.Elem() + } + + // Record found field and index sequence. + if opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { + tagged := opts.name != "" + name := opts.name + if name == "" { + name = sf.Name + } + fields = append(fields, field{name, tagged, index, ft}) + if count[f.typ] > 1 { + // If there were multiple instances, add a second, + // so that the annihilation code will see a duplicate. + // It only cares about the distinction between 1 or 2, + // so don't bother generating any more copies. + fields = append(fields, fields[len(fields)-1]) + } + continue + } + + // Record new anonymous struct to explore in next round. + nextCount[ft]++ + if nextCount[ft] == 1 { + f := field{name: ft.Name(), index: index, typ: ft} + next = append(next, f) + } + } + } + } + + sort.Sort(byName(fields)) + + // Delete all fields that are hidden by the Go rules for embedded fields, + // except that fields with TOML tags are promoted. + + // The fields are sorted in primary order of name, secondary order + // of field index length. Loop over names; for each name, delete + // hidden fields by choosing the one dominant field that survives. + out := fields[:0] + for advance, i := 0, 0; i < len(fields); i += advance { + // One iteration per name. + // Find the sequence of fields with the name of this first field. + fi := fields[i] + name := fi.name + for advance = 1; i+advance < len(fields); advance++ { + fj := fields[i+advance] + if fj.name != name { + break + } + } + if advance == 1 { // Only one field with this name + out = append(out, fi) + continue + } + dominant, ok := dominantField(fields[i : i+advance]) + if ok { + out = append(out, dominant) + } + } + + fields = out + sort.Sort(byIndex(fields)) + + return fields +} + +// dominantField looks through the fields, all of which are known to +// have the same name, to find the single field that dominates the +// others using Go's embedding rules, modified by the presence of +// TOML tags. If there are multiple top-level fields, the boolean +// will be false: This condition is an error in Go and we skip all +// the fields. +func dominantField(fields []field) (field, bool) { + // The fields are sorted in increasing index-length order. The winner + // must therefore be one with the shortest index length. Drop all + // longer entries, which is easy: just truncate the slice. + length := len(fields[0].index) + tagged := -1 // Index of first tagged field. + for i, f := range fields { + if len(f.index) > length { + fields = fields[:i] + break + } + if f.tag { + if tagged >= 0 { + // Multiple tagged fields at the same level: conflict. + // Return no field. + return field{}, false + } + tagged = i + } + } + if tagged >= 0 { + return fields[tagged], true + } + // All remaining fields have the same length. If there's more than one, + // we have a conflict (two fields named "X" at the same level) and we + // return no field. + if len(fields) > 1 { + return field{}, false + } + return fields[0], true +} + +var fieldCache struct { + sync.RWMutex + m map[reflect.Type][]field +} + +// cachedTypeFields is like typeFields but uses a cache to avoid repeated work. +func cachedTypeFields(t reflect.Type) []field { + fieldCache.RLock() + f := fieldCache.m[t] + fieldCache.RUnlock() + if f != nil { + return f + } + + // Compute fields without lock. + // Might duplicate effort but won't hold other computations back. + f = typeFields(t) + if f == nil { + f = []field{} + } + + fieldCache.Lock() + if fieldCache.m == nil { + fieldCache.m = map[reflect.Type][]field{} + } + fieldCache.m[t] = f + fieldCache.Unlock() + return f +} diff --git a/vendor/github.com/fatih/camelcase/.travis.yml b/vendor/github.com/fatih/camelcase/.travis.yml new file mode 100644 index 000000000..3489e3871 --- /dev/null +++ b/vendor/github.com/fatih/camelcase/.travis.yml @@ -0,0 +1,3 @@ +language: go +go: 1.x + diff --git a/vendor/github.com/fatih/camelcase/LICENSE.md b/vendor/github.com/fatih/camelcase/LICENSE.md new file mode 100644 index 000000000..aa4a536ca --- /dev/null +++ b/vendor/github.com/fatih/camelcase/LICENSE.md @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2015 Fatih Arslan + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/fatih/camelcase/README.md b/vendor/github.com/fatih/camelcase/README.md new file mode 100644 index 000000000..105a6ae33 --- /dev/null +++ b/vendor/github.com/fatih/camelcase/README.md @@ -0,0 +1,58 @@ +# CamelCase [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/fatih/camelcase) [![Build Status](http://img.shields.io/travis/fatih/camelcase.svg?style=flat-square)](https://travis-ci.org/fatih/camelcase) + +CamelCase is a Golang (Go) package to split the words of a camelcase type +string into a slice of words. It can be used to convert a camelcase word (lower +or upper case) into any type of word. + +## Splitting rules: + +1. If string is not valid UTF-8, return it without splitting as + single item array. +2. Assign all unicode characters into one of 4 sets: lower case + letters, upper case letters, numbers, and all other characters. +3. Iterate through characters of string, introducing splits + between adjacent characters that belong to different sets. +4. Iterate through array of split strings, and if a given string + is upper case: + * if subsequent string is lower case: + * move last character of upper case string to beginning of + lower case string + +## Install + +```bash +go get github.com/fatih/camelcase +``` + +## Usage and examples + +```go +splitted := camelcase.Split("GolangPackage") + +fmt.Println(splitted[0], splitted[1]) // prints: "Golang", "Package" +``` + +Both lower camel case and upper camel case are supported. For more info please +check: [http://en.wikipedia.org/wiki/CamelCase](http://en.wikipedia.org/wiki/CamelCase) + +Below are some example cases: + +``` +"" => [] +"lowercase" => ["lowercase"] +"Class" => ["Class"] +"MyClass" => ["My", "Class"] +"MyC" => ["My", "C"] +"HTML" => ["HTML"] +"PDFLoader" => ["PDF", "Loader"] +"AString" => ["A", "String"] +"SimpleXMLParser" => ["Simple", "XML", "Parser"] +"vimRPCPlugin" => ["vim", "RPC", "Plugin"] +"GL11Version" => ["GL", "11", "Version"] +"99Bottles" => ["99", "Bottles"] +"May5" => ["May", "5"] +"BFG9000" => ["BFG", "9000"] +"BöseÜberraschung" => ["Böse", "Überraschung"] +"Two spaces" => ["Two", " ", "spaces"] +"BadUTF8\xe2\xe2\xa1" => ["BadUTF8\xe2\xe2\xa1"] +``` diff --git a/vendor/github.com/fatih/camelcase/camelcase.go b/vendor/github.com/fatih/camelcase/camelcase.go new file mode 100644 index 000000000..02160c9a4 --- /dev/null +++ b/vendor/github.com/fatih/camelcase/camelcase.go @@ -0,0 +1,90 @@ +// Package camelcase is a micro package to split the words of a camelcase type +// string into a slice of words. +package camelcase + +import ( + "unicode" + "unicode/utf8" +) + +// Split splits the camelcase word and returns a list of words. It also +// supports digits. Both lower camel case and upper camel case are supported. +// For more info please check: http://en.wikipedia.org/wiki/CamelCase +// +// Examples +// +// "" => [""] +// "lowercase" => ["lowercase"] +// "Class" => ["Class"] +// "MyClass" => ["My", "Class"] +// "MyC" => ["My", "C"] +// "HTML" => ["HTML"] +// "PDFLoader" => ["PDF", "Loader"] +// "AString" => ["A", "String"] +// "SimpleXMLParser" => ["Simple", "XML", "Parser"] +// "vimRPCPlugin" => ["vim", "RPC", "Plugin"] +// "GL11Version" => ["GL", "11", "Version"] +// "99Bottles" => ["99", "Bottles"] +// "May5" => ["May", "5"] +// "BFG9000" => ["BFG", "9000"] +// "BöseÜberraschung" => ["Böse", "Überraschung"] +// "Two spaces" => ["Two", " ", "spaces"] +// "BadUTF8\xe2\xe2\xa1" => ["BadUTF8\xe2\xe2\xa1"] +// +// Splitting rules +// +// 1) If string is not valid UTF-8, return it without splitting as +// single item array. +// 2) Assign all unicode characters into one of 4 sets: lower case +// letters, upper case letters, numbers, and all other characters. +// 3) Iterate through characters of string, introducing splits +// between adjacent characters that belong to different sets. +// 4) Iterate through array of split strings, and if a given string +// is upper case: +// if subsequent string is lower case: +// move last character of upper case string to beginning of +// lower case string +func Split(src string) (entries []string) { + // don't split invalid utf8 + if !utf8.ValidString(src) { + return []string{src} + } + entries = []string{} + var runes [][]rune + lastClass := 0 + class := 0 + // split into fields based on class of unicode character + for _, r := range src { + switch true { + case unicode.IsLower(r): + class = 1 + case unicode.IsUpper(r): + class = 2 + case unicode.IsDigit(r): + class = 3 + default: + class = 4 + } + if class == lastClass { + runes[len(runes)-1] = append(runes[len(runes)-1], r) + } else { + runes = append(runes, []rune{r}) + } + lastClass = class + } + // handle upper case -> lower case sequences, e.g. + // "PDFL", "oader" -> "PDF", "Loader" + for i := 0; i < len(runes)-1; i++ { + if unicode.IsUpper(runes[i][0]) && unicode.IsLower(runes[i+1][0]) { + runes[i+1] = append([]rune{runes[i][len(runes[i])-1]}, runes[i+1]...) + runes[i] = runes[i][:len(runes[i])-1] + } + } + // construct []string from results + for _, s := range runes { + if len(s) > 0 { + entries = append(entries, string(s)) + } + } + return +} diff --git a/vendor/github.com/globalsign/mgo/LICENSE b/vendor/github.com/globalsign/mgo/LICENSE new file mode 100644 index 000000000..770c7672b --- /dev/null +++ b/vendor/github.com/globalsign/mgo/LICENSE @@ -0,0 +1,25 @@ +mgo - MongoDB driver for Go + +Copyright (c) 2010-2013 - Gustavo Niemeyer + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/globalsign/mgo/bson/LICENSE b/vendor/github.com/globalsign/mgo/bson/LICENSE new file mode 100644 index 000000000..890326017 --- /dev/null +++ b/vendor/github.com/globalsign/mgo/bson/LICENSE @@ -0,0 +1,25 @@ +BSON library for Go + +Copyright (c) 2010-2012 - Gustavo Niemeyer + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/globalsign/mgo/bson/README.md b/vendor/github.com/globalsign/mgo/bson/README.md new file mode 100644 index 000000000..5c5819e61 --- /dev/null +++ b/vendor/github.com/globalsign/mgo/bson/README.md @@ -0,0 +1,12 @@ +[![GoDoc](https://godoc.org/github.com/globalsign/mgo/bson?status.svg)](https://godoc.org/github.com/globalsign/mgo/bson) + +An Implementation of BSON for Go +-------------------------------- + +Package bson is an implementation of the [BSON specification](http://bsonspec.org) for Go. + +While the BSON package implements the BSON spec as faithfully as possible, there +is some MongoDB specific behaviour (such as map keys `$in`, `$all`, etc) in the +`bson` package. The priority is for backwards compatibility for the `mgo` +driver, though fixes for obviously buggy behaviour is welcome (and features, etc +behind feature flags). diff --git a/vendor/github.com/globalsign/mgo/bson/bson.go b/vendor/github.com/globalsign/mgo/bson/bson.go new file mode 100644 index 000000000..eb87ef620 --- /dev/null +++ b/vendor/github.com/globalsign/mgo/bson/bson.go @@ -0,0 +1,836 @@ +// BSON library for Go +// +// Copyright (c) 2010-2012 - Gustavo Niemeyer +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Package bson is an implementation of the BSON specification for Go: +// +// http://bsonspec.org +// +// It was created as part of the mgo MongoDB driver for Go, but is standalone +// and may be used on its own without the driver. +package bson + +import ( + "bytes" + "crypto/md5" + "crypto/rand" + "encoding/binary" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "math" + "os" + "reflect" + "runtime" + "strings" + "sync" + "sync/atomic" + "time" +) + +//go:generate go run bson_corpus_spec_test_generator.go + +// -------------------------------------------------------------------------- +// The public API. + +// Element types constants from BSON specification. +const ( + ElementFloat64 byte = 0x01 + ElementString byte = 0x02 + ElementDocument byte = 0x03 + ElementArray byte = 0x04 + ElementBinary byte = 0x05 + Element06 byte = 0x06 + ElementObjectId byte = 0x07 + ElementBool byte = 0x08 + ElementDatetime byte = 0x09 + ElementNil byte = 0x0A + ElementRegEx byte = 0x0B + ElementDBPointer byte = 0x0C + ElementJavaScriptWithoutScope byte = 0x0D + ElementSymbol byte = 0x0E + ElementJavaScriptWithScope byte = 0x0F + ElementInt32 byte = 0x10 + ElementTimestamp byte = 0x11 + ElementInt64 byte = 0x12 + ElementDecimal128 byte = 0x13 + ElementMinKey byte = 0xFF + ElementMaxKey byte = 0x7F + + BinaryGeneric byte = 0x00 + BinaryFunction byte = 0x01 + BinaryBinaryOld byte = 0x02 + BinaryUUIDOld byte = 0x03 + BinaryUUID byte = 0x04 + BinaryMD5 byte = 0x05 + BinaryUserDefined byte = 0x80 +) + +// Getter interface: a value implementing the bson.Getter interface will have its GetBSON +// method called when the given value has to be marshalled, and the result +// of this method will be marshaled in place of the actual object. +// +// If GetBSON returns return a non-nil error, the marshalling procedure +// will stop and error out with the provided value. +type Getter interface { + GetBSON() (interface{}, error) +} + +// Setter interface: a value implementing the bson.Setter interface will receive the BSON +// value via the SetBSON method during unmarshaling, and the object +// itself will not be changed as usual. +// +// If setting the value works, the method should return nil or alternatively +// bson.ErrSetZero to set the respective field to its zero value (nil for +// pointer types). If SetBSON returns a value of type bson.TypeError, the +// BSON value will be omitted from a map or slice being decoded and the +// unmarshalling will continue. If it returns any other non-nil error, the +// unmarshalling procedure will stop and error out with the provided value. +// +// This interface is generally useful in pointer receivers, since the method +// will want to change the receiver. A type field that implements the Setter +// interface doesn't have to be a pointer, though. +// +// Unlike the usual behavior, unmarshalling onto a value that implements a +// Setter interface will NOT reset the value to its zero state. This allows +// the value to decide by itself how to be unmarshalled. +// +// For example: +// +// type MyString string +// +// func (s *MyString) SetBSON(raw bson.Raw) error { +// return raw.Unmarshal(s) +// } +// +type Setter interface { + SetBSON(raw Raw) error +} + +// ErrSetZero may be returned from a SetBSON method to have the value set to +// its respective zero value. When used in pointer values, this will set the +// field to nil rather than to the pre-allocated value. +var ErrSetZero = errors.New("set to zero") + +// M is a convenient alias for a map[string]interface{} map, useful for +// dealing with BSON in a native way. For instance: +// +// bson.M{"a": 1, "b": true} +// +// There's no special handling for this type in addition to what's done anyway +// for an equivalent map type. Elements in the map will be dumped in an +// undefined ordered. See also the bson.D type for an ordered alternative. +type M map[string]interface{} + +// D represents a BSON document containing ordered elements. For example: +// +// bson.D{{"a", 1}, {"b", true}} +// +// In some situations, such as when creating indexes for MongoDB, the order in +// which the elements are defined is important. If the order is not important, +// using a map is generally more comfortable. See bson.M and bson.RawD. +type D []DocElem + +// DocElem is an element of the bson.D document representation. +type DocElem struct { + Name string + Value interface{} +} + +// Map returns a map out of the ordered element name/value pairs in d. +func (d D) Map() (m M) { + m = make(M, len(d)) + for _, item := range d { + m[item.Name] = item.Value + } + return m +} + +// The Raw type represents raw unprocessed BSON documents and elements. +// Kind is the kind of element as defined per the BSON specification, and +// Data is the raw unprocessed data for the respective element. +// Using this type it is possible to unmarshal or marshal values partially. +// +// Relevant documentation: +// +// http://bsonspec.org/#/specification +// +type Raw struct { + Kind byte + Data []byte +} + +// RawD represents a BSON document containing raw unprocessed elements. +// This low-level representation may be useful when lazily processing +// documents of uncertain content, or when manipulating the raw content +// documents in general. +type RawD []RawDocElem + +// RawDocElem elements of RawD type. +type RawDocElem struct { + Name string + Value Raw +} + +// ObjectId is a unique ID identifying a BSON value. It must be exactly 12 bytes +// long. MongoDB objects by default have such a property set in their "_id" +// property. +// +// http://www.mongodb.org/display/DOCS/Object+Ids +type ObjectId string + +// ObjectIdHex returns an ObjectId from the provided hex representation. +// Calling this function with an invalid hex representation will +// cause a runtime panic. See the IsObjectIdHex function. +func ObjectIdHex(s string) ObjectId { + d, err := hex.DecodeString(s) + if err != nil || len(d) != 12 { + panic(fmt.Sprintf("invalid input to ObjectIdHex: %q", s)) + } + return ObjectId(d) +} + +// IsObjectIdHex returns whether s is a valid hex representation of +// an ObjectId. See the ObjectIdHex function. +func IsObjectIdHex(s string) bool { + if len(s) != 24 { + return false + } + _, err := hex.DecodeString(s) + return err == nil +} + +// objectIdCounter is atomically incremented when generating a new ObjectId +// using NewObjectId() function. It's used as a counter part of an id. +var objectIdCounter = readRandomUint32() + +// readRandomUint32 returns a random objectIdCounter. +func readRandomUint32() uint32 { + var b [4]byte + _, err := io.ReadFull(rand.Reader, b[:]) + if err != nil { + panic(fmt.Errorf("cannot read random object id: %v", err)) + } + return uint32((uint32(b[0]) << 0) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24)) +} + +// machineId stores machine id generated once and used in subsequent calls +// to NewObjectId function. +var machineId = readMachineId() +var processId = os.Getpid() + +// readMachineId generates and returns a machine id. +// If this function fails to get the hostname it will cause a runtime error. +func readMachineId() []byte { + var sum [3]byte + id := sum[:] + hostname, err1 := os.Hostname() + if err1 != nil { + _, err2 := io.ReadFull(rand.Reader, id) + if err2 != nil { + panic(fmt.Errorf("cannot get hostname: %v; %v", err1, err2)) + } + return id + } + hw := md5.New() + hw.Write([]byte(hostname)) + copy(id, hw.Sum(nil)) + return id +} + +// NewObjectId returns a new unique ObjectId. +func NewObjectId() ObjectId { + var b [12]byte + // Timestamp, 4 bytes, big endian + binary.BigEndian.PutUint32(b[:], uint32(time.Now().Unix())) + // Machine, first 3 bytes of md5(hostname) + b[4] = machineId[0] + b[5] = machineId[1] + b[6] = machineId[2] + // Pid, 2 bytes, specs don't specify endianness, but we use big endian. + b[7] = byte(processId >> 8) + b[8] = byte(processId) + // Increment, 3 bytes, big endian + i := atomic.AddUint32(&objectIdCounter, 1) + b[9] = byte(i >> 16) + b[10] = byte(i >> 8) + b[11] = byte(i) + return ObjectId(b[:]) +} + +// NewObjectIdWithTime returns a dummy ObjectId with the timestamp part filled +// with the provided number of seconds from epoch UTC, and all other parts +// filled with zeroes. It's not safe to insert a document with an id generated +// by this method, it is useful only for queries to find documents with ids +// generated before or after the specified timestamp. +func NewObjectIdWithTime(t time.Time) ObjectId { + var b [12]byte + binary.BigEndian.PutUint32(b[:4], uint32(t.Unix())) + return ObjectId(string(b[:])) +} + +// String returns a hex string representation of the id. +// Example: ObjectIdHex("4d88e15b60f486e428412dc9"). +func (id ObjectId) String() string { + return fmt.Sprintf(`ObjectIdHex("%x")`, string(id)) +} + +// Hex returns a hex representation of the ObjectId. +func (id ObjectId) Hex() string { + return hex.EncodeToString([]byte(id)) +} + +// MarshalJSON turns a bson.ObjectId into a json.Marshaller. +func (id ObjectId) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%x"`, string(id))), nil +} + +var nullBytes = []byte("null") + +// UnmarshalJSON turns *bson.ObjectId into a json.Unmarshaller. +func (id *ObjectId) UnmarshalJSON(data []byte) error { + if len(data) > 0 && (data[0] == '{' || data[0] == 'O') { + var v struct { + Id json.RawMessage `json:"$oid"` + Func struct { + Id json.RawMessage + } `json:"$oidFunc"` + } + err := jdec(data, &v) + if err == nil { + if len(v.Id) > 0 { + data = []byte(v.Id) + } else { + data = []byte(v.Func.Id) + } + } + } + if len(data) == 2 && data[0] == '"' && data[1] == '"' || bytes.Equal(data, nullBytes) { + *id = "" + return nil + } + if len(data) != 26 || data[0] != '"' || data[25] != '"' { + return fmt.Errorf("invalid ObjectId in JSON: %s", string(data)) + } + var buf [12]byte + _, err := hex.Decode(buf[:], data[1:25]) + if err != nil { + return fmt.Errorf("invalid ObjectId in JSON: %s (%s)", string(data), err) + } + *id = ObjectId(string(buf[:])) + return nil +} + +// MarshalText turns bson.ObjectId into an encoding.TextMarshaler. +func (id ObjectId) MarshalText() ([]byte, error) { + return []byte(fmt.Sprintf("%x", string(id))), nil +} + +// UnmarshalText turns *bson.ObjectId into an encoding.TextUnmarshaler. +func (id *ObjectId) UnmarshalText(data []byte) error { + if len(data) == 1 && data[0] == ' ' || len(data) == 0 { + *id = "" + return nil + } + if len(data) != 24 { + return fmt.Errorf("invalid ObjectId: %s", data) + } + var buf [12]byte + _, err := hex.Decode(buf[:], data[:]) + if err != nil { + return fmt.Errorf("invalid ObjectId: %s (%s)", data, err) + } + *id = ObjectId(string(buf[:])) + return nil +} + +// Valid returns true if id is valid. A valid id must contain exactly 12 bytes. +func (id ObjectId) Valid() bool { + return len(id) == 12 +} + +// byteSlice returns byte slice of id from start to end. +// Calling this function with an invalid id will cause a runtime panic. +func (id ObjectId) byteSlice(start, end int) []byte { + if len(id) != 12 { + panic(fmt.Sprintf("invalid ObjectId: %q", string(id))) + } + return []byte(string(id)[start:end]) +} + +// Time returns the timestamp part of the id. +// It's a runtime error to call this method with an invalid id. +func (id ObjectId) Time() time.Time { + // First 4 bytes of ObjectId is 32-bit big-endian seconds from epoch. + secs := int64(binary.BigEndian.Uint32(id.byteSlice(0, 4))) + return time.Unix(secs, 0) +} + +// Machine returns the 3-byte machine id part of the id. +// It's a runtime error to call this method with an invalid id. +func (id ObjectId) Machine() []byte { + return id.byteSlice(4, 7) +} + +// Pid returns the process id part of the id. +// It's a runtime error to call this method with an invalid id. +func (id ObjectId) Pid() uint16 { + return binary.BigEndian.Uint16(id.byteSlice(7, 9)) +} + +// Counter returns the incrementing value part of the id. +// It's a runtime error to call this method with an invalid id. +func (id ObjectId) Counter() int32 { + b := id.byteSlice(9, 12) + // Counter is stored as big-endian 3-byte value + return int32(uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2])) +} + +// The Symbol type is similar to a string and is used in languages with a +// distinct symbol type. +type Symbol string + +// Now returns the current time with millisecond precision. MongoDB stores +// timestamps with the same precision, so a Time returned from this method +// will not change after a roundtrip to the database. That's the only reason +// why this function exists. Using the time.Now function also works fine +// otherwise. +func Now() time.Time { + return time.Unix(0, time.Now().UnixNano()/1e6*1e6) +} + +// MongoTimestamp is a special internal type used by MongoDB that for some +// strange reason has its own datatype defined in BSON. +type MongoTimestamp int64 + +// Time returns the time part of ts which is stored with second precision. +func (ts MongoTimestamp) Time() time.Time { + return time.Unix(int64(uint64(ts)>>32), 0) +} + +// Counter returns the counter part of ts. +func (ts MongoTimestamp) Counter() uint32 { + return uint32(ts) +} + +// NewMongoTimestamp creates a timestamp using the given +// date `t` (with second precision) and counter `c` (unique for `t`). +// +// Returns an error if time `t` is not between 1970-01-01T00:00:00Z +// and 2106-02-07T06:28:15Z (inclusive). +// +// Note that two MongoTimestamps should never have the same (time, counter) combination: +// the caller must ensure the counter `c` is increased if creating multiple MongoTimestamp +// values for the same time `t` (ignoring fractions of seconds). +func NewMongoTimestamp(t time.Time, c uint32) (MongoTimestamp, error) { + u := t.Unix() + if u < 0 || u > math.MaxUint32 { + return -1, errors.New("invalid value for time") + } + + i := int64(u<<32 | int64(c)) + + return MongoTimestamp(i), nil +} + +type orderKey int64 + +// MaxKey is a special value that compares higher than all other possible BSON +// values in a MongoDB database. +var MaxKey = orderKey(1<<63 - 1) + +// MinKey is a special value that compares lower than all other possible BSON +// values in a MongoDB database. +var MinKey = orderKey(-1 << 63) + +type undefined struct{} + +// Undefined represents the undefined BSON value. +var Undefined undefined + +// Binary is a representation for non-standard binary values. Any kind should +// work, but the following are known as of this writing: +// +// 0x00 - Generic. This is decoded as []byte(data), not Binary{0x00, data}. +// 0x01 - Function (!?) +// 0x02 - Obsolete generic. +// 0x03 - UUID +// 0x05 - MD5 +// 0x80 - User defined. +// +type Binary struct { + Kind byte + Data []byte +} + +// RegEx represents a regular expression. The Options field may contain +// individual characters defining the way in which the pattern should be +// applied, and must be sorted. Valid options as of this writing are 'i' for +// case insensitive matching, 'm' for multi-line matching, 'x' for verbose +// mode, 'l' to make \w, \W, and similar be locale-dependent, 's' for dot-all +// mode (a '.' matches everything), and 'u' to make \w, \W, and similar match +// unicode. The value of the Options parameter is not verified before being +// marshaled into the BSON format. +type RegEx struct { + Pattern string + Options string +} + +// JavaScript is a type that holds JavaScript code. If Scope is non-nil, it +// will be marshaled as a mapping from identifiers to values that may be +// used when evaluating the provided Code. +type JavaScript struct { + Code string + Scope interface{} +} + +// DBPointer refers to a document id in a namespace. +// +// This type is deprecated in the BSON specification and should not be used +// except for backwards compatibility with ancient applications. +type DBPointer struct { + Namespace string + Id ObjectId +} + +const initialBufferSize = 64 + +func handleErr(err *error) { + if r := recover(); r != nil { + if _, ok := r.(runtime.Error); ok { + panic(r) + } else if _, ok := r.(externalPanic); ok { + panic(r) + } else if s, ok := r.(string); ok { + *err = errors.New(s) + } else if e, ok := r.(error); ok { + *err = e + } else { + panic(r) + } + } +} + +// Marshal serializes the in value, which may be a map or a struct value. +// In the case of struct values, only exported fields will be serialized, +// and the order of serialized fields will match that of the struct itself. +// The lowercased field name is used as the key for each exported field, +// but this behavior may be changed using the respective field tag. +// The tag may also contain flags to tweak the marshalling behavior for +// the field. The tag formats accepted are: +// +// "[][,[,]]" +// +// `(...) bson:"[][,[,]]" (...)` +// +// The following flags are currently supported: +// +// omitempty Only include the field if it's not set to the zero +// value for the type or to empty slices or maps. +// +// minsize Marshal an int64 value as an int32, if that's feasible +// while preserving the numeric value. +// +// inline Inline the field, which must be a struct or a map, +// causing all of its fields or keys to be processed as if +// they were part of the outer struct. For maps, keys must +// not conflict with the bson keys of other struct fields. +// +// Some examples: +// +// type T struct { +// A bool +// B int "myb" +// C string "myc,omitempty" +// D string `bson:",omitempty" json:"jsonkey"` +// E int64 ",minsize" +// F int64 "myf,omitempty,minsize" +// } +// +func Marshal(in interface{}) (out []byte, err error) { + return MarshalBuffer(in, make([]byte, 0, initialBufferSize)) +} + +// MarshalBuffer behaves the same way as Marshal, except that instead of +// allocating a new byte slice it tries to use the received byte slice and +// only allocates more memory if necessary to fit the marshaled value. +func MarshalBuffer(in interface{}, buf []byte) (out []byte, err error) { + defer handleErr(&err) + e := &encoder{buf} + e.addDoc(reflect.ValueOf(in)) + return e.out, nil +} + +// Unmarshal deserializes data from in into the out value. The out value +// must be a map, a pointer to a struct, or a pointer to a bson.D value. +// In the case of struct values, only exported fields will be deserialized. +// The lowercased field name is used as the key for each exported field, +// but this behavior may be changed using the respective field tag. +// The tag may also contain flags to tweak the marshalling behavior for +// the field. The tag formats accepted are: +// +// "[][,[,]]" +// +// `(...) bson:"[][,[,]]" (...)` +// +// The following flags are currently supported during unmarshal (see the +// Marshal method for other flags): +// +// inline Inline the field, which must be a struct or a map. +// Inlined structs are handled as if its fields were part +// of the outer struct. An inlined map causes keys that do +// not match any other struct field to be inserted in the +// map rather than being discarded as usual. +// +// The target field or element types of out may not necessarily match +// the BSON values of the provided data. The following conversions are +// made automatically: +// +// - Numeric types are converted if at least the integer part of the +// value would be preserved correctly +// - Bools are converted to numeric types as 1 or 0 +// - Numeric types are converted to bools as true if not 0 or false otherwise +// - Binary and string BSON data is converted to a string, array or byte slice +// +// If the value would not fit the type and cannot be converted, it's +// silently skipped. +// +// Pointer values are initialized when necessary. +func Unmarshal(in []byte, out interface{}) (err error) { + if raw, ok := out.(*Raw); ok { + raw.Kind = 3 + raw.Data = in + return nil + } + defer handleErr(&err) + v := reflect.ValueOf(out) + switch v.Kind() { + case reflect.Ptr: + fallthrough + case reflect.Map: + d := newDecoder(in) + d.readDocTo(v) + if d.i < len(d.in) { + return errors.New("document is corrupted") + } + case reflect.Struct: + return errors.New("unmarshal can't deal with struct values. Use a pointer") + default: + return errors.New("unmarshal needs a map or a pointer to a struct") + } + return nil +} + +// Unmarshal deserializes raw into the out value. If the out value type +// is not compatible with raw, a *bson.TypeError is returned. +// +// See the Unmarshal function documentation for more details on the +// unmarshalling process. +func (raw Raw) Unmarshal(out interface{}) (err error) { + defer handleErr(&err) + v := reflect.ValueOf(out) + switch v.Kind() { + case reflect.Ptr: + v = v.Elem() + fallthrough + case reflect.Map: + d := newDecoder(raw.Data) + good := d.readElemTo(v, raw.Kind) + if !good { + return &TypeError{v.Type(), raw.Kind} + } + case reflect.Struct: + return errors.New("raw Unmarshal can't deal with struct values. Use a pointer") + default: + return errors.New("raw Unmarshal needs a map or a valid pointer") + } + return nil +} + +// TypeError store details for type error occuring +// during unmarshaling +type TypeError struct { + Type reflect.Type + Kind byte +} + +func (e *TypeError) Error() string { + return fmt.Sprintf("BSON kind 0x%02x isn't compatible with type %s", e.Kind, e.Type.String()) +} + +// -------------------------------------------------------------------------- +// Maintain a mapping of keys to structure field indexes + +type structInfo struct { + FieldsMap map[string]fieldInfo + FieldsList []fieldInfo + InlineMap int + Zero reflect.Value +} + +type fieldInfo struct { + Key string + Num int + OmitEmpty bool + MinSize bool + Inline []int +} + +var structMap = make(map[reflect.Type]*structInfo) +var structMapMutex sync.RWMutex + +type externalPanic string + +func (e externalPanic) String() string { + return string(e) +} + +func getStructInfo(st reflect.Type) (*structInfo, error) { + structMapMutex.RLock() + sinfo, found := structMap[st] + structMapMutex.RUnlock() + if found { + return sinfo, nil + } + n := st.NumField() + fieldsMap := make(map[string]fieldInfo) + fieldsList := make([]fieldInfo, 0, n) + inlineMap := -1 + for i := 0; i != n; i++ { + field := st.Field(i) + if field.PkgPath != "" && !field.Anonymous { + continue // Private field + } + + info := fieldInfo{Num: i} + + tag := field.Tag.Get("bson") + + // Fall-back to JSON struct tag, if feature flag is set. + if tag == "" && useJSONTagFallback { + tag = field.Tag.Get("json") + } + + // If there's no bson/json tag available. + if tag == "" { + // If there's no tag, and also no tag: value splits (i.e. no colon) + // then assume the entire tag is the value + if strings.Index(string(field.Tag), ":") < 0 { + tag = string(field.Tag) + } + } + + if tag == "-" { + continue + } + + inline := false + fields := strings.Split(tag, ",") + if len(fields) > 1 { + for _, flag := range fields[1:] { + switch flag { + case "omitempty": + info.OmitEmpty = true + case "minsize": + info.MinSize = true + case "inline": + inline = true + default: + msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st) + panic(externalPanic(msg)) + } + } + tag = fields[0] + } + + if inline { + switch field.Type.Kind() { + case reflect.Map: + if inlineMap >= 0 { + return nil, errors.New("Multiple ,inline maps in struct " + st.String()) + } + if field.Type.Key() != reflect.TypeOf("") { + return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String()) + } + inlineMap = info.Num + case reflect.Ptr: + // allow only pointer to struct + if kind := field.Type.Elem().Kind(); kind != reflect.Struct { + return nil, errors.New("Option ,inline allows a pointer only to a struct, was given pointer to " + kind.String()) + } + + field.Type = field.Type.Elem() + fallthrough + case reflect.Struct: + sinfo, err := getStructInfo(field.Type) + if err != nil { + return nil, err + } + for _, finfo := range sinfo.FieldsList { + if _, found := fieldsMap[finfo.Key]; found { + msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String() + return nil, errors.New(msg) + } + if finfo.Inline == nil { + finfo.Inline = []int{i, finfo.Num} + } else { + finfo.Inline = append([]int{i}, finfo.Inline...) + } + fieldsMap[finfo.Key] = finfo + fieldsList = append(fieldsList, finfo) + } + default: + panic("Option ,inline needs a struct value or a pointer to a struct or map field") + } + continue + } + + if tag != "" { + info.Key = tag + } else { + info.Key = strings.ToLower(field.Name) + } + + if _, found = fieldsMap[info.Key]; found { + msg := "Duplicated key '" + info.Key + "' in struct " + st.String() + return nil, errors.New(msg) + } + + fieldsList = append(fieldsList, info) + fieldsMap[info.Key] = info + } + sinfo = &structInfo{ + fieldsMap, + fieldsList, + inlineMap, + reflect.New(st).Elem(), + } + structMapMutex.Lock() + structMap[st] = sinfo + structMapMutex.Unlock() + return sinfo, nil +} diff --git a/vendor/github.com/globalsign/mgo/bson/compatibility.go b/vendor/github.com/globalsign/mgo/bson/compatibility.go new file mode 100644 index 000000000..66efd465f --- /dev/null +++ b/vendor/github.com/globalsign/mgo/bson/compatibility.go @@ -0,0 +1,29 @@ +package bson + +// Current state of the JSON tag fallback option. +var useJSONTagFallback = false +var useRespectNilValues = false + +// SetJSONTagFallback enables or disables the JSON-tag fallback for structure tagging. When this is enabled, structures +// without BSON tags on a field will fall-back to using the JSON tag (if present). +func SetJSONTagFallback(state bool) { + useJSONTagFallback = state +} + +// JSONTagFallbackState returns the current status of the JSON tag fallback compatability option. See SetJSONTagFallback +// for more information. +func JSONTagFallbackState() bool { + return useJSONTagFallback +} + +// SetRespectNilValues enables or disables serializing nil slices or maps to `null` values. +// In other words it enables `encoding/json` compatible behaviour. +func SetRespectNilValues(state bool) { + useRespectNilValues = state +} + +// RespectNilValuesState returns the current status of the JSON nil slices and maps fallback compatibility option. +// See SetRespectNilValues for more information. +func RespectNilValuesState() bool { + return useRespectNilValues +} diff --git a/vendor/github.com/globalsign/mgo/bson/decimal.go b/vendor/github.com/globalsign/mgo/bson/decimal.go new file mode 100644 index 000000000..672ba1825 --- /dev/null +++ b/vendor/github.com/globalsign/mgo/bson/decimal.go @@ -0,0 +1,312 @@ +// BSON library for Go +// +// Copyright (c) 2010-2012 - Gustavo Niemeyer +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package bson + +import ( + "fmt" + "strconv" + "strings" +) + +// Decimal128 holds decimal128 BSON values. +type Decimal128 struct { + h, l uint64 +} + +func (d Decimal128) String() string { + var pos int // positive sign + var e int // exponent + var h, l uint64 // significand high/low + + if d.h>>63&1 == 0 { + pos = 1 + } + + switch d.h >> 58 & (1<<5 - 1) { + case 0x1F: + return "NaN" + case 0x1E: + return "-Inf"[pos:] + } + + l = d.l + if d.h>>61&3 == 3 { + // Bits: 1*sign 2*ignored 14*exponent 111*significand. + // Implicit 0b100 prefix in significand. + e = int(d.h>>47&(1<<14-1)) - 6176 + //h = 4<<47 | d.h&(1<<47-1) + // Spec says all of these values are out of range. + h, l = 0, 0 + } else { + // Bits: 1*sign 14*exponent 113*significand + e = int(d.h>>49&(1<<14-1)) - 6176 + h = d.h & (1<<49 - 1) + } + + // Would be handled by the logic below, but that's trivial and common. + if h == 0 && l == 0 && e == 0 { + return "-0"[pos:] + } + + var repr [48]byte // Loop 5 times over 9 digits plus dot, negative sign, and leading zero. + var last = len(repr) + var i = len(repr) + var dot = len(repr) + e + var rem uint32 +Loop: + for d9 := 0; d9 < 5; d9++ { + h, l, rem = divmod(h, l, 1e9) + for d1 := 0; d1 < 9; d1++ { + // Handle "-0.0", "0.00123400", "-1.00E-6", "1.050E+3", etc. + if i < len(repr) && (dot == i || l == 0 && h == 0 && rem > 0 && rem < 10 && (dot < i-6 || e > 0)) { + e += len(repr) - i + i-- + repr[i] = '.' + last = i - 1 + dot = len(repr) // Unmark. + } + c := '0' + byte(rem%10) + rem /= 10 + i-- + repr[i] = c + // Handle "0E+3", "1E+3", etc. + if l == 0 && h == 0 && rem == 0 && i == len(repr)-1 && (dot < i-5 || e > 0) { + last = i + break Loop + } + if c != '0' { + last = i + } + // Break early. Works without it, but why. + if dot > i && l == 0 && h == 0 && rem == 0 { + break Loop + } + } + } + repr[last-1] = '-' + last-- + + if e > 0 { + return string(repr[last+pos:]) + "E+" + strconv.Itoa(e) + } + if e < 0 { + return string(repr[last+pos:]) + "E" + strconv.Itoa(e) + } + return string(repr[last+pos:]) +} + +func divmod(h, l uint64, div uint32) (qh, ql uint64, rem uint32) { + div64 := uint64(div) + a := h >> 32 + aq := a / div64 + ar := a % div64 + b := ar<<32 + h&(1<<32-1) + bq := b / div64 + br := b % div64 + c := br<<32 + l>>32 + cq := c / div64 + cr := c % div64 + d := cr<<32 + l&(1<<32-1) + dq := d / div64 + dr := d % div64 + return (aq<<32 | bq), (cq<<32 | dq), uint32(dr) +} + +var dNaN = Decimal128{0x1F << 58, 0} +var dPosInf = Decimal128{0x1E << 58, 0} +var dNegInf = Decimal128{0x3E << 58, 0} + +func dErr(s string) (Decimal128, error) { + return dNaN, fmt.Errorf("cannot parse %q as a decimal128", s) +} + +// ParseDecimal128 parse a string and return the corresponding value as +// a decimal128 +func ParseDecimal128(s string) (Decimal128, error) { + orig := s + if s == "" { + return dErr(orig) + } + neg := s[0] == '-' + if neg || s[0] == '+' { + s = s[1:] + } + + if (len(s) == 3 || len(s) == 8) && (s[0] == 'N' || s[0] == 'n' || s[0] == 'I' || s[0] == 'i') { + if s == "NaN" || s == "nan" || strings.EqualFold(s, "nan") { + return dNaN, nil + } + if s == "Inf" || s == "inf" || strings.EqualFold(s, "inf") || strings.EqualFold(s, "infinity") { + if neg { + return dNegInf, nil + } + return dPosInf, nil + } + return dErr(orig) + } + + var h, l uint64 + var e int + + var add, ovr uint32 + var mul uint32 = 1 + var dot = -1 + var digits = 0 + var i = 0 + for i < len(s) { + c := s[i] + if mul == 1e9 { + h, l, ovr = muladd(h, l, mul, add) + mul, add = 1, 0 + if ovr > 0 || h&((1<<15-1)<<49) > 0 { + return dErr(orig) + } + } + if c >= '0' && c <= '9' { + i++ + if c > '0' || digits > 0 { + digits++ + } + if digits > 34 { + if c == '0' { + // Exact rounding. + e++ + continue + } + return dErr(orig) + } + mul *= 10 + add *= 10 + add += uint32(c - '0') + continue + } + if c == '.' { + i++ + if dot >= 0 || i == 1 && len(s) == 1 { + return dErr(orig) + } + if i == len(s) { + break + } + if s[i] < '0' || s[i] > '9' || e > 0 { + return dErr(orig) + } + dot = i + continue + } + break + } + if i == 0 { + return dErr(orig) + } + if mul > 1 { + h, l, ovr = muladd(h, l, mul, add) + if ovr > 0 || h&((1<<15-1)<<49) > 0 { + return dErr(orig) + } + } + if dot >= 0 { + e += dot - i + } + if i+1 < len(s) && (s[i] == 'E' || s[i] == 'e') { + i++ + eneg := s[i] == '-' + if eneg || s[i] == '+' { + i++ + if i == len(s) { + return dErr(orig) + } + } + n := 0 + for i < len(s) && n < 1e4 { + c := s[i] + i++ + if c < '0' || c > '9' { + return dErr(orig) + } + n *= 10 + n += int(c - '0') + } + if eneg { + n = -n + } + e += n + for e < -6176 { + // Subnormal. + var div uint32 = 1 + for div < 1e9 && e < -6176 { + div *= 10 + e++ + } + var rem uint32 + h, l, rem = divmod(h, l, div) + if rem > 0 { + return dErr(orig) + } + } + for e > 6111 { + // Clamped. + var mul uint32 = 1 + for mul < 1e9 && e > 6111 { + mul *= 10 + e-- + } + h, l, ovr = muladd(h, l, mul, 0) + if ovr > 0 || h&((1<<15-1)<<49) > 0 { + return dErr(orig) + } + } + if e < -6176 || e > 6111 { + return dErr(orig) + } + } + + if i < len(s) { + return dErr(orig) + } + + h |= uint64(e+6176) & uint64(1<<14-1) << 49 + if neg { + h |= 1 << 63 + } + return Decimal128{h, l}, nil +} + +func muladd(h, l uint64, mul uint32, add uint32) (resh, resl uint64, overflow uint32) { + mul64 := uint64(mul) + a := mul64 * (l & (1<<32 - 1)) + b := a>>32 + mul64*(l>>32) + c := b>>32 + mul64*(h&(1<<32-1)) + d := c>>32 + mul64*(h>>32) + + a = a&(1<<32-1) + uint64(add) + b = b&(1<<32-1) + a>>32 + c = c&(1<<32-1) + b>>32 + d = d&(1<<32-1) + c>>32 + + return (d<<32 | c&(1<<32-1)), (b<<32 | a&(1<<32-1)), uint32(d >> 32) +} diff --git a/vendor/github.com/globalsign/mgo/bson/decode.go b/vendor/github.com/globalsign/mgo/bson/decode.go new file mode 100644 index 000000000..658856add --- /dev/null +++ b/vendor/github.com/globalsign/mgo/bson/decode.go @@ -0,0 +1,1055 @@ +// BSON library for Go +// +// Copyright (c) 2010-2012 - Gustavo Niemeyer +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// gobson - BSON library for Go. + +package bson + +import ( + "errors" + "fmt" + "io" + "math" + "net/url" + "reflect" + "strconv" + "sync" + "time" +) + +type decoder struct { + in []byte + i int + docType reflect.Type +} + +var typeM = reflect.TypeOf(M{}) + +func newDecoder(in []byte) *decoder { + return &decoder{in, 0, typeM} +} + +// -------------------------------------------------------------------------- +// Some helper functions. + +func corrupted() { + panic("Document is corrupted") +} + +// -------------------------------------------------------------------------- +// Unmarshaling of documents. + +const ( + setterUnknown = iota + setterNone + setterType + setterAddr +) + +var setterStyles map[reflect.Type]int +var setterIface reflect.Type +var setterMutex sync.RWMutex + +func init() { + var iface Setter + setterIface = reflect.TypeOf(&iface).Elem() + setterStyles = make(map[reflect.Type]int) +} + +func setterStyle(outt reflect.Type) int { + setterMutex.RLock() + style := setterStyles[outt] + setterMutex.RUnlock() + if style != setterUnknown { + return style + } + + setterMutex.Lock() + defer setterMutex.Unlock() + if outt.Implements(setterIface) { + style = setterType + } else if reflect.PtrTo(outt).Implements(setterIface) { + style = setterAddr + } else { + style = setterNone + } + setterStyles[outt] = style + return style +} + +func getSetter(outt reflect.Type, out reflect.Value) Setter { + style := setterStyle(outt) + if style == setterNone { + return nil + } + if style == setterAddr { + if !out.CanAddr() { + return nil + } + out = out.Addr() + } else if outt.Kind() == reflect.Ptr && out.IsNil() { + out.Set(reflect.New(outt.Elem())) + } + return out.Interface().(Setter) +} + +func clearMap(m reflect.Value) { + var none reflect.Value + for _, k := range m.MapKeys() { + m.SetMapIndex(k, none) + } +} + +func (d *decoder) readDocTo(out reflect.Value) { + var elemType reflect.Type + outt := out.Type() + outk := outt.Kind() + + for { + if outk == reflect.Ptr && out.IsNil() { + out.Set(reflect.New(outt.Elem())) + } + if setter := getSetter(outt, out); setter != nil { + raw := d.readRaw(ElementDocument) + err := setter.SetBSON(raw) + if _, ok := err.(*TypeError); err != nil && !ok { + panic(err) + } + return + } + if outk == reflect.Ptr { + out = out.Elem() + outt = out.Type() + outk = out.Kind() + continue + } + break + } + + var fieldsMap map[string]fieldInfo + var inlineMap reflect.Value + if outt == typeRaw { + out.Set(reflect.ValueOf(d.readRaw(ElementDocument))) + return + } + + origout := out + if outk == reflect.Interface { + if d.docType.Kind() == reflect.Map { + mv := reflect.MakeMap(d.docType) + out.Set(mv) + out = mv + } else { + dv := reflect.New(d.docType).Elem() + out.Set(dv) + out = dv + } + outt = out.Type() + outk = outt.Kind() + } + + docType := d.docType + keyType := typeString + convertKey := false + switch outk { + case reflect.Map: + keyType = outt.Key() + if keyType != typeString { + convertKey = true + } + elemType = outt.Elem() + if elemType == typeIface { + d.docType = outt + } + if out.IsNil() { + out.Set(reflect.MakeMap(out.Type())) + } else if out.Len() > 0 { + clearMap(out) + } + case reflect.Struct: + sinfo, err := getStructInfo(out.Type()) + if err != nil { + panic(err) + } + fieldsMap = sinfo.FieldsMap + out.Set(sinfo.Zero) + if sinfo.InlineMap != -1 { + inlineMap = out.Field(sinfo.InlineMap) + if !inlineMap.IsNil() && inlineMap.Len() > 0 { + clearMap(inlineMap) + } + elemType = inlineMap.Type().Elem() + if elemType == typeIface { + d.docType = inlineMap.Type() + } + } + case reflect.Slice: + switch outt.Elem() { + case typeDocElem: + origout.Set(d.readDocElems(outt)) + return + case typeRawDocElem: + origout.Set(d.readRawDocElems(outt)) + return + } + fallthrough + default: + panic("Unsupported document type for unmarshalling: " + out.Type().String()) + } + + end := int(d.readInt32()) + end += d.i - 4 + if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' { + corrupted() + } + for d.in[d.i] != '\x00' { + kind := d.readByte() + name := d.readCStr() + if d.i >= end { + corrupted() + } + + switch outk { + case reflect.Map: + e := reflect.New(elemType).Elem() + if d.readElemTo(e, kind) { + k := reflect.ValueOf(name) + if convertKey { + mapKeyType := out.Type().Key() + mapKeyKind := mapKeyType.Kind() + + switch mapKeyKind { + case reflect.Int: + fallthrough + case reflect.Int8: + fallthrough + case reflect.Int16: + fallthrough + case reflect.Int32: + fallthrough + case reflect.Int64: + fallthrough + case reflect.Uint: + fallthrough + case reflect.Uint8: + fallthrough + case reflect.Uint16: + fallthrough + case reflect.Uint32: + fallthrough + case reflect.Uint64: + fallthrough + case reflect.Float32: + fallthrough + case reflect.Float64: + parsed := d.parseMapKeyAsFloat(k, mapKeyKind) + k = reflect.ValueOf(parsed) + case reflect.String: + mapKeyType = keyType + default: + panic("BSON map must have string or decimal keys. Got: " + outt.String()) + } + + k = k.Convert(mapKeyType) + } + out.SetMapIndex(k, e) + } + case reflect.Struct: + if info, ok := fieldsMap[name]; ok { + if info.Inline == nil { + d.readElemTo(out.Field(info.Num), kind) + } else { + d.readElemTo(out.FieldByIndex(info.Inline), kind) + } + } else if inlineMap.IsValid() { + if inlineMap.IsNil() { + inlineMap.Set(reflect.MakeMap(inlineMap.Type())) + } + e := reflect.New(elemType).Elem() + if d.readElemTo(e, kind) { + inlineMap.SetMapIndex(reflect.ValueOf(name), e) + } + } else { + d.dropElem(kind) + } + case reflect.Slice: + } + + if d.i >= end { + corrupted() + } + } + d.i++ // '\x00' + if d.i != end { + corrupted() + } + d.docType = docType +} + +func (decoder) parseMapKeyAsFloat(k reflect.Value, mapKeyKind reflect.Kind) float64 { + parsed, err := strconv.ParseFloat(k.String(), 64) + if err != nil { + panic("Map key is defined to be a decimal type (" + mapKeyKind.String() + ") but got error " + + err.Error()) + } + + return parsed +} + +func (d *decoder) readArrayDocTo(out reflect.Value) { + end := int(d.readInt32()) + end += d.i - 4 + if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' { + corrupted() + } + i := 0 + l := out.Len() + for d.in[d.i] != '\x00' { + if i >= l { + panic("Length mismatch on array field") + } + kind := d.readByte() + for d.i < end && d.in[d.i] != '\x00' { + d.i++ + } + if d.i >= end { + corrupted() + } + d.i++ + d.readElemTo(out.Index(i), kind) + if d.i >= end { + corrupted() + } + i++ + } + if i != l { + panic("Length mismatch on array field") + } + d.i++ // '\x00' + if d.i != end { + corrupted() + } +} + +func (d *decoder) readSliceDoc(t reflect.Type) interface{} { + tmp := make([]reflect.Value, 0, 8) + elemType := t.Elem() + if elemType == typeRawDocElem { + d.dropElem(ElementArray) + return reflect.Zero(t).Interface() + } + if elemType == typeRaw { + return d.readSliceOfRaw() + } + + end := int(d.readInt32()) + end += d.i - 4 + if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' { + corrupted() + } + for d.in[d.i] != '\x00' { + kind := d.readByte() + for d.i < end && d.in[d.i] != '\x00' { + d.i++ + } + if d.i >= end { + corrupted() + } + d.i++ + e := reflect.New(elemType).Elem() + if d.readElemTo(e, kind) { + tmp = append(tmp, e) + } + if d.i >= end { + corrupted() + } + } + d.i++ // '\x00' + if d.i != end { + corrupted() + } + + n := len(tmp) + slice := reflect.MakeSlice(t, n, n) + for i := 0; i != n; i++ { + slice.Index(i).Set(tmp[i]) + } + return slice.Interface() +} + +func BSONElementSize(kind byte, offset int, buffer []byte) (int, error) { + switch kind { + case ElementFloat64: // Float64 + return 8, nil + case ElementJavaScriptWithoutScope: // JavaScript without scope + fallthrough + case ElementSymbol: // Symbol + fallthrough + case ElementString: // UTF-8 string + size, err := getSize(offset, buffer) + if err != nil { + return 0, err + } + if size < 1 { + return 0, errors.New("String size can't be less then one byte") + } + size += 4 + if offset+size > len(buffer) { + return 0, io.ErrUnexpectedEOF + } + if buffer[offset+size-1] != 0 { + return 0, errors.New("Invalid string: non zero-terminated") + } + return size, nil + case ElementArray: // Array + fallthrough + case ElementDocument: // Document + size, err := getSize(offset, buffer) + if err != nil { + return 0, err + } + if size < 5 { + return 0, errors.New("Declared document size is too small") + } + return size, nil + case ElementBinary: // Binary + size, err := getSize(offset, buffer) + if err != nil { + return 0, err + } + if size < 0 { + return 0, errors.New("Binary data size can't be negative") + } + return size + 5, nil + case Element06: // Undefined (obsolete, but still seen in the wild) + return 0, nil + case ElementObjectId: // ObjectId + return 12, nil + case ElementBool: // Bool + return 1, nil + case ElementDatetime: // Timestamp + return 8, nil + case ElementNil: // Nil + return 0, nil + case ElementRegEx: // RegEx + end := offset + for i := 0; i < 2; i++ { + for end < len(buffer) && buffer[end] != '\x00' { + end++ + } + end++ + } + if end > len(buffer) { + return 0, io.ErrUnexpectedEOF + } + return end - offset, nil + case ElementDBPointer: // DBPointer + size, err := getSize(offset, buffer) + if err != nil { + return 0, err + } + if size < 1 { + return 0, errors.New("String size can't be less then one byte") + } + return size + 12 + 4, nil + case ElementJavaScriptWithScope: // JavaScript with scope + size, err := getSize(offset, buffer) + if err != nil { + return 0, err + } + if size < 4+5+5 { + return 0, errors.New("Declared document element is too small") + } + return size, nil + case ElementInt32: // Int32 + return 4, nil + case ElementTimestamp: // Mongo-specific timestamp + return 8, nil + case ElementInt64: // Int64 + return 8, nil + case ElementDecimal128: // Decimal128 + return 16, nil + case ElementMaxKey: // Max key + return 0, nil + case ElementMinKey: // Min key + return 0, nil + default: + return 0, errors.New(fmt.Sprintf("Unknown element kind (0x%02X)", kind)) + } +} + +func (d *decoder) readRaw(kind byte) Raw { + size, err := BSONElementSize(kind, d.i, d.in) + if err != nil { + corrupted() + } + if d.i+size > len(d.in) { + corrupted() + } + d.i += size + return Raw{ + Kind: kind, + Data: d.in[d.i-size : d.i], + } +} + +func (d *decoder) readSliceOfRaw() interface{} { + tmp := make([]Raw, 0, 8) + end := int(d.readInt32()) + end += d.i - 4 + if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' { + corrupted() + } + for d.in[d.i] != '\x00' { + kind := d.readByte() + for d.i < end && d.in[d.i] != '\x00' { + d.i++ + } + if d.i >= end { + corrupted() + } + d.i++ + e := d.readRaw(kind) + tmp = append(tmp, e) + if d.i >= end { + corrupted() + } + } + d.i++ // '\x00' + if d.i != end { + corrupted() + } + return tmp +} + +var typeSlice = reflect.TypeOf([]interface{}{}) +var typeIface = typeSlice.Elem() + +func (d *decoder) readDocElems(typ reflect.Type) reflect.Value { + docType := d.docType + d.docType = typ + slice := make([]DocElem, 0, 8) + d.readDocWith(func(kind byte, name string) { + e := DocElem{Name: name} + v := reflect.ValueOf(&e.Value) + if d.readElemTo(v.Elem(), kind) { + slice = append(slice, e) + } + }) + slicev := reflect.New(typ).Elem() + slicev.Set(reflect.ValueOf(slice)) + d.docType = docType + return slicev +} + +func (d *decoder) readRawDocElems(typ reflect.Type) reflect.Value { + docType := d.docType + d.docType = typ + slice := make([]RawDocElem, 0, 8) + d.readDocWith(func(kind byte, name string) { + e := RawDocElem{Name: name, Value: d.readRaw(kind)} + slice = append(slice, e) + }) + slicev := reflect.New(typ).Elem() + slicev.Set(reflect.ValueOf(slice)) + d.docType = docType + return slicev +} + +func (d *decoder) readDocWith(f func(kind byte, name string)) { + end := int(d.readInt32()) + end += d.i - 4 + if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' { + corrupted() + } + for d.in[d.i] != '\x00' { + kind := d.readByte() + name := d.readCStr() + if d.i >= end { + corrupted() + } + f(kind, name) + if d.i >= end { + corrupted() + } + } + d.i++ // '\x00' + if d.i != end { + corrupted() + } +} + +// -------------------------------------------------------------------------- +// Unmarshaling of individual elements within a document. +func (d *decoder) dropElem(kind byte) { + size, err := BSONElementSize(kind, d.i, d.in) + if err != nil { + corrupted() + } + if d.i+size > len(d.in) { + corrupted() + } + d.i += size +} + +// Attempt to decode an element from the document and put it into out. +// If the types are not compatible, the returned ok value will be +// false and out will be unchanged. +func (d *decoder) readElemTo(out reflect.Value, kind byte) (good bool) { + outt := out.Type() + + if outt == typeRaw { + out.Set(reflect.ValueOf(d.readRaw(kind))) + return true + } + + if outt == typeRawPtr { + raw := d.readRaw(kind) + out.Set(reflect.ValueOf(&raw)) + return true + } + + if kind == ElementDocument { + // Delegate unmarshaling of documents. + outt := out.Type() + outk := out.Kind() + switch outk { + case reflect.Interface, reflect.Ptr, reflect.Struct, reflect.Map: + d.readDocTo(out) + return true + } + if setterStyle(outt) != setterNone { + d.readDocTo(out) + return true + } + if outk == reflect.Slice { + switch outt.Elem() { + case typeDocElem: + out.Set(d.readDocElems(outt)) + case typeRawDocElem: + out.Set(d.readRawDocElems(outt)) + default: + d.dropElem(kind) + } + return true + } + d.dropElem(kind) + return true + } + + if setter := getSetter(outt, out); setter != nil { + err := setter.SetBSON(d.readRaw(kind)) + if err == ErrSetZero { + out.Set(reflect.Zero(outt)) + return true + } + if err == nil { + return true + } + if _, ok := err.(*TypeError); !ok { + panic(err) + } + return false + } + + var in interface{} + + switch kind { + case ElementFloat64: + in = d.readFloat64() + case ElementString: + in = d.readStr() + case ElementDocument: + panic("Can't happen. Handled above.") + case ElementArray: + outt := out.Type() + if setterStyle(outt) != setterNone { + // Skip the value so its data is handed to the setter below. + d.dropElem(kind) + break + } + for outt.Kind() == reflect.Ptr { + outt = outt.Elem() + } + switch outt.Kind() { + case reflect.Array: + d.readArrayDocTo(out) + return true + case reflect.Slice: + in = d.readSliceDoc(outt) + default: + in = d.readSliceDoc(typeSlice) + } + case ElementBinary: + b := d.readBinary() + if b.Kind == BinaryGeneric || b.Kind == BinaryBinaryOld { + in = b.Data + } else { + in = b + } + case Element06: // Undefined (obsolete, but still seen in the wild) + in = Undefined + case ElementObjectId: + in = ObjectId(d.readBytes(12)) + case ElementBool: + in = d.readBool() + case ElementDatetime: // Timestamp + // MongoDB handles timestamps as milliseconds. + i := d.readInt64() + if i == -62135596800000 { + in = time.Time{} // In UTC for convenience. + } else { + in = time.Unix(i/1e3, i%1e3*1e6).UTC() + } + case ElementNil: + in = nil + case ElementRegEx: + in = d.readRegEx() + case ElementDBPointer: + in = DBPointer{Namespace: d.readStr(), Id: ObjectId(d.readBytes(12))} + case ElementJavaScriptWithoutScope: + in = JavaScript{Code: d.readStr()} + case ElementSymbol: + in = Symbol(d.readStr()) + case ElementJavaScriptWithScope: + start := d.i + l := int(d.readInt32()) + js := JavaScript{d.readStr(), make(M)} + d.readDocTo(reflect.ValueOf(js.Scope)) + if d.i != start+l { + corrupted() + } + in = js + case ElementInt32: + in = int(d.readInt32()) + case ElementTimestamp: // Mongo-specific timestamp + in = MongoTimestamp(d.readInt64()) + case ElementInt64: + switch out.Type() { + case typeTimeDuration: + in = time.Duration(time.Duration(d.readInt64()) * time.Millisecond) + default: + in = d.readInt64() + } + case ElementDecimal128: + in = Decimal128{ + l: uint64(d.readInt64()), + h: uint64(d.readInt64()), + } + case ElementMaxKey: + in = MaxKey + case ElementMinKey: + in = MinKey + default: + panic(fmt.Sprintf("Unknown element kind (0x%02X)", kind)) + } + + if in == nil { + out.Set(reflect.Zero(outt)) + return true + } + + outk := outt.Kind() + + // Dereference and initialize pointer if necessary. + first := true + for outk == reflect.Ptr { + if !out.IsNil() { + out = out.Elem() + } else { + elem := reflect.New(outt.Elem()) + if first { + // Only set if value is compatible. + first = false + defer func(out, elem reflect.Value) { + if good { + out.Set(elem) + } + }(out, elem) + } else { + out.Set(elem) + } + out = elem + } + outt = out.Type() + outk = outt.Kind() + } + + inv := reflect.ValueOf(in) + if outt == inv.Type() { + out.Set(inv) + return true + } + + switch outk { + case reflect.Interface: + out.Set(inv) + return true + case reflect.String: + switch inv.Kind() { + case reflect.String: + out.SetString(inv.String()) + return true + case reflect.Slice: + if b, ok := in.([]byte); ok { + out.SetString(string(b)) + return true + } + case reflect.Int, reflect.Int64: + if outt == typeJSONNumber { + out.SetString(strconv.FormatInt(inv.Int(), 10)) + return true + } + case reflect.Float64: + if outt == typeJSONNumber { + out.SetString(strconv.FormatFloat(inv.Float(), 'f', -1, 64)) + return true + } + } + case reflect.Slice, reflect.Array: + // Remember, array (0x04) slices are built with the correct + // element type. If we are here, must be a cross BSON kind + // conversion (e.g. 0x05 unmarshalling on string). + if outt.Elem().Kind() != reflect.Uint8 { + break + } + switch inv.Kind() { + case reflect.String: + slice := []byte(inv.String()) + out.Set(reflect.ValueOf(slice)) + return true + case reflect.Slice: + switch outt.Kind() { + case reflect.Array: + reflect.Copy(out, inv) + case reflect.Slice: + out.SetBytes(inv.Bytes()) + } + return true + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch inv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + out.SetInt(inv.Int()) + return true + case reflect.Float32, reflect.Float64: + out.SetInt(int64(inv.Float())) + return true + case reflect.Bool: + if inv.Bool() { + out.SetInt(1) + } else { + out.SetInt(0) + } + return true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + panic("can't happen: no uint types in BSON (!?)") + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + switch inv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + out.SetUint(uint64(inv.Int())) + return true + case reflect.Float32, reflect.Float64: + out.SetUint(uint64(inv.Float())) + return true + case reflect.Bool: + if inv.Bool() { + out.SetUint(1) + } else { + out.SetUint(0) + } + return true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + panic("Can't happen. No uint types in BSON.") + } + case reflect.Float32, reflect.Float64: + switch inv.Kind() { + case reflect.Float32, reflect.Float64: + out.SetFloat(inv.Float()) + return true + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + out.SetFloat(float64(inv.Int())) + return true + case reflect.Bool: + if inv.Bool() { + out.SetFloat(1) + } else { + out.SetFloat(0) + } + return true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + panic("Can't happen. No uint types in BSON?") + } + case reflect.Bool: + switch inv.Kind() { + case reflect.Bool: + out.SetBool(inv.Bool()) + return true + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + out.SetBool(inv.Int() != 0) + return true + case reflect.Float32, reflect.Float64: + out.SetBool(inv.Float() != 0) + return true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + panic("Can't happen. No uint types in BSON?") + } + case reflect.Struct: + if outt == typeURL && inv.Kind() == reflect.String { + u, err := url.Parse(inv.String()) + if err != nil { + panic(err) + } + out.Set(reflect.ValueOf(u).Elem()) + return true + } + if outt == typeBinary { + if b, ok := in.([]byte); ok { + out.Set(reflect.ValueOf(Binary{Data: b})) + return true + } + } + } + + return false +} + +// -------------------------------------------------------------------------- +// Parsers of basic types. + +func (d *decoder) readRegEx() RegEx { + re := RegEx{} + re.Pattern = d.readCStr() + re.Options = d.readCStr() + return re +} + +func (d *decoder) readBinary() Binary { + l := d.readInt32() + b := Binary{} + b.Kind = d.readByte() + if b.Kind == BinaryBinaryOld && l > 4 { + // Weird obsolete format with redundant length. + rl := d.readInt32() + if rl != l-4 { + corrupted() + } + l = rl + } + b.Data = d.readBytes(l) + return b +} + +func (d *decoder) readStr() string { + l := d.readInt32() + b := d.readBytes(l - 1) + if d.readByte() != '\x00' { + corrupted() + } + return string(b) +} + +func (d *decoder) readCStr() string { + start := d.i + end := start + l := len(d.in) + for ; end != l; end++ { + if d.in[end] == '\x00' { + break + } + } + d.i = end + 1 + if d.i > l { + corrupted() + } + return string(d.in[start:end]) +} + +func (d *decoder) readBool() bool { + b := d.readByte() + if b == 0 { + return false + } + if b == 1 { + return true + } + panic(fmt.Sprintf("encoded boolean must be 1 or 0, found %d", b)) +} + +func (d *decoder) readFloat64() float64 { + return math.Float64frombits(uint64(d.readInt64())) +} + +func (d *decoder) readInt32() int32 { + b := d.readBytes(4) + return int32((uint32(b[0]) << 0) | + (uint32(b[1]) << 8) | + (uint32(b[2]) << 16) | + (uint32(b[3]) << 24)) +} + +func getSize(offset int, b []byte) (int, error) { + if offset+4 > len(b) { + return 0, io.ErrUnexpectedEOF + } + return int((uint32(b[offset]) << 0) | + (uint32(b[offset+1]) << 8) | + (uint32(b[offset+2]) << 16) | + (uint32(b[offset+3]) << 24)), nil +} + +func (d *decoder) readInt64() int64 { + b := d.readBytes(8) + return int64((uint64(b[0]) << 0) | + (uint64(b[1]) << 8) | + (uint64(b[2]) << 16) | + (uint64(b[3]) << 24) | + (uint64(b[4]) << 32) | + (uint64(b[5]) << 40) | + (uint64(b[6]) << 48) | + (uint64(b[7]) << 56)) +} + +func (d *decoder) readByte() byte { + i := d.i + d.i++ + if d.i > len(d.in) { + corrupted() + } + return d.in[i] +} + +func (d *decoder) readBytes(length int32) []byte { + if length < 0 { + corrupted() + } + start := d.i + d.i += int(length) + if d.i < start || d.i > len(d.in) { + corrupted() + } + return d.in[start : start+int(length)] +} diff --git a/vendor/github.com/globalsign/mgo/bson/encode.go b/vendor/github.com/globalsign/mgo/bson/encode.go new file mode 100644 index 000000000..d0c6b2a85 --- /dev/null +++ b/vendor/github.com/globalsign/mgo/bson/encode.go @@ -0,0 +1,645 @@ +// BSON library for Go +// +// Copyright (c) 2010-2012 - Gustavo Niemeyer +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// gobson - BSON library for Go. + +package bson + +import ( + "encoding/json" + "fmt" + "math" + "net/url" + "reflect" + "sort" + "strconv" + "sync" + "time" +) + +// -------------------------------------------------------------------------- +// Some internal infrastructure. + +var ( + typeBinary = reflect.TypeOf(Binary{}) + typeObjectId = reflect.TypeOf(ObjectId("")) + typeDBPointer = reflect.TypeOf(DBPointer{"", ObjectId("")}) + typeSymbol = reflect.TypeOf(Symbol("")) + typeMongoTimestamp = reflect.TypeOf(MongoTimestamp(0)) + typeOrderKey = reflect.TypeOf(MinKey) + typeDocElem = reflect.TypeOf(DocElem{}) + typeRawDocElem = reflect.TypeOf(RawDocElem{}) + typeRaw = reflect.TypeOf(Raw{}) + typeRawPtr = reflect.PtrTo(reflect.TypeOf(Raw{})) + typeURL = reflect.TypeOf(url.URL{}) + typeTime = reflect.TypeOf(time.Time{}) + typeString = reflect.TypeOf("") + typeJSONNumber = reflect.TypeOf(json.Number("")) + typeTimeDuration = reflect.TypeOf(time.Duration(0)) +) + +var ( + // spec for []uint8 or []byte encoding + arrayOps = map[string]bool{ + "$in": true, + "$nin": true, + "$all": true, + } +) + +const itoaCacheSize = 32 + +const ( + getterUnknown = iota + getterNone + getterTypeVal + getterTypePtr + getterAddr +) + +var itoaCache []string + +var getterStyles map[reflect.Type]int +var getterIface reflect.Type +var getterMutex sync.RWMutex + +func init() { + itoaCache = make([]string, itoaCacheSize) + for i := 0; i != itoaCacheSize; i++ { + itoaCache[i] = strconv.Itoa(i) + } + var iface Getter + getterIface = reflect.TypeOf(&iface).Elem() + getterStyles = make(map[reflect.Type]int) +} + +func itoa(i int) string { + if i < itoaCacheSize { + return itoaCache[i] + } + return strconv.Itoa(i) +} + +func getterStyle(outt reflect.Type) int { + getterMutex.RLock() + style := getterStyles[outt] + getterMutex.RUnlock() + if style != getterUnknown { + return style + } + + getterMutex.Lock() + defer getterMutex.Unlock() + if outt.Implements(getterIface) { + vt := outt + for vt.Kind() == reflect.Ptr { + vt = vt.Elem() + } + if vt.Implements(getterIface) { + style = getterTypeVal + } else { + style = getterTypePtr + } + } else if reflect.PtrTo(outt).Implements(getterIface) { + style = getterAddr + } else { + style = getterNone + } + getterStyles[outt] = style + return style +} + +func getGetter(outt reflect.Type, out reflect.Value) Getter { + style := getterStyle(outt) + if style == getterNone { + return nil + } + if style == getterAddr { + if !out.CanAddr() { + return nil + } + return out.Addr().Interface().(Getter) + } + if style == getterTypeVal && out.Kind() == reflect.Ptr && out.IsNil() { + return nil + } + return out.Interface().(Getter) +} + +// -------------------------------------------------------------------------- +// Marshaling of the document value itself. + +type encoder struct { + out []byte +} + +func (e *encoder) addDoc(v reflect.Value) { + for { + if vi, ok := v.Interface().(Getter); ok { + getv, err := vi.GetBSON() + if err != nil { + panic(err) + } + v = reflect.ValueOf(getv) + continue + } + if v.Kind() == reflect.Ptr { + v = v.Elem() + continue + } + break + } + + if v.Type() == typeRaw { + raw := v.Interface().(Raw) + if raw.Kind != 0x03 && raw.Kind != 0x00 { + panic("Attempted to marshal Raw kind " + strconv.Itoa(int(raw.Kind)) + " as a document") + } + if len(raw.Data) == 0 { + panic("Attempted to marshal empty Raw document") + } + e.addBytes(raw.Data...) + return + } + + start := e.reserveInt32() + + switch v.Kind() { + case reflect.Map: + e.addMap(v) + case reflect.Struct: + e.addStruct(v) + case reflect.Array, reflect.Slice: + e.addSlice(v) + default: + panic("Can't marshal " + v.Type().String() + " as a BSON document") + } + + e.addBytes(0) + e.setInt32(start, int32(len(e.out)-start)) +} + +func (e *encoder) addMap(v reflect.Value) { + for _, k := range v.MapKeys() { + e.addElem(fmt.Sprint(k), v.MapIndex(k), false) + } +} + +func (e *encoder) addStruct(v reflect.Value) { + sinfo, err := getStructInfo(v.Type()) + if err != nil { + panic(err) + } + var value reflect.Value + if sinfo.InlineMap >= 0 { + m := v.Field(sinfo.InlineMap) + if m.Len() > 0 { + for _, k := range m.MapKeys() { + ks := k.String() + if _, found := sinfo.FieldsMap[ks]; found { + panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", ks)) + } + e.addElem(ks, m.MapIndex(k), false) + } + } + } + for _, info := range sinfo.FieldsList { + if info.Inline == nil { + value = v.Field(info.Num) + } else { + // as pointers to struct are allowed here, + // there is no guarantee that pointer won't be nil. + // + // It is expected allowed behaviour + // so info.Inline MAY consist index to a nil pointer + // and that is why we safely call v.FieldByIndex and just continue on panic + field, errField := safeFieldByIndex(v, info.Inline) + if errField != nil { + continue + } + + value = field + } + if info.OmitEmpty && isZero(value) { + continue + } + if useRespectNilValues && + (value.Kind() == reflect.Slice || value.Kind() == reflect.Map) && + value.IsNil() { + e.addElem(info.Key, reflect.ValueOf(nil), info.MinSize) + continue + } + e.addElem(info.Key, value, info.MinSize) + } +} + +func safeFieldByIndex(v reflect.Value, index []int) (result reflect.Value, err error) { + defer func() { + if recovered := recover(); recovered != nil { + switch r := recovered.(type) { + case string: + err = fmt.Errorf("%s", r) + case error: + err = r + } + } + }() + + result = v.FieldByIndex(index) + return +} + +func isZero(v reflect.Value) bool { + switch v.Kind() { + case reflect.String: + return len(v.String()) == 0 + case reflect.Ptr, reflect.Interface: + return v.IsNil() + case reflect.Slice: + return v.Len() == 0 + case reflect.Map: + return v.Len() == 0 + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Struct: + vt := v.Type() + if vt == typeTime { + return v.Interface().(time.Time).IsZero() + } + for i := 0; i < v.NumField(); i++ { + if vt.Field(i).PkgPath != "" && !vt.Field(i).Anonymous { + continue // Private field + } + if !isZero(v.Field(i)) { + return false + } + } + return true + } + return false +} + +func (e *encoder) addSlice(v reflect.Value) { + vi := v.Interface() + if d, ok := vi.(D); ok { + for _, elem := range d { + e.addElem(elem.Name, reflect.ValueOf(elem.Value), false) + } + return + } + if d, ok := vi.(RawD); ok { + for _, elem := range d { + e.addElem(elem.Name, reflect.ValueOf(elem.Value), false) + } + return + } + l := v.Len() + et := v.Type().Elem() + if et == typeDocElem { + for i := 0; i < l; i++ { + elem := v.Index(i).Interface().(DocElem) + e.addElem(elem.Name, reflect.ValueOf(elem.Value), false) + } + return + } + if et == typeRawDocElem { + for i := 0; i < l; i++ { + elem := v.Index(i).Interface().(RawDocElem) + e.addElem(elem.Name, reflect.ValueOf(elem.Value), false) + } + return + } + for i := 0; i < l; i++ { + e.addElem(itoa(i), v.Index(i), false) + } +} + +// -------------------------------------------------------------------------- +// Marshaling of elements in a document. + +func (e *encoder) addElemName(kind byte, name string) { + e.addBytes(kind) + e.addBytes([]byte(name)...) + e.addBytes(0) +} + +func (e *encoder) addElem(name string, v reflect.Value, minSize bool) { + + if !v.IsValid() { + e.addElemName(0x0A, name) + return + } + + if getter := getGetter(v.Type(), v); getter != nil { + getv, err := getter.GetBSON() + if err != nil { + panic(err) + } + e.addElem(name, reflect.ValueOf(getv), minSize) + return + } + + switch v.Kind() { + + case reflect.Interface: + e.addElem(name, v.Elem(), minSize) + + case reflect.Ptr: + e.addElem(name, v.Elem(), minSize) + + case reflect.String: + s := v.String() + switch v.Type() { + case typeObjectId: + if len(s) != 12 { + panic("ObjectIDs must be exactly 12 bytes long (got " + + strconv.Itoa(len(s)) + ")") + } + e.addElemName(0x07, name) + e.addBytes([]byte(s)...) + case typeSymbol: + e.addElemName(0x0E, name) + e.addStr(s) + case typeJSONNumber: + n := v.Interface().(json.Number) + if i, err := n.Int64(); err == nil { + e.addElemName(0x12, name) + e.addInt64(i) + } else if f, err := n.Float64(); err == nil { + e.addElemName(0x01, name) + e.addFloat64(f) + } else { + panic("failed to convert json.Number to a number: " + s) + } + default: + e.addElemName(0x02, name) + e.addStr(s) + } + + case reflect.Float32, reflect.Float64: + e.addElemName(0x01, name) + e.addFloat64(v.Float()) + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + u := v.Uint() + if int64(u) < 0 { + panic("BSON has no uint64 type, and value is too large to fit correctly in an int64") + } else if u <= math.MaxInt32 && (minSize || v.Kind() <= reflect.Uint32) { + e.addElemName(0x10, name) + e.addInt32(int32(u)) + } else { + e.addElemName(0x12, name) + e.addInt64(int64(u)) + } + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch v.Type() { + case typeMongoTimestamp: + e.addElemName(0x11, name) + e.addInt64(v.Int()) + + case typeOrderKey: + if v.Int() == int64(MaxKey) { + e.addElemName(0x7F, name) + } else { + e.addElemName(0xFF, name) + } + case typeTimeDuration: + // Stored as int64 + e.addElemName(0x12, name) + + e.addInt64(int64(v.Int() / 1e6)) + default: + i := v.Int() + if (minSize || v.Type().Kind() != reflect.Int64) && i >= math.MinInt32 && i <= math.MaxInt32 { + // It fits into an int32, encode as such. + e.addElemName(0x10, name) + e.addInt32(int32(i)) + } else { + e.addElemName(0x12, name) + e.addInt64(i) + } + } + + case reflect.Bool: + e.addElemName(0x08, name) + if v.Bool() { + e.addBytes(1) + } else { + e.addBytes(0) + } + + case reflect.Map: + e.addElemName(0x03, name) + e.addDoc(v) + + case reflect.Slice: + vt := v.Type() + et := vt.Elem() + if et.Kind() == reflect.Uint8 { + if arrayOps[name] { + e.addElemName(0x04, name) + e.addDoc(v) + } else { + e.addElemName(0x05, name) + e.addBinary(0x00, v.Bytes()) + } + } else if et == typeDocElem || et == typeRawDocElem { + e.addElemName(0x03, name) + e.addDoc(v) + } else { + e.addElemName(0x04, name) + e.addDoc(v) + } + + case reflect.Array: + et := v.Type().Elem() + if et.Kind() == reflect.Uint8 { + if arrayOps[name] { + e.addElemName(0x04, name) + e.addDoc(v) + } else { + e.addElemName(0x05, name) + if v.CanAddr() { + e.addBinary(0x00, v.Slice(0, v.Len()).Interface().([]byte)) + } else { + n := v.Len() + e.addInt32(int32(n)) + e.addBytes(0x00) + for i := 0; i < n; i++ { + el := v.Index(i) + e.addBytes(byte(el.Uint())) + } + } + } + } else { + e.addElemName(0x04, name) + e.addDoc(v) + } + + case reflect.Struct: + switch s := v.Interface().(type) { + + case Raw: + kind := s.Kind + if kind == 0x00 { + kind = 0x03 + } + if len(s.Data) == 0 && kind != 0x06 && kind != 0x0A && kind != 0xFF && kind != 0x7F { + panic("Attempted to marshal empty Raw document") + } + e.addElemName(kind, name) + e.addBytes(s.Data...) + + case Binary: + e.addElemName(0x05, name) + e.addBinary(s.Kind, s.Data) + + case Decimal128: + e.addElemName(0x13, name) + e.addInt64(int64(s.l)) + e.addInt64(int64(s.h)) + + case DBPointer: + e.addElemName(0x0C, name) + e.addStr(s.Namespace) + if len(s.Id) != 12 { + panic("ObjectIDs must be exactly 12 bytes long (got " + + strconv.Itoa(len(s.Id)) + ")") + } + e.addBytes([]byte(s.Id)...) + + case RegEx: + e.addElemName(0x0B, name) + e.addCStr(s.Pattern) + options := runes(s.Options) + sort.Sort(options) + e.addCStr(string(options)) + + case JavaScript: + if s.Scope == nil { + e.addElemName(0x0D, name) + e.addStr(s.Code) + } else { + e.addElemName(0x0F, name) + start := e.reserveInt32() + e.addStr(s.Code) + e.addDoc(reflect.ValueOf(s.Scope)) + e.setInt32(start, int32(len(e.out)-start)) + } + + case time.Time: + // MongoDB handles timestamps as milliseconds. + e.addElemName(0x09, name) + e.addInt64(s.Unix()*1000 + int64(s.Nanosecond()/1e6)) + + case url.URL: + e.addElemName(0x02, name) + e.addStr(s.String()) + + case undefined: + e.addElemName(0x06, name) + + default: + e.addElemName(0x03, name) + e.addDoc(v) + } + + default: + panic("Can't marshal " + v.Type().String() + " in a BSON document") + } +} + +// ------------- +// Helper method for sorting regex options +type runes []rune + +func (a runes) Len() int { return len(a) } +func (a runes) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a runes) Less(i, j int) bool { return a[i] < a[j] } + +// -------------------------------------------------------------------------- +// Marshaling of base types. + +func (e *encoder) addBinary(subtype byte, v []byte) { + if subtype == 0x02 { + // Wonder how that brilliant idea came to life. Obsolete, luckily. + e.addInt32(int32(len(v) + 4)) + e.addBytes(subtype) + e.addInt32(int32(len(v))) + } else { + e.addInt32(int32(len(v))) + e.addBytes(subtype) + } + e.addBytes(v...) +} + +func (e *encoder) addStr(v string) { + e.addInt32(int32(len(v) + 1)) + e.addCStr(v) +} + +func (e *encoder) addCStr(v string) { + e.addBytes([]byte(v)...) + e.addBytes(0) +} + +func (e *encoder) reserveInt32() (pos int) { + pos = len(e.out) + e.addBytes(0, 0, 0, 0) + return pos +} + +func (e *encoder) setInt32(pos int, v int32) { + e.out[pos+0] = byte(v) + e.out[pos+1] = byte(v >> 8) + e.out[pos+2] = byte(v >> 16) + e.out[pos+3] = byte(v >> 24) +} + +func (e *encoder) addInt32(v int32) { + u := uint32(v) + e.addBytes(byte(u), byte(u>>8), byte(u>>16), byte(u>>24)) +} + +func (e *encoder) addInt64(v int64) { + u := uint64(v) + e.addBytes(byte(u), byte(u>>8), byte(u>>16), byte(u>>24), + byte(u>>32), byte(u>>40), byte(u>>48), byte(u>>56)) +} + +func (e *encoder) addFloat64(v float64) { + e.addInt64(int64(math.Float64bits(v))) +} + +func (e *encoder) addBytes(v ...byte) { + e.out = append(e.out, v...) +} diff --git a/vendor/github.com/globalsign/mgo/bson/json.go b/vendor/github.com/globalsign/mgo/bson/json.go new file mode 100644 index 000000000..045c71301 --- /dev/null +++ b/vendor/github.com/globalsign/mgo/bson/json.go @@ -0,0 +1,384 @@ +package bson + +import ( + "bytes" + "encoding/base64" + "fmt" + "strconv" + "strings" + "time" + + "github.com/globalsign/mgo/internal/json" +) + +// UnmarshalJSON unmarshals a JSON value that may hold non-standard +// syntax as defined in BSON's extended JSON specification. +func UnmarshalJSON(data []byte, value interface{}) error { + d := json.NewDecoder(bytes.NewBuffer(data)) + d.Extend(&jsonExt) + return d.Decode(value) +} + +// MarshalJSON marshals a JSON value that may hold non-standard +// syntax as defined in BSON's extended JSON specification. +func MarshalJSON(value interface{}) ([]byte, error) { + var buf bytes.Buffer + e := json.NewEncoder(&buf) + e.Extend(&jsonExt) + err := e.Encode(value) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// jdec is used internally by the JSON decoding functions +// so they may unmarshal functions without getting into endless +// recursion due to keyed objects. +func jdec(data []byte, value interface{}) error { + d := json.NewDecoder(bytes.NewBuffer(data)) + d.Extend(&funcExt) + return d.Decode(value) +} + +var jsonExt json.Extension +var funcExt json.Extension + +// TODO +// - Shell regular expressions ("/regexp/opts") + +func init() { + jsonExt.DecodeUnquotedKeys(true) + jsonExt.DecodeTrailingCommas(true) + + funcExt.DecodeFunc("BinData", "$binaryFunc", "$type", "$binary") + jsonExt.DecodeKeyed("$binary", jdecBinary) + jsonExt.DecodeKeyed("$binaryFunc", jdecBinary) + jsonExt.EncodeType([]byte(nil), jencBinarySlice) + jsonExt.EncodeType(Binary{}, jencBinaryType) + + funcExt.DecodeFunc("ISODate", "$dateFunc", "S") + funcExt.DecodeFunc("new Date", "$dateFunc", "S") + jsonExt.DecodeKeyed("$date", jdecDate) + jsonExt.DecodeKeyed("$dateFunc", jdecDate) + jsonExt.EncodeType(time.Time{}, jencDate) + + funcExt.DecodeFunc("Timestamp", "$timestamp", "t", "i") + jsonExt.DecodeKeyed("$timestamp", jdecTimestamp) + jsonExt.EncodeType(MongoTimestamp(0), jencTimestamp) + + funcExt.DecodeConst("undefined", Undefined) + + jsonExt.DecodeKeyed("$regex", jdecRegEx) + jsonExt.EncodeType(RegEx{}, jencRegEx) + + funcExt.DecodeFunc("ObjectId", "$oidFunc", "Id") + jsonExt.DecodeKeyed("$oid", jdecObjectId) + jsonExt.DecodeKeyed("$oidFunc", jdecObjectId) + jsonExt.EncodeType(ObjectId(""), jencObjectId) + + funcExt.DecodeFunc("DBRef", "$dbrefFunc", "$ref", "$id") + jsonExt.DecodeKeyed("$dbrefFunc", jdecDBRef) + + funcExt.DecodeFunc("NumberLong", "$numberLongFunc", "N") + jsonExt.DecodeKeyed("$numberLong", jdecNumberLong) + jsonExt.DecodeKeyed("$numberLongFunc", jdecNumberLong) + jsonExt.EncodeType(int64(0), jencNumberLong) + jsonExt.EncodeType(int(0), jencInt) + + funcExt.DecodeConst("MinKey", MinKey) + funcExt.DecodeConst("MaxKey", MaxKey) + jsonExt.DecodeKeyed("$minKey", jdecMinKey) + jsonExt.DecodeKeyed("$maxKey", jdecMaxKey) + jsonExt.EncodeType(orderKey(0), jencMinMaxKey) + + jsonExt.DecodeKeyed("$undefined", jdecUndefined) + jsonExt.EncodeType(Undefined, jencUndefined) + + jsonExt.Extend(&funcExt) +} + +func fbytes(format string, args ...interface{}) []byte { + var buf bytes.Buffer + fmt.Fprintf(&buf, format, args...) + return buf.Bytes() +} + +func jdecBinary(data []byte) (interface{}, error) { + var v struct { + Binary []byte `json:"$binary"` + Type string `json:"$type"` + Func struct { + Binary []byte `json:"$binary"` + Type int64 `json:"$type"` + } `json:"$binaryFunc"` + } + err := jdec(data, &v) + if err != nil { + return nil, err + } + + var binData []byte + var binKind int64 + if v.Type == "" && v.Binary == nil { + binData = v.Func.Binary + binKind = v.Func.Type + } else if v.Type == "" { + return v.Binary, nil + } else { + binData = v.Binary + binKind, err = strconv.ParseInt(v.Type, 0, 64) + if err != nil { + binKind = -1 + } + } + + if binKind == 0 { + return binData, nil + } + if binKind < 0 || binKind > 255 { + return nil, fmt.Errorf("invalid type in binary object: %s", data) + } + + return Binary{Kind: byte(binKind), Data: binData}, nil +} + +func jencBinarySlice(v interface{}) ([]byte, error) { + in := v.([]byte) + out := make([]byte, base64.StdEncoding.EncodedLen(len(in))) + base64.StdEncoding.Encode(out, in) + return fbytes(`{"$binary":"%s","$type":"0x0"}`, out), nil +} + +func jencBinaryType(v interface{}) ([]byte, error) { + in := v.(Binary) + out := make([]byte, base64.StdEncoding.EncodedLen(len(in.Data))) + base64.StdEncoding.Encode(out, in.Data) + return fbytes(`{"$binary":"%s","$type":"0x%x"}`, out, in.Kind), nil +} + +const jdateFormat = "2006-01-02T15:04:05.999Z07:00" + +func jdecDate(data []byte) (interface{}, error) { + var v struct { + S string `json:"$date"` + Func struct { + S string + } `json:"$dateFunc"` + } + _ = jdec(data, &v) + if v.S == "" { + v.S = v.Func.S + } + if v.S != "" { + var errs []string + for _, format := range []string{jdateFormat, "2006-01-02"} { + t, err := time.Parse(format, v.S) + if err == nil { + return t, nil + } + errs = append(errs, err.Error()) + } + return nil, fmt.Errorf("cannot parse date: %q [%s]", v.S, strings.Join(errs, ", ")) + } + + var vn struct { + Date struct { + N int64 `json:"$numberLong,string"` + } `json:"$date"` + Func struct { + S int64 + } `json:"$dateFunc"` + } + err := jdec(data, &vn) + if err != nil { + return nil, fmt.Errorf("cannot parse date: %q", data) + } + n := vn.Date.N + if n == 0 { + n = vn.Func.S + } + return time.Unix(n/1000, n%1000*1e6).UTC(), nil +} + +func jencDate(v interface{}) ([]byte, error) { + t := v.(time.Time) + return fbytes(`{"$date":%q}`, t.Format(jdateFormat)), nil +} + +func jdecTimestamp(data []byte) (interface{}, error) { + var v struct { + Func struct { + T int32 `json:"t"` + I int32 `json:"i"` + } `json:"$timestamp"` + } + err := jdec(data, &v) + if err != nil { + return nil, err + } + return MongoTimestamp(uint64(v.Func.T)<<32 | uint64(uint32(v.Func.I))), nil +} + +func jencTimestamp(v interface{}) ([]byte, error) { + ts := uint64(v.(MongoTimestamp)) + return fbytes(`{"$timestamp":{"t":%d,"i":%d}}`, ts>>32, uint32(ts)), nil +} + +func jdecRegEx(data []byte) (interface{}, error) { + var v struct { + Regex string `json:"$regex"` + Options string `json:"$options"` + } + err := jdec(data, &v) + if err != nil { + return nil, err + } + return RegEx{v.Regex, v.Options}, nil +} + +func jencRegEx(v interface{}) ([]byte, error) { + re := v.(RegEx) + type regex struct { + Regex string `json:"$regex"` + Options string `json:"$options"` + } + return json.Marshal(regex{re.Pattern, re.Options}) +} + +func jdecObjectId(data []byte) (interface{}, error) { + var v struct { + Id string `json:"$oid"` + Func struct { + Id string + } `json:"$oidFunc"` + } + err := jdec(data, &v) + if err != nil { + return nil, err + } + if v.Id == "" { + v.Id = v.Func.Id + } + return ObjectIdHex(v.Id), nil +} + +func jencObjectId(v interface{}) ([]byte, error) { + return fbytes(`{"$oid":"%s"}`, v.(ObjectId).Hex()), nil +} + +func jdecDBRef(data []byte) (interface{}, error) { + // TODO Support unmarshaling $ref and $id into the input value. + var v struct { + Obj map[string]interface{} `json:"$dbrefFunc"` + } + // TODO Fix this. Must not be required. + v.Obj = make(map[string]interface{}) + err := jdec(data, &v) + if err != nil { + return nil, err + } + return v.Obj, nil +} + +func jdecNumberLong(data []byte) (interface{}, error) { + var v struct { + N int64 `json:"$numberLong,string"` + Func struct { + N int64 `json:",string"` + } `json:"$numberLongFunc"` + } + var vn struct { + N int64 `json:"$numberLong"` + Func struct { + N int64 + } `json:"$numberLongFunc"` + } + err := jdec(data, &v) + if err != nil { + err = jdec(data, &vn) + v.N = vn.N + v.Func.N = vn.Func.N + } + if err != nil { + return nil, err + } + if v.N != 0 { + return v.N, nil + } + return v.Func.N, nil +} + +func jencNumberLong(v interface{}) ([]byte, error) { + n := v.(int64) + f := `{"$numberLong":"%d"}` + if n <= 1<<53 { + f = `{"$numberLong":%d}` + } + return fbytes(f, n), nil +} + +func jencInt(v interface{}) ([]byte, error) { + n := v.(int) + f := `{"$numberLong":"%d"}` + if int64(n) <= 1<<53 { + f = `%d` + } + return fbytes(f, n), nil +} + +func jdecMinKey(data []byte) (interface{}, error) { + var v struct { + N int64 `json:"$minKey"` + } + err := jdec(data, &v) + if err != nil { + return nil, err + } + if v.N != 1 { + return nil, fmt.Errorf("invalid $minKey object: %s", data) + } + return MinKey, nil +} + +func jdecMaxKey(data []byte) (interface{}, error) { + var v struct { + N int64 `json:"$maxKey"` + } + err := jdec(data, &v) + if err != nil { + return nil, err + } + if v.N != 1 { + return nil, fmt.Errorf("invalid $maxKey object: %s", data) + } + return MaxKey, nil +} + +func jencMinMaxKey(v interface{}) ([]byte, error) { + switch v.(orderKey) { + case MinKey: + return []byte(`{"$minKey":1}`), nil + case MaxKey: + return []byte(`{"$maxKey":1}`), nil + } + panic(fmt.Sprintf("invalid $minKey/$maxKey value: %d", v)) +} + +func jdecUndefined(data []byte) (interface{}, error) { + var v struct { + B bool `json:"$undefined"` + } + err := jdec(data, &v) + if err != nil { + return nil, err + } + if !v.B { + return nil, fmt.Errorf("invalid $undefined object: %s", data) + } + return Undefined, nil +} + +func jencUndefined(v interface{}) ([]byte, error) { + return []byte(`{"$undefined":true}`), nil +} diff --git a/vendor/github.com/globalsign/mgo/bson/stream.go b/vendor/github.com/globalsign/mgo/bson/stream.go new file mode 100644 index 000000000..466528457 --- /dev/null +++ b/vendor/github.com/globalsign/mgo/bson/stream.go @@ -0,0 +1,90 @@ +package bson + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" +) + +const ( + // MinDocumentSize is the size of the smallest possible valid BSON document: + // an int32 size header + 0x00 (end of document). + MinDocumentSize = 5 + + // MaxDocumentSize is the largest possible size for a BSON document allowed by MongoDB, + // that is, 16 MiB (see https://docs.mongodb.com/manual/reference/limits/). + MaxDocumentSize = 16777216 +) + +// ErrInvalidDocumentSize is an error returned when a BSON document's header +// contains a size smaller than MinDocumentSize or greater than MaxDocumentSize. +type ErrInvalidDocumentSize struct { + DocumentSize int32 +} + +func (e ErrInvalidDocumentSize) Error() string { + return fmt.Sprintf("invalid document size %d", e.DocumentSize) +} + +// A Decoder reads and decodes BSON values from an input stream. +type Decoder struct { + source io.Reader +} + +// NewDecoder returns a new Decoder that reads from source. +// It does not add any extra buffering, and may not read data from source beyond the BSON values requested. +func NewDecoder(source io.Reader) *Decoder { + return &Decoder{source: source} +} + +// Decode reads the next BSON-encoded value from its input and stores it in the value pointed to by v. +// See the documentation for Unmarshal for details about the conversion of BSON into a Go value. +func (dec *Decoder) Decode(v interface{}) (err error) { + // BSON documents start with their size as a *signed* int32. + var docSize int32 + if err = binary.Read(dec.source, binary.LittleEndian, &docSize); err != nil { + return + } + + if docSize < MinDocumentSize || docSize > MaxDocumentSize { + return ErrInvalidDocumentSize{DocumentSize: docSize} + } + + docBuffer := bytes.NewBuffer(make([]byte, 0, docSize)) + if err = binary.Write(docBuffer, binary.LittleEndian, docSize); err != nil { + return + } + + // docSize is the *full* document's size (including the 4-byte size header, + // which has already been read). + if _, err = io.CopyN(docBuffer, dec.source, int64(docSize-4)); err != nil { + return + } + + // Let Unmarshal handle the rest. + defer handleErr(&err) + return Unmarshal(docBuffer.Bytes(), v) +} + +// An Encoder encodes and writes BSON values to an output stream. +type Encoder struct { + target io.Writer +} + +// NewEncoder returns a new Encoder that writes to target. +func NewEncoder(target io.Writer) *Encoder { + return &Encoder{target: target} +} + +// Encode encodes v to BSON, and if successful writes it to the Encoder's output stream. +// See the documentation for Marshal for details about the conversion of Go values to BSON. +func (enc *Encoder) Encode(v interface{}) error { + data, err := Marshal(v) + if err != nil { + return err + } + + _, err = enc.target.Write(data) + return err +} diff --git a/vendor/github.com/globalsign/mgo/internal/json/LICENSE b/vendor/github.com/globalsign/mgo/internal/json/LICENSE new file mode 100644 index 000000000..744875676 --- /dev/null +++ b/vendor/github.com/globalsign/mgo/internal/json/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/globalsign/mgo/internal/json/decode.go b/vendor/github.com/globalsign/mgo/internal/json/decode.go new file mode 100644 index 000000000..d5ca1f9a8 --- /dev/null +++ b/vendor/github.com/globalsign/mgo/internal/json/decode.go @@ -0,0 +1,1685 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Represents JSON data structure using native Go types: booleans, floats, +// strings, arrays, and maps. + +package json + +import ( + "bytes" + "encoding" + "encoding/base64" + "errors" + "fmt" + "reflect" + "runtime" + "strconv" + "unicode" + "unicode/utf16" + "unicode/utf8" +) + +// Unmarshal parses the JSON-encoded data and stores the result +// in the value pointed to by v. +// +// Unmarshal uses the inverse of the encodings that +// Marshal uses, allocating maps, slices, and pointers as necessary, +// with the following additional rules: +// +// To unmarshal JSON into a pointer, Unmarshal first handles the case of +// the JSON being the JSON literal null. In that case, Unmarshal sets +// the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into +// the value pointed at by the pointer. If the pointer is nil, Unmarshal +// allocates a new value for it to point to. +// +// To unmarshal JSON into a struct, Unmarshal matches incoming object +// keys to the keys used by Marshal (either the struct field name or its tag), +// preferring an exact match but also accepting a case-insensitive match. +// Unmarshal will only set exported fields of the struct. +// +// To unmarshal JSON into an interface value, +// Unmarshal stores one of these in the interface value: +// +// bool, for JSON booleans +// float64, for JSON numbers +// string, for JSON strings +// []interface{}, for JSON arrays +// map[string]interface{}, for JSON objects +// nil for JSON null +// +// To unmarshal a JSON array into a slice, Unmarshal resets the slice length +// to zero and then appends each element to the slice. +// As a special case, to unmarshal an empty JSON array into a slice, +// Unmarshal replaces the slice with a new empty slice. +// +// To unmarshal a JSON array into a Go array, Unmarshal decodes +// JSON array elements into corresponding Go array elements. +// If the Go array is smaller than the JSON array, +// the additional JSON array elements are discarded. +// If the JSON array is smaller than the Go array, +// the additional Go array elements are set to zero values. +// +// To unmarshal a JSON object into a map, Unmarshal first establishes a map to +// use, If the map is nil, Unmarshal allocates a new map. Otherwise Unmarshal +// reuses the existing map, keeping existing entries. Unmarshal then stores key- +// value pairs from the JSON object into the map. The map's key type must +// either be a string or implement encoding.TextUnmarshaler. +// +// If a JSON value is not appropriate for a given target type, +// or if a JSON number overflows the target type, Unmarshal +// skips that field and completes the unmarshaling as best it can. +// If no more serious errors are encountered, Unmarshal returns +// an UnmarshalTypeError describing the earliest such error. +// +// The JSON null value unmarshals into an interface, map, pointer, or slice +// by setting that Go value to nil. Because null is often used in JSON to mean +// ``not present,'' unmarshaling a JSON null into any other Go type has no effect +// on the value and produces no error. +// +// When unmarshaling quoted strings, invalid UTF-8 or +// invalid UTF-16 surrogate pairs are not treated as an error. +// Instead, they are replaced by the Unicode replacement +// character U+FFFD. +// +func Unmarshal(data []byte, v interface{}) error { + // Check for well-formedness. + // Avoids filling out half a data structure + // before discovering a JSON syntax error. + var d decodeState + err := checkValid(data, &d.scan) + if err != nil { + return err + } + + d.init(data) + return d.unmarshal(v) +} + +// Unmarshaler is the interface implemented by types +// that can unmarshal a JSON description of themselves. +// The input can be assumed to be a valid encoding of +// a JSON value. UnmarshalJSON must copy the JSON data +// if it wishes to retain the data after returning. +type Unmarshaler interface { + UnmarshalJSON([]byte) error +} + +// An UnmarshalTypeError describes a JSON value that was +// not appropriate for a value of a specific Go type. +type UnmarshalTypeError struct { + Value string // description of JSON value - "bool", "array", "number -5" + Type reflect.Type // type of Go value it could not be assigned to + Offset int64 // error occurred after reading Offset bytes +} + +func (e *UnmarshalTypeError) Error() string { + return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() +} + +// An UnmarshalFieldError describes a JSON object key that +// led to an unexported (and therefore unwritable) struct field. +// (No longer used; kept for compatibility.) +type UnmarshalFieldError struct { + Key string + Type reflect.Type + Field reflect.StructField +} + +func (e *UnmarshalFieldError) Error() string { + return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String() +} + +// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. +// (The argument to Unmarshal must be a non-nil pointer.) +type InvalidUnmarshalError struct { + Type reflect.Type +} + +func (e *InvalidUnmarshalError) Error() string { + if e.Type == nil { + return "json: Unmarshal(nil)" + } + + if e.Type.Kind() != reflect.Ptr { + return "json: Unmarshal(non-pointer " + e.Type.String() + ")" + } + return "json: Unmarshal(nil " + e.Type.String() + ")" +} + +func (d *decodeState) unmarshal(v interface{}) (err error) { + defer func() { + if r := recover(); r != nil { + if _, ok := r.(runtime.Error); ok { + panic(r) + } + err = r.(error) + } + }() + + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Ptr || rv.IsNil() { + return &InvalidUnmarshalError{reflect.TypeOf(v)} + } + + d.scan.reset() + // We decode rv not rv.Elem because the Unmarshaler interface + // test must be applied at the top level of the value. + d.value(rv) + return d.savedError +} + +// A Number represents a JSON number literal. +type Number string + +// String returns the literal text of the number. +func (n Number) String() string { return string(n) } + +// Float64 returns the number as a float64. +func (n Number) Float64() (float64, error) { + return strconv.ParseFloat(string(n), 64) +} + +// Int64 returns the number as an int64. +func (n Number) Int64() (int64, error) { + return strconv.ParseInt(string(n), 10, 64) +} + +// isValidNumber reports whether s is a valid JSON number literal. +func isValidNumber(s string) bool { + // This function implements the JSON numbers grammar. + // See https://tools.ietf.org/html/rfc7159#section-6 + // and http://json.org/number.gif + + if s == "" { + return false + } + + // Optional - + if s[0] == '-' { + s = s[1:] + if s == "" { + return false + } + } + + // Digits + switch { + default: + return false + + case s[0] == '0': + s = s[1:] + + case '1' <= s[0] && s[0] <= '9': + s = s[1:] + for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { + s = s[1:] + } + } + + // . followed by 1 or more digits. + if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' { + s = s[2:] + for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { + s = s[1:] + } + } + + // e or E followed by an optional - or + and + // 1 or more digits. + if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') { + s = s[1:] + if s[0] == '+' || s[0] == '-' { + s = s[1:] + if s == "" { + return false + } + } + for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { + s = s[1:] + } + } + + // Make sure we are at the end. + return s == "" +} + +// decodeState represents the state while decoding a JSON value. +type decodeState struct { + data []byte + off int // read offset in data + scan scanner + nextscan scanner // for calls to nextValue + savedError error + useNumber bool + ext Extension +} + +// errPhase is used for errors that should not happen unless +// there is a bug in the JSON decoder or something is editing +// the data slice while the decoder executes. +var errPhase = errors.New("JSON decoder out of sync - data changing underfoot?") + +func (d *decodeState) init(data []byte) *decodeState { + d.data = data + d.off = 0 + d.savedError = nil + return d +} + +// error aborts the decoding by panicking with err. +func (d *decodeState) error(err error) { + panic(err) +} + +// saveError saves the first err it is called with, +// for reporting at the end of the unmarshal. +func (d *decodeState) saveError(err error) { + if d.savedError == nil { + d.savedError = err + } +} + +// next cuts off and returns the next full JSON value in d.data[d.off:]. +// The next value is known to be an object or array, not a literal. +func (d *decodeState) next() []byte { + c := d.data[d.off] + item, rest, err := nextValue(d.data[d.off:], &d.nextscan) + if err != nil { + d.error(err) + } + d.off = len(d.data) - len(rest) + + // Our scanner has seen the opening brace/bracket + // and thinks we're still in the middle of the object. + // invent a closing brace/bracket to get it out. + if c == '{' { + d.scan.step(&d.scan, '}') + } else if c == '[' { + d.scan.step(&d.scan, ']') + } else { + // Was inside a function name. Get out of it. + d.scan.step(&d.scan, '(') + d.scan.step(&d.scan, ')') + } + + return item +} + +// scanWhile processes bytes in d.data[d.off:] until it +// receives a scan code not equal to op. +// It updates d.off and returns the new scan code. +func (d *decodeState) scanWhile(op int) int { + var newOp int + for { + if d.off >= len(d.data) { + newOp = d.scan.eof() + d.off = len(d.data) + 1 // mark processed EOF with len+1 + } else { + c := d.data[d.off] + d.off++ + newOp = d.scan.step(&d.scan, c) + } + if newOp != op { + break + } + } + return newOp +} + +// value decodes a JSON value from d.data[d.off:] into the value. +// it updates d.off to point past the decoded value. +func (d *decodeState) value(v reflect.Value) { + if !v.IsValid() { + _, rest, err := nextValue(d.data[d.off:], &d.nextscan) + if err != nil { + d.error(err) + } + d.off = len(d.data) - len(rest) + + // d.scan thinks we're still at the beginning of the item. + // Feed in an empty string - the shortest, simplest value - + // so that it knows we got to the end of the value. + if d.scan.redo { + // rewind. + d.scan.redo = false + d.scan.step = stateBeginValue + } + d.scan.step(&d.scan, '"') + d.scan.step(&d.scan, '"') + + n := len(d.scan.parseState) + if n > 0 && d.scan.parseState[n-1] == parseObjectKey { + // d.scan thinks we just read an object key; finish the object + d.scan.step(&d.scan, ':') + d.scan.step(&d.scan, '"') + d.scan.step(&d.scan, '"') + d.scan.step(&d.scan, '}') + } + + return + } + + switch op := d.scanWhile(scanSkipSpace); op { + default: + d.error(errPhase) + + case scanBeginArray: + d.array(v) + + case scanBeginObject: + d.object(v) + + case scanBeginLiteral: + d.literal(v) + + case scanBeginName: + d.name(v) + } +} + +type unquotedValue struct{} + +// valueQuoted is like value but decodes a +// quoted string literal or literal null into an interface value. +// If it finds anything other than a quoted string literal or null, +// valueQuoted returns unquotedValue{}. +func (d *decodeState) valueQuoted() interface{} { + switch op := d.scanWhile(scanSkipSpace); op { + default: + d.error(errPhase) + + case scanBeginArray: + d.array(reflect.Value{}) + + case scanBeginObject: + d.object(reflect.Value{}) + + case scanBeginName: + switch v := d.nameInterface().(type) { + case nil, string: + return v + } + + case scanBeginLiteral: + switch v := d.literalInterface().(type) { + case nil, string: + return v + } + } + return unquotedValue{} +} + +// indirect walks down v allocating pointers as needed, +// until it gets to a non-pointer. +// if it encounters an Unmarshaler, indirect stops and returns that. +// if decodingNull is true, indirect stops at the last pointer so it can be set to nil. +func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnmarshaler, reflect.Value) { + // If v is a named type and is addressable, + // start with its address, so that if the type has pointer methods, + // we find them. + if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() { + v = v.Addr() + } + for { + // Load value from interface, but only if the result will be + // usefully addressable. + if v.Kind() == reflect.Interface && !v.IsNil() { + e := v.Elem() + if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) { + v = e + continue + } + } + + if v.Kind() != reflect.Ptr { + break + } + + if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() { + break + } + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + if v.Type().NumMethod() > 0 { + if u, ok := v.Interface().(Unmarshaler); ok { + return u, nil, v + } + if u, ok := v.Interface().(encoding.TextUnmarshaler); ok { + return nil, u, v + } + } + v = v.Elem() + } + return nil, nil, v +} + +// array consumes an array from d.data[d.off-1:], decoding into the value v. +// the first byte of the array ('[') has been read already. +func (d *decodeState) array(v reflect.Value) { + // Check for unmarshaler. + u, ut, pv := d.indirect(v, false) + if u != nil { + d.off-- + err := u.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + if ut != nil { + d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)}) + d.off-- + d.next() + return + } + + v = pv + + // Check type of target. + switch v.Kind() { + case reflect.Interface: + if v.NumMethod() == 0 { + // Decoding into nil interface? Switch to non-reflect code. + v.Set(reflect.ValueOf(d.arrayInterface())) + return + } + // Otherwise it's invalid. + fallthrough + default: + d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)}) + d.off-- + d.next() + return + case reflect.Array: + case reflect.Slice: + break + } + + i := 0 + for { + // Look ahead for ] - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + // Get element of array, growing if necessary. + if v.Kind() == reflect.Slice { + // Grow slice if necessary + if i >= v.Cap() { + newcap := v.Cap() + v.Cap()/2 + if newcap < 4 { + newcap = 4 + } + newv := reflect.MakeSlice(v.Type(), v.Len(), newcap) + reflect.Copy(newv, v) + v.Set(newv) + } + if i >= v.Len() { + v.SetLen(i + 1) + } + } + + if i < v.Len() { + // Decode into element. + d.value(v.Index(i)) + } else { + // Ran out of fixed array: skip. + d.value(reflect.Value{}) + } + i++ + + // Next token must be , or ]. + op = d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + if op != scanArrayValue { + d.error(errPhase) + } + } + + if i < v.Len() { + if v.Kind() == reflect.Array { + // Array. Zero the rest. + z := reflect.Zero(v.Type().Elem()) + for ; i < v.Len(); i++ { + v.Index(i).Set(z) + } + } else { + v.SetLen(i) + } + } + if i == 0 && v.Kind() == reflect.Slice { + v.Set(reflect.MakeSlice(v.Type(), 0, 0)) + } +} + +var nullLiteral = []byte("null") +var textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem() + +// object consumes an object from d.data[d.off-1:], decoding into the value v. +// the first byte ('{') of the object has been read already. +func (d *decodeState) object(v reflect.Value) { + // Check for unmarshaler. + u, ut, pv := d.indirect(v, false) + if d.storeKeyed(pv) { + return + } + if u != nil { + d.off-- + err := u.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + if ut != nil { + d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + d.off-- + d.next() // skip over { } in input + return + } + v = pv + + // Decoding into nil interface? Switch to non-reflect code. + if v.Kind() == reflect.Interface && v.NumMethod() == 0 { + v.Set(reflect.ValueOf(d.objectInterface())) + return + } + + // Check type of target: + // struct or + // map[string]T or map[encoding.TextUnmarshaler]T + switch v.Kind() { + case reflect.Map: + // Map key must either have string kind or be an encoding.TextUnmarshaler. + t := v.Type() + if t.Key().Kind() != reflect.String && + !reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) { + d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + d.off-- + d.next() // skip over { } in input + return + } + if v.IsNil() { + v.Set(reflect.MakeMap(t)) + } + case reflect.Struct: + + default: + d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + d.off-- + d.next() // skip over { } in input + return + } + + var mapElem reflect.Value + + empty := true + for { + // Read opening " of string key or closing }. + op := d.scanWhile(scanSkipSpace) + if op == scanEndObject { + if !empty && !d.ext.trailingCommas { + d.syntaxError("beginning of object key string") + } + break + } + empty = false + if op == scanBeginName { + if !d.ext.unquotedKeys { + d.syntaxError("beginning of object key string") + } + } else if op != scanBeginLiteral { + d.error(errPhase) + } + unquotedKey := op == scanBeginName + + // Read key. + start := d.off - 1 + op = d.scanWhile(scanContinue) + item := d.data[start : d.off-1] + var key []byte + if unquotedKey { + key = item + // TODO Fix code below to quote item when necessary. + } else { + var ok bool + key, ok = unquoteBytes(item) + if !ok { + d.error(errPhase) + } + } + + // Figure out field corresponding to key. + var subv reflect.Value + destring := false // whether the value is wrapped in a string to be decoded first + + if v.Kind() == reflect.Map { + elemType := v.Type().Elem() + if !mapElem.IsValid() { + mapElem = reflect.New(elemType).Elem() + } else { + mapElem.Set(reflect.Zero(elemType)) + } + subv = mapElem + } else { + var f *field + fields := cachedTypeFields(v.Type()) + for i := range fields { + ff := &fields[i] + if bytes.Equal(ff.nameBytes, key) { + f = ff + break + } + if f == nil && ff.equalFold(ff.nameBytes, key) { + f = ff + } + } + if f != nil { + subv = v + destring = f.quoted + for _, i := range f.index { + if subv.Kind() == reflect.Ptr { + if subv.IsNil() { + subv.Set(reflect.New(subv.Type().Elem())) + } + subv = subv.Elem() + } + subv = subv.Field(i) + } + } + } + + // Read : before value. + if op == scanSkipSpace { + op = d.scanWhile(scanSkipSpace) + } + if op != scanObjectKey { + d.error(errPhase) + } + + // Read value. + if destring { + switch qv := d.valueQuoted().(type) { + case nil: + d.literalStore(nullLiteral, subv, false) + case string: + d.literalStore([]byte(qv), subv, true) + default: + d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal unquoted value into %v", subv.Type())) + } + } else { + d.value(subv) + } + + // Write value back to map; + // if using struct, subv points into struct already. + if v.Kind() == reflect.Map { + kt := v.Type().Key() + var kv reflect.Value + switch { + case kt.Kind() == reflect.String: + kv = reflect.ValueOf(key).Convert(v.Type().Key()) + case reflect.PtrTo(kt).Implements(textUnmarshalerType): + kv = reflect.New(v.Type().Key()) + d.literalStore(item, kv, true) + kv = kv.Elem() + default: + panic("json: Unexpected key type") // should never occur + } + v.SetMapIndex(kv, subv) + } + + // Next token must be , or }. + op = d.scanWhile(scanSkipSpace) + if op == scanEndObject { + break + } + if op != scanObjectValue { + d.error(errPhase) + } + } +} + +// isNull returns whether there's a null literal at the provided offset. +func (d *decodeState) isNull(off int) bool { + if off+4 >= len(d.data) || d.data[off] != 'n' || d.data[off+1] != 'u' || d.data[off+2] != 'l' || d.data[off+3] != 'l' { + return false + } + d.nextscan.reset() + for i, c := range d.data[off:] { + if i > 4 { + return false + } + switch d.nextscan.step(&d.nextscan, c) { + case scanContinue, scanBeginName: + continue + } + break + } + return true +} + +// name consumes a const or function from d.data[d.off-1:], decoding into the value v. +// the first byte of the function name has been read already. +func (d *decodeState) name(v reflect.Value) { + if d.isNull(d.off - 1) { + d.literal(v) + return + } + + // Check for unmarshaler. + u, ut, pv := d.indirect(v, false) + if d.storeKeyed(pv) { + return + } + if u != nil { + d.off-- + err := u.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + if ut != nil { + d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + d.off-- + d.next() // skip over function in input + return + } + v = pv + + // Decoding into nil interface? Switch to non-reflect code. + if v.Kind() == reflect.Interface && v.NumMethod() == 0 { + out := d.nameInterface() + if out == nil { + v.Set(reflect.Zero(v.Type())) + } else { + v.Set(reflect.ValueOf(out)) + } + return + } + + nameStart := d.off - 1 + + op := d.scanWhile(scanContinue) + + name := d.data[nameStart : d.off-1] + if op != scanParam { + // Back up so the byte just read is consumed next. + d.off-- + d.scan.undo(op) + if l, ok := d.convertLiteral(name); ok { + d.storeValue(v, l) + return + } + d.error(&SyntaxError{fmt.Sprintf("json: unknown constant %q", name), int64(d.off)}) + } + + funcName := string(name) + funcData := d.ext.funcs[funcName] + if funcData.key == "" { + d.error(fmt.Errorf("json: unknown function %q", funcName)) + } + + // Check type of target: + // struct or + // map[string]T or map[encoding.TextUnmarshaler]T + switch v.Kind() { + case reflect.Map: + // Map key must either have string kind or be an encoding.TextUnmarshaler. + t := v.Type() + if t.Key().Kind() != reflect.String && + !reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) { + d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + d.off-- + d.next() // skip over { } in input + return + } + if v.IsNil() { + v.Set(reflect.MakeMap(t)) + } + case reflect.Struct: + + default: + d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + d.off-- + d.next() // skip over { } in input + return + } + + // TODO Fix case of func field as map. + //topv := v + + // Figure out field corresponding to function. + key := []byte(funcData.key) + if v.Kind() == reflect.Map { + elemType := v.Type().Elem() + v = reflect.New(elemType).Elem() + } else { + var f *field + fields := cachedTypeFields(v.Type()) + for i := range fields { + ff := &fields[i] + if bytes.Equal(ff.nameBytes, key) { + f = ff + break + } + if f == nil && ff.equalFold(ff.nameBytes, key) { + f = ff + } + } + if f != nil { + for _, i := range f.index { + if v.Kind() == reflect.Ptr { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + } + v = v.Field(i) + } + if v.Kind() == reflect.Ptr { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + } + } + } + + // Check for unmarshaler on func field itself. + u, _, _ = d.indirect(v, false) + if u != nil { + d.off = nameStart + err := u.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + + var mapElem reflect.Value + + // Parse function arguments. + for i := 0; ; i++ { + // closing ) - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndParams { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + if i >= len(funcData.args) { + d.error(fmt.Errorf("json: too many arguments for function %s", funcName)) + } + key := []byte(funcData.args[i]) + + // Figure out field corresponding to key. + var subv reflect.Value + destring := false // whether the value is wrapped in a string to be decoded first + + if v.Kind() == reflect.Map { + elemType := v.Type().Elem() + if !mapElem.IsValid() { + mapElem = reflect.New(elemType).Elem() + } else { + mapElem.Set(reflect.Zero(elemType)) + } + subv = mapElem + } else { + var f *field + fields := cachedTypeFields(v.Type()) + for i := range fields { + ff := &fields[i] + if bytes.Equal(ff.nameBytes, key) { + f = ff + break + } + if f == nil && ff.equalFold(ff.nameBytes, key) { + f = ff + } + } + if f != nil { + subv = v + destring = f.quoted + for _, i := range f.index { + if subv.Kind() == reflect.Ptr { + if subv.IsNil() { + subv.Set(reflect.New(subv.Type().Elem())) + } + subv = subv.Elem() + } + subv = subv.Field(i) + } + } + } + + // Read value. + if destring { + switch qv := d.valueQuoted().(type) { + case nil: + d.literalStore(nullLiteral, subv, false) + case string: + d.literalStore([]byte(qv), subv, true) + default: + d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal unquoted value into %v", subv.Type())) + } + } else { + d.value(subv) + } + + // Write value back to map; + // if using struct, subv points into struct already. + if v.Kind() == reflect.Map { + kt := v.Type().Key() + var kv reflect.Value + switch { + case kt.Kind() == reflect.String: + kv = reflect.ValueOf(key).Convert(v.Type().Key()) + case reflect.PtrTo(kt).Implements(textUnmarshalerType): + kv = reflect.New(v.Type().Key()) + d.literalStore(key, kv, true) + kv = kv.Elem() + default: + panic("json: Unexpected key type") // should never occur + } + v.SetMapIndex(kv, subv) + } + + // Next token must be , or ). + op = d.scanWhile(scanSkipSpace) + if op == scanEndParams { + break + } + if op != scanParam { + d.error(errPhase) + } + } +} + +// keyed attempts to decode an object or function using a keyed doc extension, +// and returns the value and true on success, or nil and false otherwise. +func (d *decodeState) keyed() (interface{}, bool) { + if len(d.ext.keyed) == 0 { + return nil, false + } + + unquote := false + + // Look-ahead first key to check for a keyed document extension. + d.nextscan.reset() + var start, end int + for i, c := range d.data[d.off-1:] { + switch op := d.nextscan.step(&d.nextscan, c); op { + case scanSkipSpace, scanContinue, scanBeginObject: + continue + case scanBeginLiteral, scanBeginName: + unquote = op == scanBeginLiteral + start = i + continue + } + end = i + break + } + + name := bytes.Trim(d.data[d.off-1+start:d.off-1+end], " \n\t") + + var key []byte + var ok bool + if unquote { + key, ok = unquoteBytes(name) + if !ok { + d.error(errPhase) + } + } else { + funcData, ok := d.ext.funcs[string(name)] + if !ok { + return nil, false + } + key = []byte(funcData.key) + } + + decode, ok := d.ext.keyed[string(key)] + if !ok { + return nil, false + } + + d.off-- + out, err := decode(d.next()) + if err != nil { + d.error(err) + } + return out, true +} + +func (d *decodeState) storeKeyed(v reflect.Value) bool { + keyed, ok := d.keyed() + if !ok { + return false + } + d.storeValue(v, keyed) + return true +} + +var ( + trueBytes = []byte("true") + falseBytes = []byte("false") + nullBytes = []byte("null") +) + +func (d *decodeState) storeValue(v reflect.Value, from interface{}) { + switch from { + case nil: + d.literalStore(nullBytes, v, false) + return + case true: + d.literalStore(trueBytes, v, false) + return + case false: + d.literalStore(falseBytes, v, false) + return + } + fromv := reflect.ValueOf(from) + for fromv.Kind() == reflect.Ptr && !fromv.IsNil() { + fromv = fromv.Elem() + } + fromt := fromv.Type() + for v.Kind() == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + vt := v.Type() + if fromt.AssignableTo(vt) { + v.Set(fromv) + } else if fromt.ConvertibleTo(vt) { + v.Set(fromv.Convert(vt)) + } else { + d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + } +} + +func (d *decodeState) convertLiteral(name []byte) (interface{}, bool) { + if len(name) == 0 { + return nil, false + } + switch name[0] { + case 't': + if bytes.Equal(name, trueBytes) { + return true, true + } + case 'f': + if bytes.Equal(name, falseBytes) { + return false, true + } + case 'n': + if bytes.Equal(name, nullBytes) { + return nil, true + } + } + if l, ok := d.ext.consts[string(name)]; ok { + return l, true + } + return nil, false +} + +// literal consumes a literal from d.data[d.off-1:], decoding into the value v. +// The first byte of the literal has been read already +// (that's how the caller knows it's a literal). +func (d *decodeState) literal(v reflect.Value) { + // All bytes inside literal return scanContinue op code. + start := d.off - 1 + op := d.scanWhile(scanContinue) + + // Scan read one byte too far; back up. + d.off-- + d.scan.undo(op) + + d.literalStore(d.data[start:d.off], v, false) +} + +// convertNumber converts the number literal s to a float64 or a Number +// depending on the setting of d.useNumber. +func (d *decodeState) convertNumber(s string) (interface{}, error) { + if d.useNumber { + return Number(s), nil + } + f, err := strconv.ParseFloat(s, 64) + if err != nil { + return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)} + } + return f, nil +} + +var numberType = reflect.TypeOf(Number("")) + +// literalStore decodes a literal stored in item into v. +// +// fromQuoted indicates whether this literal came from unwrapping a +// string from the ",string" struct tag option. this is used only to +// produce more helpful error messages. +func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) { + // Check for unmarshaler. + if len(item) == 0 { + //Empty string given + d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + return + } + wantptr := item[0] == 'n' // null + u, ut, pv := d.indirect(v, wantptr) + if u != nil { + err := u.UnmarshalJSON(item) + if err != nil { + d.error(err) + } + return + } + if ut != nil { + if item[0] != '"' { + if fromQuoted { + d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) + } + return + } + s, ok := unquoteBytes(item) + if !ok { + if fromQuoted { + d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.error(errPhase) + } + } + err := ut.UnmarshalText(s) + if err != nil { + d.error(err) + } + return + } + + v = pv + + switch c := item[0]; c { + case 'n': // null + switch v.Kind() { + case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: + v.Set(reflect.Zero(v.Type())) + // otherwise, ignore null for primitives/string + } + case 't', 'f': // true, false + value := c == 't' + switch v.Kind() { + default: + if fromQuoted { + d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)}) + } + case reflect.Bool: + v.SetBool(value) + case reflect.Interface: + if v.NumMethod() == 0 { + v.Set(reflect.ValueOf(value)) + } else { + d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)}) + } + } + + case '"': // string + s, ok := unquoteBytes(item) + if !ok { + if fromQuoted { + d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.error(errPhase) + } + } + switch v.Kind() { + default: + d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) + case reflect.Slice: + if v.Type().Elem().Kind() != reflect.Uint8 { + d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) + break + } + b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) + n, err := base64.StdEncoding.Decode(b, s) + if err != nil { + d.saveError(err) + break + } + v.SetBytes(b[:n]) + case reflect.String: + v.SetString(string(s)) + case reflect.Interface: + if v.NumMethod() == 0 { + v.Set(reflect.ValueOf(string(s))) + } else { + d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) + } + } + + default: // number + if c != '-' && (c < '0' || c > '9') { + if fromQuoted { + d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.error(errPhase) + } + } + s := string(item) + switch v.Kind() { + default: + if v.Kind() == reflect.String && v.Type() == numberType { + v.SetString(s) + if !isValidNumber(s) { + d.error(fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", item)) + } + break + } + if fromQuoted { + d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.error(&UnmarshalTypeError{"number", v.Type(), int64(d.off)}) + } + case reflect.Interface: + n, err := d.convertNumber(s) + if err != nil { + d.saveError(err) + break + } + if v.NumMethod() != 0 { + d.saveError(&UnmarshalTypeError{"number", v.Type(), int64(d.off)}) + break + } + v.Set(reflect.ValueOf(n)) + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + n, err := strconv.ParseInt(s, 10, 64) + if err != nil || v.OverflowInt(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) + break + } + v.SetInt(n) + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + n, err := strconv.ParseUint(s, 10, 64) + if err != nil || v.OverflowUint(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) + break + } + v.SetUint(n) + + case reflect.Float32, reflect.Float64: + n, err := strconv.ParseFloat(s, v.Type().Bits()) + if err != nil || v.OverflowFloat(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) + break + } + v.SetFloat(n) + } + } +} + +// The xxxInterface routines build up a value to be stored +// in an empty interface. They are not strictly necessary, +// but they avoid the weight of reflection in this common case. + +// valueInterface is like value but returns interface{} +func (d *decodeState) valueInterface() interface{} { + switch d.scanWhile(scanSkipSpace) { + default: + d.error(errPhase) + panic("unreachable") + case scanBeginArray: + return d.arrayInterface() + case scanBeginObject: + return d.objectInterface() + case scanBeginLiteral: + return d.literalInterface() + case scanBeginName: + return d.nameInterface() + } +} + +func (d *decodeState) syntaxError(expected string) { + msg := fmt.Sprintf("invalid character '%c' looking for %s", d.data[d.off-1], expected) + d.error(&SyntaxError{msg, int64(d.off)}) +} + +// arrayInterface is like array but returns []interface{}. +func (d *decodeState) arrayInterface() []interface{} { + var v = make([]interface{}, 0) + for { + // Look ahead for ] - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndArray { + if len(v) > 0 && !d.ext.trailingCommas { + d.syntaxError("beginning of value") + } + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + v = append(v, d.valueInterface()) + + // Next token must be , or ]. + op = d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + if op != scanArrayValue { + d.error(errPhase) + } + } + return v +} + +// objectInterface is like object but returns map[string]interface{}. +func (d *decodeState) objectInterface() interface{} { + v, ok := d.keyed() + if ok { + return v + } + + m := make(map[string]interface{}) + for { + // Read opening " of string key or closing }. + op := d.scanWhile(scanSkipSpace) + if op == scanEndObject { + if len(m) > 0 && !d.ext.trailingCommas { + d.syntaxError("beginning of object key string") + } + break + } + if op == scanBeginName { + if !d.ext.unquotedKeys { + d.syntaxError("beginning of object key string") + } + } else if op != scanBeginLiteral { + d.error(errPhase) + } + unquotedKey := op == scanBeginName + + // Read string key. + start := d.off - 1 + op = d.scanWhile(scanContinue) + item := d.data[start : d.off-1] + var key string + if unquotedKey { + key = string(item) + } else { + var ok bool + key, ok = unquote(item) + if !ok { + d.error(errPhase) + } + } + + // Read : before value. + if op == scanSkipSpace { + op = d.scanWhile(scanSkipSpace) + } + if op != scanObjectKey { + d.error(errPhase) + } + + // Read value. + m[key] = d.valueInterface() + + // Next token must be , or }. + op = d.scanWhile(scanSkipSpace) + if op == scanEndObject { + break + } + if op != scanObjectValue { + d.error(errPhase) + } + } + return m +} + +// literalInterface is like literal but returns an interface value. +func (d *decodeState) literalInterface() interface{} { + // All bytes inside literal return scanContinue op code. + start := d.off - 1 + op := d.scanWhile(scanContinue) + + // Scan read one byte too far; back up. + d.off-- + d.scan.undo(op) + item := d.data[start:d.off] + + switch c := item[0]; c { + case 'n': // null + return nil + + case 't', 'f': // true, false + return c == 't' + + case '"': // string + s, ok := unquote(item) + if !ok { + d.error(errPhase) + } + return s + + default: // number + if c != '-' && (c < '0' || c > '9') { + d.error(errPhase) + } + n, err := d.convertNumber(string(item)) + if err != nil { + d.saveError(err) + } + return n + } +} + +// nameInterface is like function but returns map[string]interface{}. +func (d *decodeState) nameInterface() interface{} { + v, ok := d.keyed() + if ok { + return v + } + + nameStart := d.off - 1 + + op := d.scanWhile(scanContinue) + + name := d.data[nameStart : d.off-1] + if op != scanParam { + // Back up so the byte just read is consumed next. + d.off-- + d.scan.undo(op) + if l, ok := d.convertLiteral(name); ok { + return l + } + d.error(&SyntaxError{fmt.Sprintf("json: unknown constant %q", name), int64(d.off)}) + } + + funcName := string(name) + funcData := d.ext.funcs[funcName] + if funcData.key == "" { + d.error(fmt.Errorf("json: unknown function %q", funcName)) + } + + m := make(map[string]interface{}) + for i := 0; ; i++ { + // Look ahead for ) - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndParams { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + if i >= len(funcData.args) { + d.error(fmt.Errorf("json: too many arguments for function %s", funcName)) + } + m[funcData.args[i]] = d.valueInterface() + + // Next token must be , or ). + op = d.scanWhile(scanSkipSpace) + if op == scanEndParams { + break + } + if op != scanParam { + d.error(errPhase) + } + } + return map[string]interface{}{funcData.key: m} +} + +// getu4 decodes \uXXXX from the beginning of s, returning the hex value, +// or it returns -1. +func getu4(s []byte) rune { + if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { + return -1 + } + r, err := strconv.ParseUint(string(s[2:6]), 16, 64) + if err != nil { + return -1 + } + return rune(r) +} + +// unquote converts a quoted JSON string literal s into an actual string t. +// The rules are different than for Go, so cannot use strconv.Unquote. +func unquote(s []byte) (t string, ok bool) { + s, ok = unquoteBytes(s) + t = string(s) + return +} + +func unquoteBytes(s []byte) (t []byte, ok bool) { + if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { + return + } + s = s[1 : len(s)-1] + + // Check for unusual characters. If there are none, + // then no unquoting is needed, so return a slice of the + // original bytes. + r := 0 + for r < len(s) { + c := s[r] + if c == '\\' || c == '"' || c < ' ' { + break + } + if c < utf8.RuneSelf { + r++ + continue + } + rr, size := utf8.DecodeRune(s[r:]) + if rr == utf8.RuneError && size == 1 { + break + } + r += size + } + if r == len(s) { + return s, true + } + + b := make([]byte, len(s)+2*utf8.UTFMax) + w := copy(b, s[0:r]) + for r < len(s) { + // Out of room? Can only happen if s is full of + // malformed UTF-8 and we're replacing each + // byte with RuneError. + if w >= len(b)-2*utf8.UTFMax { + nb := make([]byte, (len(b)+utf8.UTFMax)*2) + copy(nb, b[0:w]) + b = nb + } + switch c := s[r]; { + case c == '\\': + r++ + if r >= len(s) { + return + } + switch s[r] { + default: + return + case '"', '\\', '/', '\'': + b[w] = s[r] + r++ + w++ + case 'b': + b[w] = '\b' + r++ + w++ + case 'f': + b[w] = '\f' + r++ + w++ + case 'n': + b[w] = '\n' + r++ + w++ + case 'r': + b[w] = '\r' + r++ + w++ + case 't': + b[w] = '\t' + r++ + w++ + case 'u': + r-- + rr := getu4(s[r:]) + if rr < 0 { + return + } + r += 6 + if utf16.IsSurrogate(rr) { + rr1 := getu4(s[r:]) + if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { + // A valid pair; consume. + r += 6 + w += utf8.EncodeRune(b[w:], dec) + break + } + // Invalid surrogate; fall back to replacement rune. + rr = unicode.ReplacementChar + } + w += utf8.EncodeRune(b[w:], rr) + } + + // Quote, control characters are invalid. + case c == '"', c < ' ': + return + + // ASCII + case c < utf8.RuneSelf: + b[w] = c + r++ + w++ + + // Coerce to well-formed UTF-8. + default: + rr, size := utf8.DecodeRune(s[r:]) + r += size + w += utf8.EncodeRune(b[w:], rr) + } + } + return b[0:w], true +} diff --git a/vendor/github.com/globalsign/mgo/internal/json/encode.go b/vendor/github.com/globalsign/mgo/internal/json/encode.go new file mode 100644 index 000000000..e4b8f8648 --- /dev/null +++ b/vendor/github.com/globalsign/mgo/internal/json/encode.go @@ -0,0 +1,1260 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package json implements encoding and decoding of JSON as defined in +// RFC 4627. The mapping between JSON and Go values is described +// in the documentation for the Marshal and Unmarshal functions. +// +// See "JSON and Go" for an introduction to this package: +// https://golang.org/doc/articles/json_and_go.html +package json + +import ( + "bytes" + "encoding" + "encoding/base64" + "fmt" + "math" + "reflect" + "runtime" + "sort" + "strconv" + "strings" + "sync" + "unicode" + "unicode/utf8" +) + +// Marshal returns the JSON encoding of v. +// +// Marshal traverses the value v recursively. +// If an encountered value implements the Marshaler interface +// and is not a nil pointer, Marshal calls its MarshalJSON method +// to produce JSON. If no MarshalJSON method is present but the +// value implements encoding.TextMarshaler instead, Marshal calls +// its MarshalText method. +// The nil pointer exception is not strictly necessary +// but mimics a similar, necessary exception in the behavior of +// UnmarshalJSON. +// +// Otherwise, Marshal uses the following type-dependent default encodings: +// +// Boolean values encode as JSON booleans. +// +// Floating point, integer, and Number values encode as JSON numbers. +// +// String values encode as JSON strings coerced to valid UTF-8, +// replacing invalid bytes with the Unicode replacement rune. +// The angle brackets "<" and ">" are escaped to "\u003c" and "\u003e" +// to keep some browsers from misinterpreting JSON output as HTML. +// Ampersand "&" is also escaped to "\u0026" for the same reason. +// This escaping can be disabled using an Encoder with DisableHTMLEscaping. +// +// Array and slice values encode as JSON arrays, except that +// []byte encodes as a base64-encoded string, and a nil slice +// encodes as the null JSON value. +// +// Struct values encode as JSON objects. Each exported struct field +// becomes a member of the object unless +// - the field's tag is "-", or +// - the field is empty and its tag specifies the "omitempty" option. +// The empty values are false, 0, any +// nil pointer or interface value, and any array, slice, map, or string of +// length zero. The object's default key string is the struct field name +// but can be specified in the struct field's tag value. The "json" key in +// the struct field's tag value is the key name, followed by an optional comma +// and options. Examples: +// +// // Field is ignored by this package. +// Field int `json:"-"` +// +// // Field appears in JSON as key "myName". +// Field int `json:"myName"` +// +// // Field appears in JSON as key "myName" and +// // the field is omitted from the object if its value is empty, +// // as defined above. +// Field int `json:"myName,omitempty"` +// +// // Field appears in JSON as key "Field" (the default), but +// // the field is skipped if empty. +// // Note the leading comma. +// Field int `json:",omitempty"` +// +// The "string" option signals that a field is stored as JSON inside a +// JSON-encoded string. It applies only to fields of string, floating point, +// integer, or boolean types. This extra level of encoding is sometimes used +// when communicating with JavaScript programs: +// +// Int64String int64 `json:",string"` +// +// The key name will be used if it's a non-empty string consisting of +// only Unicode letters, digits, dollar signs, percent signs, hyphens, +// underscores and slashes. +// +// Anonymous struct fields are usually marshaled as if their inner exported fields +// were fields in the outer struct, subject to the usual Go visibility rules amended +// as described in the next paragraph. +// An anonymous struct field with a name given in its JSON tag is treated as +// having that name, rather than being anonymous. +// An anonymous struct field of interface type is treated the same as having +// that type as its name, rather than being anonymous. +// +// The Go visibility rules for struct fields are amended for JSON when +// deciding which field to marshal or unmarshal. If there are +// multiple fields at the same level, and that level is the least +// nested (and would therefore be the nesting level selected by the +// usual Go rules), the following extra rules apply: +// +// 1) Of those fields, if any are JSON-tagged, only tagged fields are considered, +// even if there are multiple untagged fields that would otherwise conflict. +// 2) If there is exactly one field (tagged or not according to the first rule), that is selected. +// 3) Otherwise there are multiple fields, and all are ignored; no error occurs. +// +// Handling of anonymous struct fields is new in Go 1.1. +// Prior to Go 1.1, anonymous struct fields were ignored. To force ignoring of +// an anonymous struct field in both current and earlier versions, give the field +// a JSON tag of "-". +// +// Map values encode as JSON objects. The map's key type must either be a string +// or implement encoding.TextMarshaler. The map keys are used as JSON object +// keys, subject to the UTF-8 coercion described for string values above. +// +// Pointer values encode as the value pointed to. +// A nil pointer encodes as the null JSON value. +// +// Interface values encode as the value contained in the interface. +// A nil interface value encodes as the null JSON value. +// +// Channel, complex, and function values cannot be encoded in JSON. +// Attempting to encode such a value causes Marshal to return +// an UnsupportedTypeError. +// +// JSON cannot represent cyclic data structures and Marshal does not +// handle them. Passing cyclic structures to Marshal will result in +// an infinite recursion. +// +func Marshal(v interface{}) ([]byte, error) { + e := &encodeState{} + err := e.marshal(v, encOpts{escapeHTML: true}) + if err != nil { + return nil, err + } + return e.Bytes(), nil +} + +// MarshalIndent is like Marshal but applies Indent to format the output. +func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { + b, err := Marshal(v) + if err != nil { + return nil, err + } + var buf bytes.Buffer + err = Indent(&buf, b, prefix, indent) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029 +// characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029 +// so that the JSON will be safe to embed inside HTML