From 1cd0c0599b35c804785fb2f3a27f1e27ab54264e Mon Sep 17 00:00:00 2001 From: Michael Golowka <72365+pcman312@users.noreply.github.com> Date: Fri, 18 Sep 2020 15:10:54 -0600 Subject: [PATCH] [DBPW 4/X] Update DB engine to support v4 and v5 interfaces with password policies (#9878) --- builtin/logical/database/backend.go | 57 +- builtin/logical/database/backend_test.go | 6 +- builtin/logical/database/mocks_test.go | 101 + builtin/logical/database/mockv4.go | 118 + builtin/logical/database/mockv5.go | 85 + .../database/path_config_connection.go | 74 +- builtin/logical/database/path_creds_create.go | 47 +- .../database/path_rotate_credentials.go | 66 +- builtin/logical/database/rollback.go | 35 +- builtin/logical/database/rollback_test.go | 35 +- builtin/logical/database/rotation.go | 33 +- builtin/logical/database/secret_creds.go | 73 +- builtin/logical/database/version_wrapper.go | 268 ++ .../logical/database/version_wrapper_test.go | 994 ++++++ .../logical/database/versioning_large_test.go | 317 ++ go.mod | 6 +- go.sum | 71 +- vault/plugin_catalog.go | 90 +- .../sdk/database/newdbplugin/database.go | 178 + .../sdk/database/newdbplugin/grpc_client.go | 254 ++ .../newdbplugin/grpc_database_plugin.go | 42 + .../sdk/database/newdbplugin/grpc_server.go | 190 ++ .../sdk/database/newdbplugin/marshalling.go | 36 + .../sdk/database/newdbplugin/middleware.go | 274 ++ .../sdk/database/newdbplugin/plugin_client.go | 80 + .../database/newdbplugin/plugin_factory.go | 78 + .../sdk/database/newdbplugin/plugin_server.go | 42 + .../database/newdbplugin/proto/database.pb.go | 1409 ++++++++ .../database/newdbplugin/proto/database.proto | 95 + .../github.com/stretchr/objx/.codeclimate.yml | 21 + vendor/github.com/stretchr/objx/.gitignore | 11 + vendor/github.com/stretchr/objx/.travis.yml | 30 + vendor/github.com/stretchr/objx/LICENSE | 22 + vendor/github.com/stretchr/objx/README.md | 80 + vendor/github.com/stretchr/objx/Taskfile.yml | 30 + vendor/github.com/stretchr/objx/accessors.go | 119 + .../github.com/stretchr/objx/conversions.go | 280 ++ vendor/github.com/stretchr/objx/doc.go | 66 + vendor/github.com/stretchr/objx/go.mod | 8 + vendor/github.com/stretchr/objx/go.sum | 8 + vendor/github.com/stretchr/objx/map.go | 228 ++ vendor/github.com/stretchr/objx/mutations.go | 77 + vendor/github.com/stretchr/objx/security.go | 12 + vendor/github.com/stretchr/objx/tests.go | 17 + .../github.com/stretchr/objx/type_specific.go | 346 ++ .../stretchr/objx/type_specific_codegen.go | 2251 ++++++++++++ vendor/github.com/stretchr/objx/value.go | 159 + vendor/github.com/stretchr/testify/LICENSE | 2 +- ...ssertion_order.go => assertion_compare.go} | 173 +- .../testify/assert/assertion_format.go | 40 +- .../testify/assert/assertion_forward.go | 62 +- .../stretchr/testify/assert/assertions.go | 193 +- .../testify/assert/http_assertions.go | 25 +- .../github.com/stretchr/testify/mock/doc.go | 44 + .../github.com/stretchr/testify/mock/mock.go | 981 ++++++ .../stretchr/testify/require/require.go | 74 +- .../testify/require/require_forward.go | 62 +- vendor/gopkg.in/yaml.v3/.travis.yml | 16 + vendor/gopkg.in/yaml.v3/LICENSE | 50 + vendor/gopkg.in/yaml.v3/NOTICE | 13 + vendor/gopkg.in/yaml.v3/README.md | 150 + vendor/gopkg.in/yaml.v3/apic.go | 746 ++++ vendor/gopkg.in/yaml.v3/decode.go | 931 +++++ vendor/gopkg.in/yaml.v3/emitterc.go | 1992 +++++++++++ vendor/gopkg.in/yaml.v3/encode.go | 561 +++ vendor/gopkg.in/yaml.v3/go.mod | 5 + vendor/gopkg.in/yaml.v3/parserc.go | 1229 +++++++ vendor/gopkg.in/yaml.v3/readerc.go | 434 +++ vendor/gopkg.in/yaml.v3/resolve.go | 326 ++ vendor/gopkg.in/yaml.v3/scannerc.go | 3025 +++++++++++++++++ vendor/gopkg.in/yaml.v3/sorter.go | 134 + vendor/gopkg.in/yaml.v3/writerc.go | 48 + vendor/gopkg.in/yaml.v3/yaml.go | 662 ++++ vendor/gopkg.in/yaml.v3/yamlh.go | 805 +++++ vendor/gopkg.in/yaml.v3/yamlprivateh.go | 198 ++ vendor/modules.txt | 9 +- 76 files changed, 21485 insertions(+), 424 deletions(-) create mode 100644 builtin/logical/database/mocks_test.go create mode 100644 builtin/logical/database/mockv4.go create mode 100644 builtin/logical/database/mockv5.go create mode 100644 builtin/logical/database/version_wrapper.go create mode 100644 builtin/logical/database/version_wrapper_test.go create mode 100644 builtin/logical/database/versioning_large_test.go create mode 100644 vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/database.go create mode 100644 vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/grpc_client.go create mode 100644 vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/grpc_database_plugin.go create mode 100644 vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/grpc_server.go create mode 100644 vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/marshalling.go create mode 100644 vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/middleware.go create mode 100644 vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/plugin_client.go create mode 100644 vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/plugin_factory.go create mode 100644 vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/plugin_server.go create mode 100644 vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/proto/database.pb.go create mode 100644 vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/proto/database.proto create mode 100644 vendor/github.com/stretchr/objx/.codeclimate.yml create mode 100644 vendor/github.com/stretchr/objx/.gitignore create mode 100644 vendor/github.com/stretchr/objx/.travis.yml create mode 100644 vendor/github.com/stretchr/objx/LICENSE create mode 100644 vendor/github.com/stretchr/objx/README.md create mode 100644 vendor/github.com/stretchr/objx/Taskfile.yml create mode 100644 vendor/github.com/stretchr/objx/accessors.go create mode 100644 vendor/github.com/stretchr/objx/conversions.go create mode 100644 vendor/github.com/stretchr/objx/doc.go create mode 100644 vendor/github.com/stretchr/objx/go.mod create mode 100644 vendor/github.com/stretchr/objx/go.sum create mode 100644 vendor/github.com/stretchr/objx/map.go create mode 100644 vendor/github.com/stretchr/objx/mutations.go create mode 100644 vendor/github.com/stretchr/objx/security.go create mode 100644 vendor/github.com/stretchr/objx/tests.go create mode 100644 vendor/github.com/stretchr/objx/type_specific.go create mode 100644 vendor/github.com/stretchr/objx/type_specific_codegen.go create mode 100644 vendor/github.com/stretchr/objx/value.go rename vendor/github.com/stretchr/testify/assert/{assertion_order.go => assertion_compare.go} (62%) create mode 100644 vendor/github.com/stretchr/testify/mock/doc.go create mode 100644 vendor/github.com/stretchr/testify/mock/mock.go create mode 100644 vendor/gopkg.in/yaml.v3/.travis.yml create mode 100644 vendor/gopkg.in/yaml.v3/LICENSE create mode 100644 vendor/gopkg.in/yaml.v3/NOTICE create mode 100644 vendor/gopkg.in/yaml.v3/README.md create mode 100644 vendor/gopkg.in/yaml.v3/apic.go create mode 100644 vendor/gopkg.in/yaml.v3/decode.go create mode 100644 vendor/gopkg.in/yaml.v3/emitterc.go create mode 100644 vendor/gopkg.in/yaml.v3/encode.go create mode 100644 vendor/gopkg.in/yaml.v3/go.mod create mode 100644 vendor/gopkg.in/yaml.v3/parserc.go create mode 100644 vendor/gopkg.in/yaml.v3/readerc.go create mode 100644 vendor/gopkg.in/yaml.v3/resolve.go create mode 100644 vendor/gopkg.in/yaml.v3/scannerc.go create mode 100644 vendor/gopkg.in/yaml.v3/sorter.go create mode 100644 vendor/gopkg.in/yaml.v3/writerc.go create mode 100644 vendor/gopkg.in/yaml.v3/yaml.go create mode 100644 vendor/gopkg.in/yaml.v3/yamlh.go create mode 100644 vendor/gopkg.in/yaml.v3/yamlprivateh.go diff --git a/builtin/logical/database/backend.go b/builtin/logical/database/backend.go index 7c8592efae..4ac4d396db 100644 --- a/builtin/logical/database/backend.go +++ b/builtin/logical/database/backend.go @@ -8,12 +8,12 @@ import ( "sync" "time" - log "github.com/hashicorp/go-hclog" - "github.com/hashicorp/errwrap" - uuid "github.com/hashicorp/go-uuid" + log "github.com/hashicorp/go-hclog" + "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/sdk/database/dbplugin" "github.com/hashicorp/vault/sdk/database/helper/dbutil" + "github.com/hashicorp/vault/sdk/database/newdbplugin" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/helper/locksutil" "github.com/hashicorp/vault/sdk/helper/strutil" @@ -30,7 +30,7 @@ const ( type dbPluginInstance struct { sync.RWMutex - dbplugin.Database + database databaseVersionWrapper id string name string @@ -46,7 +46,7 @@ func (dbi *dbPluginInstance) Close() error { } dbi.closed = true - return dbi.Database.Close() + return dbi.database.Close() } func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) { @@ -89,7 +89,7 @@ func Backend(conf *logical.BackendConfig) *databaseBackend { pathListRoles(&b), pathRoles(&b), pathCredsCreate(&b), - pathRotateCredentials(&b), + pathRotateRootCredentials(&b), ), Secrets: []*framework.Secret{ @@ -240,9 +240,9 @@ func (b *databaseBackend) GetConnectionWithConfig(ctx context.Context, name stri unlockFunc := b.RUnlock defer func() { unlockFunc() }() - db, ok := b.connections[name] + dbi, ok := b.connections[name] if ok { - return db, nil + return dbi, nil } // Upgrade lock @@ -250,20 +250,9 @@ func (b *databaseBackend) GetConnectionWithConfig(ctx context.Context, name stri b.Lock() unlockFunc = b.Unlock - db, ok = b.connections[name] + dbi, ok = b.connections[name] if ok { - return db, nil - } - - dbp, err := dbplugin.PluginFactory(ctx, config.PluginName, b.System(), b.logger) - if err != nil { - return nil, err - } - - _, err = dbp.Init(ctx, config.ConnectionDetails, true) - if err != nil { - dbp.Close() - return nil, err + return dbi, nil } id, err := uuid.GenerateUUID() @@ -271,14 +260,28 @@ func (b *databaseBackend) GetConnectionWithConfig(ctx context.Context, name stri return nil, err } - db = &dbPluginInstance{ - Database: dbp, - name: name, - id: id, + dbw, err := newDatabaseWrapper(ctx, config.PluginName, b.System(), b.logger) + if err != nil { + return nil, fmt.Errorf("unable to create database instance: %w", err) } - b.connections[name] = db - return db, nil + initReq := newdbplugin.InitializeRequest{ + Config: config.ConnectionDetails, + VerifyConnection: true, + } + _, err = dbw.Initialize(ctx, initReq) + if err != nil { + dbw.Close() + return nil, err + } + + dbi = &dbPluginInstance{ + database: dbw, + id: id, + name: name, + } + b.connections[name] = dbi + return dbi, nil } // invalidateQueue cancels any background queue loading and destroys the queue. diff --git a/builtin/logical/database/backend_test.go b/builtin/logical/database/backend_test.go index 26eca31159..2fd2caae89 100644 --- a/builtin/logical/database/backend_test.go +++ b/builtin/logical/database/backend_test.go @@ -11,7 +11,7 @@ import ( "time" "github.com/go-test/deep" - "github.com/hashicorp/vault-plugin-database-mongodbatlas" + mongodbatlas "github.com/hashicorp/vault-plugin-database-mongodbatlas" "github.com/hashicorp/vault/api" "github.com/hashicorp/vault/helper/namespace" postgreshelper "github.com/hashicorp/vault/helper/testhelpers/postgresql" @@ -231,6 +231,7 @@ func TestBackend_config_connection(t *testing.T) { }, "allowed_roles": []string{"*"}, "root_credentials_rotate_statements": []string{}, + "password_policy": "", } configReq.Operation = logical.ReadOperation resp, err = b.HandleRequest(namespace.RootContext(nil), configReq) @@ -283,6 +284,7 @@ func TestBackend_config_connection(t *testing.T) { }, "allowed_roles": []string{"*"}, "root_credentials_rotate_statements": []string{}, + "password_policy": "", } configReq.Operation = logical.ReadOperation resp, err = b.HandleRequest(namespace.RootContext(nil), configReq) @@ -324,6 +326,7 @@ func TestBackend_config_connection(t *testing.T) { }, "allowed_roles": []string{"flu", "barre"}, "root_credentials_rotate_statements": []string{}, + "password_policy": "", } configReq.Operation = logical.ReadOperation resp, err = b.HandleRequest(namespace.RootContext(nil), configReq) @@ -711,6 +714,7 @@ func TestBackend_connectionCrud(t *testing.T) { }, "allowed_roles": []string{"plugin-role-test"}, "root_credentials_rotate_statements": []string(nil), + "password_policy": "", } req.Operation = logical.ReadOperation resp, err = b.HandleRequest(namespace.RootContext(nil), req) diff --git a/builtin/logical/database/mocks_test.go b/builtin/logical/database/mocks_test.go new file mode 100644 index 0000000000..5eac016341 --- /dev/null +++ b/builtin/logical/database/mocks_test.go @@ -0,0 +1,101 @@ +package database + +import ( + "context" + "time" + + "github.com/hashicorp/vault/sdk/database/dbplugin" + "github.com/hashicorp/vault/sdk/database/newdbplugin" + "github.com/stretchr/testify/mock" +) + +var _ newdbplugin.Database = &mockNewDatabase{} + +type mockNewDatabase struct { + mock.Mock +} + +func (m *mockNewDatabase) Initialize(ctx context.Context, req newdbplugin.InitializeRequest) (newdbplugin.InitializeResponse, error) { + args := m.Called(ctx, req) + return args.Get(0).(newdbplugin.InitializeResponse), args.Error(1) +} + +func (m *mockNewDatabase) NewUser(ctx context.Context, req newdbplugin.NewUserRequest) (newdbplugin.NewUserResponse, error) { + args := m.Called(ctx, req) + return args.Get(0).(newdbplugin.NewUserResponse), args.Error(1) +} + +func (m *mockNewDatabase) UpdateUser(ctx context.Context, req newdbplugin.UpdateUserRequest) (newdbplugin.UpdateUserResponse, error) { + args := m.Called(ctx, req) + return args.Get(0).(newdbplugin.UpdateUserResponse), args.Error(1) +} + +func (m *mockNewDatabase) DeleteUser(ctx context.Context, req newdbplugin.DeleteUserRequest) (newdbplugin.DeleteUserResponse, error) { + args := m.Called(ctx, req) + return args.Get(0).(newdbplugin.DeleteUserResponse), args.Error(1) +} + +func (m *mockNewDatabase) Type() (string, error) { + args := m.Called() + return args.String(0), args.Error(1) +} + +func (m *mockNewDatabase) Close() error { + args := m.Called() + return args.Error(0) +} + +var _ dbplugin.Database = &mockLegacyDatabase{} + +type mockLegacyDatabase struct { + mock.Mock +} + +func (m *mockLegacyDatabase) CreateUser(ctx context.Context, statements dbplugin.Statements, usernameConfig dbplugin.UsernameConfig, expiration time.Time) (username string, password string, err error) { + args := m.Called(ctx, statements, usernameConfig, expiration) + return args.String(0), args.String(1), args.Error(2) +} + +func (m *mockLegacyDatabase) RenewUser(ctx context.Context, statements dbplugin.Statements, username string, expiration time.Time) error { + args := m.Called(ctx, statements, username, expiration) + return args.Error(0) +} + +func (m *mockLegacyDatabase) RevokeUser(ctx context.Context, statements dbplugin.Statements, username string) error { + args := m.Called(ctx, statements, username) + return args.Error(0) +} + +func (m *mockLegacyDatabase) RotateRootCredentials(ctx context.Context, statements []string) (config map[string]interface{}, err error) { + args := m.Called(ctx, statements) + return args.Get(0).(map[string]interface{}), args.Error(1) +} + +func (m *mockLegacyDatabase) GenerateCredentials(ctx context.Context) (string, error) { + args := m.Called(ctx) + return args.String(0), args.Error(1) +} + +func (m *mockLegacyDatabase) SetCredentials(ctx context.Context, statements dbplugin.Statements, staticConfig dbplugin.StaticUserConfig) (username string, password string, err error) { + args := m.Called(ctx, statements, staticConfig) + return args.String(0), args.String(1), args.Error(2) +} + +func (m *mockLegacyDatabase) Init(ctx context.Context, config map[string]interface{}, verifyConnection bool) (saveConfig map[string]interface{}, err error) { + args := m.Called(ctx, config, verifyConnection) + return args.Get(0).(map[string]interface{}), args.Error(1) +} + +func (m *mockLegacyDatabase) Type() (string, error) { + args := m.Called() + return args.String(0), args.Error(1) +} + +func (m *mockLegacyDatabase) Close() error { + args := m.Called() + return args.Error(0) +} + +func (m *mockLegacyDatabase) Initialize(ctx context.Context, config map[string]interface{}, verifyConnection bool) (err error) { + panic("Initialize should not be called") +} diff --git a/builtin/logical/database/mockv4.go b/builtin/logical/database/mockv4.go new file mode 100644 index 0000000000..a9a7f043ed --- /dev/null +++ b/builtin/logical/database/mockv4.go @@ -0,0 +1,118 @@ +package database + +import ( + "context" + "fmt" + "time" + + log "github.com/hashicorp/go-hclog" + "github.com/hashicorp/vault/api" + "github.com/hashicorp/vault/sdk/database/dbplugin" +) + +const mockV4Type = "mockv4" + +// MockDatabaseV4 is an implementation of Database interface +type MockDatabaseV4 struct { + config map[string]interface{} +} + +var _ dbplugin.Database = &MockDatabaseV4{} + +// New returns a new in-memory instance +func NewV4() (interface{}, error) { + return MockDatabaseV4{}, nil +} + +// RunV4 instantiates a MongoDB object, and runs the RPC server for the plugin +func RunV4(apiTLSConfig *api.TLSConfig) error { + dbType, err := NewV4() + if err != nil { + return err + } + + dbplugin.Serve(dbType.(dbplugin.Database), api.VaultPluginTLSProvider(apiTLSConfig)) + + return nil +} + +func (m MockDatabaseV4) Init(ctx context.Context, config map[string]interface{}, verifyConnection bool) (saveConfig map[string]interface{}, err error) { + log.Default().Info("Init called", + "config", config, + "verifyConnection", verifyConnection) + + return config, nil +} + +func (m MockDatabaseV4) Initialize(ctx context.Context, config map[string]interface{}, verifyConnection bool) (err error) { + _, err = m.Init(ctx, config, verifyConnection) + return err +} + +func (m MockDatabaseV4) CreateUser(ctx context.Context, statements dbplugin.Statements, usernameConfig dbplugin.UsernameConfig, expiration time.Time) (username string, password string, err error) { + log.Default().Info("CreateUser called", + "statements", statements, + "usernameConfig", usernameConfig, + "expiration", expiration) + + now := time.Now() + user := fmt.Sprintf("mockv4_user_%s", now.Format(time.RFC3339)) + pass, err := m.GenerateCredentials(ctx) + if err != nil { + return "", "", fmt.Errorf("failed to generate credentials: %w", err) + } + return user, pass, nil +} + +func (m MockDatabaseV4) RenewUser(ctx context.Context, statements dbplugin.Statements, username string, expiration time.Time) error { + log.Default().Info("RenewUser called", + "statements", statements, + "username", username, + "expiration", expiration) + + return nil +} + +func (m MockDatabaseV4) RevokeUser(ctx context.Context, statements dbplugin.Statements, username string) error { + log.Default().Info("RevokeUser called", + "statements", statements, + "username", username) + + return nil +} + +func (m MockDatabaseV4) RotateRootCredentials(ctx context.Context, statements []string) (config map[string]interface{}, err error) { + log.Default().Info("RotateRootCredentials called", + "statements", statements) + + newPassword, err := m.GenerateCredentials(ctx) + if err != nil { + return config, fmt.Errorf("failed to generate credentials: %w", err) + } + config["password"] = newPassword + + return m.config, nil +} + +func (m MockDatabaseV4) SetCredentials(ctx context.Context, statements dbplugin.Statements, staticConfig dbplugin.StaticUserConfig) (username string, password string, err error) { + log.Default().Info("SetCredentials called", + "statements", statements, + "staticConfig", staticConfig) + return "", "", nil +} + +func (m MockDatabaseV4) GenerateCredentials(ctx context.Context) (password string, err error) { + now := time.Now() + pass := fmt.Sprintf("mockv4_password_%s", now.Format(time.RFC3339)) + return pass, nil +} + +func (m MockDatabaseV4) Type() (string, error) { + log.Default().Info("Type called") + return mockV4Type, nil +} + +func (m MockDatabaseV4) Close() error { + log.Default().Info("Close called") + return nil +} diff --git a/builtin/logical/database/mockv5.go b/builtin/logical/database/mockv5.go new file mode 100644 index 0000000000..320e065703 --- /dev/null +++ b/builtin/logical/database/mockv5.go @@ -0,0 +1,85 @@ +package database + +import ( + "context" + "fmt" + "time" + + log "github.com/hashicorp/go-hclog" + "github.com/hashicorp/vault/api" + "github.com/hashicorp/vault/sdk/database/newdbplugin" +) + +const mockV5Type = "mockv5" + +// MockDatabaseV5 is an implementation of Database interface +type MockDatabaseV5 struct { + config map[string]interface{} +} + +var _ newdbplugin.Database = &MockDatabaseV5{} + +// New returns a new in-memory instance +func New() (interface{}, error) { + db := MockDatabaseV5{} + return db, nil +} + +// Run instantiates a MongoDB object, and runs the RPC server for the plugin +func RunV5(apiTLSConfig *api.TLSConfig) error { + dbType, err := New() + if err != nil { + return err + } + + newdbplugin.Serve(dbType.(newdbplugin.Database), api.VaultPluginTLSProvider(apiTLSConfig)) + + return nil +} + +func (m MockDatabaseV5) Initialize(ctx context.Context, req newdbplugin.InitializeRequest) (newdbplugin.InitializeResponse, error) { + log.Default().Info("Initialize called", + "req", req) + + config := req.Config + config["from-plugin"] = "this value is from the plugin itself" + + resp := newdbplugin.InitializeResponse{ + Config: req.Config, + } + return resp, nil +} + +func (m MockDatabaseV5) NewUser(ctx context.Context, req newdbplugin.NewUserRequest) (newdbplugin.NewUserResponse, error) { + log.Default().Info("NewUser called", + "req", req) + + now := time.Now() + user := fmt.Sprintf("mockv5_user_%s", now.Format(time.RFC3339)) + resp := newdbplugin.NewUserResponse{ + Username: user, + } + return resp, nil +} + +func (m MockDatabaseV5) UpdateUser(ctx context.Context, req newdbplugin.UpdateUserRequest) (newdbplugin.UpdateUserResponse, error) { + log.Default().Info("UpdateUser called", + "req", req) + return newdbplugin.UpdateUserResponse{}, nil +} + +func (m MockDatabaseV5) DeleteUser(ctx context.Context, req newdbplugin.DeleteUserRequest) (newdbplugin.DeleteUserResponse, error) { + log.Default().Info("DeleteUser called", + "req", req) + return newdbplugin.DeleteUserResponse{}, nil +} + +func (m MockDatabaseV5) Type() (string, error) { + log.Default().Info("Type called") + return mockV5Type, nil +} + +func (m MockDatabaseV5) Close() error { + log.Default().Info("Close called") + return nil +} diff --git a/builtin/logical/database/path_config_connection.go b/builtin/logical/database/path_config_connection.go index ac40d9f66a..75fade1e16 100644 --- a/builtin/logical/database/path_config_connection.go +++ b/builtin/logical/database/path_config_connection.go @@ -10,7 +10,7 @@ import ( "github.com/fatih/structs" "github.com/hashicorp/errwrap" uuid "github.com/hashicorp/go-uuid" - "github.com/hashicorp/vault/sdk/database/dbplugin" + "github.com/hashicorp/vault/sdk/database/newdbplugin" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/logical" ) @@ -30,6 +30,8 @@ type DatabaseConfig struct { AllowedRoles []string `json:"allowed_roles" structs:"allowed_roles" mapstructure:"allowed_roles"` RootCredentialsRotateStatements []string `json:"root_credentials_rotate_statements" structs:"root_credentials_rotate_statements" mapstructure:"root_credentials_rotate_statements"` + + PasswordPolicy string `json:"password_policy" structs:"password_policy" mapstructure:"password_policy"` } // pathResetConnection configures a path to reset a plugin. @@ -114,6 +116,10 @@ func pathConfigurePluginConnection(b *databaseBackend) *framework.Path { page for more information on support and formatting for this parameter.`, }, + "password_policy": &framework.FieldSchema{ + Type: framework.TypeString, + Description: `Password policy to use when generating passwords.`, + }, }, ExistenceCheck: b.connectionExistenceCheck(), @@ -138,7 +144,7 @@ func (b *databaseBackend) connectionExistenceCheck() framework.ExistenceFunc { entry, err := req.Storage.Get(ctx, fmt.Sprintf("config/%s", name)) if err != nil { - return false, errors.New("failed to read connection configuration") + return false, fmt.Errorf("failed to read connection configuration: %w", err) } return entry != nil, nil @@ -179,7 +185,7 @@ func (b *databaseBackend) connectionReadHandler() framework.OperationFunc { entry, err := req.Storage.Get(ctx, fmt.Sprintf("config/%s", name)) if err != nil { - return nil, errors.New("failed to read connection configuration") + return nil, fmt.Errorf("failed to read connection configuration: %w", err) } if entry == nil { return nil, nil @@ -245,7 +251,7 @@ func (b *databaseBackend) connectionWriteHandler() framework.OperationFunc { entry, err := req.Storage.Get(ctx, fmt.Sprintf("config/%s", name)) if err != nil { - return nil, errors.New("failed to read connection configuration") + return nil, fmt.Errorf("failed to read connection configuration: %w", err) } if entry != nil { if err := entry.DecodeJSON(config); err != nil { @@ -274,6 +280,10 @@ func (b *databaseBackend) connectionWriteHandler() framework.OperationFunc { config.RootCredentialsRotateStatements = data.Get("root_rotation_statements").([]string) } + if passwordPolicyRaw, ok := data.GetOk("password_policy"); ok { + config.PasswordPolicy = passwordPolicyRaw.(string) + } + // Remove these entries from the data before we store it keyed under // ConnectionDetails. delete(data.Raw, "name") @@ -281,11 +291,11 @@ func (b *databaseBackend) connectionWriteHandler() framework.OperationFunc { delete(data.Raw, "allowed_roles") delete(data.Raw, "verify_connection") delete(data.Raw, "root_rotation_statements") + delete(data.Raw, "password_policy") - // Create a database plugin and initialize it. - db, err := dbplugin.PluginFactory(ctx, config.PluginName, b.System(), b.logger) + id, err := uuid.GenerateUUID() if err != nil { - return logical.ErrorResponse(fmt.Sprintf("error creating database object: %s", err)), nil + return nil, err } // If this is an update, take any new values, overwrite what was there @@ -302,37 +312,39 @@ func (b *databaseBackend) connectionWriteHandler() framework.OperationFunc { } } - config.ConnectionDetails, err = db.Init(ctx, config.ConnectionDetails, verifyConnection) + // Create a database plugin and initialize it. + dbw, err := newDatabaseWrapper(ctx, config.PluginName, b.System(), b.logger) if err != nil { - db.Close() - return logical.ErrorResponse(fmt.Sprintf("error creating database object: %s", err)), nil + return logical.ErrorResponse("error creating database object: %s", err), nil } + initReq := newdbplugin.InitializeRequest{ + Config: config.ConnectionDetails, + VerifyConnection: verifyConnection, + } + initResp, err := dbw.Initialize(ctx, initReq) + if err != nil { + dbw.Close() + return logical.ErrorResponse("error creating database object: %s", err), nil + } + config.ConnectionDetails = initResp.Config + b.Lock() defer b.Unlock() // Close and remove the old connection b.clearConnection(name) - id, err := uuid.GenerateUUID() - if err != nil { - return nil, err - } - b.connections[name] = &dbPluginInstance{ - Database: db, + database: dbw, name: name, id: id, } - // Store it - entry, err = logical.StorageEntryJSON(fmt.Sprintf("config/%s", name), config) + err = storeConfig(ctx, req.Storage, name, config) if err != nil { return nil, err } - if err := req.Storage.Put(ctx, entry); err != nil { - return nil, err - } resp := &logical.Response{} @@ -346,10 +358,30 @@ func (b *databaseBackend) connectionWriteHandler() framework.OperationFunc { } } + // If using a legacy DB plugin and set the `password_policy` field, send a warning to the user indicating + // the `password_policy` will not be used + if dbw.isV4() && config.PasswordPolicy != "" { + resp.AddWarning(fmt.Sprintf("%s does not support password policies - upgrade to the latest version of "+ + "Vault (or the sdk if using a custom plugin) to gain password policy support", config.PluginName)) + } + return resp, nil } } +func storeConfig(ctx context.Context, storage logical.Storage, name string, config *DatabaseConfig) error { + entry, err := logical.StorageEntryJSON(fmt.Sprintf("config/%s", name), config) + if err != nil { + return fmt.Errorf("unable to marshal object to JSON: %w", err) + } + + err = storage.Put(ctx, entry) + if err != nil { + return fmt.Errorf("failed to save object: %w", err) + } + return nil +} + const pathConfigConnectionHelpSyn = ` Configure connection details to a database plugin. ` diff --git a/builtin/logical/database/path_creds_create.go b/builtin/logical/database/path_creds_create.go index e66d6d13e4..6688cc17d7 100644 --- a/builtin/logical/database/path_creds_create.go +++ b/builtin/logical/database/path_creds_create.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "github.com/hashicorp/vault/sdk/database/dbplugin" + "github.com/hashicorp/vault/sdk/database/newdbplugin" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/helper/strutil" "github.com/hashicorp/vault/sdk/logical" @@ -73,13 +73,13 @@ func (b *databaseBackend) pathCredsCreateRead() framework.OperationFunc { } // Get the Database object - db, err := b.GetConnection(ctx, req.Storage, role.DBName) + dbi, err := b.GetConnection(ctx, req.Storage, role.DBName) if err != nil { return nil, err } - db.RLock() - defer db.RUnlock() + dbi.RLock() + defer dbi.RUnlock() ttl, _, err := framework.CalculateTTL(b.System(), 0, role.DefaultTTL, 0, role.MaxTTL, 0, time.Time{}) if err != nil { @@ -90,27 +90,44 @@ func (b *databaseBackend) pathCredsCreateRead() framework.OperationFunc { // to ensure the database credential does not expire before the lease expiration = expiration.Add(5 * time.Second) - usernameConfig := dbplugin.UsernameConfig{ - DisplayName: req.DisplayName, - RoleName: name, + password, err := dbi.database.GeneratePassword(ctx, b.System(), dbConfig.PasswordPolicy) + if err != nil { + return nil, fmt.Errorf("unable to generate password: %w", err) } - // Create the user - username, password, err := db.CreateUser(ctx, role.Statements, usernameConfig, expiration) + newUserReq := newdbplugin.NewUserRequest{ + UsernameConfig: newdbplugin.UsernameMetadata{ + DisplayName: req.DisplayName, + RoleName: name, + }, + Statements: newdbplugin.Statements{ + Commands: role.Statements.Creation, + }, + RollbackStatements: newdbplugin.Statements{ + Commands: role.Statements.Rollback, + }, + Password: password, + Expiration: expiration, + } + + // Overwriting the password in the event this is a legacy database plugin and the provided password is ignored + newUserResp, password, err := dbi.database.NewUser(ctx, newUserReq) if err != nil { - b.CloseIfShutdown(db, err) + b.CloseIfShutdown(dbi, err) return nil, err } - resp := b.Secret(SecretCredsType).Response(map[string]interface{}{ - "username": username, + respData := map[string]interface{}{ + "username": newUserResp.Username, "password": password, - }, map[string]interface{}{ - "username": username, + } + internal := map[string]interface{}{ + "username": newUserResp.Username, "role": name, "db_name": role.DBName, "revocation_statements": role.Statements.Revocation, - }) + } + resp := b.Secret(SecretCredsType).Response(respData, internal) resp.Secret.TTL = role.DefaultTTL resp.Secret.MaxTTL = role.MaxTTL return resp, nil diff --git a/builtin/logical/database/path_rotate_credentials.go b/builtin/logical/database/path_rotate_credentials.go index 8059a49d2d..c1f3e102f6 100644 --- a/builtin/logical/database/path_rotate_credentials.go +++ b/builtin/logical/database/path_rotate_credentials.go @@ -5,16 +5,13 @@ import ( "fmt" "time" - "github.com/hashicorp/vault/sdk/database/dbplugin" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - + "github.com/hashicorp/vault/sdk/database/newdbplugin" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/logical" "github.com/hashicorp/vault/sdk/queue" ) -func pathRotateCredentials(b *databaseBackend) []*framework.Path { +func pathRotateRootCredentials(b *databaseBackend) []*framework.Path { return []*framework.Path{ &framework.Path{ Pattern: "rotate-root/" + framework.GenericNameRegex("name"), @@ -27,7 +24,7 @@ func pathRotateCredentials(b *databaseBackend) []*framework.Path { Operations: map[logical.Operation]framework.OperationHandler{ logical.UpdateOperation: &framework.PathOperation{ - Callback: b.pathRotateCredentialsUpdate(), + Callback: b.pathRotateRootCredentialsUpdate(), ForwardPerformanceSecondary: true, ForwardPerformanceStandby: true, }, @@ -59,7 +56,7 @@ func pathRotateCredentials(b *databaseBackend) []*framework.Path { } } -func (b *databaseBackend) pathRotateCredentialsUpdate() framework.OperationFunc { +func (b *databaseBackend) pathRotateRootCredentialsUpdate() framework.OperationFunc { return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { name := data.Get("name").(string) if name == "" { @@ -71,15 +68,15 @@ func (b *databaseBackend) pathRotateCredentialsUpdate() framework.OperationFunc return nil, err } - db, err := b.GetConnection(ctx, req.Storage, name) + dbi, err := b.GetConnection(ctx, req.Storage, name) if err != nil { return nil, err } defer func() { // Close the plugin - db.closed = true - if err := db.Database.Close(); err != nil { + dbi.closed = true + if err := dbi.database.Close(); err != nil { b.Logger().Error("error closing the database plugin connection", "err", err) } // Even on error, still remove the connection @@ -91,13 +88,13 @@ func (b *databaseBackend) pathRotateCredentialsUpdate() framework.OperationFunc defer b.Unlock() // Take the write lock on the instance - db.Lock() - defer db.Unlock() + dbi.Lock() + defer dbi.Unlock() // Generate new credentials - userName := config.ConnectionDetails["username"].(string) + username := config.ConnectionDetails["username"].(string) oldPassword := config.ConnectionDetails["password"].(string) - newPassword, err := db.GenerateCredentials(ctx) + newPassword, err := dbi.database.GeneratePassword(ctx, b.System(), config.PasswordPolicy) if err != nil { return nil, err } @@ -106,7 +103,7 @@ func (b *databaseBackend) pathRotateCredentialsUpdate() framework.OperationFunc // Write a WAL entry walID, err := framework.PutWAL(ctx, req.Storage, rotateRootWALKey, &rotateRootCredentialsWAL{ ConnectionName: name, - UserName: userName, + UserName: username, OldPassword: oldPassword, NewPassword: newPassword, }) @@ -114,37 +111,32 @@ func (b *databaseBackend) pathRotateCredentialsUpdate() framework.OperationFunc return nil, err } - // Attempt to use SetCredentials for the root credential rotation - statements := dbplugin.Statements{Rotation: config.RootCredentialsRotateStatements} - userConfig := dbplugin.StaticUserConfig{ - Username: userName, - Password: newPassword, + updateReq := newdbplugin.UpdateUserRequest{ + Username: username, + Password: &newdbplugin.ChangePassword{ + NewPassword: newPassword, + Statements: newdbplugin.Statements{ + Commands: config.RootCredentialsRotateStatements, + }, + }, } - if _, _, err := db.SetCredentials(ctx, statements, userConfig); err != nil { - if status.Code(err) == codes.Unimplemented { - // Fall back to using RotateRootCredentials if unimplemented - config.ConnectionDetails, err = db.RotateRootCredentials(ctx, - config.RootCredentialsRotateStatements) - } - if err != nil { - return nil, err - } + newConfigDetails, err := dbi.database.UpdateUser(ctx, updateReq, true) + if err != nil { + return nil, fmt.Errorf("failed to update user: %w", err) + } + if newConfigDetails != nil { + config.ConnectionDetails = newConfigDetails } - // Update storage with the new root credentials - entry, err := logical.StorageEntryJSON(fmt.Sprintf("config/%s", name), config) + err = storeConfig(ctx, req.Storage, name, config) if err != nil { return nil, err } - if err := req.Storage.Put(ctx, entry); err != nil { - return nil, err - } - // Delete the WAL entry after successfully rotating root credentials - if err := framework.DeleteWAL(ctx, req.Storage, walID); err != nil { + err = framework.DeleteWAL(ctx, req.Storage, walID) + if err != nil { b.Logger().Warn("unable to delete WAL", "error", err, "WAL ID", walID) } - return nil, nil } } diff --git a/builtin/logical/database/rollback.go b/builtin/logical/database/rollback.go index 30e02b250d..8631d7e33d 100644 --- a/builtin/logical/database/rollback.go +++ b/builtin/logical/database/rollback.go @@ -4,7 +4,7 @@ import ( "context" "errors" - "github.com/hashicorp/vault/sdk/database/dbplugin" + "github.com/hashicorp/vault/sdk/database/newdbplugin" "github.com/hashicorp/vault/sdk/logical" "github.com/mitchellh/mapstructure" "google.golang.org/grpc/codes" @@ -73,13 +73,13 @@ func (b *databaseBackend) walRollback(ctx context.Context, req *logical.Request, } // rollbackDatabaseCredentials rolls back root database credentials for -// the connection associated with the passed WAL entry. It will creates +// the connection associated with the passed WAL entry. It will create // a connection to the database using the WAL entry new password in // order to alter the password to be the WAL entry old password. func (b *databaseBackend) rollbackDatabaseCredentials(ctx context.Context, config *DatabaseConfig, entry rotateRootCredentialsWAL) error { // Attempt to get a connection with the WAL entry new password. config.ConnectionDetails["password"] = entry.NewPassword - dbc, err := b.GetConnectionWithConfig(ctx, entry.ConnectionName, config) + dbi, err := b.GetConnectionWithConfig(ctx, entry.ConnectionName, config) if err != nil { return err } @@ -91,22 +91,21 @@ func (b *databaseBackend) rollbackDatabaseCredentials(ctx context.Context, confi } }() - // Roll back the database password to the WAL entry old password - statements := dbplugin.Statements{Rotation: config.RootCredentialsRotateStatements} - userConfig := dbplugin.StaticUserConfig{ + updateReq := newdbplugin.UpdateUserRequest{ Username: entry.UserName, - Password: entry.OldPassword, - } - if _, _, err := dbc.SetCredentials(ctx, statements, userConfig); err != nil { - // If the database plugin doesn't implement SetCredentials, the root - // credentials can't be rolled back. This means the root credential - // rotation happened via the plugin RotateRootCredentials RPC. - if status.Code(err) == codes.Unimplemented { - return nil - } - - return err + Password: &newdbplugin.ChangePassword{ + NewPassword: entry.OldPassword, + Statements: newdbplugin.Statements{ + Commands: config.RootCredentialsRotateStatements, + }, + }, } - return nil + // It actually is the root user here, but we only want to use SetCredentials since + // RotateRootCredentials doesn't give any control over what password is used + _, err = dbi.database.UpdateUser(ctx, updateReq, false) + if status.Code(err) == codes.Unimplemented { + return nil + } + return err } diff --git a/builtin/logical/database/rollback_test.go b/builtin/logical/database/rollback_test.go index ec2bbc82c8..3f41b5362e 100644 --- a/builtin/logical/database/rollback_test.go +++ b/builtin/logical/database/rollback_test.go @@ -4,10 +4,11 @@ import ( "context" "strings" "testing" + "time" "github.com/hashicorp/vault/helper/namespace" postgreshelper "github.com/hashicorp/vault/helper/testhelpers/postgresql" - "github.com/hashicorp/vault/sdk/database/dbplugin" + "github.com/hashicorp/vault/sdk/database/newdbplugin" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/logical" ) @@ -92,18 +93,22 @@ func TestBackend_RotateRootCredentials_WAL_rollback(t *testing.T) { } // Get a connection to the database plugin - pc, err := dbBackend.GetConnection(context.Background(), + dbi, err := dbBackend.GetConnection(context.Background(), config.StorageView, "plugin-test") if err != nil { t.Fatal(err) } // Alter the database password so it no longer matches what is in storage - _, _, err = pc.SetCredentials(context.Background(), dbplugin.Statements{}, - dbplugin.StaticUserConfig{ - Username: databaseUser, - Password: "newSecret", - }) + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + updateReq := newdbplugin.UpdateUserRequest{ + Username: databaseUser, + Password: &newdbplugin.ChangePassword{ + NewPassword: "newSecret", + }, + } + _, err = dbi.database.UpdateUser(ctx, updateReq, false) if err != nil { t.Fatal(err) } @@ -335,17 +340,21 @@ func TestBackend_RotateRootCredentials_WAL_no_rollback_2(t *testing.T) { } // Get a connection to the database plugin - pc, err := dbBackend.GetConnection(context.Background(), config.StorageView, "plugin-test") + dbi, err := dbBackend.GetConnection(context.Background(), config.StorageView, "plugin-test") if err != nil { t.Fatal(err) } // Alter the database password - _, _, err = pc.SetCredentials(context.Background(), dbplugin.Statements{}, - dbplugin.StaticUserConfig{ - Username: databaseUser, - Password: "newSecret", - }) + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + updateReq := newdbplugin.UpdateUserRequest{ + Username: databaseUser, + Password: &newdbplugin.ChangePassword{ + NewPassword: "newSecret", + }, + } + _, err = dbi.database.UpdateUser(ctx, updateReq, false) if err != nil { t.Fatal(err) } diff --git a/builtin/logical/database/rotation.go b/builtin/logical/database/rotation.go index 1d89c3498b..0194fb4905 100644 --- a/builtin/logical/database/rotation.go +++ b/builtin/logical/database/rotation.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/errwrap" "github.com/hashicorp/go-multierror" "github.com/hashicorp/vault/sdk/database/dbplugin" + "github.com/hashicorp/vault/sdk/database/newdbplugin" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/helper/consts" "github.com/hashicorp/vault/sdk/helper/locksutil" @@ -319,21 +320,20 @@ func (b *databaseBackend) setStaticAccount(ctx context.Context, s logical.Storag } // Get the Database object - db, err := b.GetConnection(ctx, s, input.Role.DBName) + dbi, err := b.GetConnection(ctx, s, input.Role.DBName) if err != nil { return output, err } - db.RLock() - defer db.RUnlock() + dbi.RLock() + defer dbi.RUnlock() // Use password from input if available. This happens if we're restoring from // a WAL item or processing the rotation queue with an item that has a WAL // associated with it newPassword := input.Password if newPassword == "" { - // Generate a new password - newPassword, err = db.GenerateCredentials(ctx) + newPassword, err = dbi.database.GeneratePassword(ctx, b.System(), dbConfig.PasswordPolicy) if err != nil { return output, err } @@ -358,21 +358,26 @@ func (b *databaseBackend) setStaticAccount(ctx context.Context, s logical.Storag } } - _, password, err := db.SetCredentials(ctx, input.Role.Statements, config) - if err != nil { - b.CloseIfShutdown(db, err) - return output, errwrap.Wrapf("error setting credentials: {{err}}", err) + updateReq := newdbplugin.UpdateUserRequest{ + Username: input.Role.StaticAccount.Username, + Password: &newdbplugin.ChangePassword{ + NewPassword: newPassword, + Statements: newdbplugin.Statements{ + Commands: input.Role.Statements.Rotation, + }, + }, } - - if newPassword != password { - return output, errors.New("mismatch passwords returned") + _, err = dbi.database.UpdateUser(ctx, updateReq, false) + if err != nil { + b.CloseIfShutdown(dbi, err) + return output, errwrap.Wrapf("error setting credentials: {{err}}", err) } // Store updated role information // lvr is the known LastVaultRotation lvr := time.Now() input.Role.StaticAccount.LastVaultRotation = lvr - input.Role.StaticAccount.Password = password + input.Role.StaticAccount.Password = newPassword output.RotationTime = lvr entry, err := logical.StorageEntryJSON(databaseStaticRolePath+input.RoleName, input.Role) @@ -393,7 +398,7 @@ func (b *databaseBackend) setStaticAccount(ctx context.Context, s logical.Storag return &setStaticAccountOutput{RotationTime: lvr}, merr } -// initQueue preforms the necessary checks and initializations needed to preform +// initQueue preforms the necessary checks and initializations needed to perform // automatic credential rotation for roles associated with static accounts. This // method verifies if a queue is needed (primary server or local mount), and if // so initializes the queue and launches a go-routine to periodically invoke a diff --git a/builtin/logical/database/secret_creds.go b/builtin/logical/database/secret_creds.go index 186dd16656..56609e2b61 100644 --- a/builtin/logical/database/secret_creds.go +++ b/builtin/logical/database/secret_creds.go @@ -6,6 +6,7 @@ import ( "time" "github.com/hashicorp/vault/sdk/database/dbplugin" + "github.com/hashicorp/vault/sdk/database/newdbplugin" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/logical" ) @@ -45,13 +46,13 @@ func (b *databaseBackend) secretCredsRenew() framework.OperationFunc { } // Get the Database object - db, err := b.GetConnection(ctx, req.Storage, role.DBName) + dbi, err := b.GetConnection(ctx, req.Storage, role.DBName) if err != nil { return nil, err } - db.RLock() - defer db.RUnlock() + dbi.RLock() + defer dbi.RUnlock() // Make sure we increase the VALID UNTIL endpoint for this user. ttl, _, err := framework.CalculateTTL(b.System(), req.Secret.Increment, role.DefaultTTL, 0, role.MaxTTL, 0, req.Secret.IssueTime) @@ -63,9 +64,19 @@ func (b *databaseBackend) secretCredsRenew() framework.OperationFunc { // Adding a small buffer since the TTL will be calculated again after this call // to ensure the database credential does not expire before the lease expireTime = expireTime.Add(5 * time.Second) - err := db.RenewUser(ctx, role.Statements, username, expireTime) + + updateReq := newdbplugin.UpdateUserRequest{ + Username: username, + Expiration: &newdbplugin.ChangeExpiration{ + NewExpiration: expireTime, + Statements: newdbplugin.Statements{ + Commands: role.Statements.Renewal, + }, + }, + } + _, err := dbi.database.UpdateUser(ctx, updateReq, false) if err != nil { - b.CloseIfShutdown(db, err) + b.CloseIfShutdown(dbi, err) return nil, err } } @@ -103,41 +114,49 @@ func (b *databaseBackend) secretCredsRevoke() framework.OperationFunc { dbName = role.DBName statements = role.Statements } else { - if dbNameRaw, ok := req.Secret.InternalData["db_name"]; !ok { + dbNameRaw, ok := req.Secret.InternalData["db_name"] + if !ok { return nil, fmt.Errorf("error during revoke: could not find role with name %q or embedded revocation db name data", req.Secret.InternalData["role"]) - } else { - dbName = dbNameRaw.(string) } - if statementsRaw, ok := req.Secret.InternalData["revocation_statements"]; !ok { + dbName = dbNameRaw.(string) + + statementsRaw, ok := req.Secret.InternalData["revocation_statements"] + if !ok { return nil, fmt.Errorf("error during revoke: could not find role with name %q or embedded revocation statement data", req.Secret.InternalData["role"]) - } else { - // If we don't actually have any statements, because none were - // set in the role, we'll end up with an empty one and the - // default for the db type will be attempted - if statementsRaw != nil { - statementsSlice, ok := statementsRaw.([]interface{}) - if !ok { - return nil, fmt.Errorf("error during revoke: could not find role with name %q and embedded reovcation data could not be read", req.Secret.InternalData["role"]) - } else { - for _, v := range statementsSlice { - statements.Revocation = append(statements.Revocation, v.(string)) - } - } + } + + // If we don't actually have any statements, because none were + // set in the role, we'll end up with an empty one and the + // default for the db type will be attempted + if statementsRaw != nil { + statementsSlice, ok := statementsRaw.([]interface{}) + if !ok { + return nil, fmt.Errorf("error during revoke: could not find role with name %q and embedded reovcation data could not be read", req.Secret.InternalData["role"]) + } + for _, v := range statementsSlice { + statements.Revocation = append(statements.Revocation, v.(string)) } } } // Get our connection - db, err := b.GetConnection(ctx, req.Storage, dbName) + dbi, err := b.GetConnection(ctx, req.Storage, dbName) if err != nil { return nil, err } - db.RLock() - defer db.RUnlock() + dbi.RLock() + defer dbi.RUnlock() - if err := db.RevokeUser(ctx, statements, username); err != nil { - b.CloseIfShutdown(db, err) + deleteReq := newdbplugin.DeleteUserRequest{ + Username: username, + Statements: newdbplugin.Statements{ + Commands: statements.Revocation, + }, + } + _, err = dbi.database.DeleteUser(ctx, deleteReq) + if err != nil { + b.CloseIfShutdown(dbi, err) return nil, err } return resp, nil diff --git a/builtin/logical/database/version_wrapper.go b/builtin/logical/database/version_wrapper.go new file mode 100644 index 0000000000..e8867d87c6 --- /dev/null +++ b/builtin/logical/database/version_wrapper.go @@ -0,0 +1,268 @@ +package database + +import ( + "context" + "crypto/rand" + "fmt" + + log "github.com/hashicorp/go-hclog" + "github.com/hashicorp/vault/helper/random" + "github.com/hashicorp/vault/sdk/database/dbplugin" + "github.com/hashicorp/vault/sdk/database/newdbplugin" + "github.com/hashicorp/vault/sdk/helper/pluginutil" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type databaseVersionWrapper struct { + v4 dbplugin.Database + v5 newdbplugin.Database +} + +// newDatabaseWrapper figures out which version of the database the pluginName is referring to and returns a wrapper object +// that can be used to make operations on the underlying database plugin. +func newDatabaseWrapper(ctx context.Context, pluginName string, sys pluginutil.LookRunnerUtil, logger log.Logger) (dbw databaseVersionWrapper, err error) { + newDB, err := newdbplugin.PluginFactory(ctx, pluginName, sys, logger) + if err == nil { + dbw = databaseVersionWrapper{ + v5: newDB, + } + return dbw, nil + } + + legacyDB, err := dbplugin.PluginFactory(ctx, pluginName, sys, logger) + if err == nil { + dbw = databaseVersionWrapper{ + v4: legacyDB, + } + return dbw, nil + } + + return dbw, fmt.Errorf("invalid database version") +} + +// Initialize the underlying database. This is analogous to a constructor on the database plugin object. +// Errors if the wrapper does not contain an underlying database. +func (d databaseVersionWrapper) Initialize(ctx context.Context, req newdbplugin.InitializeRequest) (newdbplugin.InitializeResponse, error) { + if !d.isV5() && !d.isV4() { + return newdbplugin.InitializeResponse{}, fmt.Errorf("no underlying database specified") + } + + // v5 Database + if d.isV5() { + return d.v5.Initialize(ctx, req) + } + + // v4 Database + saveConfig, err := d.v4.Init(ctx, req.Config, req.VerifyConnection) + if err != nil { + return newdbplugin.InitializeResponse{}, err + } + resp := newdbplugin.InitializeResponse{ + Config: saveConfig, + } + return resp, nil +} + +// NewUser in the database. This is different from the v5 Database in that it returns a password as well. +// This is done because the v4 Database is expected to generate a password and return it. The NewUserResponse +// does not have a way of returning the password so this function signature needs to be different. +// The password returned here should be considered the source of truth, not the provided password. +// Errors if the wrapper does not contain an underlying database. +func (d databaseVersionWrapper) NewUser(ctx context.Context, req newdbplugin.NewUserRequest) (resp newdbplugin.NewUserResponse, password string, err error) { + if !d.isV5() && !d.isV4() { + return newdbplugin.NewUserResponse{}, "", fmt.Errorf("no underlying database specified") + } + + // v5 Database + if d.isV5() { + resp, err = d.v5.NewUser(ctx, req) + return resp, req.Password, err + } + + // v4 Database + stmts := dbplugin.Statements{ + Creation: req.Statements.Commands, + Rollback: req.RollbackStatements.Commands, + } + usernameConfig := dbplugin.UsernameConfig{ + DisplayName: req.UsernameConfig.DisplayName, + RoleName: req.UsernameConfig.RoleName, + } + username, password, err := d.v4.CreateUser(ctx, stmts, usernameConfig, req.Expiration) + if err != nil { + return resp, "", err + } + + resp = newdbplugin.NewUserResponse{ + Username: username, + } + return resp, password, nil +} + +// UpdateUser in the underlying database. This is used to update any information currently supported +// in the UpdateUserRequest such as password credentials or user TTL. +// Errors if the wrapper does not contain an underlying database. +func (d databaseVersionWrapper) UpdateUser(ctx context.Context, req newdbplugin.UpdateUserRequest, isRootUser bool) (saveConfig map[string]interface{}, err error) { + if !d.isV5() && !d.isV4() { + return nil, fmt.Errorf("no underlying database specified") + } + + // v5 Database + if d.isV5() { + _, err := d.v5.UpdateUser(ctx, req) + return nil, err + } + + // v4 Database + if req.Password == nil && req.Expiration == nil { + return nil, fmt.Errorf("missing change to be sent to the database") + } + if req.Password != nil && req.Expiration != nil { + // We could support this, but it would require handling partial + // errors which I'm punting on since we don't need it for now + return nil, fmt.Errorf("cannot specify both password and expiration change at the same time") + } + + // Change password + if req.Password != nil { + return d.changePasswordLegacy(ctx, req.Username, req.Password, isRootUser) + } + + // Change expiration date + if req.Expiration != nil { + stmts := dbplugin.Statements{ + Renewal: req.Expiration.Statements.Commands, + } + err := d.v4.RenewUser(ctx, stmts, req.Username, req.Expiration.NewExpiration) + return nil, err + } + return nil, nil +} + +// changePasswordLegacy attempts to use SetCredentials to change the password for the user with the password provided +// in ChangePassword. If that user is the root user and SetCredentials is unimplemented, it will fall back to using +// RotateRootCredentials. If not the root user, this will not use RotateRootCredentials. +func (d databaseVersionWrapper) changePasswordLegacy(ctx context.Context, username string, passwordChange *newdbplugin.ChangePassword, isRootUser bool) (saveConfig map[string]interface{}, err error) { + err = d.changeUserPasswordLegacy(ctx, username, passwordChange) + + // If changing the root user's password but SetCredentials is unimplemented, fall back to RotateRootCredentials + if isRootUser && status.Code(err) == codes.Unimplemented { + saveConfig, err = d.changeRootUserPasswordLegacy(ctx, passwordChange) + if err != nil { + return nil, err + } + return saveConfig, nil + } + if err != nil { + return nil, err + } + return nil, nil +} + +func (d databaseVersionWrapper) changeUserPasswordLegacy(ctx context.Context, username string, passwordChange *newdbplugin.ChangePassword) (err error) { + stmts := dbplugin.Statements{ + Rotation: passwordChange.Statements.Commands, + } + staticConfig := dbplugin.StaticUserConfig{ + Username: username, + Password: passwordChange.NewPassword, + } + _, _, err = d.v4.SetCredentials(ctx, stmts, staticConfig) + return err +} + +func (d databaseVersionWrapper) changeRootUserPasswordLegacy(ctx context.Context, passwordChange *newdbplugin.ChangePassword) (saveConfig map[string]interface{}, err error) { + return d.v4.RotateRootCredentials(ctx, passwordChange.Statements.Commands) +} + +// DeleteUser in the underlying database. Errors if the wrapper does not contain an underlying database. +func (d databaseVersionWrapper) DeleteUser(ctx context.Context, req newdbplugin.DeleteUserRequest) (newdbplugin.DeleteUserResponse, error) { + if !d.isV5() && !d.isV4() { + return newdbplugin.DeleteUserResponse{}, fmt.Errorf("no underlying database specified") + } + + // v5 Database + if d.isV5() { + return d.v5.DeleteUser(ctx, req) + } + + // v4 Database + stmts := dbplugin.Statements{ + Revocation: req.Statements.Commands, + } + err := d.v4.RevokeUser(ctx, stmts, req.Username) + return newdbplugin.DeleteUserResponse{}, err +} + +// Type of the underlying database. Errors if the wrapper does not contain an underlying database. +func (d databaseVersionWrapper) Type() (string, error) { + if !d.isV5() && !d.isV4() { + return "", fmt.Errorf("no underlying database specified") + } + + // v5 Database + if d.isV5() { + return d.v5.Type() + } + + // v4 Database + return d.v4.Type() +} + +// Close the underlying database. Errors if the wrapper does not contain an underlying database. +func (d databaseVersionWrapper) Close() error { + if !d.isV5() && !d.isV4() { + return fmt.Errorf("no underlying database specified") + } + // v5 Database + if d.isV5() { + return d.v5.Close() + } + + // v4 Database + return d.v4.Close() +} + +// ///////////////////////////////////////////////////////////////////////////////// +// Password generation +// ///////////////////////////////////////////////////////////////////////////////// + +type passwordGenerator interface { + GeneratePasswordFromPolicy(ctx context.Context, policyName string) (password string, err error) +} + +var ( + defaultPasswordGenerator = random.DefaultStringGenerator +) + +// GeneratePassword either from the v4 database or by using the provided password policy. If using a v5 database +// and no password policy is specified, this will have a reasonable default password generator. +func (d databaseVersionWrapper) GeneratePassword(ctx context.Context, generator passwordGenerator, passwordPolicy string) (password string, err error) { + if !d.isV5() && !d.isV4() { + return "", fmt.Errorf("no underlying database specified") + } + + // If using the legacy database, use GenerateCredentials instead of password policies + // This will keep the existing behavior even though passwords can be generated with a policy + if d.isV4() { + password, err := d.v4.GenerateCredentials(ctx) + if err != nil { + return "", err + } + return password, nil + } + + if passwordPolicy == "" { + return defaultPasswordGenerator.Generate(ctx, rand.Reader) + } + return generator.GeneratePasswordFromPolicy(ctx, passwordPolicy) +} + +func (d databaseVersionWrapper) isV5() bool { + return d.v5 != nil +} + +func (d databaseVersionWrapper) isV4() bool { + return d.v4 != nil +} diff --git a/builtin/logical/database/version_wrapper_test.go b/builtin/logical/database/version_wrapper_test.go new file mode 100644 index 0000000000..63cc6cb119 --- /dev/null +++ b/builtin/logical/database/version_wrapper_test.go @@ -0,0 +1,994 @@ +package database + +import ( + "context" + "fmt" + "reflect" + "regexp" + "testing" + "time" + + "github.com/hashicorp/vault/sdk/database/newdbplugin" + "github.com/hashicorp/vault/sdk/logical" + "github.com/stretchr/testify/mock" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func TestInitDatabase_missingDB(t *testing.T) { + dbw := databaseVersionWrapper{} + + req := newdbplugin.InitializeRequest{} + resp, err := dbw.Initialize(context.Background(), req) + if err == nil { + t.Fatalf("err expected, got nil") + } + + expectedResp := newdbplugin.InitializeResponse{} + if !reflect.DeepEqual(resp, expectedResp) { + t.Fatalf("Actual resp: %#v\nExpected resp: %#v", resp, expectedResp) + } +} + +func TestInitDatabase_newDB(t *testing.T) { + type testCase struct { + req newdbplugin.InitializeRequest + + newInitResp newdbplugin.InitializeResponse + newInitErr error + newInitCalls int + + expectedResp newdbplugin.InitializeResponse + expectErr bool + } + + tests := map[string]testCase{ + "success": { + req: newdbplugin.InitializeRequest{ + Config: map[string]interface{}{ + "foo": "bar", + }, + VerifyConnection: true, + }, + newInitResp: newdbplugin.InitializeResponse{ + Config: map[string]interface{}{ + "foo": "bar", + }, + }, + newInitCalls: 1, + expectedResp: newdbplugin.InitializeResponse{ + Config: map[string]interface{}{ + "foo": "bar", + }, + }, + expectErr: false, + }, + "error": { + req: newdbplugin.InitializeRequest{ + Config: map[string]interface{}{ + "foo": "bar", + }, + VerifyConnection: true, + }, + newInitResp: newdbplugin.InitializeResponse{}, + newInitErr: fmt.Errorf("test error"), + newInitCalls: 1, + expectedResp: newdbplugin.InitializeResponse{}, + expectErr: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + newDB := new(mockNewDatabase) + newDB.On("Initialize", mock.Anything, mock.Anything). + Return(test.newInitResp, test.newInitErr) + defer newDB.AssertNumberOfCalls(t, "Initialize", test.newInitCalls) + + dbw := databaseVersionWrapper{ + v5: newDB, + } + + resp, err := dbw.Initialize(context.Background(), test.req) + if test.expectErr && err == nil { + t.Fatalf("err expected, got nil") + } + if !test.expectErr && err != nil { + t.Fatalf("no error expected, got: %s", err) + } + + if !reflect.DeepEqual(resp, test.expectedResp) { + t.Fatalf("Actual resp: %#v\nExpected resp: %#v", resp, test.expectedResp) + } + }) + } +} + +func TestInitDatabase_legacyDB(t *testing.T) { + type testCase struct { + req newdbplugin.InitializeRequest + + initConfig map[string]interface{} + initErr error + initCalls int + + expectedResp newdbplugin.InitializeResponse + expectErr bool + } + + tests := map[string]testCase{ + "success": { + req: newdbplugin.InitializeRequest{ + Config: map[string]interface{}{ + "foo": "bar", + }, + VerifyConnection: true, + }, + initConfig: map[string]interface{}{ + "foo": "bar", + }, + initCalls: 1, + expectedResp: newdbplugin.InitializeResponse{ + Config: map[string]interface{}{ + "foo": "bar", + }, + }, + expectErr: false, + }, + "error": { + req: newdbplugin.InitializeRequest{ + Config: map[string]interface{}{ + "foo": "bar", + }, + VerifyConnection: true, + }, + initErr: fmt.Errorf("test error"), + initCalls: 1, + expectedResp: newdbplugin.InitializeResponse{}, + expectErr: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + legacyDB := new(mockLegacyDatabase) + legacyDB.On("Init", mock.Anything, mock.Anything, mock.Anything). + Return(test.initConfig, test.initErr) + defer legacyDB.AssertNumberOfCalls(t, "Init", test.initCalls) + + dbw := databaseVersionWrapper{ + v4: legacyDB, + } + + resp, err := dbw.Initialize(context.Background(), test.req) + if test.expectErr && err == nil { + t.Fatalf("err expected, got nil") + } + if !test.expectErr && err != nil { + t.Fatalf("no error expected, got: %s", err) + } + + if !reflect.DeepEqual(resp, test.expectedResp) { + t.Fatalf("Actual resp: %#v\nExpected resp: %#v", resp, test.expectedResp) + } + }) + } +} + +type fakePasswordGenerator struct { + password string + err error +} + +func (pg fakePasswordGenerator) GeneratePasswordFromPolicy(ctx context.Context, policy string) (string, error) { + return pg.password, pg.err +} + +func TestGeneratePassword_missingDB(t *testing.T) { + dbw := databaseVersionWrapper{} + + gen := fakePasswordGenerator{ + err: fmt.Errorf("this shouldn't be called"), + } + pass, err := dbw.GeneratePassword(context.Background(), gen, "policy") + if err == nil { + t.Fatalf("err expected, got nil") + } + + if pass != "" { + t.Fatalf("Password should be empty but was: %s", pass) + } +} + +func TestGeneratePassword_legacy(t *testing.T) { + type testCase struct { + legacyPassword string + legacyErr error + legacyCalls int + + expectedPassword string + expectErr bool + } + + tests := map[string]testCase{ + "legacy password generation": { + legacyPassword: "legacy_password", + legacyErr: nil, + legacyCalls: 1, + + expectedPassword: "legacy_password", + expectErr: false, + }, + "legacy password failure": { + legacyPassword: "", + legacyErr: fmt.Errorf("failed :("), + legacyCalls: 1, + + expectedPassword: "", + expectErr: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + legacyDB := new(mockLegacyDatabase) + legacyDB.On("GenerateCredentials", mock.Anything). + Return(test.legacyPassword, test.legacyErr) + defer legacyDB.AssertNumberOfCalls(t, "GenerateCredentials", test.legacyCalls) + + dbw := databaseVersionWrapper{ + v4: legacyDB, + } + + passGen := fakePasswordGenerator{ + err: fmt.Errorf("this should not be called"), + } + + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + password, err := dbw.GeneratePassword(ctx, passGen, "test_policy") + if test.expectErr && err == nil { + t.Fatalf("err expected, got nil") + } + if !test.expectErr && err != nil { + t.Fatalf("no error expected, got: %s", err) + } + if password != test.expectedPassword { + t.Fatalf("Actual password: %s Expected password: %s", password, test.expectedPassword) + } + }) + } +} + +func TestGeneratePassword_policies(t *testing.T) { + type testCase struct { + passwordPolicyPassword string + passwordPolicyErr error + + expectedPassword string + expectErr bool + } + + tests := map[string]testCase{ + "password policy generation": { + passwordPolicyPassword: "new_password", + + expectedPassword: "new_password", + expectErr: false, + }, + "password policy error": { + passwordPolicyPassword: "", + passwordPolicyErr: fmt.Errorf("test error"), + + expectedPassword: "", + expectErr: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + newDB := new(mockNewDatabase) + defer newDB.AssertExpectations(t) + + dbw := databaseVersionWrapper{ + v5: newDB, + } + + passGen := fakePasswordGenerator{ + password: test.passwordPolicyPassword, + err: test.passwordPolicyErr, + } + + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + password, err := dbw.GeneratePassword(ctx, passGen, "test_policy") + if test.expectErr && err == nil { + t.Fatalf("err expected, got nil") + } + if !test.expectErr && err != nil { + t.Fatalf("no error expected, got: %s", err) + } + if password != test.expectedPassword { + t.Fatalf("Actual password: %s Expected password: %s", password, test.expectedPassword) + } + }) + } +} + +func TestGeneratePassword_no_policy(t *testing.T) { + newDB := new(mockNewDatabase) + defer newDB.AssertExpectations(t) + + dbw := databaseVersionWrapper{ + v5: newDB, + } + + passGen := fakePasswordGenerator{ + password: "", + err: fmt.Errorf("should not be called"), + } + + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + password, err := dbw.GeneratePassword(ctx, passGen, "") + if err != nil { + t.Fatalf("no error expected, got: %s", err) + } + if password == "" { + t.Fatalf("missing password") + } + + rawRegex := "^[a-zA-Z0-9-]{20}$" + re := regexp.MustCompile(rawRegex) + if !re.MatchString(password) { + t.Fatalf("password %q did not match regex: %q", password, rawRegex) + } +} + +func TestNewUser_missingDB(t *testing.T) { + dbw := databaseVersionWrapper{} + + req := newdbplugin.NewUserRequest{} + resp, pass, err := dbw.NewUser(context.Background(), req) + if err == nil { + t.Fatalf("err expected, got nil") + } + + expectedResp := newdbplugin.NewUserResponse{} + if !reflect.DeepEqual(resp, expectedResp) { + t.Fatalf("Actual resp: %#v\nExpected resp: %#v", resp, expectedResp) + } + + if pass != "" { + t.Fatalf("Password should be empty but was: %s", pass) + } +} + +func TestNewUser_newDB(t *testing.T) { + type testCase struct { + req newdbplugin.NewUserRequest + + newUserResp newdbplugin.NewUserResponse + newUserErr error + newUserCalls int + + expectedResp newdbplugin.NewUserResponse + expectErr bool + } + + tests := map[string]testCase{ + "success": { + req: newdbplugin.NewUserRequest{ + Password: "new_password", + }, + + newUserResp: newdbplugin.NewUserResponse{ + Username: "newuser", + }, + newUserCalls: 1, + + expectedResp: newdbplugin.NewUserResponse{ + Username: "newuser", + }, + expectErr: false, + }, + "error": { + req: newdbplugin.NewUserRequest{ + Password: "new_password", + }, + + newUserErr: fmt.Errorf("test error"), + newUserCalls: 1, + + expectedResp: newdbplugin.NewUserResponse{}, + expectErr: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + newDB := new(mockNewDatabase) + newDB.On("NewUser", mock.Anything, mock.Anything). + Return(test.newUserResp, test.newUserErr) + defer newDB.AssertNumberOfCalls(t, "NewUser", test.newUserCalls) + + dbw := databaseVersionWrapper{ + v5: newDB, + } + + resp, password, err := dbw.NewUser(context.Background(), test.req) + if test.expectErr && err == nil { + t.Fatalf("err expected, got nil") + } + if !test.expectErr && err != nil { + t.Fatalf("no error expected, got: %s", err) + } + + if !reflect.DeepEqual(resp, test.expectedResp) { + t.Fatalf("Actual resp: %#v\nExpected resp: %#v", resp, test.expectedResp) + } + + if password != test.req.Password { + t.Fatalf("Actual password: %s Expected password: %s", password, test.req.Password) + } + }) + } +} + +func TestNewUser_legacyDB(t *testing.T) { + type testCase struct { + req newdbplugin.NewUserRequest + + createUserUsername string + createUserPassword string + createUserErr error + createUserCalls int + + expectedResp newdbplugin.NewUserResponse + expectedPassword string + expectErr bool + } + + tests := map[string]testCase{ + "success": { + req: newdbplugin.NewUserRequest{ + Password: "new_password", + }, + + createUserUsername: "newuser", + createUserPassword: "securepassword", + createUserCalls: 1, + + expectedResp: newdbplugin.NewUserResponse{ + Username: "newuser", + }, + expectedPassword: "securepassword", + expectErr: false, + }, + "error": { + req: newdbplugin.NewUserRequest{ + Password: "new_password", + }, + + createUserErr: fmt.Errorf("test error"), + createUserCalls: 1, + + expectedResp: newdbplugin.NewUserResponse{}, + expectErr: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + legacyDB := new(mockLegacyDatabase) + legacyDB.On("CreateUser", mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(test.createUserUsername, test.createUserPassword, test.createUserErr) + defer legacyDB.AssertNumberOfCalls(t, "CreateUser", test.createUserCalls) + + dbw := databaseVersionWrapper{ + v4: legacyDB, + } + + resp, password, err := dbw.NewUser(context.Background(), test.req) + if test.expectErr && err == nil { + t.Fatalf("err expected, got nil") + } + if !test.expectErr && err != nil { + t.Fatalf("no error expected, got: %s", err) + } + + if !reflect.DeepEqual(resp, test.expectedResp) { + t.Fatalf("Actual resp: %#v\nExpected resp: %#v", resp, test.expectedResp) + } + + if password != test.expectedPassword { + t.Fatalf("Actual password: %s Expected password: %s", password, test.req.Password) + } + }) + } +} + +func TestUpdateUser_missingDB(t *testing.T) { + dbw := databaseVersionWrapper{} + + req := newdbplugin.UpdateUserRequest{} + resp, err := dbw.UpdateUser(context.Background(), req, false) + if err == nil { + t.Fatalf("err expected, got nil") + } + + expectedConfig := map[string]interface{}(nil) + if !reflect.DeepEqual(resp, expectedConfig) { + t.Fatalf("Actual config: %#v\nExpected config: %#v", resp, expectedConfig) + } +} + +func TestUpdateUser_newDB(t *testing.T) { + type testCase struct { + req newdbplugin.UpdateUserRequest + + updateUserErr error + updateUserCalls int + + expectedResp newdbplugin.UpdateUserResponse + expectErr bool + } + + tests := map[string]testCase{ + "success": { + req: newdbplugin.UpdateUserRequest{ + Username: "existing_user", + }, + updateUserCalls: 1, + expectErr: false, + }, + "error": { + req: newdbplugin.UpdateUserRequest{ + Username: "existing_user", + }, + updateUserErr: fmt.Errorf("test error"), + updateUserCalls: 1, + expectErr: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + newDB := new(mockNewDatabase) + newDB.On("UpdateUser", mock.Anything, mock.Anything). + Return(newdbplugin.UpdateUserResponse{}, test.updateUserErr) + defer newDB.AssertNumberOfCalls(t, "UpdateUser", test.updateUserCalls) + + dbw := databaseVersionWrapper{ + v5: newDB, + } + + _, err := dbw.UpdateUser(context.Background(), test.req, false) + if test.expectErr && err == nil { + t.Fatalf("err expected, got nil") + } + if !test.expectErr && err != nil { + t.Fatalf("no error expected, got: %s", err) + } + }) + } +} + +func TestUpdateUser_legacyDB(t *testing.T) { + type testCase struct { + req newdbplugin.UpdateUserRequest + isRootUser bool + + setCredentialsErr error + setCredentialsCalls int + + rotateRootConfig map[string]interface{} + rotateRootErr error + rotateRootCalls int + + renewUserErr error + renewUserCalls int + + expectedConfig map[string]interface{} + expectErr bool + } + + tests := map[string]testCase{ + "missing changes": { + req: newdbplugin.UpdateUserRequest{ + Username: "existing_user", + }, + isRootUser: false, + + setCredentialsCalls: 0, + rotateRootCalls: 0, + renewUserCalls: 0, + + expectErr: true, + }, + "both password and expiration changes": { + req: newdbplugin.UpdateUserRequest{ + Username: "existing_user", + Password: &newdbplugin.ChangePassword{}, + Expiration: &newdbplugin.ChangeExpiration{}, + }, + isRootUser: false, + + setCredentialsCalls: 0, + rotateRootCalls: 0, + renewUserCalls: 0, + + expectErr: true, + }, + "change password - SetCredentials": { + req: newdbplugin.UpdateUserRequest{ + Username: "existing_user", + Password: &newdbplugin.ChangePassword{ + NewPassword: "newpassowrd", + }, + }, + isRootUser: false, + + setCredentialsErr: nil, + setCredentialsCalls: 1, + rotateRootCalls: 0, + renewUserCalls: 0, + + expectedConfig: nil, + expectErr: false, + }, + "change password - SetCredentials failed": { + req: newdbplugin.UpdateUserRequest{ + Username: "existing_user", + Password: &newdbplugin.ChangePassword{ + NewPassword: "newpassowrd", + }, + }, + isRootUser: false, + + setCredentialsErr: fmt.Errorf("set credentials failed"), + setCredentialsCalls: 1, + rotateRootCalls: 0, + renewUserCalls: 0, + + expectedConfig: nil, + expectErr: true, + }, + "change password - SetCredentials unimplemented but not a root user": { + req: newdbplugin.UpdateUserRequest{ + Username: "existing_user", + Password: &newdbplugin.ChangePassword{ + NewPassword: "newpassowrd", + }, + }, + isRootUser: false, + + setCredentialsErr: status.Error(codes.Unimplemented, "SetCredentials is not implemented"), + setCredentialsCalls: 1, + + rotateRootCalls: 0, + renewUserCalls: 0, + + expectedConfig: nil, + expectErr: true, + }, + "change password - RotateRootCredentials": { + req: newdbplugin.UpdateUserRequest{ + Username: "existing_user", + Password: &newdbplugin.ChangePassword{ + NewPassword: "newpassowrd", + }, + }, + isRootUser: true, + + setCredentialsErr: status.Error(codes.Unimplemented, "SetCredentials is not implemented"), + setCredentialsCalls: 1, + + rotateRootConfig: map[string]interface{}{ + "foo": "bar", + }, + rotateRootCalls: 1, + + renewUserCalls: 0, + + expectedConfig: map[string]interface{}{ + "foo": "bar", + }, + expectErr: false, + }, + "change password - RotateRootCredentials failed": { + req: newdbplugin.UpdateUserRequest{ + Username: "existing_user", + Password: &newdbplugin.ChangePassword{ + NewPassword: "newpassowrd", + }, + }, + isRootUser: true, + + setCredentialsErr: status.Error(codes.Unimplemented, "SetCredentials is not implemented"), + setCredentialsCalls: 1, + + rotateRootErr: fmt.Errorf("rotate root failed"), + rotateRootCalls: 1, + renewUserCalls: 0, + + expectedConfig: nil, + expectErr: true, + }, + + "change expiration": { + req: newdbplugin.UpdateUserRequest{ + Username: "existing_user", + Expiration: &newdbplugin.ChangeExpiration{ + NewExpiration: time.Now(), + }, + }, + isRootUser: false, + + setCredentialsCalls: 0, + rotateRootCalls: 0, + + renewUserErr: nil, + renewUserCalls: 1, + + expectedConfig: nil, + expectErr: false, + }, + "change expiration failed": { + req: newdbplugin.UpdateUserRequest{ + Username: "existing_user", + Expiration: &newdbplugin.ChangeExpiration{ + NewExpiration: time.Now(), + }, + }, + isRootUser: false, + + setCredentialsCalls: 0, + rotateRootCalls: 0, + + renewUserErr: fmt.Errorf("test error"), + renewUserCalls: 1, + + expectedConfig: nil, + expectErr: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + legacyDB := new(mockLegacyDatabase) + legacyDB.On("SetCredentials", mock.Anything, mock.Anything, mock.Anything). + Return("", "", test.setCredentialsErr) + defer legacyDB.AssertNumberOfCalls(t, "SetCredentials", test.setCredentialsCalls) + + legacyDB.On("RotateRootCredentials", mock.Anything, mock.Anything). + Return(test.rotateRootConfig, test.rotateRootErr) + defer legacyDB.AssertNumberOfCalls(t, "RotateRootCredentials", test.rotateRootCalls) + + legacyDB.On("RenewUser", mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(test.renewUserErr) + defer legacyDB.AssertNumberOfCalls(t, "RenewUser", test.renewUserCalls) + + dbw := databaseVersionWrapper{ + v4: legacyDB, + } + + newConfig, err := dbw.UpdateUser(context.Background(), test.req, test.isRootUser) + if test.expectErr && err == nil { + t.Fatalf("err expected, got nil") + } + if !test.expectErr && err != nil { + t.Fatalf("no error expected, got: %s", err) + } + + if !reflect.DeepEqual(newConfig, test.expectedConfig) { + t.Fatalf("Actual config: %#v\nExpected config: %#v", newConfig, test.expectedConfig) + } + }) + } +} + +func TestDeleteUser_missingDB(t *testing.T) { + dbw := databaseVersionWrapper{} + + req := newdbplugin.DeleteUserRequest{} + _, err := dbw.DeleteUser(context.Background(), req) + if err == nil { + t.Fatalf("err expected, got nil") + } +} + +func TestDeleteUser_newDB(t *testing.T) { + type testCase struct { + req newdbplugin.DeleteUserRequest + + deleteUserErr error + deleteUserCalls int + + expectErr bool + } + + tests := map[string]testCase{ + "success": { + req: newdbplugin.DeleteUserRequest{ + Username: "existing_user", + }, + + deleteUserErr: nil, + deleteUserCalls: 1, + + expectErr: false, + }, + "error": { + req: newdbplugin.DeleteUserRequest{ + Username: "existing_user", + }, + + deleteUserErr: fmt.Errorf("test error"), + deleteUserCalls: 1, + + expectErr: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + newDB := new(mockNewDatabase) + newDB.On("DeleteUser", mock.Anything, mock.Anything). + Return(newdbplugin.DeleteUserResponse{}, test.deleteUserErr) + defer newDB.AssertNumberOfCalls(t, "DeleteUser", test.deleteUserCalls) + + dbw := databaseVersionWrapper{ + v5: newDB, + } + + _, err := dbw.DeleteUser(context.Background(), test.req) + if test.expectErr && err == nil { + t.Fatalf("err expected, got nil") + } + if !test.expectErr && err != nil { + t.Fatalf("no error expected, got: %s", err) + } + }) + } +} + +func TestDeleteUser_legacyDB(t *testing.T) { + type testCase struct { + req newdbplugin.DeleteUserRequest + + revokeUserErr error + revokeUserCalls int + + expectErr bool + } + + tests := map[string]testCase{ + "success": { + req: newdbplugin.DeleteUserRequest{ + Username: "existing_user", + }, + + revokeUserErr: nil, + revokeUserCalls: 1, + + expectErr: false, + }, + "error": { + req: newdbplugin.DeleteUserRequest{ + Username: "existing_user", + }, + + revokeUserErr: fmt.Errorf("test error"), + revokeUserCalls: 1, + + expectErr: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + legacyDB := new(mockLegacyDatabase) + legacyDB.On("RevokeUser", mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(test.revokeUserErr) + defer legacyDB.AssertNumberOfCalls(t, "RevokeUser", test.revokeUserCalls) + + dbw := databaseVersionWrapper{ + v4: legacyDB, + } + + _, err := dbw.DeleteUser(context.Background(), test.req) + if test.expectErr && err == nil { + t.Fatalf("err expected, got nil") + } + if !test.expectErr && err != nil { + t.Fatalf("no error expected, got: %s", err) + } + }) + } +} + +type badValue struct{} + +func (badValue) MarshalJSON() ([]byte, error) { + return nil, fmt.Errorf("this value cannot be marshalled to JSON") +} + +var _ logical.Storage = fakeStorage{} + +type fakeStorage struct { + putErr error +} + +func (f fakeStorage) Put(ctx context.Context, entry *logical.StorageEntry) error { + return f.putErr +} + +func (f fakeStorage) List(ctx context.Context, s string) ([]string, error) { + panic("list not implemented") +} +func (f fakeStorage) Get(ctx context.Context, s string) (*logical.StorageEntry, error) { + panic("get not implemented") +} +func (f fakeStorage) Delete(ctx context.Context, s string) error { + panic("delete not implemented") +} + +func TestStoreConfig(t *testing.T) { + type testCase struct { + config *DatabaseConfig + putErr error + expectErr bool + } + + tests := map[string]testCase{ + "bad config": { + config: &DatabaseConfig{ + PluginName: "testplugin", + ConnectionDetails: map[string]interface{}{ + "bad value": badValue{}, + }, + }, + putErr: nil, + expectErr: true, + }, + "storage error": { + config: &DatabaseConfig{ + PluginName: "testplugin", + ConnectionDetails: map[string]interface{}{ + "foo": "bar", + }, + }, + putErr: fmt.Errorf("failed to store config"), + expectErr: true, + }, + "happy path": { + config: &DatabaseConfig{ + PluginName: "testplugin", + ConnectionDetails: map[string]interface{}{ + "foo": "bar", + }, + }, + putErr: nil, + expectErr: false, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + storage := fakeStorage{ + putErr: test.putErr, + } + + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + err := storeConfig(ctx, storage, "testconfig", test.config) + if test.expectErr && err == nil { + t.Fatalf("err expected, got nil") + } + if !test.expectErr && err != nil { + t.Fatalf("no error expected, got: %s", err) + } + }) + } +} diff --git a/builtin/logical/database/versioning_large_test.go b/builtin/logical/database/versioning_large_test.go new file mode 100644 index 0000000000..7bbe040850 --- /dev/null +++ b/builtin/logical/database/versioning_large_test.go @@ -0,0 +1,317 @@ +package database + +// This file contains all "large"/expensive tests. These are running requests against a running backend + +import ( + "context" + "fmt" + "os" + "regexp" + "strings" + "testing" + "time" + + "github.com/hashicorp/vault/api" + "github.com/hashicorp/vault/sdk/helper/consts" + "github.com/hashicorp/vault/sdk/helper/pluginutil" + "github.com/hashicorp/vault/sdk/logical" + "github.com/hashicorp/vault/vault" +) + +func TestPlugin_lifecycle(t *testing.T) { + cluster, sys := getCluster(t) + defer cluster.Cleanup() + + vault.TestAddTestPlugin(t, cluster.Cores[0].Core, "mock-v4-database-plugin", consts.PluginTypeDatabase, "TestBackend_PluginMain_MockV4", []string{}, "") + vault.TestAddTestPlugin(t, cluster.Cores[0].Core, "mock-v5-database-plugin", consts.PluginTypeDatabase, "TestBackend_PluginMain_MockV5", []string{}, "") + + config := logical.TestBackendConfig() + config.StorageView = &logical.InmemStorage{} + config.System = sys + lb, err := Factory(context.Background(), config) + if err != nil { + t.Fatal(err) + } + b, ok := lb.(*databaseBackend) + if !ok { + t.Fatal("could not convert to database backend") + } + defer b.Cleanup(context.Background()) + + type testCase struct { + dbName string + dbType string + configData map[string]interface{} + assertDynamicUsername stringAssertion + assertDynamicPassword stringAssertion + } + + tests := map[string]testCase{ + "v4": { + dbName: "mockv4", + dbType: "mock-v4-database-plugin", + configData: map[string]interface{}{ + "name": "mockv4", + "plugin_name": "mock-v4-database-plugin", + "connection_url": "sample_connection_url", + "verify_connection": true, + "allowed_roles": []string{"*"}, + "username": "mockv4-user", + "password": "mysecurepassword", + }, + assertDynamicUsername: assertStringPrefix("mockv4_user_"), + assertDynamicPassword: assertStringPrefix("mockv4_"), + }, + "v5": { + dbName: "mockv5", + dbType: "mock-v5-database-plugin", + configData: map[string]interface{}{ + "connection_url": "sample_connection_url", + "plugin_name": "mock-v5-database-plugin", + "verify_connection": true, + "allowed_roles": []string{"*"}, + "name": "mockv5", + "username": "mockv5-user", + "password": "mysecurepassword", + }, + assertDynamicUsername: assertStringPrefix("mockv5_user_"), + assertDynamicPassword: assertStringRegex("^[a-zA-Z0-9-]{20}"), + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + cleanupReqs := []*logical.Request{} + defer cleanup(t, b, cleanupReqs) + + // ///////////////////////////////////////////////////////////////// + // Configure + req := &logical.Request{ + Operation: logical.CreateOperation, + Path: fmt.Sprintf("config/%s", test.dbName), + Storage: config.StorageView, + Data: test.configData, + } + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + resp, err := b.HandleRequest(ctx, req) + assertErrIsNil(t, err) + assertRespHasNoErr(t, resp) + assertNoRespData(t, resp) + + cleanupReqs = append(cleanupReqs, &logical.Request{ + Operation: logical.DeleteOperation, + Path: fmt.Sprintf("config/%s", test.dbName), + Storage: config.StorageView, + }) + + // ///////////////////////////////////////////////////////////////// + // Rotate root credentials + req = &logical.Request{ + Operation: logical.UpdateOperation, + Path: fmt.Sprintf("rotate-root/%s", test.dbName), + Storage: config.StorageView, + } + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + resp, err = b.HandleRequest(ctx, req) + assertErrIsNil(t, err) + assertRespHasNoErr(t, resp) + assertNoRespData(t, resp) + + // ///////////////////////////////////////////////////////////////// + // Dynamic credentials + + // Create role + dynamicRoleName := "dynamic-role" + req = &logical.Request{ + Operation: logical.UpdateOperation, + Path: fmt.Sprintf("roles/%s", dynamicRoleName), + Storage: config.StorageView, + Data: map[string]interface{}{ + "db_name": test.dbName, + "default_ttl": "5s", + "max_ttl": "1m", + }, + } + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + resp, err = b.HandleRequest(ctx, req) + assertErrIsNil(t, err) + assertRespHasNoErr(t, resp) + assertNoRespData(t, resp) + + cleanupReqs = append(cleanupReqs, &logical.Request{ + Operation: logical.DeleteOperation, + Path: fmt.Sprintf("roles/%s", dynamicRoleName), + Storage: config.StorageView, + }) + + // Generate credentials + req = &logical.Request{ + Operation: logical.ReadOperation, + Path: fmt.Sprintf("creds/%s", dynamicRoleName), + Storage: config.StorageView, + } + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + resp, err = b.HandleRequest(ctx, req) + assertErrIsNil(t, err) + assertRespHasNoErr(t, resp) + assertRespHasData(t, resp) + + // TODO: Figure out how to make a call to the cluster that gives back a lease ID + // And also rotates the secret out after its TTL + + // ///////////////////////////////////////////////////////////////// + // Static credentials + + // Create static role + staticRoleName := "static-role" + req = &logical.Request{ + Operation: logical.UpdateOperation, + Path: fmt.Sprintf("static-roles/%s", staticRoleName), + Storage: config.StorageView, + Data: map[string]interface{}{ + "db_name": test.dbName, + "username": "static-username", + "rotation_period": "5", + }, + } + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + resp, err = b.HandleRequest(ctx, req) + assertErrIsNil(t, err) + assertRespHasNoErr(t, resp) + assertNoRespData(t, resp) + + cleanupReqs = append(cleanupReqs, &logical.Request{ + Operation: logical.DeleteOperation, + Path: fmt.Sprintf("static-roles/%s", staticRoleName), + Storage: config.StorageView, + }) + + // Get credentials + req = &logical.Request{ + Operation: logical.ReadOperation, + Path: fmt.Sprintf("static-creds/%s", staticRoleName), + Storage: config.StorageView, + } + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + resp, err = b.HandleRequest(ctx, req) + assertErrIsNil(t, err) + assertRespHasNoErr(t, resp) + assertRespHasData(t, resp) + }) + } +} + +func cleanup(t *testing.T, b *databaseBackend, reqs []*logical.Request) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // Go in stack order so it works similar to defer + for i := len(reqs) - 1; i >= 0; i-- { + req := reqs[i] + resp, err := b.HandleRequest(ctx, req) + if err != nil { + t.Fatalf("Error cleaning up: %s", err) + } + if resp != nil && resp.IsError() { + t.Fatalf("Error cleaning up: %s", resp.Error()) + } + } +} + +func TestBackend_PluginMain_MockV4(t *testing.T) { + if os.Getenv(pluginutil.PluginUnwrapTokenEnv) == "" { + return + } + + caPEM := os.Getenv(pluginutil.PluginCACertPEMEnv) + if caPEM == "" { + t.Fatal("CA cert not passed in") + } + + args := []string{"--ca-cert=" + caPEM} + + apiClientMeta := &api.PluginAPIClientMeta{} + flags := apiClientMeta.FlagSet() + flags.Parse(args) + + RunV4(apiClientMeta.GetTLSConfig()) +} + +func TestBackend_PluginMain_MockV5(t *testing.T) { + if os.Getenv(pluginutil.PluginUnwrapTokenEnv) == "" { + return + } + + caPEM := os.Getenv(pluginutil.PluginCACertPEMEnv) + if caPEM == "" { + t.Fatal("CA cert not passed in") + } + + args := []string{"--ca-cert=" + caPEM} + + apiClientMeta := &api.PluginAPIClientMeta{} + flags := apiClientMeta.FlagSet() + flags.Parse(args) + + RunV5(apiClientMeta.GetTLSConfig()) +} + +func assertNoRespData(t *testing.T, resp *logical.Response) { + t.Helper() + if resp != nil && len(resp.Data) > 0 { + t.Fatalf("Response had data when none was expected: %#v", resp.Data) + } +} + +func assertRespHasData(t *testing.T, resp *logical.Response) { + t.Helper() + if resp == nil || len(resp.Data) == 0 { + t.Fatalf("Response didn't have any data when some was expected") + } +} + +type stringAssertion func(t *testing.T, str string) + +func assertStringPrefix(expectedPrefix string) stringAssertion { + return func(t *testing.T, str string) { + t.Helper() + if !strings.HasPrefix(str, expectedPrefix) { + t.Fatalf("Missing prefix '%s': Actual: '%s'", expectedPrefix, str) + } + } +} + +func assertStringRegex(expectedRegex string) stringAssertion { + re := regexp.MustCompile(expectedRegex) + return func(t *testing.T, str string) { + if !re.MatchString(str) { + t.Fatalf("Actual: '%s' did not match regexp '%s'", str, expectedRegex) + } + } +} + +func assertRespHasNoErr(t *testing.T, resp *logical.Response) { + t.Helper() + if resp != nil && resp.IsError() { + t.Fatalf("response is error: %#v\n", resp) + } +} + +func assertErrIsNil(t *testing.T, err error) { + t.Helper() + if err != nil { + t.Fatalf("No error expected, got: %s", err) + } +} diff --git a/go.mod b/go.mod index 3214adc893..899a0a6c3b 100644 --- a/go.mod +++ b/go.mod @@ -30,11 +30,9 @@ require ( github.com/chrismalek/oktasdk-go v0.0.0-20181212195951-3430665dfaa0 github.com/client9/misspell v0.3.4 github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c - github.com/containerd/containerd v1.3.4 // indirect github.com/coreos/go-semver v0.2.0 github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc github.com/dnaeon/go-vcr v1.0.1 // indirect - github.com/docker/distribution v2.7.1+incompatible // indirect github.com/docker/docker v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible github.com/docker/go-connections v0.4.0 github.com/dsnet/compress v0.0.1 // indirect @@ -123,9 +121,9 @@ require ( github.com/nwaples/rardecode v1.1.0 // indirect github.com/oklog/run v1.0.0 github.com/okta/okta-sdk-golang/v2 v2.0.0 + github.com/opencontainers/runc v1.0.0-rc9 // indirect github.com/oracle/oci-go-sdk v12.5.0+incompatible github.com/ory/dockertest v3.3.5+incompatible - github.com/ory/dockertest/v3 v3.6.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect github.com/pkg/errors v0.9.1 @@ -140,7 +138,7 @@ require ( github.com/sasha-s/go-deadlock v0.2.0 github.com/sethvargo/go-limiter v0.3.0 github.com/shirou/gopsutil v2.20.6-0.20200630091542-01afd763e6c0+incompatible - github.com/stretchr/testify v1.5.1 + github.com/stretchr/testify v1.6.1 github.com/tidwall/pretty v1.0.1 // indirect github.com/ulikunitz/xz v0.5.7 // indirect github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect diff --git a/go.sum b/go.sum index b865628ae1..275a3a4a1d 100644 --- a/go.sum +++ b/go.sum @@ -17,12 +17,15 @@ cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbf cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.6.0 h1:ajp/DjpiCHO71SyIhwb83YsUGAyWuzVvMko+9xCsJLw= cloud.google.com/go/bigquery v1.6.0/go.mod h1:hyFDG0qSGdHNz8Q6nDN8rYIkld0q/+5uBZaelxiDLfE= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/spanner v1.5.1 h1:dWyj10TLlaxH2No6+tXsSCaq9oWgrRbXy1N3x/bhMGU= cloud.google.com/go/spanner v1.5.1/go.mod h1:e1+8M6PF3ntV9Xr57X2Gf+UhylXXYF6gI4WRZ1kfu2A= @@ -61,6 +64,7 @@ github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSW github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc= github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= github.com/Azure/go-autorest/autorest/to v0.3.0 h1:zebkZaadz7+wIQYgC7GXaz3Wb28yKYfVkkBKwc38VF8= github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA= @@ -101,6 +105,7 @@ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWX github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -132,6 +137,7 @@ github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzs github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.30.27 h1:9gPjZWVDSoQrBO2AvqrWObS6KAZByfEJxQoCYo4ZfK0= github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -140,7 +146,9 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= +github.com/bitly/go-hostpool v0.1.0 h1:XKmsF6k5el6xHG3WPJ8U0Ku/ye7njX7W81Ng7O2ioR0= github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw= +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/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= @@ -172,10 +180,13 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381 h1:rdRS5BT13Iae9ssvcslol66gfOOXjaLYwqerEn/cl9s= github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381/go.mod h1:e5+USP2j8Le2M0Jo3qKPFnNhuo1wueU4nWHCXBOfQ14= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c h1:2zRrJWIt/f9c9HhNHAgrRgq0San5gRRUJTBXLkchal0= github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= @@ -183,7 +194,6 @@ github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX github.com/containerd/containerd v1.3.4 h1:3o0smo5SKY7H6AJCmJhsnCjR2/V2T8VmiHt7seN2/kI= github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20200709052629-daa8e1ccc0bc/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe h1:PEmIrUvwG9Yyv+0WKZqjXfSFDeZjs/q15g0m08BYS9k= github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= @@ -221,6 +231,7 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= +github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= @@ -239,6 +250,7 @@ github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdf github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 h1:2MIhn2R6oXQbgW5yHfS+d6YqyMfXiu2L55rFZC4UD/M= github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74/go.mod h1:UqXY1lYT/ERa4OEAywUqdok1T4RCRdArkhic1Opuavo= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= @@ -258,7 +270,9 @@ github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/frankban/quicktest v1.4.0/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ= github.com/frankban/quicktest v1.4.1/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ= +github.com/frankban/quicktest v1.10.0 h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8gVkE= github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa h1:RDBNVkRviHZtvDvId8XSGPu3rmpmSe+wKRcEWNgsfWU= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= @@ -283,6 +297,7 @@ github.com/go-ldap/ldap/v3 v3.1.10 h1:7WsKqasmPThNvdl0Q5GPpbTDD/ZD98CfuawrMIuh7q github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= @@ -322,6 +337,7 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -342,6 +358,7 @@ github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8l github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -359,6 +376,7 @@ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= 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/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -374,24 +392,32 @@ github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 h1:f0n1xnMSmBLzVfsMMvriDyA75NB/oBgILX2GcHXIQzY= github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ= github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI= github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.6.2/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= @@ -400,6 +426,7 @@ github.com/hashicorp/consul-template v0.25.1/go.mod h1:/vUsrJvDuuQHcxEw0zik+YXTS github.com/hashicorp/consul/api v1.4.0 h1:jfESivXnO5uLdH650JU/6AnjRoHrLhULq0FnC3Kp9EY= github.com/hashicorp/consul/api v1.4.0/go.mod h1:xc8u05kyMa3Wjr9eEAsIAo3dg8+LywT5E/Cl7cNS5nU= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.4.0 h1:zBtCfKJZcJDBvSCkQJch4ulp59m1rATFLKwNo/LYY30= github.com/hashicorp/consul/sdk v0.4.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -408,6 +435,7 @@ github.com/hashicorp/go-bindata v3.0.8-0.20180209072458-bf7910af8997+incompatibl github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-gatedio v0.5.0 h1:Jm1X5yP4yCqqWj5L1TgW7iZwCVPGtVc+mro5r/XX7Tg= github.com/hashicorp/go-gatedio v0.5.0/go.mod h1:Lr3t8L6IyxD3DAeaUxGcgl2JnRUpWMCsmBl4Omu/2t4= github.com/hashicorp/go-gcp-common v0.5.0/go.mod h1:IDGUI2N/OS3PiU4qZcXJeWKPI6O/9Y8hOrbSiMcqyYw= github.com/hashicorp/go-gcp-common v0.6.0 h1:m1X+DK003bj4WEjqOnv+Csepb3zpfN/bidboUeUSj68= @@ -472,6 +500,7 @@ github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.1.4 h1:gkyML/r71w3FL8gUi74Vk76avkj/9lYAY9lvg0OcoGs= github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/nomad/api v0.0.0-20191220223628-edc62acd919d h1:BXqsASWhyiAiEVm6FcltF0dg8XvoookQwmpHn8lstu8= github.com/hashicorp/nomad/api v0.0.0-20191220223628-edc62acd919d/go.mod h1:WKCL+tLVhN1D+APwH3JiTRZoxcdwRk86bWu1LVCUPaE= @@ -531,6 +560,7 @@ github.com/hashicorp/vault-plugin-secrets-openldap v0.1.5/go.mod h1:NM+5N+URHHg8 github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huaweicloud/golangsdk v0.0.0-20200304081349-45ec0797f2a4/go.mod h1:WQBcHRNX9shz3928lWEvstQJtAtYI7ks6XlgtRT9Tcw= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -538,6 +568,7 @@ github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb v0.0.0-20190411212539-d24b7ba8c4c4 h1:3K3KcD4S6/Y2hevi70EzUTNKOS3cryQyhUnkjE6Tz0w= github.com/influxdata/influxdb v0.0.0-20190411212539-d24b7ba8c4c4/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= +github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc= github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= github.com/jackc/pgx v3.3.0+incompatible h1:Wa90/+qsITBAPkAZjiByeIGHFcj3Ztu+VzrrIpHjL90= github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= @@ -564,6 +595,7 @@ github.com/jefferai/jsonx v1.0.0/go.mod h1:OGmqmi2tTeI/PS+qQfBDToLHHJIy/RMp24fPo github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f h1:ENpDacvnr8faw5ugQmEF1QYk+f/Y9lXFvuYmRxykago= github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f/go.mod h1:KDSfL7qe5ZfQqvlDMkVjCztbmcpp/c8M77vhQP8ZPvk= @@ -577,6 +609,7 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kelseyhightower/envconfig v1.3.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= @@ -590,7 +623,6 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -602,12 +634,12 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lestrrat-go/jwx v0.9.0/go.mod h1:iEoxlYfZjvoGpuWwxUz+eR5e6KTJGsaRcy/YNA/UnBk= -github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11 h1:YFh+sjyJTMQSYjKwM4dFKhJPJC/wfo98tPUc17HdoYw= github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11/go.mod h1:Ah2dBMoxZEqk118as2T4u4fjfXarE0pPnMJaArZQZsI= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -631,6 +663,7 @@ github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1 github.com/michaelklishin/rabbit-hole v0.0.0-20191008194146-93d9988f0cd5 h1:uA3b4GgZMZxAJsTkd+CVQ85b7KBlD7HLpd/FfTNlGN0= github.com/michaelklishin/rabbit-hole v0.0.0-20191008194146-93d9988f0cd5/go.mod h1:+pmbihVqjC3GPdfWv1V2TnRSuVvwrWLKfEP/MZVB/Wc= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.15 h1:CSSIDtllwGLMoA6zjdKnaE6Tx6eVUxQ29LUgGetiDCI= github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -667,6 +700,7 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mongodb/go-client-mongodb-atlas v0.1.2 h1:qmUme1TlQBPZupmXMnpD8DxnfGXLVGs3w+0Z17HBiSA= github.com/mongodb/go-client-mongodb-atlas v0.1.2/go.mod h1:LS8O0YLkA+sbtOb3fZLF10yY3tJM+1xATXMJ3oU35LU= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mwielbut/pointy v1.1.0 h1:U5/YEfoIkaGCHv0St3CgjduqXID4FNRoyZgLM1kY9vg= github.com/mwielbut/pointy v1.1.0/go.mod h1:MvvO+uMFj9T5DMda33HlvogsFBX7pWWKAkFIn4teYwY= @@ -690,10 +724,12 @@ github.com/olekukonko/tablewriter v0.0.0-20180130162743-b8a9be070da4/go.mod h1:v github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -714,9 +750,10 @@ github.com/oracle/oci-go-sdk v12.5.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35uk github.com/ory/dockertest v3.3.4+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= -github.com/ory/dockertest/v3 v3.6.0/go.mod h1:4ZOpj8qBUmh8fcBSVzkH2bws2s91JdGvHUqan4GHEuQ= +github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 h1:CXwSGu/LYmbjEab5aMCs5usQRVBGThelUKBNnoSOuso= github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2/go.mod h1:L3UMQOThbttwfYRNFOWLLVXMhk5Lkio4GGOtw5UrxS0= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= @@ -792,11 +829,13 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/conswriter v0.0.0-20180208195008-f5ae3917a627/go.mod h1:7zjs06qF79/FKAJpBvFx3P8Ww4UTIMAe+lpNXDHziac= github.com/sean-/pager v0.0.0-20180208200047-666be9bf53b5/go.mod h1:BeybITEsBEg6qbIiqJ6/Bqeq25bCLbL7YFmpaFfJDuM= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sethvargo/go-limiter v0.3.0 h1:yRMc+Qs2yqw6YJp6UxrO2iUs6DOSq4zcnljbB7/rMns= github.com/sethvargo/go-limiter v0.3.0/go.mod h1:C0kbSFbiriE5k2FFOe18M1YZbAR2Fiwf72uGu0CXCcU= github.com/shirou/gopsutil v2.20.6-0.20200630091542-01afd763e6c0+incompatible h1:IYOqH6sML3rQGNVEQ5foLtpDt4TeW8PIUBuI9f8itkI= github.com/shirou/gopsutil v2.20.6-0.20200630091542-01afd763e6c0+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -805,9 +844,12 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/smartystreets/assertions v0.0.0-20180725160413-e900ae048470/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= @@ -819,23 +861,30 @@ github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tL github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/square/go-jose v2.4.1+incompatible/go.mod h1:7MxpAF/1WTVUu8Am+T5kNy+t0902CaLWM4Z745MkOa8= github.com/square/go-jose/v3 v3.0.0-20200225220504-708a9fe87ddc/go.mod h1:JbpHhNyeVc538vtj/ECJ3gPYm1VEitNjsLhm4eJQQbg= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94 h1:0ngsPmuP6XIjiFRNFYlvKwSr5zff2v+uPHaffZ6/M4k= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tencentcloud/tencentcloud-sdk-go v3.0.171+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4= +github.com/tidwall/pretty v1.0.1 h1:WE4RBSZ1x6McVVC8S/Md+Qse8YUv6HRObAx6ke00NY8= github.com/tidwall/pretty v1.0.1/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +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/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= @@ -852,6 +901,7 @@ github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -879,6 +929,7 @@ go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.14.1 h1:nYDKopTbvAPq/NrUVZwT15y2lpROBiLLyoRTbXOYWOo= @@ -959,7 +1010,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1024,7 +1074,6 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200121082415-34d275377bf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1193,12 +1242,15 @@ google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= @@ -1208,6 +1260,7 @@ gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/jcmturner/goidentity.v3 v3.0.0 h1:1duIyWiTaYvVx3YX2CYtpJbUFd7/UuPYCfgXtQ3VTbI= gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4= +gopkg.in/ldap.v3 v3.0.3 h1:YKRHW/2sIl05JsCtx/5ZuUueFuJyoj/6+DGXe3wp6ro= gopkg.in/ldap.v3 v3.0.3/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= @@ -1219,15 +1272,18 @@ gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76 gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1251,4 +1307,5 @@ layeh.com/radius v0.0.0-20190322222518-890bc1058917/go.mod h1:fywZKyu//X7iRzaxLg rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/vault/plugin_catalog.go b/vault/plugin_catalog.go index 78c5e80523..47f7c6f8bf 100644 --- a/vault/plugin_catalog.go +++ b/vault/plugin_catalog.go @@ -10,11 +10,11 @@ import ( "strings" "sync" + "github.com/hashicorp/errwrap" log "github.com/hashicorp/go-hclog" multierror "github.com/hashicorp/go-multierror" - - "github.com/hashicorp/errwrap" "github.com/hashicorp/vault/sdk/database/dbplugin" + "github.com/hashicorp/vault/sdk/database/newdbplugin" "github.com/hashicorp/vault/sdk/helper/consts" "github.com/hashicorp/vault/sdk/helper/jsonutil" "github.com/hashicorp/vault/sdk/helper/pluginutil" @@ -64,46 +64,70 @@ func (c *Core) setupPluginCatalog(ctx context.Context) error { // type. It will first attempt to run as a database plugin then a backend // plugin. Both of these will be run in metadata mode. func (c *PluginCatalog) getPluginTypeFromUnknown(ctx context.Context, logger log.Logger, plugin *pluginutil.PluginRunner) (consts.PluginType, error) { + merr := &multierror.Error{} + err := isDatabasePlugin(ctx, plugin) + if err == nil { + return consts.PluginTypeDatabase, nil + } + merr = multierror.Append(merr, err) - { - // Attempt to run as database plugin - client, err := dbplugin.NewPluginClient(ctx, nil, plugin, log.NewNullLogger(), true) - if err == nil { - // Close the client and cleanup the plugin process - client.Close() - return consts.PluginTypeDatabase, nil - } else { - logger.Warn(fmt.Sprintf("received %s attempting as db plugin, attempting as auth/secret plugin", err)) + // Attempt to run as backend plugin + client, err := backendplugin.NewPluginClient(ctx, nil, plugin, log.NewNullLogger(), true) + if err == nil { + err := client.Setup(ctx, &logical.BackendConfig{}) + if err != nil { + return consts.PluginTypeUnknown, err } + + backendType := client.Type() + client.Cleanup(ctx) + + switch backendType { + case logical.TypeCredential: + return consts.PluginTypeCredential, nil + case logical.TypeLogical: + return consts.PluginTypeSecrets, nil + } + } else { + merr = multierror.Append(merr, err) } - { - // Attempt to run as backend plugin - client, err := backendplugin.NewPluginClient(ctx, nil, plugin, log.NewNullLogger(), true) - if err == nil { - err := client.Setup(ctx, &logical.BackendConfig{}) - if err != nil { - return consts.PluginTypeUnknown, err - } - - backendType := client.Type() - client.Cleanup(ctx) - - switch backendType { - case logical.TypeCredential: - return consts.PluginTypeCredential, nil - case logical.TypeLogical: - return consts.PluginTypeSecrets, nil - } - logger.Warn(fmt.Sprintf("unknown backendType of %s", backendType)) - } else { - logger.Warn(fmt.Sprintf("received %s attempting as an auth/secret plugin, continuing", err)) - } + if client == nil || client.Type() == logical.TypeUnknown { + logger.Warn("unknown plugin type", + "plugin name", plugin.Name, + "error", merr.Error()) + } else { + logger.Warn("unsupported plugin type", + "plugin name", plugin.Name, + "plugin type", client.Type().String(), + "error", merr.Error()) } return consts.PluginTypeUnknown, nil } +func isDatabasePlugin(ctx context.Context, plugin *pluginutil.PluginRunner) error { + merr := &multierror.Error{} + // Attempt to run as database V5 plugin + v5Client, err := newdbplugin.NewPluginClient(ctx, nil, plugin, log.NewNullLogger(), true) + if err == nil { + // Close the client and cleanup the plugin process + v5Client.Close() + return nil + } + merr = multierror.Append(merr, fmt.Errorf("failed to load plugin as database v5: %w", err)) + + v4Client, err := dbplugin.NewPluginClient(ctx, nil, plugin, log.NewNullLogger(), true) + if err == nil { + // Close the client and cleanup the plugin process + v4Client.Close() + return nil + } + merr = multierror.Append(merr, fmt.Errorf("failed to load plugin as database v4: %w", err)) + + return merr.ErrorOrNil() +} + // UpdatePlugins will loop over all the plugins of unknown type and attempt to // upgrade them to typed plugins func (c *PluginCatalog) UpgradePlugins(ctx context.Context, logger log.Logger) error { diff --git a/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/database.go b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/database.go new file mode 100644 index 0000000000..4ce7268271 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/database.go @@ -0,0 +1,178 @@ +package newdbplugin + +import ( + "context" + "time" +) + +// Database to manipulate users within an external system (typically a database). +type Database interface { + // Initialize the database plugin. This is the equivalent of a constructor for the + // database object itself. + Initialize(ctx context.Context, req InitializeRequest) (InitializeResponse, error) + + // NewUser creates a new user within the database. This user is temporary in that it + // will exist until the TTL expires. + NewUser(ctx context.Context, req NewUserRequest) (NewUserResponse, error) + + // UpdateUser updates an existing user within the database. + UpdateUser(ctx context.Context, req UpdateUserRequest) (UpdateUserResponse, error) + + // DeleteUser from the database. This should not error if the user didn't + // exist prior to this call. + DeleteUser(ctx context.Context, req DeleteUserRequest) (DeleteUserResponse, error) + + // Type returns the Name for the particular database backend implementation. + // This type name is usually set as a constant within the database backend + // implementation, e.g. "mysql" for the MySQL database backend. This is used + // for things like metrics and logging. No behavior is switched on this. + Type() (string, error) + + // Close attempts to close the underlying database connection that was + // established by the backend. + Close() error +} + +// /////////////////////////////////////////////////////////////////////////// +// Database Request & Response Objects +// These request and response objects are *not* protobuf types because gRPC does not +// support all types that we need in a nice way. For instance, gRPC does not support +// map[string]interface{}. It does have an `Any` type, but converting it to a map +// requires extensive use of reflection and knowing what types to support ahead of +// time. Instead these types are made as user-friendly as possible so the conversion +// between protobuf types and request/response objects is handled by Vault developers +// rather than needing to be handled by external plugin developers. +// /////////////////////////////////////////////////////////////////////////// + +// /////////////////////////////////////////////////////// +// Initialize() +// /////////////////////////////////////////////////////// + +// InitializeRequest contains all information needed to initialize a database plugin. +type InitializeRequest struct { + // Config to initialize the database with. This can include things like connection details, + // a "root" username & password, etc. This will not include all configuration items specified + // when configuring the database. Some values will be stripped out by the database engine + // prior to being passed to the plugin. + Config map[string]interface{} + + // VerifyConnection during initialization. If true, a connection should be made to the + // database to verify the connection can be made. If false, no connection should be made + // on initialization. + VerifyConnection bool +} + +// InitializeResponse returns any information Vault needs to know after initializing +// a database plugin. +type InitializeResponse struct { + // Config that should be saved in Vault. This may differ from the config in the request, + // but should contain everything required to Initialize the database. + // REQUIRED in order to save the configuration into Vault after initialization + Config map[string]interface{} +} + +// /////////////////////////////////////////////////////// +// NewUser() +// /////////////////////////////////////////////////////// + +// NewUserRequest request a new user is created +type NewUserRequest struct { + // UsernameConfig is metadata that can be used to generate a username + // within the database plugin + UsernameConfig UsernameMetadata + + // Statements is an ordered list of commands to run within the database when + // creating a new user. This frequently includes permissions to give the + // user or similar actions. + Statements Statements + + // RollbackStatements is an ordered list of commands to run within the database + // if the new user creation process fails. + RollbackStatements Statements + + // Password credentials to use when creating the user + Password string + + // Expiration of the user. Not all database plugins will support this. + Expiration time.Time +} + +// UsernameMetadata is metadata the database plugin can use to generate a username +type UsernameMetadata struct { + DisplayName string + RoleName string +} + +// NewUserResponse returns any information Vault needs to know after creating a new user. +type NewUserResponse struct { + // Username of the user created within the database. + // REQUIRED so Vault knows the name of the user that was created + Username string +} + +// /////////////////////////////////////////////////////// +// UpdateUser() +// /////////////////////////////////////////////////////// + +type UpdateUserRequest struct { + // Username to make changes to. + Username string + + // Password indicates the new password to change to. + // If nil, no change is requested. + Password *ChangePassword + + // Expiration indicates the new expiration date to change to. + // If nil, no change is requested. + Expiration *ChangeExpiration +} + +// ChangePassword of a given user +type ChangePassword struct { + // NewPassword for the user + NewPassword string + + // Statements is an ordered list of commands to run within the database + // when changing the user's password. + Statements Statements +} + +// ChangeExpiration of a given user +type ChangeExpiration struct { + // NewExpiration of the user + NewExpiration time.Time + + // Statements is an ordered list of commands to run within the database + // when changing the user's expiration. + Statements Statements +} + +type UpdateUserResponse struct{} + +// /////////////////////////////////////////////////////// +// DeleteUser() +// /////////////////////////////////////////////////////// + +type DeleteUserRequest struct { + // Username to delete from the database + Username string + + // Statements is an ordered list of commands to run within the database + // when deleting a user. + Statements Statements +} + +type DeleteUserResponse struct{} + +// /////////////////////////////////////////////////////// +// Used across multiple functions +// /////////////////////////////////////////////////////// + +// Statements wraps a collection of statements to run in a database when an +// operation is performed (create, update, etc.). This is a struct rather than +// a string slice so we can easily add more information to this in the future. +type Statements struct { + // Commands is an ordered list of commands to execute in the database. + // These commands may include templated fields such as {{username}} and {{password}} + Commands []string +} diff --git a/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/grpc_client.go b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/grpc_client.go new file mode 100644 index 0000000000..2d51d2e04d --- /dev/null +++ b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/grpc_client.go @@ -0,0 +1,254 @@ +package newdbplugin + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/golang/protobuf/ptypes" + "github.com/hashicorp/vault/sdk/database/newdbplugin/proto" + "github.com/hashicorp/vault/sdk/helper/pluginutil" +) + +var ( + _ Database = gRPCClient{} + + ErrPluginShutdown = errors.New("plugin shutdown") +) + +type gRPCClient struct { + client proto.DatabaseClient + doneCtx context.Context +} + +func (c gRPCClient) Initialize(ctx context.Context, req InitializeRequest) (InitializeResponse, error) { + rpcReq, err := initReqToProto(req) + if err != nil { + return InitializeResponse{}, err + } + + rpcResp, err := c.client.Initialize(ctx, rpcReq) + if err != nil { + return InitializeResponse{}, fmt.Errorf("unable to initialize: %s", err.Error()) + } + + return initRespFromProto(rpcResp) +} + +func initReqToProto(req InitializeRequest) (*proto.InitializeRequest, error) { + config, err := mapToStruct(req.Config) + if err != nil { + return nil, fmt.Errorf("unable to marshal config: %w", err) + } + + rpcReq := &proto.InitializeRequest{ + ConfigData: config, + VerifyConnection: req.VerifyConnection, + } + return rpcReq, nil +} + +func initRespFromProto(rpcResp *proto.InitializeResponse) (InitializeResponse, error) { + newConfig := structToMap(rpcResp.GetConfigData()) + + resp := InitializeResponse{ + Config: newConfig, + } + return resp, nil +} + +func (c gRPCClient) NewUser(ctx context.Context, req NewUserRequest) (NewUserResponse, error) { + ctx, cancel := context.WithCancel(ctx) + quitCh := pluginutil.CtxCancelIfCanceled(cancel, c.doneCtx) + defer close(quitCh) + defer cancel() + + rpcReq, err := newUserReqToProto(req) + if err != nil { + return NewUserResponse{}, err + } + + rpcResp, err := c.client.NewUser(ctx, rpcReq) + if err != nil { + if c.doneCtx.Err() != nil { + return NewUserResponse{}, ErrPluginShutdown + } + return NewUserResponse{}, fmt.Errorf("unable to create new user: %w", err) + } + + return newUserRespFromProto(rpcResp) +} + +func newUserReqToProto(req NewUserRequest) (*proto.NewUserRequest, error) { + if req.Password == "" { + return nil, fmt.Errorf("missing password") + } + + expiration, err := ptypes.TimestampProto(req.Expiration) + if err != nil { + return nil, fmt.Errorf("unable to marshal expiration date: %w", err) + } + + rpcReq := &proto.NewUserRequest{ + UsernameConfig: &proto.UsernameConfig{ + DisplayName: req.UsernameConfig.DisplayName, + RoleName: req.UsernameConfig.RoleName, + }, + Password: req.Password, + Expiration: expiration, + Statements: &proto.Statements{ + Commands: req.Statements.Commands, + }, + RollbackStatements: &proto.Statements{ + Commands: req.RollbackStatements.Commands, + }, + } + return rpcReq, nil +} + +func newUserRespFromProto(rpcResp *proto.NewUserResponse) (NewUserResponse, error) { + resp := NewUserResponse{ + Username: rpcResp.GetUsername(), + } + return resp, nil +} + +func (c gRPCClient) UpdateUser(ctx context.Context, req UpdateUserRequest) (UpdateUserResponse, error) { + rpcReq, err := updateUserReqToProto(req) + if err != nil { + return UpdateUserResponse{}, err + } + + rpcResp, err := c.client.UpdateUser(ctx, rpcReq) + if err != nil { + if c.doneCtx.Err() != nil { + return UpdateUserResponse{}, ErrPluginShutdown + } + + return UpdateUserResponse{}, fmt.Errorf("unable to update user: %w", err) + } + + return updateUserRespFromProto(rpcResp) +} + +func updateUserReqToProto(req UpdateUserRequest) (*proto.UpdateUserRequest, error) { + if req.Username == "" { + return nil, fmt.Errorf("missing username") + } + + if (req.Password == nil || req.Password.NewPassword == "") && + (req.Expiration == nil || req.Expiration.NewExpiration.IsZero()) { + return nil, fmt.Errorf("missing changes") + } + + expiration, err := expirationToProto(req.Expiration) + if err != nil { + return nil, fmt.Errorf("unable to parse new expiration date: %w", err) + } + + var password *proto.ChangePassword + if req.Password != nil && req.Password.NewPassword != "" { + password = &proto.ChangePassword{ + NewPassword: req.Password.NewPassword, + Statements: &proto.Statements{ + Commands: req.Password.Statements.Commands, + }, + } + } + + rpcReq := &proto.UpdateUserRequest{ + Username: req.Username, + Password: password, + Expiration: expiration, + } + return rpcReq, nil +} + +func updateUserRespFromProto(rpcResp *proto.UpdateUserResponse) (UpdateUserResponse, error) { + // Placeholder for future conversion if data is returned + return UpdateUserResponse{}, nil +} + +func expirationToProto(exp *ChangeExpiration) (*proto.ChangeExpiration, error) { + if exp == nil { + return nil, nil + } + + expiration, err := ptypes.TimestampProto(exp.NewExpiration) + if err != nil { + return nil, err + } + + changeExp := &proto.ChangeExpiration{ + NewExpiration: expiration, + Statements: &proto.Statements{ + Commands: exp.Statements.Commands, + }, + } + return changeExp, nil +} + +func (c gRPCClient) DeleteUser(ctx context.Context, req DeleteUserRequest) (DeleteUserResponse, error) { + rpcReq, err := deleteUserReqToProto(req) + if err != nil { + return DeleteUserResponse{}, err + } + + rpcResp, err := c.client.DeleteUser(ctx, rpcReq) + if err != nil { + if c.doneCtx.Err() != nil { + return DeleteUserResponse{}, ErrPluginShutdown + } + return DeleteUserResponse{}, fmt.Errorf("unable to update user: %w", err) + } + + return deleteUserRespFromProto(rpcResp) +} + +func deleteUserReqToProto(req DeleteUserRequest) (*proto.DeleteUserRequest, error) { + if req.Username == "" { + return nil, fmt.Errorf("missing username") + } + + rpcReq := &proto.DeleteUserRequest{ + Username: req.Username, + Statements: &proto.Statements{ + Commands: req.Statements.Commands, + }, + } + return rpcReq, nil +} + +func deleteUserRespFromProto(rpcResp *proto.DeleteUserResponse) (DeleteUserResponse, error) { + // Placeholder for future conversion if data is returned + return DeleteUserResponse{}, nil +} + +func (c gRPCClient) Type() (string, error) { + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + typeResp, err := c.client.Type(ctx, &proto.Empty{}) + if err != nil { + if c.doneCtx.Err() != nil { + return "", ErrPluginShutdown + } + return "", fmt.Errorf("unable to get database plugin type: %w", err) + } + return typeResp.GetType(), nil +} + +func (c gRPCClient) Close() error { + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + _, err := c.client.Close(ctx, &proto.Empty{}) + if err != nil { + if c.doneCtx.Err() != nil { + return ErrPluginShutdown + } + return err + } + return nil +} diff --git a/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/grpc_database_plugin.go b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/grpc_database_plugin.go new file mode 100644 index 0000000000..66468640ce --- /dev/null +++ b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/grpc_database_plugin.go @@ -0,0 +1,42 @@ +package newdbplugin + +import ( + "context" + + "github.com/hashicorp/go-plugin" + "github.com/hashicorp/vault/sdk/database/newdbplugin/proto" + "google.golang.org/grpc" +) + +// handshakeConfigs are used to just do a basic handshake between +// a plugin and host. If the handshake fails, a user friendly error is shown. +// This prevents users from executing bad plugins or executing a plugin +// directory. It is a UX feature, not a security feature. +var handshakeConfig = plugin.HandshakeConfig{ + ProtocolVersion: 5, + MagicCookieKey: "VAULT_DATABASE_PLUGIN", + MagicCookieValue: "926a0820-aea2-be28-51d6-83cdf00e8edb", +} + +type GRPCDatabasePlugin struct { + Impl Database + + // Embeding this will disable the netRPC protocol + plugin.NetRPCUnsupportedPlugin +} + +var _ plugin.Plugin = &GRPCDatabasePlugin{} +var _ plugin.GRPCPlugin = &GRPCDatabasePlugin{} + +func (d GRPCDatabasePlugin) GRPCServer(_ *plugin.GRPCBroker, s *grpc.Server) error { + proto.RegisterDatabaseServer(s, gRPCServer{impl: d.Impl}) + return nil +} + +func (GRPCDatabasePlugin) GRPCClient(doneCtx context.Context, _ *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { + client := gRPCClient{ + client: proto.NewDatabaseClient(c), + doneCtx: doneCtx, + } + return client, nil +} diff --git a/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/grpc_server.go b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/grpc_server.go new file mode 100644 index 0000000000..646e894ed3 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/grpc_server.go @@ -0,0 +1,190 @@ +package newdbplugin + +import ( + "context" + "fmt" + "time" + + "github.com/golang/protobuf/ptypes" + "github.com/hashicorp/vault/sdk/database/newdbplugin/proto" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +var _ proto.DatabaseServer = gRPCServer{} + +type gRPCServer struct { + impl Database +} + +// Initialize the database plugin +func (g gRPCServer) Initialize(ctx context.Context, request *proto.InitializeRequest) (*proto.InitializeResponse, error) { + rawConfig := structToMap(request.ConfigData) + + dbReq := InitializeRequest{ + Config: rawConfig, + VerifyConnection: request.VerifyConnection, + } + + dbResp, err := g.impl.Initialize(ctx, dbReq) + if err != nil { + return &proto.InitializeResponse{}, status.Errorf(codes.Internal, "failed to initialize: %s", err) + } + + newConfig, err := mapToStruct(dbResp.Config) + if err != nil { + return &proto.InitializeResponse{}, status.Errorf(codes.Internal, "failed to marshal new config to JSON: %s", err) + } + + resp := &proto.InitializeResponse{ + ConfigData: newConfig, + } + + return resp, nil +} + +func (g gRPCServer) NewUser(ctx context.Context, req *proto.NewUserRequest) (*proto.NewUserResponse, error) { + if req.GetUsernameConfig() == nil { + return &proto.NewUserResponse{}, status.Errorf(codes.InvalidArgument, "missing username config") + } + + var expiration time.Time + + if req.GetExpiration() != nil { + exp, err := ptypes.Timestamp(req.GetExpiration()) + if err != nil { + return &proto.NewUserResponse{}, status.Errorf(codes.InvalidArgument, "unable to parse expiration date: %s", err) + } + expiration = exp + } + + dbReq := NewUserRequest{ + UsernameConfig: UsernameMetadata{ + DisplayName: req.GetUsernameConfig().GetDisplayName(), + RoleName: req.GetUsernameConfig().GetRoleName(), + }, + Password: req.GetPassword(), + Expiration: expiration, + Statements: getStatementsFromProto(req.GetStatements()), + RollbackStatements: getStatementsFromProto(req.GetRollbackStatements()), + } + + dbResp, err := g.impl.NewUser(ctx, dbReq) + if err != nil { + return &proto.NewUserResponse{}, status.Errorf(codes.Internal, "unable to create new user: %s", err) + } + + resp := &proto.NewUserResponse{ + Username: dbResp.Username, + } + return resp, nil +} + +func (g gRPCServer) UpdateUser(ctx context.Context, req *proto.UpdateUserRequest) (*proto.UpdateUserResponse, error) { + if req.GetUsername() == "" { + return &proto.UpdateUserResponse{}, status.Errorf(codes.InvalidArgument, "no username provided") + } + + dbReq, err := getUpdateUserRequest(req) + if err != nil { + return &proto.UpdateUserResponse{}, status.Errorf(codes.InvalidArgument, err.Error()) + } + + _, err = g.impl.UpdateUser(ctx, dbReq) + if err != nil { + return &proto.UpdateUserResponse{}, status.Errorf(codes.Internal, "unable to update user: %s", err) + } + return &proto.UpdateUserResponse{}, nil +} + +func getUpdateUserRequest(req *proto.UpdateUserRequest) (UpdateUserRequest, error) { + var password *ChangePassword + if req.GetPassword() != nil && req.GetPassword().GetNewPassword() != "" { + password = &ChangePassword{ + NewPassword: req.GetPassword().GetNewPassword(), + Statements: getStatementsFromProto(req.GetPassword().GetStatements()), + } + } + + var expiration *ChangeExpiration + if req.GetExpiration() != nil && req.GetExpiration().GetNewExpiration() != nil { + newExpiration, err := ptypes.Timestamp(req.GetExpiration().GetNewExpiration()) + if err != nil { + return UpdateUserRequest{}, fmt.Errorf("unable to parse new expiration: %w", err) + } + + expiration = &ChangeExpiration{ + NewExpiration: newExpiration, + Statements: getStatementsFromProto(req.GetExpiration().GetStatements()), + } + } + + dbReq := UpdateUserRequest{ + Username: req.GetUsername(), + Password: password, + Expiration: expiration, + } + + if !hasChange(dbReq) { + return UpdateUserRequest{}, fmt.Errorf("update user request has no changes") + } + + return dbReq, nil +} + +func hasChange(dbReq UpdateUserRequest) bool { + if dbReq.Password != nil && dbReq.Password.NewPassword != "" { + return true + } + if dbReq.Expiration != nil && !dbReq.Expiration.NewExpiration.IsZero() { + return true + } + return false +} + +func (g gRPCServer) DeleteUser(ctx context.Context, req *proto.DeleteUserRequest) (*proto.DeleteUserResponse, error) { + if req.GetUsername() == "" { + return &proto.DeleteUserResponse{}, status.Errorf(codes.InvalidArgument, "no username provided") + } + dbReq := DeleteUserRequest{ + Username: req.GetUsername(), + Statements: getStatementsFromProto(req.GetStatements()), + } + + _, err := g.impl.DeleteUser(ctx, dbReq) + if err != nil { + return &proto.DeleteUserResponse{}, status.Errorf(codes.Internal, "unable to delete user: %s", err) + } + return &proto.DeleteUserResponse{}, nil +} + +func (g gRPCServer) Type(ctx context.Context, _ *proto.Empty) (*proto.TypeResponse, error) { + t, err := g.impl.Type() + if err != nil { + return &proto.TypeResponse{}, status.Errorf(codes.Internal, "unable to retrieve type: %s", err) + } + + resp := &proto.TypeResponse{ + Type: t, + } + return resp, nil +} + +func (g gRPCServer) Close(ctx context.Context, _ *proto.Empty) (*proto.Empty, error) { + err := g.impl.Close() + if err != nil { + return &proto.Empty{}, status.Errorf(codes.Internal, "unable to close database plugin: %s", err) + } + return &proto.Empty{}, nil +} + +func getStatementsFromProto(protoStmts *proto.Statements) (statements Statements) { + if protoStmts == nil { + return statements + } + cmds := protoStmts.GetCommands() + statements = Statements{ + Commands: cmds, + } + return statements +} diff --git a/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/marshalling.go b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/marshalling.go new file mode 100644 index 0000000000..54cc40eacf --- /dev/null +++ b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/marshalling.go @@ -0,0 +1,36 @@ +package newdbplugin + +import ( + "math" + + "google.golang.org/protobuf/types/known/structpb" +) + +func mapToStruct(m map[string]interface{}) (*structpb.Struct, error) { + return structpb.NewStruct(m) +} + +func structToMap(strct *structpb.Struct) map[string]interface{} { + m := strct.AsMap() + coerceFloatsToInt(m) + return m +} + +// coerceFloatsToInt if the floats can be coerced to an integer without losing data +func coerceFloatsToInt(m map[string]interface{}) { + for k, v := range m { + fVal, ok := v.(float64) + if !ok { + continue + } + if isInt(fVal) { + m[k] = int64(fVal) + } + } +} + +// isInt attempts to determine if the given floating point number could be represented as an integer without losing data +// This does not work for very large floats, however in this usage that's okay since we don't expect numbers that large. +func isInt(f float64) bool { + return math.Floor(f) == f +} diff --git a/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/middleware.go b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/middleware.go new file mode 100644 index 0000000000..3ac1069e3e --- /dev/null +++ b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/middleware.go @@ -0,0 +1,274 @@ +package newdbplugin + +import ( + "context" + "errors" + "net/url" + "strings" + "time" + + metrics "github.com/armon/go-metrics" + "github.com/hashicorp/errwrap" + log "github.com/hashicorp/go-hclog" + "google.golang.org/grpc/status" +) + +// /////////////////////////////////////////////////// +// Tracing Middleware +// /////////////////////////////////////////////////// + +var _ Database = databaseTracingMiddleware{} + +// databaseTracingMiddleware wraps a implementation of Database and executes +// trace logging on function call. +type databaseTracingMiddleware struct { + next Database + logger log.Logger +} + +func (mw databaseTracingMiddleware) Initialize(ctx context.Context, req InitializeRequest) (resp InitializeResponse, err error) { + defer func(then time.Time) { + mw.logger.Trace("initialize", + "status", "finished", + "verify", req.VerifyConnection, + "err", err, + "took", time.Since(then)) + }(time.Now()) + + mw.logger.Trace("initialize", "status", "started") + return mw.next.Initialize(ctx, req) +} + +func (mw databaseTracingMiddleware) NewUser(ctx context.Context, req NewUserRequest) (resp NewUserResponse, err error) { + defer func(then time.Time) { + mw.logger.Trace("create user", + "status", "finished", + "err", err, + "took", time.Since(then)) + }(time.Now()) + + mw.logger.Trace("create user", + "status", "started") + return mw.next.NewUser(ctx, req) +} + +func (mw databaseTracingMiddleware) UpdateUser(ctx context.Context, req UpdateUserRequest) (resp UpdateUserResponse, err error) { + defer func(then time.Time) { + mw.logger.Trace("update user", + "status", "finished", + "err", err, + "took", time.Since(then)) + }(time.Now()) + + mw.logger.Trace("update user", "status", "started") + return mw.next.UpdateUser(ctx, req) +} + +func (mw databaseTracingMiddleware) DeleteUser(ctx context.Context, req DeleteUserRequest) (resp DeleteUserResponse, err error) { + defer func(then time.Time) { + mw.logger.Trace("delete user", + "status", "finished", + "err", err, + "took", time.Since(then)) + }(time.Now()) + + mw.logger.Trace("delete user", + "status", "started") + return mw.next.DeleteUser(ctx, req) +} + +func (mw databaseTracingMiddleware) Type() (string, error) { + return mw.next.Type() +} + +func (mw databaseTracingMiddleware) Close() (err error) { + defer func(then time.Time) { + mw.logger.Trace("close", + "status", "finished", + "err", err, + "took", time.Since(then)) + }(time.Now()) + + mw.logger.Trace("close", + "status", "started") + return mw.next.Close() +} + +// /////////////////////////////////////////////////// +// Metrics Middleware Domain +// /////////////////////////////////////////////////// + +var _ Database = databaseMetricsMiddleware{} + +// databaseMetricsMiddleware wraps an implementation of Databases and on +// function call logs metrics about this instance. +type databaseMetricsMiddleware struct { + next Database + + typeStr string +} + +func (mw databaseMetricsMiddleware) Initialize(ctx context.Context, req InitializeRequest) (resp InitializeResponse, err error) { + defer func(now time.Time) { + metrics.MeasureSince([]string{"database", "Initialize"}, now) + metrics.MeasureSince([]string{"database", mw.typeStr, "Initialize"}, now) + + if err != nil { + metrics.IncrCounter([]string{"database", "Initialize", "error"}, 1) + metrics.IncrCounter([]string{"database", mw.typeStr, "Initialize", "error"}, 1) + } + }(time.Now()) + + metrics.IncrCounter([]string{"database", "Initialize"}, 1) + metrics.IncrCounter([]string{"database", mw.typeStr, "Initialize"}, 1) + return mw.next.Initialize(ctx, req) +} + +func (mw databaseMetricsMiddleware) NewUser(ctx context.Context, req NewUserRequest) (resp NewUserResponse, err error) { + defer func(start time.Time) { + metrics.MeasureSince([]string{"database", "NewUser"}, start) + metrics.MeasureSince([]string{"database", mw.typeStr, "NewUser"}, start) + + if err != nil { + metrics.IncrCounter([]string{"database", "NewUser", "error"}, 1) + metrics.IncrCounter([]string{"database", mw.typeStr, "NewUser", "error"}, 1) + } + }(time.Now()) + + metrics.IncrCounter([]string{"database", "NewUser"}, 1) + metrics.IncrCounter([]string{"database", mw.typeStr, "NewUser"}, 1) + return mw.next.NewUser(ctx, req) +} + +func (mw databaseMetricsMiddleware) UpdateUser(ctx context.Context, req UpdateUserRequest) (resp UpdateUserResponse, err error) { + defer func(now time.Time) { + metrics.MeasureSince([]string{"database", "UpdateUser"}, now) + metrics.MeasureSince([]string{"database", mw.typeStr, "UpdateUser"}, now) + + if err != nil { + metrics.IncrCounter([]string{"database", "UpdateUser", "error"}, 1) + metrics.IncrCounter([]string{"database", mw.typeStr, "UpdateUser", "error"}, 1) + } + }(time.Now()) + + metrics.IncrCounter([]string{"database", "UpdateUser"}, 1) + metrics.IncrCounter([]string{"database", mw.typeStr, "UpdateUser"}, 1) + return mw.next.UpdateUser(ctx, req) +} + +func (mw databaseMetricsMiddleware) DeleteUser(ctx context.Context, req DeleteUserRequest) (resp DeleteUserResponse, err error) { + defer func(now time.Time) { + metrics.MeasureSince([]string{"database", "DeleteUser"}, now) + metrics.MeasureSince([]string{"database", mw.typeStr, "DeleteUser"}, now) + + if err != nil { + metrics.IncrCounter([]string{"database", "DeleteUser", "error"}, 1) + metrics.IncrCounter([]string{"database", mw.typeStr, "DeleteUser", "error"}, 1) + } + }(time.Now()) + + metrics.IncrCounter([]string{"database", "DeleteUser"}, 1) + metrics.IncrCounter([]string{"database", mw.typeStr, "DeleteUser"}, 1) + return mw.next.DeleteUser(ctx, req) +} + +func (mw databaseMetricsMiddleware) Type() (string, error) { + return mw.next.Type() +} + +func (mw databaseMetricsMiddleware) Close() (err error) { + defer func(now time.Time) { + metrics.MeasureSince([]string{"database", "Close"}, now) + metrics.MeasureSince([]string{"database", mw.typeStr, "Close"}, now) + + if err != nil { + metrics.IncrCounter([]string{"database", "Close", "error"}, 1) + metrics.IncrCounter([]string{"database", mw.typeStr, "Close", "error"}, 1) + } + }(time.Now()) + + metrics.IncrCounter([]string{"database", "Close"}, 1) + metrics.IncrCounter([]string{"database", mw.typeStr, "Close"}, 1) + return mw.next.Close() +} + +// /////////////////////////////////////////////////// +// Error Sanitizer Middleware Domain +// /////////////////////////////////////////////////// + +var _ Database = DatabaseErrorSanitizerMiddleware{} + +// DatabaseErrorSanitizerMiddleware wraps an implementation of Databases and +// sanitizes returned error messages +type DatabaseErrorSanitizerMiddleware struct { + next Database + secretsFn secretsFn +} + +type secretsFn func() map[string]string + +func NewDatabaseErrorSanitizerMiddleware(next Database, secrets secretsFn) DatabaseErrorSanitizerMiddleware { + return DatabaseErrorSanitizerMiddleware{ + next: next, + secretsFn: secrets, + } +} + +func (mw DatabaseErrorSanitizerMiddleware) Initialize(ctx context.Context, req InitializeRequest) (resp InitializeResponse, err error) { + resp, err = mw.next.Initialize(ctx, req) + return resp, mw.sanitize(err) +} + +func (mw DatabaseErrorSanitizerMiddleware) NewUser(ctx context.Context, req NewUserRequest) (resp NewUserResponse, err error) { + resp, err = mw.next.NewUser(ctx, req) + return resp, mw.sanitize(err) +} + +func (mw DatabaseErrorSanitizerMiddleware) UpdateUser(ctx context.Context, req UpdateUserRequest) (UpdateUserResponse, error) { + resp, err := mw.next.UpdateUser(ctx, req) + return resp, mw.sanitize(err) +} + +func (mw DatabaseErrorSanitizerMiddleware) DeleteUser(ctx context.Context, req DeleteUserRequest) (DeleteUserResponse, error) { + resp, err := mw.next.DeleteUser(ctx, req) + return resp, mw.sanitize(err) +} + +func (mw DatabaseErrorSanitizerMiddleware) Type() (string, error) { + dbType, err := mw.next.Type() + return dbType, mw.sanitize(err) +} + +func (mw DatabaseErrorSanitizerMiddleware) Close() (err error) { + return mw.sanitize(mw.next.Close()) +} + +// sanitize errors by removing any sensitive strings within their messages. This uses +// the secretsFn to determine what fields should be sanitized. +func (mw DatabaseErrorSanitizerMiddleware) sanitize(err error) error { + if err == nil { + return nil + } + if errwrap.ContainsType(err, new(url.Error)) { + return errors.New("unable to parse connection url") + } + if mw.secretsFn == nil { + return err + } + for find, replace := range mw.secretsFn() { + if find == "" { + continue + } + + // Attempt to keep the status code attached to the + // error while changing the actual error message + s, ok := status.FromError(err) + if ok { + err = status.Error(s.Code(), strings.Replace(s.Message(), find, replace, -1)) + continue + } + + err = errors.New(strings.Replace(err.Error(), find, replace, -1)) + } + return err +} diff --git a/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/plugin_client.go b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/plugin_client.go new file mode 100644 index 0000000000..812adc2b57 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/plugin_client.go @@ -0,0 +1,80 @@ +package newdbplugin + +import ( + "context" + "errors" + "sync" + + log "github.com/hashicorp/go-hclog" + plugin "github.com/hashicorp/go-plugin" + "github.com/hashicorp/vault/sdk/helper/pluginutil" +) + +// DatabasePluginClient embeds a databasePluginRPCClient and wraps it's Close +// method to also call Kill() on the plugin.Client. +type DatabasePluginClient struct { + client *plugin.Client + sync.Mutex + + Database +} + +// This wraps the Close call and ensures we both close the database connection +// and kill the plugin. +func (dc *DatabasePluginClient) Close() error { + err := dc.Database.Close() + dc.client.Kill() + + return err +} + +// NewPluginClient returns a databaseRPCClient with a connection to a running +// plugin. The client is wrapped in a DatabasePluginClient object to ensure the +// plugin is killed on call of Close(). +func NewPluginClient(ctx context.Context, sys pluginutil.RunnerUtil, pluginRunner *pluginutil.PluginRunner, logger log.Logger, isMetadataMode bool) (Database, error) { + // pluginSets is the map of plugins we can dispense. + pluginSets := map[int]plugin.PluginSet{ + 5: plugin.PluginSet{ + "database": new(GRPCDatabasePlugin), + }, + } + + var client *plugin.Client + var err error + if isMetadataMode { + client, err = pluginRunner.RunMetadataMode(ctx, sys, pluginSets, handshakeConfig, []string{}, logger) + } else { + client, err = pluginRunner.Run(ctx, sys, pluginSets, handshakeConfig, []string{}, logger) + } + if err != nil { + return nil, err + } + + // Connect via RPC + rpcClient, err := client.Client() + if err != nil { + return nil, err + } + + // Request the plugin + raw, err := rpcClient.Dispense("database") + if err != nil { + return nil, err + } + + // We should have a database type now. This feels like a normal interface + // implementation but is in fact over an RPC connection. + var db Database + switch raw.(type) { + case gRPCClient: + db = raw.(gRPCClient) + default: + return nil, errors.New("unsupported client type") + } + + // Wrap RPC implementation in DatabasePluginClient + return &DatabasePluginClient{ + client: client, + Database: db, + }, nil +} diff --git a/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/plugin_factory.go b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/plugin_factory.go new file mode 100644 index 0000000000..eb8fd15d75 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/plugin_factory.go @@ -0,0 +1,78 @@ +package newdbplugin + +import ( + "context" + "fmt" + + "github.com/hashicorp/errwrap" + log "github.com/hashicorp/go-hclog" + "github.com/hashicorp/vault/sdk/helper/consts" + "github.com/hashicorp/vault/sdk/helper/pluginutil" +) + +// PluginFactory is used to build plugin database types. It wraps the database +// object in a logging and metrics middleware. +func PluginFactory(ctx context.Context, pluginName string, sys pluginutil.LookRunnerUtil, logger log.Logger) (Database, error) { + // Look for plugin in the plugin catalog + pluginRunner, err := sys.LookupPlugin(ctx, pluginName, consts.PluginTypeDatabase) + if err != nil { + return nil, err + } + + namedLogger := logger.Named(pluginName) + + var transport string + var db Database + if pluginRunner.Builtin { + // Plugin is builtin so we can retrieve an instance of the interface + // from the pluginRunner. Then cast it to a Database. + dbRaw, err := pluginRunner.BuiltinFactory() + if err != nil { + return nil, errwrap.Wrapf("error initializing plugin: {{err}}", err) + } + + var ok bool + db, ok = dbRaw.(Database) + if !ok { + return nil, fmt.Errorf("unsupported database type: %q", pluginName) + } + + transport = "builtin" + + } else { + // create a DatabasePluginClient instance + db, err = NewPluginClient(ctx, sys, pluginRunner, namedLogger, false) + if err != nil { + return nil, err + } + + // Switch on the underlying database client type to get the transport + // method. + switch db.(*DatabasePluginClient).Database.(type) { + case *gRPCClient: + transport = "gRPC" + } + + } + + typeStr, err := db.Type() + if err != nil { + return nil, errwrap.Wrapf("error getting plugin type: {{err}}", err) + } + + // Wrap with metrics middleware + db = &databaseMetricsMiddleware{ + next: db, + typeStr: typeStr, + } + + // Wrap with tracing middleware + if namedLogger.IsTrace() { + db = &databaseTracingMiddleware{ + next: db, + logger: namedLogger.With("transport", transport), + } + } + + return db, nil +} diff --git a/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/plugin_server.go b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/plugin_server.go new file mode 100644 index 0000000000..ccb178c874 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/plugin_server.go @@ -0,0 +1,42 @@ +package newdbplugin + +import ( + "crypto/tls" + "fmt" + + "github.com/hashicorp/go-plugin" + "github.com/hashicorp/vault/sdk/helper/pluginutil" +) + +// Serve is called from within a plugin and wraps the provided +// Database implementation in a databasePluginRPCServer object and starts a +// RPC server. +func Serve(db Database, tlsProvider func() (*tls.Config, error)) { + plugin.Serve(ServeConfig(db, tlsProvider)) +} + +func ServeConfig(db Database, tlsProvider func() (*tls.Config, error)) *plugin.ServeConfig { + err := pluginutil.OptionallyEnableMlock() + if err != nil { + fmt.Println(err) + return nil + } + + // pluginSets is the map of plugins we can dispense. + pluginSets := map[int]plugin.PluginSet{ + 5: plugin.PluginSet{ + "database": &GRPCDatabasePlugin{ + Impl: db, + }, + }, + } + + conf := &plugin.ServeConfig{ + HandshakeConfig: handshakeConfig, + VersionedPlugins: pluginSets, + TLSProvider: tlsProvider, + GRPCServer: plugin.DefaultGRPCServer, + } + + return conf +} diff --git a/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/proto/database.pb.go b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/proto/database.pb.go new file mode 100644 index 0000000000..b22f1d10a1 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/proto/database.pb.go @@ -0,0 +1,1409 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.23.0 +// protoc v3.13.0 +// source: sdk/database/newdbplugin/proto/database.proto + +package proto + +import ( + context "context" + proto "github.com/golang/protobuf/proto" + _struct "github.com/golang/protobuf/ptypes/struct" + timestamp "github.com/golang/protobuf/ptypes/timestamp" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +///////////////// +// Initialize() +///////////////// +type InitializeRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ConfigData *_struct.Struct `protobuf:"bytes,1,opt,name=config_data,json=configData,proto3" json:"config_data,omitempty"` + VerifyConnection bool `protobuf:"varint,2,opt,name=verify_connection,json=verifyConnection,proto3" json:"verify_connection,omitempty"` +} + +func (x *InitializeRequest) Reset() { + *x = InitializeRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InitializeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InitializeRequest) ProtoMessage() {} + +func (x *InitializeRequest) ProtoReflect() protoreflect.Message { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InitializeRequest.ProtoReflect.Descriptor instead. +func (*InitializeRequest) Descriptor() ([]byte, []int) { + return file_sdk_database_newdbplugin_proto_database_proto_rawDescGZIP(), []int{0} +} + +func (x *InitializeRequest) GetConfigData() *_struct.Struct { + if x != nil { + return x.ConfigData + } + return nil +} + +func (x *InitializeRequest) GetVerifyConnection() bool { + if x != nil { + return x.VerifyConnection + } + return false +} + +type InitializeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ConfigData *_struct.Struct `protobuf:"bytes,1,opt,name=config_data,json=configData,proto3" json:"config_data,omitempty"` +} + +func (x *InitializeResponse) Reset() { + *x = InitializeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InitializeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InitializeResponse) ProtoMessage() {} + +func (x *InitializeResponse) ProtoReflect() protoreflect.Message { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InitializeResponse.ProtoReflect.Descriptor instead. +func (*InitializeResponse) Descriptor() ([]byte, []int) { + return file_sdk_database_newdbplugin_proto_database_proto_rawDescGZIP(), []int{1} +} + +func (x *InitializeResponse) GetConfigData() *_struct.Struct { + if x != nil { + return x.ConfigData + } + return nil +} + +type NewUserRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UsernameConfig *UsernameConfig `protobuf:"bytes,1,opt,name=username_config,json=usernameConfig,proto3" json:"username_config,omitempty"` + Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` + Expiration *timestamp.Timestamp `protobuf:"bytes,3,opt,name=expiration,proto3" json:"expiration,omitempty"` + Statements *Statements `protobuf:"bytes,4,opt,name=statements,proto3" json:"statements,omitempty"` + RollbackStatements *Statements `protobuf:"bytes,5,opt,name=rollback_statements,json=rollbackStatements,proto3" json:"rollback_statements,omitempty"` +} + +func (x *NewUserRequest) Reset() { + *x = NewUserRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NewUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NewUserRequest) ProtoMessage() {} + +func (x *NewUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NewUserRequest.ProtoReflect.Descriptor instead. +func (*NewUserRequest) Descriptor() ([]byte, []int) { + return file_sdk_database_newdbplugin_proto_database_proto_rawDescGZIP(), []int{2} +} + +func (x *NewUserRequest) GetUsernameConfig() *UsernameConfig { + if x != nil { + return x.UsernameConfig + } + return nil +} + +func (x *NewUserRequest) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +func (x *NewUserRequest) GetExpiration() *timestamp.Timestamp { + if x != nil { + return x.Expiration + } + return nil +} + +func (x *NewUserRequest) GetStatements() *Statements { + if x != nil { + return x.Statements + } + return nil +} + +func (x *NewUserRequest) GetRollbackStatements() *Statements { + if x != nil { + return x.RollbackStatements + } + return nil +} + +type UsernameConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DisplayName string `protobuf:"bytes,1,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` + RoleName string `protobuf:"bytes,2,opt,name=role_name,json=roleName,proto3" json:"role_name,omitempty"` +} + +func (x *UsernameConfig) Reset() { + *x = UsernameConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UsernameConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UsernameConfig) ProtoMessage() {} + +func (x *UsernameConfig) ProtoReflect() protoreflect.Message { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UsernameConfig.ProtoReflect.Descriptor instead. +func (*UsernameConfig) Descriptor() ([]byte, []int) { + return file_sdk_database_newdbplugin_proto_database_proto_rawDescGZIP(), []int{3} +} + +func (x *UsernameConfig) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + +func (x *UsernameConfig) GetRoleName() string { + if x != nil { + return x.RoleName + } + return "" +} + +type NewUserResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` +} + +func (x *NewUserResponse) Reset() { + *x = NewUserResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NewUserResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NewUserResponse) ProtoMessage() {} + +func (x *NewUserResponse) ProtoReflect() protoreflect.Message { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NewUserResponse.ProtoReflect.Descriptor instead. +func (*NewUserResponse) Descriptor() ([]byte, []int) { + return file_sdk_database_newdbplugin_proto_database_proto_rawDescGZIP(), []int{4} +} + +func (x *NewUserResponse) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +///////////////// +// UpdateUser() +///////////////// +type UpdateUserRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + Password *ChangePassword `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` + Expiration *ChangeExpiration `protobuf:"bytes,3,opt,name=expiration,proto3" json:"expiration,omitempty"` +} + +func (x *UpdateUserRequest) Reset() { + *x = UpdateUserRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateUserRequest) ProtoMessage() {} + +func (x *UpdateUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateUserRequest.ProtoReflect.Descriptor instead. +func (*UpdateUserRequest) Descriptor() ([]byte, []int) { + return file_sdk_database_newdbplugin_proto_database_proto_rawDescGZIP(), []int{5} +} + +func (x *UpdateUserRequest) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *UpdateUserRequest) GetPassword() *ChangePassword { + if x != nil { + return x.Password + } + return nil +} + +func (x *UpdateUserRequest) GetExpiration() *ChangeExpiration { + if x != nil { + return x.Expiration + } + return nil +} + +type ChangePassword struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NewPassword string `protobuf:"bytes,1,opt,name=new_password,json=newPassword,proto3" json:"new_password,omitempty"` + Statements *Statements `protobuf:"bytes,2,opt,name=statements,proto3" json:"statements,omitempty"` +} + +func (x *ChangePassword) Reset() { + *x = ChangePassword{} + if protoimpl.UnsafeEnabled { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChangePassword) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChangePassword) ProtoMessage() {} + +func (x *ChangePassword) ProtoReflect() protoreflect.Message { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChangePassword.ProtoReflect.Descriptor instead. +func (*ChangePassword) Descriptor() ([]byte, []int) { + return file_sdk_database_newdbplugin_proto_database_proto_rawDescGZIP(), []int{6} +} + +func (x *ChangePassword) GetNewPassword() string { + if x != nil { + return x.NewPassword + } + return "" +} + +func (x *ChangePassword) GetStatements() *Statements { + if x != nil { + return x.Statements + } + return nil +} + +type ChangeExpiration struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NewExpiration *timestamp.Timestamp `protobuf:"bytes,1,opt,name=new_expiration,json=newExpiration,proto3" json:"new_expiration,omitempty"` + Statements *Statements `protobuf:"bytes,2,opt,name=statements,proto3" json:"statements,omitempty"` +} + +func (x *ChangeExpiration) Reset() { + *x = ChangeExpiration{} + if protoimpl.UnsafeEnabled { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChangeExpiration) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChangeExpiration) ProtoMessage() {} + +func (x *ChangeExpiration) ProtoReflect() protoreflect.Message { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChangeExpiration.ProtoReflect.Descriptor instead. +func (*ChangeExpiration) Descriptor() ([]byte, []int) { + return file_sdk_database_newdbplugin_proto_database_proto_rawDescGZIP(), []int{7} +} + +func (x *ChangeExpiration) GetNewExpiration() *timestamp.Timestamp { + if x != nil { + return x.NewExpiration + } + return nil +} + +func (x *ChangeExpiration) GetStatements() *Statements { + if x != nil { + return x.Statements + } + return nil +} + +type UpdateUserResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *UpdateUserResponse) Reset() { + *x = UpdateUserResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateUserResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateUserResponse) ProtoMessage() {} + +func (x *UpdateUserResponse) ProtoReflect() protoreflect.Message { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateUserResponse.ProtoReflect.Descriptor instead. +func (*UpdateUserResponse) Descriptor() ([]byte, []int) { + return file_sdk_database_newdbplugin_proto_database_proto_rawDescGZIP(), []int{8} +} + +///////////////// +// DeleteUser() +///////////////// +type DeleteUserRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + Statements *Statements `protobuf:"bytes,2,opt,name=statements,proto3" json:"statements,omitempty"` +} + +func (x *DeleteUserRequest) Reset() { + *x = DeleteUserRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteUserRequest) ProtoMessage() {} + +func (x *DeleteUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteUserRequest.ProtoReflect.Descriptor instead. +func (*DeleteUserRequest) Descriptor() ([]byte, []int) { + return file_sdk_database_newdbplugin_proto_database_proto_rawDescGZIP(), []int{9} +} + +func (x *DeleteUserRequest) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *DeleteUserRequest) GetStatements() *Statements { + if x != nil { + return x.Statements + } + return nil +} + +type DeleteUserResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *DeleteUserResponse) Reset() { + *x = DeleteUserResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteUserResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteUserResponse) ProtoMessage() {} + +func (x *DeleteUserResponse) ProtoReflect() protoreflect.Message { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteUserResponse.ProtoReflect.Descriptor instead. +func (*DeleteUserResponse) Descriptor() ([]byte, []int) { + return file_sdk_database_newdbplugin_proto_database_proto_rawDescGZIP(), []int{10} +} + +///////////////// +// Type() +///////////////// +type TypeResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Type string `protobuf:"bytes,1,opt,name=Type,proto3" json:"Type,omitempty"` +} + +func (x *TypeResponse) Reset() { + *x = TypeResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TypeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TypeResponse) ProtoMessage() {} + +func (x *TypeResponse) ProtoReflect() protoreflect.Message { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TypeResponse.ProtoReflect.Descriptor instead. +func (*TypeResponse) Descriptor() ([]byte, []int) { + return file_sdk_database_newdbplugin_proto_database_proto_rawDescGZIP(), []int{11} +} + +func (x *TypeResponse) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +///////////////// +// General purpose +///////////////// +type Statements struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Commands []string `protobuf:"bytes,1,rep,name=Commands,proto3" json:"Commands,omitempty"` +} + +func (x *Statements) Reset() { + *x = Statements{} + if protoimpl.UnsafeEnabled { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Statements) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Statements) ProtoMessage() {} + +func (x *Statements) ProtoReflect() protoreflect.Message { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Statements.ProtoReflect.Descriptor instead. +func (*Statements) Descriptor() ([]byte, []int) { + return file_sdk_database_newdbplugin_proto_database_proto_rawDescGZIP(), []int{12} +} + +func (x *Statements) GetCommands() []string { + if x != nil { + return x.Commands + } + return nil +} + +type Empty struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Empty) Reset() { + *x = Empty{} + if protoimpl.UnsafeEnabled { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Empty) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Empty) ProtoMessage() {} + +func (x *Empty) ProtoReflect() protoreflect.Message { + mi := &file_sdk_database_newdbplugin_proto_database_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Empty.ProtoReflect.Descriptor instead. +func (*Empty) Descriptor() ([]byte, []int) { + return file_sdk_database_newdbplugin_proto_database_proto_rawDescGZIP(), []int{13} +} + +var File_sdk_database_newdbplugin_proto_database_proto protoreflect.FileDescriptor + +var file_sdk_database_newdbplugin_proto_database_proto_rawDesc = []byte{ + 0x0a, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x2f, 0x6e, + 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x0b, 0x6e, 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x1a, 0x1c, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x7a, 0x0a, 0x11, 0x49, + 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x38, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0a, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2b, 0x0a, 0x11, 0x76, 0x65, + 0x72, 0x69, 0x66, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x43, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x4e, 0x0a, 0x12, 0x49, 0x6e, 0x69, 0x74, 0x69, + 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, + 0x0b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0a, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x44, 0x61, 0x74, 0x61, 0x22, 0xb1, 0x02, 0x0a, 0x0e, 0x4e, 0x65, 0x77, 0x55, + 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x0f, 0x75, 0x73, + 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x3a, 0x0a, 0x0a, + 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x65, 0x78, + 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6e, + 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x12, 0x48, 0x0a, 0x13, 0x72, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x6e, 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x12, 0x72, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x50, 0x0a, 0x0e, 0x55, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x21, 0x0a, + 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x2d, 0x0a, + 0x0f, 0x4e, 0x65, 0x77, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xa7, 0x01, 0x0a, + 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x37, + 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x6e, 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x43, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x08, 0x70, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x3d, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6e, 0x65, + 0x77, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x6c, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x77, 0x5f, + 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x6e, 0x65, 0x77, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x37, 0x0a, 0x0a, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x6e, 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x45, + 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x41, 0x0a, 0x0e, 0x6e, 0x65, 0x77, + 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x6e, + 0x65, 0x77, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x0a, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x6e, 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x14, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, + 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x68, 0x0a, 0x11, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x0a, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x6e, 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x14, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, + 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x0a, 0x0c, 0x54, + 0x79, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x54, + 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x22, + 0x28, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1a, 0x0a, + 0x08, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x08, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x32, 0xa5, 0x03, 0x0a, 0x08, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, + 0x4d, 0x0a, 0x0a, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x12, 0x1e, 0x2e, + 0x6e, 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x49, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, + 0x6e, 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x49, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, + 0x0a, 0x07, 0x4e, 0x65, 0x77, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1b, 0x2e, 0x6e, 0x65, 0x77, 0x64, + 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x65, 0x77, 0x55, 0x73, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6e, 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x65, 0x77, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, + 0x65, 0x72, 0x12, 0x1e, 0x2e, 0x6e, 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6e, 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, + 0x72, 0x12, 0x1e, 0x2e, 0x6e, 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1f, 0x2e, 0x6e, 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x2e, 0x6e, 0x65, 0x77, + 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, + 0x2e, 0x6e, 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x54, 0x79, 0x70, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x05, 0x43, 0x6c, 0x6f, + 0x73, 0x65, 0x12, 0x12, 0x2e, 0x6e, 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x6e, 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, + 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x64, 0x61, 0x74, + 0x61, 0x62, 0x61, 0x73, 0x65, 0x2f, 0x6e, 0x65, 0x77, 0x64, 0x62, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_sdk_database_newdbplugin_proto_database_proto_rawDescOnce sync.Once + file_sdk_database_newdbplugin_proto_database_proto_rawDescData = file_sdk_database_newdbplugin_proto_database_proto_rawDesc +) + +func file_sdk_database_newdbplugin_proto_database_proto_rawDescGZIP() []byte { + file_sdk_database_newdbplugin_proto_database_proto_rawDescOnce.Do(func() { + file_sdk_database_newdbplugin_proto_database_proto_rawDescData = protoimpl.X.CompressGZIP(file_sdk_database_newdbplugin_proto_database_proto_rawDescData) + }) + return file_sdk_database_newdbplugin_proto_database_proto_rawDescData +} + +var file_sdk_database_newdbplugin_proto_database_proto_msgTypes = make([]protoimpl.MessageInfo, 14) +var file_sdk_database_newdbplugin_proto_database_proto_goTypes = []interface{}{ + (*InitializeRequest)(nil), // 0: newdbplugin.InitializeRequest + (*InitializeResponse)(nil), // 1: newdbplugin.InitializeResponse + (*NewUserRequest)(nil), // 2: newdbplugin.NewUserRequest + (*UsernameConfig)(nil), // 3: newdbplugin.UsernameConfig + (*NewUserResponse)(nil), // 4: newdbplugin.NewUserResponse + (*UpdateUserRequest)(nil), // 5: newdbplugin.UpdateUserRequest + (*ChangePassword)(nil), // 6: newdbplugin.ChangePassword + (*ChangeExpiration)(nil), // 7: newdbplugin.ChangeExpiration + (*UpdateUserResponse)(nil), // 8: newdbplugin.UpdateUserResponse + (*DeleteUserRequest)(nil), // 9: newdbplugin.DeleteUserRequest + (*DeleteUserResponse)(nil), // 10: newdbplugin.DeleteUserResponse + (*TypeResponse)(nil), // 11: newdbplugin.TypeResponse + (*Statements)(nil), // 12: newdbplugin.Statements + (*Empty)(nil), // 13: newdbplugin.Empty + (*_struct.Struct)(nil), // 14: google.protobuf.Struct + (*timestamp.Timestamp)(nil), // 15: google.protobuf.Timestamp +} +var file_sdk_database_newdbplugin_proto_database_proto_depIdxs = []int32{ + 14, // 0: newdbplugin.InitializeRequest.config_data:type_name -> google.protobuf.Struct + 14, // 1: newdbplugin.InitializeResponse.config_data:type_name -> google.protobuf.Struct + 3, // 2: newdbplugin.NewUserRequest.username_config:type_name -> newdbplugin.UsernameConfig + 15, // 3: newdbplugin.NewUserRequest.expiration:type_name -> google.protobuf.Timestamp + 12, // 4: newdbplugin.NewUserRequest.statements:type_name -> newdbplugin.Statements + 12, // 5: newdbplugin.NewUserRequest.rollback_statements:type_name -> newdbplugin.Statements + 6, // 6: newdbplugin.UpdateUserRequest.password:type_name -> newdbplugin.ChangePassword + 7, // 7: newdbplugin.UpdateUserRequest.expiration:type_name -> newdbplugin.ChangeExpiration + 12, // 8: newdbplugin.ChangePassword.statements:type_name -> newdbplugin.Statements + 15, // 9: newdbplugin.ChangeExpiration.new_expiration:type_name -> google.protobuf.Timestamp + 12, // 10: newdbplugin.ChangeExpiration.statements:type_name -> newdbplugin.Statements + 12, // 11: newdbplugin.DeleteUserRequest.statements:type_name -> newdbplugin.Statements + 0, // 12: newdbplugin.Database.Initialize:input_type -> newdbplugin.InitializeRequest + 2, // 13: newdbplugin.Database.NewUser:input_type -> newdbplugin.NewUserRequest + 5, // 14: newdbplugin.Database.UpdateUser:input_type -> newdbplugin.UpdateUserRequest + 9, // 15: newdbplugin.Database.DeleteUser:input_type -> newdbplugin.DeleteUserRequest + 13, // 16: newdbplugin.Database.Type:input_type -> newdbplugin.Empty + 13, // 17: newdbplugin.Database.Close:input_type -> newdbplugin.Empty + 1, // 18: newdbplugin.Database.Initialize:output_type -> newdbplugin.InitializeResponse + 4, // 19: newdbplugin.Database.NewUser:output_type -> newdbplugin.NewUserResponse + 8, // 20: newdbplugin.Database.UpdateUser:output_type -> newdbplugin.UpdateUserResponse + 10, // 21: newdbplugin.Database.DeleteUser:output_type -> newdbplugin.DeleteUserResponse + 11, // 22: newdbplugin.Database.Type:output_type -> newdbplugin.TypeResponse + 13, // 23: newdbplugin.Database.Close:output_type -> newdbplugin.Empty + 18, // [18:24] is the sub-list for method output_type + 12, // [12:18] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name +} + +func init() { file_sdk_database_newdbplugin_proto_database_proto_init() } +func file_sdk_database_newdbplugin_proto_database_proto_init() { + if File_sdk_database_newdbplugin_proto_database_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_sdk_database_newdbplugin_proto_database_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InitializeRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sdk_database_newdbplugin_proto_database_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InitializeResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sdk_database_newdbplugin_proto_database_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewUserRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sdk_database_newdbplugin_proto_database_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UsernameConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sdk_database_newdbplugin_proto_database_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewUserResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sdk_database_newdbplugin_proto_database_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateUserRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sdk_database_newdbplugin_proto_database_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChangePassword); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sdk_database_newdbplugin_proto_database_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ChangeExpiration); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sdk_database_newdbplugin_proto_database_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateUserResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sdk_database_newdbplugin_proto_database_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteUserRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sdk_database_newdbplugin_proto_database_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteUserResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sdk_database_newdbplugin_proto_database_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TypeResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sdk_database_newdbplugin_proto_database_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Statements); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sdk_database_newdbplugin_proto_database_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Empty); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_sdk_database_newdbplugin_proto_database_proto_rawDesc, + NumEnums: 0, + NumMessages: 14, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_sdk_database_newdbplugin_proto_database_proto_goTypes, + DependencyIndexes: file_sdk_database_newdbplugin_proto_database_proto_depIdxs, + MessageInfos: file_sdk_database_newdbplugin_proto_database_proto_msgTypes, + }.Build() + File_sdk_database_newdbplugin_proto_database_proto = out.File + file_sdk_database_newdbplugin_proto_database_proto_rawDesc = nil + file_sdk_database_newdbplugin_proto_database_proto_goTypes = nil + file_sdk_database_newdbplugin_proto_database_proto_depIdxs = nil +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConnInterface + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion6 + +// DatabaseClient is the client API for Database service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type DatabaseClient interface { + Initialize(ctx context.Context, in *InitializeRequest, opts ...grpc.CallOption) (*InitializeResponse, error) + NewUser(ctx context.Context, in *NewUserRequest, opts ...grpc.CallOption) (*NewUserResponse, error) + UpdateUser(ctx context.Context, in *UpdateUserRequest, opts ...grpc.CallOption) (*UpdateUserResponse, error) + DeleteUser(ctx context.Context, in *DeleteUserRequest, opts ...grpc.CallOption) (*DeleteUserResponse, error) + Type(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TypeResponse, error) + Close(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) +} + +type databaseClient struct { + cc grpc.ClientConnInterface +} + +func NewDatabaseClient(cc grpc.ClientConnInterface) DatabaseClient { + return &databaseClient{cc} +} + +func (c *databaseClient) Initialize(ctx context.Context, in *InitializeRequest, opts ...grpc.CallOption) (*InitializeResponse, error) { + out := new(InitializeResponse) + err := c.cc.Invoke(ctx, "/newdbplugin.Database/Initialize", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *databaseClient) NewUser(ctx context.Context, in *NewUserRequest, opts ...grpc.CallOption) (*NewUserResponse, error) { + out := new(NewUserResponse) + err := c.cc.Invoke(ctx, "/newdbplugin.Database/NewUser", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *databaseClient) UpdateUser(ctx context.Context, in *UpdateUserRequest, opts ...grpc.CallOption) (*UpdateUserResponse, error) { + out := new(UpdateUserResponse) + err := c.cc.Invoke(ctx, "/newdbplugin.Database/UpdateUser", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *databaseClient) DeleteUser(ctx context.Context, in *DeleteUserRequest, opts ...grpc.CallOption) (*DeleteUserResponse, error) { + out := new(DeleteUserResponse) + err := c.cc.Invoke(ctx, "/newdbplugin.Database/DeleteUser", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *databaseClient) Type(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*TypeResponse, error) { + out := new(TypeResponse) + err := c.cc.Invoke(ctx, "/newdbplugin.Database/Type", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *databaseClient) Close(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) { + out := new(Empty) + err := c.cc.Invoke(ctx, "/newdbplugin.Database/Close", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// DatabaseServer is the server API for Database service. +type DatabaseServer interface { + Initialize(context.Context, *InitializeRequest) (*InitializeResponse, error) + NewUser(context.Context, *NewUserRequest) (*NewUserResponse, error) + UpdateUser(context.Context, *UpdateUserRequest) (*UpdateUserResponse, error) + DeleteUser(context.Context, *DeleteUserRequest) (*DeleteUserResponse, error) + Type(context.Context, *Empty) (*TypeResponse, error) + Close(context.Context, *Empty) (*Empty, error) +} + +// UnimplementedDatabaseServer can be embedded to have forward compatible implementations. +type UnimplementedDatabaseServer struct { +} + +func (*UnimplementedDatabaseServer) Initialize(context.Context, *InitializeRequest) (*InitializeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Initialize not implemented") +} +func (*UnimplementedDatabaseServer) NewUser(context.Context, *NewUserRequest) (*NewUserResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method NewUser not implemented") +} +func (*UnimplementedDatabaseServer) UpdateUser(context.Context, *UpdateUserRequest) (*UpdateUserResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateUser not implemented") +} +func (*UnimplementedDatabaseServer) DeleteUser(context.Context, *DeleteUserRequest) (*DeleteUserResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteUser not implemented") +} +func (*UnimplementedDatabaseServer) Type(context.Context, *Empty) (*TypeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Type not implemented") +} +func (*UnimplementedDatabaseServer) Close(context.Context, *Empty) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Close not implemented") +} + +func RegisterDatabaseServer(s *grpc.Server, srv DatabaseServer) { + s.RegisterService(&_Database_serviceDesc, srv) +} + +func _Database_Initialize_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InitializeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DatabaseServer).Initialize(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/newdbplugin.Database/Initialize", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DatabaseServer).Initialize(ctx, req.(*InitializeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Database_NewUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NewUserRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DatabaseServer).NewUser(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/newdbplugin.Database/NewUser", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DatabaseServer).NewUser(ctx, req.(*NewUserRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Database_UpdateUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateUserRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DatabaseServer).UpdateUser(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/newdbplugin.Database/UpdateUser", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DatabaseServer).UpdateUser(ctx, req.(*UpdateUserRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Database_DeleteUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteUserRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DatabaseServer).DeleteUser(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/newdbplugin.Database/DeleteUser", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DatabaseServer).DeleteUser(ctx, req.(*DeleteUserRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Database_Type_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DatabaseServer).Type(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/newdbplugin.Database/Type", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DatabaseServer).Type(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Database_Close_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DatabaseServer).Close(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/newdbplugin.Database/Close", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DatabaseServer).Close(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +var _Database_serviceDesc = grpc.ServiceDesc{ + ServiceName: "newdbplugin.Database", + HandlerType: (*DatabaseServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Initialize", + Handler: _Database_Initialize_Handler, + }, + { + MethodName: "NewUser", + Handler: _Database_NewUser_Handler, + }, + { + MethodName: "UpdateUser", + Handler: _Database_UpdateUser_Handler, + }, + { + MethodName: "DeleteUser", + Handler: _Database_DeleteUser_Handler, + }, + { + MethodName: "Type", + Handler: _Database_Type_Handler, + }, + { + MethodName: "Close", + Handler: _Database_Close_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "sdk/database/newdbplugin/proto/database.proto", +} diff --git a/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/proto/database.proto b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/proto/database.proto new file mode 100644 index 0000000000..74eb4f6701 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/sdk/database/newdbplugin/proto/database.proto @@ -0,0 +1,95 @@ +syntax = "proto3"; +package newdbplugin; // TODO: Update name + +option go_package = "github.com/hashicorp/vault/sdk/database/newdbplugin/proto"; + +import "google/protobuf/struct.proto"; +import "google/protobuf/timestamp.proto"; + +///////////////// +// Initialize() +///////////////// +message InitializeRequest { + google.protobuf.Struct config_data = 1; + bool verify_connection = 2; +} + +message InitializeResponse { + google.protobuf.Struct config_data = 1; +} +///////////////// +// NewUser() +///////////////// + +message NewUserRequest { + UsernameConfig username_config = 1; + string password = 2; + google.protobuf.Timestamp expiration = 3; + Statements statements = 4; + Statements rollback_statements = 5; +} + +message UsernameConfig { + string display_name = 1; + string role_name = 2; +} + +message NewUserResponse { + string username = 1; +} + +///////////////// +// UpdateUser() +///////////////// +message UpdateUserRequest { + string username = 1; + ChangePassword password = 2; + ChangeExpiration expiration = 3; +} + +message ChangePassword { + string new_password = 1; + Statements statements = 2; +} + +message ChangeExpiration { + google.protobuf.Timestamp new_expiration = 1; + Statements statements = 2; +} + +message UpdateUserResponse {} + +///////////////// +// DeleteUser() +///////////////// +message DeleteUserRequest { + string username = 1; + Statements statements = 2; +} + +message DeleteUserResponse {} + +///////////////// +// Type() +///////////////// +message TypeResponse { + string Type = 1; +} + +///////////////// +// General purpose +///////////////// +message Statements { + repeated string Commands = 1; +} + +message Empty {} + +service Database { + rpc Initialize(InitializeRequest) returns (InitializeResponse); + rpc NewUser(NewUserRequest) returns (NewUserResponse); + rpc UpdateUser(UpdateUserRequest) returns (UpdateUserResponse); + rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse); + rpc Type(Empty) returns (TypeResponse); + rpc Close(Empty) returns (Empty); +} diff --git a/vendor/github.com/stretchr/objx/.codeclimate.yml b/vendor/github.com/stretchr/objx/.codeclimate.yml new file mode 100644 index 0000000000..559fa399c1 --- /dev/null +++ b/vendor/github.com/stretchr/objx/.codeclimate.yml @@ -0,0 +1,21 @@ +engines: + gofmt: + enabled: true + golint: + enabled: true + govet: + enabled: true + +exclude_patterns: +- ".github/" +- "vendor/" +- "codegen/" +- "*.yml" +- ".*.yml" +- "*.md" +- "Gopkg.*" +- "doc.go" +- "type_specific_codegen_test.go" +- "type_specific_codegen.go" +- ".gitignore" +- "LICENSE" diff --git a/vendor/github.com/stretchr/objx/.gitignore b/vendor/github.com/stretchr/objx/.gitignore new file mode 100644 index 0000000000..ea58090bd2 --- /dev/null +++ b/vendor/github.com/stretchr/objx/.gitignore @@ -0,0 +1,11 @@ +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out diff --git a/vendor/github.com/stretchr/objx/.travis.yml b/vendor/github.com/stretchr/objx/.travis.yml new file mode 100644 index 0000000000..cde6eb2aff --- /dev/null +++ b/vendor/github.com/stretchr/objx/.travis.yml @@ -0,0 +1,30 @@ +language: go +go: + - "1.10.x" + - "1.11.x" + - "1.12.x" + - master + +matrix: + allow_failures: + - go: master +fast_finish: true + +env: + global: + - CC_TEST_REPORTER_ID=68feaa3410049ce73e145287acbcdacc525087a30627f96f04e579e75bd71c00 + +before_script: + - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter + - chmod +x ./cc-test-reporter + - ./cc-test-reporter before-build + +install: + - curl -sL https://taskfile.dev/install.sh | sh + +script: + - diff -u <(echo -n) <(./bin/task lint) + - ./bin/task test-coverage + +after_script: + - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT diff --git a/vendor/github.com/stretchr/objx/LICENSE b/vendor/github.com/stretchr/objx/LICENSE new file mode 100644 index 0000000000..44d4d9d5a7 --- /dev/null +++ b/vendor/github.com/stretchr/objx/LICENSE @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2014 Stretchr, Inc. +Copyright (c) 2017-2018 objx contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/stretchr/objx/README.md b/vendor/github.com/stretchr/objx/README.md new file mode 100644 index 0000000000..246660b21a --- /dev/null +++ b/vendor/github.com/stretchr/objx/README.md @@ -0,0 +1,80 @@ +# Objx +[](https://travis-ci.org/stretchr/objx) +[](https://goreportcard.com/report/github.com/stretchr/objx) +[](https://codeclimate.com/github/stretchr/objx/maintainability) +[](https://codeclimate.com/github/stretchr/objx/test_coverage) +[](https://sourcegraph.com/github.com/stretchr/objx) +[](https://godoc.org/github.com/stretchr/objx) + +Objx - Go package for dealing with maps, slices, JSON and other data. + +Get started: + +- Install Objx with [one line of code](#installation), or [update it with another](#staying-up-to-date) +- Check out the API Documentation http://godoc.org/github.com/stretchr/objx + +## Overview +Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes a powerful `Get` method (among others) that allows you to easily and quickly get access to data within the map, without having to worry too much about type assertions, missing data, default values etc. + +### Pattern +Objx uses a preditable pattern to make access data from within `map[string]interface{}` easy. Call one of the `objx.` functions to create your `objx.Map` to get going: + + m, err := objx.FromJSON(json) + +NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, the rest will be optimistic and try to figure things out without panicking. + +Use `Get` to access the value you're interested in. You can use dot and array +notation too: + + m.Get("places[0].latlng") + +Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type. + + if m.Get("code").IsStr() { // Your code... } + +Or you can just assume the type, and use one of the strong type methods to extract the real value: + + m.Get("code").Int() + +If there's no value there (or if it's the wrong type) then a default value will be returned, or you can be explicit about the default value. + + Get("code").Int(-1) + +If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating, manipulating and selecting that data. You can find out more by exploring the index below. + +### Reading data +A simple example of how to use Objx: + + // Use MustFromJSON to make an objx.Map from some JSON + m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) + + // Get the details + name := m.Get("name").Str() + age := m.Get("age").Int() + + // Get their nickname (or use their name if they don't have one) + nickname := m.Get("nickname").Str(name) + +### Ranging +Since `objx.Map` is a `map[string]interface{}` you can treat it as such. For example, to `range` the data, do what you would expect: + + m := objx.MustFromJSON(json) + for key, value := range m { + // Your code... + } + +## Installation +To install Objx, use go get: + + go get github.com/stretchr/objx + +### Staying up to date +To update Objx to the latest version, run: + + go get -u github.com/stretchr/objx + +### Supported go versions +We support the lastest three major Go versions, which are 1.10, 1.11 and 1.12 at the moment. + +## Contributing +Please feel free to submit issues, fork the repository and send pull requests! diff --git a/vendor/github.com/stretchr/objx/Taskfile.yml b/vendor/github.com/stretchr/objx/Taskfile.yml new file mode 100644 index 0000000000..a749ac5492 --- /dev/null +++ b/vendor/github.com/stretchr/objx/Taskfile.yml @@ -0,0 +1,30 @@ +version: '2' + +env: + GOFLAGS: -mod=vendor + +tasks: + default: + deps: [test] + + lint: + desc: Checks code style + cmds: + - gofmt -d -s *.go + - go vet ./... + silent: true + + lint-fix: + desc: Fixes code style + cmds: + - gofmt -w -s *.go + + test: + desc: Runs go tests + cmds: + - go test -race ./... + + test-coverage: + desc: Runs go tests and calucates test coverage + cmds: + - go test -race -coverprofile=c.out ./... diff --git a/vendor/github.com/stretchr/objx/accessors.go b/vendor/github.com/stretchr/objx/accessors.go new file mode 100644 index 0000000000..6763162811 --- /dev/null +++ b/vendor/github.com/stretchr/objx/accessors.go @@ -0,0 +1,119 @@ +package objx + +import ( + "regexp" + "strconv" + "strings" +) + +const ( + // PathSeparator is the character used to separate the elements + // of the keypath. + // + // For example, `location.address.city` + PathSeparator string = "." + + // arrayAccesRegexString is the regex used to extract the array number + // from the access path + arrayAccesRegexString = `^(.+)\[([0-9]+)\]$` +) + +// arrayAccesRegex is the compiled arrayAccesRegexString +var arrayAccesRegex = regexp.MustCompile(arrayAccesRegexString) + +// Get gets the value using the specified selector and +// returns it inside a new Obj object. +// +// If it cannot find the value, Get will return a nil +// value inside an instance of Obj. +// +// Get can only operate directly on map[string]interface{} and []interface. +// +// Example +// +// To access the title of the third chapter of the second book, do: +// +// o.Get("books[1].chapters[2].title") +func (m Map) Get(selector string) *Value { + rawObj := access(m, selector, nil, false) + return &Value{data: rawObj} +} + +// Set sets the value using the specified selector and +// returns the object on which Set was called. +// +// Set can only operate directly on map[string]interface{} and []interface +// +// Example +// +// To set the title of the third chapter of the second book, do: +// +// o.Set("books[1].chapters[2].title","Time to Go") +func (m Map) Set(selector string, value interface{}) Map { + access(m, selector, value, true) + return m +} + +// getIndex returns the index, which is hold in s by two braches. +// It also returns s withour the index part, e.g. name[1] will return (1, name). +// If no index is found, -1 is returned +func getIndex(s string) (int, string) { + arrayMatches := arrayAccesRegex.FindStringSubmatch(s) + if len(arrayMatches) > 0 { + // Get the key into the map + selector := arrayMatches[1] + // Get the index into the array at the key + // We know this cannt fail because arrayMatches[2] is an int for sure + index, _ := strconv.Atoi(arrayMatches[2]) + return index, selector + } + return -1, s +} + +// access accesses the object using the selector and performs the +// appropriate action. +func access(current interface{}, selector string, value interface{}, isSet bool) interface{} { + selSegs := strings.SplitN(selector, PathSeparator, 2) + thisSel := selSegs[0] + index := -1 + + if strings.Contains(thisSel, "[") { + index, thisSel = getIndex(thisSel) + } + + if curMap, ok := current.(Map); ok { + current = map[string]interface{}(curMap) + } + // get the object in question + switch current.(type) { + case map[string]interface{}: + curMSI := current.(map[string]interface{}) + if len(selSegs) <= 1 && isSet { + curMSI[thisSel] = value + return nil + } + + _, ok := curMSI[thisSel].(map[string]interface{}) + if (curMSI[thisSel] == nil || !ok) && index == -1 && isSet { + curMSI[thisSel] = map[string]interface{}{} + } + + current = curMSI[thisSel] + default: + current = nil + } + // do we need to access the item of an array? + if index > -1 { + if array, ok := current.([]interface{}); ok { + if index < len(array) { + current = array[index] + } else { + current = nil + } + } + } + if len(selSegs) > 1 { + current = access(current, selSegs[1], value, isSet) + } + return current +} diff --git a/vendor/github.com/stretchr/objx/conversions.go b/vendor/github.com/stretchr/objx/conversions.go new file mode 100644 index 0000000000..080aa46e47 --- /dev/null +++ b/vendor/github.com/stretchr/objx/conversions.go @@ -0,0 +1,280 @@ +package objx + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "net/url" + "strconv" +) + +// SignatureSeparator is the character that is used to +// separate the Base64 string from the security signature. +const SignatureSeparator = "_" + +// URLValuesSliceKeySuffix is the character that is used to +// specify a suffic for slices parsed by URLValues. +// If the suffix is set to "[i]", then the index of the slice +// is used in place of i +// Ex: Suffix "[]" would have the form a[]=b&a[]=c +// OR Suffix "[i]" would have the form a[0]=b&a[1]=c +// OR Suffix "" would have the form a=b&a=c +var urlValuesSliceKeySuffix = "[]" + +const ( + URLValuesSliceKeySuffixEmpty = "" + URLValuesSliceKeySuffixArray = "[]" + URLValuesSliceKeySuffixIndex = "[i]" +) + +// SetURLValuesSliceKeySuffix sets the character that is used to +// specify a suffic for slices parsed by URLValues. +// If the suffix is set to "[i]", then the index of the slice +// is used in place of i +// Ex: Suffix "[]" would have the form a[]=b&a[]=c +// OR Suffix "[i]" would have the form a[0]=b&a[1]=c +// OR Suffix "" would have the form a=b&a=c +func SetURLValuesSliceKeySuffix(s string) error { + if s == URLValuesSliceKeySuffixEmpty || s == URLValuesSliceKeySuffixArray || s == URLValuesSliceKeySuffixIndex { + urlValuesSliceKeySuffix = s + return nil + } + + return errors.New("objx: Invalid URLValuesSliceKeySuffix provided.") +} + +// JSON converts the contained object to a JSON string +// representation +func (m Map) JSON() (string, error) { + for k, v := range m { + m[k] = cleanUp(v) + } + + result, err := json.Marshal(m) + if err != nil { + err = errors.New("objx: JSON encode failed with: " + err.Error()) + } + return string(result), err +} + +func cleanUpInterfaceArray(in []interface{}) []interface{} { + result := make([]interface{}, len(in)) + for i, v := range in { + result[i] = cleanUp(v) + } + return result +} + +func cleanUpInterfaceMap(in map[interface{}]interface{}) Map { + result := Map{} + for k, v := range in { + result[fmt.Sprintf("%v", k)] = cleanUp(v) + } + return result +} + +func cleanUpStringMap(in map[string]interface{}) Map { + result := Map{} + for k, v := range in { + result[k] = cleanUp(v) + } + return result +} + +func cleanUpMSIArray(in []map[string]interface{}) []Map { + result := make([]Map, len(in)) + for i, v := range in { + result[i] = cleanUpStringMap(v) + } + return result +} + +func cleanUpMapArray(in []Map) []Map { + result := make([]Map, len(in)) + for i, v := range in { + result[i] = cleanUpStringMap(v) + } + return result +} + +func cleanUp(v interface{}) interface{} { + switch v := v.(type) { + case []interface{}: + return cleanUpInterfaceArray(v) + case []map[string]interface{}: + return cleanUpMSIArray(v) + case map[interface{}]interface{}: + return cleanUpInterfaceMap(v) + case Map: + return cleanUpStringMap(v) + case []Map: + return cleanUpMapArray(v) + default: + return v + } +} + +// MustJSON converts the contained object to a JSON string +// representation and panics if there is an error +func (m Map) MustJSON() string { + result, err := m.JSON() + if err != nil { + panic(err.Error()) + } + return result +} + +// Base64 converts the contained object to a Base64 string +// representation of the JSON string representation +func (m Map) Base64() (string, error) { + var buf bytes.Buffer + + jsonData, err := m.JSON() + if err != nil { + return "", err + } + + encoder := base64.NewEncoder(base64.StdEncoding, &buf) + _, _ = encoder.Write([]byte(jsonData)) + _ = encoder.Close() + + return buf.String(), nil +} + +// MustBase64 converts the contained object to a Base64 string +// representation of the JSON string representation and panics +// if there is an error +func (m Map) MustBase64() string { + result, err := m.Base64() + if err != nil { + panic(err.Error()) + } + return result +} + +// SignedBase64 converts the contained object to a Base64 string +// representation of the JSON string representation and signs it +// using the provided key. +func (m Map) SignedBase64(key string) (string, error) { + base64, err := m.Base64() + if err != nil { + return "", err + } + + sig := HashWithKey(base64, key) + return base64 + SignatureSeparator + sig, nil +} + +// MustSignedBase64 converts the contained object to a Base64 string +// representation of the JSON string representation and signs it +// using the provided key and panics if there is an error +func (m Map) MustSignedBase64(key string) string { + result, err := m.SignedBase64(key) + if err != nil { + panic(err.Error()) + } + return result +} + +/* + URL Query + ------------------------------------------------ +*/ + +// URLValues creates a url.Values object from an Obj. This +// function requires that the wrapped object be a map[string]interface{} +func (m Map) URLValues() url.Values { + vals := make(url.Values) + + m.parseURLValues(m, vals, "") + + return vals +} + +func (m Map) parseURLValues(queryMap Map, vals url.Values, key string) { + useSliceIndex := false + if urlValuesSliceKeySuffix == "[i]" { + useSliceIndex = true + } + + for k, v := range queryMap { + val := &Value{data: v} + switch { + case val.IsObjxMap(): + if key == "" { + m.parseURLValues(val.ObjxMap(), vals, k) + } else { + m.parseURLValues(val.ObjxMap(), vals, key+"["+k+"]") + } + case val.IsObjxMapSlice(): + sliceKey := k + if key != "" { + sliceKey = key + "[" + k + "]" + } + + if useSliceIndex { + for i, sv := range val.MustObjxMapSlice() { + sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]" + m.parseURLValues(sv, vals, sk) + } + } else { + sliceKey = sliceKey + urlValuesSliceKeySuffix + for _, sv := range val.MustObjxMapSlice() { + m.parseURLValues(sv, vals, sliceKey) + } + } + case val.IsMSISlice(): + sliceKey := k + if key != "" { + sliceKey = key + "[" + k + "]" + } + + if useSliceIndex { + for i, sv := range val.MustMSISlice() { + sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]" + m.parseURLValues(New(sv), vals, sk) + } + } else { + sliceKey = sliceKey + urlValuesSliceKeySuffix + for _, sv := range val.MustMSISlice() { + m.parseURLValues(New(sv), vals, sliceKey) + } + } + case val.IsStrSlice(), val.IsBoolSlice(), + val.IsFloat32Slice(), val.IsFloat64Slice(), + val.IsIntSlice(), val.IsInt8Slice(), val.IsInt16Slice(), val.IsInt32Slice(), val.IsInt64Slice(), + val.IsUintSlice(), val.IsUint8Slice(), val.IsUint16Slice(), val.IsUint32Slice(), val.IsUint64Slice(): + + sliceKey := k + if key != "" { + sliceKey = key + "[" + k + "]" + } + + if useSliceIndex { + for i, sv := range val.StringSlice() { + sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]" + vals.Set(sk, sv) + } + } else { + sliceKey = sliceKey + urlValuesSliceKeySuffix + vals[sliceKey] = val.StringSlice() + } + + default: + if key == "" { + vals.Set(k, val.String()) + } else { + vals.Set(key+"["+k+"]", val.String()) + } + } + } +} + +// URLQuery gets an encoded URL query representing the given +// Obj. This function requires that the wrapped object be a +// map[string]interface{} +func (m Map) URLQuery() (string, error) { + return m.URLValues().Encode(), nil +} diff --git a/vendor/github.com/stretchr/objx/doc.go b/vendor/github.com/stretchr/objx/doc.go new file mode 100644 index 0000000000..6d6af1a83a --- /dev/null +++ b/vendor/github.com/stretchr/objx/doc.go @@ -0,0 +1,66 @@ +/* +Objx - Go package for dealing with maps, slices, JSON and other data. + +Overview + +Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes +a powerful `Get` method (among others) that allows you to easily and quickly get +access to data within the map, without having to worry too much about type assertions, +missing data, default values etc. + +Pattern + +Objx uses a preditable pattern to make access data from within `map[string]interface{}` easy. +Call one of the `objx.` functions to create your `objx.Map` to get going: + + m, err := objx.FromJSON(json) + +NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, +the rest will be optimistic and try to figure things out without panicking. + +Use `Get` to access the value you're interested in. You can use dot and array +notation too: + + m.Get("places[0].latlng") + +Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type. + + if m.Get("code").IsStr() { // Your code... } + +Or you can just assume the type, and use one of the strong type methods to extract the real value: + + m.Get("code").Int() + +If there's no value there (or if it's the wrong type) then a default value will be returned, +or you can be explicit about the default value. + + Get("code").Int(-1) + +If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating, +manipulating and selecting that data. You can find out more by exploring the index below. + +Reading data + +A simple example of how to use Objx: + + // Use MustFromJSON to make an objx.Map from some JSON + m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) + + // Get the details + name := m.Get("name").Str() + age := m.Get("age").Int() + + // Get their nickname (or use their name if they don't have one) + nickname := m.Get("nickname").Str(name) + +Ranging + +Since `objx.Map` is a `map[string]interface{}` you can treat it as such. +For example, to `range` the data, do what you would expect: + + m := objx.MustFromJSON(json) + for key, value := range m { + // Your code... + } +*/ +package objx diff --git a/vendor/github.com/stretchr/objx/go.mod b/vendor/github.com/stretchr/objx/go.mod new file mode 100644 index 0000000000..31ec5a7d94 --- /dev/null +++ b/vendor/github.com/stretchr/objx/go.mod @@ -0,0 +1,8 @@ +module github.com/stretchr/objx + +go 1.12 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/stretchr/testify v1.3.0 +) diff --git a/vendor/github.com/stretchr/objx/go.sum b/vendor/github.com/stretchr/objx/go.sum new file mode 100644 index 0000000000..4f89841505 --- /dev/null +++ b/vendor/github.com/stretchr/objx/go.sum @@ -0,0 +1,8 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/vendor/github.com/stretchr/objx/map.go b/vendor/github.com/stretchr/objx/map.go new file mode 100644 index 0000000000..95149c06a6 --- /dev/null +++ b/vendor/github.com/stretchr/objx/map.go @@ -0,0 +1,228 @@ +package objx + +import ( + "encoding/base64" + "encoding/json" + "errors" + "io/ioutil" + "net/url" + "strings" +) + +// MSIConvertable is an interface that defines methods for converting your +// custom types to a map[string]interface{} representation. +type MSIConvertable interface { + // MSI gets a map[string]interface{} (msi) representing the + // object. + MSI() map[string]interface{} +} + +// Map provides extended functionality for working with +// untyped data, in particular map[string]interface (msi). +type Map map[string]interface{} + +// Value returns the internal value instance +func (m Map) Value() *Value { + return &Value{data: m} +} + +// Nil represents a nil Map. +var Nil = New(nil) + +// New creates a new Map containing the map[string]interface{} in the data argument. +// If the data argument is not a map[string]interface, New attempts to call the +// MSI() method on the MSIConvertable interface to create one. +func New(data interface{}) Map { + if _, ok := data.(map[string]interface{}); !ok { + if converter, ok := data.(MSIConvertable); ok { + data = converter.MSI() + } else { + return nil + } + } + return Map(data.(map[string]interface{})) +} + +// MSI creates a map[string]interface{} and puts it inside a new Map. +// +// The arguments follow a key, value pattern. +// +// +// Returns nil if any key argument is non-string or if there are an odd number of arguments. +// +// Example +// +// To easily create Maps: +// +// m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true)) +// +// // creates an Map equivalent to +// m := objx.Map{"name": "Mat", "age": 29, "subobj": objx.Map{"active": true}} +func MSI(keyAndValuePairs ...interface{}) Map { + newMap := Map{} + keyAndValuePairsLen := len(keyAndValuePairs) + if keyAndValuePairsLen%2 != 0 { + return nil + } + for i := 0; i < keyAndValuePairsLen; i = i + 2 { + key := keyAndValuePairs[i] + value := keyAndValuePairs[i+1] + + // make sure the key is a string + keyString, keyStringOK := key.(string) + if !keyStringOK { + return nil + } + newMap[keyString] = value + } + return newMap +} + +// ****** Conversion Constructors + +// MustFromJSON creates a new Map containing the data specified in the +// jsonString. +// +// Panics if the JSON is invalid. +func MustFromJSON(jsonString string) Map { + o, err := FromJSON(jsonString) + if err != nil { + panic("objx: MustFromJSON failed with error: " + err.Error()) + } + return o +} + +// FromJSON creates a new Map containing the data specified in the +// jsonString. +// +// Returns an error if the JSON is invalid. +func FromJSON(jsonString string) (Map, error) { + var m Map + err := json.Unmarshal([]byte(jsonString), &m) + if err != nil { + return Nil, err + } + m.tryConvertFloat64() + return m, nil +} + +func (m Map) tryConvertFloat64() { + for k, v := range m { + switch v.(type) { + case float64: + f := v.(float64) + if float64(int(f)) == f { + m[k] = int(f) + } + case map[string]interface{}: + t := New(v) + t.tryConvertFloat64() + m[k] = t + case []interface{}: + m[k] = tryConvertFloat64InSlice(v.([]interface{})) + } + } +} + +func tryConvertFloat64InSlice(s []interface{}) []interface{} { + for k, v := range s { + switch v.(type) { + case float64: + f := v.(float64) + if float64(int(f)) == f { + s[k] = int(f) + } + case map[string]interface{}: + t := New(v) + t.tryConvertFloat64() + s[k] = t + case []interface{}: + s[k] = tryConvertFloat64InSlice(v.([]interface{})) + } + } + return s +} + +// FromBase64 creates a new Obj containing the data specified +// in the Base64 string. +// +// The string is an encoded JSON string returned by Base64 +func FromBase64(base64String string) (Map, error) { + decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64String)) + decoded, err := ioutil.ReadAll(decoder) + if err != nil { + return nil, err + } + return FromJSON(string(decoded)) +} + +// MustFromBase64 creates a new Obj containing the data specified +// in the Base64 string and panics if there is an error. +// +// The string is an encoded JSON string returned by Base64 +func MustFromBase64(base64String string) Map { + result, err := FromBase64(base64String) + if err != nil { + panic("objx: MustFromBase64 failed with error: " + err.Error()) + } + return result +} + +// FromSignedBase64 creates a new Obj containing the data specified +// in the Base64 string. +// +// The string is an encoded JSON string returned by SignedBase64 +func FromSignedBase64(base64String, key string) (Map, error) { + parts := strings.Split(base64String, SignatureSeparator) + if len(parts) != 2 { + return nil, errors.New("objx: Signed base64 string is malformed") + } + + sig := HashWithKey(parts[0], key) + if parts[1] != sig { + return nil, errors.New("objx: Signature for base64 data does not match") + } + return FromBase64(parts[0]) +} + +// MustFromSignedBase64 creates a new Obj containing the data specified +// in the Base64 string and panics if there is an error. +// +// The string is an encoded JSON string returned by Base64 +func MustFromSignedBase64(base64String, key string) Map { + result, err := FromSignedBase64(base64String, key) + if err != nil { + panic("objx: MustFromSignedBase64 failed with error: " + err.Error()) + } + return result +} + +// FromURLQuery generates a new Obj by parsing the specified +// query. +// +// For queries with multiple values, the first value is selected. +func FromURLQuery(query string) (Map, error) { + vals, err := url.ParseQuery(query) + if err != nil { + return nil, err + } + m := Map{} + for k, vals := range vals { + m[k] = vals[0] + } + return m, nil +} + +// MustFromURLQuery generates a new Obj by parsing the specified +// query. +// +// For queries with multiple values, the first value is selected. +// +// Panics if it encounters an error +func MustFromURLQuery(query string) Map { + o, err := FromURLQuery(query) + if err != nil { + panic("objx: MustFromURLQuery failed with error: " + err.Error()) + } + return o +} diff --git a/vendor/github.com/stretchr/objx/mutations.go b/vendor/github.com/stretchr/objx/mutations.go new file mode 100644 index 0000000000..c3400a3f70 --- /dev/null +++ b/vendor/github.com/stretchr/objx/mutations.go @@ -0,0 +1,77 @@ +package objx + +// Exclude returns a new Map with the keys in the specified []string +// excluded. +func (m Map) Exclude(exclude []string) Map { + excluded := make(Map) + for k, v := range m { + if !contains(exclude, k) { + excluded[k] = v + } + } + return excluded +} + +// Copy creates a shallow copy of the Obj. +func (m Map) Copy() Map { + copied := Map{} + for k, v := range m { + copied[k] = v + } + return copied +} + +// Merge blends the specified map with a copy of this map and returns the result. +// +// Keys that appear in both will be selected from the specified map. +// This method requires that the wrapped object be a map[string]interface{} +func (m Map) Merge(merge Map) Map { + return m.Copy().MergeHere(merge) +} + +// MergeHere blends the specified map with this map and returns the current map. +// +// Keys that appear in both will be selected from the specified map. The original map +// will be modified. This method requires that +// the wrapped object be a map[string]interface{} +func (m Map) MergeHere(merge Map) Map { + for k, v := range merge { + m[k] = v + } + return m +} + +// Transform builds a new Obj giving the transformer a chance +// to change the keys and values as it goes. This method requires that +// the wrapped object be a map[string]interface{} +func (m Map) Transform(transformer func(key string, value interface{}) (string, interface{})) Map { + newMap := Map{} + for k, v := range m { + modifiedKey, modifiedVal := transformer(k, v) + newMap[modifiedKey] = modifiedVal + } + return newMap +} + +// TransformKeys builds a new map using the specified key mapping. +// +// Unspecified keys will be unaltered. +// This method requires that the wrapped object be a map[string]interface{} +func (m Map) TransformKeys(mapping map[string]string) Map { + return m.Transform(func(key string, value interface{}) (string, interface{}) { + if newKey, ok := mapping[key]; ok { + return newKey, value + } + return key, value + }) +} + +// Checks if a string slice contains a string +func contains(s []string, e string) bool { + for _, a := range s { + if a == e { + return true + } + } + return false +} diff --git a/vendor/github.com/stretchr/objx/security.go b/vendor/github.com/stretchr/objx/security.go new file mode 100644 index 0000000000..692be8e2a9 --- /dev/null +++ b/vendor/github.com/stretchr/objx/security.go @@ -0,0 +1,12 @@ +package objx + +import ( + "crypto/sha1" + "encoding/hex" +) + +// HashWithKey hashes the specified string using the security key +func HashWithKey(data, key string) string { + d := sha1.Sum([]byte(data + ":" + key)) + return hex.EncodeToString(d[:]) +} diff --git a/vendor/github.com/stretchr/objx/tests.go b/vendor/github.com/stretchr/objx/tests.go new file mode 100644 index 0000000000..d9e0b479a4 --- /dev/null +++ b/vendor/github.com/stretchr/objx/tests.go @@ -0,0 +1,17 @@ +package objx + +// Has gets whether there is something at the specified selector +// or not. +// +// If m is nil, Has will always return false. +func (m Map) Has(selector string) bool { + if m == nil { + return false + } + return !m.Get(selector).IsNil() +} + +// IsNil gets whether the data is nil or not. +func (v *Value) IsNil() bool { + return v == nil || v.data == nil +} diff --git a/vendor/github.com/stretchr/objx/type_specific.go b/vendor/github.com/stretchr/objx/type_specific.go new file mode 100644 index 0000000000..80f88d9fa2 --- /dev/null +++ b/vendor/github.com/stretchr/objx/type_specific.go @@ -0,0 +1,346 @@ +package objx + +/* + MSI (map[string]interface{} and []map[string]interface{}) +*/ + +// MSI gets the value as a map[string]interface{}, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) MSI(optionalDefault ...map[string]interface{}) map[string]interface{} { + if s, ok := v.data.(map[string]interface{}); ok { + return s + } + if s, ok := v.data.(Map); ok { + return map[string]interface{}(s) + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustMSI gets the value as a map[string]interface{}. +// +// Panics if the object is not a map[string]interface{}. +func (v *Value) MustMSI() map[string]interface{} { + if s, ok := v.data.(Map); ok { + return map[string]interface{}(s) + } + return v.data.(map[string]interface{}) +} + +// MSISlice gets the value as a []map[string]interface{}, returns the optionalDefault +// value or nil if the value is not a []map[string]interface{}. +func (v *Value) MSISlice(optionalDefault ...[]map[string]interface{}) []map[string]interface{} { + if s, ok := v.data.([]map[string]interface{}); ok { + return s + } + + s := v.ObjxMapSlice() + if s == nil { + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil + } + + result := make([]map[string]interface{}, len(s)) + for i := range s { + result[i] = s[i].Value().MSI() + } + return result +} + +// MustMSISlice gets the value as a []map[string]interface{}. +// +// Panics if the object is not a []map[string]interface{}. +func (v *Value) MustMSISlice() []map[string]interface{} { + if s := v.MSISlice(); s != nil { + return s + } + + return v.data.([]map[string]interface{}) +} + +// IsMSI gets whether the object contained is a map[string]interface{} or not. +func (v *Value) IsMSI() bool { + _, ok := v.data.(map[string]interface{}) + if !ok { + _, ok = v.data.(Map) + } + return ok +} + +// IsMSISlice gets whether the object contained is a []map[string]interface{} or not. +func (v *Value) IsMSISlice() bool { + _, ok := v.data.([]map[string]interface{}) + if !ok { + _, ok = v.data.([]Map) + if !ok { + s, ok := v.data.([]interface{}) + if ok { + for i := range s { + switch s[i].(type) { + case Map: + case map[string]interface{}: + default: + return false + } + } + return true + } + } + } + return ok +} + +// EachMSI calls the specified callback for each object +// in the []map[string]interface{}. +// +// Panics if the object is the wrong type. +func (v *Value) EachMSI(callback func(int, map[string]interface{}) bool) *Value { + for index, val := range v.MustMSISlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereMSI uses the specified decider function to select items +// from the []map[string]interface{}. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereMSI(decider func(int, map[string]interface{}) bool) *Value { + var selected []map[string]interface{} + v.EachMSI(func(index int, val map[string]interface{}) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupMSI uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]map[string]interface{}. +func (v *Value) GroupMSI(grouper func(int, map[string]interface{}) string) *Value { + groups := make(map[string][]map[string]interface{}) + v.EachMSI(func(index int, val map[string]interface{}) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]map[string]interface{}, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceMSI uses the specified function to replace each map[string]interface{}s +// by iterating each item. The data in the returned result will be a +// []map[string]interface{} containing the replaced items. +func (v *Value) ReplaceMSI(replacer func(int, map[string]interface{}) map[string]interface{}) *Value { + arr := v.MustMSISlice() + replaced := make([]map[string]interface{}, len(arr)) + v.EachMSI(func(index int, val map[string]interface{}) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectMSI uses the specified collector function to collect a value +// for each of the map[string]interface{}s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectMSI(collector func(int, map[string]interface{}) interface{}) *Value { + arr := v.MustMSISlice() + collected := make([]interface{}, len(arr)) + v.EachMSI(func(index int, val map[string]interface{}) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + ObjxMap ((Map) and [](Map)) +*/ + +// ObjxMap gets the value as a (Map), returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) ObjxMap(optionalDefault ...(Map)) Map { + if s, ok := v.data.((Map)); ok { + return s + } + if s, ok := v.data.(map[string]interface{}); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return New(nil) +} + +// MustObjxMap gets the value as a (Map). +// +// Panics if the object is not a (Map). +func (v *Value) MustObjxMap() Map { + if s, ok := v.data.(map[string]interface{}); ok { + return s + } + return v.data.((Map)) +} + +// ObjxMapSlice gets the value as a [](Map), returns the optionalDefault +// value or nil if the value is not a [](Map). +func (v *Value) ObjxMapSlice(optionalDefault ...[](Map)) [](Map) { + if s, ok := v.data.([]Map); ok { + return s + } + + if s, ok := v.data.([]map[string]interface{}); ok { + result := make([]Map, len(s)) + for i := range s { + result[i] = s[i] + } + return result + } + + s, ok := v.data.([]interface{}) + if !ok { + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil + } + + result := make([]Map, len(s)) + for i := range s { + switch s[i].(type) { + case Map: + result[i] = s[i].(Map) + case map[string]interface{}: + result[i] = New(s[i]) + default: + return nil + } + } + return result +} + +// MustObjxMapSlice gets the value as a [](Map). +// +// Panics if the object is not a [](Map). +func (v *Value) MustObjxMapSlice() [](Map) { + if s := v.ObjxMapSlice(); s != nil { + return s + } + return v.data.([](Map)) +} + +// IsObjxMap gets whether the object contained is a (Map) or not. +func (v *Value) IsObjxMap() bool { + _, ok := v.data.((Map)) + if !ok { + _, ok = v.data.(map[string]interface{}) + } + return ok +} + +// IsObjxMapSlice gets whether the object contained is a [](Map) or not. +func (v *Value) IsObjxMapSlice() bool { + _, ok := v.data.([](Map)) + if !ok { + _, ok = v.data.([]map[string]interface{}) + if !ok { + s, ok := v.data.([]interface{}) + if ok { + for i := range s { + switch s[i].(type) { + case Map: + case map[string]interface{}: + default: + return false + } + } + return true + } + } + } + + return ok +} + +// EachObjxMap calls the specified callback for each object +// in the [](Map). +// +// Panics if the object is the wrong type. +func (v *Value) EachObjxMap(callback func(int, Map) bool) *Value { + for index, val := range v.MustObjxMapSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereObjxMap uses the specified decider function to select items +// from the [](Map). The object contained in the result will contain +// only the selected items. +func (v *Value) WhereObjxMap(decider func(int, Map) bool) *Value { + var selected [](Map) + v.EachObjxMap(func(index int, val Map) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupObjxMap uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][](Map). +func (v *Value) GroupObjxMap(grouper func(int, Map) string) *Value { + groups := make(map[string][](Map)) + v.EachObjxMap(func(index int, val Map) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([](Map), 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceObjxMap uses the specified function to replace each (Map)s +// by iterating each item. The data in the returned result will be a +// [](Map) containing the replaced items. +func (v *Value) ReplaceObjxMap(replacer func(int, Map) Map) *Value { + arr := v.MustObjxMapSlice() + replaced := make([](Map), len(arr)) + v.EachObjxMap(func(index int, val Map) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectObjxMap uses the specified collector function to collect a value +// for each of the (Map)s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectObjxMap(collector func(int, Map) interface{}) *Value { + arr := v.MustObjxMapSlice() + collected := make([]interface{}, len(arr)) + v.EachObjxMap(func(index int, val Map) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} diff --git a/vendor/github.com/stretchr/objx/type_specific_codegen.go b/vendor/github.com/stretchr/objx/type_specific_codegen.go new file mode 100644 index 0000000000..9859b407f0 --- /dev/null +++ b/vendor/github.com/stretchr/objx/type_specific_codegen.go @@ -0,0 +1,2251 @@ +package objx + +/* + Inter (interface{} and []interface{}) +*/ + +// Inter gets the value as a interface{}, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Inter(optionalDefault ...interface{}) interface{} { + if s, ok := v.data.(interface{}); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustInter gets the value as a interface{}. +// +// Panics if the object is not a interface{}. +func (v *Value) MustInter() interface{} { + return v.data.(interface{}) +} + +// InterSlice gets the value as a []interface{}, returns the optionalDefault +// value or nil if the value is not a []interface{}. +func (v *Value) InterSlice(optionalDefault ...[]interface{}) []interface{} { + if s, ok := v.data.([]interface{}); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustInterSlice gets the value as a []interface{}. +// +// Panics if the object is not a []interface{}. +func (v *Value) MustInterSlice() []interface{} { + return v.data.([]interface{}) +} + +// IsInter gets whether the object contained is a interface{} or not. +func (v *Value) IsInter() bool { + _, ok := v.data.(interface{}) + return ok +} + +// IsInterSlice gets whether the object contained is a []interface{} or not. +func (v *Value) IsInterSlice() bool { + _, ok := v.data.([]interface{}) + return ok +} + +// EachInter calls the specified callback for each object +// in the []interface{}. +// +// Panics if the object is the wrong type. +func (v *Value) EachInter(callback func(int, interface{}) bool) *Value { + for index, val := range v.MustInterSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereInter uses the specified decider function to select items +// from the []interface{}. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereInter(decider func(int, interface{}) bool) *Value { + var selected []interface{} + v.EachInter(func(index int, val interface{}) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupInter uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]interface{}. +func (v *Value) GroupInter(grouper func(int, interface{}) string) *Value { + groups := make(map[string][]interface{}) + v.EachInter(func(index int, val interface{}) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]interface{}, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceInter uses the specified function to replace each interface{}s +// by iterating each item. The data in the returned result will be a +// []interface{} containing the replaced items. +func (v *Value) ReplaceInter(replacer func(int, interface{}) interface{}) *Value { + arr := v.MustInterSlice() + replaced := make([]interface{}, len(arr)) + v.EachInter(func(index int, val interface{}) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectInter uses the specified collector function to collect a value +// for each of the interface{}s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectInter(collector func(int, interface{}) interface{}) *Value { + arr := v.MustInterSlice() + collected := make([]interface{}, len(arr)) + v.EachInter(func(index int, val interface{}) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Bool (bool and []bool) +*/ + +// Bool gets the value as a bool, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Bool(optionalDefault ...bool) bool { + if s, ok := v.data.(bool); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return false +} + +// MustBool gets the value as a bool. +// +// Panics if the object is not a bool. +func (v *Value) MustBool() bool { + return v.data.(bool) +} + +// BoolSlice gets the value as a []bool, returns the optionalDefault +// value or nil if the value is not a []bool. +func (v *Value) BoolSlice(optionalDefault ...[]bool) []bool { + if s, ok := v.data.([]bool); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustBoolSlice gets the value as a []bool. +// +// Panics if the object is not a []bool. +func (v *Value) MustBoolSlice() []bool { + return v.data.([]bool) +} + +// IsBool gets whether the object contained is a bool or not. +func (v *Value) IsBool() bool { + _, ok := v.data.(bool) + return ok +} + +// IsBoolSlice gets whether the object contained is a []bool or not. +func (v *Value) IsBoolSlice() bool { + _, ok := v.data.([]bool) + return ok +} + +// EachBool calls the specified callback for each object +// in the []bool. +// +// Panics if the object is the wrong type. +func (v *Value) EachBool(callback func(int, bool) bool) *Value { + for index, val := range v.MustBoolSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereBool uses the specified decider function to select items +// from the []bool. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereBool(decider func(int, bool) bool) *Value { + var selected []bool + v.EachBool(func(index int, val bool) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupBool uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]bool. +func (v *Value) GroupBool(grouper func(int, bool) string) *Value { + groups := make(map[string][]bool) + v.EachBool(func(index int, val bool) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]bool, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceBool uses the specified function to replace each bools +// by iterating each item. The data in the returned result will be a +// []bool containing the replaced items. +func (v *Value) ReplaceBool(replacer func(int, bool) bool) *Value { + arr := v.MustBoolSlice() + replaced := make([]bool, len(arr)) + v.EachBool(func(index int, val bool) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectBool uses the specified collector function to collect a value +// for each of the bools in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectBool(collector func(int, bool) interface{}) *Value { + arr := v.MustBoolSlice() + collected := make([]interface{}, len(arr)) + v.EachBool(func(index int, val bool) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Str (string and []string) +*/ + +// Str gets the value as a string, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Str(optionalDefault ...string) string { + if s, ok := v.data.(string); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return "" +} + +// MustStr gets the value as a string. +// +// Panics if the object is not a string. +func (v *Value) MustStr() string { + return v.data.(string) +} + +// StrSlice gets the value as a []string, returns the optionalDefault +// value or nil if the value is not a []string. +func (v *Value) StrSlice(optionalDefault ...[]string) []string { + if s, ok := v.data.([]string); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustStrSlice gets the value as a []string. +// +// Panics if the object is not a []string. +func (v *Value) MustStrSlice() []string { + return v.data.([]string) +} + +// IsStr gets whether the object contained is a string or not. +func (v *Value) IsStr() bool { + _, ok := v.data.(string) + return ok +} + +// IsStrSlice gets whether the object contained is a []string or not. +func (v *Value) IsStrSlice() bool { + _, ok := v.data.([]string) + return ok +} + +// EachStr calls the specified callback for each object +// in the []string. +// +// Panics if the object is the wrong type. +func (v *Value) EachStr(callback func(int, string) bool) *Value { + for index, val := range v.MustStrSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereStr uses the specified decider function to select items +// from the []string. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereStr(decider func(int, string) bool) *Value { + var selected []string + v.EachStr(func(index int, val string) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupStr uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]string. +func (v *Value) GroupStr(grouper func(int, string) string) *Value { + groups := make(map[string][]string) + v.EachStr(func(index int, val string) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]string, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceStr uses the specified function to replace each strings +// by iterating each item. The data in the returned result will be a +// []string containing the replaced items. +func (v *Value) ReplaceStr(replacer func(int, string) string) *Value { + arr := v.MustStrSlice() + replaced := make([]string, len(arr)) + v.EachStr(func(index int, val string) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectStr uses the specified collector function to collect a value +// for each of the strings in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectStr(collector func(int, string) interface{}) *Value { + arr := v.MustStrSlice() + collected := make([]interface{}, len(arr)) + v.EachStr(func(index int, val string) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Int (int and []int) +*/ + +// Int gets the value as a int, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Int(optionalDefault ...int) int { + if s, ok := v.data.(int); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustInt gets the value as a int. +// +// Panics if the object is not a int. +func (v *Value) MustInt() int { + return v.data.(int) +} + +// IntSlice gets the value as a []int, returns the optionalDefault +// value or nil if the value is not a []int. +func (v *Value) IntSlice(optionalDefault ...[]int) []int { + if s, ok := v.data.([]int); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustIntSlice gets the value as a []int. +// +// Panics if the object is not a []int. +func (v *Value) MustIntSlice() []int { + return v.data.([]int) +} + +// IsInt gets whether the object contained is a int or not. +func (v *Value) IsInt() bool { + _, ok := v.data.(int) + return ok +} + +// IsIntSlice gets whether the object contained is a []int or not. +func (v *Value) IsIntSlice() bool { + _, ok := v.data.([]int) + return ok +} + +// EachInt calls the specified callback for each object +// in the []int. +// +// Panics if the object is the wrong type. +func (v *Value) EachInt(callback func(int, int) bool) *Value { + for index, val := range v.MustIntSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereInt uses the specified decider function to select items +// from the []int. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereInt(decider func(int, int) bool) *Value { + var selected []int + v.EachInt(func(index int, val int) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupInt uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]int. +func (v *Value) GroupInt(grouper func(int, int) string) *Value { + groups := make(map[string][]int) + v.EachInt(func(index int, val int) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]int, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceInt uses the specified function to replace each ints +// by iterating each item. The data in the returned result will be a +// []int containing the replaced items. +func (v *Value) ReplaceInt(replacer func(int, int) int) *Value { + arr := v.MustIntSlice() + replaced := make([]int, len(arr)) + v.EachInt(func(index int, val int) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectInt uses the specified collector function to collect a value +// for each of the ints in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectInt(collector func(int, int) interface{}) *Value { + arr := v.MustIntSlice() + collected := make([]interface{}, len(arr)) + v.EachInt(func(index int, val int) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Int8 (int8 and []int8) +*/ + +// Int8 gets the value as a int8, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Int8(optionalDefault ...int8) int8 { + if s, ok := v.data.(int8); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustInt8 gets the value as a int8. +// +// Panics if the object is not a int8. +func (v *Value) MustInt8() int8 { + return v.data.(int8) +} + +// Int8Slice gets the value as a []int8, returns the optionalDefault +// value or nil if the value is not a []int8. +func (v *Value) Int8Slice(optionalDefault ...[]int8) []int8 { + if s, ok := v.data.([]int8); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustInt8Slice gets the value as a []int8. +// +// Panics if the object is not a []int8. +func (v *Value) MustInt8Slice() []int8 { + return v.data.([]int8) +} + +// IsInt8 gets whether the object contained is a int8 or not. +func (v *Value) IsInt8() bool { + _, ok := v.data.(int8) + return ok +} + +// IsInt8Slice gets whether the object contained is a []int8 or not. +func (v *Value) IsInt8Slice() bool { + _, ok := v.data.([]int8) + return ok +} + +// EachInt8 calls the specified callback for each object +// in the []int8. +// +// Panics if the object is the wrong type. +func (v *Value) EachInt8(callback func(int, int8) bool) *Value { + for index, val := range v.MustInt8Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereInt8 uses the specified decider function to select items +// from the []int8. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereInt8(decider func(int, int8) bool) *Value { + var selected []int8 + v.EachInt8(func(index int, val int8) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupInt8 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]int8. +func (v *Value) GroupInt8(grouper func(int, int8) string) *Value { + groups := make(map[string][]int8) + v.EachInt8(func(index int, val int8) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]int8, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceInt8 uses the specified function to replace each int8s +// by iterating each item. The data in the returned result will be a +// []int8 containing the replaced items. +func (v *Value) ReplaceInt8(replacer func(int, int8) int8) *Value { + arr := v.MustInt8Slice() + replaced := make([]int8, len(arr)) + v.EachInt8(func(index int, val int8) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectInt8 uses the specified collector function to collect a value +// for each of the int8s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectInt8(collector func(int, int8) interface{}) *Value { + arr := v.MustInt8Slice() + collected := make([]interface{}, len(arr)) + v.EachInt8(func(index int, val int8) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Int16 (int16 and []int16) +*/ + +// Int16 gets the value as a int16, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Int16(optionalDefault ...int16) int16 { + if s, ok := v.data.(int16); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustInt16 gets the value as a int16. +// +// Panics if the object is not a int16. +func (v *Value) MustInt16() int16 { + return v.data.(int16) +} + +// Int16Slice gets the value as a []int16, returns the optionalDefault +// value or nil if the value is not a []int16. +func (v *Value) Int16Slice(optionalDefault ...[]int16) []int16 { + if s, ok := v.data.([]int16); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustInt16Slice gets the value as a []int16. +// +// Panics if the object is not a []int16. +func (v *Value) MustInt16Slice() []int16 { + return v.data.([]int16) +} + +// IsInt16 gets whether the object contained is a int16 or not. +func (v *Value) IsInt16() bool { + _, ok := v.data.(int16) + return ok +} + +// IsInt16Slice gets whether the object contained is a []int16 or not. +func (v *Value) IsInt16Slice() bool { + _, ok := v.data.([]int16) + return ok +} + +// EachInt16 calls the specified callback for each object +// in the []int16. +// +// Panics if the object is the wrong type. +func (v *Value) EachInt16(callback func(int, int16) bool) *Value { + for index, val := range v.MustInt16Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereInt16 uses the specified decider function to select items +// from the []int16. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereInt16(decider func(int, int16) bool) *Value { + var selected []int16 + v.EachInt16(func(index int, val int16) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupInt16 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]int16. +func (v *Value) GroupInt16(grouper func(int, int16) string) *Value { + groups := make(map[string][]int16) + v.EachInt16(func(index int, val int16) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]int16, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceInt16 uses the specified function to replace each int16s +// by iterating each item. The data in the returned result will be a +// []int16 containing the replaced items. +func (v *Value) ReplaceInt16(replacer func(int, int16) int16) *Value { + arr := v.MustInt16Slice() + replaced := make([]int16, len(arr)) + v.EachInt16(func(index int, val int16) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectInt16 uses the specified collector function to collect a value +// for each of the int16s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectInt16(collector func(int, int16) interface{}) *Value { + arr := v.MustInt16Slice() + collected := make([]interface{}, len(arr)) + v.EachInt16(func(index int, val int16) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Int32 (int32 and []int32) +*/ + +// Int32 gets the value as a int32, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Int32(optionalDefault ...int32) int32 { + if s, ok := v.data.(int32); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustInt32 gets the value as a int32. +// +// Panics if the object is not a int32. +func (v *Value) MustInt32() int32 { + return v.data.(int32) +} + +// Int32Slice gets the value as a []int32, returns the optionalDefault +// value or nil if the value is not a []int32. +func (v *Value) Int32Slice(optionalDefault ...[]int32) []int32 { + if s, ok := v.data.([]int32); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustInt32Slice gets the value as a []int32. +// +// Panics if the object is not a []int32. +func (v *Value) MustInt32Slice() []int32 { + return v.data.([]int32) +} + +// IsInt32 gets whether the object contained is a int32 or not. +func (v *Value) IsInt32() bool { + _, ok := v.data.(int32) + return ok +} + +// IsInt32Slice gets whether the object contained is a []int32 or not. +func (v *Value) IsInt32Slice() bool { + _, ok := v.data.([]int32) + return ok +} + +// EachInt32 calls the specified callback for each object +// in the []int32. +// +// Panics if the object is the wrong type. +func (v *Value) EachInt32(callback func(int, int32) bool) *Value { + for index, val := range v.MustInt32Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereInt32 uses the specified decider function to select items +// from the []int32. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereInt32(decider func(int, int32) bool) *Value { + var selected []int32 + v.EachInt32(func(index int, val int32) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupInt32 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]int32. +func (v *Value) GroupInt32(grouper func(int, int32) string) *Value { + groups := make(map[string][]int32) + v.EachInt32(func(index int, val int32) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]int32, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceInt32 uses the specified function to replace each int32s +// by iterating each item. The data in the returned result will be a +// []int32 containing the replaced items. +func (v *Value) ReplaceInt32(replacer func(int, int32) int32) *Value { + arr := v.MustInt32Slice() + replaced := make([]int32, len(arr)) + v.EachInt32(func(index int, val int32) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectInt32 uses the specified collector function to collect a value +// for each of the int32s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectInt32(collector func(int, int32) interface{}) *Value { + arr := v.MustInt32Slice() + collected := make([]interface{}, len(arr)) + v.EachInt32(func(index int, val int32) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Int64 (int64 and []int64) +*/ + +// Int64 gets the value as a int64, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Int64(optionalDefault ...int64) int64 { + if s, ok := v.data.(int64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustInt64 gets the value as a int64. +// +// Panics if the object is not a int64. +func (v *Value) MustInt64() int64 { + return v.data.(int64) +} + +// Int64Slice gets the value as a []int64, returns the optionalDefault +// value or nil if the value is not a []int64. +func (v *Value) Int64Slice(optionalDefault ...[]int64) []int64 { + if s, ok := v.data.([]int64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustInt64Slice gets the value as a []int64. +// +// Panics if the object is not a []int64. +func (v *Value) MustInt64Slice() []int64 { + return v.data.([]int64) +} + +// IsInt64 gets whether the object contained is a int64 or not. +func (v *Value) IsInt64() bool { + _, ok := v.data.(int64) + return ok +} + +// IsInt64Slice gets whether the object contained is a []int64 or not. +func (v *Value) IsInt64Slice() bool { + _, ok := v.data.([]int64) + return ok +} + +// EachInt64 calls the specified callback for each object +// in the []int64. +// +// Panics if the object is the wrong type. +func (v *Value) EachInt64(callback func(int, int64) bool) *Value { + for index, val := range v.MustInt64Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereInt64 uses the specified decider function to select items +// from the []int64. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereInt64(decider func(int, int64) bool) *Value { + var selected []int64 + v.EachInt64(func(index int, val int64) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupInt64 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]int64. +func (v *Value) GroupInt64(grouper func(int, int64) string) *Value { + groups := make(map[string][]int64) + v.EachInt64(func(index int, val int64) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]int64, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceInt64 uses the specified function to replace each int64s +// by iterating each item. The data in the returned result will be a +// []int64 containing the replaced items. +func (v *Value) ReplaceInt64(replacer func(int, int64) int64) *Value { + arr := v.MustInt64Slice() + replaced := make([]int64, len(arr)) + v.EachInt64(func(index int, val int64) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectInt64 uses the specified collector function to collect a value +// for each of the int64s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectInt64(collector func(int, int64) interface{}) *Value { + arr := v.MustInt64Slice() + collected := make([]interface{}, len(arr)) + v.EachInt64(func(index int, val int64) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Uint (uint and []uint) +*/ + +// Uint gets the value as a uint, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Uint(optionalDefault ...uint) uint { + if s, ok := v.data.(uint); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustUint gets the value as a uint. +// +// Panics if the object is not a uint. +func (v *Value) MustUint() uint { + return v.data.(uint) +} + +// UintSlice gets the value as a []uint, returns the optionalDefault +// value or nil if the value is not a []uint. +func (v *Value) UintSlice(optionalDefault ...[]uint) []uint { + if s, ok := v.data.([]uint); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustUintSlice gets the value as a []uint. +// +// Panics if the object is not a []uint. +func (v *Value) MustUintSlice() []uint { + return v.data.([]uint) +} + +// IsUint gets whether the object contained is a uint or not. +func (v *Value) IsUint() bool { + _, ok := v.data.(uint) + return ok +} + +// IsUintSlice gets whether the object contained is a []uint or not. +func (v *Value) IsUintSlice() bool { + _, ok := v.data.([]uint) + return ok +} + +// EachUint calls the specified callback for each object +// in the []uint. +// +// Panics if the object is the wrong type. +func (v *Value) EachUint(callback func(int, uint) bool) *Value { + for index, val := range v.MustUintSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereUint uses the specified decider function to select items +// from the []uint. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereUint(decider func(int, uint) bool) *Value { + var selected []uint + v.EachUint(func(index int, val uint) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupUint uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]uint. +func (v *Value) GroupUint(grouper func(int, uint) string) *Value { + groups := make(map[string][]uint) + v.EachUint(func(index int, val uint) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]uint, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceUint uses the specified function to replace each uints +// by iterating each item. The data in the returned result will be a +// []uint containing the replaced items. +func (v *Value) ReplaceUint(replacer func(int, uint) uint) *Value { + arr := v.MustUintSlice() + replaced := make([]uint, len(arr)) + v.EachUint(func(index int, val uint) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectUint uses the specified collector function to collect a value +// for each of the uints in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectUint(collector func(int, uint) interface{}) *Value { + arr := v.MustUintSlice() + collected := make([]interface{}, len(arr)) + v.EachUint(func(index int, val uint) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Uint8 (uint8 and []uint8) +*/ + +// Uint8 gets the value as a uint8, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Uint8(optionalDefault ...uint8) uint8 { + if s, ok := v.data.(uint8); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustUint8 gets the value as a uint8. +// +// Panics if the object is not a uint8. +func (v *Value) MustUint8() uint8 { + return v.data.(uint8) +} + +// Uint8Slice gets the value as a []uint8, returns the optionalDefault +// value or nil if the value is not a []uint8. +func (v *Value) Uint8Slice(optionalDefault ...[]uint8) []uint8 { + if s, ok := v.data.([]uint8); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustUint8Slice gets the value as a []uint8. +// +// Panics if the object is not a []uint8. +func (v *Value) MustUint8Slice() []uint8 { + return v.data.([]uint8) +} + +// IsUint8 gets whether the object contained is a uint8 or not. +func (v *Value) IsUint8() bool { + _, ok := v.data.(uint8) + return ok +} + +// IsUint8Slice gets whether the object contained is a []uint8 or not. +func (v *Value) IsUint8Slice() bool { + _, ok := v.data.([]uint8) + return ok +} + +// EachUint8 calls the specified callback for each object +// in the []uint8. +// +// Panics if the object is the wrong type. +func (v *Value) EachUint8(callback func(int, uint8) bool) *Value { + for index, val := range v.MustUint8Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereUint8 uses the specified decider function to select items +// from the []uint8. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereUint8(decider func(int, uint8) bool) *Value { + var selected []uint8 + v.EachUint8(func(index int, val uint8) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupUint8 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]uint8. +func (v *Value) GroupUint8(grouper func(int, uint8) string) *Value { + groups := make(map[string][]uint8) + v.EachUint8(func(index int, val uint8) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]uint8, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceUint8 uses the specified function to replace each uint8s +// by iterating each item. The data in the returned result will be a +// []uint8 containing the replaced items. +func (v *Value) ReplaceUint8(replacer func(int, uint8) uint8) *Value { + arr := v.MustUint8Slice() + replaced := make([]uint8, len(arr)) + v.EachUint8(func(index int, val uint8) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectUint8 uses the specified collector function to collect a value +// for each of the uint8s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectUint8(collector func(int, uint8) interface{}) *Value { + arr := v.MustUint8Slice() + collected := make([]interface{}, len(arr)) + v.EachUint8(func(index int, val uint8) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Uint16 (uint16 and []uint16) +*/ + +// Uint16 gets the value as a uint16, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Uint16(optionalDefault ...uint16) uint16 { + if s, ok := v.data.(uint16); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustUint16 gets the value as a uint16. +// +// Panics if the object is not a uint16. +func (v *Value) MustUint16() uint16 { + return v.data.(uint16) +} + +// Uint16Slice gets the value as a []uint16, returns the optionalDefault +// value or nil if the value is not a []uint16. +func (v *Value) Uint16Slice(optionalDefault ...[]uint16) []uint16 { + if s, ok := v.data.([]uint16); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustUint16Slice gets the value as a []uint16. +// +// Panics if the object is not a []uint16. +func (v *Value) MustUint16Slice() []uint16 { + return v.data.([]uint16) +} + +// IsUint16 gets whether the object contained is a uint16 or not. +func (v *Value) IsUint16() bool { + _, ok := v.data.(uint16) + return ok +} + +// IsUint16Slice gets whether the object contained is a []uint16 or not. +func (v *Value) IsUint16Slice() bool { + _, ok := v.data.([]uint16) + return ok +} + +// EachUint16 calls the specified callback for each object +// in the []uint16. +// +// Panics if the object is the wrong type. +func (v *Value) EachUint16(callback func(int, uint16) bool) *Value { + for index, val := range v.MustUint16Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereUint16 uses the specified decider function to select items +// from the []uint16. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereUint16(decider func(int, uint16) bool) *Value { + var selected []uint16 + v.EachUint16(func(index int, val uint16) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupUint16 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]uint16. +func (v *Value) GroupUint16(grouper func(int, uint16) string) *Value { + groups := make(map[string][]uint16) + v.EachUint16(func(index int, val uint16) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]uint16, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceUint16 uses the specified function to replace each uint16s +// by iterating each item. The data in the returned result will be a +// []uint16 containing the replaced items. +func (v *Value) ReplaceUint16(replacer func(int, uint16) uint16) *Value { + arr := v.MustUint16Slice() + replaced := make([]uint16, len(arr)) + v.EachUint16(func(index int, val uint16) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectUint16 uses the specified collector function to collect a value +// for each of the uint16s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectUint16(collector func(int, uint16) interface{}) *Value { + arr := v.MustUint16Slice() + collected := make([]interface{}, len(arr)) + v.EachUint16(func(index int, val uint16) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Uint32 (uint32 and []uint32) +*/ + +// Uint32 gets the value as a uint32, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Uint32(optionalDefault ...uint32) uint32 { + if s, ok := v.data.(uint32); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustUint32 gets the value as a uint32. +// +// Panics if the object is not a uint32. +func (v *Value) MustUint32() uint32 { + return v.data.(uint32) +} + +// Uint32Slice gets the value as a []uint32, returns the optionalDefault +// value or nil if the value is not a []uint32. +func (v *Value) Uint32Slice(optionalDefault ...[]uint32) []uint32 { + if s, ok := v.data.([]uint32); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustUint32Slice gets the value as a []uint32. +// +// Panics if the object is not a []uint32. +func (v *Value) MustUint32Slice() []uint32 { + return v.data.([]uint32) +} + +// IsUint32 gets whether the object contained is a uint32 or not. +func (v *Value) IsUint32() bool { + _, ok := v.data.(uint32) + return ok +} + +// IsUint32Slice gets whether the object contained is a []uint32 or not. +func (v *Value) IsUint32Slice() bool { + _, ok := v.data.([]uint32) + return ok +} + +// EachUint32 calls the specified callback for each object +// in the []uint32. +// +// Panics if the object is the wrong type. +func (v *Value) EachUint32(callback func(int, uint32) bool) *Value { + for index, val := range v.MustUint32Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereUint32 uses the specified decider function to select items +// from the []uint32. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereUint32(decider func(int, uint32) bool) *Value { + var selected []uint32 + v.EachUint32(func(index int, val uint32) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupUint32 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]uint32. +func (v *Value) GroupUint32(grouper func(int, uint32) string) *Value { + groups := make(map[string][]uint32) + v.EachUint32(func(index int, val uint32) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]uint32, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceUint32 uses the specified function to replace each uint32s +// by iterating each item. The data in the returned result will be a +// []uint32 containing the replaced items. +func (v *Value) ReplaceUint32(replacer func(int, uint32) uint32) *Value { + arr := v.MustUint32Slice() + replaced := make([]uint32, len(arr)) + v.EachUint32(func(index int, val uint32) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectUint32 uses the specified collector function to collect a value +// for each of the uint32s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectUint32(collector func(int, uint32) interface{}) *Value { + arr := v.MustUint32Slice() + collected := make([]interface{}, len(arr)) + v.EachUint32(func(index int, val uint32) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Uint64 (uint64 and []uint64) +*/ + +// Uint64 gets the value as a uint64, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Uint64(optionalDefault ...uint64) uint64 { + if s, ok := v.data.(uint64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustUint64 gets the value as a uint64. +// +// Panics if the object is not a uint64. +func (v *Value) MustUint64() uint64 { + return v.data.(uint64) +} + +// Uint64Slice gets the value as a []uint64, returns the optionalDefault +// value or nil if the value is not a []uint64. +func (v *Value) Uint64Slice(optionalDefault ...[]uint64) []uint64 { + if s, ok := v.data.([]uint64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustUint64Slice gets the value as a []uint64. +// +// Panics if the object is not a []uint64. +func (v *Value) MustUint64Slice() []uint64 { + return v.data.([]uint64) +} + +// IsUint64 gets whether the object contained is a uint64 or not. +func (v *Value) IsUint64() bool { + _, ok := v.data.(uint64) + return ok +} + +// IsUint64Slice gets whether the object contained is a []uint64 or not. +func (v *Value) IsUint64Slice() bool { + _, ok := v.data.([]uint64) + return ok +} + +// EachUint64 calls the specified callback for each object +// in the []uint64. +// +// Panics if the object is the wrong type. +func (v *Value) EachUint64(callback func(int, uint64) bool) *Value { + for index, val := range v.MustUint64Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereUint64 uses the specified decider function to select items +// from the []uint64. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereUint64(decider func(int, uint64) bool) *Value { + var selected []uint64 + v.EachUint64(func(index int, val uint64) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupUint64 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]uint64. +func (v *Value) GroupUint64(grouper func(int, uint64) string) *Value { + groups := make(map[string][]uint64) + v.EachUint64(func(index int, val uint64) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]uint64, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceUint64 uses the specified function to replace each uint64s +// by iterating each item. The data in the returned result will be a +// []uint64 containing the replaced items. +func (v *Value) ReplaceUint64(replacer func(int, uint64) uint64) *Value { + arr := v.MustUint64Slice() + replaced := make([]uint64, len(arr)) + v.EachUint64(func(index int, val uint64) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectUint64 uses the specified collector function to collect a value +// for each of the uint64s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectUint64(collector func(int, uint64) interface{}) *Value { + arr := v.MustUint64Slice() + collected := make([]interface{}, len(arr)) + v.EachUint64(func(index int, val uint64) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Uintptr (uintptr and []uintptr) +*/ + +// Uintptr gets the value as a uintptr, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Uintptr(optionalDefault ...uintptr) uintptr { + if s, ok := v.data.(uintptr); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustUintptr gets the value as a uintptr. +// +// Panics if the object is not a uintptr. +func (v *Value) MustUintptr() uintptr { + return v.data.(uintptr) +} + +// UintptrSlice gets the value as a []uintptr, returns the optionalDefault +// value or nil if the value is not a []uintptr. +func (v *Value) UintptrSlice(optionalDefault ...[]uintptr) []uintptr { + if s, ok := v.data.([]uintptr); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustUintptrSlice gets the value as a []uintptr. +// +// Panics if the object is not a []uintptr. +func (v *Value) MustUintptrSlice() []uintptr { + return v.data.([]uintptr) +} + +// IsUintptr gets whether the object contained is a uintptr or not. +func (v *Value) IsUintptr() bool { + _, ok := v.data.(uintptr) + return ok +} + +// IsUintptrSlice gets whether the object contained is a []uintptr or not. +func (v *Value) IsUintptrSlice() bool { + _, ok := v.data.([]uintptr) + return ok +} + +// EachUintptr calls the specified callback for each object +// in the []uintptr. +// +// Panics if the object is the wrong type. +func (v *Value) EachUintptr(callback func(int, uintptr) bool) *Value { + for index, val := range v.MustUintptrSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereUintptr uses the specified decider function to select items +// from the []uintptr. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereUintptr(decider func(int, uintptr) bool) *Value { + var selected []uintptr + v.EachUintptr(func(index int, val uintptr) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupUintptr uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]uintptr. +func (v *Value) GroupUintptr(grouper func(int, uintptr) string) *Value { + groups := make(map[string][]uintptr) + v.EachUintptr(func(index int, val uintptr) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]uintptr, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceUintptr uses the specified function to replace each uintptrs +// by iterating each item. The data in the returned result will be a +// []uintptr containing the replaced items. +func (v *Value) ReplaceUintptr(replacer func(int, uintptr) uintptr) *Value { + arr := v.MustUintptrSlice() + replaced := make([]uintptr, len(arr)) + v.EachUintptr(func(index int, val uintptr) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectUintptr uses the specified collector function to collect a value +// for each of the uintptrs in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectUintptr(collector func(int, uintptr) interface{}) *Value { + arr := v.MustUintptrSlice() + collected := make([]interface{}, len(arr)) + v.EachUintptr(func(index int, val uintptr) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Float32 (float32 and []float32) +*/ + +// Float32 gets the value as a float32, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Float32(optionalDefault ...float32) float32 { + if s, ok := v.data.(float32); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustFloat32 gets the value as a float32. +// +// Panics if the object is not a float32. +func (v *Value) MustFloat32() float32 { + return v.data.(float32) +} + +// Float32Slice gets the value as a []float32, returns the optionalDefault +// value or nil if the value is not a []float32. +func (v *Value) Float32Slice(optionalDefault ...[]float32) []float32 { + if s, ok := v.data.([]float32); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustFloat32Slice gets the value as a []float32. +// +// Panics if the object is not a []float32. +func (v *Value) MustFloat32Slice() []float32 { + return v.data.([]float32) +} + +// IsFloat32 gets whether the object contained is a float32 or not. +func (v *Value) IsFloat32() bool { + _, ok := v.data.(float32) + return ok +} + +// IsFloat32Slice gets whether the object contained is a []float32 or not. +func (v *Value) IsFloat32Slice() bool { + _, ok := v.data.([]float32) + return ok +} + +// EachFloat32 calls the specified callback for each object +// in the []float32. +// +// Panics if the object is the wrong type. +func (v *Value) EachFloat32(callback func(int, float32) bool) *Value { + for index, val := range v.MustFloat32Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereFloat32 uses the specified decider function to select items +// from the []float32. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereFloat32(decider func(int, float32) bool) *Value { + var selected []float32 + v.EachFloat32(func(index int, val float32) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupFloat32 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]float32. +func (v *Value) GroupFloat32(grouper func(int, float32) string) *Value { + groups := make(map[string][]float32) + v.EachFloat32(func(index int, val float32) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]float32, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceFloat32 uses the specified function to replace each float32s +// by iterating each item. The data in the returned result will be a +// []float32 containing the replaced items. +func (v *Value) ReplaceFloat32(replacer func(int, float32) float32) *Value { + arr := v.MustFloat32Slice() + replaced := make([]float32, len(arr)) + v.EachFloat32(func(index int, val float32) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectFloat32 uses the specified collector function to collect a value +// for each of the float32s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectFloat32(collector func(int, float32) interface{}) *Value { + arr := v.MustFloat32Slice() + collected := make([]interface{}, len(arr)) + v.EachFloat32(func(index int, val float32) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Float64 (float64 and []float64) +*/ + +// Float64 gets the value as a float64, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Float64(optionalDefault ...float64) float64 { + if s, ok := v.data.(float64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustFloat64 gets the value as a float64. +// +// Panics if the object is not a float64. +func (v *Value) MustFloat64() float64 { + return v.data.(float64) +} + +// Float64Slice gets the value as a []float64, returns the optionalDefault +// value or nil if the value is not a []float64. +func (v *Value) Float64Slice(optionalDefault ...[]float64) []float64 { + if s, ok := v.data.([]float64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustFloat64Slice gets the value as a []float64. +// +// Panics if the object is not a []float64. +func (v *Value) MustFloat64Slice() []float64 { + return v.data.([]float64) +} + +// IsFloat64 gets whether the object contained is a float64 or not. +func (v *Value) IsFloat64() bool { + _, ok := v.data.(float64) + return ok +} + +// IsFloat64Slice gets whether the object contained is a []float64 or not. +func (v *Value) IsFloat64Slice() bool { + _, ok := v.data.([]float64) + return ok +} + +// EachFloat64 calls the specified callback for each object +// in the []float64. +// +// Panics if the object is the wrong type. +func (v *Value) EachFloat64(callback func(int, float64) bool) *Value { + for index, val := range v.MustFloat64Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereFloat64 uses the specified decider function to select items +// from the []float64. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereFloat64(decider func(int, float64) bool) *Value { + var selected []float64 + v.EachFloat64(func(index int, val float64) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupFloat64 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]float64. +func (v *Value) GroupFloat64(grouper func(int, float64) string) *Value { + groups := make(map[string][]float64) + v.EachFloat64(func(index int, val float64) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]float64, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceFloat64 uses the specified function to replace each float64s +// by iterating each item. The data in the returned result will be a +// []float64 containing the replaced items. +func (v *Value) ReplaceFloat64(replacer func(int, float64) float64) *Value { + arr := v.MustFloat64Slice() + replaced := make([]float64, len(arr)) + v.EachFloat64(func(index int, val float64) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectFloat64 uses the specified collector function to collect a value +// for each of the float64s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectFloat64(collector func(int, float64) interface{}) *Value { + arr := v.MustFloat64Slice() + collected := make([]interface{}, len(arr)) + v.EachFloat64(func(index int, val float64) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Complex64 (complex64 and []complex64) +*/ + +// Complex64 gets the value as a complex64, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Complex64(optionalDefault ...complex64) complex64 { + if s, ok := v.data.(complex64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustComplex64 gets the value as a complex64. +// +// Panics if the object is not a complex64. +func (v *Value) MustComplex64() complex64 { + return v.data.(complex64) +} + +// Complex64Slice gets the value as a []complex64, returns the optionalDefault +// value or nil if the value is not a []complex64. +func (v *Value) Complex64Slice(optionalDefault ...[]complex64) []complex64 { + if s, ok := v.data.([]complex64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustComplex64Slice gets the value as a []complex64. +// +// Panics if the object is not a []complex64. +func (v *Value) MustComplex64Slice() []complex64 { + return v.data.([]complex64) +} + +// IsComplex64 gets whether the object contained is a complex64 or not. +func (v *Value) IsComplex64() bool { + _, ok := v.data.(complex64) + return ok +} + +// IsComplex64Slice gets whether the object contained is a []complex64 or not. +func (v *Value) IsComplex64Slice() bool { + _, ok := v.data.([]complex64) + return ok +} + +// EachComplex64 calls the specified callback for each object +// in the []complex64. +// +// Panics if the object is the wrong type. +func (v *Value) EachComplex64(callback func(int, complex64) bool) *Value { + for index, val := range v.MustComplex64Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereComplex64 uses the specified decider function to select items +// from the []complex64. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereComplex64(decider func(int, complex64) bool) *Value { + var selected []complex64 + v.EachComplex64(func(index int, val complex64) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupComplex64 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]complex64. +func (v *Value) GroupComplex64(grouper func(int, complex64) string) *Value { + groups := make(map[string][]complex64) + v.EachComplex64(func(index int, val complex64) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]complex64, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceComplex64 uses the specified function to replace each complex64s +// by iterating each item. The data in the returned result will be a +// []complex64 containing the replaced items. +func (v *Value) ReplaceComplex64(replacer func(int, complex64) complex64) *Value { + arr := v.MustComplex64Slice() + replaced := make([]complex64, len(arr)) + v.EachComplex64(func(index int, val complex64) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectComplex64 uses the specified collector function to collect a value +// for each of the complex64s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectComplex64(collector func(int, complex64) interface{}) *Value { + arr := v.MustComplex64Slice() + collected := make([]interface{}, len(arr)) + v.EachComplex64(func(index int, val complex64) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Complex128 (complex128 and []complex128) +*/ + +// Complex128 gets the value as a complex128, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Complex128(optionalDefault ...complex128) complex128 { + if s, ok := v.data.(complex128); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustComplex128 gets the value as a complex128. +// +// Panics if the object is not a complex128. +func (v *Value) MustComplex128() complex128 { + return v.data.(complex128) +} + +// Complex128Slice gets the value as a []complex128, returns the optionalDefault +// value or nil if the value is not a []complex128. +func (v *Value) Complex128Slice(optionalDefault ...[]complex128) []complex128 { + if s, ok := v.data.([]complex128); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustComplex128Slice gets the value as a []complex128. +// +// Panics if the object is not a []complex128. +func (v *Value) MustComplex128Slice() []complex128 { + return v.data.([]complex128) +} + +// IsComplex128 gets whether the object contained is a complex128 or not. +func (v *Value) IsComplex128() bool { + _, ok := v.data.(complex128) + return ok +} + +// IsComplex128Slice gets whether the object contained is a []complex128 or not. +func (v *Value) IsComplex128Slice() bool { + _, ok := v.data.([]complex128) + return ok +} + +// EachComplex128 calls the specified callback for each object +// in the []complex128. +// +// Panics if the object is the wrong type. +func (v *Value) EachComplex128(callback func(int, complex128) bool) *Value { + for index, val := range v.MustComplex128Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereComplex128 uses the specified decider function to select items +// from the []complex128. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereComplex128(decider func(int, complex128) bool) *Value { + var selected []complex128 + v.EachComplex128(func(index int, val complex128) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupComplex128 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]complex128. +func (v *Value) GroupComplex128(grouper func(int, complex128) string) *Value { + groups := make(map[string][]complex128) + v.EachComplex128(func(index int, val complex128) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]complex128, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceComplex128 uses the specified function to replace each complex128s +// by iterating each item. The data in the returned result will be a +// []complex128 containing the replaced items. +func (v *Value) ReplaceComplex128(replacer func(int, complex128) complex128) *Value { + arr := v.MustComplex128Slice() + replaced := make([]complex128, len(arr)) + v.EachComplex128(func(index int, val complex128) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectComplex128 uses the specified collector function to collect a value +// for each of the complex128s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectComplex128(collector func(int, complex128) interface{}) *Value { + arr := v.MustComplex128Slice() + collected := make([]interface{}, len(arr)) + v.EachComplex128(func(index int, val complex128) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} diff --git a/vendor/github.com/stretchr/objx/value.go b/vendor/github.com/stretchr/objx/value.go new file mode 100644 index 0000000000..4e5f9b77e6 --- /dev/null +++ b/vendor/github.com/stretchr/objx/value.go @@ -0,0 +1,159 @@ +package objx + +import ( + "fmt" + "strconv" +) + +// Value provides methods for extracting interface{} data in various +// types. +type Value struct { + // data contains the raw data being managed by this Value + data interface{} +} + +// Data returns the raw data contained by this Value +func (v *Value) Data() interface{} { + return v.data +} + +// String returns the value always as a string +func (v *Value) String() string { + switch { + case v.IsNil(): + return "" + case v.IsStr(): + return v.Str() + case v.IsBool(): + return strconv.FormatBool(v.Bool()) + case v.IsFloat32(): + return strconv.FormatFloat(float64(v.Float32()), 'f', -1, 32) + case v.IsFloat64(): + return strconv.FormatFloat(v.Float64(), 'f', -1, 64) + case v.IsInt(): + return strconv.FormatInt(int64(v.Int()), 10) + case v.IsInt8(): + return strconv.FormatInt(int64(v.Int8()), 10) + case v.IsInt16(): + return strconv.FormatInt(int64(v.Int16()), 10) + case v.IsInt32(): + return strconv.FormatInt(int64(v.Int32()), 10) + case v.IsInt64(): + return strconv.FormatInt(v.Int64(), 10) + case v.IsUint(): + return strconv.FormatUint(uint64(v.Uint()), 10) + case v.IsUint8(): + return strconv.FormatUint(uint64(v.Uint8()), 10) + case v.IsUint16(): + return strconv.FormatUint(uint64(v.Uint16()), 10) + case v.IsUint32(): + return strconv.FormatUint(uint64(v.Uint32()), 10) + case v.IsUint64(): + return strconv.FormatUint(v.Uint64(), 10) + } + return fmt.Sprintf("%#v", v.Data()) +} + +// StringSlice returns the value always as a []string +func (v *Value) StringSlice(optionalDefault ...[]string) []string { + switch { + case v.IsStrSlice(): + return v.MustStrSlice() + case v.IsBoolSlice(): + slice := v.MustBoolSlice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatBool(iv) + } + return vals + case v.IsFloat32Slice(): + slice := v.MustFloat32Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatFloat(float64(iv), 'f', -1, 32) + } + return vals + case v.IsFloat64Slice(): + slice := v.MustFloat64Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatFloat(iv, 'f', -1, 64) + } + return vals + case v.IsIntSlice(): + slice := v.MustIntSlice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatInt(int64(iv), 10) + } + return vals + case v.IsInt8Slice(): + slice := v.MustInt8Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatInt(int64(iv), 10) + } + return vals + case v.IsInt16Slice(): + slice := v.MustInt16Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatInt(int64(iv), 10) + } + return vals + case v.IsInt32Slice(): + slice := v.MustInt32Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatInt(int64(iv), 10) + } + return vals + case v.IsInt64Slice(): + slice := v.MustInt64Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatInt(iv, 10) + } + return vals + case v.IsUintSlice(): + slice := v.MustUintSlice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatUint(uint64(iv), 10) + } + return vals + case v.IsUint8Slice(): + slice := v.MustUint8Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatUint(uint64(iv), 10) + } + return vals + case v.IsUint16Slice(): + slice := v.MustUint16Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatUint(uint64(iv), 10) + } + return vals + case v.IsUint32Slice(): + slice := v.MustUint32Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatUint(uint64(iv), 10) + } + return vals + case v.IsUint64Slice(): + slice := v.MustUint64Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatUint(iv, 10) + } + return vals + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + + return []string{} +} diff --git a/vendor/github.com/stretchr/testify/LICENSE b/vendor/github.com/stretchr/testify/LICENSE index f38ec5956b..4b0421cf9e 100644 --- a/vendor/github.com/stretchr/testify/LICENSE +++ b/vendor/github.com/stretchr/testify/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2012-2018 Mat Ryer and Tyler Bunnell +Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_compare.go similarity index 62% rename from vendor/github.com/stretchr/testify/assert/assertion_order.go rename to vendor/github.com/stretchr/testify/assert/assertion_compare.go index 15a486ca6e..dc200395ce 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_order.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare.go @@ -5,20 +5,28 @@ import ( "reflect" ) -func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { +type CompareType int + +const ( + compareLess CompareType = iota - 1 + compareEqual + compareGreater +) + +func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { switch kind { case reflect.Int: { intobj1 := obj1.(int) intobj2 := obj2.(int) if intobj1 > intobj2 { - return -1, true + return compareGreater, true } if intobj1 == intobj2 { - return 0, true + return compareEqual, true } if intobj1 < intobj2 { - return 1, true + return compareLess, true } } case reflect.Int8: @@ -26,13 +34,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { int8obj1 := obj1.(int8) int8obj2 := obj2.(int8) if int8obj1 > int8obj2 { - return -1, true + return compareGreater, true } if int8obj1 == int8obj2 { - return 0, true + return compareEqual, true } if int8obj1 < int8obj2 { - return 1, true + return compareLess, true } } case reflect.Int16: @@ -40,13 +48,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { int16obj1 := obj1.(int16) int16obj2 := obj2.(int16) if int16obj1 > int16obj2 { - return -1, true + return compareGreater, true } if int16obj1 == int16obj2 { - return 0, true + return compareEqual, true } if int16obj1 < int16obj2 { - return 1, true + return compareLess, true } } case reflect.Int32: @@ -54,13 +62,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { int32obj1 := obj1.(int32) int32obj2 := obj2.(int32) if int32obj1 > int32obj2 { - return -1, true + return compareGreater, true } if int32obj1 == int32obj2 { - return 0, true + return compareEqual, true } if int32obj1 < int32obj2 { - return 1, true + return compareLess, true } } case reflect.Int64: @@ -68,13 +76,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { int64obj1 := obj1.(int64) int64obj2 := obj2.(int64) if int64obj1 > int64obj2 { - return -1, true + return compareGreater, true } if int64obj1 == int64obj2 { - return 0, true + return compareEqual, true } if int64obj1 < int64obj2 { - return 1, true + return compareLess, true } } case reflect.Uint: @@ -82,13 +90,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { uintobj1 := obj1.(uint) uintobj2 := obj2.(uint) if uintobj1 > uintobj2 { - return -1, true + return compareGreater, true } if uintobj1 == uintobj2 { - return 0, true + return compareEqual, true } if uintobj1 < uintobj2 { - return 1, true + return compareLess, true } } case reflect.Uint8: @@ -96,13 +104,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { uint8obj1 := obj1.(uint8) uint8obj2 := obj2.(uint8) if uint8obj1 > uint8obj2 { - return -1, true + return compareGreater, true } if uint8obj1 == uint8obj2 { - return 0, true + return compareEqual, true } if uint8obj1 < uint8obj2 { - return 1, true + return compareLess, true } } case reflect.Uint16: @@ -110,13 +118,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { uint16obj1 := obj1.(uint16) uint16obj2 := obj2.(uint16) if uint16obj1 > uint16obj2 { - return -1, true + return compareGreater, true } if uint16obj1 == uint16obj2 { - return 0, true + return compareEqual, true } if uint16obj1 < uint16obj2 { - return 1, true + return compareLess, true } } case reflect.Uint32: @@ -124,13 +132,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { uint32obj1 := obj1.(uint32) uint32obj2 := obj2.(uint32) if uint32obj1 > uint32obj2 { - return -1, true + return compareGreater, true } if uint32obj1 == uint32obj2 { - return 0, true + return compareEqual, true } if uint32obj1 < uint32obj2 { - return 1, true + return compareLess, true } } case reflect.Uint64: @@ -138,13 +146,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { uint64obj1 := obj1.(uint64) uint64obj2 := obj2.(uint64) if uint64obj1 > uint64obj2 { - return -1, true + return compareGreater, true } if uint64obj1 == uint64obj2 { - return 0, true + return compareEqual, true } if uint64obj1 < uint64obj2 { - return 1, true + return compareLess, true } } case reflect.Float32: @@ -152,13 +160,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { float32obj1 := obj1.(float32) float32obj2 := obj2.(float32) if float32obj1 > float32obj2 { - return -1, true + return compareGreater, true } if float32obj1 == float32obj2 { - return 0, true + return compareEqual, true } if float32obj1 < float32obj2 { - return 1, true + return compareLess, true } } case reflect.Float64: @@ -166,13 +174,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { float64obj1 := obj1.(float64) float64obj2 := obj2.(float64) if float64obj1 > float64obj2 { - return -1, true + return compareGreater, true } if float64obj1 == float64obj2 { - return 0, true + return compareEqual, true } if float64obj1 < float64obj2 { - return 1, true + return compareLess, true } } case reflect.String: @@ -180,18 +188,18 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { stringobj1 := obj1.(string) stringobj2 := obj2.(string) if stringobj1 > stringobj2 { - return -1, true + return compareGreater, true } if stringobj1 == stringobj2 { - return 0, true + return compareEqual, true } if stringobj1 < stringobj2 { - return 1, true + return compareLess, true } } } - return 0, false + return compareEqual, false } // Greater asserts that the first element is greater than the second @@ -200,26 +208,7 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { // assert.Greater(t, float64(2), float64(1)) // assert.Greater(t, "b", "a") func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - e1Kind := reflect.ValueOf(e1).Kind() - e2Kind := reflect.ValueOf(e2).Kind() - if e1Kind != e2Kind { - return Fail(t, "Elements should be the same type", msgAndArgs...) - } - - res, isComparable := compare(e1, e2, e1Kind) - if !isComparable { - return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) - } - - if res != -1 { - return Fail(t, fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2), msgAndArgs...) - } - - return true + return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs) } // GreaterOrEqual asserts that the first element is greater than or equal to the second @@ -229,26 +218,7 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface // assert.GreaterOrEqual(t, "b", "a") // assert.GreaterOrEqual(t, "b", "b") func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - e1Kind := reflect.ValueOf(e1).Kind() - e2Kind := reflect.ValueOf(e2).Kind() - if e1Kind != e2Kind { - return Fail(t, "Elements should be the same type", msgAndArgs...) - } - - res, isComparable := compare(e1, e2, e1Kind) - if !isComparable { - return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) - } - - if res != -1 && res != 0 { - return Fail(t, fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2), msgAndArgs...) - } - - return true + return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs) } // Less asserts that the first element is less than the second @@ -257,26 +227,7 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in // assert.Less(t, float64(1), float64(2)) // assert.Less(t, "a", "b") func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - e1Kind := reflect.ValueOf(e1).Kind() - e2Kind := reflect.ValueOf(e2).Kind() - if e1Kind != e2Kind { - return Fail(t, "Elements should be the same type", msgAndArgs...) - } - - res, isComparable := compare(e1, e2, e1Kind) - if !isComparable { - return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) - } - - if res != 1 { - return Fail(t, fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2), msgAndArgs...) - } - - return true + return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs) } // LessOrEqual asserts that the first element is less than or equal to the second @@ -286,6 +237,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) // assert.LessOrEqual(t, "a", "b") // assert.LessOrEqual(t, "b", "b") func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { + return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) +} + +func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } @@ -296,14 +251,24 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter return Fail(t, "Elements should be the same type", msgAndArgs...) } - res, isComparable := compare(e1, e2, e1Kind) + compareResult, isComparable := compare(e1, e2, e1Kind) if !isComparable { return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) } - if res != 1 && res != 0 { - return Fail(t, fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2), msgAndArgs...) + if !containsValue(allowedComparesResults, compareResult) { + return Fail(t, fmt.Sprintf(failMessage, e1, e2), msgAndArgs...) } return true } + +func containsValue(values []CompareType, value CompareType) bool { + for _, v := range values { + if v == value { + return true + } + } + + return false +} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index bf89ecd21f..49370eb167 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -93,7 +93,7 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123)) +// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -127,7 +127,7 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick // Exactlyf asserts that two objects are equal in value and type. // -// assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123)) +// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -173,7 +173,7 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool // Greaterf asserts that the first element is greater than the second // // assert.Greaterf(t, 2, 1, "error message %s", "formatted") -// assert.Greaterf(t, float64(2, "error message %s", "formatted"), float64(1)) +// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") // assert.Greaterf(t, "b", "a", "error message %s", "formatted") func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { @@ -225,7 +225,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u // // assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // -// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). +// Returns whether the assertion was successful (true) or not (false). func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -237,7 +237,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, // // assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // -// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). +// Returns whether the assertion was successful (true) or not (false). func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -245,6 +245,18 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...) } +// HTTPStatusCodef asserts that a specified handler returns a specified status code. +// +// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return HTTPStatusCode(t, handler, method, url, values, statuscode, append([]interface{}{msg}, args...)...) +} + // HTTPSuccessf asserts that a specified handler returns a success status code. // // assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") @@ -259,7 +271,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin // Implementsf asserts that an object is implemented by the specified interface. // -// assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) +// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -341,7 +353,7 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf // Lessf asserts that the first element is less than the second // // assert.Lessf(t, 1, 2, "error message %s", "formatted") -// assert.Lessf(t, float64(1, "error message %s", "formatted"), float64(2)) +// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") // assert.Lessf(t, "a", "b", "error message %s", "formatted") func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { @@ -454,6 +466,16 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...) } +// NotEqualValuesf asserts that two objects are not equal even when converted to the same type +// +// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") +func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) +} + // NotNilf asserts that the specified object is not nil. // // assert.NotNilf(t, err, "error message %s", "formatted") @@ -476,7 +498,7 @@ func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bo // NotRegexpf asserts that a specified regexp does not match a string. // -// assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") +// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") // assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { @@ -552,7 +574,7 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str // Regexpf asserts that a specified regexp matches a string. // -// assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") +// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") // assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index 75ecdcaa2f..9db889427a 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -169,7 +169,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123)) +// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -251,7 +251,7 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg // Exactlyf asserts that two objects are equal in value and type. // -// a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123)) +// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -370,7 +370,7 @@ func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, // Greaterf asserts that the first element is greater than the second // // a.Greaterf(2, 1, "error message %s", "formatted") -// a.Greaterf(float64(2, "error message %s", "formatted"), float64(1)) +// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") // a.Greaterf("b", "a", "error message %s", "formatted") func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { @@ -447,7 +447,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri // // a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // -// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). +// Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -471,7 +471,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s // // a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // -// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). +// Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -479,6 +479,30 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url return HTTPRedirectf(a.t, handler, method, url, values, msg, args...) } +// HTTPStatusCode asserts that a specified handler returns a specified status code. +// +// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...) +} + +// HTTPStatusCodef asserts that a specified handler returns a specified status code. +// +// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPStatusCodef(a.t, handler, method, url, values, statuscode, msg, args...) +} + // HTTPSuccess asserts that a specified handler returns a success status code. // // a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) @@ -515,7 +539,7 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, // Implementsf asserts that an object is implemented by the specified interface. // -// a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) +// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -706,7 +730,7 @@ func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, ar // Lessf asserts that the first element is less than the second // // a.Lessf(1, 2, "error message %s", "formatted") -// a.Lessf(float64(1, "error message %s", "formatted"), float64(2)) +// a.Lessf(float64(1), float64(2), "error message %s", "formatted") // a.Lessf("a", "b", "error message %s", "formatted") func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { @@ -884,6 +908,26 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr return NotEqual(a.t, expected, actual, msgAndArgs...) } +// NotEqualValues asserts that two objects are not equal even when converted to the same type +// +// a.NotEqualValues(obj1, obj2) +func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotEqualValues(a.t, expected, actual, msgAndArgs...) +} + +// NotEqualValuesf asserts that two objects are not equal even when converted to the same type +// +// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") +func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotEqualValuesf(a.t, expected, actual, msg, args...) +} + // NotEqualf asserts that the specified values are NOT equal. // // a.NotEqualf(obj1, obj2, "error message %s", "formatted") @@ -950,7 +994,7 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in // NotRegexpf asserts that a specified regexp does not match a string. // -// a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") +// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") // a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { @@ -1102,7 +1146,7 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter // Regexpf asserts that a specified regexp matches a string. // -// a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") +// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") // a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index bdd81389a9..914a10d83a 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -19,7 +19,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/pmezard/go-difflib/difflib" - yaml "gopkg.in/yaml.v2" + yaml "gopkg.in/yaml.v3" ) //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl" @@ -45,7 +45,7 @@ type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool // for table driven tests. type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool -// Comparison a custom function that returns true on success and false on failure +// Comparison is a custom function that returns true on success and false on failure type Comparison func() (success bool) /* @@ -104,11 +104,11 @@ the problem actually occurred in calling code.*/ // failed. func CallerInfo() []string { - pc := uintptr(0) - file := "" - line := 0 - ok := false - name := "" + var pc uintptr + var ok bool + var file string + var line int + var name string callers := []string{} for i := 0; ; i++ { @@ -429,14 +429,27 @@ func samePointers(first, second interface{}) bool { // to a type conversion in the Go grammar. func formatUnequalValues(expected, actual interface{}) (e string, a string) { if reflect.TypeOf(expected) != reflect.TypeOf(actual) { - return fmt.Sprintf("%T(%#v)", expected, expected), - fmt.Sprintf("%T(%#v)", actual, actual) + return fmt.Sprintf("%T(%s)", expected, truncatingFormat(expected)), + fmt.Sprintf("%T(%s)", actual, truncatingFormat(actual)) } switch expected.(type) { case time.Duration: return fmt.Sprintf("%v", expected), fmt.Sprintf("%v", actual) } - return fmt.Sprintf("%#v", expected), fmt.Sprintf("%#v", actual) + return truncatingFormat(expected), truncatingFormat(actual) +} + +// truncatingFormat formats the data and truncates it if it's too long. +// +// This helps keep formatted error messages lines from exceeding the +// bufio.MaxScanTokenSize max line length that the go testing framework imposes. +func truncatingFormat(data interface{}) string { + value := fmt.Sprintf("%#v", data) + max := bufio.MaxScanTokenSize - 100 // Give us some space the type info too if needed. + if len(value) > max { + value = value[0:max] + "<... truncated>" + } + return value } // EqualValues asserts that two objects are equal or convertable to the same types @@ -483,12 +496,12 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} // // assert.NotNil(t, err) func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } if !isNil(object) { return true } + if h, ok := t.(tHelper); ok { + h.Helper() + } return Fail(t, "Expected value not to be nil.", msgAndArgs...) } @@ -529,12 +542,12 @@ func isNil(object interface{}) bool { // // assert.Nil(t, err) func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } if isNil(object) { return true } + if h, ok := t.(tHelper); ok { + h.Helper() + } return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...) } @@ -571,12 +584,11 @@ func isEmpty(object interface{}) bool { // // assert.Empty(t, obj) func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - pass := isEmpty(object) if !pass { + if h, ok := t.(tHelper); ok { + h.Helper() + } Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...) } @@ -591,12 +603,11 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { // assert.Equal(t, "two", obj[1]) // } func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - pass := !isEmpty(object) if !pass { + if h, ok := t.(tHelper); ok { + h.Helper() + } Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...) } @@ -639,16 +650,10 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) // // assert.True(t, myBool) func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if h, ok := t.(interface { - Helper() - }); ok { - h.Helper() - } - - if value != true { + if !value { + if h, ok := t.(tHelper); ok { + h.Helper() + } return Fail(t, "Should be true", msgAndArgs...) } @@ -660,11 +665,10 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { // // assert.False(t, myBool) func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - if value != false { + if value { + if h, ok := t.(tHelper); ok { + h.Helper() + } return Fail(t, "Should be false", msgAndArgs...) } @@ -695,6 +699,21 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{ } +// NotEqualValues asserts that two objects are not equal even when converted to the same type +// +// assert.NotEqualValues(t, obj1, obj2) +func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if ObjectsAreEqualValues(expected, actual) { + return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) + } + + return true +} + // containsElement try loop over the list check if the list includes the element. // return (false, false) if impossible. // return (true, false) if element was not found. @@ -747,10 +766,10 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo ok, found := includeElement(s, contains) if !ok { - return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) } if !found { - return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", s, contains), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v does not contain %#v", s, contains), msgAndArgs...) } return true @@ -881,27 +900,39 @@ func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface return true } - aKind := reflect.TypeOf(listA).Kind() - bKind := reflect.TypeOf(listB).Kind() - - if aKind != reflect.Array && aKind != reflect.Slice { - return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listA, aKind), msgAndArgs...) + if !isList(t, listA, msgAndArgs...) || !isList(t, listB, msgAndArgs...) { + return false } - if bKind != reflect.Array && bKind != reflect.Slice { - return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listB, bKind), msgAndArgs...) + extraA, extraB := diffLists(listA, listB) + + if len(extraA) == 0 && len(extraB) == 0 { + return true } + return Fail(t, formatListDiff(listA, listB, extraA, extraB), msgAndArgs...) +} + +// isList checks that the provided value is array or slice. +func isList(t TestingT, list interface{}, msgAndArgs ...interface{}) (ok bool) { + kind := reflect.TypeOf(list).Kind() + if kind != reflect.Array && kind != reflect.Slice { + return Fail(t, fmt.Sprintf("%q has an unsupported type %s, expecting array or slice", list, kind), + msgAndArgs...) + } + return true +} + +// diffLists diffs two arrays/slices and returns slices of elements that are only in A and only in B. +// If some element is present multiple times, each instance is counted separately (e.g. if something is 2x in A and +// 5x in B, it will be 0x in extraA and 3x in extraB). The order of items in both lists is ignored. +func diffLists(listA, listB interface{}) (extraA, extraB []interface{}) { aValue := reflect.ValueOf(listA) bValue := reflect.ValueOf(listB) aLen := aValue.Len() bLen := bValue.Len() - if aLen != bLen { - return Fail(t, fmt.Sprintf("lengths don't match: %d != %d", aLen, bLen), msgAndArgs...) - } - // Mark indexes in bValue that we already used visited := make([]bool, bLen) for i := 0; i < aLen; i++ { @@ -918,11 +949,38 @@ func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface } } if !found { - return Fail(t, fmt.Sprintf("element %s appears more times in %s than in %s", element, aValue, bValue), msgAndArgs...) + extraA = append(extraA, element) } } - return true + for j := 0; j < bLen; j++ { + if visited[j] { + continue + } + extraB = append(extraB, bValue.Index(j).Interface()) + } + + return +} + +func formatListDiff(listA, listB interface{}, extraA, extraB []interface{}) string { + var msg bytes.Buffer + + msg.WriteString("elements differ") + if len(extraA) > 0 { + msg.WriteString("\n\nextra elements in list A:\n") + msg.WriteString(spewConfig.Sdump(extraA)) + } + if len(extraB) > 0 { + msg.WriteString("\n\nextra elements in list B:\n") + msg.WriteString(spewConfig.Sdump(extraB)) + } + msg.WriteString("\n\nlistA:\n") + msg.WriteString(spewConfig.Sdump(listA)) + msg.WriteString("\n\nlistB:\n") + msg.WriteString(spewConfig.Sdump(listB)) + + return msg.String() } // Condition uses a Comparison to assert a complex condition. @@ -1058,6 +1116,8 @@ func toFloat(x interface{}) (float64, bool) { xok := true switch xn := x.(type) { + case uint: + xf = float64(xn) case uint8: xf = float64(xn) case uint16: @@ -1079,7 +1139,7 @@ func toFloat(x interface{}) (float64, bool) { case float32: xf = float64(xn) case float64: - xf = float64(xn) + xf = xn case time.Duration: xf = float64(xn) default: @@ -1193,6 +1253,9 @@ func calcRelativeError(expected, actual interface{}) (float64, error) { if !aok { return 0, fmt.Errorf("expected value %q cannot be converted to float", expected) } + if math.IsNaN(af) { + return 0, errors.New("expected value must not be NaN") + } if af == 0 { return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error") } @@ -1200,6 +1263,9 @@ func calcRelativeError(expected, actual interface{}) (float64, error) { if !bok { return 0, fmt.Errorf("actual value %q cannot be converted to float", actual) } + if math.IsNaN(bf) { + return 0, errors.New("actual value must not be NaN") + } return math.Abs(af-bf) / math.Abs(af), nil } @@ -1209,6 +1275,9 @@ func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAnd if h, ok := t.(tHelper); ok { h.Helper() } + if math.IsNaN(epsilon) { + return Fail(t, "epsilon must not be NaN") + } actualEpsilon, err := calcRelativeError(expected, actual) if err != nil { return Fail(t, err.Error(), msgAndArgs...) @@ -1256,10 +1325,10 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m // assert.Equal(t, expectedObj, actualObj) // } func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } if err != nil { + if h, ok := t.(tHelper); ok { + h.Helper() + } return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...) } @@ -1273,11 +1342,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { // assert.Equal(t, expectedError, err) // } func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if err == nil { + if h, ok := t.(tHelper); ok { + h.Helper() + } return Fail(t, "An error is expected but got nil.", msgAndArgs...) } @@ -1553,6 +1621,7 @@ var spewConfig = spew.ConfigState{ DisablePointerAddresses: true, DisableCapacities: true, SortKeys: true, + DisableMethods: true, } type tHelper interface { diff --git a/vendor/github.com/stretchr/testify/assert/http_assertions.go b/vendor/github.com/stretchr/testify/assert/http_assertions.go index df46fa777a..4ed341dd28 100644 --- a/vendor/github.com/stretchr/testify/assert/http_assertions.go +++ b/vendor/github.com/stretchr/testify/assert/http_assertions.go @@ -33,7 +33,6 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value code, err := httpCode(handler, method, url, values) if err != nil { Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) - return false } isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent @@ -56,7 +55,6 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu code, err := httpCode(handler, method, url, values) if err != nil { Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) - return false } isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect @@ -79,7 +77,6 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values code, err := httpCode(handler, method, url, values) if err != nil { Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) - return false } isErrorCode := code >= http.StatusBadRequest @@ -90,6 +87,28 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values return isErrorCode } +// HTTPStatusCode asserts that a specified handler returns a specified status code. +// +// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + code, err := httpCode(handler, method, url, values) + if err != nil { + Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) + } + + successful := code == statuscode + if !successful { + Fail(t, fmt.Sprintf("Expected HTTP status code %d for %q but received %d", statuscode, url+"?"+values.Encode(), code)) + } + + return successful +} + // HTTPBody is a helper that returns HTTP body of the response. It returns // empty string if building a new request fails. func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { diff --git a/vendor/github.com/stretchr/testify/mock/doc.go b/vendor/github.com/stretchr/testify/mock/doc.go new file mode 100644 index 0000000000..7324128ef1 --- /dev/null +++ b/vendor/github.com/stretchr/testify/mock/doc.go @@ -0,0 +1,44 @@ +// Package mock provides a system by which it is possible to mock your objects +// and verify calls are happening as expected. +// +// Example Usage +// +// The mock package provides an object, Mock, that tracks activity on another object. It is usually +// embedded into a test object as shown below: +// +// type MyTestObject struct { +// // add a Mock object instance +// mock.Mock +// +// // other fields go here as normal +// } +// +// When implementing the methods of an interface, you wire your functions up +// to call the Mock.Called(args...) method, and return the appropriate values. +// +// For example, to mock a method that saves the name and age of a person and returns +// the year of their birth or an error, you might write this: +// +// func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) { +// args := o.Called(firstname, lastname, age) +// return args.Int(0), args.Error(1) +// } +// +// The Int, Error and Bool methods are examples of strongly typed getters that take the argument +// index position. Given this argument list: +// +// (12, true, "Something") +// +// You could read them out strongly typed like this: +// +// args.Int(0) +// args.Bool(1) +// args.String(2) +// +// For objects of your own type, use the generic Arguments.Get(index) method and make a type assertion: +// +// return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine) +// +// This may cause a panic if the object you are getting is nil (the type assertion will fail), in those +// cases you should check for nil first. +package mock diff --git a/vendor/github.com/stretchr/testify/mock/mock.go b/vendor/github.com/stretchr/testify/mock/mock.go new file mode 100644 index 0000000000..c6df4485ab --- /dev/null +++ b/vendor/github.com/stretchr/testify/mock/mock.go @@ -0,0 +1,981 @@ +package mock + +import ( + "errors" + "fmt" + "reflect" + "regexp" + "runtime" + "strings" + "sync" + "time" + + "github.com/davecgh/go-spew/spew" + "github.com/pmezard/go-difflib/difflib" + "github.com/stretchr/objx" + "github.com/stretchr/testify/assert" +) + +// TestingT is an interface wrapper around *testing.T +type TestingT interface { + Logf(format string, args ...interface{}) + Errorf(format string, args ...interface{}) + FailNow() +} + +/* + Call +*/ + +// Call represents a method call and is used for setting expectations, +// as well as recording activity. +type Call struct { + Parent *Mock + + // The name of the method that was or will be called. + Method string + + // Holds the arguments of the method. + Arguments Arguments + + // Holds the arguments that should be returned when + // this method is called. + ReturnArguments Arguments + + // Holds the caller info for the On() call + callerInfo []string + + // The number of times to return the return arguments when setting + // expectations. 0 means to always return the value. + Repeatability int + + // Amount of times this call has been called + totalCalls int + + // Call to this method can be optional + optional bool + + // Holds a channel that will be used to block the Return until it either + // receives a message or is closed. nil means it returns immediately. + WaitFor <-chan time.Time + + waitTime time.Duration + + // Holds a handler used to manipulate arguments content that are passed by + // reference. It's useful when mocking methods such as unmarshalers or + // decoders. + RunFn func(Arguments) + + // PanicMsg holds msg to be used to mock panic on the function call + // if the PanicMsg is set to a non nil string the function call will panic + // irrespective of other settings + PanicMsg *string +} + +func newCall(parent *Mock, methodName string, callerInfo []string, methodArguments ...interface{}) *Call { + return &Call{ + Parent: parent, + Method: methodName, + Arguments: methodArguments, + ReturnArguments: make([]interface{}, 0), + callerInfo: callerInfo, + Repeatability: 0, + WaitFor: nil, + RunFn: nil, + PanicMsg: nil, + } +} + +func (c *Call) lock() { + c.Parent.mutex.Lock() +} + +func (c *Call) unlock() { + c.Parent.mutex.Unlock() +} + +// Return specifies the return arguments for the expectation. +// +// Mock.On("DoSomething").Return(errors.New("failed")) +func (c *Call) Return(returnArguments ...interface{}) *Call { + c.lock() + defer c.unlock() + + c.ReturnArguments = returnArguments + + return c +} + +// Panic specifies if the functon call should fail and the panic message +// +// Mock.On("DoSomething").Panic("test panic") +func (c *Call) Panic(msg string) *Call { + c.lock() + defer c.unlock() + + c.PanicMsg = &msg + + return c +} + +// Once indicates that that the mock should only return the value once. +// +// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once() +func (c *Call) Once() *Call { + return c.Times(1) +} + +// Twice indicates that that the mock should only return the value twice. +// +// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice() +func (c *Call) Twice() *Call { + return c.Times(2) +} + +// Times indicates that that the mock should only return the indicated number +// of times. +// +// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5) +func (c *Call) Times(i int) *Call { + c.lock() + defer c.unlock() + c.Repeatability = i + return c +} + +// WaitUntil sets the channel that will block the mock's return until its closed +// or a message is received. +// +// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second)) +func (c *Call) WaitUntil(w <-chan time.Time) *Call { + c.lock() + defer c.unlock() + c.WaitFor = w + return c +} + +// After sets how long to block until the call returns +// +// Mock.On("MyMethod", arg1, arg2).After(time.Second) +func (c *Call) After(d time.Duration) *Call { + c.lock() + defer c.unlock() + c.waitTime = d + return c +} + +// Run sets a handler to be called before returning. It can be used when +// mocking a method (such as an unmarshaler) that takes a pointer to a struct and +// sets properties in such struct +// +// Mock.On("Unmarshal", AnythingOfType("*map[string]interface{}")).Return().Run(func(args Arguments) { +// arg := args.Get(0).(*map[string]interface{}) +// arg["foo"] = "bar" +// }) +func (c *Call) Run(fn func(args Arguments)) *Call { + c.lock() + defer c.unlock() + c.RunFn = fn + return c +} + +// Maybe allows the method call to be optional. Not calling an optional method +// will not cause an error while asserting expectations +func (c *Call) Maybe() *Call { + c.lock() + defer c.unlock() + c.optional = true + return c +} + +// On chains a new expectation description onto the mocked interface. This +// allows syntax like. +// +// Mock. +// On("MyMethod", 1).Return(nil). +// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error")) +//go:noinline +func (c *Call) On(methodName string, arguments ...interface{}) *Call { + return c.Parent.On(methodName, arguments...) +} + +// Mock is the workhorse used to track activity on another object. +// For an example of its usage, refer to the "Example Usage" section at the top +// of this document. +type Mock struct { + // Represents the calls that are expected of + // an object. + ExpectedCalls []*Call + + // Holds the calls that were made to this mocked object. + Calls []Call + + // test is An optional variable that holds the test struct, to be used when an + // invalid mock call was made. + test TestingT + + // TestData holds any data that might be useful for testing. Testify ignores + // this data completely allowing you to do whatever you like with it. + testData objx.Map + + mutex sync.Mutex +} + +// TestData holds any data that might be useful for testing. Testify ignores +// this data completely allowing you to do whatever you like with it. +func (m *Mock) TestData() objx.Map { + + if m.testData == nil { + m.testData = make(objx.Map) + } + + return m.testData +} + +/* + Setting expectations +*/ + +// Test sets the test struct variable of the mock object +func (m *Mock) Test(t TestingT) { + m.mutex.Lock() + defer m.mutex.Unlock() + m.test = t +} + +// fail fails the current test with the given formatted format and args. +// In case that a test was defined, it uses the test APIs for failing a test, +// otherwise it uses panic. +func (m *Mock) fail(format string, args ...interface{}) { + m.mutex.Lock() + defer m.mutex.Unlock() + + if m.test == nil { + panic(fmt.Sprintf(format, args...)) + } + m.test.Errorf(format, args...) + m.test.FailNow() +} + +// On starts a description of an expectation of the specified method +// being called. +// +// Mock.On("MyMethod", arg1, arg2) +func (m *Mock) On(methodName string, arguments ...interface{}) *Call { + for _, arg := range arguments { + if v := reflect.ValueOf(arg); v.Kind() == reflect.Func { + panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg)) + } + } + + m.mutex.Lock() + defer m.mutex.Unlock() + c := newCall(m, methodName, assert.CallerInfo(), arguments...) + m.ExpectedCalls = append(m.ExpectedCalls, c) + return c +} + +// /* +// Recording and responding to activity +// */ + +func (m *Mock) findExpectedCall(method string, arguments ...interface{}) (int, *Call) { + var expectedCall *Call + + for i, call := range m.ExpectedCalls { + if call.Method == method { + _, diffCount := call.Arguments.Diff(arguments) + if diffCount == 0 { + expectedCall = call + if call.Repeatability > -1 { + return i, call + } + } + } + } + + return -1, expectedCall +} + +func (m *Mock) findClosestCall(method string, arguments ...interface{}) (*Call, string) { + var diffCount int + var closestCall *Call + var err string + + for _, call := range m.expectedCalls() { + if call.Method == method { + + errInfo, tempDiffCount := call.Arguments.Diff(arguments) + if tempDiffCount < diffCount || diffCount == 0 { + diffCount = tempDiffCount + closestCall = call + err = errInfo + } + + } + } + + return closestCall, err +} + +func callString(method string, arguments Arguments, includeArgumentValues bool) string { + + var argValsString string + if includeArgumentValues { + var argVals []string + for argIndex, arg := range arguments { + argVals = append(argVals, fmt.Sprintf("%d: %#v", argIndex, arg)) + } + argValsString = fmt.Sprintf("\n\t\t%s", strings.Join(argVals, "\n\t\t")) + } + + return fmt.Sprintf("%s(%s)%s", method, arguments.String(), argValsString) +} + +// Called tells the mock object that a method has been called, and gets an array +// of arguments to return. Panics if the call is unexpected (i.e. not preceded by +// appropriate .On .Return() calls) +// If Call.WaitFor is set, blocks until the channel is closed or receives a message. +func (m *Mock) Called(arguments ...interface{}) Arguments { + // get the calling function's name + pc, _, _, ok := runtime.Caller(1) + if !ok { + panic("Couldn't get the caller information") + } + functionPath := runtime.FuncForPC(pc).Name() + //Next four lines are required to use GCCGO function naming conventions. + //For Ex: github_com_docker_libkv_store_mock.WatchTree.pN39_github_com_docker_libkv_store_mock.Mock + //uses interface information unlike golang github.com/docker/libkv/store/mock.(*Mock).WatchTree + //With GCCGO we need to remove interface information starting from pN