Skip to content

Commit

Permalink
add azure storage tables keyvalue implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
tjholm committed Jan 23, 2024
1 parent f0c735f commit 3226fd0
Show file tree
Hide file tree
Showing 5 changed files with 292 additions and 8 deletions.
2 changes: 2 additions & 0 deletions cloud/azure/runtime/env/variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ var MONGODB_DIRECT = env.GetEnv("MONGODB_DIRECT", "true")

var KVAULT_NAME = env.GetEnv("KVAULT_NAME", "")

var AZURE_STORAGE_ACCOUNT_NAME = env.GetEnv("AZURE_STORAGE_ACCOUNT_NAME", "")

var AZURE_STORAGE_BLOB_ENDPOINT = env.GetEnv("AZURE_STORAGE_ACCOUNT_BLOB_ENDPOINT", "")
var AZURE_STORAGE_QUEUE_ENDPOINT = env.GetEnv("AZURE_STORAGE_ACCOUNT_QUEUE_ENDPOINT", "")

Expand Down
215 changes: 215 additions & 0 deletions cloud/azure/runtime/keyvalue/keyvalue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
// Copyright 2021 Nitric Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package keyvalue

import (
"context"
"encoding/json"
"fmt"

"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/data/aztables"
"github.com/nitrictech/nitric/cloud/aws/runtime/resource"
"github.com/nitrictech/nitric/cloud/azure/runtime/env"
document "github.com/nitrictech/nitric/core/pkg/decorators/keyvalue"
grpc_errors "github.com/nitrictech/nitric/core/pkg/grpc/errors"
keyvaluepb "github.com/nitrictech/nitric/core/pkg/proto/keyvalue/v1"
"github.com/pkg/errors"
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/types/known/structpb"
)

// DynamoKeyValueService - an AWS DynamoDB implementation of the Nitric Document Service
type AzureStorageTableKeyValueService struct {
clientFactory AzureStorageClientFactory
}

var _ keyvaluepb.KeyValueServer = &AzureStorageTableKeyValueService{}

type AztableEntity struct {
aztables.Entity

Content *structpb.Struct
}

// Get a document from the DynamoDB table
func (s *AzureStorageTableKeyValueService) Get(ctx context.Context, req *keyvaluepb.KeyValueGetRequest) (*keyvaluepb.KeyValueGetResponse, error) {
newErr := grpc_errors.ErrorsWithScope("AzureStorageTableKeyValueService.Get")
client, err := s.clientFactory(req.Key.Store)

if err != nil {
return nil, newErr(
codes.Internal,
"Unable to create client",
err,
)
}

err = document.ValidateKey(req.Key)
if err != nil {
return nil, newErr(
codes.InvalidArgument,
"Invalid key",
err,
)
}

response, err := client.GetEntity(ctx, req.Key.Store, req.Key.Key, nil)
if err != nil {
return nil, newErr(
codes.InvalidArgument,
"failed to call aztables:GetEntity",
err,
)
}

var entity AztableEntity
err = json.Unmarshal(response.Value, &entity)
if err != nil {
return nil, newErr(
codes.Internal,
"Unable to convert value to pb struct",
err,
)
}

return &keyvaluepb.KeyValueGetResponse{
Value: &keyvaluepb.Value{
Key: req.Key,
Content: entity.Content,
},
}, nil
}

// Set a document in the DynamoDB table
func (s *AzureStorageTableKeyValueService) Set(ctx context.Context, req *keyvaluepb.KeyValueSetRequest) (*keyvaluepb.KeyValueSetResponse, error) {
newErr := grpc_errors.ErrorsWithScope("AzureStorageTableKeyValueService.Set")
client, err := s.clientFactory(req.Key.Store)

if err != nil {
return nil, newErr(
codes.Internal,
"Unable to create client",
err,
)
}

if err := document.ValidateKey(req.Key); err != nil {
return nil, newErr(
codes.InvalidArgument,
"invalid key",
err,
)
}

entity := AztableEntity{
Entity: aztables.Entity{
PartitionKey: req.Key.Store,
RowKey: req.Key.Key,
},
Content: req.Content,
}

entityJson, err := json.Marshal(entity)
if err != nil {
return nil, newErr(
codes.Internal,
"Unable to struct to json",
err,
)
}

_, err = client.UpsertEntity(ctx, entityJson, nil)
if err != nil {
return nil, newErr(
codes.Internal,
"Unable to call aztables.UpsertEntity",
err,
)
}

return &keyvaluepb.KeyValueSetResponse{}, nil
}

