Merge branch 'master' of https://github.com/dapr/components-contrib into postgres-ttl
This commit is contained in:
commit
b782b520aa
|
@ -16,6 +16,8 @@ for attempt in `seq $MAX_ATTEMPTS`; do
|
|||
if vault status &&
|
||||
vault kv put secret/dapr/conftestsecret conftestsecret=abcd &&
|
||||
vault kv put secret/dapr/secondsecret secondsecret=efgh &&
|
||||
vault kv put secret/secretWithNoPrefix noPrefixKey=noProblem &&
|
||||
vault kv put secret/alternativePrefix/secretUnderAlternativePrefix altPrefixKey=altPrefixValue &&
|
||||
vault kv put secret/dapr/multiplekeyvaluessecret first=1 second=2 third=3;
|
||||
then
|
||||
echo ✅ secrets set;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
version: '3.9'
|
||||
|
||||
# Use a YAML reference to define VAULT_TOKEN and DOCKER_IMAGE only once
|
||||
x-common-vaues:
|
||||
x-common-values:
|
||||
# This should match tests/config/secrestore/hashicorp/vault/hashicorp-vault.yaml
|
||||
# This should match .github/infrastructure/conformance/hashicorp/vault_token_file.txt
|
||||
vault_token: &VAULT_TOKEN "vault-dev-root-token-id"
|
||||
# Reuse the same docker image to save on resources and because the base vault image
|
||||
# has everything we need for seeding the initial key values too.
|
||||
|
|
|
@ -53,6 +53,7 @@ jobs:
|
|||
- pubsub.mqtt
|
||||
- state.mongodb
|
||||
- state.redis
|
||||
- state.cockroachdb
|
||||
- state.postgresql
|
||||
- state.cassandra
|
||||
- state.memcached
|
||||
|
|
|
@ -20,18 +20,18 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-storage-blob-go/azblob"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blockblob"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/dapr/components-contrib/bindings"
|
||||
azauth "github.com/dapr/components-contrib/internal/authentication/azure"
|
||||
mdutils "github.com/dapr/components-contrib/metadata"
|
||||
storageinternal "github.com/dapr/components-contrib/internal/component/azure/blobstorage"
|
||||
"github.com/dapr/kit/logger"
|
||||
"github.com/dapr/kit/ptr"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -49,41 +49,23 @@ const (
|
|||
// Defines the delete snapshots option for the delete operation.
|
||||
// See: https://docs.microsoft.com/en-us/rest/api/storageservices/delete-blob#request-headers
|
||||
metadataKeyDeleteSnapshots = "deleteSnapshots"
|
||||
// HTTP headers to be associated with the blob.
|
||||
// See: https://docs.microsoft.com/en-us/rest/api/storageservices/put-blob#request-headers-all-blob-types
|
||||
metadataKeyContentType = "contentType"
|
||||
metadataKeyContentMD5 = "contentMD5"
|
||||
metadataKeyContentEncoding = "contentEncoding"
|
||||
metadataKeyContentLanguage = "contentLanguage"
|
||||
metadataKeyContentDisposition = "contentDisposition"
|
||||
metadataKeyCacheControl = "cacheControl"
|
||||
// Specifies the maximum number of HTTP GET requests that will be made while reading from a RetryReader. A value
|
||||
// of zero means that no additional HTTP GET requests will be made.
|
||||
defaultGetBlobRetryCount = 10
|
||||
// Specifies the maximum number of blobs to return, including all BlobPrefix elements. If the request does not
|
||||
// specify maxresults the server will return up to 5,000 items.
|
||||
// See: https://docs.microsoft.com/en-us/rest/api/storageservices/list-blobs#uri-parameters
|
||||
maxResults = 5000
|
||||
maxResults int32 = 5000
|
||||
endpointKey = "endpoint"
|
||||
)
|
||||
|
||||
var ErrMissingBlobName = errors.New("blobName is a required attribute")
|
||||
|
||||
// AzureBlobStorage allows saving blobs to an Azure Blob Storage account.
|
||||
type AzureBlobStorage struct {
|
||||
metadata *blobStorageMetadata
|
||||
containerURL azblob.ContainerURL
|
||||
metadata *storageinternal.BlobStorageMetadata
|
||||
containerClient *container.Client
|
||||
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
type blobStorageMetadata struct {
|
||||
AccountName string
|
||||
Container string
|
||||
GetBlobRetryCount int
|
||||
DecodeBase64 bool
|
||||
PublicAccessLevel azblob.PublicAccessType
|
||||
}
|
||||
|
||||
type createResponse struct {
|
||||
BlobURL string `json:"blobURL"`
|
||||
BlobName string `json:"blobName"`
|
||||
|
@ -111,90 +93,14 @@ func NewAzureBlobStorage(logger logger.Logger) bindings.OutputBinding {
|
|||
|
||||
// Init performs metadata parsing.
|
||||
func (a *AzureBlobStorage) Init(metadata bindings.Metadata) error {
|
||||
m, err := a.parseMetadata(metadata)
|
||||
var err error
|
||||
a.containerClient, a.metadata, err = storageinternal.CreateContainerStorageClient(a.logger, metadata.Properties)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.metadata = m
|
||||
|
||||
credential, env, err := azauth.GetAzureStorageBlobCredentials(a.logger, m.AccountName, metadata.Properties)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid credentials with error: %s", err.Error())
|
||||
}
|
||||
|
||||
userAgent := "dapr-" + logger.DaprVersion
|
||||
options := azblob.PipelineOptions{
|
||||
Telemetry: azblob.TelemetryOptions{Value: userAgent},
|
||||
}
|
||||
p := azblob.NewPipeline(credential, options)
|
||||
|
||||
var containerURL azblob.ContainerURL
|
||||
customEndpoint, ok := mdutils.GetMetadataProperty(metadata.Properties, azauth.StorageEndpointKeys...)
|
||||
if ok && customEndpoint != "" {
|
||||
URL, parseErr := url.Parse(fmt.Sprintf("%s/%s/%s", customEndpoint, m.AccountName, m.Container))
|
||||
if parseErr != nil {
|
||||
return parseErr
|
||||
}
|
||||
containerURL = azblob.NewContainerURL(*URL, p)
|
||||
} else {
|
||||
URL, _ := url.Parse(fmt.Sprintf("https://%s.blob.%s/%s", m.AccountName, env.StorageEndpointSuffix, m.Container))
|
||||
containerURL = azblob.NewContainerURL(*URL, p)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
_, err = containerURL.Create(ctx, azblob.Metadata{}, m.PublicAccessLevel)
|
||||
cancel()
|
||||
// Don't return error, container might already exist
|
||||
a.logger.Debugf("error creating container: %w", err)
|
||||
a.containerURL = containerURL
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AzureBlobStorage) parseMetadata(metadata bindings.Metadata) (*blobStorageMetadata, error) {
|
||||
var m blobStorageMetadata
|
||||
if val, ok := mdutils.GetMetadataProperty(metadata.Properties, azauth.StorageAccountNameKeys...); ok && val != "" {
|
||||
m.AccountName = val
|
||||
} else {
|
||||
return nil, fmt.Errorf("missing or empty %s field from metadata", azauth.StorageAccountNameKeys[0])
|
||||
}
|
||||
|
||||
if val, ok := mdutils.GetMetadataProperty(metadata.Properties, azauth.StorageContainerNameKeys...); ok && val != "" {
|
||||
m.Container = val
|
||||
} else {
|
||||
return nil, fmt.Errorf("missing or empty %s field from metadata", azauth.StorageContainerNameKeys[0])
|
||||
}
|
||||
|
||||
m.GetBlobRetryCount = defaultGetBlobRetryCount
|
||||
if val, ok := metadata.Properties["getBlobRetryCount"]; ok {
|
||||
n, err := strconv.Atoi(val)
|
||||
if err != nil || n == 0 {
|
||||
return nil, fmt.Errorf("invalid getBlobRetryCount field from metadata")
|
||||
}
|
||||
m.GetBlobRetryCount = n
|
||||
}
|
||||
|
||||
m.DecodeBase64 = false
|
||||
if val, ok := metadata.Properties["decodeBase64"]; ok {
|
||||
n, err := strconv.ParseBool(val)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid decodeBase64 field from metadata")
|
||||
}
|
||||
m.DecodeBase64 = n
|
||||
}
|
||||
|
||||
m.PublicAccessLevel = azblob.PublicAccessType(strings.ToLower(metadata.Properties["publicAccessLevel"]))
|
||||
// per the Dapr documentation "none" is a valid value
|
||||
if m.PublicAccessLevel == "none" {
|
||||
m.PublicAccessLevel = ""
|
||||
}
|
||||
if !a.isValidPublicAccessType(m.PublicAccessLevel) {
|
||||
return nil, fmt.Errorf("invalid public access level: %s; allowed: %s", m.PublicAccessLevel, azblob.PossiblePublicAccessTypeValues())
|
||||
}
|
||||
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func (a *AzureBlobStorage) Operations() []bindings.OperationKind {
|
||||
return []bindings.OperationKind{
|
||||
bindings.CreateOperation,
|
||||
|
@ -205,44 +111,21 @@ func (a *AzureBlobStorage) Operations() []bindings.OperationKind {
|
|||
}
|
||||
|
||||
func (a *AzureBlobStorage) create(ctx context.Context, req *bindings.InvokeRequest) (*bindings.InvokeResponse, error) {
|
||||
var blobHTTPHeaders azblob.BlobHTTPHeaders
|
||||
var blobURL azblob.BlockBlobURL
|
||||
var blobName string
|
||||
if val, ok := req.Metadata[metadataKeyBlobName]; ok && val != "" {
|
||||
blobName = val
|
||||
delete(req.Metadata, metadataKeyBlobName)
|
||||
} else {
|
||||
blobName = uuid.New().String()
|
||||
}
|
||||
blobURL = a.getBlobURL(blobName)
|
||||
|
||||
if val, ok := req.Metadata[metadataKeyContentType]; ok && val != "" {
|
||||
blobHTTPHeaders.ContentType = val
|
||||
delete(req.Metadata, metadataKeyContentType)
|
||||
}
|
||||
if val, ok := req.Metadata[metadataKeyContentMD5]; ok && val != "" {
|
||||
sDec, err := b64.StdEncoding.DecodeString(val)
|
||||
if err != nil || len(sDec) != 16 {
|
||||
return nil, fmt.Errorf("the MD5 value specified in Content MD5 is invalid, MD5 value must be 128 bits and base64 encoded")
|
||||
id, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blobHTTPHeaders.ContentMD5 = sDec
|
||||
delete(req.Metadata, metadataKeyContentMD5)
|
||||
blobName = id.String()
|
||||
}
|
||||
if val, ok := req.Metadata[metadataKeyContentEncoding]; ok && val != "" {
|
||||
blobHTTPHeaders.ContentEncoding = val
|
||||
delete(req.Metadata, metadataKeyContentEncoding)
|
||||
}
|
||||
if val, ok := req.Metadata[metadataKeyContentLanguage]; ok && val != "" {
|
||||
blobHTTPHeaders.ContentLanguage = val
|
||||
delete(req.Metadata, metadataKeyContentLanguage)
|
||||
}
|
||||
if val, ok := req.Metadata[metadataKeyContentDisposition]; ok && val != "" {
|
||||
blobHTTPHeaders.ContentDisposition = val
|
||||
delete(req.Metadata, metadataKeyContentDisposition)
|
||||
}
|
||||
if val, ok := req.Metadata[metadataKeyCacheControl]; ok && val != "" {
|
||||
blobHTTPHeaders.CacheControl = val
|
||||
delete(req.Metadata, metadataKeyCacheControl)
|
||||
|
||||
blobHTTPHeaders, err := storageinternal.CreateBlobHTTPHeadersFromRequest(req.Metadata, nil, a.logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d, err := strconv.Unquote(string(req.Data))
|
||||
|
@ -258,17 +141,21 @@ func (a *AzureBlobStorage) create(ctx context.Context, req *bindings.InvokeReque
|
|||
req.Data = decoded
|
||||
}
|
||||
|
||||
_, err = azblob.UploadBufferToBlockBlob(ctx, req.Data, blobURL, azblob.UploadToBlockBlobOptions{
|
||||
Parallelism: 16,
|
||||
Metadata: a.sanitizeMetadata(req.Metadata),
|
||||
BlobHTTPHeaders: blobHTTPHeaders,
|
||||
})
|
||||
uploadOptions := azblob.UploadBufferOptions{
|
||||
Metadata: storageinternal.SanitizeMetadata(a.logger, req.Metadata),
|
||||
HTTPHeaders: &blobHTTPHeaders,
|
||||
TransactionalContentMD5: &blobHTTPHeaders.BlobContentMD5,
|
||||
}
|
||||
|
||||
blockBlobClient := a.containerClient.NewBlockBlobClient(blobName)
|
||||
_, err = blockBlobClient.UploadBuffer(ctx, req.Data, &uploadOptions)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error uploading az blob: %w", err)
|
||||
}
|
||||
|
||||
resp := createResponse{
|
||||
BlobURL: blobURL.String(),
|
||||
BlobURL: blockBlobClient.URL(),
|
||||
}
|
||||
b, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
|
@ -286,23 +173,26 @@ func (a *AzureBlobStorage) create(ctx context.Context, req *bindings.InvokeReque
|
|||
}
|
||||
|
||||
func (a *AzureBlobStorage) get(ctx context.Context, req *bindings.InvokeRequest) (*bindings.InvokeResponse, error) {
|
||||
var blobURL azblob.BlockBlobURL
|
||||
var blockBlobClient *blockblob.Client
|
||||
if val, ok := req.Metadata[metadataKeyBlobName]; ok && val != "" {
|
||||
blobURL = a.getBlobURL(val)
|
||||
blockBlobClient = a.containerClient.NewBlockBlobClient(val)
|
||||
} else {
|
||||
return nil, ErrMissingBlobName
|
||||
}
|
||||
|
||||
resp, err := blobURL.Download(ctx, 0, azblob.CountToEnd, azblob.BlobAccessConditions{}, false)
|
||||
downloadOptions := azblob.DownloadStreamOptions{
|
||||
AccessConditions: &blob.AccessConditions{},
|
||||
}
|
||||
|
||||
blobDownloadResponse, err := blockBlobClient.DownloadStream(ctx, &downloadOptions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error downloading az blob: %w", err)
|
||||
}
|
||||
|
||||
bodyStream := resp.Body(azblob.RetryReaderOptions{MaxRetryRequests: a.metadata.GetBlobRetryCount})
|
||||
|
||||
data, err := io.ReadAll(bodyStream)
|
||||
reader := blobDownloadResponse.Body
|
||||
defer reader.Close()
|
||||
blobData, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading az blob body: %w", err)
|
||||
return nil, fmt.Errorf("error reading az blob: %w", err)
|
||||
}
|
||||
|
||||
var metadata map[string]string
|
||||
|
@ -311,45 +201,54 @@ func (a *AzureBlobStorage) get(ctx context.Context, req *bindings.InvokeRequest)
|
|||
return nil, fmt.Errorf("error parsing metadata: %w", err)
|
||||
}
|
||||
|
||||
getPropertiesOptions := blob.GetPropertiesOptions{
|
||||
AccessConditions: &blob.AccessConditions{},
|
||||
}
|
||||
|
||||
if fetchMetadata {
|
||||
props, err := blobURL.GetProperties(ctx, azblob.BlobAccessConditions{})
|
||||
props, err := blockBlobClient.GetProperties(ctx, &getPropertiesOptions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading blob metadata: %w", err)
|
||||
}
|
||||
|
||||
metadata = props.NewMetadata()
|
||||
metadata = props.Metadata
|
||||
}
|
||||
|
||||
return &bindings.InvokeResponse{
|
||||
Data: data,
|
||||
Data: blobData,
|
||||
Metadata: metadata,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *AzureBlobStorage) delete(ctx context.Context, req *bindings.InvokeRequest) (*bindings.InvokeResponse, error) {
|
||||
var blobURL azblob.BlockBlobURL
|
||||
if val, ok := req.Metadata[metadataKeyBlobName]; ok && val != "" {
|
||||
blobURL = a.getBlobURL(val)
|
||||
} else {
|
||||
var blockBlobClient *blockblob.Client
|
||||
val, ok := req.Metadata[metadataKeyBlobName]
|
||||
if !ok || val == "" {
|
||||
return nil, ErrMissingBlobName
|
||||
}
|
||||
|
||||
deleteSnapshotsOptions := azblob.DeleteSnapshotsOptionNone
|
||||
if val, ok := req.Metadata[metadataKeyDeleteSnapshots]; ok && val != "" {
|
||||
deleteSnapshotsOptions = azblob.DeleteSnapshotsOptionType(val)
|
||||
var deleteSnapshotsOptions blob.DeleteSnapshotsOptionType
|
||||
if deleteSnapShotOption, ok := req.Metadata[metadataKeyDeleteSnapshots]; ok && val != "" {
|
||||
deleteSnapshotsOptions = azblob.DeleteSnapshotsOptionType(deleteSnapShotOption)
|
||||
if !a.isValidDeleteSnapshotsOptionType(deleteSnapshotsOptions) {
|
||||
return nil, fmt.Errorf("invalid delete snapshot option type: %s; allowed: %s",
|
||||
deleteSnapshotsOptions, azblob.PossibleDeleteSnapshotsOptionTypeValues())
|
||||
}
|
||||
}
|
||||
|
||||
_, err := blobURL.Delete(ctx, deleteSnapshotsOptions, azblob.BlobAccessConditions{})
|
||||
deleteOptions := blob.DeleteOptions{
|
||||
DeleteSnapshots: &deleteSnapshotsOptions,
|
||||
AccessConditions: &blob.AccessConditions{},
|
||||
}
|
||||
|
||||
blockBlobClient = a.containerClient.NewBlockBlobClient(val)
|
||||
_, err := blockBlobClient.Delete(ctx, &deleteOptions)
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (a *AzureBlobStorage) list(ctx context.Context, req *bindings.InvokeRequest) (*bindings.InvokeResponse, error) {
|
||||
options := azblob.ListBlobsSegmentOptions{}
|
||||
options := container.ListBlobsFlatOptions{}
|
||||
|
||||
hasPayload := false
|
||||
var payload listPayload
|
||||
|
@ -360,50 +259,52 @@ func (a *AzureBlobStorage) list(ctx context.Context, req *bindings.InvokeRequest
|
|||
}
|
||||
hasPayload = true
|
||||
}
|
||||
|
||||
if hasPayload {
|
||||
options.Details.Copy = payload.Include.Copy
|
||||
options.Details.Metadata = payload.Include.Metadata
|
||||
options.Details.Snapshots = payload.Include.Snapshots
|
||||
options.Details.UncommittedBlobs = payload.Include.UncommittedBlobs
|
||||
options.Details.Deleted = payload.Include.Deleted
|
||||
options.Include.Copy = payload.Include.Copy
|
||||
options.Include.Metadata = payload.Include.Metadata
|
||||
options.Include.Snapshots = payload.Include.Snapshots
|
||||
options.Include.UncommittedBlobs = payload.Include.UncommittedBlobs
|
||||
options.Include.Deleted = payload.Include.Deleted
|
||||
}
|
||||
|
||||
if hasPayload && payload.MaxResults != int32(0) {
|
||||
options.MaxResults = payload.MaxResults
|
||||
if hasPayload && payload.MaxResults > 0 {
|
||||
options.MaxResults = &payload.MaxResults
|
||||
} else {
|
||||
options.MaxResults = maxResults
|
||||
options.MaxResults = ptr.Of(maxResults) // cannot get address of constant directly
|
||||
}
|
||||
|
||||
if hasPayload && payload.Prefix != "" {
|
||||
options.Prefix = payload.Prefix
|
||||
options.Prefix = &payload.Prefix
|
||||
}
|
||||
|
||||
var initialMarker azblob.Marker
|
||||
var initialMarker string
|
||||
if hasPayload && payload.Marker != "" {
|
||||
initialMarker = azblob.Marker{Val: &payload.Marker}
|
||||
initialMarker = payload.Marker
|
||||
} else {
|
||||
initialMarker = azblob.Marker{}
|
||||
initialMarker = ""
|
||||
}
|
||||
options.Marker = &initialMarker
|
||||
|
||||
var blobs []azblob.BlobItem
|
||||
metadata := map[string]string{}
|
||||
for currentMaker := initialMarker; currentMaker.NotDone(); {
|
||||
var listBlob *azblob.ListBlobsFlatSegmentResponse
|
||||
listBlob, err := a.containerURL.ListBlobsFlatSegment(ctx, currentMaker, options)
|
||||
blobs := []*container.BlobItem{}
|
||||
pager := a.containerClient.NewListBlobsFlatPager(&options)
|
||||
|
||||
for pager.More() {
|
||||
resp, err := pager.NextPage(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing blobs: %w", err)
|
||||
}
|
||||
|
||||
blobs = append(blobs, listBlob.Segment.BlobItems...)
|
||||
|
||||
blobs = append(blobs, resp.Segment.BlobItems...)
|
||||
numBlobs := len(blobs)
|
||||
currentMaker = listBlob.NextMarker
|
||||
metadata[metadataKeyMarker] = *currentMaker.Val
|
||||
metadata[metadataKeyNumber] = strconv.FormatInt(int64(numBlobs), 10)
|
||||
metadata[metadataKeyMarker] = ""
|
||||
if resp.Marker != nil {
|
||||
metadata[metadataKeyMarker] = *resp.Marker
|
||||
}
|
||||
|
||||
if options.MaxResults-maxResults > 0 {
|
||||
options.MaxResults -= maxResults
|
||||
if *options.MaxResults-maxResults > 0 {
|
||||
*options.MaxResults -= maxResults
|
||||
} else {
|
||||
break
|
||||
}
|
||||
|
@ -435,23 +336,6 @@ func (a *AzureBlobStorage) Invoke(ctx context.Context, req *bindings.InvokeReque
|
|||
}
|
||||
}
|
||||
|
||||
func (a *AzureBlobStorage) getBlobURL(name string) azblob.BlockBlobURL {
|
||||
blobURL := a.containerURL.NewBlockBlobURL(name)
|
||||
|
||||
return blobURL
|
||||
}
|
||||
|
||||
func (a *AzureBlobStorage) isValidPublicAccessType(accessType azblob.PublicAccessType) bool {
|
||||
validTypes := azblob.PossiblePublicAccessTypeValues()
|
||||
for _, item := range validTypes {
|
||||
if item == accessType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *AzureBlobStorage) isValidDeleteSnapshotsOptionType(accessType azblob.DeleteSnapshotsOptionType) bool {
|
||||
validTypes := azblob.PossibleDeleteSnapshotsOptionTypeValues()
|
||||
for _, item := range validTypes {
|
||||
|
@ -462,41 +346,3 @@ func (a *AzureBlobStorage) isValidDeleteSnapshotsOptionType(accessType azblob.De
|
|||
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *AzureBlobStorage) sanitizeMetadata(metadata map[string]string) map[string]string {
|
||||
for key, val := range metadata {
|
||||
// Keep only letters and digits
|
||||
n := 0
|
||||
newKey := make([]byte, len(key))
|
||||
for i := 0; i < len(key); i++ {
|
||||
if (key[i] >= 'A' && key[i] <= 'Z') ||
|
||||
(key[i] >= 'a' && key[i] <= 'z') ||
|
||||
(key[i] >= '0' && key[i] <= '9') {
|
||||
newKey[n] = key[i]
|
||||
n++
|
||||
}
|
||||
}
|
||||
|
||||
if n != len(key) {
|
||||
nks := string(newKey[:n])
|
||||
a.logger.Warnf("metadata key %s contains disallowed characters, sanitized to %s", key, nks)
|
||||
delete(metadata, key)
|
||||
metadata[nks] = val
|
||||
key = nks
|
||||
}
|
||||
|
||||
// Remove all non-ascii characters
|
||||
n = 0
|
||||
newVal := make([]byte, len(val))
|
||||
for i := 0; i < len(val); i++ {
|
||||
if val[i] > 127 {
|
||||
continue
|
||||
}
|
||||
newVal[n] = val[i]
|
||||
n++
|
||||
}
|
||||
metadata[key] = string(newVal[:n])
|
||||
}
|
||||
|
||||
return metadata
|
||||
}
|
||||
|
|
|
@ -17,83 +17,12 @@ import (
|
|||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/azure-storage-blob-go/azblob"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/dapr/components-contrib/bindings"
|
||||
"github.com/dapr/kit/logger"
|
||||
)
|
||||
|
||||
func TestParseMetadata(t *testing.T) {
|
||||
m := bindings.Metadata{}
|
||||
blobStorage := NewAzureBlobStorage(logger.NewLogger("test")).(*AzureBlobStorage)
|
||||
|
||||
t.Run("parse all metadata", func(t *testing.T) {
|
||||
m.Properties = map[string]string{
|
||||
"storageAccount": "account",
|
||||
"storageAccessKey": "key",
|
||||
"container": "test",
|
||||
"getBlobRetryCount": "5",
|
||||
"decodeBase64": "true",
|
||||
}
|
||||
meta, err := blobStorage.parseMetadata(m)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "test", meta.Container)
|
||||
assert.Equal(t, "account", meta.AccountName)
|
||||
// storageAccessKey is parsed in the azauth package
|
||||
assert.Equal(t, true, meta.DecodeBase64)
|
||||
assert.Equal(t, 5, meta.GetBlobRetryCount)
|
||||
assert.Equal(t, azblob.PublicAccessNone, meta.PublicAccessLevel)
|
||||
})
|
||||
|
||||
t.Run("parse metadata with publicAccessLevel = blob", func(t *testing.T) {
|
||||
m.Properties = map[string]string{
|
||||
"storageAccount": "account",
|
||||
"storageAccessKey": "key",
|
||||
"container": "test",
|
||||
"publicAccessLevel": "blob",
|
||||
}
|
||||
meta, err := blobStorage.parseMetadata(m)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, azblob.PublicAccessBlob, meta.PublicAccessLevel)
|
||||
})
|
||||
|
||||
t.Run("parse metadata with publicAccessLevel = container", func(t *testing.T) {
|
||||
m.Properties = map[string]string{
|
||||
"storageAccount": "account",
|
||||
"storageAccessKey": "key",
|
||||
"container": "test",
|
||||
"publicAccessLevel": "container",
|
||||
}
|
||||
meta, err := blobStorage.parseMetadata(m)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, azblob.PublicAccessContainer, meta.PublicAccessLevel)
|
||||
})
|
||||
|
||||
t.Run("parse metadata with invalid publicAccessLevel", func(t *testing.T) {
|
||||
m.Properties = map[string]string{
|
||||
"storageAccount": "account",
|
||||
"storageAccessKey": "key",
|
||||
"container": "test",
|
||||
"publicAccessLevel": "invalid",
|
||||
}
|
||||
_, err := blobStorage.parseMetadata(m)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("sanitize metadata if necessary", func(t *testing.T) {
|
||||
m.Properties = map[string]string{
|
||||
"somecustomfield": "some-custom-value",
|
||||
"specialfield": "special:valueÜ",
|
||||
"not-allowed:": "not-allowed",
|
||||
}
|
||||
meta := blobStorage.sanitizeMetadata(m.Properties)
|
||||
assert.Equal(t, meta["somecustomfield"], "some-custom-value")
|
||||
assert.Equal(t, meta["specialfield"], "special:value")
|
||||
assert.Equal(t, meta["notallowed"], "not-allowed")
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetOption(t *testing.T) {
|
||||
blobStorage := NewAzureBlobStorage(logger.NewLogger("test")).(*AzureBlobStorage)
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/mitchellh/mapstructure"
|
||||
|
||||
"github.com/dapr/components-contrib/bindings"
|
||||
"github.com/dapr/components-contrib/internal/utils"
|
||||
"github.com/dapr/kit/logger"
|
||||
)
|
||||
|
||||
|
@ -35,10 +36,10 @@ import (
|
|||
//
|
||||
//revive:disable-next-line
|
||||
type HTTPSource struct {
|
||||
metadata httpMetadata
|
||||
client *http.Client
|
||||
|
||||
logger logger.Logger
|
||||
metadata httpMetadata
|
||||
client *http.Client
|
||||
errorIfNot2XX bool
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
type httpMetadata struct {
|
||||
|
@ -70,6 +71,13 @@ func (h *HTTPSource) Init(metadata bindings.Metadata) error {
|
|||
Transport: netTransport,
|
||||
}
|
||||
|
||||
if val, ok := metadata.Properties["errorIfNot2XX"]; ok {
|
||||
h.errorIfNot2XX = utils.IsTruthy(val)
|
||||
} else {
|
||||
// Default behavior
|
||||
h.errorIfNot2XX = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -91,6 +99,9 @@ func (h *HTTPSource) Operations() []bindings.OperationKind {
|
|||
// Invoke performs an HTTP request to the configured HTTP endpoint.
|
||||
func (h *HTTPSource) Invoke(ctx context.Context, req *bindings.InvokeRequest) (*bindings.InvokeResponse, error) {
|
||||
u := h.metadata.URL
|
||||
|
||||
errorIfNot2XX := h.errorIfNot2XX // Default to the component config (default is true)
|
||||
|
||||
if req.Metadata != nil {
|
||||
if path, ok := req.Metadata["path"]; ok {
|
||||
// Simplicity and no "../../.." type exploits.
|
||||
|
@ -99,6 +110,13 @@ func (h *HTTPSource) Invoke(ctx context.Context, req *bindings.InvokeRequest) (*
|
|||
return nil, fmt.Errorf("invalid path: %s", path)
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := req.Metadata["errorIfNot2XX"]; ok {
|
||||
errorIfNot2XX = utils.IsTruthy(req.Metadata["errorIfNot2XX"])
|
||||
}
|
||||
} else {
|
||||
// Prevent things below from failing if req.Metadata is nil.
|
||||
req.Metadata = make(map[string]string)
|
||||
}
|
||||
|
||||
var body io.Reader
|
||||
|
@ -164,8 +182,8 @@ func (h *HTTPSource) Invoke(ctx context.Context, req *bindings.InvokeRequest) (*
|
|||
metadata[key] = strings.Join(values, ", ")
|
||||
}
|
||||
|
||||
// Create an error for non-200 status codes.
|
||||
if resp.StatusCode/100 != 2 {
|
||||
// Create an error for non-200 status codes unless suppressed.
|
||||
if errorIfNot2XX && resp.StatusCode/100 != 2 {
|
||||
err = fmt.Errorf("received status code %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -45,146 +46,316 @@ func TestOperations(t *testing.T) {
|
|||
}, opers)
|
||||
}
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
var path string
|
||||
type TestCase struct {
|
||||
input string
|
||||
operation string
|
||||
metadata map[string]string
|
||||
path string
|
||||
err string
|
||||
statusCode int
|
||||
}
|
||||
|
||||
s := httptest.NewServer(
|
||||
http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
path = req.URL.Path
|
||||
input := req.Method
|
||||
if req.Body != nil {
|
||||
defer req.Body.Close()
|
||||
b, _ := io.ReadAll(req.Body)
|
||||
if len(b) > 0 {
|
||||
input = string(b)
|
||||
}
|
||||
}
|
||||
inputFromHeader := req.Header.Get("X-Input")
|
||||
if inputFromHeader != "" {
|
||||
input = inputFromHeader
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
if input == "internal server error" {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
w.Write([]byte(strings.ToUpper(input)))
|
||||
}),
|
||||
)
|
||||
defer s.Close()
|
||||
func (tc TestCase) ToInvokeRequest() bindings.InvokeRequest {
|
||||
requestMetadata := tc.metadata
|
||||
|
||||
if requestMetadata == nil {
|
||||
requestMetadata = map[string]string{}
|
||||
}
|
||||
|
||||
requestMetadata["X-Status-Code"] = strconv.Itoa(tc.statusCode)
|
||||
|
||||
return bindings.InvokeRequest{
|
||||
Data: []byte(tc.input),
|
||||
Metadata: requestMetadata,
|
||||
Operation: bindings.OperationKind(tc.operation),
|
||||
}
|
||||
}
|
||||
|
||||
type HTTPHandler struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
func (h *HTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
h.Path = req.URL.Path
|
||||
|
||||
input := req.Method
|
||||
if req.Body != nil {
|
||||
defer req.Body.Close()
|
||||
b, _ := io.ReadAll(req.Body)
|
||||
if len(b) > 0 {
|
||||
input = string(b)
|
||||
}
|
||||
}
|
||||
inputFromHeader := req.Header.Get("X-Input")
|
||||
if inputFromHeader != "" {
|
||||
input = inputFromHeader
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
|
||||
statusCode := req.Header.Get("X-Status-Code")
|
||||
if statusCode != "" {
|
||||
code, _ := strconv.Atoi(statusCode)
|
||||
w.WriteHeader(code)
|
||||
}
|
||||
|
||||
w.Write([]byte(strings.ToUpper(input)))
|
||||
}
|
||||
|
||||
func NewHTTPHandler() *HTTPHandler {
|
||||
return &HTTPHandler{
|
||||
Path: "/",
|
||||
}
|
||||
}
|
||||
|
||||
func InitBinding(s *httptest.Server, extraProps map[string]string) (bindings.OutputBinding, error) {
|
||||
m := bindings.Metadata{Base: metadata.Base{
|
||||
Properties: map[string]string{
|
||||
"url": s.URL,
|
||||
},
|
||||
}}
|
||||
|
||||
if extraProps != nil {
|
||||
for k, v := range extraProps {
|
||||
m.Properties[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
hs := bindingHttp.NewHTTP(logger.NewLogger("test"))
|
||||
err := hs.Init(m)
|
||||
return hs, err
|
||||
}
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
handler := NewHTTPHandler()
|
||||
s := httptest.NewServer(handler)
|
||||
defer s.Close()
|
||||
|
||||
_, err := InitBinding(s, nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestDefaultBehavior(t *testing.T) {
|
||||
handler := NewHTTPHandler()
|
||||
s := httptest.NewServer(handler)
|
||||
defer s.Close()
|
||||
|
||||
hs, err := InitBinding(s, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := map[string]struct {
|
||||
input string
|
||||
operation string
|
||||
metadata map[string]string
|
||||
path string
|
||||
err string
|
||||
}{
|
||||
tests := map[string]TestCase{
|
||||
"get": {
|
||||
input: "GET",
|
||||
operation: "get",
|
||||
metadata: nil,
|
||||
path: "/",
|
||||
err: "",
|
||||
input: "GET",
|
||||
operation: "get",
|
||||
metadata: nil,
|
||||
path: "/",
|
||||
err: "",
|
||||
statusCode: 200,
|
||||
},
|
||||
"request headers": {
|
||||
input: "OVERRIDE",
|
||||
operation: "get",
|
||||
metadata: map[string]string{"X-Input": "override"},
|
||||
path: "/",
|
||||
err: "",
|
||||
input: "OVERRIDE",
|
||||
operation: "get",
|
||||
metadata: map[string]string{"X-Input": "override"},
|
||||
path: "/",
|
||||
err: "",
|
||||
statusCode: 200,
|
||||
},
|
||||
"post": {
|
||||
input: "expected",
|
||||
operation: "post",
|
||||
metadata: map[string]string{"path": "/test"},
|
||||
path: "/test",
|
||||
err: "",
|
||||
input: "expected",
|
||||
operation: "post",
|
||||
metadata: map[string]string{"path": "/test"},
|
||||
path: "/test",
|
||||
err: "",
|
||||
statusCode: 201,
|
||||
},
|
||||
"put": {
|
||||
input: "expected",
|
||||
operation: "put",
|
||||
metadata: map[string]string{"path": "/test"},
|
||||
path: "/test",
|
||||
err: "",
|
||||
input: "expected",
|
||||
operation: "put",
|
||||
statusCode: 204,
|
||||
metadata: map[string]string{"path": "/test"},
|
||||
path: "/test",
|
||||
err: "",
|
||||
},
|
||||
"patch": {
|
||||
input: "expected",
|
||||
operation: "patch",
|
||||
metadata: map[string]string{"path": "/test"},
|
||||
path: "/test",
|
||||
err: "",
|
||||
input: "expected",
|
||||
operation: "patch",
|
||||
metadata: map[string]string{"path": "/test"},
|
||||
path: "/test",
|
||||
err: "",
|
||||
statusCode: 206,
|
||||
},
|
||||
"delete": {
|
||||
input: "DELETE",
|
||||
operation: "delete",
|
||||
metadata: nil,
|
||||
path: "/",
|
||||
err: "",
|
||||
input: "DELETE",
|
||||
operation: "delete",
|
||||
metadata: nil,
|
||||
path: "/",
|
||||
err: "",
|
||||
statusCode: 200,
|
||||
},
|
||||
"options": {
|
||||
input: "OPTIONS",
|
||||
operation: "options",
|
||||
metadata: nil,
|
||||
path: "/",
|
||||
err: "",
|
||||
input: "OPTIONS",
|
||||
operation: "options",
|
||||
metadata: nil,
|
||||
path: "/",
|
||||
err: "",
|
||||
statusCode: 200,
|
||||
},
|
||||
"trace": {
|
||||
input: "TRACE",
|
||||
operation: "trace",
|
||||
metadata: nil,
|
||||
path: "/",
|
||||
err: "",
|
||||
input: "TRACE",
|
||||
operation: "trace",
|
||||
metadata: nil,
|
||||
path: "/",
|
||||
err: "",
|
||||
statusCode: 200,
|
||||
},
|
||||
"backward compatibility": {
|
||||
input: "expected",
|
||||
operation: "create",
|
||||
metadata: map[string]string{"path": "/test"},
|
||||
path: "/test",
|
||||
err: "",
|
||||
input: "expected",
|
||||
operation: "create",
|
||||
metadata: map[string]string{"path": "/test"},
|
||||
path: "/test",
|
||||
err: "",
|
||||
statusCode: 200,
|
||||
},
|
||||
"invalid path": {
|
||||
input: "expected",
|
||||
operation: "POST",
|
||||
metadata: map[string]string{"path": "/../test"},
|
||||
path: "",
|
||||
err: "invalid path: /../test",
|
||||
input: "expected",
|
||||
operation: "POST",
|
||||
metadata: map[string]string{"path": "/../test"},
|
||||
path: "",
|
||||
err: "invalid path: /../test",
|
||||
statusCode: 400,
|
||||
},
|
||||
"invalid operation": {
|
||||
input: "notvalid",
|
||||
operation: "notvalid",
|
||||
metadata: map[string]string{"path": "/test"},
|
||||
path: "/test",
|
||||
err: "invalid operation: notvalid",
|
||||
input: "notvalid",
|
||||
operation: "notvalid",
|
||||
metadata: map[string]string{"path": "/test"},
|
||||
path: "/test",
|
||||
err: "invalid operation: notvalid",
|
||||
statusCode: 400,
|
||||
},
|
||||
"internal server error": {
|
||||
input: "internal server error",
|
||||
operation: "post",
|
||||
metadata: map[string]string{"path": "/"},
|
||||
path: "/",
|
||||
err: "received status code 500",
|
||||
input: "internal server error",
|
||||
operation: "post",
|
||||
metadata: map[string]string{"path": "/"},
|
||||
path: "/",
|
||||
err: "received status code 500",
|
||||
statusCode: 500,
|
||||
},
|
||||
"internal server error suppressed": {
|
||||
input: "internal server error", // trigger 500 downstream
|
||||
operation: "post",
|
||||
metadata: map[string]string{"path": "/", "errorIfNot2XX": "false"},
|
||||
path: "/",
|
||||
err: "",
|
||||
statusCode: 500,
|
||||
},
|
||||
"redirect should not yield an error": {
|
||||
input: "show me the treasure!",
|
||||
operation: "post",
|
||||
metadata: map[string]string{"path": "/", "errorIfNot2XX": "false"},
|
||||
path: "/",
|
||||
err: "",
|
||||
statusCode: 302,
|
||||
},
|
||||
"redirect results in an error if not suppressed": {
|
||||
input: "show me the treasure!",
|
||||
operation: "post",
|
||||
metadata: map[string]string{"path": "/"},
|
||||
path: "/",
|
||||
err: "received status code 302",
|
||||
statusCode: 302,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
response, err := hs.Invoke(context.TODO(), &bindings.InvokeRequest{
|
||||
Data: []byte(tc.input),
|
||||
Metadata: tc.metadata,
|
||||
Operation: bindings.OperationKind(tc.operation),
|
||||
})
|
||||
req := tc.ToInvokeRequest()
|
||||
response, err := hs.Invoke(context.TODO(), &req)
|
||||
if tc.err == "" {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.path, path)
|
||||
assert.Equal(t, strings.ToUpper(tc.input), string(response.Data))
|
||||
assert.Equal(t, tc.path, handler.Path)
|
||||
if tc.statusCode != 204 {
|
||||
// 204 will return no content, so we should skip checking
|
||||
assert.Equal(t, strings.ToUpper(tc.input), string(response.Data))
|
||||
}
|
||||
assert.Equal(t, "text/plain", response.Metadata["Content-Type"])
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, tc.err, err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNon2XXErrorsSuppressed(t *testing.T) {
|
||||
handler := NewHTTPHandler()
|
||||
s := httptest.NewServer(handler)
|
||||
defer s.Close()
|
||||
|
||||
hs, err := InitBinding(s, map[string]string{"errorIfNot2XX": "false"})
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := map[string]TestCase{
|
||||
"internal server error": {
|
||||
input: "internal server error",
|
||||
operation: "post",
|
||||
metadata: map[string]string{"path": "/"},
|
||||
path: "/",
|
||||
err: "",
|
||||
statusCode: 500,
|
||||
},
|
||||
"internal server error overridden": {
|
||||
input: "internal server error",
|
||||
operation: "post",
|
||||
metadata: map[string]string{"path": "/", "errorIfNot2XX": "true"},
|
||||
path: "/",
|
||||
err: "received status code 500",
|
||||
statusCode: 500,
|
||||
},
|
||||
"internal server error suppressed by request and component": {
|
||||
input: "internal server error", // trigger 500
|
||||
operation: "post",
|
||||
metadata: map[string]string{"path": "/", "errorIfNot2XX": "false"},
|
||||
path: "/",
|
||||
err: "",
|
||||
statusCode: 500,
|
||||
},
|
||||
"trace": {
|
||||
input: "TRACE",
|
||||
operation: "trace",
|
||||
metadata: nil,
|
||||
path: "/",
|
||||
err: "",
|
||||
statusCode: 200,
|
||||
},
|
||||
"backward compatibility": {
|
||||
input: "expected",
|
||||
operation: "create",
|
||||
metadata: map[string]string{"path": "/test"},
|
||||
path: "/test",
|
||||
err: "",
|
||||
statusCode: 200,
|
||||
},
|
||||
"invalid path": {
|
||||
input: "expected",
|
||||
operation: "POST",
|
||||
metadata: map[string]string{"path": "/../test"},
|
||||
path: "",
|
||||
err: "invalid path: /../test",
|
||||
statusCode: 400,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
req := tc.ToInvokeRequest()
|
||||
response, err := hs.Invoke(context.TODO(), &req)
|
||||
if tc.err == "" {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.path, handler.Path)
|
||||
if tc.statusCode != 204 {
|
||||
// 204 will return no content, so we should skip checking
|
||||
assert.Equal(t, strings.ToUpper(tc.input), string(response.Data))
|
||||
}
|
||||
assert.Equal(t, "text/plain", response.Metadata["Content-Type"])
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
|
|
3
go.mod
3
go.mod
|
@ -18,6 +18,7 @@ require (
|
|||
github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.0.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.10.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.5.1
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0
|
||||
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd
|
||||
github.com/Azure/go-amqp v0.17.5
|
||||
|
@ -133,7 +134,7 @@ require (
|
|||
github.com/99designs/keyring v1.2.1 // indirect
|
||||
github.com/AthenZ/athenz v1.10.39 // indirect
|
||||
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.0 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
|
||||
|
|
6
go.sum
6
go.sum
|
@ -110,14 +110,16 @@ github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos v0.3.2/go.mod h1:Fy3bbChFm4c
|
|||
github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.0.1 h1:bFa9IcjvrCber6gGgDAUZ+I2bO8J7s8JxXmu9fhi2ss=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.0.1/go.mod h1:l3wvZkG9oW07GLBW5Cd0WwG5asOfJ8aqE8raUvNzLpk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 h1:jp0dGvZ7ZK0mgqnTSClMxa5xuRL7NZgHameVYF6BurY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 h1:XUNQ4mw+zJmaA2KXzP9JlQiecy1SI+Eog7xVkPiqIbg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.10.1 h1:AhZnZn4kUKz36bHJ8AK/FH2tH/q3CAkG+Gme+2ibuak=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.10.1/go.mod h1:S78i9yTr4o/nXlH76bKjGUye9Z2wSxO5Tz7GoDr4vfI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.0 h1:Lg6BW0VPmCwcMlvOviL3ruHFO+H9tZNqscK0AeuFjGM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.0/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.1 h1:Zm7A6yKHT3evC/0lquPWJ9hrkRGVIeZOmIvHPv6xV9Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.1/go.mod h1:LH9XQnMr2ZYxQdVdCrzLO9mxeDyrDFa6wbSI3x5zCZk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.5.1 h1:BMTdr+ib5ljLa9MxTJK8x/Ds0MbBb4MfuW5BL0zMJnI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.5.1/go.mod h1:c6WvOhtmjNUWbLfOG1qxM/q0SPvQNSVJvolm+C52dIU=
|
||||
github.com/Azure/azure-storage-blob-go v0.6.0/go.mod h1:oGfmITT1V6x//CswqY2gtAHND+xIP64/qL7a5QJix0Y=
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0 h1:evCwGreYo3XLeBV4vSxLbLiYb6e0SzsJiXQVRGsRXxs=
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0/go.mod h1:ep1edmW+kNQx4UfWM9heESNmQdijykocJ0YOxmMX8SE=
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
Copyright 2021 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package blobstorage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
|
||||
|
||||
azauth "github.com/dapr/components-contrib/internal/authentication/azure"
|
||||
mdutils "github.com/dapr/components-contrib/metadata"
|
||||
"github.com/dapr/kit/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
// Specifies the maximum number of HTTP requests that will be made to retry blob operations. A value
|
||||
// of zero means that no additional HTTP requests will be made.
|
||||
defaultBlobRetryCount = 3
|
||||
)
|
||||
|
||||
func CreateContainerStorageClient(log logger.Logger, meta map[string]string) (*container.Client, *BlobStorageMetadata, error) {
|
||||
m, err := parseMetadata(meta)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
userAgent := "dapr-" + logger.DaprVersion
|
||||
options := container.ClientOptions{
|
||||
ClientOptions: azcore.ClientOptions{
|
||||
Retry: policy.RetryOptions{
|
||||
MaxRetries: m.RetryCount,
|
||||
},
|
||||
Telemetry: policy.TelemetryOptions{
|
||||
ApplicationID: userAgent,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
settings, err := azauth.NewEnvironmentSettings("storage", meta)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var customEndpoint string
|
||||
if val, ok := mdutils.GetMetadataProperty(meta, azauth.StorageEndpointKeys...); ok && val != "" {
|
||||
customEndpoint = val
|
||||
}
|
||||
var URL *url.URL
|
||||
if customEndpoint != "" {
|
||||
var parseErr error
|
||||
URL, parseErr = url.Parse(fmt.Sprintf("%s/%s/%s", customEndpoint, m.AccountName, m.ContainerName))
|
||||
if parseErr != nil {
|
||||
return nil, nil, parseErr
|
||||
}
|
||||
} else {
|
||||
env := settings.AzureEnvironment
|
||||
URL, _ = url.Parse(fmt.Sprintf("https://%s.blob.%s/%s", m.AccountName, env.StorageEndpointSuffix, m.ContainerName))
|
||||
}
|
||||
|
||||
var clientErr error
|
||||
var client *container.Client
|
||||
// Try using shared key credentials first
|
||||
if m.AccountKey != "" {
|
||||
credential, newSharedKeyErr := azblob.NewSharedKeyCredential(m.AccountName, m.AccountKey)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid shared key credentials with error: %w", newSharedKeyErr)
|
||||
}
|
||||
client, clientErr = container.NewClientWithSharedKeyCredential(URL.String(), credential, &options)
|
||||
if clientErr != nil {
|
||||
return nil, nil, fmt.Errorf("cannot init Blobstorage container client: %w", err)
|
||||
}
|
||||
} else {
|
||||
// fallback to AAD
|
||||
credential, tokenErr := settings.GetTokenCredential()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid token credentials with error: %w", tokenErr)
|
||||
}
|
||||
client, clientErr = container.NewClient(URL.String(), credential, &options)
|
||||
}
|
||||
if clientErr != nil {
|
||||
return nil, nil, fmt.Errorf("cannot init Blobstorage client: %w", clientErr)
|
||||
}
|
||||
|
||||
createContainerOptions := container.CreateOptions{
|
||||
Access: &m.PublicAccessLevel,
|
||||
Metadata: map[string]string{},
|
||||
}
|
||||
timeoutCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
_, err = client.Create(timeoutCtx, &createContainerOptions)
|
||||
cancel()
|
||||
// Don't return error, container might already exist
|
||||
log.Debugf("error creating container: %v", err)
|
||||
|
||||
return client, m, nil
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
Copyright 2021 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package blobstorage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
azauth "github.com/dapr/components-contrib/internal/authentication/azure"
|
||||
"github.com/dapr/kit/logger"
|
||||
)
|
||||
|
||||
type scenario struct {
|
||||
metadata map[string]string
|
||||
expectedFailureSubString string
|
||||
}
|
||||
|
||||
func TestClientInitFailures(t *testing.T) {
|
||||
log := logger.NewLogger("test")
|
||||
|
||||
scenarios := map[string]scenario{
|
||||
"missing accountName": {
|
||||
metadata: createTestMetadata(false, true, true),
|
||||
expectedFailureSubString: "missing or empty accountName field from metadata",
|
||||
},
|
||||
"missing container": {
|
||||
metadata: createTestMetadata(true, true, false),
|
||||
expectedFailureSubString: "missing or empty containerName field from metadata",
|
||||
},
|
||||
}
|
||||
|
||||
for name, s := range scenarios {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
_, _, err := CreateContainerStorageClient(log, s.metadata)
|
||||
assert.Contains(t, err.Error(), s.expectedFailureSubString)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func createTestMetadata(accountName bool, accountKey bool, container bool) map[string]string {
|
||||
m := map[string]string{}
|
||||
if accountName {
|
||||
m[azauth.StorageAccountNameKeys[0]] = "account"
|
||||
}
|
||||
if accountKey {
|
||||
m[azauth.StorageAccountKeyKeys[0]] = "key"
|
||||
}
|
||||
if container {
|
||||
m[azauth.StorageContainerNameKeys[0]] = "test"
|
||||
}
|
||||
return m
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
Copyright 2021 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package blobstorage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
|
||||
|
||||
azauth "github.com/dapr/components-contrib/internal/authentication/azure"
|
||||
mdutils "github.com/dapr/components-contrib/metadata"
|
||||
)
|
||||
|
||||
type BlobStorageMetadata struct {
|
||||
AccountName string
|
||||
AccountKey string
|
||||
ContainerName string
|
||||
RetryCount int32 `json:"retryCount,string"`
|
||||
DecodeBase64 bool `json:"decodeBase64,string"`
|
||||
PublicAccessLevel azblob.PublicAccessType
|
||||
}
|
||||
|
||||
func parseMetadata(meta map[string]string) (*BlobStorageMetadata, error) {
|
||||
m := BlobStorageMetadata{
|
||||
RetryCount: defaultBlobRetryCount,
|
||||
}
|
||||
mdutils.DecodeMetadata(meta, &m)
|
||||
|
||||
if val, ok := mdutils.GetMetadataProperty(meta, azauth.StorageAccountNameKeys...); ok && val != "" {
|
||||
m.AccountName = val
|
||||
} else {
|
||||
return nil, fmt.Errorf("missing or empty %s field from metadata", azauth.StorageAccountNameKeys[0])
|
||||
}
|
||||
|
||||
if val, ok := mdutils.GetMetadataProperty(meta, azauth.StorageContainerNameKeys...); ok && val != "" {
|
||||
m.ContainerName = val
|
||||
} else {
|
||||
return nil, fmt.Errorf("missing or empty %s field from metadata", azauth.StorageContainerNameKeys[0])
|
||||
}
|
||||
|
||||
if val, ok := mdutils.GetMetadataProperty(meta, azauth.StorageAccountKeyKeys...); ok && val != "" {
|
||||
m.AccountKey = val
|
||||
}
|
||||
|
||||
// per the Dapr documentation "none" is a valid value
|
||||
if m.PublicAccessLevel == "none" {
|
||||
m.PublicAccessLevel = ""
|
||||
}
|
||||
if m.PublicAccessLevel != "" && !isValidPublicAccessType(m.PublicAccessLevel) {
|
||||
return nil, fmt.Errorf("invalid public access level: %s; allowed: %s",
|
||||
m.PublicAccessLevel, azblob.PossiblePublicAccessTypeValues())
|
||||
}
|
||||
|
||||
// we need this key for backwards compatibility
|
||||
if val, ok := meta["getBlobRetryCount"]; ok && val != "" {
|
||||
// convert val from string to int32
|
||||
parseInt, err := strconv.ParseInt(val, 10, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.RetryCount = int32(parseInt)
|
||||
}
|
||||
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
func isValidPublicAccessType(accessType azblob.PublicAccessType) bool {
|
||||
validTypes := azblob.PossiblePublicAccessTypeValues()
|
||||
for _, item := range validTypes {
|
||||
if item == accessType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
Copyright 2021 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package blobstorage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseMetadata(t *testing.T) {
|
||||
var m map[string]string
|
||||
|
||||
t.Run("parse all metadata", func(t *testing.T) {
|
||||
m = map[string]string{
|
||||
"storageAccount": "account",
|
||||
"storageAccessKey": "key",
|
||||
"container": "test",
|
||||
"getBlobRetryCount": "5",
|
||||
"decodeBase64": "true",
|
||||
}
|
||||
meta, err := parseMetadata(m)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "test", meta.ContainerName)
|
||||
assert.Equal(t, "account", meta.AccountName)
|
||||
// storageAccessKey is parsed in the azauth package
|
||||
assert.Equal(t, true, meta.DecodeBase64)
|
||||
assert.Equal(t, int32(5), meta.RetryCount)
|
||||
assert.Equal(t, "", string(meta.PublicAccessLevel))
|
||||
})
|
||||
|
||||
t.Run("parse metadata with publicAccessLevel = blob", func(t *testing.T) {
|
||||
m = map[string]string{
|
||||
"storageAccount": "account",
|
||||
"storageAccessKey": "key",
|
||||
"container": "test",
|
||||
"publicAccessLevel": "blob",
|
||||
}
|
||||
meta, err := parseMetadata(m)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, azblob.PublicAccessTypeBlob, meta.PublicAccessLevel)
|
||||
})
|
||||
|
||||
t.Run("parse metadata with publicAccessLevel = container", func(t *testing.T) {
|
||||
m = map[string]string{
|
||||
"storageAccount": "account",
|
||||
"storageAccessKey": "key",
|
||||
"container": "test",
|
||||
"publicAccessLevel": "container",
|
||||
}
|
||||
meta, err := parseMetadata(m)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, azblob.PublicAccessTypeContainer, meta.PublicAccessLevel)
|
||||
})
|
||||
|
||||
t.Run("parse metadata with invalid publicAccessLevel", func(t *testing.T) {
|
||||
m = map[string]string{
|
||||
"storageAccount": "account",
|
||||
"storageAccessKey": "key",
|
||||
"container": "test",
|
||||
"publicAccessLevel": "invalid",
|
||||
}
|
||||
_, err := parseMetadata(m)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
Copyright 2021 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package blobstorage
|
||||
|
||||
import (
|
||||
b64 "encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob"
|
||||
|
||||
"github.com/dapr/kit/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
contentTypeKey = "contenttype"
|
||||
contentMD5Key = "contentmd5"
|
||||
contentEncodingKey = "contentencoding"
|
||||
contentLanguageKey = "contentlanguage"
|
||||
contentDispositionKey = "contentdisposition"
|
||||
cacheControlKey = "cachecontrol"
|
||||
)
|
||||
|
||||
func CreateBlobHTTPHeadersFromRequest(meta map[string]string, contentType *string, log logger.Logger) (blob.HTTPHeaders, error) {
|
||||
// build map to support arbitrary case
|
||||
caseMap := make(map[string]string)
|
||||
for k := range meta {
|
||||
caseMap[strings.ToLower(k)] = k
|
||||
}
|
||||
|
||||
blobHTTPHeaders := blob.HTTPHeaders{}
|
||||
if val, ok := meta[caseMap[contentTypeKey]]; ok && val != "" {
|
||||
blobHTTPHeaders.BlobContentType = &val
|
||||
delete(meta, caseMap[contentTypeKey])
|
||||
}
|
||||
|
||||
if contentType != nil {
|
||||
if blobHTTPHeaders.BlobContentType != nil {
|
||||
log.Warnf("ContentType received from request Metadata %s, as well as ContentType property %s, choosing value from contentType property", blobHTTPHeaders.BlobContentType, *contentType)
|
||||
}
|
||||
blobHTTPHeaders.BlobContentType = contentType
|
||||
}
|
||||
|
||||
if val, ok := meta[caseMap[contentMD5Key]]; ok && val != "" {
|
||||
sDec, err := b64.StdEncoding.DecodeString(val)
|
||||
if err != nil || len(sDec) != 16 {
|
||||
return blob.HTTPHeaders{}, fmt.Errorf("the MD5 value specified in Content MD5 is invalid, MD5 value must be 128 bits and base64 encoded")
|
||||
}
|
||||
blobHTTPHeaders.BlobContentMD5 = sDec
|
||||
delete(meta, caseMap[contentMD5Key])
|
||||
}
|
||||
if val, ok := meta[caseMap[contentEncodingKey]]; ok && val != "" {
|
||||
blobHTTPHeaders.BlobContentEncoding = &val
|
||||
delete(meta, caseMap[contentEncodingKey])
|
||||
}
|
||||
if val, ok := meta[caseMap[contentLanguageKey]]; ok && val != "" {
|
||||
blobHTTPHeaders.BlobContentLanguage = &val
|
||||
delete(meta, caseMap[contentLanguageKey])
|
||||
}
|
||||
if val, ok := meta[caseMap[contentDispositionKey]]; ok && val != "" {
|
||||
blobHTTPHeaders.BlobContentDisposition = &val
|
||||
delete(meta, caseMap[contentDispositionKey])
|
||||
}
|
||||
if val, ok := meta[caseMap[cacheControlKey]]; ok && val != "" {
|
||||
blobHTTPHeaders.BlobCacheControl = &val
|
||||
delete(meta, caseMap[cacheControlKey])
|
||||
}
|
||||
return blobHTTPHeaders, nil
|
||||
}
|
||||
|
||||
func SanitizeMetadata(log logger.Logger, metadata map[string]string) map[string]string {
|
||||
for key, val := range metadata {
|
||||
// Keep only letters and digits
|
||||
n := 0
|
||||
newKey := make([]byte, len(key))
|
||||
for i := 0; i < len(key); i++ {
|
||||
if (key[i] >= 'A' && key[i] <= 'Z') ||
|
||||
(key[i] >= 'a' && key[i] <= 'z') ||
|
||||
(key[i] >= '0' && key[i] <= '9') {
|
||||
newKey[n] = key[i]
|
||||
n++
|
||||
}
|
||||
}
|
||||
|
||||
if n != len(key) {
|
||||
nks := string(newKey[:n])
|
||||
log.Warnf("metadata key %s contains disallowed characters, sanitized to %s", key, nks)
|
||||
delete(metadata, key)
|
||||
metadata[nks] = val
|
||||
key = nks
|
||||
}
|
||||
|
||||
// Remove all non-ascii characters
|
||||
n = 0
|
||||
newVal := make([]byte, len(val))
|
||||
for i := 0; i < len(val); i++ {
|
||||
if val[i] > 127 {
|
||||
continue
|
||||
}
|
||||
newVal[n] = val[i]
|
||||
n++
|
||||
}
|
||||
metadata[key] = string(newVal[:n])
|
||||
}
|
||||
|
||||
return metadata
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
Copyright 2021 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package blobstorage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/dapr/kit/logger"
|
||||
)
|
||||
|
||||
func TestBlobHTTPHeaderGeneration(t *testing.T) {
|
||||
log := logger.NewLogger("test")
|
||||
|
||||
t.Run("Content type is set from request, forward compatibility", func(t *testing.T) {
|
||||
contentType := "application/json"
|
||||
requestMetadata := map[string]string{}
|
||||
|
||||
blobHeaders, err := CreateBlobHTTPHeadersFromRequest(requestMetadata, &contentType, log)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "application/json", *blobHeaders.BlobContentType)
|
||||
})
|
||||
t.Run("Content type and metadata provided (conflict), content type chosen", func(t *testing.T) {
|
||||
contentType := "application/json"
|
||||
requestMetadata := map[string]string{
|
||||
contentTypeKey: "text/plain",
|
||||
}
|
||||
|
||||
blobHeaders, err := CreateBlobHTTPHeadersFromRequest(requestMetadata, &contentType, log)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "application/json", *blobHeaders.BlobContentType)
|
||||
})
|
||||
t.Run("ContentType not provided, metadata provided set backward compatibility", func(t *testing.T) {
|
||||
requestMetadata := map[string]string{
|
||||
contentTypeKey: "text/plain",
|
||||
}
|
||||
blobHeaders, err := CreateBlobHTTPHeadersFromRequest(requestMetadata, nil, log)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "text/plain", *blobHeaders.BlobContentType)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSanitizeRequestMetadata(t *testing.T) {
|
||||
log := logger.NewLogger("test")
|
||||
t.Run("sanitize metadata if necessary", func(t *testing.T) {
|
||||
m := map[string]string{
|
||||
"somecustomfield": "some-custom-value",
|
||||
"specialfield": "special:valueÜ",
|
||||
"not-allowed:": "not-allowed",
|
||||
}
|
||||
meta := SanitizeMetadata(log, m)
|
||||
assert.Equal(t, meta["somecustomfield"], "some-custom-value")
|
||||
assert.Equal(t, meta["specialfield"], "special:value")
|
||||
assert.Equal(t, meta["notallowed"], "not-allowed")
|
||||
})
|
||||
}
|
|
@ -162,6 +162,7 @@ func (js *jetstreamPubSub) Subscribe(ctx context.Context, req pubsub.SubscribeRe
|
|||
if js.meta.hearbeat != 0 {
|
||||
consumerConfig.Heartbeat = js.meta.hearbeat
|
||||
}
|
||||
consumerConfig.FilterSubject = req.Topic
|
||||
|
||||
natsHandler := func(m *nats.Msg) {
|
||||
jsm, err := m.Metadata()
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -276,8 +277,9 @@ func (m *mqttPubSub) onMessage(ctx context.Context) func(client mqtt.Client, mqt
|
|||
}()
|
||||
|
||||
msg := pubsub.NewMessage{
|
||||
Topic: mqttMsg.Topic(),
|
||||
Data: mqttMsg.Payload(),
|
||||
Topic: mqttMsg.Topic(),
|
||||
Data: mqttMsg.Payload(),
|
||||
Metadata: map[string]string{"retained": strconv.FormatBool(mqttMsg.Retained())},
|
||||
}
|
||||
|
||||
topicHandler := m.handlerForTopic(msg.Topic)
|
||||
|
|
|
@ -4,4 +4,4 @@ Secret Stores provide a common way to interact with different secret stores, clo
|
|||
|
||||
## Implementing a new Secret Store
|
||||
|
||||
A compliant secret store needs to implement the `SecretStore` inteface included in the [`secret_store.go`](secret_store.go) file.
|
||||
A compliant secret store needs to implement the `SecretStore` interface included in the [`secret_store.go`](secret_store.go) file.
|
||||
|
|
|
@ -508,5 +508,9 @@ func readCertificateFolder(certPool *x509.CertPool, path string) error {
|
|||
|
||||
// Features returns the features available in this secret store.
|
||||
func (v *vaultSecretStore) Features() []secretstores.Feature {
|
||||
if v.vaultValueType == valueTypeText {
|
||||
return []secretstores.Feature{}
|
||||
}
|
||||
|
||||
return []secretstores.Feature{secretstores.FeatureMultipleKeyValuesPerSecret}
|
||||
}
|
||||
|
|
|
@ -28,49 +28,53 @@ import (
|
|||
|
||||
const (
|
||||
// base64 encoded certificate.
|
||||
certificate = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURVakNDQWpvQ0NRRFlZdzdMeXN4VXRUQU5CZ2txaGtpRzl3MEJBUXNGQURCck1Rc3dDUVlEVlFRR0V3SkQKUVRFWk1CY0dBMVVFQ0F3UVFuSnBkR2x6YUNCRGIyeDFiV0pwWVRFU01CQUdBMVVFQnd3SlZtRnVZMjkxZG1WeQpNUk13RVFZRFZRUUtEQXB0YVhOb2NtRmpiM0p3TVJnd0ZnWURWUVFEREE5MllYVnNkSEJ5YjJwbFkzUXVhVzh3CkhoY05NVGt4TVRBeE1UQTBPREV5V2hjTk1qQXhNRE14TVRBME9ERXlXakJyTVFzd0NRWURWUVFHRXdKRFFURVoKTUJjR0ExVUVDQXdRUW5KcGRHbHphQ0JEYjJ4MWJXSnBZVEVTTUJBR0ExVUVCd3dKVm1GdVkyOTFkbVZ5TVJNdwpFUVlEVlFRS0RBcHRhWE5vY21GamIzSndNUmd3RmdZRFZRUUREQTkyWVhWc2RIQnliMnBsWTNRdWFXOHdnZ0VpCk1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQ3JtaitTTmtGUHEvK2FXUFV1MlpFamtSK3AKTm1PeEVNSnZZcGhHNkJvRFAySE9ZbGRzdk9FWkRkbTBpWFlmeFIwZm5rUmtTMWEzSlZiYmhINWJnTElKb0dxcwo5aWpzN2hyQ0Rrdk9uRWxpUEZuc05pQ2NWNDNxNkZYaFMvNFpoNGpOMnlyUkU2UmZiS1BEeUw0a282NkFhSld1CnVkTldKVWpzSFZBSWowZHlnTXFKYm0rT29iSzk5ckUxcDg5Z3RNUStJdzFkWnUvUFF4SjlYOStMeXdxZUxPckQKOWhpNWkxajNFUUp2RXQxSVUzclEwc2E0NU5zZkt4YzEwZjdhTjJuSDQzSnhnMVRiZXNPOWYrcWlyeDBHYmVSYQpyVmNaazNVaFc2cHZmam9XbDBEc0NwNTJwZDBQN05rUmhmak44b2RMN0h3bFVIc1NqemlSYytsTG5YREJBZ01CCkFBRXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBSVdKdmRPZ01PUnQxWk53SENkNTNieTlkMlBkcW5tWHFZZ20KNDZHK2Fvb1dSeTJKMEMwS3ZOVGZGbEJFOUlydzNXUTVNMnpqY25qSUp5bzNLRUM5TDdPMnQ1WC9LTGVDck5ZVgpIc1d4cU5BTVBGY2VBa09HT0I1TThGVllkdjJTaVV2UDJjMEZQSzc2WFVzcVNkdnRsWGFkTk5ENzE3T0NTNm0yCnBIVjh1NWJNd1VmR2NCVFpEV2o4bjIzRVdHaXdnYkJkdDc3Z3h3YWc5NTROZkM2Ny9nSUc5ZlRrTTQ4aVJCUzEKc0NGYVBjMkFIT3hiMSs0ajVCMVY2Z29iZDZYWkFvbHdNaTNHUUtkbEM1NXZNeTNwK09WbDNNbEc3RWNTVUpMdApwZ2ZKaWw3L3dTWWhpUnhJU3hrYkk5cWhvNEwzZm5PZVB3clFVd2FzU1ZiL1lxbHZ2WHM9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
|
||||
expectedTok = "myRootToken"
|
||||
expectedTokMountPath = "./vault.txt"
|
||||
certificate = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURVakNDQWpvQ0NRRFlZdzdMeXN4VXRUQU5CZ2txaGtpRzl3MEJBUXNGQURCck1Rc3dDUVlEVlFRR0V3SkQKUVRFWk1CY0dBMVVFQ0F3UVFuSnBkR2x6YUNCRGIyeDFiV0pwWVRFU01CQUdBMVVFQnd3SlZtRnVZMjkxZG1WeQpNUk13RVFZRFZRUUtEQXB0YVhOb2NtRmpiM0p3TVJnd0ZnWURWUVFEREE5MllYVnNkSEJ5YjJwbFkzUXVhVzh3CkhoY05NVGt4TVRBeE1UQTBPREV5V2hjTk1qQXhNRE14TVRBME9ERXlXakJyTVFzd0NRWURWUVFHRXdKRFFURVoKTUJjR0ExVUVDQXdRUW5KcGRHbHphQ0JEYjJ4MWJXSnBZVEVTTUJBR0ExVUVCd3dKVm1GdVkyOTFkbVZ5TVJNdwpFUVlEVlFRS0RBcHRhWE5vY21GamIzSndNUmd3RmdZRFZRUUREQTkyWVhWc2RIQnliMnBsWTNRdWFXOHdnZ0VpCk1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQ3JtaitTTmtGUHEvK2FXUFV1MlpFamtSK3AKTm1PeEVNSnZZcGhHNkJvRFAySE9ZbGRzdk9FWkRkbTBpWFlmeFIwZm5rUmtTMWEzSlZiYmhINWJnTElKb0dxcwo5aWpzN2hyQ0Rrdk9uRWxpUEZuc05pQ2NWNDNxNkZYaFMvNFpoNGpOMnlyUkU2UmZiS1BEeUw0a282NkFhSld1CnVkTldKVWpzSFZBSWowZHlnTXFKYm0rT29iSzk5ckUxcDg5Z3RNUStJdzFkWnUvUFF4SjlYOStMeXdxZUxPckQKOWhpNWkxajNFUUp2RXQxSVUzclEwc2E0NU5zZkt4YzEwZjdhTjJuSDQzSnhnMVRiZXNPOWYrcWlyeDBHYmVSYQpyVmNaazNVaFc2cHZmam9XbDBEc0NwNTJwZDBQN05rUmhmak44b2RMN0h3bFVIc1NqemlSYytsTG5YREJBZ01CCkFBRXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBSVdKdmRPZ01PUnQxWk53SENkNTNieTlkMlBkcW5tWHFZZ20KNDZHK2Fvb1dSeTJKMEMwS3ZOVGZGbEJFOUlydzNXUTVNMnpqY25qSUp5bzNLRUM5TDdPMnQ1WC9LTGVDck5ZVgpIc1d4cU5BTVBGY2VBa09HT0I1TThGVllkdjJTaVV2UDJjMEZQSzc2WFVzcVNkdnRsWGFkTk5ENzE3T0NTNm0yCnBIVjh1NWJNd1VmR2NCVFpEV2o4bjIzRVdHaXdnYkJkdDc3Z3h3YWc5NTROZkM2Ny9nSUc5ZlRrTTQ4aVJCUzEKc0NGYVBjMkFIT3hiMSs0ajVCMVY2Z29iZDZYWkFvbHdNaTNHUUtkbEM1NXZNeTNwK09WbDNNbEc3RWNTVUpMdApwZ2ZKaWw3L3dTWWhpUnhJU3hrYkk5cWhvNEwzZm5PZVB3clFVd2FzU1ZiL1lxbHZ2WHM9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
|
||||
expectedTok = "myRootToken"
|
||||
expectedTokenMountFileContents = "Hey! TokenMountFile contents here!"
|
||||
)
|
||||
|
||||
func createTempFileWithContent(t *testing.T, contents string) (fileName string, cleanUpFunc func()) {
|
||||
dir := os.TempDir()
|
||||
f, err := os.CreateTemp(dir, "vault-token")
|
||||
assert.NoError(t, err)
|
||||
fileName = f.Name()
|
||||
cleanUpFunc = func() {
|
||||
os.Remove(fileName)
|
||||
}
|
||||
|
||||
_, err = f.WriteString(contents)
|
||||
assert.NoError(t, err)
|
||||
|
||||
return fileName, cleanUpFunc
|
||||
}
|
||||
|
||||
func createTokenMountPathFile(t *testing.T) (fileName string, cleanUpFunc func()) {
|
||||
return createTempFileWithContent(t, expectedTokenMountFileContents)
|
||||
}
|
||||
|
||||
func TestReadVaultToken(t *testing.T) {
|
||||
tokenString := "This-IS-TheRootToken"
|
||||
tmpFileName, cleanUpFunc := createTempFileWithContent(t, tokenString)
|
||||
defer cleanUpFunc()
|
||||
|
||||
t.Run("read correct token", func(t *testing.T) {
|
||||
dir := os.TempDir()
|
||||
f, err := os.CreateTemp(dir, "vault-token")
|
||||
assert.NoError(t, err)
|
||||
fileName := f.Name()
|
||||
defer os.Remove(fileName)
|
||||
|
||||
tokenString := "thisisnottheroottoken"
|
||||
_, err = f.WriteString(tokenString)
|
||||
assert.NoError(t, err)
|
||||
|
||||
v := vaultSecretStore{
|
||||
vaultTokenMountPath: f.Name(),
|
||||
vaultTokenMountPath: tmpFileName,
|
||||
}
|
||||
|
||||
err = v.initVaultToken()
|
||||
err := v.initVaultToken()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, tokenString, v.vaultToken)
|
||||
})
|
||||
|
||||
t.Run("read incorrect token", func(t *testing.T) {
|
||||
dir := os.TempDir()
|
||||
f, err := os.CreateTemp(dir, "vault-token")
|
||||
assert.NoError(t, err)
|
||||
fileName := f.Name()
|
||||
defer os.Remove(fileName)
|
||||
|
||||
tokenString := "thisisnottheroottoken"
|
||||
_, err = f.WriteString(tokenString)
|
||||
assert.NoError(t, err)
|
||||
|
||||
v := vaultSecretStore{
|
||||
vaultTokenMountPath: f.Name(),
|
||||
vaultTokenMountPath: tmpFileName,
|
||||
}
|
||||
err = v.initVaultToken()
|
||||
|
||||
err := v.initVaultToken()
|
||||
assert.Nil(t, err)
|
||||
assert.NotEqual(t, "thisistheroottoken", v.vaultToken)
|
||||
assert.NotEqual(t, "ThisIs-NOT-TheRootToken", v.vaultToken)
|
||||
})
|
||||
|
||||
t.Run("read token from vaultToken", func(t *testing.T) {
|
||||
|
@ -126,6 +130,9 @@ func TestVaultEnginePath(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestVaultTokenPrefix(t *testing.T) {
|
||||
expectedTokMountPath, cleanUpFunc := createTokenMountPathFile(t)
|
||||
defer cleanUpFunc()
|
||||
|
||||
t.Run("default value of vaultKVUsePrefix is true to emulate previous behaviour", func(t *testing.T) {
|
||||
properties := map[string]string{
|
||||
componentVaultToken: expectedTok,
|
||||
|
@ -140,11 +147,9 @@ func TestVaultTokenPrefix(t *testing.T) {
|
|||
logger: nil,
|
||||
}
|
||||
|
||||
// This call will throw an error on Windows systems because of the of
|
||||
// the call x509.SystemCertPool() because system root pool is not
|
||||
// available on Windows so ignore the error for when the tests are run
|
||||
// on the Windows platform during CI
|
||||
_ = target.Init(m)
|
||||
if err := target.Init(m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, defaultVaultKVPrefix, target.vaultKVPrefix)
|
||||
})
|
||||
|
@ -165,11 +170,9 @@ func TestVaultTokenPrefix(t *testing.T) {
|
|||
logger: nil,
|
||||
}
|
||||
|
||||
// This call will throw an error on Windows systems because of the of
|
||||
// the call x509.SystemCertPool() because system root pool is not
|
||||
// available on Windows so ignore the error for when the tests are run
|
||||
// on the Windows platform during CI
|
||||
_ = target.Init(m)
|
||||
if err := target.Init(m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "", target.vaultKVPrefix)
|
||||
})
|
||||
|
@ -190,10 +193,6 @@ func TestVaultTokenPrefix(t *testing.T) {
|
|||
logger: nil,
|
||||
}
|
||||
|
||||
// This call will throw an error on Windows systems because of the of
|
||||
// the call x509.SystemCertPool() because system root pool is not
|
||||
// available on Windows so ignore the error for when the tests are run
|
||||
// on the Windows platform during CI
|
||||
err := target.Init(m)
|
||||
|
||||
assert.NotNil(t, err)
|
||||
|
@ -201,6 +200,9 @@ func TestVaultTokenPrefix(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestVaultTokenMountPathOrVaultTokenRequired(t *testing.T) {
|
||||
expectedTokMountPath, cleanUpFunc := createTokenMountPathFile(t)
|
||||
defer cleanUpFunc()
|
||||
|
||||
t.Run("without vaultTokenMount or vaultToken", func(t *testing.T) {
|
||||
properties := map[string]string{}
|
||||
|
||||
|
@ -235,13 +237,11 @@ func TestVaultTokenMountPathOrVaultTokenRequired(t *testing.T) {
|
|||
logger: nil,
|
||||
}
|
||||
|
||||
// This call will throw an error on Windows systems because of the of
|
||||
// the call x509.SystemCertPool() because system root pool is not
|
||||
// available on Windows so ignore the error for when the tests are run
|
||||
// on the Windows platform during CI
|
||||
_ = target.Init(m)
|
||||
if err := target.Init(m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "", target.vaultToken)
|
||||
assert.Equal(t, expectedTokenMountFileContents, target.vaultToken)
|
||||
assert.Equal(t, expectedTokMountPath, target.vaultTokenMountPath)
|
||||
})
|
||||
|
||||
|
@ -259,11 +259,9 @@ func TestVaultTokenMountPathOrVaultTokenRequired(t *testing.T) {
|
|||
logger: nil,
|
||||
}
|
||||
|
||||
// This call will throw an error on Windows systems because of the of
|
||||
// the call x509.SystemCertPool() because system root pool is not
|
||||
// available on Windows so ignore the error for when the tests are run
|
||||
// on the Windows platform during CI
|
||||
_ = target.Init(m)
|
||||
if err := target.Init(m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "", target.vaultTokenMountPath)
|
||||
assert.Equal(t, expectedTok, target.vaultToken)
|
||||
|
@ -294,9 +292,12 @@ func TestVaultTokenMountPathOrVaultTokenRequired(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDefaultVaultAddress(t *testing.T) {
|
||||
expectedTokMountPath, cleanUpFunc := createTokenMountPathFile(t)
|
||||
defer cleanUpFunc()
|
||||
|
||||
t.Run("with blank vaultAddr", func(t *testing.T) {
|
||||
properties := map[string]string{
|
||||
"vaultTokenMountPath": "./vault.txt",
|
||||
"vaultTokenMountPath": expectedTokMountPath,
|
||||
}
|
||||
|
||||
m := secretstores.Metadata{
|
||||
|
@ -308,11 +309,9 @@ func TestDefaultVaultAddress(t *testing.T) {
|
|||
logger: nil,
|
||||
}
|
||||
|
||||
// This call will throw an error on Windows systems because of the of
|
||||
// the call x509.SystemCertPool() because system root pool is not
|
||||
// available on Windows so ignore the error for when the tests are run
|
||||
// on the Windows platform during CI
|
||||
_ = target.Init(m)
|
||||
if err := target.Init(m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, defaultVaultAddress, target.vaultAddress, "default was not set")
|
||||
})
|
||||
|
@ -409,10 +408,49 @@ func getCertificate() []byte {
|
|||
}
|
||||
|
||||
func TestGetFeatures(t *testing.T) {
|
||||
s := NewHashiCorpVaultSecretStore(logger.NewLogger("test"))
|
||||
// Yes, we are skipping initialization as feature retrieval doesn't depend on it.
|
||||
t.Run("Vault supports MULTIPLE_KEY_VALUES_PER_SECRET", func(t *testing.T) {
|
||||
initVaultWithVaultValueType := func(vaultValueType string) secretstores.SecretStore {
|
||||
properties := map[string]string{
|
||||
"vaultToken": expectedTok,
|
||||
"skipVerify": "true",
|
||||
"vaultValueType": vaultValueType,
|
||||
}
|
||||
|
||||
m := secretstores.Metadata{
|
||||
Base: metadata.Base{Properties: properties},
|
||||
}
|
||||
|
||||
target := &vaultSecretStore{
|
||||
client: nil,
|
||||
logger: nil,
|
||||
}
|
||||
|
||||
// This call will throw an error on Windows systems because of the of
|
||||
// the call x509.SystemCertPool() because system root pool is not
|
||||
// available on Windows so ignore the error for when the tests are run
|
||||
// on the Windows platform during CI
|
||||
_ = target.Init(m)
|
||||
|
||||
return target
|
||||
}
|
||||
|
||||
t.Run("Vault supports MULTIPLE_KEY_VALUES_PER_SECRET by default", func(t *testing.T) {
|
||||
// Yes, we are skipping initialization as feature retrieval doesn't depend on it for the default value
|
||||
s := NewHashiCorpVaultSecretStore(logger.NewLogger("test"))
|
||||
f := s.Features()
|
||||
assert.True(t, secretstores.FeatureMultipleKeyValuesPerSecret.IsPresent(f))
|
||||
})
|
||||
|
||||
t.Run("Vault supports MULTIPLE_KEY_VALUES_PER_SECRET if configured with vaultValueType=map", func(t *testing.T) {
|
||||
// Yes, we are skipping initialization as feature retrieval doesn't depend on it for the default value
|
||||
s := initVaultWithVaultValueType("text")
|
||||
f := s.Features()
|
||||
assert.False(t, secretstores.FeatureMultipleKeyValuesPerSecret.IsPresent(f))
|
||||
})
|
||||
|
||||
t.Run("Vault does not support MULTIPLE_KEY_VALUES_PER_SECRET if configured with vaultValueType=text", func(t *testing.T) {
|
||||
// Yes, we are skipping initialization as feature retrieval doesn't depend on it for the default value
|
||||
s := initVaultWithVaultValueType("text")
|
||||
f := s.Features()
|
||||
assert.False(t, secretstores.FeatureMultipleKeyValuesPerSecret.IsPresent(f))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -37,18 +37,19 @@ package blobstorage
|
|||
|
||||
import (
|
||||
"context"
|
||||
b64 "encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/azure-storage-blob-go/azblob"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
|
||||
azauth "github.com/dapr/components-contrib/internal/authentication/azure"
|
||||
storageinternal "github.com/dapr/components-contrib/internal/component/azure/blobstorage"
|
||||
mdutils "github.com/dapr/components-contrib/metadata"
|
||||
"github.com/dapr/components-contrib/state"
|
||||
"github.com/dapr/kit/logger"
|
||||
|
@ -56,72 +57,26 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
keyDelimiter = "||"
|
||||
contentType = "ContentType"
|
||||
contentMD5 = "ContentMD5"
|
||||
contentEncoding = "ContentEncoding"
|
||||
contentLanguage = "ContentLanguage"
|
||||
contentDisposition = "ContentDisposition"
|
||||
cacheControl = "CacheControl"
|
||||
keyDelimiter = "||"
|
||||
)
|
||||
|
||||
// StateStore Type.
|
||||
type StateStore struct {
|
||||
state.DefaultBulkStore
|
||||
containerURL azblob.ContainerURL
|
||||
json jsoniter.API
|
||||
containerClient *container.Client
|
||||
json jsoniter.API
|
||||
|
||||
features []state.Feature
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
type blobStorageMetadata struct {
|
||||
AccountName string
|
||||
ContainerName string
|
||||
}
|
||||
|
||||
// Init the connection to blob storage, optionally creates a blob container if it doesn't exist.
|
||||
func (r *StateStore) Init(metadata state.Metadata) error {
|
||||
meta, err := getBlobStorageMetadata(metadata.Properties)
|
||||
var err error
|
||||
r.containerClient, _, err = storageinternal.CreateContainerStorageClient(r.logger, metadata.Properties)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
credential, env, err := azauth.GetAzureStorageBlobCredentials(r.logger, meta.AccountName, metadata.Properties)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid credentials with error: %s", err.Error())
|
||||
}
|
||||
|
||||
userAgent := "dapr-" + logger.DaprVersion
|
||||
options := azblob.PipelineOptions{
|
||||
Telemetry: azblob.TelemetryOptions{Value: userAgent},
|
||||
}
|
||||
p := azblob.NewPipeline(credential, options)
|
||||
|
||||
var URL *url.URL
|
||||
customEndpoint, ok := mdutils.GetMetadataProperty(metadata.Properties, azauth.StorageEndpointKeys...)
|
||||
if ok && customEndpoint != "" {
|
||||
URL, err = url.Parse(fmt.Sprintf("%s/%s/%s", customEndpoint, meta.AccountName, meta.ContainerName))
|
||||
} else {
|
||||
URL, err = url.Parse(fmt.Sprintf("https://%s.blob.%s/%s", meta.AccountName, env.StorageEndpointSuffix, meta.ContainerName))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
containerURL := azblob.NewContainerURL(*URL, p)
|
||||
|
||||
_, err = net.LookupHost(URL.Hostname())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
_, err = containerURL.Create(ctx, azblob.Metadata{}, azblob.PublicAccessNone)
|
||||
r.logger.Debugf("error creating container: %s", err)
|
||||
|
||||
r.containerURL = containerURL
|
||||
r.logger.Debugf("using container '%s'", meta.ContainerName)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -132,34 +87,29 @@ func (r *StateStore) Features() []state.Feature {
|
|||
|
||||
// Delete the state.
|
||||
func (r *StateStore) Delete(req *state.DeleteRequest) error {
|
||||
r.logger.Debugf("delete %s", req.Key)
|
||||
return r.deleteFile(context.Background(), req)
|
||||
}
|
||||
|
||||
// Get the state.
|
||||
func (r *StateStore) Get(req *state.GetRequest) (*state.GetResponse, error) {
|
||||
r.logger.Debugf("get %s", req.Key)
|
||||
return r.readFile(context.Background(), req)
|
||||
}
|
||||
|
||||
// Set the state.
|
||||
func (r *StateStore) Set(req *state.SetRequest) error {
|
||||
r.logger.Debugf("saving %s", req.Key)
|
||||
return r.writeFile(context.Background(), req)
|
||||
}
|
||||
|
||||
func (r *StateStore) Ping() error {
|
||||
accessConditions := azblob.BlobAccessConditions{}
|
||||
|
||||
if _, err := r.containerURL.GetProperties(context.Background(), accessConditions.LeaseAccessConditions); err != nil {
|
||||
return fmt.Errorf("blob storage: error connecting to Blob storage at %s: %s", r.containerURL.URL().Host, err)
|
||||
if _, err := r.containerClient.GetProperties(context.Background(), nil); err != nil {
|
||||
return fmt.Errorf("blob storage: error connecting to Blob storage at %s: %s", r.containerClient.URL(), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *StateStore) GetComponentMetadata() map[string]string {
|
||||
metadataStruct := blobStorageMetadata{}
|
||||
metadataStruct := storageinternal.BlobStorageMetadata{}
|
||||
metadataInfo := map[string]string{}
|
||||
mdutils.GetMetadataInfoFromStructType(reflect.TypeOf(metadataStruct), &metadataInfo)
|
||||
return metadataInfo
|
||||
|
@ -177,32 +127,10 @@ func NewAzureBlobStorageStore(logger logger.Logger) state.Store {
|
|||
return s
|
||||
}
|
||||
|
||||
func getBlobStorageMetadata(meta map[string]string) (*blobStorageMetadata, error) {
|
||||
m := blobStorageMetadata{}
|
||||
err := mdutils.DecodeMetadata(meta, &m)
|
||||
|
||||
if val, ok := mdutils.GetMetadataProperty(meta, azauth.StorageAccountNameKeys...); ok && val != "" {
|
||||
m.AccountName = val
|
||||
} else {
|
||||
return nil, fmt.Errorf("missing or empty %s field from metadata", azauth.StorageAccountNameKeys[0])
|
||||
}
|
||||
|
||||
if val, ok := mdutils.GetMetadataProperty(meta, azauth.StorageContainerNameKeys...); ok && val != "" {
|
||||
m.ContainerName = val
|
||||
} else {
|
||||
return nil, fmt.Errorf("missing or empty %s field from metadata", azauth.StorageContainerNameKeys[0])
|
||||
}
|
||||
|
||||
return &m, err
|
||||
}
|
||||
|
||||
func (r *StateStore) readFile(ctx context.Context, req *state.GetRequest) (*state.GetResponse, error) {
|
||||
blobURL := r.containerURL.NewBlockBlobURL(getFileName(req.Key))
|
||||
|
||||
resp, err := blobURL.Download(ctx, 0, azblob.CountToEnd, azblob.BlobAccessConditions{}, false)
|
||||
blockBlobClient := r.containerClient.NewBlockBlobClient(getFileName(req.Key))
|
||||
blobDownloadResponse, err := blockBlobClient.DownloadStream(ctx, nil)
|
||||
if err != nil {
|
||||
r.logger.Debugf("download file %s, err %s", req.Key, err)
|
||||
|
||||
if isNotFoundError(err) {
|
||||
return &state.GetResponse{}, nil
|
||||
}
|
||||
|
@ -210,110 +138,79 @@ func (r *StateStore) readFile(ctx context.Context, req *state.GetRequest) (*stat
|
|||
return &state.GetResponse{}, err
|
||||
}
|
||||
|
||||
bodyStream := resp.Body(azblob.RetryReaderOptions{})
|
||||
data, err := io.ReadAll(bodyStream)
|
||||
reader := blobDownloadResponse.Body
|
||||
defer reader.Close()
|
||||
blobData, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
r.logger.Debugf("read file %s, err %s", req.Key, err)
|
||||
return &state.GetResponse{}, err
|
||||
return &state.GetResponse{}, fmt.Errorf("error reading az blob: %w", err)
|
||||
}
|
||||
|
||||
contentType := resp.ContentType()
|
||||
contentType := blobDownloadResponse.ContentType
|
||||
|
||||
return &state.GetResponse{
|
||||
Data: data,
|
||||
ETag: ptr.Of(string(resp.ETag())),
|
||||
ContentType: &contentType,
|
||||
Data: blobData,
|
||||
ETag: ptr.Of(string(*blobDownloadResponse.ETag)),
|
||||
ContentType: contentType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *StateStore) writeFile(ctx context.Context, req *state.SetRequest) error {
|
||||
accessConditions := azblob.BlobAccessConditions{}
|
||||
modifiedAccessConditions := blob.ModifiedAccessConditions{}
|
||||
|
||||
if req.ETag != nil && *req.ETag != "" {
|
||||
accessConditions.IfMatch = azblob.ETag(*req.ETag)
|
||||
modifiedAccessConditions.IfMatch = ptr.Of(azcore.ETag(*req.ETag))
|
||||
}
|
||||
if req.Options.Concurrency == state.FirstWrite && (req.ETag == nil || *req.ETag == "") {
|
||||
accessConditions.IfNoneMatch = azblob.ETag("*")
|
||||
modifiedAccessConditions.IfNoneMatch = ptr.Of(azcore.ETagAny)
|
||||
}
|
||||
|
||||
blobURL := r.containerURL.NewBlockBlobURL(getFileName(req.Key))
|
||||
accessConditions := blob.AccessConditions{
|
||||
ModifiedAccessConditions: &modifiedAccessConditions,
|
||||
}
|
||||
|
||||
blobHTTPHeaders, err := r.createBlobHTTPHeadersFromRequest(req)
|
||||
blobHTTPHeaders, err := storageinternal.CreateBlobHTTPHeadersFromRequest(req.Metadata, req.ContentType, r.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = azblob.UploadBufferToBlockBlob(ctx, r.marshal(req), blobURL, azblob.UploadToBlockBlobOptions{
|
||||
Metadata: req.Metadata,
|
||||
AccessConditions: accessConditions,
|
||||
BlobHTTPHeaders: blobHTTPHeaders,
|
||||
})
|
||||
if err != nil {
|
||||
r.logger.Debugf("write file %s, err %s", req.Key, err)
|
||||
|
||||
uploadOptions := azblob.UploadBufferOptions{
|
||||
AccessConditions: &accessConditions,
|
||||
Metadata: storageinternal.SanitizeMetadata(r.logger, req.Metadata),
|
||||
HTTPHeaders: &blobHTTPHeaders,
|
||||
}
|
||||
|
||||
blockBlobClient := r.containerClient.NewBlockBlobClient(getFileName(req.Key))
|
||||
_, err = blockBlobClient.UploadBuffer(ctx, r.marshal(req), &uploadOptions)
|
||||
|
||||
if err != nil {
|
||||
// Check if the error is due to ETag conflict
|
||||
if req.ETag != nil && isETagConflictError(err) {
|
||||
return state.NewETagError(state.ETagMismatch, err)
|
||||
}
|
||||
|
||||
return err
|
||||
return fmt.Errorf("error uploading az blob: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *StateStore) createBlobHTTPHeadersFromRequest(req *state.SetRequest) (azblob.BlobHTTPHeaders, error) {
|
||||
var blobHTTPHeaders azblob.BlobHTTPHeaders
|
||||
if val, ok := req.Metadata[contentType]; ok && val != "" {
|
||||
blobHTTPHeaders.ContentType = val
|
||||
delete(req.Metadata, contentType)
|
||||
}
|
||||
|
||||
if req.ContentType != nil {
|
||||
if blobHTTPHeaders.ContentType != "" {
|
||||
r.logger.Warnf("ContentType received from request Metadata %s, as well as ContentType property %s, choosing value from contentType property", blobHTTPHeaders.ContentType, *req.ContentType)
|
||||
}
|
||||
blobHTTPHeaders.ContentType = *req.ContentType
|
||||
}
|
||||
|
||||
if val, ok := req.Metadata[contentMD5]; ok && val != "" {
|
||||
sDec, err := b64.StdEncoding.DecodeString(val)
|
||||
if err != nil || len(sDec) != 16 {
|
||||
return azblob.BlobHTTPHeaders{}, fmt.Errorf("the MD5 value specified in Content MD5 is invalid, MD5 value must be 128 bits and base64 encoded")
|
||||
}
|
||||
blobHTTPHeaders.ContentMD5 = sDec
|
||||
delete(req.Metadata, contentMD5)
|
||||
}
|
||||
if val, ok := req.Metadata[contentEncoding]; ok && val != "" {
|
||||
blobHTTPHeaders.ContentEncoding = val
|
||||
delete(req.Metadata, contentEncoding)
|
||||
}
|
||||
if val, ok := req.Metadata[contentLanguage]; ok && val != "" {
|
||||
blobHTTPHeaders.ContentLanguage = val
|
||||
delete(req.Metadata, contentLanguage)
|
||||
}
|
||||
if val, ok := req.Metadata[contentDisposition]; ok && val != "" {
|
||||
blobHTTPHeaders.ContentDisposition = val
|
||||
delete(req.Metadata, contentDisposition)
|
||||
}
|
||||
if val, ok := req.Metadata[cacheControl]; ok && val != "" {
|
||||
blobHTTPHeaders.CacheControl = val
|
||||
delete(req.Metadata, cacheControl)
|
||||
}
|
||||
return blobHTTPHeaders, nil
|
||||
}
|
||||
|
||||
func (r *StateStore) deleteFile(ctx context.Context, req *state.DeleteRequest) error {
|
||||
blobURL := r.containerURL.NewBlockBlobURL(getFileName(req.Key))
|
||||
accessConditions := azblob.BlobAccessConditions{}
|
||||
blockBlobClient := r.containerClient.NewBlockBlobClient(getFileName(req.Key))
|
||||
|
||||
modifiedAccessConditions := blob.ModifiedAccessConditions{}
|
||||
if req.ETag != nil && *req.ETag != "" {
|
||||
accessConditions.IfMatch = azblob.ETag(*req.ETag)
|
||||
modifiedAccessConditions.IfMatch = ptr.Of(azcore.ETag(*req.ETag))
|
||||
}
|
||||
|
||||
_, err := blobURL.Delete(ctx, azblob.DeleteSnapshotsOptionNone, accessConditions)
|
||||
if err != nil {
|
||||
r.logger.Debugf("delete file %s, err %s", req.Key, err)
|
||||
deleteOptions := blob.DeleteOptions{
|
||||
DeleteSnapshots: nil,
|
||||
AccessConditions: &blob.AccessConditions{
|
||||
ModifiedAccessConditions: &modifiedAccessConditions,
|
||||
},
|
||||
}
|
||||
|
||||
_, err := blockBlobClient.Delete(ctx, &deleteOptions)
|
||||
if err != nil {
|
||||
if req.ETag != nil && isETagConflictError(err) {
|
||||
return state.NewETagError(state.ETagMismatch, err)
|
||||
} else if isNotFoundError(err) {
|
||||
|
@ -349,13 +246,9 @@ func (r *StateStore) marshal(req *state.SetRequest) []byte {
|
|||
}
|
||||
|
||||
func isNotFoundError(err error) bool {
|
||||
azureError, ok := err.(azblob.StorageError)
|
||||
|
||||
return ok && azureError.ServiceCode() == azblob.ServiceCodeBlobNotFound
|
||||
return bloberror.HasCode(err, bloberror.BlobNotFound)
|
||||
}
|
||||
|
||||
func isETagConflictError(err error) bool {
|
||||
azureError, ok := err.(azblob.StorageError)
|
||||
|
||||
return ok && azureError.ServiceCode() == azblob.ServiceCodeConditionNotMet
|
||||
return bloberror.HasCode(err, bloberror.ConditionNotMet)
|
||||
}
|
||||
|
|
|
@ -34,8 +34,7 @@ func TestInit(t *testing.T) {
|
|||
}
|
||||
err := s.Init(m)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "acc.blob.core.windows.net", s.containerURL.URL().Host)
|
||||
assert.Equal(t, "/dapr", s.containerURL.URL().Path)
|
||||
assert.Equal(t, "https://acc.blob.core.windows.net/dapr", s.containerClient.URL())
|
||||
})
|
||||
|
||||
t.Run("Init with missing metadata", func(t *testing.T) {
|
||||
|
@ -53,31 +52,12 @@ func TestInit(t *testing.T) {
|
|||
"accountKey": "e+Dnvl8EOxYxV94nurVaRQ==",
|
||||
"containerName": "dapr",
|
||||
}
|
||||
err := s.Init(m)
|
||||
s.Init(m)
|
||||
err := s.Ping()
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetBlobStorageMetaData(t *testing.T) {
|
||||
t.Run("Nothing at all passed", func(t *testing.T) {
|
||||
m := make(map[string]string)
|
||||
_, err := getBlobStorageMetadata(m)
|
||||
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
|
||||
t.Run("All parameters passed and parsed", func(t *testing.T) {
|
||||
m := make(map[string]string)
|
||||
m["accountName"] = "acc"
|
||||
m["containerName"] = "dapr"
|
||||
meta, err := getBlobStorageMetadata(m)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "acc", meta.AccountName)
|
||||
assert.Equal(t, "dapr", meta.ContainerName)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFileName(t *testing.T) {
|
||||
t.Run("Valid composite key", func(t *testing.T) {
|
||||
key := getFileName("app_id||key")
|
||||
|
@ -89,41 +69,3 @@ func TestFileName(t *testing.T) {
|
|||
assert.Equal(t, "key", key)
|
||||
})
|
||||
}
|
||||
|
||||
func TestBlobHTTPHeaderGeneration(t *testing.T) {
|
||||
s := NewAzureBlobStorageStore(logger.NewLogger("logger")).(*StateStore)
|
||||
t.Run("Content type is set from request, forward compatibility", func(t *testing.T) {
|
||||
contentType := "application/json"
|
||||
req := &state.SetRequest{
|
||||
ContentType: &contentType,
|
||||
}
|
||||
|
||||
blobHeaders, err := s.createBlobHTTPHeadersFromRequest(req)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "application/json", blobHeaders.ContentType)
|
||||
})
|
||||
t.Run("Content type and metadata provided (conflict), content type chosen", func(t *testing.T) {
|
||||
contentType := "application/json"
|
||||
req := &state.SetRequest{
|
||||
ContentType: &contentType,
|
||||
Metadata: map[string]string{
|
||||
contentType: "text/plain",
|
||||
},
|
||||
}
|
||||
|
||||
blobHeaders, err := s.createBlobHTTPHeadersFromRequest(req)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "application/json", blobHeaders.ContentType)
|
||||
})
|
||||
t.Run("ContentType not provided, metadata provided set backward compatibility", func(t *testing.T) {
|
||||
req := &state.SetRequest{
|
||||
Metadata: map[string]string{
|
||||
contentType: "text/plain",
|
||||
},
|
||||
}
|
||||
|
||||
blobHeaders, err := s.createBlobHTTPHeadersFromRequest(req)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "text/plain", blobHeaders.ContentType)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -15,11 +15,13 @@ package cockroachdb
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/dapr/components-contrib/metadata"
|
||||
"github.com/dapr/components-contrib/state"
|
||||
|
@ -27,15 +29,17 @@ import (
|
|||
"github.com/dapr/components-contrib/state/utils"
|
||||
"github.com/dapr/kit/logger"
|
||||
"github.com/dapr/kit/ptr"
|
||||
"github.com/dapr/kit/retry"
|
||||
|
||||
// Blank import for the underlying PostgreSQL driver.
|
||||
_ "github.com/jackc/pgx/v5/stdlib"
|
||||
)
|
||||
|
||||
const (
|
||||
connectionStringKey = "connectionString"
|
||||
errMissingConnectionString = "missing connection string"
|
||||
tableName = "state"
|
||||
connectionStringKey = "connectionString"
|
||||
errMissingConnectionString = "missing connection string"
|
||||
tableName = "state"
|
||||
defaultMaxConnectionAttempts = 5 // A bad driver connection error can occur inside the sql code so this essentially allows for more retries since the sql code does not allow that to be changed
|
||||
)
|
||||
|
||||
// cockroachDBAccess implements dbaccess.
|
||||
|
@ -47,8 +51,9 @@ type cockroachDBAccess struct {
|
|||
}
|
||||
|
||||
type cockroachDBMetadata struct {
|
||||
ConnectionString string
|
||||
TableName string
|
||||
ConnectionString string
|
||||
TableName string
|
||||
MaxConnectionAttempts *int
|
||||
}
|
||||
|
||||
// newCockroachDBAccess creates a new instance of cockroachDBAccess.
|
||||
|
@ -109,6 +114,12 @@ func (p *cockroachDBAccess) Init(metadata state.Metadata) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Ensure that a connection to the database is actually established
|
||||
err = p.Ping()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -188,6 +199,7 @@ func (p *cockroachDBAccess) BulkSet(req []state.SetRequest) error {
|
|||
// Get returns data from the database. If data does not exist for the key an empty state.GetResponse will be returned.
|
||||
func (p *cockroachDBAccess) Get(req *state.GetRequest) (*state.GetResponse, error) {
|
||||
p.logger.Debug("Getting state value from CockroachDB")
|
||||
|
||||
if req.Key == "" {
|
||||
return nil, fmt.Errorf("missing key in get operation")
|
||||
}
|
||||
|
@ -236,6 +248,7 @@ func (p *cockroachDBAccess) Get(req *state.GetRequest) (*state.GetResponse, erro
|
|||
// Delete removes an item from the state store.
|
||||
func (p *cockroachDBAccess) Delete(req *state.DeleteRequest) error {
|
||||
p.logger.Debug("Deleting state value from CockroachDB")
|
||||
|
||||
if req.Key == "" {
|
||||
return fmt.Errorf("missing key in delete operation")
|
||||
}
|
||||
|
@ -386,7 +399,27 @@ func (p *cockroachDBAccess) Query(req *state.QueryRequest) (*state.QueryResponse
|
|||
|
||||
// Ping implements database ping.
|
||||
func (p *cockroachDBAccess) Ping() error {
|
||||
return p.db.Ping()
|
||||
retryCount := defaultMaxConnectionAttempts
|
||||
if p.metadata.MaxConnectionAttempts != nil && *p.metadata.MaxConnectionAttempts >= 0 {
|
||||
retryCount = *p.metadata.MaxConnectionAttempts
|
||||
}
|
||||
config := retry.DefaultConfig()
|
||||
config.Policy = retry.PolicyExponential
|
||||
config.MaxInterval = 100 * time.Millisecond
|
||||
config.MaxRetries = int64(retryCount)
|
||||
backoff := config.NewBackOff()
|
||||
|
||||
return retry.NotifyRecover(func() error {
|
||||
err := p.db.Ping()
|
||||
if errors.Is(err, driver.ErrBadConn) {
|
||||
return fmt.Errorf("error when attempting to establish connection with cockroachDB: %v", err)
|
||||
}
|
||||
return nil
|
||||
}, backoff, func(err error, _ time.Duration) {
|
||||
p.logger.Debugf("Could not establish connection with cockroachDB. Retrying...: %v", err)
|
||||
}, func() {
|
||||
p.logger.Debug("Successfully established connection with cockroachDB after it previously failed")
|
||||
})
|
||||
}
|
||||
|
||||
// Close implements io.Close.
|
||||
|
|
|
@ -7,7 +7,7 @@ require (
|
|||
github.com/apache/dubbo-go-hessian2 v1.11.3
|
||||
github.com/dapr/components-contrib v1.9.1-0.20221025205611-e38369028650
|
||||
github.com/dapr/components-contrib/tests/certification v0.0.0-20211026011813-36b75e9ae272
|
||||
github.com/dapr/dapr v1.9.1-0.20221101183153-7e3635f1491e
|
||||
github.com/dapr/dapr v1.9.4-0.20221121055721-6683f7582ac4
|
||||
github.com/dapr/go-sdk v1.5.1-0.20221004175845-b465b1fa0721
|
||||
github.com/dapr/kit v0.0.3-0.20221102045011-c213121f0b4f
|
||||
github.com/stretchr/testify v1.8.1
|
||||
|
@ -103,6 +103,7 @@ require (
|
|||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.1 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.13.0 // indirect
|
||||
|
|
|
@ -696,6 +696,7 @@ github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhEC
|
|||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
|
|
|
@ -5,7 +5,7 @@ go 1.19
|
|||
require (
|
||||
github.com/dapr/components-contrib v1.9.1-0.20221025205611-e38369028650
|
||||
github.com/dapr/components-contrib/tests/certification v0.0.0-20211026011813-36b75e9ae272
|
||||
github.com/dapr/dapr v1.9.1-0.20221101183153-7e3635f1491e
|
||||
github.com/dapr/dapr v1.9.4-0.20221121055721-6683f7582ac4
|
||||
github.com/dapr/go-sdk v1.5.1-0.20221004175845-b465b1fa0721
|
||||
github.com/dapr/kit v0.0.3-0.20221102045011-c213121f0b4f
|
||||
github.com/nacos-group/nacos-sdk-go/v2 v2.1.2
|
||||
|
@ -78,6 +78,7 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.1 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.13.0 // indirect
|
||||
|
|
|
@ -397,6 +397,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
|||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
|
|
|
@ -33,12 +33,16 @@ import (
|
|||
dapr_testing "github.com/dapr/dapr/pkg/testing"
|
||||
daprsdk "github.com/dapr/go-sdk/client"
|
||||
"github.com/dapr/kit/logger"
|
||||
"github.com/dapr/kit/ptr"
|
||||
|
||||
"github.com/dapr/components-contrib/tests/certification/embedded"
|
||||
"github.com/dapr/components-contrib/tests/certification/flow"
|
||||
"github.com/dapr/components-contrib/tests/certification/flow/sidecar"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -104,10 +108,12 @@ func listBlobRequest(ctx flow.Context, client daprsdk.Client, prefix string, mar
|
|||
}
|
||||
|
||||
// deleteBlobRequest is used to make a common binding request for the delete operation.
|
||||
func deleteBlobRequest(ctx flow.Context, client daprsdk.Client, name string, deleteSnapshotsOption string) (out *daprsdk.BindingEvent, err error) {
|
||||
func deleteBlobRequest(ctx flow.Context, client daprsdk.Client, name string, deleteSnapshotsOption *string) (out *daprsdk.BindingEvent, err error) {
|
||||
invokeDeleteMetadata := map[string]string{
|
||||
"blobName": name,
|
||||
"deleteSnapshots": deleteSnapshotsOption,
|
||||
"blobName": name,
|
||||
}
|
||||
if deleteSnapshotsOption != nil {
|
||||
invokeDeleteMetadata["deleteSnapshots"] = *deleteSnapshotsOption
|
||||
}
|
||||
|
||||
invokeGetRequest := &daprsdk.InvokeBindingRequest{
|
||||
|
@ -185,19 +191,19 @@ func TestBlobStorage(t *testing.T) {
|
|||
assert.Equal(t, newString, input2)
|
||||
|
||||
// cleanup.
|
||||
out, invokeDeleteErr := deleteBlobRequest(ctx, client, blobName, "")
|
||||
out, invokeDeleteErr := deleteBlobRequest(ctx, client, blobName, nil)
|
||||
assert.NoError(t, invokeDeleteErr)
|
||||
assert.Empty(t, out.Data)
|
||||
|
||||
// confirm the deletion.
|
||||
_, invokeSecondGetErr := getBlobRequest(ctx, client, blobName, false)
|
||||
assert.Error(t, invokeSecondGetErr)
|
||||
assert.Contains(t, invokeSecondGetErr.Error(), "ServiceCode=BlobNotFound")
|
||||
assert.Contains(t, invokeSecondGetErr.Error(), bloberror.BlobNotFound)
|
||||
|
||||
// deleting the key again should fail.
|
||||
_, invokeDeleteErr2 := deleteBlobRequest(ctx, client, blobName, "")
|
||||
_, invokeDeleteErr2 := deleteBlobRequest(ctx, client, blobName, nil)
|
||||
assert.Error(t, invokeDeleteErr2)
|
||||
assert.Contains(t, invokeDeleteErr2.Error(), "ServiceCode=BlobNotFound")
|
||||
assert.Contains(t, invokeDeleteErr2.Error(), bloberror.BlobNotFound)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -230,7 +236,7 @@ func TestBlobStorage(t *testing.T) {
|
|||
|
||||
_, invokeCreateErr := client.InvokeBinding(ctx, invokeCreateRequest)
|
||||
assert.Error(t, invokeCreateErr)
|
||||
assert.Contains(t, invokeCreateErr.Error(), "ServiceCode=Md5Mismatch")
|
||||
assert.Contains(t, invokeCreateErr.Error(), bloberror.MD5Mismatch)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -272,14 +278,14 @@ func TestBlobStorage(t *testing.T) {
|
|||
assert.Equal(t, responseData, dataBytes)
|
||||
assert.Empty(t, out.Metadata)
|
||||
|
||||
out, invokeDeleteErr := deleteBlobRequest(ctx, client, blobName, "")
|
||||
out, invokeDeleteErr := deleteBlobRequest(ctx, client, blobName, nil)
|
||||
assert.NoError(t, invokeDeleteErr)
|
||||
assert.Empty(t, out.Data)
|
||||
|
||||
// confirm the deletion.
|
||||
_, invokeSecondGetErr := getBlobRequest(ctx, client, blobName, false)
|
||||
assert.Error(t, invokeSecondGetErr)
|
||||
assert.Contains(t, invokeSecondGetErr.Error(), "ServiceCode=BlobNotFound")
|
||||
assert.Contains(t, invokeSecondGetErr.Error(), bloberror.BlobNotFound)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -327,7 +333,7 @@ func TestBlobStorage(t *testing.T) {
|
|||
}
|
||||
|
||||
// cleanup.
|
||||
_, invokeDeleteErr := deleteBlobRequest(ctx, client, blobName, "")
|
||||
_, invokeDeleteErr := deleteBlobRequest(ctx, client, blobName, nil)
|
||||
assert.NoError(t, invokeDeleteErr)
|
||||
|
||||
return nil
|
||||
|
@ -384,9 +390,9 @@ func TestBlobStorage(t *testing.T) {
|
|||
|
||||
out, invokeGetErr := client.InvokeBinding(ctx, invokeGetRequest)
|
||||
assert.NoError(t, invokeGetErr)
|
||||
assert.Equal(t, string(out.Data), input)
|
||||
assert.Contains(t, out.Metadata, "custom")
|
||||
assert.Equal(t, out.Metadata["custom"], "hello-world")
|
||||
assert.Equal(t, input, string(out.Data))
|
||||
assert.Contains(t, out.Metadata, "Custom")
|
||||
assert.Equal(t, "hello-world", out.Metadata["Custom"])
|
||||
|
||||
out, invokeErr := listBlobRequest(ctx, client, "", "", -1, true, false, false, false, false)
|
||||
assert.NoError(t, invokeErr)
|
||||
|
@ -412,14 +418,14 @@ func TestBlobStorage(t *testing.T) {
|
|||
}
|
||||
assert.True(t, found)
|
||||
|
||||
out, invokeDeleteErr := deleteBlobRequest(ctx, client, "filename.txt", "")
|
||||
out, invokeDeleteErr := deleteBlobRequest(ctx, client, "filename.txt", nil)
|
||||
assert.NoError(t, invokeDeleteErr)
|
||||
assert.Empty(t, out.Data)
|
||||
|
||||
// confirm the deletion.
|
||||
_, invokeSecondGetErr := getBlobRequest(ctx, client, "filename.txt", false)
|
||||
assert.Error(t, invokeSecondGetErr)
|
||||
assert.Contains(t, invokeSecondGetErr.Error(), "ServiceCode=BlobNotFound")
|
||||
assert.Contains(t, invokeSecondGetErr.Error(), bloberror.BlobNotFound)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -505,8 +511,8 @@ func TestBlobStorage(t *testing.T) {
|
|||
unmarshalErr := json.Unmarshal(out.Data, &output)
|
||||
assert.NoError(t, unmarshalErr)
|
||||
|
||||
assert.Equal(t, len(output), 1)
|
||||
assert.Equal(t, output[0]["Name"], "prefixA/filename.txt")
|
||||
assert.Equal(t, 1, len(output))
|
||||
assert.Contains(t, output[0]["Name"], "prefixA")
|
||||
|
||||
nextMarker := out.Metadata["marker"]
|
||||
|
||||
|
@ -518,27 +524,27 @@ func TestBlobStorage(t *testing.T) {
|
|||
err2 := json.Unmarshal(out2.Data, &output2)
|
||||
assert.NoError(t, err2)
|
||||
|
||||
assert.Equal(t, len(output2), 1)
|
||||
assert.Equal(t, output2[0]["Name"], "prefixAfilename.txt")
|
||||
assert.Equal(t, 1, len(output2))
|
||||
assert.Contains(t, output2[0]["Name"], "prefixA")
|
||||
|
||||
// cleanup.
|
||||
_, invokeDeleteErr1 := deleteBlobRequest(ctx, client, "prefixA/filename.txt", "")
|
||||
_, invokeDeleteErr1 := deleteBlobRequest(ctx, client, "prefixA/filename.txt", nil)
|
||||
assert.NoError(t, invokeDeleteErr1)
|
||||
_, invokeDeleteErr2 := deleteBlobRequest(ctx, client, "prefixAfilename.txt", "")
|
||||
_, invokeDeleteErr2 := deleteBlobRequest(ctx, client, "prefixAfilename.txt", nil)
|
||||
assert.NoError(t, invokeDeleteErr2)
|
||||
_, invokeDeleteErr3 := deleteBlobRequest(ctx, client, "prefixB/filename.txt", "")
|
||||
_, invokeDeleteErr3 := deleteBlobRequest(ctx, client, "prefixB/filename.txt", nil)
|
||||
assert.NoError(t, invokeDeleteErr3)
|
||||
|
||||
// list deleted items with prefix.
|
||||
out3, listErr3 := listBlobRequest(ctx, client, "prefixA", "", -1, false, false, false, false, true)
|
||||
out3, listErr3 := listBlobRequest(ctx, client, "prefixA/", "", -1, false, false, false, false, true)
|
||||
assert.NoError(t, listErr3)
|
||||
|
||||
// this will only return the deleted items if soft delete policy is enabled for the blob service.
|
||||
assert.Equal(t, out3.Metadata["number"], "2")
|
||||
assert.Equal(t, "1", out3.Metadata["number"])
|
||||
var output3 []map[string]interface{}
|
||||
err3 := json.Unmarshal(out3.Data, &output3)
|
||||
assert.NoError(t, err3)
|
||||
assert.Equal(t, len(output3), 2)
|
||||
assert.Equal(t, len(output3), 1)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -553,17 +559,15 @@ func TestBlobStorage(t *testing.T) {
|
|||
defer client.Close()
|
||||
|
||||
cred, _ := azblob.NewSharedKeyCredential(os.Getenv("AzureBlobStorageAccount"), os.Getenv("AzureBlobStorageAccessKey"))
|
||||
service, _ := azblob.NewServiceClientWithSharedKey(fmt.Sprintf("https://%s.blob.core.windows.net/", os.Getenv("AzureBlobStorageAccount")), cred, nil)
|
||||
containerClient, _ := service.NewContainerClient(os.Getenv("AzureBlobStorageContainer"))
|
||||
containerClient, _ := container.NewClientWithSharedKeyCredential(fmt.Sprintf("https://%s.blob.core.windows.net/%s", os.Getenv("AzureBlobStorageAccount"), os.Getenv("AzureBlobStorageContainer")), cred, nil)
|
||||
|
||||
blobClient, _ := containerClient.NewBlockBlobClient("snapshotthis.txt")
|
||||
uploadResp, uploadErr := blobClient.UploadBuffer(
|
||||
blobClient := containerClient.NewBlockBlobClient("snapshotthis.txt")
|
||||
_, uploadErr := blobClient.UploadBuffer(
|
||||
ctx, []byte("some example content"),
|
||||
azblob.UploadOption{}) //nolint:exhaustivestruct
|
||||
&azblob.UploadBufferOptions{}) //nolint:exhaustivestruct
|
||||
assert.NoError(t, uploadErr)
|
||||
uploadResp.Body.Close()
|
||||
_, createSnapshotErr := blobClient.CreateSnapshot(
|
||||
ctx, &azblob.BlobCreateSnapshotOptions{}) //nolint:exhaustivestruct
|
||||
ctx, &blob.CreateSnapshotOptions{}) //nolint:exhaustivestruct
|
||||
assert.NoError(t, createSnapshotErr)
|
||||
|
||||
// list the contents of the container including snapshots for the specific blob only.
|
||||
|
@ -572,27 +576,27 @@ func TestBlobStorage(t *testing.T) {
|
|||
assert.Equal(t, out.Metadata["number"], "2")
|
||||
|
||||
// delete snapshots.
|
||||
_, invokeDeleteErr := deleteBlobRequest(ctx, client, "snapshotthis.txt", "only")
|
||||
_, invokeDeleteErr := deleteBlobRequest(ctx, client, "snapshotthis.txt", ptr.Of(string(blob.DeleteSnapshotsOptionTypeOnly)))
|
||||
assert.NoError(t, invokeDeleteErr)
|
||||
|
||||
// verify snapshot is deleted.
|
||||
out2, listErr2 := listBlobRequest(ctx, client, "snapshotthis.txt", "", -1, false, true, false, false, false)
|
||||
assert.NoError(t, listErr2)
|
||||
assert.Equal(t, out2.Metadata["number"], "1")
|
||||
assert.Equal(t, "1", out2.Metadata["number"])
|
||||
|
||||
// create another snapshot.
|
||||
_, createSnapshotErr2 := blobClient.CreateSnapshot(
|
||||
ctx, &azblob.BlobCreateSnapshotOptions{}) //nolint:exhaustivestruct
|
||||
ctx, &blob.CreateSnapshotOptions{}) //nolint:exhaustivestruct
|
||||
assert.NoError(t, createSnapshotErr2)
|
||||
|
||||
// delete base blob and snapshots all at once.
|
||||
_, invokeDeleteErr2 := deleteBlobRequest(ctx, client, "snapshotthis.txt", "include")
|
||||
_, invokeDeleteErr2 := deleteBlobRequest(ctx, client, "snapshotthis.txt", ptr.Of(string(blob.DeleteSnapshotsOptionTypeInclude)))
|
||||
assert.NoError(t, invokeDeleteErr2)
|
||||
|
||||
// verify base blob and snapshots are deleted.
|
||||
out3, listErr3 := listBlobRequest(ctx, client, "snapshotthis.txt", "", -1, false, true, false, false, false)
|
||||
assert.NoError(t, listErr3)
|
||||
assert.Equal(t, out3.Metadata["number"], "0")
|
||||
assert.Equal(t, "0", out3.Metadata["number"])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@ module github.com/dapr/components-contrib/tests/certification/bindings/azure/blo
|
|||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.5.1
|
||||
github.com/dapr/components-contrib v1.9.1-0.20221025205611-e38369028650
|
||||
github.com/dapr/components-contrib/tests/certification v0.0.0-20211130185200-4918900c09e1
|
||||
github.com/dapr/dapr v1.9.1-0.20221101183153-7e3635f1491e
|
||||
github.com/dapr/dapr v1.9.4-0.20221121055721-6683f7582ac4
|
||||
github.com/dapr/go-sdk v1.5.1-0.20221004175845-b465b1fa0721
|
||||
github.com/dapr/kit v0.0.3-0.20221102045011-c213121f0b4f
|
||||
github.com/stretchr/testify v1.8.1
|
||||
|
@ -19,7 +19,7 @@ require (
|
|||
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 // indirect
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0 // indirect
|
||||
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
|
@ -96,6 +96,7 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.1 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
|
|
|
@ -45,10 +45,10 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 h1:pqrAR74b6EoR4kcxF7L7Wg2B8
|
|||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4SathZPhDhF4mVwpBMFlYjyAqy8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0/go.mod h1:bhXu1AjYL+wutSL/kpSq6s7733q2Rb0yuot9Zgfqa/0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 h1:jp0dGvZ7ZK0mgqnTSClMxa5xuRL7NZgHameVYF6BurY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.1 h1:QSdcrd/UFJv6Bp/CfoVf2SrENpFn9P6Yh8yb+xNhYMM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.1/go.mod h1:eZ4g6GUvXiGulfIbbhh1Xr4XwUYaYaWMqzGD/284wCA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 h1:XUNQ4mw+zJmaA2KXzP9JlQiecy1SI+Eog7xVkPiqIbg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.5.1 h1:BMTdr+ib5ljLa9MxTJK8x/Ds0MbBb4MfuW5BL0zMJnI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.5.1/go.mod h1:c6WvOhtmjNUWbLfOG1qxM/q0SPvQNSVJvolm+C52dIU=
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0 h1:evCwGreYo3XLeBV4vSxLbLiYb6e0SzsJiXQVRGsRXxs=
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0/go.mod h1:ep1edmW+kNQx4UfWM9heESNmQdijykocJ0YOxmMX8SE=
|
||||
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd h1:b3wyxBl3vvr15tUAziPBPK354y+LSdfPCpex5oBttHo=
|
||||
|
@ -454,6 +454,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
|||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
|
|
@ -6,7 +6,7 @@ require (
|
|||
github.com/a8m/documentdb v1.3.1-0.20220405205223-5b41ba0aaeb1
|
||||
github.com/dapr/components-contrib v1.9.1-0.20221025205611-e38369028650
|
||||
github.com/dapr/components-contrib/tests/certification v0.0.0-20211130185200-4918900c09e1
|
||||
github.com/dapr/dapr v1.9.1-0.20221101183153-7e3635f1491e
|
||||
github.com/dapr/dapr v1.9.4-0.20221121055721-6683f7582ac4
|
||||
github.com/dapr/go-sdk v1.5.1-0.20221004175845-b465b1fa0721
|
||||
github.com/dapr/kit v0.0.3-0.20221102045011-c213121f0b4f
|
||||
github.com/google/uuid v1.3.0
|
||||
|
@ -22,7 +22,7 @@ require (
|
|||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos v0.3.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 // indirect
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0 // indirect
|
||||
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
|
@ -98,6 +98,7 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.1 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
|
|
|
@ -49,8 +49,8 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4Sath
|
|||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0/go.mod h1:bhXu1AjYL+wutSL/kpSq6s7733q2Rb0yuot9Zgfqa/0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos v0.3.2 h1:yJegJqjhrMJ3Oe5s43jOTGL2AsE7pJyx+7Yqls/65tw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos v0.3.2/go.mod h1:Fy3bbChFm4cZn6oIxYYqKB2FG3rBDxk3NZDLDJCHl+Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 h1:jp0dGvZ7ZK0mgqnTSClMxa5xuRL7NZgHameVYF6BurY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 h1:XUNQ4mw+zJmaA2KXzP9JlQiecy1SI+Eog7xVkPiqIbg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0 h1:evCwGreYo3XLeBV4vSxLbLiYb6e0SzsJiXQVRGsRXxs=
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0/go.mod h1:ep1edmW+kNQx4UfWM9heESNmQdijykocJ0YOxmMX8SE=
|
||||
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd h1:b3wyxBl3vvr15tUAziPBPK354y+LSdfPCpex5oBttHo=
|
||||
|
@ -459,6 +459,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
|||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
|
|
@ -5,7 +5,7 @@ go 1.19
|
|||
require (
|
||||
github.com/dapr/components-contrib v1.9.1-0.20221025205611-e38369028650
|
||||
github.com/dapr/components-contrib/tests/certification v0.0.0-20211026011813-36b75e9ae272
|
||||
github.com/dapr/dapr v1.9.1-0.20221101183153-7e3635f1491e
|
||||
github.com/dapr/dapr v1.9.4-0.20221121055721-6683f7582ac4
|
||||
github.com/dapr/go-sdk v1.5.1-0.20221004175845-b465b1fa0721
|
||||
github.com/dapr/kit v0.0.3-0.20221102045011-c213121f0b4f
|
||||
github.com/google/uuid v1.3.0
|
||||
|
@ -22,7 +22,7 @@ require (
|
|||
github.com/Azure/azure-sdk-for-go v67.0.0+incompatible // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 // indirect
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0 // indirect
|
||||
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd // indirect
|
||||
github.com/Azure/go-amqp v0.17.5 // indirect
|
||||
|
@ -104,6 +104,7 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.1 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
|
|
|
@ -51,8 +51,8 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 h1:pqrAR74b6EoR4kcxF7L7Wg2B8
|
|||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4SathZPhDhF4mVwpBMFlYjyAqy8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0/go.mod h1:bhXu1AjYL+wutSL/kpSq6s7733q2Rb0yuot9Zgfqa/0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 h1:jp0dGvZ7ZK0mgqnTSClMxa5xuRL7NZgHameVYF6BurY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 h1:XUNQ4mw+zJmaA2KXzP9JlQiecy1SI+Eog7xVkPiqIbg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-storage-blob-go v0.6.0/go.mod h1:oGfmITT1V6x//CswqY2gtAHND+xIP64/qL7a5QJix0Y=
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0 h1:evCwGreYo3XLeBV4vSxLbLiYb6e0SzsJiXQVRGsRXxs=
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0/go.mod h1:ep1edmW+kNQx4UfWM9heESNmQdijykocJ0YOxmMX8SE=
|
||||
|
@ -479,6 +479,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
|||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
|
|
@ -5,7 +5,7 @@ go 1.19
|
|||
require (
|
||||
github.com/dapr/components-contrib v1.9.1-0.20221025205611-e38369028650
|
||||
github.com/dapr/components-contrib/tests/certification v0.0.0-20211026011813-36b75e9ae272
|
||||
github.com/dapr/dapr v1.9.1-0.20221101183153-7e3635f1491e
|
||||
github.com/dapr/dapr v1.9.4-0.20221121055721-6683f7582ac4
|
||||
github.com/dapr/go-sdk v1.5.1-0.20221004175845-b465b1fa0721
|
||||
github.com/dapr/kit v0.0.3-0.20221102045011-c213121f0b4f
|
||||
github.com/stretchr/testify v1.8.1
|
||||
|
@ -19,7 +19,7 @@ require (
|
|||
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.1 // indirect
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0 // indirect
|
||||
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd // indirect
|
||||
|
@ -100,6 +100,7 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.1 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
|
|
|
@ -45,8 +45,8 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 h1:pqrAR74b6EoR4kcxF7L7Wg2B8
|
|||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4SathZPhDhF4mVwpBMFlYjyAqy8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0/go.mod h1:bhXu1AjYL+wutSL/kpSq6s7733q2Rb0yuot9Zgfqa/0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 h1:jp0dGvZ7ZK0mgqnTSClMxa5xuRL7NZgHameVYF6BurY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 h1:XUNQ4mw+zJmaA2KXzP9JlQiecy1SI+Eog7xVkPiqIbg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.1 h1:Zm7A6yKHT3evC/0lquPWJ9hrkRGVIeZOmIvHPv6xV9Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.1/go.mod h1:LH9XQnMr2ZYxQdVdCrzLO9mxeDyrDFa6wbSI3x5zCZk=
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0 h1:evCwGreYo3XLeBV4vSxLbLiYb6e0SzsJiXQVRGsRXxs=
|
||||
|
@ -462,6 +462,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
|||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
|
|
@ -5,7 +5,7 @@ go 1.19
|
|||
require (
|
||||
github.com/dapr/components-contrib v1.9.1-0.20221025205611-e38369028650
|
||||
github.com/dapr/components-contrib/tests/certification v0.0.0-20211026011813-36b75e9ae272
|
||||
github.com/dapr/dapr v1.9.1-0.20221101183153-7e3635f1491e
|
||||
github.com/dapr/dapr v1.9.4-0.20221121055721-6683f7582ac4
|
||||
github.com/dapr/go-sdk v1.5.1-0.20221004175845-b465b1fa0721
|
||||
github.com/dapr/kit v0.0.3-0.20221102045011-c213121f0b4f
|
||||
github.com/stretchr/testify v1.8.1
|
||||
|
@ -19,7 +19,7 @@ require (
|
|||
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 // indirect
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0 // indirect
|
||||
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
|
@ -97,6 +97,7 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.1 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
|
|
|
@ -45,8 +45,8 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 h1:pqrAR74b6EoR4kcxF7L7Wg2B8
|
|||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4SathZPhDhF4mVwpBMFlYjyAqy8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0/go.mod h1:bhXu1AjYL+wutSL/kpSq6s7733q2Rb0yuot9Zgfqa/0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 h1:jp0dGvZ7ZK0mgqnTSClMxa5xuRL7NZgHameVYF6BurY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 h1:XUNQ4mw+zJmaA2KXzP9JlQiecy1SI+Eog7xVkPiqIbg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0 h1:evCwGreYo3XLeBV4vSxLbLiYb6e0SzsJiXQVRGsRXxs=
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0/go.mod h1:ep1edmW+kNQx4UfWM9heESNmQdijykocJ0YOxmMX8SE=
|
||||
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd h1:b3wyxBl3vvr15tUAziPBPK354y+LSdfPCpex5oBttHo=
|
||||
|
@ -454,6 +454,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
|||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
|
|
@ -7,7 +7,7 @@ require (
|
|||
github.com/cenkalti/backoff/v4 v4.1.3
|
||||
github.com/dapr/components-contrib v1.9.1-0.20221025205611-e38369028650
|
||||
github.com/dapr/components-contrib/tests/certification v0.0.0-20220519061249-c2cb1dad5bb0
|
||||
github.com/dapr/dapr v1.9.1-0.20221101183153-7e3635f1491e
|
||||
github.com/dapr/dapr v1.9.4-0.20221121055721-6683f7582ac4
|
||||
github.com/dapr/go-sdk v1.5.1-0.20221004175845-b465b1fa0721
|
||||
github.com/dapr/kit v0.0.3-0.20221102045011-c213121f0b4f
|
||||
github.com/google/uuid v1.3.0
|
||||
|
@ -87,6 +87,7 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.1 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.17 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
|
|
|
@ -409,6 +409,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
|||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
|
||||
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
|
|
@ -5,7 +5,7 @@ go 1.19
|
|||
require (
|
||||
github.com/dapr/components-contrib v1.9.1-0.20221025205611-e38369028650
|
||||
github.com/dapr/components-contrib/tests/certification v0.0.0-00010101000000-000000000000
|
||||
github.com/dapr/dapr v1.9.1-0.20221101183153-7e3635f1491e
|
||||
github.com/dapr/dapr v1.9.4-0.20221121055721-6683f7582ac4
|
||||
github.com/dapr/go-sdk v1.5.1-0.20221004175845-b465b1fa0721
|
||||
github.com/dapr/kit v0.0.3-0.20221102045011-c213121f0b4f
|
||||
github.com/stretchr/testify v1.8.1
|
||||
|
@ -75,6 +75,7 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.1 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.13.0 // indirect
|
||||
|
|
|
@ -381,6 +381,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
|||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
|
|
|
@ -5,7 +5,7 @@ go 1.19
|
|||
require (
|
||||
github.com/dapr/components-contrib v1.9.1-0.20221025205611-e38369028650
|
||||
github.com/dapr/components-contrib/tests/certification v0.0.0-20220526162429-d03aeba3e0d6
|
||||
github.com/dapr/dapr v1.9.1-0.20221101183153-7e3635f1491e
|
||||
github.com/dapr/dapr v1.9.4-0.20221121055721-6683f7582ac4
|
||||
github.com/dapr/go-sdk v1.5.1-0.20221004175845-b465b1fa0721
|
||||
github.com/dapr/kit v0.0.3-0.20221102045011-c213121f0b4f
|
||||
github.com/lib/pq v1.10.7
|
||||
|
@ -79,6 +79,7 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.1 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.13.0 // indirect
|
||||
|
|
|
@ -389,6 +389,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
|||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
|
|
|
@ -5,7 +5,7 @@ go 1.19
|
|||
require (
|
||||
github.com/dapr/components-contrib v1.9.1-0.20221025205611-e38369028650
|
||||
github.com/dapr/components-contrib/tests/certification v0.0.0-20211130185200-4918900c09e1
|
||||
github.com/dapr/dapr v1.9.1-0.20221101183153-7e3635f1491e
|
||||
github.com/dapr/dapr v1.9.4-0.20221121055721-6683f7582ac4
|
||||
github.com/dapr/go-sdk v1.5.1-0.20221004175845-b465b1fa0721
|
||||
github.com/dapr/kit v0.0.3-0.20221102045011-c213121f0b4f
|
||||
github.com/rabbitmq/amqp091-go v1.5.0
|
||||
|
@ -77,6 +77,7 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.1 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.13.0 // indirect
|
||||
|
|
|
@ -381,6 +381,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
|||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
|
|
|
@ -5,7 +5,7 @@ go 1.19
|
|||
require (
|
||||
github.com/dapr/components-contrib v1.9.1-0.20221025205611-e38369028650
|
||||
github.com/dapr/components-contrib/tests/certification v0.0.0-20220908221803-2b5650c2faa4
|
||||
github.com/dapr/dapr v1.9.1-0.20221101183153-7e3635f1491e
|
||||
github.com/dapr/dapr v1.9.4-0.20221121055721-6683f7582ac4
|
||||
github.com/dapr/go-sdk v1.5.1-0.20221004175845-b465b1fa0721
|
||||
github.com/dapr/kit v0.0.3-0.20221102045011-c213121f0b4f
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
|
@ -76,6 +76,7 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.1 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.13.0 // indirect
|
||||
|
|
|
@ -385,6 +385,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
|||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
|
|
|
@ -22,13 +22,13 @@ import (
|
|||
global_config "github.com/dapr/dapr/pkg/config"
|
||||
env "github.com/dapr/dapr/pkg/config/env"
|
||||
"github.com/dapr/dapr/pkg/cors"
|
||||
"github.com/dapr/dapr/pkg/grpc"
|
||||
"github.com/dapr/dapr/pkg/modes"
|
||||
"github.com/dapr/dapr/pkg/operator/client"
|
||||
"github.com/dapr/dapr/pkg/resiliency"
|
||||
"github.com/dapr/dapr/pkg/runtime"
|
||||
"github.com/dapr/dapr/pkg/runtime/security"
|
||||
"github.com/dapr/kit/logger"
|
||||
"github.com/phayes/freeport"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -138,7 +138,7 @@ func NewRuntime(appID string, opts ...Option) (*runtime.DaprRuntime, *runtime.Co
|
|||
}
|
||||
|
||||
if runtimeConfig.InternalGRPCPort == 0 {
|
||||
if runtimeConfig.InternalGRPCPort, err = grpc.GetFreePort(); err != nil {
|
||||
if runtimeConfig.InternalGRPCPort, err = freeport.GetFreePort(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ go 1.19
|
|||
|
||||
require (
|
||||
github.com/dapr/components-contrib v1.9.1-0.20221025205611-e38369028650
|
||||
github.com/dapr/dapr v1.9.1-0.20221101183153-7e3635f1491e
|
||||
github.com/dapr/dapr v1.9.4-0.20221121055721-6683f7582ac4
|
||||
github.com/dapr/go-sdk v1.5.1-0.20221004175845-b465b1fa0721
|
||||
github.com/dapr/kit v0.0.3-0.20221102045011-c213121f0b4f
|
||||
github.com/google/go-cmp v0.5.9
|
||||
|
@ -134,6 +134,7 @@ require (
|
|||
|
||||
require (
|
||||
github.com/cenkalti/backoff/v4 v4.1.3
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5
|
||||
github.com/tylertreat/comcast v1.0.1
|
||||
|
||||
)
|
||||
|
|
|
@ -381,6 +381,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
|||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
|
|
|
@ -5,7 +5,7 @@ go 1.19
|
|||
require (
|
||||
github.com/dapr/components-contrib v1.9.1-0.20221025205611-e38369028650
|
||||
github.com/dapr/components-contrib/tests/certification v1.4.0-rc2
|
||||
github.com/dapr/dapr v1.9.1-0.20221101183153-7e3635f1491e
|
||||
github.com/dapr/dapr v1.9.4-0.20221121055721-6683f7582ac4
|
||||
github.com/dapr/go-sdk v1.5.1-0.20221004175845-b465b1fa0721
|
||||
github.com/dapr/kit v0.0.3-0.20221102045011-c213121f0b4f
|
||||
github.com/google/uuid v1.3.0
|
||||
|
@ -22,7 +22,7 @@ require (
|
|||
github.com/Azure/azure-sdk-for-go v67.0.0+incompatible // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 // indirect
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0 // indirect
|
||||
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd // indirect
|
||||
github.com/Azure/go-amqp v0.17.5 // indirect
|
||||
|
@ -104,6 +104,7 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.1 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
|
|
|
@ -51,8 +51,8 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 h1:pqrAR74b6EoR4kcxF7L7Wg2B8
|
|||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4SathZPhDhF4mVwpBMFlYjyAqy8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0/go.mod h1:bhXu1AjYL+wutSL/kpSq6s7733q2Rb0yuot9Zgfqa/0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 h1:jp0dGvZ7ZK0mgqnTSClMxa5xuRL7NZgHameVYF6BurY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 h1:XUNQ4mw+zJmaA2KXzP9JlQiecy1SI+Eog7xVkPiqIbg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-storage-blob-go v0.6.0/go.mod h1:oGfmITT1V6x//CswqY2gtAHND+xIP64/qL7a5QJix0Y=
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0 h1:evCwGreYo3XLeBV4vSxLbLiYb6e0SzsJiXQVRGsRXxs=
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0/go.mod h1:ep1edmW+kNQx4UfWM9heESNmQdijykocJ0YOxmMX8SE=
|
||||
|
@ -479,6 +479,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
|||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
|
|
@ -5,7 +5,7 @@ go 1.19
|
|||
require (
|
||||
github.com/dapr/components-contrib v1.9.1-0.20221025205611-e38369028650
|
||||
github.com/dapr/components-contrib/tests/certification v0.0.0-20211026011813-36b75e9ae272
|
||||
github.com/dapr/dapr v1.9.1-0.20221101183153-7e3635f1491e
|
||||
github.com/dapr/dapr v1.9.4-0.20221121055721-6683f7582ac4
|
||||
github.com/dapr/go-sdk v1.5.1-0.20221004175845-b465b1fa0721
|
||||
github.com/dapr/kit v0.0.3-0.20221102045011-c213121f0b4f
|
||||
github.com/google/uuid v1.3.0
|
||||
|
@ -20,7 +20,7 @@ require (
|
|||
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.1 // indirect
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0 // indirect
|
||||
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd // indirect
|
||||
|
@ -100,6 +100,7 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.1 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
|
|
|
@ -45,8 +45,8 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 h1:pqrAR74b6EoR4kcxF7L7Wg2B8
|
|||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4SathZPhDhF4mVwpBMFlYjyAqy8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0/go.mod h1:bhXu1AjYL+wutSL/kpSq6s7733q2Rb0yuot9Zgfqa/0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 h1:jp0dGvZ7ZK0mgqnTSClMxa5xuRL7NZgHameVYF6BurY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 h1:XUNQ4mw+zJmaA2KXzP9JlQiecy1SI+Eog7xVkPiqIbg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.1 h1:Zm7A6yKHT3evC/0lquPWJ9hrkRGVIeZOmIvHPv6xV9Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.1/go.mod h1:LH9XQnMr2ZYxQdVdCrzLO9mxeDyrDFa6wbSI3x5zCZk=
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0 h1:evCwGreYo3XLeBV4vSxLbLiYb6e0SzsJiXQVRGsRXxs=
|
||||
|
@ -462,6 +462,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
|||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
|
|
@ -7,7 +7,7 @@ require (
|
|||
github.com/cenkalti/backoff/v4 v4.1.3
|
||||
github.com/dapr/components-contrib v1.9.1-0.20221025205611-e38369028650
|
||||
github.com/dapr/components-contrib/tests/certification v0.0.0-20220519061249-c2cb1dad5bb0
|
||||
github.com/dapr/dapr v1.9.1-0.20221101183153-7e3635f1491e
|
||||
github.com/dapr/dapr v1.9.4-0.20221121055721-6683f7582ac4
|
||||
github.com/dapr/go-sdk v1.5.1-0.20221004175845-b465b1fa0721
|
||||
github.com/dapr/kit v0.0.3-0.20221102045011-c213121f0b4f
|
||||
github.com/google/uuid v1.3.0
|
||||
|
@ -87,6 +87,7 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.1 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.17 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
|
|
|
@ -409,6 +409,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
|||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
|
||||
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
|
|
@ -6,7 +6,7 @@ require (
|
|||
github.com/cenkalti/backoff/v4 v4.1.3
|
||||
github.com/dapr/components-contrib v1.9.1-0.20221025205611-e38369028650
|
||||
github.com/dapr/components-contrib/tests/certification v1.4.0-rc2
|
||||
github.com/dapr/dapr v1.9.1-0.20221101183153-7e3635f1491e
|
||||
github.com/dapr/dapr v1.9.4-0.20221121055721-6683f7582ac4
|
||||
github.com/dapr/go-sdk v1.5.1-0.20221004175845-b465b1fa0721
|
||||
github.com/dapr/kit v0.0.3-0.20221102045011-c213121f0b4f
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.2
|
||||
|
@ -79,6 +79,7 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.1 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.13.0 // indirect
|
||||
|
|
|
@ -388,6 +388,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
|||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
|
|
|
@ -6,7 +6,7 @@ require (
|
|||
github.com/cenkalti/backoff/v4 v4.1.3
|
||||
github.com/dapr/components-contrib v1.9.1-0.20221025205611-e38369028650
|
||||
github.com/dapr/components-contrib/tests/certification v0.0.0-20211130185200-4918900c09e1
|
||||
github.com/dapr/dapr v1.9.1-0.20221101183153-7e3635f1491e
|
||||
github.com/dapr/dapr v1.9.4-0.20221121055721-6683f7582ac4
|
||||
github.com/dapr/go-sdk v1.5.1-0.20221004175845-b465b1fa0721
|
||||
github.com/dapr/kit v0.0.3-0.20221102045011-c213121f0b4f
|
||||
github.com/rabbitmq/amqp091-go v1.5.0
|
||||
|
@ -77,6 +77,7 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.1 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.13.0 // indirect
|
||||
|
|
|
@ -381,6 +381,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
|||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
|
|
|
@ -5,7 +5,7 @@ go 1.19
|
|||
require (
|
||||
github.com/dapr/components-contrib v1.9.1-0.20221025205611-e38369028650
|
||||
github.com/dapr/components-contrib/tests/certification v0.0.0-20211130185200-4918900c09e1
|
||||
github.com/dapr/dapr v1.9.1-0.20221101183153-7e3635f1491e
|
||||
github.com/dapr/dapr v1.9.4-0.20221121055721-6683f7582ac4
|
||||
github.com/dapr/go-sdk v1.5.1-0.20221004175845-b465b1fa0721
|
||||
github.com/dapr/kit v0.0.3-0.20221102045011-c213121f0b4f
|
||||
github.com/stretchr/testify v1.8.1
|
||||
|
@ -18,7 +18,7 @@ require (
|
|||
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.10.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.0 // indirect
|
||||
github.com/Azure/azure-storage-blob-go v0.10.0 // indirect
|
||||
|
@ -97,6 +97,7 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.1 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
|
|
|
@ -45,8 +45,8 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 h1:pqrAR74b6EoR4kcxF7L7Wg2B8
|
|||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4SathZPhDhF4mVwpBMFlYjyAqy8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0/go.mod h1:bhXu1AjYL+wutSL/kpSq6s7733q2Rb0yuot9Zgfqa/0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 h1:jp0dGvZ7ZK0mgqnTSClMxa5xuRL7NZgHameVYF6BurY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 h1:XUNQ4mw+zJmaA2KXzP9JlQiecy1SI+Eog7xVkPiqIbg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.10.1 h1:AhZnZn4kUKz36bHJ8AK/FH2tH/q3CAkG+Gme+2ibuak=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.10.1/go.mod h1:S78i9yTr4o/nXlH76bKjGUye9Z2wSxO5Tz7GoDr4vfI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.0 h1:Lg6BW0VPmCwcMlvOviL3ruHFO+H9tZNqscK0AeuFjGM=
|
||||
|
@ -456,6 +456,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
|||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
|
|
@ -16,17 +16,118 @@ This secret store [supports the following features][features]:
|
|||
## Test network instability
|
||||
1. Vault component does not expose a time out configuration option. For this test, let's assume a 1 minute timeout.
|
||||
2. Retrieve a key to show the connection is fine.
|
||||
3. Interrupt the network (Vault port, 8200) for longer than the established timeout value.
|
||||
3. Interrupt the network on Vault's port (8200) for longer than the established timeout value.
|
||||
4. Wait a few seconds (less than the timeout value).
|
||||
5. Try to read the key from step 2 and assert it is still there.
|
||||
|
||||
|
||||
## Test support for multiple keys under the same secret
|
||||
1. Test retrieval of secrets with multiple keys under it
|
||||
1. Test retrieval of secrets with multiple keys under it.
|
||||
|
||||
## Tests for metadata fields
|
||||
|
||||
### Tests for `vaultKVPrefix`, `vaultKVUsePrefix` and `vaultValueTypeText`
|
||||
|
||||
1. Verify `vaultKVPrefix` is used
|
||||
* set field to to non default value
|
||||
* run dapr application with component
|
||||
* component should successfully initialize
|
||||
* component should advertise `multipleKeyValuesPerSecret` feature
|
||||
* retrieval of key under registered under new prefix should succeed
|
||||
* keys under default and empty prefixes should be missing
|
||||
1. Verify `vaultKVUsePrefix` is used
|
||||
* set field to `false` (non default value)
|
||||
* run dapr application with component
|
||||
* component should successfully initialize
|
||||
* component should advertise `multipleKeyValuesPerSecret` feature
|
||||
* retrieval of key registered without (empty) prefix should succeed
|
||||
* keys under default and non-default prefix from step above should be missing
|
||||
1. Verify `vaultValueTypeText` is used
|
||||
* set field to to non default value `text`
|
||||
* run dapr application with component
|
||||
* component should successfully initialize
|
||||
* component should **not** advertise `multipleKeyValuesPerSecret` feature
|
||||
* retrieval of key under registered under new prefix should succeed
|
||||
* keys under default and empty prefixes should be missing
|
||||
|
||||
|
||||
### Tests for `vaultToken` and `vaultTokenMountPath`
|
||||
|
||||
1. Verify `vaultToken` is used (happy case)
|
||||
* The baseline fo this test is all the previous test are using a known-to-work value that matches what our docker-compose environment sets up.
|
||||
1. Verify failure when we use a `vaultToken` value that does not match what our environment sets up
|
||||
1. Verify `vaultTokenMountPath` is used (happy case)
|
||||
1. Verify failure when `vaultTokenMountPath` points to a broken path
|
||||
1. Verify failure when both `vaultToken` and `vaultTokenMountPath` are missing
|
||||
1. Verify failure when both `vaultToken` and `vaultTokenMountPath` are present
|
||||
|
||||
|
||||
### Tests for vaultAddr
|
||||
|
||||
1. Verify `vaultAddr` is used (happy case)
|
||||
* The baseline fo this test is all the previous test are using this flag with a known-to-work value that matches what our docker-compose environment sets up and is **not the default**.
|
||||
1. Verify initialization and operation success when `vaultAddr` is missing `skipVerify` is `true`
|
||||
* Start a vault instance using a self-signed HTTPS certificate.
|
||||
* Component configuration lacks `vaultAddr` and defaults to address `https://127.0.0.1:8200`
|
||||
* Due to `skipVerify` the component accepts the self-signed certificate
|
||||
1. Verify initialization success but operation failure when `vaultAddr` is missing `skipVerify` is `false`
|
||||
* Start a vault instance using a self-signed HTTPS certificate.
|
||||
* Component configuration lacks `vaultAddr` and defaults to address `https://127.0.0.1:8200`
|
||||
* Since `skipVerify` is disable the component requires a valid TLS certificate and refuses to connect to our vault instance, failing requests.
|
||||
1. Verify `vaultAddr` is used when it points to a non-std port
|
||||
* Start a vault instance in dev-mode (HTTP) but listening on a non-std port
|
||||
* Modify component configuration to use this non-std port
|
||||
* Ensure component initialization success and successful retrieval of secrets
|
||||
1. Verify successful initialization but secret retrieval failure when `vaultAddr` points to an address not served by a Vault
|
||||
* Start a vault instance in dev-mode (HTTP) listening on std port
|
||||
* Modify component configuration to use a distinct (non-std) port
|
||||
* Ensure component initialization success but secret retrieval failure
|
||||
|
||||
|
||||
### Tests for enginePath
|
||||
|
||||
1. Verify that setting `enginePath` to an explicit default value works
|
||||
1. Verify that setting `enginePath` to a custom value (`TestEnginePathCustomSecretsPath`) works
|
||||
* Start a vault instance in dev-mode
|
||||
* In the companion shell script that seeds the vault instance with secrets,
|
||||
1. Create a new **path** named `customSecretsPath` that uses the KV engine version 2 (`-version=2 kv` or `kv-v2`)
|
||||
* We cannot use version 1 as the vault component lacks support for non-versioned engines.
|
||||
2. Seeds this path with a secret specific for this test (to avoid the risk of false-positive tests)
|
||||
* Verify that the custom path has secrets under it using BulkList (this is a sanity check)
|
||||
* Verify that the custom path-specific secret is found
|
||||
|
||||
|
||||
### Tests for CA and other certificate-related parameters
|
||||
|
||||
TODO: Should we add code to enforce that only one of these is provided?
|
||||
|
||||
1. Verify happy-case behavior when `caPem` is set to valid CA certificate and `tlsServerName` matches the server name and `skipVerify` is false.
|
||||
1. Verify happy-case behavior when `caPath` is set to valid CA certificate and `tlsServerName` matches the server name and `skipVerify` is false.
|
||||
1. Verify happy-case behavior when `caCert` is set to valid CA certificate and `tlsServerName` matches the server name and `skipVerify` is false.
|
||||
1. Verify successful initialization but secret retrieval failure when `caPem` is set to a valid server certificate (baseline) but `tlsServerName` does not match the server name and `skipVerify` is false.
|
||||
1. Verify successful initialization but secret retrieval failure when `caPem` is set to an invalid server certificate (flag under test) despite `tlsServerName` matching the server name and `skipVerify` is false.
|
||||
|
||||
1. Same as the one above but `skipVerify` is true and we should observe happy-case behavior.
|
||||
|
||||
1. skipVerify
|
||||
* We also tested before with the vault-generated self-signed cert while testing for `vaultAddr`
|
||||
|
||||
|
||||
#### Versioning
|
||||
|
||||
1. Verify success on retrieval of a past version of a secret
|
||||
* Start a vault instance in dev-mode (HTTP)
|
||||
* Seed vault instance with multiple versions of a single secret
|
||||
* Use standard component configuration
|
||||
* Ensure component initialization success
|
||||
* Ensure successful retrieval of latest version of the secret
|
||||
* Ensure successful retrieval of an specific version of the secret
|
||||
|
||||
|
||||
## Out of scope
|
||||
|
||||
1. Verifying how vault component handles engines that do not support versioning, like KV version 1
|
||||
* Vault component only handles engines with version support (Bug?)
|
||||
1. Tests verifying writing and updating a secret since secret stores do not expose this functionality.
|
||||
|
||||
|
||||
|
@ -34,8 +135,24 @@ This secret store [supports the following features][features]:
|
|||
|
||||
Under the current directory run:
|
||||
|
||||
```shell
|
||||
GOLANG_PROTOBUF_REGISTRATION_CONFLICT=warn go test -v .
|
||||
```
|
||||
go test -v vault_test.go
|
||||
|
||||
To run an specific test run (replacing `TestVersioning` with the name of the test method):
|
||||
|
||||
```shell
|
||||
GOLANG_PROTOBUF_REGISTRATION_CONFLICT=warn go test -run TestVersioning -v .
|
||||
```
|
||||
|
||||
### Docker-compose
|
||||
|
||||
You might need to verify if docker-compose is doing what you think it is doing: seeding the right secrets or even booting up properly.
|
||||
|
||||
Head to the directory hosting the `docker-compose-hashicorp-vault.yml` file and run:
|
||||
|
||||
```shell
|
||||
docker-compose -f docker-compose-hashicorp-vault.yml up --remove-orphans
|
||||
```
|
||||
|
||||
# References:
|
||||
|
@ -44,7 +161,10 @@ go test -v vault_test.go
|
|||
* [List of secret store components and their features][features]
|
||||
* [PR with Conformance tests for Hashicorp Vault][conformance]
|
||||
* [HashiCorp Vault API reference](https://www.vaultproject.io/api-docs)
|
||||
* [Vault Official Docker image documentation][vault-docker]
|
||||
|
||||
|
||||
[HashiCorp Vault Secret Store]: https://docs.dapr.io/reference/components-reference/supported-secret-stores/hashicorp-vault/
|
||||
[features]: https://docs.dapr.io/reference/components-reference/supported-secret-stores/
|
||||
[conformance]: https://github.com/dapr/components-contrib/pull/2031
|
||||
[conformance]: https://github.com/dapr/components-contrib/pull/2031
|
||||
[vault-docker]: https://hub.docker.com/_/vault/
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
Copyright 2021 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package vault_test
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/dapr/components-contrib/tests/certification/flow"
|
||||
"github.com/dapr/kit/logger"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
//
|
||||
// Helper functions for asserting error messages during component initialization
|
||||
//
|
||||
// These can be exported to their own module.
|
||||
// Do notice that they have side effects: using more than one in a single
|
||||
// flow will cause only the latest to work. Perhaps this functionality
|
||||
// (dapr.runtime log capture) could be baked into flows themselves?
|
||||
//
|
||||
// Also: this is not thread-safe nor concurrent safe: only one test
|
||||
// can be run at a time to ensure deterministic capture of dapr.runtime output.
|
||||
|
||||
type InitErrorChecker func(ctx flow.Context, errorLine string) error
|
||||
|
||||
func CaptureLogsAndCheckInitErrors(checker InitErrorChecker) flow.Runnable {
|
||||
// Setup log capture
|
||||
logCaptor := &bytes.Buffer{}
|
||||
runtimeLogger := logger.NewLogger("dapr.runtime")
|
||||
runtimeLogger.SetOutput(io.MultiWriter(os.Stdout, logCaptor))
|
||||
|
||||
// Stop log capture, reset buffer just for good mesure
|
||||
cleanup := func() {
|
||||
logCaptor.Reset()
|
||||
runtimeLogger.SetOutput(os.Stdout)
|
||||
}
|
||||
|
||||
grepInitErrorFromLogs := func() (string, error) {
|
||||
errorMarker := []byte("INIT_COMPONENT_FAILURE")
|
||||
scanner := bufio.NewScanner(logCaptor)
|
||||
for scanner.Scan() {
|
||||
if err := scanner.Err(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if bytes.Contains(scanner.Bytes(), errorMarker) {
|
||||
return scanner.Text(), nil
|
||||
}
|
||||
}
|
||||
return "", scanner.Err()
|
||||
}
|
||||
|
||||
// Wraps our InitErrorChecker with cleanup and error-grepping logic so we only care about the
|
||||
// log error
|
||||
return func(ctx flow.Context) error {
|
||||
defer cleanup()
|
||||
|
||||
errorLine, err := grepInitErrorFromLogs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.Logf("captured errorLine: %s", errorLine)
|
||||
|
||||
return checker(ctx, errorLine)
|
||||
}
|
||||
}
|
||||
|
||||
func AssertNoInitializationErrorsForComponent(componentName string) flow.Runnable {
|
||||
checker := func(ctx flow.Context, errorLine string) error {
|
||||
componentFailedToInitialize := strings.Contains(errorLine, componentName)
|
||||
assert.False(ctx.T, componentFailedToInitialize,
|
||||
"Found component name mentioned in an component initialization error message: %s", errorLine)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return CaptureLogsAndCheckInitErrors(checker)
|
||||
}
|
||||
|
||||
func AssertInitializationFailedWithErrorsForComponent(componentName string, additionalSubStringsToMatch ...string) flow.Runnable {
|
||||
checker := func(ctx flow.Context, errorLine string) error {
|
||||
assert.NotEmpty(ctx.T, errorLine, "Expected a component initialization error message but none found")
|
||||
assert.Contains(ctx.T, errorLine, componentName,
|
||||
"Expected to find component '%s' mentioned in error message but found none: %s", componentName, errorLine)
|
||||
|
||||
for _, subString := range additionalSubStringsToMatch {
|
||||
assert.Contains(ctx.T, errorLine, subString,
|
||||
"Expected to find '%s' mentioned in error message but found none: %s", componentName, errorLine)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return CaptureLogsAndCheckInitErrors(checker)
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
TARGETS = certificates/key.pem certificates/cert.pem caPem/hashicorp-vault.yml certificates/incorrect-ca-key.pem certificates/incorrect-ca-cert.pem
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
# Create cert and key lasting 10 years, no password, no prompt for
|
||||
# subject details. Also set subjectAltName so we avoid the
|
||||
# "x509: certificate relies on legacy Common Name field" errors
|
||||
|
||||
certificates/key.pem certificates/cert.pem:
|
||||
set -e; \
|
||||
mkdir -v -p certificates; \
|
||||
openssl req -x509 -newkey rsa:4096 \
|
||||
-keyout certificates/key.pem \
|
||||
-out certificates/cert.pem \
|
||||
-sha256 -days 3650 \
|
||||
-nodes \
|
||||
-addext "subjectAltName = DNS:hashicorp_vault,DNS:localhost,IP:127.0.0.1" \
|
||||
-subj "/C=CA/ST=BC/L=Vancouver/O=Dapr Testing/OU=Org/CN=www.dapr.io"; \
|
||||
chmod -v 644 certificates/key.pem certificates/cert.pem
|
||||
|
||||
# We use this for negative tests that ensure we reject connecting to
|
||||
# a server using a distinct Certificate Authority -- despite the server certificate
|
||||
# having all the right identifiers
|
||||
certificates/incorrect-ca-key.pem certificates/incorrect-ca-cert.pem:
|
||||
set -e; \
|
||||
mkdir -v -p certificates; \
|
||||
openssl req -x509 -newkey rsa:4096 \
|
||||
-keyout certificates/incorrect-ca-key.pem \
|
||||
-out certificates/incorrect-ca-cert.pem \
|
||||
-sha256 -days 3650 \
|
||||
-nodes \
|
||||
-addext "subjectAltName = DNS:hashicorp_vault,DNS:localhost,IP:127.0.0.1" \
|
||||
-subj "/C=CA/ST=BC/L=Vancouver/O=Dapr Testing/OU=Org/CN=www.dapr.io" ; \
|
||||
chmod -v 644 certificates/incorrect-ca-key.pem certificates/incorrect-ca-cert.pem
|
||||
|
||||
|
||||
caPem/hashicorp-vault.yml: caPem/hashicorp-vault.yml.template certificates/cert.pem
|
||||
set -e; \
|
||||
echo "#\n# THIS FILE IS AUTO-GENERATED - DO NOT EDIT\n#\n\n" > $@.tmp; \
|
||||
cat caPem/hashicorp-vault.yml.template >> $@.tmp;\
|
||||
sed 's/^/ /' certificates/cert.pem >> $@.tmp; \
|
||||
mv -f $@.tmp $@
|
||||
|
||||
# %: .tmp.%
|
||||
# mv $< $@
|
||||
|
||||
clean:
|
||||
rm -f -v $(TARGETS)
|
||||
rmdir certificates
|
||||
|
||||
.PHONY: clean
|
||||
.PHONY: all
|
|
@ -0,0 +1,27 @@
|
|||
# Certificate-related tests
|
||||
|
||||
These tests are particularly tricky to setup- so a little README in case you decide to change
|
||||
this in the future and things break.
|
||||
|
||||
# vault, `-dev-tls` and its default port
|
||||
|
||||
To make our testing easier we start vault with `-dev-tls`. We do this:
|
||||
* to keep the development behavior on,
|
||||
* to force vault to start with a default TLS listener with its own self-signed TLS cert. We will
|
||||
use this listener for our negative tests (for `skipValidation` and `tlsServerName`).
|
||||
|
||||
To keep the rest of the test setup consistent and similar to other tests, we move this listener to a non-default port.
|
||||
|
||||
|
||||
# Using and generating our very own certificate and key
|
||||
|
||||
Besides `-dev-tls`, we also instruct vault to use a configuration that defines another listener using `-config /vault/config/vault_server.hcl`. This listener, defined in the `config/vault_server.hcl` is configured use a certificate-key pair we generated ourselves. It also binds this listener to the default vault port - to make the keep some sort of consistency in the test setup.
|
||||
|
||||
We use this certificate we generated to assist with the validation of `caPem`, `caCert`, `caPath`, `skipValidate` and `tlsServerName` flags. All of these refer to the same certificate. Testing `caPem` is a bit special in that it needs the certificate inlined in the component YAML file.
|
||||
|
||||
A Makefile is included here in order to document and to ease re-generation of the certificate and keys. It will also re-generate the `caPem`-dependent component YAML, so one does not have to remember updating it whenever the certificate is updated or regenerated.
|
||||
As a matter of fact, our code does not ship with any of these certificates. Instead, this Makefile is invoked at the begging on `TestCaFamilyOfFields` test.
|
||||
|
||||
# Misc. references
|
||||
|
||||
For how to configure the vault docker image we are using check https://hub.docker.com/_/vault/
|
|
@ -0,0 +1,46 @@
|
|||
version: '3.9'
|
||||
|
||||
# Use a YAML reference to define VAULT_TOKEN and DOCKER_IMAGE only once
|
||||
x-common-values:
|
||||
# This should match tests/config/secrestore/hashicorp/vault/hashicorp-vault.yaml
|
||||
# This should match .github/infrastructure/conformance/hashicorp/vault_token_file.txt
|
||||
vault_token: &VAULT_TOKEN "vault-dev-root-token-id"
|
||||
# Reuse the same docker image to save on resources and because the base vault image
|
||||
# has everything we need for seeding the initial key values too.
|
||||
vault_docker_image: &VAULT_DOCKER_IMAGE vault:1.12.1
|
||||
|
||||
services:
|
||||
hashicorp_vault:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
ports:
|
||||
- '8200:8200'
|
||||
cap_add:
|
||||
- IPC_LOCK
|
||||
environment:
|
||||
# Move vault's dev-mode self-signed TLS listener to another port so we can use the default one for
|
||||
# our own listener with our own self-signed certificate.
|
||||
VAULT_DEV_LISTEN_ADDRESS: "0.0.0.0:7200"
|
||||
VAULT_DEV_ROOT_TOKEN_ID: *VAULT_TOKEN
|
||||
volumes:
|
||||
- ../vaultConfig:/vault/config/:ro
|
||||
- ../certificates:/certificates:ro
|
||||
# Force vault to use TLS/HTTPS in dev mode
|
||||
entrypoint: vault server -dev-tls -config /vault/config/vault_server.hcl
|
||||
|
||||
# We define a aux. service to seed the expected conformance secrets to vault
|
||||
seed_conformance_secrets:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
depends_on:
|
||||
- hashicorp_vault
|
||||
environment:
|
||||
VAULT_TOKEN : *VAULT_TOKEN
|
||||
# We are using HTTPS
|
||||
VAULT_ADDR: https://hashicorp_vault:8200/
|
||||
# Force the server to use our own certificate
|
||||
VAULT_SKIP_VERIFY: 'false'
|
||||
VAULT_CACERT: /certificates/cert.pem
|
||||
volumes:
|
||||
- ../../../../../../../../.github/infrastructure/conformance/hashicorp:/setup:ro
|
||||
- ../certificates:/certificates:ro
|
||||
entrypoint: /setup/setup-hashicorp-vault-secrets.sh
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault-TestCaFamilyOfFields-badCaCert
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
metadata:
|
||||
- name: vaultAddr
|
||||
value: "https://127.0.0.1:8200"
|
||||
# Enforce TLS verification because this test is all about this
|
||||
- name: skipVerify
|
||||
value: false
|
||||
- name: vaultToken # Matches docker compose VAULT_DEV_ROOT_TOKEN_ID env. var.
|
||||
value: "vault-dev-root-token-id"
|
||||
- name: tlsServerName
|
||||
value: hashicorp_vault
|
||||
- name: caCert
|
||||
value: components/caFamily/certificates/incorrect-ca-cert.pem # <<<<< We should fail authentication
|
|
@ -0,0 +1,46 @@
|
|||
version: '3.9'
|
||||
|
||||
# Use a YAML reference to define VAULT_TOKEN and DOCKER_IMAGE only once
|
||||
x-common-values:
|
||||
# This should match tests/config/secrestore/hashicorp/vault/hashicorp-vault.yaml
|
||||
# This should match .github/infrastructure/conformance/hashicorp/vault_token_file.txt
|
||||
vault_token: &VAULT_TOKEN "vault-dev-root-token-id"
|
||||
# Reuse the same docker image to save on resources and because the base vault image
|
||||
# has everything we need for seeding the initial key values too.
|
||||
vault_docker_image: &VAULT_DOCKER_IMAGE vault:1.12.1
|
||||
|
||||
services:
|
||||
hashicorp_vault:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
ports:
|
||||
- '8200:8200'
|
||||
cap_add:
|
||||
- IPC_LOCK
|
||||
environment:
|
||||
# Move vault's dev-mode self-signed TLS listener to another port so we can use the default one for
|
||||
# our own listener with our own self-signed certificate.
|
||||
VAULT_DEV_LISTEN_ADDRESS: "0.0.0.0:7200"
|
||||
VAULT_DEV_ROOT_TOKEN_ID: *VAULT_TOKEN
|
||||
volumes:
|
||||
- ../vaultConfig:/vault/config/:ro
|
||||
- ../certificates:/certificates:ro
|
||||
# Force vault to use TLS/HTTPS in dev mode
|
||||
entrypoint: vault server -dev-tls -config /vault/config/vault_server.hcl
|
||||
|
||||
# We define a aux. service to seed the expected conformance secrets to vault
|
||||
seed_conformance_secrets:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
depends_on:
|
||||
- hashicorp_vault
|
||||
environment:
|
||||
VAULT_TOKEN : *VAULT_TOKEN
|
||||
# We are using HTTPS
|
||||
VAULT_ADDR: https://hashicorp_vault:8200/
|
||||
# Force the server to use our own certificate
|
||||
VAULT_SKIP_VERIFY: 'false'
|
||||
VAULT_CACERT: /certificates/cert.pem
|
||||
volumes:
|
||||
- ../../../../../../../../.github/infrastructure/conformance/hashicorp:/setup:ro
|
||||
- ../certificates:/certificates:ro
|
||||
entrypoint: /setup/setup-hashicorp-vault-secrets.sh
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault-TestCaFamilyOfFields-badCaCertAndSkipVerify
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
metadata:
|
||||
- name: vaultAddr
|
||||
value: "https://127.0.0.1:8200"
|
||||
# Enforce TLS verification because this test is all about this
|
||||
- name: skipVerify
|
||||
value: true # <<< flag under test
|
||||
- name: vaultToken # Matches docker compose VAULT_DEV_ROOT_TOKEN_ID env. var.
|
||||
value: "vault-dev-root-token-id"
|
||||
- name: tlsServerName
|
||||
value: hashicorp_vault
|
||||
- name: caCert
|
||||
value: components/caFamily/certificates/incorrect-ca-cert.pem # <<<<< We would fail authentication if it wasn't for skipVerify
|
|
@ -0,0 +1,46 @@
|
|||
version: '3.9'
|
||||
|
||||
# Use a YAML reference to define VAULT_TOKEN and DOCKER_IMAGE only once
|
||||
x-common-values:
|
||||
# This should match tests/config/secrestore/hashicorp/vault/hashicorp-vault.yaml
|
||||
# This should match .github/infrastructure/conformance/hashicorp/vault_token_file.txt
|
||||
vault_token: &VAULT_TOKEN "vault-dev-root-token-id"
|
||||
# Reuse the same docker image to save on resources and because the base vault image
|
||||
# has everything we need for seeding the initial key values too.
|
||||
vault_docker_image: &VAULT_DOCKER_IMAGE vault:1.12.1
|
||||
|
||||
services:
|
||||
hashicorp_vault:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
ports:
|
||||
- '8200:8200'
|
||||
cap_add:
|
||||
- IPC_LOCK
|
||||
environment:
|
||||
# Move vault's dev-mode self-signed TLS listener to another port so we can use the default one for
|
||||
# our own listener with our own self-signed certificate.
|
||||
VAULT_DEV_LISTEN_ADDRESS: "0.0.0.0:7200"
|
||||
VAULT_DEV_ROOT_TOKEN_ID: *VAULT_TOKEN
|
||||
volumes:
|
||||
- ../vaultConfig:/vault/config/:ro
|
||||
- ../certificates:/certificates:ro
|
||||
# Force vault to use TLS/HTTPS in dev mode
|
||||
entrypoint: vault server -dev-tls -config /vault/config/vault_server.hcl
|
||||
|
||||
# We define a aux. service to seed the expected conformance secrets to vault
|
||||
seed_conformance_secrets:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
depends_on:
|
||||
- hashicorp_vault
|
||||
environment:
|
||||
VAULT_TOKEN : *VAULT_TOKEN
|
||||
# We are using HTTPS
|
||||
VAULT_ADDR: https://hashicorp_vault:8200/
|
||||
# Force the server to use our own certificate
|
||||
VAULT_SKIP_VERIFY: 'false'
|
||||
VAULT_CACERT: /certificates/cert.pem
|
||||
volumes:
|
||||
- ../../../../../../../../.github/infrastructure/conformance/hashicorp:/setup:ro
|
||||
- ../certificates:/certificates:ro
|
||||
entrypoint: /setup/setup-hashicorp-vault-secrets.sh
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault-TestCaFamilyOfFields-badTlsServerName
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
metadata:
|
||||
- name: vaultAddr
|
||||
value: "https://127.0.0.1:8200"
|
||||
# Enforce TLS verification because this test is all about this
|
||||
- name: skipVerify
|
||||
value: false
|
||||
- name: vaultToken # Matches docker compose VAULT_DEV_ROOT_TOKEN_ID env. var.
|
||||
value: "vault-dev-root-token-id"
|
||||
- name: tlsServerName
|
||||
value: badTlsServerName # <<<<< Ooops, this won't match our cert.
|
||||
- name: caCert
|
||||
value: components/caFamily/certificates/cert.pem
|
|
@ -0,0 +1,46 @@
|
|||
version: '3.9'
|
||||
|
||||
# Use a YAML reference to define VAULT_TOKEN and DOCKER_IMAGE only once
|
||||
x-common-values:
|
||||
# This should match tests/config/secrestore/hashicorp/vault/hashicorp-vault.yaml
|
||||
# This should match .github/infrastructure/conformance/hashicorp/vault_token_file.txt
|
||||
vault_token: &VAULT_TOKEN "vault-dev-root-token-id"
|
||||
# Reuse the same docker image to save on resources and because the base vault image
|
||||
# has everything we need for seeding the initial key values too.
|
||||
vault_docker_image: &VAULT_DOCKER_IMAGE vault:1.12.1
|
||||
|
||||
services:
|
||||
hashicorp_vault:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
ports:
|
||||
- '8200:8200'
|
||||
cap_add:
|
||||
- IPC_LOCK
|
||||
environment:
|
||||
# Move vault's dev-mode self-signed TLS listener to another port so we can use the default one for
|
||||
# our own listener with our own self-signed certificate.
|
||||
VAULT_DEV_LISTEN_ADDRESS: "0.0.0.0:7200"
|
||||
VAULT_DEV_ROOT_TOKEN_ID: *VAULT_TOKEN
|
||||
volumes:
|
||||
- ../vaultConfig:/vault/config/:ro
|
||||
- ../certificates:/certificates:ro
|
||||
# Force vault to use TLS/HTTPS in dev mode
|
||||
entrypoint: vault server -dev-tls -config /vault/config/vault_server.hcl
|
||||
|
||||
# We define a aux. service to seed the expected conformance secrets to vault
|
||||
seed_conformance_secrets:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
depends_on:
|
||||
- hashicorp_vault
|
||||
environment:
|
||||
VAULT_TOKEN : *VAULT_TOKEN
|
||||
# We are using HTTPS
|
||||
VAULT_ADDR: https://hashicorp_vault:8200/
|
||||
# Force the server to use our own certificate
|
||||
VAULT_SKIP_VERIFY: 'false'
|
||||
VAULT_CACERT: /certificates/cert.pem
|
||||
volumes:
|
||||
- ../../../../../../../../.github/infrastructure/conformance/hashicorp:/setup:ro
|
||||
- ../certificates:/certificates:ro
|
||||
entrypoint: /setup/setup-hashicorp-vault-secrets.sh
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault-TestCaFamilyOfFields-caCert
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
metadata:
|
||||
- name: vaultAddr
|
||||
value: "https://127.0.0.1:8200"
|
||||
# Enforce TLS verification because this test is all about this
|
||||
- name: skipVerify
|
||||
value: false
|
||||
- name: vaultToken # Matches docker compose VAULT_DEV_ROOT_TOKEN_ID env. var.
|
||||
value: "vault-dev-root-token-id"
|
||||
- name: tlsServerName
|
||||
value: hashicorp_vault
|
||||
- name: caCert
|
||||
value: components/caFamily/certificates/cert.pem
|
|
@ -0,0 +1,46 @@
|
|||
version: '3.9'
|
||||
|
||||
# Use a YAML reference to define VAULT_TOKEN and DOCKER_IMAGE only once
|
||||
x-common-values:
|
||||
# This should match tests/config/secrestore/hashicorp/vault/hashicorp-vault.yaml
|
||||
# This should match .github/infrastructure/conformance/hashicorp/vault_token_file.txt
|
||||
vault_token: &VAULT_TOKEN "vault-dev-root-token-id"
|
||||
# Reuse the same docker image to save on resources and because the base vault image
|
||||
# has everything we need for seeding the initial key values too.
|
||||
vault_docker_image: &VAULT_DOCKER_IMAGE vault:1.12.1
|
||||
|
||||
services:
|
||||
hashicorp_vault:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
ports:
|
||||
- '8200:8200'
|
||||
cap_add:
|
||||
- IPC_LOCK
|
||||
environment:
|
||||
# Move vault's dev-mode self-signed TLS listener to another port so we can use the default one for
|
||||
# our own listener with our own self-signed certificate.
|
||||
VAULT_DEV_LISTEN_ADDRESS: "0.0.0.0:7200"
|
||||
VAULT_DEV_ROOT_TOKEN_ID: *VAULT_TOKEN
|
||||
volumes:
|
||||
- ../vaultConfig:/vault/config/:ro
|
||||
- ../certificates:/certificates:ro
|
||||
# Force vault to use TLS/HTTPS in dev mode
|
||||
entrypoint: vault server -dev-tls -config /vault/config/vault_server.hcl
|
||||
|
||||
# We define a aux. service to seed the expected conformance secrets to vault
|
||||
seed_conformance_secrets:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
depends_on:
|
||||
- hashicorp_vault
|
||||
environment:
|
||||
VAULT_TOKEN : *VAULT_TOKEN
|
||||
# We are using HTTPS
|
||||
VAULT_ADDR: https://hashicorp_vault:8200/
|
||||
# Force the server to use our own certificate
|
||||
VAULT_SKIP_VERIFY: 'false'
|
||||
VAULT_CACERT: /certificates/cert.pem
|
||||
volumes:
|
||||
- ../../../../../../../../.github/infrastructure/conformance/hashicorp:/setup:ro
|
||||
- ../certificates:/certificates:ro
|
||||
entrypoint: /setup/setup-hashicorp-vault-secrets.sh
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault-TestCaFamilyOfFields-caPath
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
metadata:
|
||||
- name: vaultAddr
|
||||
value: "https://127.0.0.1:8200"
|
||||
# Enforce TLS verification because this test is all about this
|
||||
- name: skipVerify
|
||||
value: false
|
||||
- name: vaultToken # Matches docker compose VAULT_DEV_ROOT_TOKEN_ID env. var.
|
||||
value: "vault-dev-root-token-id"
|
||||
- name: tlsServerName
|
||||
value: hashicorp_vault
|
||||
- name: caPath
|
||||
value: components/caFamily/certificates/
|
|
@ -0,0 +1,46 @@
|
|||
version: '3.9'
|
||||
|
||||
# Use a YAML reference to define VAULT_TOKEN and DOCKER_IMAGE only once
|
||||
x-common-values:
|
||||
# This should match tests/config/secrestore/hashicorp/vault/hashicorp-vault.yaml
|
||||
# This should match .github/infrastructure/conformance/hashicorp/vault_token_file.txt
|
||||
vault_token: &VAULT_TOKEN "vault-dev-root-token-id"
|
||||
# Reuse the same docker image to save on resources and because the base vault image
|
||||
# has everything we need for seeding the initial key values too.
|
||||
vault_docker_image: &VAULT_DOCKER_IMAGE vault:1.12.1
|
||||
|
||||
services:
|
||||
hashicorp_vault:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
ports:
|
||||
- '8200:8200'
|
||||
cap_add:
|
||||
- IPC_LOCK
|
||||
environment:
|
||||
# Move vault's dev-mode self-signed TLS listener to another port so we can use the default one for
|
||||
# our own listener with our own self-signed certificate.
|
||||
VAULT_DEV_LISTEN_ADDRESS: "0.0.0.0:7200"
|
||||
VAULT_DEV_ROOT_TOKEN_ID: *VAULT_TOKEN
|
||||
volumes:
|
||||
- ../vaultConfig:/vault/config/:ro
|
||||
- ../certificates:/certificates:ro
|
||||
# Force vault to use TLS/HTTPS in dev mode
|
||||
entrypoint: vault server -dev-tls -config /vault/config/vault_server.hcl
|
||||
|
||||
# We define a aux. service to seed the expected conformance secrets to vault
|
||||
seed_conformance_secrets:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
depends_on:
|
||||
- hashicorp_vault
|
||||
environment:
|
||||
VAULT_TOKEN : *VAULT_TOKEN
|
||||
# We are using HTTPS
|
||||
VAULT_ADDR: https://hashicorp_vault:8200/
|
||||
# Force the server to use our own certificate
|
||||
VAULT_SKIP_VERIFY: 'false'
|
||||
VAULT_CACERT: /certificates/cert.pem
|
||||
volumes:
|
||||
- ../../../../../../../../.github/infrastructure/conformance/hashicorp:/setup:ro
|
||||
- ../certificates:/certificates:ro
|
||||
entrypoint: /setup/setup-hashicorp-vault-secrets.sh
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault-TestCaFamilyOfFields-caPem
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
metadata:
|
||||
- name: vaultAddr
|
||||
value: "https://127.0.0.1:8200"
|
||||
# Enforce TLS verification because this test is all about this
|
||||
- name: skipVerify
|
||||
value: false
|
||||
- name: vaultToken # Matches docker compose VAULT_DEV_ROOT_TOKEN_ID env. var.
|
||||
value: "vault-dev-root-token-id"
|
||||
- name: tlsServerName
|
||||
value: hashicorp_vault
|
||||
- name: caPem
|
||||
value: |-
|
|
@ -0,0 +1,6 @@
|
|||
listener "tcp" {
|
||||
address = "0.0.0.0:8200"
|
||||
tls_disable = "false"
|
||||
tls_cert_file = "/certificates/cert.pem"
|
||||
tls_key_file = "/certificates/key.pem"
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
version: '3.9'
|
||||
|
||||
# Use a YAML reference to define VAULT_TOKEN and DOCKER_IMAGE only once
|
||||
x-common-values:
|
||||
# This should match tests/config/secrestore/hashicorp/vault/hashicorp-vault.yaml
|
||||
# This should match .github/infrastructure/conformance/hashicorp/vault_token_file.txt
|
||||
vault_token: &VAULT_TOKEN "vault-dev-root-token-id"
|
||||
# Reuse the same docker image to save on resources and because the base vault image
|
||||
# has everything we need for seeding the initial key values too.
|
||||
vault_docker_image: &VAULT_DOCKER_IMAGE vault:1.12.1
|
||||
|
||||
services:
|
||||
hashicorp_vault:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
ports:
|
||||
- '8200:8200'
|
||||
cap_add:
|
||||
- IPC_LOCK
|
||||
environment:
|
||||
VAULT_DEV_LISTEN_ADDRESS: "0.0.0.0:8200"
|
||||
VAULT_DEV_ROOT_TOKEN_ID: *VAULT_TOKEN
|
||||
|
||||
|
||||
# We define a aux. service to seed the expected conformance secrets to vault
|
||||
seed_conformance_secrets:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
depends_on:
|
||||
- hashicorp_vault
|
||||
environment:
|
||||
VAULT_TOKEN : *VAULT_TOKEN
|
||||
VAULT_ADDR: http://hashicorp_vault:8200/
|
||||
volumes:
|
||||
- .:/setup:ro
|
||||
entrypoint: /setup/setup-hashicorp-vault-secrets.sh # <<< Use our custom secret seeder for the enginePath/customSecretsPath
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault-TestEnginePath-customSecretsPath
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
metadata:
|
||||
- name: vaultAddr
|
||||
value: "http://127.0.0.1:8200"
|
||||
- name: vaultToken # Matches docker compose VAULT_DEV_ROOT_TOKEN_ID env. var.
|
||||
value: "vault-dev-root-token-id"
|
||||
- name: enginePath
|
||||
value: customSecretsPath
|
|
@ -0,0 +1,31 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Notice that while hashicorp supports multiple keys in a secret,
|
||||
# our confirmance tests needs to go for the common demominator
|
||||
# which is a secret store that only has name/value semantic.
|
||||
# Hence we setup secret containing a single key with the their
|
||||
# same name.
|
||||
|
||||
set -eu
|
||||
|
||||
MAX_ATTEMPTS=30
|
||||
|
||||
for attempt in `seq $MAX_ATTEMPTS`; do
|
||||
# Test connectivity to vault server and create secrets to match
|
||||
# conformance tests / contents from tests/conformance/secrets.json
|
||||
if vault status &&
|
||||
vault secrets enable -path=customSecretsPath kv-v2 && # Enable this path with kv/version2 engine
|
||||
vault kv put customSecretsPath/dapr/secretUnderCustomPath the=trick was=the path=parameter &&
|
||||
vault kv get customSecretsPath/dapr/secretUnderCustomPath ;
|
||||
then
|
||||
echo ✅ secrets set;
|
||||
sleep 1;
|
||||
exit 0;
|
||||
else
|
||||
echo "⏰ vault not available, waiting... - attempt $attempt of $MAX_ATTEMPTS";
|
||||
sleep 1;
|
||||
fi
|
||||
done;
|
||||
|
||||
echo ❌ Failed to set secrets;
|
||||
exit 1
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault-TestEnginePath-secret
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
metadata:
|
||||
- name: vaultAddr
|
||||
value: "http://127.0.0.1:8200"
|
||||
- name: vaultToken # Matches docker compose VAULT_DEV_ROOT_TOKEN_ID env. var.
|
||||
value: "vault-dev-root-token-id"
|
||||
- name: enginePath
|
||||
value: secret
|
|
@ -0,0 +1,38 @@
|
|||
version: '3.9'
|
||||
|
||||
# Use a YAML reference to define VAULT_TOKEN and DOCKER_IMAGE only once
|
||||
x-common-values:
|
||||
# This should match tests/config/secrestore/hashicorp/vault/hashicorp-vault.yaml
|
||||
# This should match .github/infrastructure/conformance/hashicorp/vault_token_file.txt
|
||||
vault_token: &VAULT_TOKEN "vault-dev-root-token-id"
|
||||
# Reuse the same docker image to save on resources and because the base vault image
|
||||
# has everything we need for seeding the initial key values too.
|
||||
vault_docker_image: &VAULT_DOCKER_IMAGE vault:1.12.1
|
||||
|
||||
services:
|
||||
hashicorp_vault:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
ports:
|
||||
- '8200:8200'
|
||||
cap_add:
|
||||
- IPC_LOCK
|
||||
environment:
|
||||
VAULT_DEV_LISTEN_ADDRESS: "0.0.0.0:8200"
|
||||
VAULT_DEV_ROOT_TOKEN_ID: *VAULT_TOKEN
|
||||
# Force vault to use TLS/HTTPS in dev mode
|
||||
entrypoint: vault server -dev-tls=true
|
||||
|
||||
# We define a aux. service to seed the expected conformance secrets to vault
|
||||
seed_conformance_secrets:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
depends_on:
|
||||
- hashicorp_vault
|
||||
environment:
|
||||
VAULT_TOKEN : *VAULT_TOKEN
|
||||
# We are using HTTPS
|
||||
VAULT_ADDR: https://hashicorp_vault:8200/
|
||||
VAULT_SKIP_VERIFY: 'true'
|
||||
volumes:
|
||||
- ../../../../../../../../.github/infrastructure/conformance/hashicorp:/setup:ro
|
||||
entrypoint: /setup/setup-hashicorp-vault-secrets.sh
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault-TestVaultAddr-missing
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
metadata:
|
||||
# Yes, we commented out vaultAddr.
|
||||
# Default value should kick in: https://127.0.0.1:8200. Notice: HTTPS (TLS)
|
||||
# - name: vaultAddr
|
||||
# value: "http://127.0.0.1:8200"
|
||||
# Do not perform TLS verification since e are starting vault in development
|
||||
# mode with a self-signed certificate
|
||||
- name: skipVerify
|
||||
value: true
|
||||
- name: vaultToken # Matches docker compose VAULT_DEV_ROOT_TOKEN_ID env. var.
|
||||
value: "vault-dev-root-token-id"
|
|
@ -0,0 +1,38 @@
|
|||
version: '3.9'
|
||||
|
||||
# Use a YAML reference to define VAULT_TOKEN and DOCKER_IMAGE only once
|
||||
x-common-values:
|
||||
# This should match tests/config/secrestore/hashicorp/vault/hashicorp-vault.yaml
|
||||
# This should match .github/infrastructure/conformance/hashicorp/vault_token_file.txt
|
||||
vault_token: &VAULT_TOKEN "vault-dev-root-token-id"
|
||||
# Reuse the same docker image to save on resources and because the base vault image
|
||||
# has everything we need for seeding the initial key values too.
|
||||
vault_docker_image: &VAULT_DOCKER_IMAGE vault:1.12.1
|
||||
|
||||
services:
|
||||
hashicorp_vault:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
ports:
|
||||
- '8200:8200'
|
||||
cap_add:
|
||||
- IPC_LOCK
|
||||
environment:
|
||||
VAULT_DEV_LISTEN_ADDRESS: "0.0.0.0:8200"
|
||||
VAULT_DEV_ROOT_TOKEN_ID: *VAULT_TOKEN
|
||||
# Force vault to use TLS/HTTPS in dev mode
|
||||
entrypoint: vault server -dev-tls=true
|
||||
|
||||
# We define a aux. service to seed the expected conformance secrets to vault
|
||||
seed_conformance_secrets:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
depends_on:
|
||||
- hashicorp_vault
|
||||
environment:
|
||||
VAULT_TOKEN : *VAULT_TOKEN
|
||||
# We are using HTTPS
|
||||
VAULT_ADDR: https://hashicorp_vault:8200/
|
||||
VAULT_SKIP_VERIFY: 'true'
|
||||
volumes:
|
||||
- ../../../../../../../../.github/infrastructure/conformance/hashicorp:/setup:ro
|
||||
entrypoint: /setup/setup-hashicorp-vault-secrets.sh
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault-TestVaultAddr-missingSkipVerifyFalse
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
metadata:
|
||||
# Yes, we commented out vaultAddr.
|
||||
# Default value should kick in: https://127.0.0.1:8200. Notice: HTTPS (TLS)
|
||||
# - name: vaultAddr
|
||||
# value: "http://127.0.0.1:8200"
|
||||
# Do not perform TLS verification since e are starting vault in development
|
||||
# mode with a self-signed certificate
|
||||
- name: skipVerify
|
||||
value: false
|
||||
- name: vaultToken # Matches docker compose VAULT_DEV_ROOT_TOKEN_ID env. var.
|
||||
value: "vault-dev-root-token-id"
|
|
@ -0,0 +1,36 @@
|
|||
version: '3.9'
|
||||
|
||||
# Use a YAML reference to define VAULT_TOKEN and DOCKER_IMAGE only once
|
||||
x-common-values:
|
||||
# This should match tests/config/secrestore/hashicorp/vault/hashicorp-vault.yaml
|
||||
# This should match .github/infrastructure/conformance/hashicorp/vault_token_file.txt
|
||||
vault_token: &VAULT_TOKEN "vault-dev-root-token-id"
|
||||
# Reuse the same docker image to save on resources and because the base vault image
|
||||
# has everything we need for seeding the initial key values too.
|
||||
vault_docker_image: &VAULT_DOCKER_IMAGE vault:1.12.1
|
||||
|
||||
services:
|
||||
hashicorp_vault:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
ports:
|
||||
- '11200:11200'
|
||||
cap_add:
|
||||
- IPC_LOCK
|
||||
environment:
|
||||
VAULT_DEV_LISTEN_ADDRESS: "0.0.0.0:11200"
|
||||
VAULT_DEV_ROOT_TOKEN_ID: *VAULT_TOKEN
|
||||
|
||||
# We define a aux. service to seed the expected conformance secrets to vault
|
||||
seed_conformance_secrets:
|
||||
image: *VAULT_DOCKER_IMAGE
|
||||
depends_on:
|
||||
- hashicorp_vault
|
||||
environment:
|
||||
VAULT_TOKEN : *VAULT_TOKEN
|
||||
# We are using HTTPS
|
||||
VAULT_ADDR: http://hashicorp_vault:11200/
|
||||
VAULT_SKIP_VERIFY: 'true'
|
||||
volumes:
|
||||
- ../../../../../../../../.github/infrastructure/conformance/hashicorp:/setup:ro
|
||||
entrypoint: /setup/setup-hashicorp-vault-secrets.sh
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault-TestVaultAddr-nonStdPort
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
metadata:
|
||||
# Use non-standard port
|
||||
- name: vaultAddr
|
||||
value: "http://127.0.0.1:11200"
|
||||
- name: vaultToken # Matches docker compose VAULT_DEV_ROOT_TOKEN_ID env. var.
|
||||
value: "vault-dev-root-token-id"
|
|
@ -0,0 +1,13 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault-TestVaultAddr-wrongAddress
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
metadata:
|
||||
- name: vaultAddr
|
||||
value: "http://127.0.0.1:28234" # yeah.. let's hope no one starts a Vault here
|
||||
- name: vaultToken # Matches docker compose VAULT_DEV_ROOT_TOKEN_ID env. var.
|
||||
value: "vault-dev-root-token-id"
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
metadata:
|
||||
- name: vaultAddr
|
||||
value: "http://127.0.0.1:8200"
|
||||
- name: vaultToken # Matches docker compose VAULT_DEV_ROOT_TOKEN_ID env. var.
|
||||
value: "vault-dev-root-token-id"
|
||||
- name: vaultKVPrefix
|
||||
value: alternativePrefix
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
metadata:
|
||||
- name: vaultAddr
|
||||
value: "http://127.0.0.1:8200"
|
||||
- name: vaultToken # Matches docker compose VAULT_DEV_ROOT_TOKEN_ID env. var.
|
||||
value: "vault-dev-root-token-id"
|
||||
- name: vaultKVUsePrefix
|
||||
value: false
|
|
@ -0,0 +1,14 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault-TestTokenAndTokenMountPath-badVaultToken
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
ignoreErrors: true # This component will fail to load but we don't want Dapr to FATAL because of it.
|
||||
metadata:
|
||||
- name: vaultAddr
|
||||
value: "http://127.0.0.1:8200"
|
||||
- name: vaultToken
|
||||
value: "this-is-not-the-correct-vault-token-to-talk-to-our-server"
|
|
@ -0,0 +1,16 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault-TestTokenAndTokenMountPath-both
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
ignoreErrors: true # This component will fail to load but we don't want Dapr to FATAL because of it.
|
||||
metadata:
|
||||
- name: vaultAddr
|
||||
value: "http://127.0.0.1:8200"
|
||||
- name: vaultToken
|
||||
value: "vault-dev-root-token-id"
|
||||
- name: vaultTokenMountPath
|
||||
value: /tmp/vaultToken.txt
|
|
@ -0,0 +1,13 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault-TestTokenAndTokenMountPath-neither
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
ignoreErrors: true # This component will fail to load but we don't want Dapr to FATAL because of it.
|
||||
metadata:
|
||||
- name: vaultAddr
|
||||
value: "http://127.0.0.1:8200"
|
||||
# no vaultToken or vaultTokenMountPath
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault-TestTokenAndTokenMountPath-tokenMountPathHappyCase
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
ignoreErrors: true # This component will fail to load but we don't want Dapr to FATAL because of it.
|
||||
metadata:
|
||||
- name: vaultAddr
|
||||
value: "http://127.0.0.1:8200"
|
||||
- name: vaultTokenMountPath
|
||||
# This is a long path but this file is in current directory ;)
|
||||
value: components/vaultTokenAndTokenMountPath/tokenMountPathHappyCase/vault_token_file.txt
|
|
@ -0,0 +1 @@
|
|||
vault-dev-root-token-id
|
|
@ -0,0 +1,14 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault-TestTokenAndTokenMountPath-tokenMountPathPointsToBrokenPath
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
ignoreErrors: true # This component will fail to load but we don't want Dapr to FATAL because of it.
|
||||
metadata:
|
||||
- name: vaultAddr
|
||||
value: "http://127.0.0.1:8200"
|
||||
- name: vaultTokenMountPath
|
||||
value: /this/path/does/not/exist/vaultToken.txt
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: my-hashicorp-vault
|
||||
namespace: default
|
||||
spec:
|
||||
type: secretstores.hashicorp.vault
|
||||
version: v1
|
||||
metadata:
|
||||
- name: vaultAddr
|
||||
value: "http://127.0.0.1:8200"
|
||||
- name: vaultToken # Matches docker compose VAULT_DEV_ROOT_TOKEN_ID env. var.
|
||||
value: "vault-dev-root-token-id"
|
||||
- name: vaultValueType
|
||||
value: text # Turns Vault into a secret store with name/value semantics.
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue