From 397b49ff515b4ee75b597e74d3349a3e8bc90883 Mon Sep 17 00:00:00 2001 From: Pawel Kosiec Date: Fri, 25 Feb 2022 19:05:59 +0100 Subject: [PATCH] Introduce improvements requested in review --- cmd/secret-storage-backend/README.md | 11 +- cmd/secret-storage-backend/main.go | 3 +- docs/proposal/20211207-delegated-storage.md | 24 +- hack/gen-graphql-resources.sh | 1 - hack/gen-grpc-resources.sh | 5 +- hack/gen-k8s-resources.sh | 1 - internal/secret-storage-backend/server.go | 225 ++++++++++++----- .../secret-storage-backend/server_test.go | 203 +++++++-------- pkg/hub/api/grpc/storage_backend.proto | 18 +- .../api/grpc/storage_backend/client_test.go | 189 ++++++++++++++ .../api/grpc/storage_backend/example_test.go | 201 --------------- .../storage_backend/storage_backend.pb.go | 238 ++++++++---------- 12 files changed, 597 insertions(+), 522 deletions(-) create mode 100644 pkg/hub/api/grpc/storage_backend/client_test.go delete mode 100644 pkg/hub/api/grpc/storage_backend/example_test.go diff --git a/cmd/secret-storage-backend/README.md b/cmd/secret-storage-backend/README.md index 3e566a6e0..642f4e9a9 100644 --- a/cmd/secret-storage-backend/README.md +++ b/cmd/secret-storage-backend/README.md @@ -4,11 +4,10 @@ Secret Storage Backend is a service which handles multiple secret storages for TypeInstances. -This service is implemented according to the [Delegated Storage](../../docs/proposal/20211207-delegated-storage.md) concept. - ## Prerequisites - [Go](https://golang.org) +- (Optional - if AWS Secrets Manager provider should be used) an AWS account with **AdministratorAccess** permissions on it ## Usage @@ -29,7 +28,7 @@ By default, the Secret Storage Backend has the `aws_secretsmanager` provider ena APP_LOGGER_DEV_MODE=true go run ./cmd/secret-storage-backend/main.go ``` -The server will listen to gRPC calls according to the [Storage Backend Protocol Buffers schema](../../pkg/hub/api/grpc/storage_backend.proto). +The server listens to gRPC calls according to the [Storage Backend Protocol Buffers schema](../../pkg/hub/api/grpc/storage_backend.proto). To perform such calls, you can use e.g. [Insomnia](https://insomnia.rest/) tool. ### Dotenv provider @@ -46,10 +45,10 @@ To run the server with `dotenv` provider enabled, which stores data in files, ex | Name | Required | Default | Description | |-------------------------|----------|----------------------|-------------------------------------------------------------------------------------------------------------------------------| -| APP_GRPC_ADDR | no | `:50051` | TCP address the gRPC server binds to | -| APP_HEALTHZ_ADDR | no | `:8082` | TCP address the health probes endpoint binds to | +| APP_GRPC_ADDR | no | `:50051` | TCP address the gRPC server binds to. | +| APP_HEALTHZ_ADDR | no | `:8082` | TCP address the health probes endpoint binds to. | | APP_SUPPORTED_PROVIDERS | no | `aws_secretsmanager` | Supported secret providers separated by `,`. A given provider must be passed in additional parameters of gRPC request inputs. | -| APP_LOGGER_DEV_MODE | no | `false` | Enable development mode logging | +| APP_LOGGER_DEV_MODE | no | `false` | Enable development mode logging. | To configure providers, use environmental variables described in the [Providers](https://github.com/SpectralOps/teller#providers) paragraph for Teller's Readme. diff --git a/cmd/secret-storage-backend/main.go b/cmd/secret-storage-backend/main.go index 6e957ada5..05c8455d4 100644 --- a/cmd/secret-storage-backend/main.go +++ b/cmd/secret-storage-backend/main.go @@ -20,12 +20,13 @@ import ( // Config holds application related configuration. type Config struct { - // Address is the TCP address the gRPC server binds to. + // GRPCAddr is the TCP address the gRPC server binds to. GRPCAddr string `envconfig:"default=:50051"` // HealthzAddr is the TCP address the health probes endpoint binds to. HealthzAddr string `envconfig:"default=:8082"` + // SupportedProviders holds enabled secret providers separated by comma. SupportedProviders []string `envconfig:"default=aws_secretsmanager"` Logger logger.Config diff --git a/docs/proposal/20211207-delegated-storage.md b/docs/proposal/20211207-delegated-storage.md index a8965515b..274a6a885 100644 --- a/docs/proposal/20211207-delegated-storage.md +++ b/docs/proposal/20211207-delegated-storage.md @@ -417,16 +417,16 @@ Also, the additional, nice-to-have goals are: ```proto message TypeInstanceData { string id = 1; - google.protobuf.Any value = 2; + bytes value = 2; } message OnCreateRequest { TypeInstanceData typeinstance = 1; - google.protobuf.Any additional_parameters = 2; + bytes context = 2; } message OnCreateResponse { - google.protobuf.Any additional_parameters = 1; + optional bytes context = 1; } service SearchService { @@ -483,11 +483,11 @@ Capact Local Hub calls proper storage backend service while accessing the TypeIn message OnCreateRequest { string typeinstance_id = 1; bytes value = 2; - bytes additional_parameters = 3; + bytes context = 3; } message OnCreateResponse { - optional bytes additional_parameters = 1; + optional bytes context = 1; } message TypeInstanceResourceVersion { @@ -499,16 +499,16 @@ Capact Local Hub calls proper storage backend service while accessing the TypeIn string typeinstance_id = 1; uint32 new_resource_version = 2; bytes new_value = 3; - optional bytes additional_parameters = 4; + optional bytes context = 4; } message OnUpdateResponse { - optional bytes additional_parameters = 1; + optional bytes context = 1; } message OnDeleteRequest { string typeinstance_id = 1; - bytes additional_parameters = 2; + bytes context = 2; } message OnDeleteResponse {} @@ -516,7 +516,7 @@ Capact Local Hub calls proper storage backend service while accessing the TypeIn message GetValueRequest { string typeinstance_id = 1; uint32 resource_version = 2; - bytes additional_parameters = 3; + bytes context = 3; } message GetValueResponse { @@ -528,7 +528,7 @@ Capact Local Hub calls proper storage backend service while accessing the TypeIn message GetLockedByRequest { string typeinstance_id = 1; - bytes additional_parameters = 2; + bytes context = 2; } message GetLockedByResponse { @@ -537,7 +537,7 @@ Capact Local Hub calls proper storage backend service while accessing the TypeIn message OnLockRequest { string typeinstance_id = 1; - bytes additional_parameters = 2; + bytes context = 2; string locked_by = 3; } @@ -545,7 +545,7 @@ Capact Local Hub calls proper storage backend service while accessing the TypeIn message OnUnlockRequest { string typeinstance_id = 1; - bytes additional_parameters = 2; + bytes context = 2; } message OnUnlockResponse {} diff --git a/hack/gen-graphql-resources.sh b/hack/gen-graphql-resources.sh index b41b36812..f10d7c479 100755 --- a/hack/gen-graphql-resources.sh +++ b/hack/gen-graphql-resources.sh @@ -38,7 +38,6 @@ host::install::gqlgen() { pushd "$TMP_DIR" >/dev/null - go mod init tmp go install "github.com/99designs/gqlgen@${STABLE_GQLGEN_VERSION}" popd >/dev/null diff --git a/hack/gen-grpc-resources.sh b/hack/gen-grpc-resources.sh index 034f21df6..6985b3d8c 100755 --- a/hack/gen-grpc-resources.sh +++ b/hack/gen-grpc-resources.sh @@ -51,7 +51,9 @@ host::install::protoc() { shout "Install the protoc ${STABLE_PROTOC_VERSION} locally to a tempdir..." mkdir -p "${TMP_DIR}/bin" - export PATH="${TMP_DIR}/bin:${PATH}" + export GOBIN="${TMP_DIR}/bin" + export PATH="${GOBIN}:${PATH}" + pushd "$TMP_DIR" >/dev/null os=$(host::os) @@ -65,7 +67,6 @@ host::install::protoc() { # extract the archive unzip "${name}".zip - go mod init tmp go install "google.golang.org/protobuf/cmd/protoc-gen-go@${STABLE_PROTOC_GEN_GO_VERSION}" go install "google.golang.org/grpc/cmd/protoc-gen-go-grpc@${STABLE_PROTOC_GEN_GO_GRPC_VERSION}" diff --git a/hack/gen-k8s-resources.sh b/hack/gen-k8s-resources.sh index 59208c1ee..3c6c4716e 100755 --- a/hack/gen-k8s-resources.sh +++ b/hack/gen-k8s-resources.sh @@ -41,7 +41,6 @@ host::install::controller-gen() { pushd "$TMP_DIR" >/dev/null - go mod init tmp go install sigs.k8s.io/controller-tools/cmd/controller-gen@$STABLE_CONTROLLER_GEN_VERSION popd >/dev/null diff --git a/internal/secret-storage-backend/server.go b/internal/secret-storage-backend/server.go index 2a471e5cd..5aebd6855 100644 --- a/internal/secret-storage-backend/server.go +++ b/internal/secret-storage-backend/server.go @@ -15,8 +15,8 @@ import ( "google.golang.org/grpc/status" ) -// AdditionalParameters holds Secret storage backend specific parameters. -type AdditionalParameters struct { +// Context holds Secret storage backend specific parameters. +type Context struct { Provider string `json:"provider"` } @@ -55,7 +55,7 @@ func (h *Handler) GetValue(_ context.Context, request *pb.GetValueRequest) (*pb. return nil, NilRequestInputError } - provider, err := h.getProviderFromAdditionalParams(request.AdditionalParameters) + provider, err := h.getProviderFromContext(request.Context) if err != nil { return nil, err } @@ -66,13 +66,12 @@ func (h *Handler) GetValue(_ context.Context, request *pb.GetValueRequest) (*pb. return nil, err } - var value []byte - if entry.IsFound { - value = []byte(entry.Value) + if !entry.IsFound { + return nil, status.Error(codes.NotFound, fmt.Sprintf("TypeInstance %q in revision %d was not found", request.TypeinstanceId, request.ResourceVersion)) } return &pb.GetValueResponse{ - Value: value, + Value: []byte(entry.Value), }, nil } @@ -82,20 +81,28 @@ func (h *Handler) GetLockedBy(_ context.Context, request *pb.GetLockedByRequest) return nil, NilRequestInputError } - provider, err := h.getProviderFromAdditionalParams(request.AdditionalParameters) + provider, err := h.getProviderFromContext(request.Context) if err != nil { return nil, err } - key := h.storageKeyForLockedBy(provider, request.TypeinstanceId) - entry, err := h.getEntry(provider, key) + key := tellercore.KeyPath{ + Path: h.storagePathForTypeInstance(provider, request.TypeinstanceId), + } + entries, err := h.getEntriesForPath(provider, key) if err != nil { return nil, err } + if len(entries) == 0 { + return nil, status.Error(codes.NotFound, fmt.Sprintf("TypeInstance %q not found: secret from path %q is empty", request.TypeinstanceId, key.Path)) + } + var lockedBy *string - if entry.IsFound && entry.Value != "" { - lockedBy = ptr.String(entry.Value) + for _, entry := range entries { + if entry.Key == lockedByField && entry.Value != "" { + lockedBy = ptr.String(entry.Value) + } } return &pb.GetLockedByResponse{ @@ -109,12 +116,17 @@ func (h *Handler) OnCreate(_ context.Context, request *pb.OnCreateRequest) (*pb. return nil, NilRequestInputError } - err := h.handlePutValue( - request.AdditionalParameters, - request.TypeinstanceId, - firstResourceVersion, - request.Value, - ) + provider, err := h.getProviderFromContext(request.Context) + if err != nil { + return nil, err + } + + key := h.storageKeyForTypeInstanceValue(provider, request.TypeinstanceId, firstResourceVersion) + if err := h.ensureSecretCanBeCreated(provider, key); err != nil { + return nil, err + } + + err = h.putEntry(provider, key, request.Value) if err != nil { return nil, err } @@ -128,12 +140,18 @@ func (h *Handler) OnUpdate(_ context.Context, request *pb.OnUpdateRequest) (*pb. return nil, NilRequestInputError } - err := h.handlePutValue( - request.AdditionalParameters, - request.TypeinstanceId, - request.NewResourceVersion, - request.NewValue, - ) + provider, err := h.getProviderFromContext(request.Context) + if err != nil { + return nil, err + } + + key := h.storageKeyForTypeInstanceValue(provider, request.TypeinstanceId, request.NewResourceVersion) + + if err := h.ensureSecretCanBeUpdated(provider, key); err != nil { + return nil, err + } + + err = h.putEntry(provider, key, request.NewValue) if err != nil { return nil, err } @@ -143,18 +161,23 @@ func (h *Handler) OnUpdate(_ context.Context, request *pb.OnUpdateRequest) (*pb. // OnLock handles TypeInstance locking by setting a secret entry in a given provider. // It doesn't check whether a given TypeInstance is already locked, but overrides the value in place -// TODO(review): Is that valid assumption? Is there a need to complicate the flow here? func (h *Handler) OnLock(_ context.Context, request *pb.OnLockRequest) (*pb.OnLockResponse, error) { if request == nil { return nil, NilRequestInputError } - provider, err := h.getProviderFromAdditionalParams(request.AdditionalParameters) + provider, err := h.getProviderFromContext(request.Context) + if err != nil { + return nil, err + } + + err = h.ensureSecretIsNotLocked(provider, request.TypeinstanceId) if err != nil { return nil, err } key := h.storageKeyForLockedBy(provider, request.TypeinstanceId) + err = h.putEntry(provider, key, []byte(request.LockedBy)) if err != nil { return nil, err @@ -169,13 +192,21 @@ func (h *Handler) OnUnlock(_ context.Context, request *pb.OnUnlockRequest) (*pb. return nil, NilRequestInputError } - provider, err := h.getProviderFromAdditionalParams(request.AdditionalParameters) + provider, err := h.getProviderFromContext(request.Context) if err != nil { return nil, err } - key := h.storageKeyForLockedBy(provider, request.TypeinstanceId) - err = h.deleteEntry(provider, key) + key := tellercore.KeyPath{ + Path: h.storagePathForTypeInstance(provider, request.TypeinstanceId), + } + err = h.ensureSecretCanBeUnlocked(provider, key) + if err != nil { + return nil, err + } + + lockedByKey := h.storageKeyForLockedBy(provider, request.TypeinstanceId) + err = h.deleteEntry(provider, lockedByKey) if err != nil { return nil, err } @@ -184,58 +215,43 @@ func (h *Handler) OnUnlock(_ context.Context, request *pb.OnUnlockRequest) (*pb. } // OnDelete handles TypeInstance deletion by removing a secret in a given provider. -// It doesn't check whether a given TypeInstance is locked. It assumes the caller ensured it's unlocked state. -// TODO(review): Is that a valid assumption? +// It checks whether a given TypeInstance is locked before doing such operation. func (h *Handler) OnDelete(_ context.Context, request *pb.OnDeleteRequest) (*pb.OnDeleteResponse, error) { if request == nil { return nil, NilRequestInputError } - provider, err := h.getProviderFromAdditionalParams(request.AdditionalParameters) + provider, err := h.getProviderFromContext(request.Context) if err != nil { return nil, err } - err = provider.DeleteMapping(tellercore.KeyPath{ + key := tellercore.KeyPath{ Path: h.storagePathForTypeInstance(provider, request.TypeinstanceId), - }) - if err != nil { - return nil, h.internalError(errors.Wrapf(err, "while deleting TypeInstance %q", request.TypeinstanceId)) } - - return &pb.OnDeleteResponse{}, nil -} - -func (h *Handler) handlePutValue(additionalParamsBytes []byte, typeInstanceID string, resourceVersion uint32, value []byte) error { - provider, err := h.getProviderFromAdditionalParams(additionalParamsBytes) + err = h.ensureSecretCanBeDeleted(provider, key) if err != nil { - return err - } - - key := h.storageKeyForTypeInstanceValue(provider, typeInstanceID, resourceVersion) - - if err := h.ensureEntryDoesNotExist(provider, key); err != nil { - return err + return nil, err } - err = h.putEntry(provider, key, value) + err = provider.DeleteMapping(key) if err != nil { - return err + return nil, h.internalError(errors.Wrapf(err, "while deleting TypeInstance %q", request.TypeinstanceId)) } - return nil + return &pb.OnDeleteResponse{}, nil } -func (h *Handler) getProviderFromAdditionalParams(additionalParamsBytes []byte) (tellercore.Provider, error) { - var additionalParams AdditionalParameters - err := json.Unmarshal(additionalParamsBytes, &additionalParams) +func (h *Handler) getProviderFromContext(contextBytes []byte) (tellercore.Provider, error) { + var context Context + err := json.Unmarshal(contextBytes, &context) if err != nil { return nil, h.internalError(errors.Wrap(err, "while unmarshaling additional parameters")) } - provider, ok := h.providers[additionalParams.Provider] + provider, ok := h.providers[context.Provider] if !ok { - return nil, h.internalError(fmt.Errorf("missing loaded provider with name %q", additionalParams.Provider)) + return nil, h.internalError(fmt.Errorf("missing loaded provider with name %q", context.Provider)) } return provider, nil @@ -251,6 +267,16 @@ func (h *Handler) getEntry(provider tellercore.Provider, key tellercore.KeyPath) return entry, nil } +func (h *Handler) getEntriesForPath(provider tellercore.Provider, key tellercore.KeyPath) ([]tellercore.EnvEntry, error) { + h.log.Info("getting whole secret", zap.String("path", key.Path), zap.String("provider", provider.Name())) + entries, err := provider.GetMapping(key) + if err != nil { + return nil, h.internalError(errors.Wrapf(err, "while getting value by path %q", key.Path)) + } + + return entries, nil +} + func (h *Handler) putEntry(provider tellercore.Provider, key tellercore.KeyPath, value []byte) error { h.log.Info("putting entry", zap.String("path", key.Path), zap.String("field", key.Field), zap.String("provider", provider.Name())) err := provider.Put(key, string(value)) @@ -293,18 +319,89 @@ func (h *Handler) storagePathForTypeInstance(provider tellercore.Provider, tiID switch provider.Name() { case "dotenv": prefix = "/tmp/" + default: + prefix = "/" } - return fmt.Sprintf("%s/capact/%s", prefix, tiID) + return fmt.Sprintf("%scapact/%s", prefix, tiID) } -func (h *Handler) ensureEntryDoesNotExist(provider tellercore.Provider, key tellercore.KeyPath) error { +func (h *Handler) ensureSecretCanBeCreated(provider tellercore.Provider, key tellercore.KeyPath) error { + entries, err := h.getEntriesForPath(provider, key) + if err != nil { + return h.internalError(err) + } + + if len(entries) != 0 { + return status.Error(codes.AlreadyExists, fmt.Sprintf("path %q in provider %q already exist", key.Path, provider.Name())) + } + + return nil +} + +func (h *Handler) ensureSecretCanBeUpdated(provider tellercore.Provider, key tellercore.KeyPath) error { + entries, err := h.getEntriesForPath(provider, key) + if err != nil { + return h.internalError(err) + } + + if len(entries) == 0 { + return status.Error(codes.NotFound, fmt.Sprintf("path %q in provider %q not found", key.Path, provider.Name())) + } + + for _, entry := range entries { + if entry.Key == lockedByField { + return h.typeInstanceLockedError(key.Path, entry.Value) + } + if entry.Key == key.Field { + return status.Error(codes.AlreadyExists, fmt.Sprintf("field %q for path %q in provider %q already exist", key.Field, key.Path, provider.Name())) + } + } + + return nil +} + +func (h *Handler) ensureSecretIsNotLocked(provider tellercore.Provider, typeInstanceID string) error { + key := h.storageKeyForLockedBy(provider, typeInstanceID) entry, err := h.getEntry(provider, key) if err != nil { - return h.internalError(errors.Wrapf(err, "while getting field %q for path %q", key.Field, key.Path)) + return h.internalError(errors.Wrapf(err, "while getting entry")) + } + if entry.IsFound && entry.Value != "" { + return h.typeInstanceLockedError(key.Path, entry.Value) + } + + return nil +} + +func (h *Handler) ensureSecretCanBeDeleted(provider tellercore.Provider, key tellercore.KeyPath) error { + entries, err := h.getEntriesForPath(provider, key) + if err != nil { + return h.internalError(err) + } + + if len(entries) == 0 { + return status.Error(codes.NotFound, fmt.Sprintf("path %q in provider %q not found", key.Path, provider.Name())) + } + + for _, entry := range entries { + if entry.Key != lockedByField { + continue + } + return h.typeInstanceLockedError(key.Path, entry.Value) + } + + return nil +} + +func (h *Handler) ensureSecretCanBeUnlocked(provider tellercore.Provider, key tellercore.KeyPath) error { + entries, err := h.getEntriesForPath(provider, key) + if err != nil { + return h.internalError(err) } - if entry.IsFound { - return status.Error(codes.AlreadyExists, fmt.Sprintf("field %q for path %q in provider %q already exist", key.Field, key.Path, provider.Name())) + + if len(entries) == 0 { + return status.Error(codes.NotFound, fmt.Sprintf("path %q in provider %q not found", key.Path, provider.Name())) } return nil @@ -313,3 +410,7 @@ func (h *Handler) ensureEntryDoesNotExist(provider tellercore.Provider, key tell func (h *Handler) internalError(err error) error { return status.Error(codes.Internal, err.Error()) } + +func (h *Handler) typeInstanceLockedError(path, lockedByValue string) error { + return status.Error(codes.FailedPrecondition, fmt.Sprintf("typeInstance locked: path %q contains %q property with value %q", path, lockedByField, lockedByValue)) +} diff --git a/internal/secret-storage-backend/server_test.go b/internal/secret-storage-backend/server_test.go index 83e83642b..bcccb8509 100644 --- a/internal/secret-storage-backend/server_test.go +++ b/internal/secret-storage-backend/server_test.go @@ -20,23 +20,25 @@ import ( func TestHandler_GetValue(t *testing.T) { // given providerName := "fake" - reqAdditionalParams := []byte(fmt.Sprintf(`{"provider":"%s"}`, providerName)) + reqContext := []byte(fmt.Sprintf(`{"provider":"%s"}`, providerName)) req := &storage_backend.GetValueRequest{ - TypeinstanceId: "uuid", - ResourceVersion: 2, - AdditionalParameters: reqAdditionalParams, + TypeinstanceId: "uuid", + ResourceVersion: 2, + Context: reqContext, } path := fmt.Sprintf("/capact/%s", req.TypeinstanceId) testCases := []struct { - Name string - InputProvider tellercore.Provider - ExpectedValue []byte + Name string + InputProvider tellercore.Provider + ExpectedValue []byte + ExpectedErrorMessage *string }{ { - Name: "No secret", - InputProvider: &fakeProvider{}, - ExpectedValue: nil, + Name: "No secret", + InputProvider: &fakeProvider{}, + ExpectedValue: nil, + ExpectedErrorMessage: ptr.String("rpc error: code = NotFound desc = TypeInstance \"uuid\" in revision 2 was not found"), }, { Name: "Success", @@ -80,8 +82,14 @@ func TestHandler_GetValue(t *testing.T) { res, err := client.GetValue(ctx, req) // then + if testCase.ExpectedErrorMessage != nil { + assert.Nil(t, res) + require.Error(t, err) + assert.EqualError(t, err, *testCase.ExpectedErrorMessage) + return + } + require.NoError(t, err) - require.NotNil(t, res) assert.Equal(t, testCase.ExpectedValue, res.Value) }) } @@ -90,28 +98,32 @@ func TestHandler_GetValue(t *testing.T) { func TestHandler_GetLockedBy(t *testing.T) { // given providerName := "fake" - reqAdditionalParams := []byte(fmt.Sprintf(`{"provider":"%s"}`, providerName)) + reqContext := []byte(fmt.Sprintf(`{"provider":"%s"}`, providerName)) req := &storage_backend.GetLockedByRequest{ - TypeinstanceId: "uuid", - AdditionalParameters: reqAdditionalParams, + TypeinstanceId: "uuid", + Context: reqContext, } path := fmt.Sprintf("/capact/%s", req.TypeinstanceId) testCases := []struct { - Name string - InputProvider tellercore.Provider - ExpectedLockedBy *string + Name string + InputProvider tellercore.Provider + ExpectedLockedBy *string + ExpectedErrorMessage *string }{ { - Name: "No data", - InputProvider: &fakeProvider{}, - ExpectedLockedBy: nil, + Name: "No data", + InputProvider: &fakeProvider{}, + ExpectedLockedBy: nil, + ExpectedErrorMessage: ptr.String("rpc error: code = NotFound desc = TypeInstance \"uuid\" not found: secret from path \"/capact/uuid\" is empty"), }, { Name: "Empty value", InputProvider: &fakeProvider{ secrets: map[string]map[string]string{ - path: {}, + path: { + "1": "bar", + }, }, }, ExpectedLockedBy: nil, @@ -147,16 +159,16 @@ func TestHandler_GetLockedBy(t *testing.T) { res, err := client.GetLockedBy(ctx, req) // then - require.NoError(t, err) - require.NotNil(t, res) - - if testCase.ExpectedLockedBy == nil { - assert.Nil(t, res.LockedBy) + if testCase.ExpectedErrorMessage != nil { + assert.Nil(t, res) + require.Error(t, err) + assert.EqualError(t, err, *testCase.ExpectedErrorMessage) return } - require.NotNil(t, res.LockedBy) - assert.Equal(t, *testCase.ExpectedLockedBy, *res.LockedBy) + require.NoError(t, err) + require.NotNil(t, res) + assert.Equal(t, testCase.ExpectedLockedBy, res.LockedBy) }) } } @@ -164,12 +176,12 @@ func TestHandler_GetLockedBy(t *testing.T) { func TestHandler_OnCreate(t *testing.T) { // given providerName := "fake" - reqAdditionalParams := []byte(fmt.Sprintf(`{"provider":"%s"}`, providerName)) + reqContext := []byte(fmt.Sprintf(`{"provider":"%s"}`, providerName)) valueBytes := []byte(`{"key": true}`) req := &storage_backend.OnCreateRequest{ - TypeinstanceId: "uuid", - Value: valueBytes, - AdditionalParameters: reqAdditionalParams, + TypeinstanceId: "uuid", + Value: valueBytes, + Context: reqContext, } path := fmt.Sprintf("/capact/%s", req.TypeinstanceId) @@ -202,23 +214,7 @@ func TestHandler_OnCreate(t *testing.T) { }, }, { - Name: "Already existing without conflict", - InputProvider: &fakeProvider{ - secrets: map[string]map[string]string{ - path: { - "locked_by": "service/foo", - }, - }, - }, - ExpectedProviderState: map[string]map[string]string{ - path: { - "1": string(valueBytes), - "locked_by": "service/foo", - }, - }, - }, - { - Name: "Already existing with conflict", + Name: "Already existing", InputProvider: &fakeProvider{ secrets: map[string]map[string]string{ path: { @@ -231,7 +227,7 @@ func TestHandler_OnCreate(t *testing.T) { "1": "original", }, }, - ExpectedErrorMessage: ptr.String("rpc error: code = AlreadyExists desc = field \"1\" for path \"/capact/uuid\" in provider \"fake\" already exist"), + ExpectedErrorMessage: ptr.String("rpc error: code = AlreadyExists desc = path \"/capact/uuid\" in provider \"fake\" already exist"), }, } @@ -265,7 +261,7 @@ func TestHandler_OnCreate(t *testing.T) { require.NoError(t, err) require.NotNil(t, res) - assert.Nil(t, res.AdditionalParameters) + assert.Nil(t, res.Context) }) } } @@ -273,13 +269,13 @@ func TestHandler_OnCreate(t *testing.T) { func TestHandler_OnUpdate(t *testing.T) { // given providerName := "fake" - reqAdditionalParams := []byte(fmt.Sprintf(`{"provider":"%s"}`, providerName)) + reqContext := []byte(fmt.Sprintf(`{"provider":"%s"}`, providerName)) valueBytes := []byte(`{"key": true}`) req := &storage_backend.OnUpdateRequest{ - TypeinstanceId: "uuid", - NewResourceVersion: 3, - NewValue: valueBytes, - AdditionalParameters: reqAdditionalParams, + TypeinstanceId: "uuid", + NewResourceVersion: 3, + NewValue: valueBytes, + Context: reqContext, } path := fmt.Sprintf("/capact/%s", req.TypeinstanceId) @@ -291,29 +287,19 @@ func TestHandler_OnUpdate(t *testing.T) { ExpectedErrorMessage *string }{ { - Name: "No data", // data for a give nrevision could reside in different storage backend - InputProvider: &fakeProvider{secrets: map[string]map[string]string{}}, - ExpectedProviderState: map[string]map[string]string{ - path: { - "3": string(valueBytes), - }, - }, - }, - { - Name: "Empty value", + Name: "Non-existing secret", InputProvider: &fakeProvider{ secrets: map[string]map[string]string{ path: {}, }, }, ExpectedProviderState: map[string]map[string]string{ - path: { - "3": string(valueBytes), - }, + path: {}, }, + ExpectedErrorMessage: ptr.String("rpc error: code = NotFound desc = path \"/capact/uuid\" in provider \"fake\" not found"), }, { - Name: "Already existing without conflict", + Name: "Already existing locked", InputProvider: &fakeProvider{ secrets: map[string]map[string]string{ path: { @@ -325,13 +311,13 @@ func TestHandler_OnUpdate(t *testing.T) { ExpectedProviderState: map[string]map[string]string{ path: { "1": "original", - "3": string(valueBytes), "locked_by": "service/foo", }, }, + ExpectedErrorMessage: ptr.String("rpc error: code = FailedPrecondition desc = typeInstance locked: path \"/capact/uuid\" contains \"locked_by\" property with value \"service/foo\""), }, { - Name: "Already existing with conflict", + Name: "Already existing not locked", InputProvider: &fakeProvider{ secrets: map[string]map[string]string{ path: { @@ -378,7 +364,7 @@ func TestHandler_OnUpdate(t *testing.T) { require.NoError(t, err) require.NotNil(t, res) - assert.Nil(t, res.AdditionalParameters) + assert.Nil(t, res.Context) }) } } @@ -386,11 +372,11 @@ func TestHandler_OnUpdate(t *testing.T) { func TestHandler_OnLock(t *testing.T) { // given providerName := "fake" - reqAdditionalParams := []byte(fmt.Sprintf(`{"provider":"%s"}`, providerName)) + reqContext := []byte(fmt.Sprintf(`{"provider":"%s"}`, providerName)) req := &storage_backend.OnLockRequest{ - TypeinstanceId: "uuid", - LockedBy: "foo/sample", - AdditionalParameters: reqAdditionalParams, + TypeinstanceId: "uuid", + LockedBy: "foo/sample", + Context: reqContext, } path := fmt.Sprintf("/capact/%s", req.TypeinstanceId) @@ -402,7 +388,7 @@ func TestHandler_OnLock(t *testing.T) { ExpectedErrorMessage *string }{ { - Name: "No data", // data for a give nrevision could reside in different storage backend + Name: "No data", InputProvider: &fakeProvider{secrets: map[string]map[string]string{}}, ExpectedProviderState: map[string]map[string]string{ path: { @@ -440,7 +426,7 @@ func TestHandler_OnLock(t *testing.T) { }, }, { - Name: "Already existing with conflict", + Name: "Already existing locked", InputProvider: &fakeProvider{ secrets: map[string]map[string]string{ path: { @@ -452,9 +438,10 @@ func TestHandler_OnLock(t *testing.T) { ExpectedProviderState: map[string]map[string]string{ path: { "3": "original", - "locked_by": "foo/sample", + "locked_by": "previous", }, }, + ExpectedErrorMessage: ptr.String("rpc error: code = FailedPrecondition desc = typeInstance locked: path \"/capact/uuid\" contains \"locked_by\" property with value \"previous\""), }, } @@ -495,10 +482,10 @@ func TestHandler_OnLock(t *testing.T) { func TestHandler_OnUnlock(t *testing.T) { // given providerName := "fake" - reqAdditionalParams := []byte(fmt.Sprintf(`{"provider":"%s"}`, providerName)) + reqContext := []byte(fmt.Sprintf(`{"provider":"%s"}`, providerName)) req := &storage_backend.OnUnlockRequest{ - TypeinstanceId: "uuid", - AdditionalParameters: reqAdditionalParams, + TypeinstanceId: "uuid", + Context: reqContext, } path := fmt.Sprintf("/capact/%s", req.TypeinstanceId) @@ -510,9 +497,10 @@ func TestHandler_OnUnlock(t *testing.T) { ExpectedErrorMessage *string }{ { - Name: "No data", // data for a give nrevision could reside in different storage backend + Name: "No data", InputProvider: &fakeProvider{secrets: map[string]map[string]string{}}, ExpectedProviderState: map[string]map[string]string{}, + ExpectedErrorMessage: ptr.String("rpc error: code = NotFound desc = path \"/capact/uuid\" in provider \"fake\" not found"), }, { Name: "Already existing without conflict", @@ -546,6 +534,21 @@ func TestHandler_OnUnlock(t *testing.T) { }, }, }, + { + Name: "Already existing without lockedBy property", + InputProvider: &fakeProvider{ + secrets: map[string]map[string]string{ + path: { + "3": "original", + }, + }, + }, + ExpectedProviderState: map[string]map[string]string{ + path: { + "3": "original", + }, + }, + }, } for _, testCase := range testCases { @@ -585,10 +588,10 @@ func TestHandler_OnUnlock(t *testing.T) { func TestHandler_OnDelete(t *testing.T) { // given providerName := "fake" - reqAdditionalParams := []byte(fmt.Sprintf(`{"provider":"%s"}`, providerName)) + reqContext := []byte(fmt.Sprintf(`{"provider":"%s"}`, providerName)) req := &storage_backend.OnDeleteRequest{ - TypeinstanceId: "uuid", - AdditionalParameters: reqAdditionalParams, + TypeinstanceId: "uuid", + Context: reqContext, } path := fmt.Sprintf("/capact/%s", req.TypeinstanceId) @@ -600,9 +603,10 @@ func TestHandler_OnDelete(t *testing.T) { ExpectedErrorMessage *string }{ { - Name: "No data", // data for a give nrevision could reside in different storage backend + Name: "No data", InputProvider: &fakeProvider{secrets: map[string]map[string]string{}}, ExpectedProviderState: map[string]map[string]string{}, + ExpectedErrorMessage: ptr.String("rpc error: code = NotFound desc = path \"/capact/uuid\" in provider \"fake\" not found"), }, { Name: "Empty value", @@ -611,15 +615,15 @@ func TestHandler_OnDelete(t *testing.T) { path: {}, }, }, - ExpectedProviderState: map[string]map[string]string{}, + ExpectedProviderState: map[string]map[string]string{path: {}}, + ExpectedErrorMessage: ptr.String("rpc error: code = NotFound desc = path \"/capact/uuid\" in provider \"fake\" not found"), }, { - Name: "Already existing", + Name: "Already existing not locked", InputProvider: &fakeProvider{ secrets: map[string]map[string]string{ path: { - "1": "original", - "locked_by": "foo/bar", + "1": "original", }, "cant-touch-this": { "Music": "hits me so hard", @@ -635,21 +639,22 @@ func TestHandler_OnDelete(t *testing.T) { }, }, { - Name: "Other data", + Name: "Already existing locked", InputProvider: &fakeProvider{ secrets: map[string]map[string]string{ - "cant-touch-this": { - "Music": "hits me so hard", - "Makes me say": "Oh, my Lord", + path: { + "1": "original", + "locked_by": "foo/bar", }, }, }, ExpectedProviderState: map[string]map[string]string{ - "cant-touch-this": { - "Music": "hits me so hard", - "Makes me say": "Oh, my Lord", + path: { + "1": "original", + "locked_by": "foo/bar", }, }, + ExpectedErrorMessage: ptr.String("rpc error: code = FailedPrecondition desc = typeInstance locked: path \"/capact/uuid\" contains \"locked_by\" property with value \"foo/bar\""), }, } diff --git a/pkg/hub/api/grpc/storage_backend.proto b/pkg/hub/api/grpc/storage_backend.proto index 0925df3db..083040fd6 100644 --- a/pkg/hub/api/grpc/storage_backend.proto +++ b/pkg/hub/api/grpc/storage_backend.proto @@ -5,11 +5,11 @@ package storage_backend; message OnCreateRequest { string typeinstance_id = 1; bytes value = 2; - bytes additional_parameters = 3; + bytes context = 3; } message OnCreateResponse { - optional bytes additional_parameters = 1; + optional bytes context = 1; } message TypeInstanceResourceVersion { @@ -21,16 +21,16 @@ message OnUpdateRequest { string typeinstance_id = 1; uint32 new_resource_version = 2; bytes new_value = 3; - optional bytes additional_parameters = 4; + optional bytes context = 4; } message OnUpdateResponse { - optional bytes additional_parameters = 1; + optional bytes context = 1; } message OnDeleteRequest { string typeinstance_id = 1; - bytes additional_parameters = 2; + bytes context = 2; } message OnDeleteResponse {} @@ -38,7 +38,7 @@ message OnDeleteResponse {} message GetValueRequest { string typeinstance_id = 1; uint32 resource_version = 2; - bytes additional_parameters = 3; + bytes context = 3; } message GetValueResponse { @@ -50,7 +50,7 @@ message GetValueResponse { message GetLockedByRequest { string typeinstance_id = 1; - bytes additional_parameters = 2; + bytes context = 2; } message GetLockedByResponse { @@ -59,7 +59,7 @@ message GetLockedByResponse { message OnLockRequest { string typeinstance_id = 1; - bytes additional_parameters = 2; + bytes context = 2; string locked_by = 3; } @@ -67,7 +67,7 @@ message OnLockResponse {} message OnUnlockRequest { string typeinstance_id = 1; - bytes additional_parameters = 2; + bytes context = 2; } message OnUnlockResponse {} diff --git a/pkg/hub/api/grpc/storage_backend/client_test.go b/pkg/hub/api/grpc/storage_backend/client_test.go new file mode 100644 index 000000000..6e093230d --- /dev/null +++ b/pkg/hub/api/grpc/storage_backend/client_test.go @@ -0,0 +1,189 @@ +package storage_backend_test + +import ( + "context" + "fmt" + "os" + "testing" + + pb "capact.io/capact/pkg/hub/api/grpc/storage_backend" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" +) + +// #nosec G101 +const secretStorageBackendAddr = "GRPC_SECRET_STORAGE_BACKEND_ADDR" + +// This test illustrates how to use gRPC Go client against real gRPC Storage Backend server. +// +// NOTE: Before running this test, make sure that the server is running under the `srvAddr` address +// and the `provider` is enabled on this server. +// +// To run this test, execute: +// GRPC_SECRET_STORAGE_BACKEND_ADDR=":50051" go test ./pkg/hub/api/grpc/storage_backend -v +func TestNewStorageBackendClient(t *testing.T) { + srvAddr := os.Getenv(secretStorageBackendAddr) + if srvAddr == "" { + t.Skipf("skipping storage backend gRPC client test as the env %s is not provided", secretStorageBackendAddr) + } + provider := "dotenv" + + valueBytes := []byte(`{"key": true}`) + typeInstanceID := "id" + + reqContext := []byte(fmt.Sprintf(`{"provider":"%s"}`, provider)) + + conn, err := grpc.Dial(srvAddr, grpc.WithInsecure()) + require.NoError(t, err) + + ctx := context.Background() + client := pb.NewStorageBackendClient(conn) + + // create + t.Logf("Creating TI %q...\n", typeInstanceID) + + _, err = client.OnCreate(ctx, &pb.OnCreateRequest{ + TypeinstanceId: typeInstanceID, + Value: valueBytes, + Context: reqContext, + }) + require.NoError(t, err) + + // get value + + var resourceVersion uint32 = 1 + res, err := client.GetValue(ctx, &pb.GetValueRequest{ + TypeinstanceId: typeInstanceID, + ResourceVersion: resourceVersion, + Context: reqContext, + }) + require.NoError(t, err) + + t.Logf("Getting TI %q: resource version %d: %s\n", typeInstanceID, resourceVersion, string(res.Value)) + assert.Equal(t, valueBytes, res.Value) + + // update + t.Logf("Updating TI %q...\n", typeInstanceID) + + newValueBytes := []byte(`{"key": "updated"}`) + _, err = client.OnUpdate(ctx, &pb.OnUpdateRequest{ + TypeinstanceId: typeInstanceID, + NewResourceVersion: 2, + NewValue: newValueBytes, + Context: reqContext, + }) + require.NoError(t, err) + + // get value + + res, err = client.GetValue(ctx, &pb.GetValueRequest{ + TypeinstanceId: typeInstanceID, + ResourceVersion: resourceVersion, + Context: reqContext, + }) + require.NoError(t, err) + + t.Logf("Getting TI %q: resource version %d: %s\n", typeInstanceID, resourceVersion, string(res.Value)) + assert.Equal(t, valueBytes, res.Value) + + resourceVersion = 2 + res, err = client.GetValue(ctx, &pb.GetValueRequest{ + TypeinstanceId: typeInstanceID, + ResourceVersion: resourceVersion, + Context: reqContext, + }) + require.NoError(t, err) + + t.Logf("Getting TI %q: resource version %d: %s\n", typeInstanceID, resourceVersion, string(res.Value)) + assert.Equal(t, newValueBytes, res.Value) + + // lock + + t.Logf("Locking TI %q...\n", typeInstanceID) + + _, err = client.OnLock(ctx, &pb.OnLockRequest{ + TypeinstanceId: typeInstanceID, + Context: reqContext, + LockedBy: "test/sample", + }) + require.NoError(t, err) + + // get lockedBy + + lockedByRes, err := client.GetLockedBy(ctx, &pb.GetLockedByRequest{ + TypeinstanceId: typeInstanceID, + Context: reqContext, + }) + require.NoError(t, err) + + require.NotNil(t, lockedByRes.LockedBy) + assert.Equal(t, "test/sample", *lockedByRes.LockedBy) + t.Logf("Getting TI %q: locked by %q\n", typeInstanceID, *lockedByRes.LockedBy) + + // unlock + + t.Logf("Unlocking TI %q...\n", typeInstanceID) + + _, err = client.OnUnlock(ctx, &pb.OnUnlockRequest{ + TypeinstanceId: typeInstanceID, + Context: reqContext, + }) + require.NoError(t, err) + + // get lockedBy + + lockedByRes, err = client.GetLockedBy(ctx, &pb.GetLockedByRequest{ + TypeinstanceId: typeInstanceID, + Context: reqContext, + }) + require.NoError(t, err) + + t.Logf("Getting TI %q: locked by: %v\n", typeInstanceID, lockedByRes.LockedBy) + assert.Nil(t, lockedByRes.LockedBy) + + // get value + + resourceVersion = 1 + res, err = client.GetValue(ctx, &pb.GetValueRequest{ + TypeinstanceId: typeInstanceID, + ResourceVersion: resourceVersion, + Context: reqContext, + }) + require.NoError(t, err) + + t.Logf("Getting TI %q: resource version %d: %s\n", typeInstanceID, resourceVersion, string(res.Value)) + assert.Equal(t, valueBytes, res.Value) + + resourceVersion = 2 + res, err = client.GetValue(ctx, &pb.GetValueRequest{ + TypeinstanceId: typeInstanceID, + ResourceVersion: resourceVersion, + Context: reqContext, + }) + require.NoError(t, err) + + t.Logf("Getting TI %q: resource version %d: %s\n", typeInstanceID, resourceVersion, string(res.Value)) + assert.Equal(t, newValueBytes, res.Value) + + // delete + t.Logf("Deleting TI %q...\n", typeInstanceID) + + _, err = client.OnDelete(ctx, &pb.OnDeleteRequest{ + TypeinstanceId: typeInstanceID, + Context: reqContext, + }) + require.NoError(t, err) + + // last get + + resourceVersion = 1 + _, err = client.GetValue(ctx, &pb.GetValueRequest{ + TypeinstanceId: typeInstanceID, + ResourceVersion: resourceVersion, + Context: reqContext, + }) + require.Error(t, err) + assert.EqualError(t, err, "rpc error: code = NotFound desc = TypeInstance \"id\" in revision 1 was not found") + t.Logf("Getting TI %q: resource version %d: error: %v\n", typeInstanceID, resourceVersion, err) +} diff --git a/pkg/hub/api/grpc/storage_backend/example_test.go b/pkg/hub/api/grpc/storage_backend/example_test.go deleted file mode 100644 index 6f198579b..000000000 --- a/pkg/hub/api/grpc/storage_backend/example_test.go +++ /dev/null @@ -1,201 +0,0 @@ -package storage_backend_test - -import ( - "context" - "fmt" - - pb "capact.io/capact/pkg/hub/api/grpc/storage_backend" - "google.golang.org/grpc" - utilrand "k8s.io/apimachinery/pkg/util/rand" -) - -// This example illustrates how to use gRPC Go client against real gRPC Storage Backend server. -// -// NOTE: Before running this example, make sure that the server is running under the `srvAddr` address -// and the `provider` is enabled on this server. -func ExampleNewStorageBackendClient() { - provider := "dotenv" - srvAddr := ":50051" // server address - - valueBytes := []byte(`{"key": true}`) - typeInstanceID := utilrand.String(10) // temp - - reqAdditionalParams := []byte(fmt.Sprintf(`{"provider":"%s"}`, provider)) - - conn, err := grpc.Dial(srvAddr, grpc.WithInsecure()) - if err != nil { - panic(err) - } - - ctx := context.Background() - client := pb.NewStorageBackendClient(conn) - - // create - fmt.Printf("Creating TI %q...\n", typeInstanceID) - - _, err = client.OnCreate(ctx, &pb.OnCreateRequest{ - TypeinstanceId: typeInstanceID, - Value: valueBytes, - AdditionalParameters: reqAdditionalParams, - }) - if err != nil { - panic(err) - } - - // get value - - var resourceVersion uint32 = 1 - res, err := client.GetValue(ctx, &pb.GetValueRequest{ - TypeinstanceId: typeInstanceID, - ResourceVersion: resourceVersion, - AdditionalParameters: reqAdditionalParams, - }) - if err != nil { - panic(err) - } - - fmt.Printf("Getting TI %q: resource version %d: %s\n", typeInstanceID, resourceVersion, string(res.Value)) - - // update - fmt.Printf("Updating TI %q...\n", typeInstanceID) - - newValueBytes := []byte(`{"key": "updated"}`) - _, err = client.OnUpdate(ctx, &pb.OnUpdateRequest{ - TypeinstanceId: typeInstanceID, - NewResourceVersion: 2, - NewValue: newValueBytes, - AdditionalParameters: reqAdditionalParams, - }) - if err != nil { - panic(err) - } - - // get value - - res, err = client.GetValue(ctx, &pb.GetValueRequest{ - TypeinstanceId: typeInstanceID, - ResourceVersion: resourceVersion, - AdditionalParameters: reqAdditionalParams, - }) - if err != nil { - panic(err) - } - - fmt.Printf("Getting TI %q: resource version %d: %s\n", typeInstanceID, resourceVersion, string(res.Value)) - - resourceVersion = 2 - res, err = client.GetValue(ctx, &pb.GetValueRequest{ - TypeinstanceId: typeInstanceID, - ResourceVersion: resourceVersion, - AdditionalParameters: reqAdditionalParams, - }) - if err != nil { - panic(err) - } - - fmt.Printf("Getting TI %q: resource version %d: %s\n", typeInstanceID, resourceVersion, string(res.Value)) - - // lock - - fmt.Printf("Locking TI %q...\n", typeInstanceID) - - _, err = client.OnLock(ctx, &pb.OnLockRequest{ - TypeinstanceId: typeInstanceID, - AdditionalParameters: reqAdditionalParams, - LockedBy: "test/sample", - }) - if err != nil { - panic(err) - } - - // get lockedBy - - lockedByRes, err := client.GetLockedBy(ctx, &pb.GetLockedByRequest{ - TypeinstanceId: typeInstanceID, - AdditionalParameters: reqAdditionalParams, - }) - if err != nil { - panic(err) - } - - if lockedByRes.LockedBy == nil { - panic("lockedBy cannot be nil") - } - - fmt.Printf("Getting TI %q: locked by %q\n", typeInstanceID, *lockedByRes.LockedBy) - - // unlock - - fmt.Printf("Unlocking TI %q...\n", typeInstanceID) - - _, err = client.OnUnlock(ctx, &pb.OnUnlockRequest{ - TypeinstanceId: typeInstanceID, - AdditionalParameters: reqAdditionalParams, - }) - if err != nil { - panic(err) - } - - // get lockedBy - - lockedByRes, err = client.GetLockedBy(ctx, &pb.GetLockedByRequest{ - TypeinstanceId: typeInstanceID, - AdditionalParameters: reqAdditionalParams, - }) - if err != nil { - panic(err) - } - - fmt.Printf("Getting TI %q: locked by: %v\n", typeInstanceID, lockedByRes.LockedBy) - - // get value - - resourceVersion = 1 - res, err = client.GetValue(ctx, &pb.GetValueRequest{ - TypeinstanceId: typeInstanceID, - ResourceVersion: resourceVersion, - AdditionalParameters: reqAdditionalParams, - }) - if err != nil { - panic(err) - } - - fmt.Printf("Getting TI %q: resource version %d: %s\n", typeInstanceID, resourceVersion, string(res.Value)) - - resourceVersion = 2 - res, err = client.GetValue(ctx, &pb.GetValueRequest{ - TypeinstanceId: typeInstanceID, - ResourceVersion: resourceVersion, - AdditionalParameters: reqAdditionalParams, - }) - if err != nil { - panic(err) - } - - fmt.Printf("Getting TI %q: resource version %d: %s\n", typeInstanceID, resourceVersion, string(res.Value)) - - // delete - fmt.Printf("Deleting TI %q...\n", typeInstanceID) - - _, err = client.OnDelete(ctx, &pb.OnDeleteRequest{ - TypeinstanceId: typeInstanceID, - AdditionalParameters: reqAdditionalParams, - }) - if err != nil { - panic(err) - } - - // last get - - resourceVersion = 1 - res, err = client.GetValue(ctx, &pb.GetValueRequest{ - TypeinstanceId: typeInstanceID, - ResourceVersion: resourceVersion, - AdditionalParameters: reqAdditionalParams, - }) - if err != nil { - panic(err) - } - - fmt.Printf("Getting TI %q: resource version %d: is nil: %v\n", typeInstanceID, resourceVersion, res.Value == nil) -} diff --git a/pkg/hub/api/grpc/storage_backend/storage_backend.pb.go b/pkg/hub/api/grpc/storage_backend/storage_backend.pb.go index f3edab4c0..7df258ba8 100644 --- a/pkg/hub/api/grpc/storage_backend/storage_backend.pb.go +++ b/pkg/hub/api/grpc/storage_backend/storage_backend.pb.go @@ -25,9 +25,9 @@ type OnCreateRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TypeinstanceId string `protobuf:"bytes,1,opt,name=typeinstance_id,json=typeinstanceId,proto3" json:"typeinstance_id,omitempty"` - Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` - AdditionalParameters []byte `protobuf:"bytes,3,opt,name=additional_parameters,json=additionalParameters,proto3" json:"additional_parameters,omitempty"` + TypeinstanceId string `protobuf:"bytes,1,opt,name=typeinstance_id,json=typeinstanceId,proto3" json:"typeinstance_id,omitempty"` + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + Context []byte `protobuf:"bytes,3,opt,name=context,proto3" json:"context,omitempty"` } func (x *OnCreateRequest) Reset() { @@ -76,9 +76,9 @@ func (x *OnCreateRequest) GetValue() []byte { return nil } -func (x *OnCreateRequest) GetAdditionalParameters() []byte { +func (x *OnCreateRequest) GetContext() []byte { if x != nil { - return x.AdditionalParameters + return x.Context } return nil } @@ -88,7 +88,7 @@ type OnCreateResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - AdditionalParameters []byte `protobuf:"bytes,1,opt,name=additional_parameters,json=additionalParameters,proto3,oneof" json:"additional_parameters,omitempty"` + Context []byte `protobuf:"bytes,1,opt,name=context,proto3,oneof" json:"context,omitempty"` } func (x *OnCreateResponse) Reset() { @@ -123,9 +123,9 @@ func (*OnCreateResponse) Descriptor() ([]byte, []int) { return file_storage_backend_proto_rawDescGZIP(), []int{1} } -func (x *OnCreateResponse) GetAdditionalParameters() []byte { +func (x *OnCreateResponse) GetContext() []byte { if x != nil { - return x.AdditionalParameters + return x.Context } return nil } @@ -190,10 +190,10 @@ type OnUpdateRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TypeinstanceId string `protobuf:"bytes,1,opt,name=typeinstance_id,json=typeinstanceId,proto3" json:"typeinstance_id,omitempty"` - NewResourceVersion uint32 `protobuf:"varint,2,opt,name=new_resource_version,json=newResourceVersion,proto3" json:"new_resource_version,omitempty"` - NewValue []byte `protobuf:"bytes,3,opt,name=new_value,json=newValue,proto3" json:"new_value,omitempty"` - AdditionalParameters []byte `protobuf:"bytes,4,opt,name=additional_parameters,json=additionalParameters,proto3,oneof" json:"additional_parameters,omitempty"` + TypeinstanceId string `protobuf:"bytes,1,opt,name=typeinstance_id,json=typeinstanceId,proto3" json:"typeinstance_id,omitempty"` + NewResourceVersion uint32 `protobuf:"varint,2,opt,name=new_resource_version,json=newResourceVersion,proto3" json:"new_resource_version,omitempty"` + NewValue []byte `protobuf:"bytes,3,opt,name=new_value,json=newValue,proto3" json:"new_value,omitempty"` + Context []byte `protobuf:"bytes,4,opt,name=context,proto3,oneof" json:"context,omitempty"` } func (x *OnUpdateRequest) Reset() { @@ -249,9 +249,9 @@ func (x *OnUpdateRequest) GetNewValue() []byte { return nil } -func (x *OnUpdateRequest) GetAdditionalParameters() []byte { +func (x *OnUpdateRequest) GetContext() []byte { if x != nil { - return x.AdditionalParameters + return x.Context } return nil } @@ -261,7 +261,7 @@ type OnUpdateResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - AdditionalParameters []byte `protobuf:"bytes,1,opt,name=additional_parameters,json=additionalParameters,proto3,oneof" json:"additional_parameters,omitempty"` + Context []byte `protobuf:"bytes,1,opt,name=context,proto3,oneof" json:"context,omitempty"` } func (x *OnUpdateResponse) Reset() { @@ -296,9 +296,9 @@ func (*OnUpdateResponse) Descriptor() ([]byte, []int) { return file_storage_backend_proto_rawDescGZIP(), []int{4} } -func (x *OnUpdateResponse) GetAdditionalParameters() []byte { +func (x *OnUpdateResponse) GetContext() []byte { if x != nil { - return x.AdditionalParameters + return x.Context } return nil } @@ -308,8 +308,8 @@ type OnDeleteRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TypeinstanceId string `protobuf:"bytes,1,opt,name=typeinstance_id,json=typeinstanceId,proto3" json:"typeinstance_id,omitempty"` - AdditionalParameters []byte `protobuf:"bytes,2,opt,name=additional_parameters,json=additionalParameters,proto3" json:"additional_parameters,omitempty"` + TypeinstanceId string `protobuf:"bytes,1,opt,name=typeinstance_id,json=typeinstanceId,proto3" json:"typeinstance_id,omitempty"` + Context []byte `protobuf:"bytes,2,opt,name=context,proto3" json:"context,omitempty"` } func (x *OnDeleteRequest) Reset() { @@ -351,9 +351,9 @@ func (x *OnDeleteRequest) GetTypeinstanceId() string { return "" } -func (x *OnDeleteRequest) GetAdditionalParameters() []byte { +func (x *OnDeleteRequest) GetContext() []byte { if x != nil { - return x.AdditionalParameters + return x.Context } return nil } @@ -401,9 +401,9 @@ type GetValueRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TypeinstanceId string `protobuf:"bytes,1,opt,name=typeinstance_id,json=typeinstanceId,proto3" json:"typeinstance_id,omitempty"` - ResourceVersion uint32 `protobuf:"varint,2,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"` - AdditionalParameters []byte `protobuf:"bytes,3,opt,name=additional_parameters,json=additionalParameters,proto3" json:"additional_parameters,omitempty"` + TypeinstanceId string `protobuf:"bytes,1,opt,name=typeinstance_id,json=typeinstanceId,proto3" json:"typeinstance_id,omitempty"` + ResourceVersion uint32 `protobuf:"varint,2,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"` + Context []byte `protobuf:"bytes,3,opt,name=context,proto3" json:"context,omitempty"` } func (x *GetValueRequest) Reset() { @@ -452,9 +452,9 @@ func (x *GetValueRequest) GetResourceVersion() uint32 { return 0 } -func (x *GetValueRequest) GetAdditionalParameters() []byte { +func (x *GetValueRequest) GetContext() []byte { if x != nil { - return x.AdditionalParameters + return x.Context } return nil } @@ -511,8 +511,8 @@ type GetLockedByRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TypeinstanceId string `protobuf:"bytes,1,opt,name=typeinstance_id,json=typeinstanceId,proto3" json:"typeinstance_id,omitempty"` - AdditionalParameters []byte `protobuf:"bytes,2,opt,name=additional_parameters,json=additionalParameters,proto3" json:"additional_parameters,omitempty"` + TypeinstanceId string `protobuf:"bytes,1,opt,name=typeinstance_id,json=typeinstanceId,proto3" json:"typeinstance_id,omitempty"` + Context []byte `protobuf:"bytes,2,opt,name=context,proto3" json:"context,omitempty"` } func (x *GetLockedByRequest) Reset() { @@ -554,9 +554,9 @@ func (x *GetLockedByRequest) GetTypeinstanceId() string { return "" } -func (x *GetLockedByRequest) GetAdditionalParameters() []byte { +func (x *GetLockedByRequest) GetContext() []byte { if x != nil { - return x.AdditionalParameters + return x.Context } return nil } @@ -613,9 +613,9 @@ type OnLockRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TypeinstanceId string `protobuf:"bytes,1,opt,name=typeinstance_id,json=typeinstanceId,proto3" json:"typeinstance_id,omitempty"` - AdditionalParameters []byte `protobuf:"bytes,2,opt,name=additional_parameters,json=additionalParameters,proto3" json:"additional_parameters,omitempty"` - LockedBy string `protobuf:"bytes,3,opt,name=locked_by,json=lockedBy,proto3" json:"locked_by,omitempty"` + TypeinstanceId string `protobuf:"bytes,1,opt,name=typeinstance_id,json=typeinstanceId,proto3" json:"typeinstance_id,omitempty"` + Context []byte `protobuf:"bytes,2,opt,name=context,proto3" json:"context,omitempty"` + LockedBy string `protobuf:"bytes,3,opt,name=locked_by,json=lockedBy,proto3" json:"locked_by,omitempty"` } func (x *OnLockRequest) Reset() { @@ -657,9 +657,9 @@ func (x *OnLockRequest) GetTypeinstanceId() string { return "" } -func (x *OnLockRequest) GetAdditionalParameters() []byte { +func (x *OnLockRequest) GetContext() []byte { if x != nil { - return x.AdditionalParameters + return x.Context } return nil } @@ -714,8 +714,8 @@ type OnUnlockRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TypeinstanceId string `protobuf:"bytes,1,opt,name=typeinstance_id,json=typeinstanceId,proto3" json:"typeinstance_id,omitempty"` - AdditionalParameters []byte `protobuf:"bytes,2,opt,name=additional_parameters,json=additionalParameters,proto3" json:"additional_parameters,omitempty"` + TypeinstanceId string `protobuf:"bytes,1,opt,name=typeinstance_id,json=typeinstanceId,proto3" json:"typeinstance_id,omitempty"` + Context []byte `protobuf:"bytes,2,opt,name=context,proto3" json:"context,omitempty"` } func (x *OnUnlockRequest) Reset() { @@ -757,9 +757,9 @@ func (x *OnUnlockRequest) GetTypeinstanceId() string { return "" } -func (x *OnUnlockRequest) GetAdditionalParameters() []byte { +func (x *OnUnlockRequest) GetContext() []byte { if x != nil { - return x.AdditionalParameters + return x.Context } return nil } @@ -807,99 +807,81 @@ var File_storage_backend_proto protoreflect.FileDescriptor var file_storage_backend_proto_rawDesc = []byte{ 0x0a, 0x15, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, - 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x22, 0x85, 0x01, 0x0a, 0x0f, 0x4f, 0x6e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, + 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x22, 0x6a, 0x0a, 0x0f, 0x4f, 0x6e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x74, + 0x79, 0x70, 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x79, 0x70, 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x22, 0x3d, 0x0a, 0x10, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x22, 0x5e, 0x0a, 0x1b, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x22, 0xb4, 0x01, 0x0a, 0x0f, 0x4f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x79, 0x70, 0x65, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0e, 0x74, 0x79, 0x70, 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, + 0x12, 0x30, 0x0a, 0x14, 0x6e, 0x65, 0x77, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, + 0x6e, 0x65, 0x77, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, + 0x1d, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, + 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0a, + 0x0a, 0x08, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x3d, 0x0a, 0x10, 0x4f, 0x6e, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, + 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, + 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, + 0x08, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x54, 0x0a, 0x0f, 0x4f, 0x6e, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x79, 0x70, 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x79, 0x70, 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x33, 0x0a, 0x15, 0x61, - 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, - 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, - 0x22, 0x66, 0x0a, 0x10, 0x4f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, - 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x88, 0x01, 0x01, 0x42, 0x18, - 0x0a, 0x16, 0x5f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x61, - 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0x5e, 0x0a, 0x1b, 0x54, 0x79, 0x70, 0x65, - 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xdd, 0x01, 0x0a, 0x0f, 0x4f, 0x6e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, - 0x74, 0x79, 0x70, 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x79, 0x70, 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x6e, 0x65, 0x77, 0x5f, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x12, 0x6e, 0x65, 0x77, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x12, 0x38, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, - 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x88, 0x01, 0x01, 0x42, 0x18, - 0x0a, 0x16, 0x5f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x61, - 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0x66, 0x0a, 0x10, 0x4f, 0x6e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x15, - 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, - 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x14, 0x61, - 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x73, 0x88, 0x01, 0x01, 0x42, 0x18, 0x0a, 0x16, 0x5f, 0x61, 0x64, 0x64, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, - 0x22, 0x6f, 0x0a, 0x0f, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, + 0x12, 0x0a, 0x10, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x7f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x79, 0x70, 0x65, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x74, 0x79, 0x70, 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, + 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x22, 0x37, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x19, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x57, 0x0a, + 0x12, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x42, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x79, 0x70, 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x79, + 0x70, 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x45, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x63, + 0x6b, 0x65, 0x64, 0x42, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, + 0x09, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x00, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x42, 0x79, 0x88, 0x01, 0x01, 0x42, + 0x0c, 0x0a, 0x0a, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x22, 0x6f, 0x0a, + 0x0d, 0x4f, 0x6e, 0x4c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, + 0x0a, 0x0f, 0x74, 0x79, 0x70, 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x79, 0x70, 0x65, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x42, 0x79, 0x22, 0x10, + 0x0a, 0x0e, 0x4f, 0x6e, 0x4c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x54, 0x0a, 0x0f, 0x4f, 0x6e, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x79, 0x70, 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x79, - 0x70, 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x33, 0x0a, 0x15, - 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, - 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x61, 0x64, 0x64, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x73, 0x22, 0x12, 0x0a, 0x10, 0x4f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x9a, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x79, 0x70, - 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0e, 0x74, 0x79, 0x70, 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x49, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, - 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x72, 0x61, - 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x61, 0x64, - 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, - 0x72, 0x73, 0x22, 0x37, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x19, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x88, 0x01, - 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x72, 0x0a, 0x12, 0x47, - 0x65, 0x74, 0x4c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x42, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x79, 0x70, 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x79, 0x70, 0x65, - 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x33, 0x0a, 0x15, 0x61, 0x64, - 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, - 0x45, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x42, 0x79, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, - 0x5f, 0x62, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x6c, 0x6f, 0x63, - 0x6b, 0x65, 0x64, 0x42, 0x79, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6c, 0x6f, 0x63, - 0x6b, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x22, 0x8a, 0x01, 0x0a, 0x0d, 0x4f, 0x6e, 0x4c, 0x6f, 0x63, - 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x79, 0x70, 0x65, - 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0e, 0x74, 0x79, 0x70, 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, - 0x64, 0x12, 0x33, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, - 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, - 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x65, - 0x64, 0x42, 0x79, 0x22, 0x10, 0x0a, 0x0e, 0x4f, 0x6e, 0x4c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6f, 0x0a, 0x0f, 0x4f, 0x6e, 0x55, 0x6e, 0x6c, 0x6f, 0x63, - 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x79, 0x70, 0x65, - 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0e, 0x74, 0x79, 0x70, 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, - 0x64, 0x12, 0x33, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, - 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0x12, 0x0a, 0x10, 0x4f, 0x6e, 0x55, 0x6e, 0x6c, 0x6f, + 0x70, 0x65, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x12, 0x0a, 0x10, 0x4f, 0x6e, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xca, 0x04, 0x0a, 0x0e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x4f, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x20, 0x2e, 0x73, 0x74, 0x6f, 0x72,