planetscale 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +24 -0
- data/.github/workflows/gem-push.yml +38 -0
- data/.github/workflows/licensed.yml +43 -0
- data/.gitignore +10 -0
- data/.licensed.yml +9 -0
- data/.licenses/go/github.com/armon/circbuf.dep.yml +31 -0
- data/.licenses/go/github.com/gorilla/mux.dep.yml +41 -0
- data/.licenses/go/github.com/hashicorp/go-cleanhttp.dep.yml +375 -0
- data/.licenses/go/github.com/mitchellh/go-homedir.dep.yml +32 -0
- data/.licenses/go/github.com/pkg/errors.dep.yml +36 -0
- data/.licenses/go/github.com/planetscale/planetscale-go/planetscale.dep.yml +160 -0
- data/.licenses/go/github.com/planetscale/sql-proxy/proxy.dep.yml +180 -0
- data/.licenses/go/go.uber.org/atomic.dep.yml +31 -0
- data/.licenses/go/go.uber.org/multierr.dep.yml +30 -0
- data/.licenses/go/go.uber.org/zap.dep.yml +30 -0
- data/.licenses/go/go.uber.org/zap/buffer.dep.yml +30 -0
- data/.licenses/go/go.uber.org/zap/internal/bufferpool.dep.yml +30 -0
- data/.licenses/go/go.uber.org/zap/internal/color.dep.yml +30 -0
- data/.licenses/go/go.uber.org/zap/internal/exit.dep.yml +31 -0
- data/.licenses/go/go.uber.org/zap/zapcore.dep.yml +31 -0
- data/.licenses/go/golang.org/x/net/context/ctxhttp.dep.yml +63 -0
- data/.licenses/go/golang.org/x/oauth2.dep.yml +44 -0
- data/.licenses/go/golang.org/x/oauth2/internal.dep.yml +38 -0
- data/.ruby_version +1 -0
- data/Gemfile +8 -0
- data/LICENSE +201 -0
- data/README.md +88 -0
- data/Rakefile +10 -0
- data/bin/console +15 -0
- data/bin/setup +17 -0
- data/controller.go +255 -0
- data/go.mod +16 -0
- data/go.sum +428 -0
- data/lib/generators/planetscale/install_generator.rb +58 -0
- data/lib/planetscale.rb +137 -0
- data/lib/planetscale/version.rb +5 -0
- data/planetscale.gemspec +44 -0
- data/proxy.go +128 -0
- data/vendor/github.com/armon/circbuf/.gitignore +22 -0
- data/vendor/github.com/armon/circbuf/LICENSE +20 -0
- data/vendor/github.com/armon/circbuf/README.md +28 -0
- data/vendor/github.com/armon/circbuf/circbuf.go +92 -0
- data/vendor/github.com/armon/circbuf/go.mod +1 -0
- data/vendor/github.com/golang/protobuf/AUTHORS +3 -0
- data/vendor/github.com/golang/protobuf/CONTRIBUTORS +3 -0
- data/vendor/github.com/golang/protobuf/LICENSE +28 -0
- data/vendor/github.com/golang/protobuf/proto/buffer.go +324 -0
- data/vendor/github.com/golang/protobuf/proto/defaults.go +63 -0
- data/vendor/github.com/golang/protobuf/proto/deprecated.go +113 -0
- data/vendor/github.com/golang/protobuf/proto/discard.go +58 -0
- data/vendor/github.com/golang/protobuf/proto/extensions.go +356 -0
- data/vendor/github.com/golang/protobuf/proto/properties.go +306 -0
- data/vendor/github.com/golang/protobuf/proto/proto.go +167 -0
- data/vendor/github.com/golang/protobuf/proto/registry.go +317 -0
- data/vendor/github.com/golang/protobuf/proto/text_decode.go +801 -0
- data/vendor/github.com/golang/protobuf/proto/text_encode.go +560 -0
- data/vendor/github.com/golang/protobuf/proto/wire.go +78 -0
- data/vendor/github.com/golang/protobuf/proto/wrappers.go +34 -0
- data/vendor/github.com/gorilla/mux/AUTHORS +8 -0
- data/vendor/github.com/gorilla/mux/LICENSE +27 -0
- data/vendor/github.com/gorilla/mux/README.md +805 -0
- data/vendor/github.com/gorilla/mux/doc.go +306 -0
- data/vendor/github.com/gorilla/mux/go.mod +3 -0
- data/vendor/github.com/gorilla/mux/middleware.go +74 -0
- data/vendor/github.com/gorilla/mux/mux.go +606 -0
- data/vendor/github.com/gorilla/mux/regexp.go +388 -0
- data/vendor/github.com/gorilla/mux/route.go +736 -0
- data/vendor/github.com/gorilla/mux/test_helpers.go +19 -0
- data/vendor/github.com/hashicorp/go-cleanhttp/LICENSE +363 -0
- data/vendor/github.com/hashicorp/go-cleanhttp/README.md +30 -0
- data/vendor/github.com/hashicorp/go-cleanhttp/cleanhttp.go +58 -0
- data/vendor/github.com/hashicorp/go-cleanhttp/doc.go +20 -0
- data/vendor/github.com/hashicorp/go-cleanhttp/go.mod +3 -0
- data/vendor/github.com/hashicorp/go-cleanhttp/handlers.go +48 -0
- data/vendor/github.com/mitchellh/go-homedir/LICENSE +21 -0
- data/vendor/github.com/mitchellh/go-homedir/README.md +14 -0
- data/vendor/github.com/mitchellh/go-homedir/go.mod +1 -0
- data/vendor/github.com/mitchellh/go-homedir/homedir.go +167 -0
- data/vendor/github.com/pkg/errors/.gitignore +24 -0
- data/vendor/github.com/pkg/errors/.travis.yml +10 -0
- data/vendor/github.com/pkg/errors/LICENSE +23 -0
- data/vendor/github.com/pkg/errors/Makefile +44 -0
- data/vendor/github.com/pkg/errors/README.md +59 -0
- data/vendor/github.com/pkg/errors/appveyor.yml +32 -0
- data/vendor/github.com/pkg/errors/errors.go +288 -0
- data/vendor/github.com/pkg/errors/go113.go +38 -0
- data/vendor/github.com/pkg/errors/stack.go +177 -0
- data/vendor/github.com/planetscale/planetscale-go/planetscale/backups.go +139 -0
- data/vendor/github.com/planetscale/planetscale-go/planetscale/branches.go +258 -0
- data/vendor/github.com/planetscale/planetscale-go/planetscale/certs.go +142 -0
- data/vendor/github.com/planetscale/planetscale-go/planetscale/client.go +305 -0
- data/vendor/github.com/planetscale/planetscale-go/planetscale/databases.go +131 -0
- data/vendor/github.com/planetscale/planetscale-go/planetscale/deploy_requests.go +368 -0
- data/vendor/github.com/planetscale/planetscale-go/planetscale/organizations.go +78 -0
- data/vendor/github.com/planetscale/planetscale-go/planetscale/service_tokens.go +163 -0
- data/vendor/github.com/planetscale/sql-proxy/proxy/client.go +467 -0
- data/vendor/github.com/planetscale/sql-proxy/proxy/tls_cache.go +73 -0
- data/vendor/go.uber.org/atomic/.codecov.yml +19 -0
- data/vendor/go.uber.org/atomic/.gitignore +12 -0
- data/vendor/go.uber.org/atomic/.travis.yml +27 -0
- data/vendor/go.uber.org/atomic/CHANGELOG.md +76 -0
- data/vendor/go.uber.org/atomic/LICENSE.txt +19 -0
- data/vendor/go.uber.org/atomic/Makefile +78 -0
- data/vendor/go.uber.org/atomic/README.md +63 -0
- data/vendor/go.uber.org/atomic/bool.go +81 -0
- data/vendor/go.uber.org/atomic/bool_ext.go +53 -0
- data/vendor/go.uber.org/atomic/doc.go +23 -0
- data/vendor/go.uber.org/atomic/duration.go +82 -0
- data/vendor/go.uber.org/atomic/duration_ext.go +40 -0
- data/vendor/go.uber.org/atomic/error.go +51 -0
- data/vendor/go.uber.org/atomic/error_ext.go +39 -0
- data/vendor/go.uber.org/atomic/float64.go +76 -0
- data/vendor/go.uber.org/atomic/float64_ext.go +47 -0
- data/vendor/go.uber.org/atomic/gen.go +26 -0
- data/vendor/go.uber.org/atomic/go.mod +8 -0
- data/vendor/go.uber.org/atomic/go.sum +9 -0
- data/vendor/go.uber.org/atomic/int32.go +102 -0
- data/vendor/go.uber.org/atomic/int64.go +102 -0
- data/vendor/go.uber.org/atomic/nocmp.go +35 -0
- data/vendor/go.uber.org/atomic/string.go +54 -0
- data/vendor/go.uber.org/atomic/string_ext.go +43 -0
- data/vendor/go.uber.org/atomic/uint32.go +102 -0
- data/vendor/go.uber.org/atomic/uint64.go +102 -0
- data/vendor/go.uber.org/atomic/value.go +31 -0
- data/vendor/go.uber.org/multierr/.codecov.yml +15 -0
- data/vendor/go.uber.org/multierr/.gitignore +4 -0
- data/vendor/go.uber.org/multierr/.travis.yml +23 -0
- data/vendor/go.uber.org/multierr/CHANGELOG.md +60 -0
- data/vendor/go.uber.org/multierr/LICENSE.txt +19 -0
- data/vendor/go.uber.org/multierr/Makefile +42 -0
- data/vendor/go.uber.org/multierr/README.md +23 -0
- data/vendor/go.uber.org/multierr/error.go +449 -0
- data/vendor/go.uber.org/multierr/glide.yaml +8 -0
- data/vendor/go.uber.org/multierr/go.mod +8 -0
- data/vendor/go.uber.org/multierr/go.sum +11 -0
- data/vendor/go.uber.org/multierr/go113.go +52 -0
- data/vendor/go.uber.org/zap/.codecov.yml +17 -0
- data/vendor/go.uber.org/zap/.gitignore +32 -0
- data/vendor/go.uber.org/zap/.readme.tmpl +109 -0
- data/vendor/go.uber.org/zap/.travis.yml +23 -0
- data/vendor/go.uber.org/zap/CHANGELOG.md +432 -0
- data/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md +75 -0
- data/vendor/go.uber.org/zap/CONTRIBUTING.md +81 -0
- data/vendor/go.uber.org/zap/FAQ.md +156 -0
- data/vendor/go.uber.org/zap/LICENSE.txt +19 -0
- data/vendor/go.uber.org/zap/Makefile +63 -0
- data/vendor/go.uber.org/zap/README.md +134 -0
- data/vendor/go.uber.org/zap/array.go +320 -0
- data/vendor/go.uber.org/zap/buffer/buffer.go +123 -0
- data/vendor/go.uber.org/zap/buffer/pool.go +49 -0
- data/vendor/go.uber.org/zap/checklicense.sh +17 -0
- data/vendor/go.uber.org/zap/config.go +264 -0
- data/vendor/go.uber.org/zap/doc.go +113 -0
- data/vendor/go.uber.org/zap/encoder.go +79 -0
- data/vendor/go.uber.org/zap/error.go +80 -0
- data/vendor/go.uber.org/zap/field.go +539 -0
- data/vendor/go.uber.org/zap/flag.go +39 -0
- data/vendor/go.uber.org/zap/glide.yaml +34 -0
- data/vendor/go.uber.org/zap/global.go +168 -0
- data/vendor/go.uber.org/zap/global_go112.go +26 -0
- data/vendor/go.uber.org/zap/global_prego112.go +26 -0
- data/vendor/go.uber.org/zap/go.mod +13 -0
- data/vendor/go.uber.org/zap/go.sum +56 -0
- data/vendor/go.uber.org/zap/http_handler.go +81 -0
- data/vendor/go.uber.org/zap/internal/bufferpool/bufferpool.go +31 -0
- data/vendor/go.uber.org/zap/internal/color/color.go +44 -0
- data/vendor/go.uber.org/zap/internal/exit/exit.go +64 -0
- data/vendor/go.uber.org/zap/level.go +132 -0
- data/vendor/go.uber.org/zap/logger.go +344 -0
- data/vendor/go.uber.org/zap/options.go +140 -0
- data/vendor/go.uber.org/zap/sink.go +161 -0
- data/vendor/go.uber.org/zap/stacktrace.go +85 -0
- data/vendor/go.uber.org/zap/sugar.go +304 -0
- data/vendor/go.uber.org/zap/time.go +27 -0
- data/vendor/go.uber.org/zap/writer.go +99 -0
- data/vendor/go.uber.org/zap/zapcore/console_encoder.go +161 -0
- data/vendor/go.uber.org/zap/zapcore/core.go +113 -0
- data/vendor/go.uber.org/zap/zapcore/doc.go +24 -0
- data/vendor/go.uber.org/zap/zapcore/encoder.go +443 -0
- data/vendor/go.uber.org/zap/zapcore/entry.go +264 -0
- data/vendor/go.uber.org/zap/zapcore/error.go +115 -0
- data/vendor/go.uber.org/zap/zapcore/field.go +227 -0
- data/vendor/go.uber.org/zap/zapcore/hook.go +68 -0
- data/vendor/go.uber.org/zap/zapcore/increase_level.go +66 -0
- data/vendor/go.uber.org/zap/zapcore/json_encoder.go +534 -0
- data/vendor/go.uber.org/zap/zapcore/level.go +175 -0
- data/vendor/go.uber.org/zap/zapcore/level_strings.go +46 -0
- data/vendor/go.uber.org/zap/zapcore/marshaler.go +61 -0
- data/vendor/go.uber.org/zap/zapcore/memory_encoder.go +179 -0
- data/vendor/go.uber.org/zap/zapcore/sampler.go +208 -0
- data/vendor/go.uber.org/zap/zapcore/tee.go +81 -0
- data/vendor/go.uber.org/zap/zapcore/write_syncer.go +123 -0
- data/vendor/golang.org/x/net/AUTHORS +3 -0
- data/vendor/golang.org/x/net/CONTRIBUTORS +3 -0
- data/vendor/golang.org/x/net/LICENSE +27 -0
- data/vendor/golang.org/x/net/PATENTS +22 -0
- data/vendor/golang.org/x/net/context/context.go +56 -0
- data/vendor/golang.org/x/net/context/ctxhttp/ctxhttp.go +71 -0
- data/vendor/golang.org/x/net/context/go17.go +73 -0
- data/vendor/golang.org/x/net/context/go19.go +21 -0
- data/vendor/golang.org/x/net/context/pre_go17.go +301 -0
- data/vendor/golang.org/x/net/context/pre_go19.go +110 -0
- data/vendor/golang.org/x/oauth2/.travis.yml +13 -0
- data/vendor/golang.org/x/oauth2/AUTHORS +3 -0
- data/vendor/golang.org/x/oauth2/CONTRIBUTING.md +26 -0
- data/vendor/golang.org/x/oauth2/CONTRIBUTORS +3 -0
- data/vendor/golang.org/x/oauth2/LICENSE +27 -0
- data/vendor/golang.org/x/oauth2/README.md +36 -0
- data/vendor/golang.org/x/oauth2/go.mod +9 -0
- data/vendor/golang.org/x/oauth2/go.sum +361 -0
- data/vendor/golang.org/x/oauth2/internal/client_appengine.go +14 -0
- data/vendor/golang.org/x/oauth2/internal/doc.go +6 -0
- data/vendor/golang.org/x/oauth2/internal/oauth2.go +37 -0
- data/vendor/golang.org/x/oauth2/internal/token.go +294 -0
- data/vendor/golang.org/x/oauth2/internal/transport.go +33 -0
- data/vendor/golang.org/x/oauth2/oauth2.go +381 -0
- data/vendor/golang.org/x/oauth2/token.go +178 -0
- data/vendor/golang.org/x/oauth2/transport.go +89 -0
- data/vendor/google.golang.org/appengine/LICENSE +202 -0
- data/vendor/google.golang.org/appengine/internal/api.go +678 -0
- data/vendor/google.golang.org/appengine/internal/api_classic.go +169 -0
- data/vendor/google.golang.org/appengine/internal/api_common.go +123 -0
- data/vendor/google.golang.org/appengine/internal/app_id.go +28 -0
- data/vendor/google.golang.org/appengine/internal/base/api_base.pb.go +308 -0
- data/vendor/google.golang.org/appengine/internal/base/api_base.proto +33 -0
- data/vendor/google.golang.org/appengine/internal/datastore/datastore_v3.pb.go +4367 -0
- data/vendor/google.golang.org/appengine/internal/datastore/datastore_v3.proto +551 -0
- data/vendor/google.golang.org/appengine/internal/identity.go +55 -0
- data/vendor/google.golang.org/appengine/internal/identity_classic.go +61 -0
- data/vendor/google.golang.org/appengine/internal/identity_flex.go +11 -0
- data/vendor/google.golang.org/appengine/internal/identity_vm.go +134 -0
- data/vendor/google.golang.org/appengine/internal/internal.go +110 -0
- data/vendor/google.golang.org/appengine/internal/log/log_service.pb.go +1313 -0
- data/vendor/google.golang.org/appengine/internal/log/log_service.proto +150 -0
- data/vendor/google.golang.org/appengine/internal/main.go +16 -0
- data/vendor/google.golang.org/appengine/internal/main_common.go +7 -0
- data/vendor/google.golang.org/appengine/internal/main_vm.go +69 -0
- data/vendor/google.golang.org/appengine/internal/metadata.go +60 -0
- data/vendor/google.golang.org/appengine/internal/net.go +56 -0
- data/vendor/google.golang.org/appengine/internal/regen.sh +40 -0
- data/vendor/google.golang.org/appengine/internal/remote_api/remote_api.pb.go +361 -0
- data/vendor/google.golang.org/appengine/internal/remote_api/remote_api.proto +44 -0
- data/vendor/google.golang.org/appengine/internal/transaction.go +115 -0
- data/vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.pb.go +527 -0
- data/vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto +64 -0
- data/vendor/google.golang.org/appengine/urlfetch/urlfetch.go +210 -0
- data/vendor/google.golang.org/protobuf/AUTHORS +3 -0
- data/vendor/google.golang.org/protobuf/CONTRIBUTORS +3 -0
- data/vendor/google.golang.org/protobuf/LICENSE +27 -0
- data/vendor/google.golang.org/protobuf/PATENTS +22 -0
- data/vendor/google.golang.org/protobuf/encoding/prototext/decode.go +773 -0
- data/vendor/google.golang.org/protobuf/encoding/prototext/doc.go +7 -0
- data/vendor/google.golang.org/protobuf/encoding/prototext/encode.go +371 -0
- data/vendor/google.golang.org/protobuf/encoding/protowire/wire.go +538 -0
- data/vendor/google.golang.org/protobuf/internal/descfmt/stringer.go +318 -0
- data/vendor/google.golang.org/protobuf/internal/descopts/options.go +29 -0
- data/vendor/google.golang.org/protobuf/internal/detrand/rand.go +69 -0
- data/vendor/google.golang.org/protobuf/internal/encoding/defval/default.go +213 -0
- data/vendor/google.golang.org/protobuf/internal/encoding/messageset/messageset.go +241 -0
- data/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go +207 -0
- data/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go +665 -0
- data/vendor/google.golang.org/protobuf/internal/encoding/text/decode_number.go +190 -0
- data/vendor/google.golang.org/protobuf/internal/encoding/text/decode_string.go +161 -0
- data/vendor/google.golang.org/protobuf/internal/encoding/text/decode_token.go +373 -0
- data/vendor/google.golang.org/protobuf/internal/encoding/text/doc.go +29 -0
- data/vendor/google.golang.org/protobuf/internal/encoding/text/encode.go +265 -0
- data/vendor/google.golang.org/protobuf/internal/errors/errors.go +89 -0
- data/vendor/google.golang.org/protobuf/internal/errors/is_go112.go +39 -0
- data/vendor/google.golang.org/protobuf/internal/errors/is_go113.go +12 -0
- data/vendor/google.golang.org/protobuf/internal/filedesc/build.go +158 -0
- data/vendor/google.golang.org/protobuf/internal/filedesc/desc.go +631 -0
- data/vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go +471 -0
- data/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go +704 -0
- data/vendor/google.golang.org/protobuf/internal/filedesc/desc_list.go +450 -0
- data/vendor/google.golang.org/protobuf/internal/filedesc/desc_list_gen.go +356 -0
- data/vendor/google.golang.org/protobuf/internal/filedesc/placeholder.go +107 -0
- data/vendor/google.golang.org/protobuf/internal/filetype/build.go +297 -0
- data/vendor/google.golang.org/protobuf/internal/flags/flags.go +24 -0
- data/vendor/google.golang.org/protobuf/internal/flags/proto_legacy_disable.go +9 -0
- data/vendor/google.golang.org/protobuf/internal/flags/proto_legacy_enable.go +9 -0
- data/vendor/google.golang.org/protobuf/internal/genid/any_gen.go +34 -0
- data/vendor/google.golang.org/protobuf/internal/genid/api_gen.go +106 -0
- data/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go +829 -0
- data/vendor/google.golang.org/protobuf/internal/genid/doc.go +11 -0
- data/vendor/google.golang.org/protobuf/internal/genid/duration_gen.go +34 -0
- data/vendor/google.golang.org/protobuf/internal/genid/empty_gen.go +19 -0
- data/vendor/google.golang.org/protobuf/internal/genid/field_mask_gen.go +31 -0
- data/vendor/google.golang.org/protobuf/internal/genid/goname.go +25 -0
- data/vendor/google.golang.org/protobuf/internal/genid/map_entry.go +16 -0
- data/vendor/google.golang.org/protobuf/internal/genid/source_context_gen.go +31 -0
- data/vendor/google.golang.org/protobuf/internal/genid/struct_gen.go +116 -0
- data/vendor/google.golang.org/protobuf/internal/genid/timestamp_gen.go +34 -0
- data/vendor/google.golang.org/protobuf/internal/genid/type_gen.go +184 -0
- data/vendor/google.golang.org/protobuf/internal/genid/wrappers.go +13 -0
- data/vendor/google.golang.org/protobuf/internal/genid/wrappers_gen.go +175 -0
- data/vendor/google.golang.org/protobuf/internal/impl/api_export.go +177 -0
- data/vendor/google.golang.org/protobuf/internal/impl/checkinit.go +141 -0
- data/vendor/google.golang.org/protobuf/internal/impl/codec_extension.go +223 -0
- data/vendor/google.golang.org/protobuf/internal/impl/codec_field.go +830 -0
- data/vendor/google.golang.org/protobuf/internal/impl/codec_gen.go +5637 -0
- data/vendor/google.golang.org/protobuf/internal/impl/codec_map.go +388 -0
- data/vendor/google.golang.org/protobuf/internal/impl/codec_map_go111.go +37 -0
- data/vendor/google.golang.org/protobuf/internal/impl/codec_map_go112.go +11 -0
- data/vendor/google.golang.org/protobuf/internal/impl/codec_message.go +217 -0
- data/vendor/google.golang.org/protobuf/internal/impl/codec_messageset.go +123 -0
- data/vendor/google.golang.org/protobuf/internal/impl/codec_reflect.go +209 -0
- data/vendor/google.golang.org/protobuf/internal/impl/codec_tables.go +557 -0
- data/vendor/google.golang.org/protobuf/internal/impl/codec_unsafe.go +17 -0
- data/vendor/google.golang.org/protobuf/internal/impl/convert.go +496 -0
- data/vendor/google.golang.org/protobuf/internal/impl/convert_list.go +141 -0
- data/vendor/google.golang.org/protobuf/internal/impl/convert_map.go +121 -0
- data/vendor/google.golang.org/protobuf/internal/impl/decode.go +276 -0
- data/vendor/google.golang.org/protobuf/internal/impl/encode.go +201 -0
- data/vendor/google.golang.org/protobuf/internal/impl/enum.go +21 -0
- data/vendor/google.golang.org/protobuf/internal/impl/extension.go +156 -0
- data/vendor/google.golang.org/protobuf/internal/impl/legacy_enum.go +219 -0
- data/vendor/google.golang.org/protobuf/internal/impl/legacy_export.go +92 -0
- data/vendor/google.golang.org/protobuf/internal/impl/legacy_extension.go +176 -0
- data/vendor/google.golang.org/protobuf/internal/impl/legacy_file.go +81 -0
- data/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go +558 -0
- data/vendor/google.golang.org/protobuf/internal/impl/merge.go +176 -0
- data/vendor/google.golang.org/protobuf/internal/impl/merge_gen.go +209 -0
- data/vendor/google.golang.org/protobuf/internal/impl/message.go +276 -0
- data/vendor/google.golang.org/protobuf/internal/impl/message_reflect.go +465 -0
- data/vendor/google.golang.org/protobuf/internal/impl/message_reflect_field.go +543 -0
- data/vendor/google.golang.org/protobuf/internal/impl/message_reflect_gen.go +249 -0
- data/vendor/google.golang.org/protobuf/internal/impl/pointer_reflect.go +178 -0
- data/vendor/google.golang.org/protobuf/internal/impl/pointer_unsafe.go +174 -0
- data/vendor/google.golang.org/protobuf/internal/impl/validate.go +576 -0
- data/vendor/google.golang.org/protobuf/internal/impl/weak.go +74 -0
- data/vendor/google.golang.org/protobuf/internal/order/order.go +89 -0
- data/vendor/google.golang.org/protobuf/internal/order/range.go +115 -0
- data/vendor/google.golang.org/protobuf/internal/pragma/pragma.go +29 -0
- data/vendor/google.golang.org/protobuf/internal/set/ints.go +58 -0
- data/vendor/google.golang.org/protobuf/internal/strs/strings.go +196 -0
- data/vendor/google.golang.org/protobuf/internal/strs/strings_pure.go +27 -0
- data/vendor/google.golang.org/protobuf/internal/strs/strings_unsafe.go +94 -0
- data/vendor/google.golang.org/protobuf/internal/version/version.go +79 -0
- data/vendor/google.golang.org/protobuf/proto/checkinit.go +71 -0
- data/vendor/google.golang.org/protobuf/proto/decode.go +278 -0
- data/vendor/google.golang.org/protobuf/proto/decode_gen.go +603 -0
- data/vendor/google.golang.org/protobuf/proto/doc.go +94 -0
- data/vendor/google.golang.org/protobuf/proto/encode.go +319 -0
- data/vendor/google.golang.org/protobuf/proto/encode_gen.go +97 -0
- data/vendor/google.golang.org/protobuf/proto/equal.go +167 -0
- data/vendor/google.golang.org/protobuf/proto/extension.go +92 -0
- data/vendor/google.golang.org/protobuf/proto/merge.go +139 -0
- data/vendor/google.golang.org/protobuf/proto/messageset.go +93 -0
- data/vendor/google.golang.org/protobuf/proto/proto.go +43 -0
- data/vendor/google.golang.org/protobuf/proto/proto_methods.go +19 -0
- data/vendor/google.golang.org/protobuf/proto/proto_reflect.go +19 -0
- data/vendor/google.golang.org/protobuf/proto/reset.go +43 -0
- data/vendor/google.golang.org/protobuf/proto/size.go +97 -0
- data/vendor/google.golang.org/protobuf/proto/size_gen.go +55 -0
- data/vendor/google.golang.org/protobuf/proto/wrappers.go +29 -0
- data/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go +276 -0
- data/vendor/google.golang.org/protobuf/reflect/protodesc/desc_init.go +248 -0
- data/vendor/google.golang.org/protobuf/reflect/protodesc/desc_resolve.go +286 -0
- data/vendor/google.golang.org/protobuf/reflect/protodesc/desc_validate.go +374 -0
- data/vendor/google.golang.org/protobuf/reflect/protodesc/proto.go +252 -0
- data/vendor/google.golang.org/protobuf/reflect/protoreflect/methods.go +77 -0
- data/vendor/google.golang.org/protobuf/reflect/protoreflect/proto.go +504 -0
- data/vendor/google.golang.org/protobuf/reflect/protoreflect/source.go +128 -0
- data/vendor/google.golang.org/protobuf/reflect/protoreflect/source_gen.go +461 -0
- data/vendor/google.golang.org/protobuf/reflect/protoreflect/type.go +665 -0
- data/vendor/google.golang.org/protobuf/reflect/protoreflect/value.go +285 -0
- data/vendor/google.golang.org/protobuf/reflect/protoreflect/value_pure.go +59 -0
- data/vendor/google.golang.org/protobuf/reflect/protoreflect/value_union.go +411 -0
- data/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe.go +98 -0
- data/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go +869 -0
- data/vendor/google.golang.org/protobuf/runtime/protoiface/legacy.go +15 -0
- data/vendor/google.golang.org/protobuf/runtime/protoiface/methods.go +167 -0
- data/vendor/google.golang.org/protobuf/runtime/protoimpl/impl.go +44 -0
- data/vendor/google.golang.org/protobuf/runtime/protoimpl/version.go +56 -0
- data/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go +4039 -0
- data/vendor/modules.txt +79 -0
- metadata +495 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
package planetscale
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"fmt"
|
|
6
|
+
"net/http"
|
|
7
|
+
"time"
|
|
8
|
+
|
|
9
|
+
"github.com/pkg/errors"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
const organizationsAPIPath = "v1/organizations"
|
|
13
|
+
|
|
14
|
+
// GetOrganizationRequest encapsulates the request for getting a single
|
|
15
|
+
// organization.
|
|
16
|
+
type GetOrganizationRequest struct {
|
|
17
|
+
Organization string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// OrganizationsService is an interface for communicating with the PlanetScale
|
|
21
|
+
// Organizations API endpoints.
|
|
22
|
+
type OrganizationsService interface {
|
|
23
|
+
Get(context.Context, *GetOrganizationRequest) (*Organization, error)
|
|
24
|
+
List(context.Context) ([]*Organization, error)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Organization represents a PlanetScale organization.
|
|
28
|
+
type Organization struct {
|
|
29
|
+
Name string `json:"name"`
|
|
30
|
+
CreatedAt time.Time `json:"created_at"`
|
|
31
|
+
UpdatedAt time.Time `json:"updated_at"`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type organizationsResponse struct {
|
|
35
|
+
Organizations []*Organization `json:"data"`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type organizationsService struct {
|
|
39
|
+
client *Client
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
var _ OrganizationsService = &organizationsService{}
|
|
43
|
+
|
|
44
|
+
func NewOrganizationsService(client *Client) *organizationsService {
|
|
45
|
+
return &organizationsService{
|
|
46
|
+
client: client,
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Get fetches a single organization by name.
|
|
51
|
+
func (o *organizationsService) Get(ctx context.Context, getReq *GetOrganizationRequest) (*Organization, error) {
|
|
52
|
+
req, err := o.client.newRequest(http.MethodGet, fmt.Sprintf("%s/%s", organizationsAPIPath, getReq.Organization), nil)
|
|
53
|
+
if err != nil {
|
|
54
|
+
return nil, errors.Wrap(err, "error creating request for get organization")
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
org := &Organization{}
|
|
58
|
+
if err := o.client.do(ctx, req, &org); err != nil {
|
|
59
|
+
return nil, err
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return org, nil
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// List returns all the organizations for a user.
|
|
66
|
+
func (o *organizationsService) List(ctx context.Context) ([]*Organization, error) {
|
|
67
|
+
req, err := o.client.newRequest(http.MethodGet, organizationsAPIPath, nil)
|
|
68
|
+
if err != nil {
|
|
69
|
+
return nil, errors.Wrap(err, "error creating request for list organization")
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
orgResponse := &organizationsResponse{}
|
|
73
|
+
if err := o.client.do(ctx, req, &orgResponse); err != nil {
|
|
74
|
+
return nil, err
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return orgResponse.Organizations, nil
|
|
78
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
package planetscale
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"fmt"
|
|
6
|
+
"net/http"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
var _ ServiceTokenService = &serviceTokenService{}
|
|
10
|
+
|
|
11
|
+
// ServiceTokenService is an interface for communicating with the PlanetScale
|
|
12
|
+
// Service Token API.
|
|
13
|
+
type ServiceTokenService interface {
|
|
14
|
+
Create(context.Context, *CreateServiceTokenRequest) (*ServiceToken, error)
|
|
15
|
+
List(context.Context, *ListServiceTokensRequest) ([]*ServiceToken, error)
|
|
16
|
+
Delete(context.Context, *DeleteServiceTokenRequest) error
|
|
17
|
+
GetAccess(context.Context, *GetServiceTokenAccessRequest) ([]*ServiceTokenAccess, error)
|
|
18
|
+
AddAccess(context.Context, *AddServiceTokenAccessRequest) ([]*ServiceTokenAccess, error)
|
|
19
|
+
DeleteAccess(context.Context, *DeleteServiceTokenAccessRequest) error
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type serviceTokenService struct {
|
|
23
|
+
client *Client
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
func (s *serviceTokenService) Create(ctx context.Context, createReq *CreateServiceTokenRequest) (*ServiceToken, error) {
|
|
27
|
+
req, err := s.client.newRequest(http.MethodPost, serviceTokensAPIPath(createReq.Organization), nil)
|
|
28
|
+
if err != nil {
|
|
29
|
+
return nil, err
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
st := &ServiceToken{}
|
|
33
|
+
if err := s.client.do(ctx, req, &st); err != nil {
|
|
34
|
+
return nil, err
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return st, nil
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
func (s *serviceTokenService) List(ctx context.Context, listReq *ListServiceTokensRequest) ([]*ServiceToken, error) {
|
|
41
|
+
req, err := s.client.newRequest(http.MethodGet, serviceTokensAPIPath(listReq.Organization), nil)
|
|
42
|
+
if err != nil {
|
|
43
|
+
return nil, err
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
tokenListResponse := serviceTokensResponse{}
|
|
47
|
+
if err := s.client.do(ctx, req, &tokenListResponse); err != nil {
|
|
48
|
+
return nil, err
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return tokenListResponse.ServiceTokens, nil
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
func (s *serviceTokenService) Delete(ctx context.Context, delReq *DeleteServiceTokenRequest) error {
|
|
55
|
+
req, err := s.client.newRequest(http.MethodDelete, serviceTokenAPIPath(delReq.Organization, delReq.ID), nil)
|
|
56
|
+
if err != nil {
|
|
57
|
+
return err
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
err = s.client.do(ctx, req, nil)
|
|
61
|
+
return err
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
func (s *serviceTokenService) GetAccess(ctx context.Context, accessReq *GetServiceTokenAccessRequest) ([]*ServiceTokenAccess, error) {
|
|
65
|
+
req, err := s.client.newRequest(http.MethodGet, serviceTokenAccessAPIPath(accessReq.Organization, accessReq.ID), nil)
|
|
66
|
+
if err != nil {
|
|
67
|
+
return nil, err
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
tokenAccess := serviceTokenAccessResponse{}
|
|
71
|
+
if err := s.client.do(ctx, req, &tokenAccess); err != nil {
|
|
72
|
+
return nil, err
|
|
73
|
+
}
|
|
74
|
+
return tokenAccess.ServiceTokenAccesses, nil
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
func (s *serviceTokenService) AddAccess(ctx context.Context, addReq *AddServiceTokenAccessRequest) ([]*ServiceTokenAccess, error) {
|
|
78
|
+
req, err := s.client.newRequest(http.MethodPost, serviceTokenAccessAPIPath(addReq.Organization, addReq.ID), addReq)
|
|
79
|
+
if err != nil {
|
|
80
|
+
return nil, err
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
tokenAccess := serviceTokenAccessResponse{}
|
|
84
|
+
if err := s.client.do(ctx, req, &tokenAccess); err != nil {
|
|
85
|
+
return nil, err
|
|
86
|
+
}
|
|
87
|
+
return tokenAccess.ServiceTokenAccesses, nil
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
func (s *serviceTokenService) DeleteAccess(ctx context.Context, delReq *DeleteServiceTokenAccessRequest) error {
|
|
91
|
+
req, err := s.client.newRequest(http.MethodDelete, serviceTokenAccessAPIPath(delReq.Organization, delReq.ID), delReq)
|
|
92
|
+
if err != nil {
|
|
93
|
+
return err
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
err = s.client.do(ctx, req, nil)
|
|
97
|
+
return err
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
type CreateServiceTokenRequest struct {
|
|
101
|
+
Organization string `json:"-"`
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
type DeleteServiceTokenRequest struct {
|
|
105
|
+
Organization string `json:"-"`
|
|
106
|
+
ID string `json:"-"`
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
type ListServiceTokensRequest struct {
|
|
110
|
+
Organization string `json:"-"`
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
type GetServiceTokenAccessRequest struct {
|
|
114
|
+
Organization string `json:"-"`
|
|
115
|
+
ID string `json:"-"`
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
type AddServiceTokenAccessRequest struct {
|
|
119
|
+
Organization string `json:"-"`
|
|
120
|
+
ID string `json:"-"`
|
|
121
|
+
Database string `json:"database"`
|
|
122
|
+
Accesses []string `json:"access"`
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
type DeleteServiceTokenAccessRequest struct {
|
|
126
|
+
Organization string `json:"-"`
|
|
127
|
+
ID string `json:"-"`
|
|
128
|
+
Database string `json:"database"`
|
|
129
|
+
Accesses []string `json:"access"`
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
type ServiceToken struct {
|
|
133
|
+
ID string `json:"id"`
|
|
134
|
+
Type string `json:"type"`
|
|
135
|
+
Token string `json:"token"`
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
type serviceTokensResponse struct {
|
|
139
|
+
ServiceTokens []*ServiceToken `json:"data"`
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
type ServiceTokenAccess struct {
|
|
143
|
+
ID int `json:"id"`
|
|
144
|
+
Access string `json:"access"`
|
|
145
|
+
Type string `json:"type"`
|
|
146
|
+
Resource Database `json:"resource"`
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
type serviceTokenAccessResponse struct {
|
|
150
|
+
ServiceTokenAccesses []*ServiceTokenAccess `json:"data"`
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
func serviceTokenAccessAPIPath(org, id string) string {
|
|
154
|
+
return fmt.Sprintf("%s/%s/access", serviceTokensAPIPath(org), id)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
func serviceTokensAPIPath(org string) string {
|
|
158
|
+
return fmt.Sprintf("v1/organizations/%s/service-tokens", org)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
func serviceTokenAPIPath(org, id string) string {
|
|
162
|
+
return fmt.Sprintf("%s/%s", serviceTokensAPIPath(org), id)
|
|
163
|
+
}
|
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
package proxy
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"crypto/tls"
|
|
6
|
+
"crypto/x509"
|
|
7
|
+
"errors"
|
|
8
|
+
"fmt"
|
|
9
|
+
"io"
|
|
10
|
+
"net"
|
|
11
|
+
"os"
|
|
12
|
+
"strings"
|
|
13
|
+
"sync/atomic"
|
|
14
|
+
"time"
|
|
15
|
+
|
|
16
|
+
"go.uber.org/zap"
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
const (
|
|
20
|
+
keepAlivePeriod = time.Minute
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
// Cert represents the client certificate key pair in the root certiciate
|
|
24
|
+
// authority that the client uses to verify server certificates.
|
|
25
|
+
|
|
26
|
+
type Cert struct {
|
|
27
|
+
ClientCert tls.Certificate
|
|
28
|
+
CACert *x509.Certificate
|
|
29
|
+
RemoteAddr string
|
|
30
|
+
Ports RemotePorts
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type RemotePorts struct {
|
|
34
|
+
Proxy int
|
|
35
|
+
MySQL int
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// CertSource is used
|
|
39
|
+
type CertSource interface {
|
|
40
|
+
// Cert returns the required certs needed to establish a TLS connection
|
|
41
|
+
// from the client to the server.
|
|
42
|
+
Cert(ctx context.Context, org, db, branch string) (*Cert, error)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Client is responsible for listening to unsecured connections over a TCP
|
|
46
|
+
// localhost port and tunneling them securely over a TLS connection to a remote
|
|
47
|
+
// database instance defined by its PlanetScale unique branch identifier.
|
|
48
|
+
type Client struct {
|
|
49
|
+
// connectionsCounter is used to enforce the optional maxConnections limit
|
|
50
|
+
// NOTE: don't move this field, as we need to make sure the fields are
|
|
51
|
+
// 64-bit aligned
|
|
52
|
+
connectionsCounter uint64
|
|
53
|
+
|
|
54
|
+
remoteAddr string
|
|
55
|
+
localAddr string
|
|
56
|
+
instance string
|
|
57
|
+
maxConnections uint64
|
|
58
|
+
certSource CertSource
|
|
59
|
+
|
|
60
|
+
log *zap.Logger
|
|
61
|
+
|
|
62
|
+
// configCache contains the TLS certificate chache for each indiviual
|
|
63
|
+
// database
|
|
64
|
+
configCache *tlsCache
|
|
65
|
+
|
|
66
|
+
listener net.Listener
|
|
67
|
+
// done is closed after a successfull net.Listen bind.
|
|
68
|
+
done chan struct{}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Options are the options for creating a new Client.
|
|
72
|
+
type Options struct {
|
|
73
|
+
// RemoteAddr defines the server address to tunnel local connections. By
|
|
74
|
+
// default we connect to the remote address given by the CertSource. This
|
|
75
|
+
// option can be used to over write it.
|
|
76
|
+
RemoteAddr string
|
|
77
|
+
|
|
78
|
+
// LocalAddr defines the address to listen for new connection
|
|
79
|
+
LocalAddr string
|
|
80
|
+
|
|
81
|
+
// Instance defines the remote DB instance to proxy new connection
|
|
82
|
+
Instance string
|
|
83
|
+
|
|
84
|
+
// MaxConnections is the maximum number of connections to establish
|
|
85
|
+
// before refusing new connections. 0 means no limit.
|
|
86
|
+
MaxConnections uint64
|
|
87
|
+
|
|
88
|
+
// CertSource defines the certificate source to obtain the required TLS
|
|
89
|
+
// certificates for the client and the remote address of the server to
|
|
90
|
+
// connect.
|
|
91
|
+
CertSource CertSource
|
|
92
|
+
|
|
93
|
+
// Logger defines which zap.Logger to use. Use it to override the default
|
|
94
|
+
// Development logger . Useful for tests.
|
|
95
|
+
Logger *zap.Logger
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// NewClient creates a new proxy client instance
|
|
99
|
+
func NewClient(opts Options) (*Client, error) {
|
|
100
|
+
c := &Client{
|
|
101
|
+
certSource: opts.CertSource,
|
|
102
|
+
localAddr: opts.LocalAddr,
|
|
103
|
+
remoteAddr: opts.RemoteAddr,
|
|
104
|
+
instance: opts.Instance,
|
|
105
|
+
configCache: newtlsCache(),
|
|
106
|
+
done: make(chan struct{}),
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if opts.Logger != nil {
|
|
110
|
+
c.log = opts.Logger
|
|
111
|
+
} else {
|
|
112
|
+
logger, err := zap.NewDevelopment(
|
|
113
|
+
zap.Fields(zap.String("app", "sql-proxy-client")),
|
|
114
|
+
)
|
|
115
|
+
if err != nil {
|
|
116
|
+
return nil, err
|
|
117
|
+
}
|
|
118
|
+
zap.ReplaceGlobals(logger)
|
|
119
|
+
c.log = logger
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// cache the certs for the given instance(s)
|
|
123
|
+
_, _, err := c.clientCerts(context.Background(), opts.Instance)
|
|
124
|
+
if err != nil {
|
|
125
|
+
c.log.Error("couldn't retrieve TLS certificate for the client", zap.Error(err))
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return c, nil
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Conn represents a connection from a client to a specific instance.
|
|
132
|
+
type Conn struct {
|
|
133
|
+
Instance string
|
|
134
|
+
Conn net.Conn
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Run runs the proxy. It listens to the configured localhost address and
|
|
138
|
+
// proxies the connection over a TLS tunnel to the remote DB instance.
|
|
139
|
+
func (c *Client) Run(ctx context.Context) error {
|
|
140
|
+
c.log.Info("ready for new connections")
|
|
141
|
+
l, err := c.getListener()
|
|
142
|
+
if err != nil {
|
|
143
|
+
return fmt.Errorf("error net.Listen: %w", err)
|
|
144
|
+
}
|
|
145
|
+
defer c.log.Sync() // nolint: errcheck
|
|
146
|
+
|
|
147
|
+
c.listener = l
|
|
148
|
+
close(c.done)
|
|
149
|
+
|
|
150
|
+
return c.run(ctx, l)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// LocalAddr returns the address of the local listener. This is by default
|
|
154
|
+
// blocking and will only return if the proxy is invoked with the Run() method.
|
|
155
|
+
func (c *Client) LocalAddr() (net.Addr, error) {
|
|
156
|
+
<-c.done
|
|
157
|
+
|
|
158
|
+
if c.listener == nil {
|
|
159
|
+
return nil, errors.New("listener is not set")
|
|
160
|
+
|
|
161
|
+
}
|
|
162
|
+
return c.listener.Addr(), nil
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
func (c *Client) getListener() (net.Listener, error) {
|
|
166
|
+
if strings.HasPrefix(c.localAddr, "unix://") {
|
|
167
|
+
p := strings.TrimPrefix(c.localAddr, "unix://")
|
|
168
|
+
if err := os.Remove(p); err != nil && !os.IsNotExist(err) {
|
|
169
|
+
return nil, fmt.Errorf("failed to remove unix domain socket file %s, error: %s", p, err)
|
|
170
|
+
}
|
|
171
|
+
return net.Listen("unix", p)
|
|
172
|
+
}
|
|
173
|
+
return net.Listen("tcp", c.localAddr)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// run is an internal function for testing the Client proxy event loop for
|
|
177
|
+
// handling TCP connections
|
|
178
|
+
func (c *Client) run(ctx context.Context, l net.Listener) error {
|
|
179
|
+
connSrc := make(chan Conn, 1)
|
|
180
|
+
go func() {
|
|
181
|
+
if err := c.listen(l, connSrc); err != nil {
|
|
182
|
+
c.log.Error("listen to local address", zap.Error(err))
|
|
183
|
+
}
|
|
184
|
+
}()
|
|
185
|
+
|
|
186
|
+
for {
|
|
187
|
+
select {
|
|
188
|
+
case <-ctx.Done():
|
|
189
|
+
termTimeout := time.Second * 1
|
|
190
|
+
c.log.Info("received context cancellation, waiting until timeout",
|
|
191
|
+
zap.Duration("timeout", termTimeout))
|
|
192
|
+
|
|
193
|
+
err := c.Shutdown(termTimeout)
|
|
194
|
+
if err != nil {
|
|
195
|
+
return fmt.Errorf("error during shutdown: %v", err)
|
|
196
|
+
}
|
|
197
|
+
return nil
|
|
198
|
+
case conn := <-connSrc:
|
|
199
|
+
go func(lc Conn) {
|
|
200
|
+
// TODO(fatih): detach context from parent
|
|
201
|
+
err := c.handleConn(ctx, lc.Conn, lc.Instance)
|
|
202
|
+
if err != nil {
|
|
203
|
+
c.log.Error("error proxying conns", zap.Error(err))
|
|
204
|
+
}
|
|
205
|
+
}(conn)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// listen listens to the client's localAddres and sends each incoming
|
|
211
|
+
// connections to the given connSrc channel.
|
|
212
|
+
func (c *Client) listen(l net.Listener, connSrc chan<- Conn) error {
|
|
213
|
+
c.log.Info("listening remote DB instance",
|
|
214
|
+
zap.String("local_addr", c.localAddr),
|
|
215
|
+
zap.String("instance", c.instance),
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
for {
|
|
219
|
+
start := time.Now()
|
|
220
|
+
conn, err := l.Accept()
|
|
221
|
+
if err != nil {
|
|
222
|
+
if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
|
|
223
|
+
d := 10*time.Millisecond - time.Since(start)
|
|
224
|
+
if d > 0 {
|
|
225
|
+
time.Sleep(d)
|
|
226
|
+
}
|
|
227
|
+
continue
|
|
228
|
+
}
|
|
229
|
+
l.Close()
|
|
230
|
+
|
|
231
|
+
return fmt.Errorf("error in accept for on %v: %w", c.localAddr, err)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
c.log.Info("new connection", zap.String("conn_addr", l.Addr().String()))
|
|
235
|
+
|
|
236
|
+
switch clientConn := conn.(type) {
|
|
237
|
+
case *net.TCPConn:
|
|
238
|
+
clientConn.SetKeepAlive(true) //nolint: errcheck
|
|
239
|
+
clientConn.SetKeepAlivePeriod(1 * time.Minute) //nolint: errcheck
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
connSrc <- Conn{
|
|
243
|
+
Conn: conn,
|
|
244
|
+
Instance: c.instance,
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
func (c *Client) handleConn(ctx context.Context, conn net.Conn, instance string) error {
|
|
250
|
+
log := c.log.With(zap.String("instance", instance))
|
|
251
|
+
active := atomic.AddUint64(&c.connectionsCounter, 1)
|
|
252
|
+
|
|
253
|
+
// Deferred decrement of ConnectionsCounter upon connection closing
|
|
254
|
+
defer atomic.AddUint64(&c.connectionsCounter, ^uint64(0))
|
|
255
|
+
|
|
256
|
+
if c.maxConnections > 0 && active > c.maxConnections {
|
|
257
|
+
conn.Close()
|
|
258
|
+
return fmt.Errorf("too many open connections (max %d)", c.maxConnections)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
cfg, remoteAddr, err := c.clientCerts(ctx, instance)
|
|
262
|
+
if err != nil {
|
|
263
|
+
return fmt.Errorf("couldn't retrieve certs for instance: %q: %w", instance, err)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// TODO(fatih): implement refreshing certs
|
|
267
|
+
// go p.refreshCertAfter(instance, timeToRefresh)
|
|
268
|
+
|
|
269
|
+
// overwrite the remote address if the user explicitly set it
|
|
270
|
+
if c.remoteAddr != "" {
|
|
271
|
+
remoteAddr = c.remoteAddr
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
c.log.Info("connecting to remote server", zap.String("remote_addr", remoteAddr))
|
|
275
|
+
|
|
276
|
+
var d net.Dialer
|
|
277
|
+
remoteConn, err := d.DialContext(ctx, "tcp", remoteAddr)
|
|
278
|
+
if err != nil {
|
|
279
|
+
conn.Close()
|
|
280
|
+
return fmt.Errorf("couldn't connect to %q: %v", remoteAddr, err)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
type setKeepAliver interface {
|
|
284
|
+
SetKeepAlive(keepalive bool) error
|
|
285
|
+
SetKeepAlivePeriod(d time.Duration) error
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if s, ok := conn.(setKeepAliver); ok {
|
|
289
|
+
if err := s.SetKeepAlive(true); err != nil {
|
|
290
|
+
log.Error("couldn't set KeepAlive to true", zap.Error(err))
|
|
291
|
+
} else if err := s.SetKeepAlivePeriod(keepAlivePeriod); err != nil {
|
|
292
|
+
log.Error("couldn't set KeepAlivePeriod", zap.Error(err), zap.Duration("keep_alive_period", keepAlivePeriod))
|
|
293
|
+
}
|
|
294
|
+
} else {
|
|
295
|
+
log.Warn("KeepAlive not supported: long-running tcp connections may be killed by the OS.")
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
secureConn := tls.Client(remoteConn, cfg)
|
|
299
|
+
if err := secureConn.Handshake(); err != nil {
|
|
300
|
+
secureConn.Close()
|
|
301
|
+
return fmt.Errorf("couldn't initiate TLS handshake to remote addr: %s", err)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Hasta la vista, baby
|
|
305
|
+
copyThenClose(
|
|
306
|
+
secureConn,
|
|
307
|
+
conn,
|
|
308
|
+
"remote connection",
|
|
309
|
+
"local connection on "+conn.LocalAddr().String(),
|
|
310
|
+
)
|
|
311
|
+
return nil
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// clientCerts returns the TLS configuration needed for the TLS handshake and
|
|
315
|
+
// connection
|
|
316
|
+
func (c *Client) clientCerts(ctx context.Context, instance string) (*tls.Config, string, error) {
|
|
317
|
+
cacheEntry, err := c.configCache.Get(instance)
|
|
318
|
+
if err == nil {
|
|
319
|
+
c.log.Info("using tls.Config from the cache", zap.String("instance", instance))
|
|
320
|
+
return cacheEntry.cfg, cacheEntry.remoteAddr, nil
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if err != errConfigNotFound {
|
|
324
|
+
return nil, "", err // we don't handle non errConfigNotFound errors
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
s := strings.Split(instance, "/")
|
|
328
|
+
if len(s) != 3 {
|
|
329
|
+
return nil, "", fmt.Errorf("instance format is malformed, should be in form organization/dbname/branch, have: %q", instance)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
cert, err := c.certSource.Cert(ctx, s[0], s[1], s[2])
|
|
333
|
+
if err != nil {
|
|
334
|
+
return nil, "", fmt.Errorf("couldn't retrieve certs from cert source: %s", err)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
rootCertPool := x509.NewCertPool()
|
|
338
|
+
rootCertPool.AddCert(cert.CACert)
|
|
339
|
+
|
|
340
|
+
serverName := fmt.Sprintf("%s.%s.%s.%s", s[2], s[1], s[0], cert.RemoteAddr)
|
|
341
|
+
fullAddr := fmt.Sprintf("%s:%d", serverName, cert.Ports.Proxy)
|
|
342
|
+
|
|
343
|
+
cfg := &tls.Config{
|
|
344
|
+
ServerName: serverName,
|
|
345
|
+
Certificates: []tls.Certificate{cert.ClientCert},
|
|
346
|
+
RootCAs: rootCertPool,
|
|
347
|
+
// Set InsecureSkipVerify to skip the default validation we are
|
|
348
|
+
// replacing. This will not disable VerifyConnection.
|
|
349
|
+
InsecureSkipVerify: true,
|
|
350
|
+
VerifyConnection: func(cs tls.ConnectionState) error {
|
|
351
|
+
// For now, only verify the server's certificate chain.
|
|
352
|
+
// We don't know yet what the server's FQDN will be.
|
|
353
|
+
//
|
|
354
|
+
// serverName := cs.ServerName
|
|
355
|
+
// commonName := cs.PeerCertificates[0].Subject.CommonName
|
|
356
|
+
// if commonName != serverName {
|
|
357
|
+
// return fmt.Errorf("invalid certificate name %q, expected %q", commonName, serverName)
|
|
358
|
+
// }
|
|
359
|
+
opts := x509.VerifyOptions{
|
|
360
|
+
Roots: rootCertPool,
|
|
361
|
+
Intermediates: x509.NewCertPool(),
|
|
362
|
+
}
|
|
363
|
+
for _, cert := range cs.PeerCertificates[1:] {
|
|
364
|
+
opts.Intermediates.AddCert(cert)
|
|
365
|
+
}
|
|
366
|
+
_, err := cs.PeerCertificates[0].Verify(opts)
|
|
367
|
+
return err
|
|
368
|
+
},
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
c.log.Info("adding tls.Config to the cache", zap.String("instance", instance))
|
|
372
|
+
c.configCache.Add(instance, cfg, fullAddr)
|
|
373
|
+
return cfg, fullAddr, nil
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Shutdown waits up to a given amount of time for all active connections to
|
|
377
|
+
// close. Returns an error if there are still active connections after waiting
|
|
378
|
+
// for the whole length of the timeout.
|
|
379
|
+
func (c *Client) Shutdown(timeout time.Duration) error {
|
|
380
|
+
term, ticker := time.After(timeout), time.NewTicker(100*time.Millisecond)
|
|
381
|
+
defer ticker.Stop()
|
|
382
|
+
|
|
383
|
+
for {
|
|
384
|
+
select {
|
|
385
|
+
case <-ticker.C:
|
|
386
|
+
if atomic.LoadUint64(&c.connectionsCounter) > 0 {
|
|
387
|
+
continue
|
|
388
|
+
}
|
|
389
|
+
c.log.Info("no connections to wait, bailing out")
|
|
390
|
+
case <-term:
|
|
391
|
+
}
|
|
392
|
+
break
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
active := atomic.LoadUint64(&c.connectionsCounter)
|
|
396
|
+
if active == 0 {
|
|
397
|
+
return nil
|
|
398
|
+
}
|
|
399
|
+
return fmt.Errorf("%d active connections still exist after waiting for %v", active, timeout)
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
func copyThenClose(remote, local io.ReadWriteCloser, remoteDesc, localDesc string) {
|
|
403
|
+
firstErr := make(chan error, 1)
|
|
404
|
+
|
|
405
|
+
go func() {
|
|
406
|
+
readErr, err := myCopy(remote, local)
|
|
407
|
+
select {
|
|
408
|
+
case firstErr <- err:
|
|
409
|
+
if readErr && err == io.EOF {
|
|
410
|
+
zap.L().Info("client closed connection",
|
|
411
|
+
zap.String("local_desc", localDesc))
|
|
412
|
+
} else {
|
|
413
|
+
logError(localDesc, remoteDesc, readErr, err)
|
|
414
|
+
}
|
|
415
|
+
remote.Close()
|
|
416
|
+
local.Close()
|
|
417
|
+
default:
|
|
418
|
+
}
|
|
419
|
+
}()
|
|
420
|
+
|
|
421
|
+
readErr, err := myCopy(local, remote)
|
|
422
|
+
select {
|
|
423
|
+
case firstErr <- err:
|
|
424
|
+
if readErr && err == io.EOF {
|
|
425
|
+
zap.L().Info("instance closed connection",
|
|
426
|
+
zap.String("remote_desc", remoteDesc))
|
|
427
|
+
} else {
|
|
428
|
+
logError(remoteDesc, localDesc, readErr, err)
|
|
429
|
+
}
|
|
430
|
+
remote.Close()
|
|
431
|
+
local.Close()
|
|
432
|
+
default:
|
|
433
|
+
// In this case, the other goroutine exited first and already printed its
|
|
434
|
+
// error (and closed the things).
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
func logError(readDesc, writeDesc string, readErr bool, err error) {
|
|
439
|
+
var desc string
|
|
440
|
+
if readErr {
|
|
441
|
+
desc = "reading data from " + readDesc
|
|
442
|
+
} else {
|
|
443
|
+
desc = "writing data to " + writeDesc
|
|
444
|
+
}
|
|
445
|
+
zap.L().Error("copy error", zap.String("desc", desc), zap.Error(err))
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// myCopy is similar to io.Copy, but reports whether the returned error was due
|
|
449
|
+
// to a bad read or write. The returned error will never be nil
|
|
450
|
+
func myCopy(dst io.Writer, src io.Reader) (readErr bool, err error) {
|
|
451
|
+
buf := make([]byte, 4096)
|
|
452
|
+
for {
|
|
453
|
+
n, err := src.Read(buf)
|
|
454
|
+
if n > 0 {
|
|
455
|
+
if _, werr := dst.Write(buf[:n]); werr != nil {
|
|
456
|
+
if err == nil {
|
|
457
|
+
return false, werr
|
|
458
|
+
}
|
|
459
|
+
// Read and write error; just report read error (it happened first).
|
|
460
|
+
return true, err
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if err != nil {
|
|
464
|
+
return true, err
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|