// Delete a document from the DynamoDB table
func (s *AzureStorageTableKeyValueService) Delete(ctx context.Context, req *keyvaluepb.KeyValueDeleteRequest) (*keyvaluepb.KeyValueDeleteResponse, error) {
newErr := grpc_errors.ErrorsWithScope("AzureStorageTableKeyValueService.Delete")
client, err := s.clientFactory(req.Key.Store)

if err != nil {
return nil, newErr(
codes.Internal,
"Unable to create client",
err,
)
}

if err := document.ValidateKey(req.Key); err != nil {
return nil, newErr(
codes.InvalidArgument,
"invalid key",
err,
)
}

_, err = client.DeleteEntity(ctx, req.Key.Store, req.Key.Key, nil)
if err != nil {
return nil, newErr(
codes.Internal,
"failed to call aztables.DeleteEntity",
err,
)
}

return &keyvaluepb.KeyValueDeleteResponse{}, nil
}

type AzureStorageClientFactory func(tableName string) (*aztables.Client, error)

func newStorageTablesClientFactory(creds *azidentity.DefaultAzureCredential, storageAccountName string) AzureStorageClientFactory {
return func(tableName string) (*aztables.Client, error) {
serviceURL := fmt.Sprintf("https://%s.table.core.windows.net/%s", storageAccountName, tableName)
return aztables.NewClient(serviceURL, creds, nil)
}
}

// New creates a new AWS DynamoDB implementation of a DocumentServiceServer
func New(provider resource.AwsResourceProvider) (*AzureStorageTableKeyValueService, error) {
storageAccountName := env.AZURE_STORAGE_ACCOUNT_NAME.String()
if storageAccountName == "" {
return nil, fmt.Errorf("failed to determine Azure Storage Account name, environment variable not set")
}

cred, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
return nil, errors.Wrap(err, "failed to locate default azure credential")
}

return &AzureStorageTableKeyValueService{
clientFactory: newStorageTablesClientFactory(cred, storageAccountName),
}, nil

}

// NewWithClient creates a DocumentServiceServer with an given DynamoDB client instance.
//
// Primarily used for testing
func NewWithClient(provider resource.AwsResourceProvider, clientFactory AzureStorageClientFactory) (*AzureStorageTableKeyValueService, error) {
return &AzureStorageTableKeyValueService{
// storageAccountName: storageAccountName,
// defaultCredential: cred,
clientFactory: clientFactory,
}, nil
}
20 changes: 14 additions & 6 deletions cloud/gcp/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ require (
github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect
github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.8.0 // indirect
golang.org/x/term v0.7.0 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/term v0.15.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
lukechampine.com/frand v1.4.2 // indirect
sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 // indirect
Expand All @@ -97,7 +97,12 @@ require (
github.com/Abirdcfly/dupword v0.0.11 // indirect
github.com/Antonboom/errname v0.1.9 // indirect
github.com/Antonboom/nilnil v0.1.3 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.1.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 // indirect
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/Djarvur/go-err113 v0.1.0 // indirect
github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 // indirect
Expand Down Expand Up @@ -155,6 +160,7 @@ require (
github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gofrs/flock v0.8.1 // indirect
github.com/golang-jwt/jwt/v5 v5.2.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 // indirect
Expand All @@ -168,7 +174,7 @@ require (
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/licenseclassifier v0.0.0-20201113175434-78a70215ca36 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
github.com/gookit/color v1.5.2 // indirect
github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28 // indirect
Expand All @@ -194,6 +200,7 @@ require (
github.com/klauspost/compress v1.16.3 // indirect
github.com/kulti/thelper v0.6.3 // indirect
github.com/kunwardeep/paralleltest v1.0.6 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/kyoh86/exportloopref v0.1.11 // indirect
github.com/ldez/gomoddirectives v0.2.3 // indirect
github.com/ldez/tagliatelle v0.4.0 // indirect
Expand Down Expand Up @@ -231,6 +238,7 @@ require (
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/polyfloyd/go-errorlint v1.4.0 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
Expand Down Expand Up @@ -300,10 +308,10 @@ require (
golang.org/x/exp v0.0.0-20221114191408-850992195362 // indirect
golang.org/x/exp/typeparams v0.0.0-20230224173230-c95f2b4c22f2 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/net v0.9.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.8.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
Expand Down
Loading

0 comments on commit 3226fd0

Please sign in to comment.