Merge branch 'master' into deps

This commit is contained in:
Bernd Verst 2023-01-23 15:34:22 -08:00 committed by GitHub
commit a53102ed04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 1860 additions and 2064 deletions

View File

@ -34,6 +34,15 @@ resource iotHub 'Microsoft.Devices/IotHubs@2021-03-31' = {
partitionCount: 2
}
}
routing: {
fallbackRoute: {
name: '$fallback'
source: 'DeviceMessages'
condition: 'true'
endpointNames: ['events']
isEnabled: true
}
}
}
}

View File

@ -228,7 +228,7 @@ STORAGE_CONTAINER_VAR_NAME="AzureBlobStorageContainer"
STORAGE_QUEUE_VAR_NAME="AzureBlobStorageQueue"
# Derived variables
ADMIN_ID="$(az ad user list --filter "userPrincipalName eq '${ADMIN_UPN}'" --query "[].objectId" --output tsv)"
ADMIN_ID="$(az ad user list --filter "userPrincipalName eq '${ADMIN_UPN}'" --query "[].id" --output tsv)"
if [[ -z "${ADMIN_ID}" ]]; then
echo "Could not find user with upn ${ADMIN_UPN}"
exit 1
@ -245,26 +245,26 @@ az config set extension.use_dynamic_install=yes_without_prompt
# Create Service Principals for use with the conformance tests
CERT_AUTH_SP_NAME="${PREFIX}-akv-conf-test-sp"
az ad sp create-for-rbac --name "${CERT_AUTH_SP_NAME}" --skip-assignment --years 1
CERT_AUTH_SP_ID="$(az ad sp list --display-name "${CERT_AUTH_SP_NAME}" --query "[].objectId" --output tsv)"
az ad sp create-for-rbac --name "${CERT_AUTH_SP_NAME}" --years 1
CERT_AUTH_SP_ID="$(az ad sp list --display-name "${CERT_AUTH_SP_NAME}" --query "[].id" --output tsv)"
echo "Created Service Principal for cert auth: ${CERT_AUTH_SP_NAME}"
if [[ -n ${CREDENTIALS_PATH} ]]; then
SDK_AUTH_SP_INFO="$(cat ${CREDENTIALS_PATH})"
SDK_AUTH_SP_APPID="$(echo "${SDK_AUTH_SP_INFO}" | grep 'clientId' | sed -E 's/(.*clientId\"\: \")|\",//g')"
SDK_AUTH_SP_CLIENT_SECRET="$(echo "${SDK_AUTH_SP_INFO}" | grep 'clientSecret' | sed -E 's/(.*clientSecret\"\: \")|\",//g')"
SDK_AUTH_SP_APPID="$(echo "${SDK_AUTH_SP_INFO}" | jq -r '.clientId')"
SDK_AUTH_SP_CLIENT_SECRET="$(echo "${SDK_AUTH_SP_INFO}" | jq -r '.clientSecret')"
if [[ -z ${SDK_AUTH_SP_APPID} || -z ${SDK_AUTH_SP_CLIENT_SECRET} ]]; then
echo "Invalid credentials JSON file. Contents should match output of 'az ad sp create-for-rbac' command."
exit 1
fi
SDK_AUTH_SP_NAME="$(az ad sp show --id "${SDK_AUTH_SP_APPID}" --query "appDisplayName" --output tsv)"
SDK_AUTH_SP_ID="$(az ad sp show --id "${SDK_AUTH_SP_APPID}" --query "objectId" --output tsv)"
SDK_AUTH_SP_ID="$(az ad sp show --id "${SDK_AUTH_SP_APPID}" --query "id" --output tsv)"
echo "Using Service Principal from ${CREDENTIALS_PATH} for SDK Auth: ${SDK_AUTH_SP_NAME}"
else
SDK_AUTH_SP_NAME="${PREFIX}-conf-test-runner-sp"
SDK_AUTH_SP_INFO="$(az ad sp create-for-rbac --name "${SDK_AUTH_SP_NAME}" --sdk-auth --skip-assignment --years 1)"
SDK_AUTH_SP_CLIENT_SECRET="$(echo "${SDK_AUTH_SP_INFO}" | grep 'clientSecret' | sed -E 's/(.*clientSecret\"\: \")|\".*//g')"
SDK_AUTH_SP_ID="$(az ad sp list --display-name "${SDK_AUTH_SP_NAME}" --query "[].objectId" --output tsv)"
SDK_AUTH_SP_INFO="$(az ad sp create-for-rbac --name "${SDK_AUTH_SP_NAME}" --sdk-auth --years 1)"
SDK_AUTH_SP_CLIENT_SECRET="$(echo "${SDK_AUTH_SP_INFO}" | jq -r '.clientSecret')"
SDK_AUTH_SP_ID="$(az ad sp list --display-name "${SDK_AUTH_SP_NAME}" --query "[].id" --output tsv)"
echo "${SDK_AUTH_SP_INFO}"
echo "Created Service Principal for SDK Auth: ${SDK_AUTH_SP_NAME}"
AZURE_CREDENTIALS_FILENAME="${OUTPUT_PATH}/AZURE_CREDENTIALS"
@ -378,15 +378,15 @@ az keyvault set-policy --name "${KEYVAULT_NAME}" -g "${RESOURCE_GROUP_NAME}" --s
# Creating service principal for service principal authentication with KeyVault
AKV_SPAUTH_SP_NAME="${PREFIX}-akv-spauth-conf-test-sp"
echo "Creating service principal ${AKV_SPAUTH_SP_NAME} for use with KeyVault ${KEYVAULT_NAME}"
{ read AKV_SPAUTH_SP_CLIENT_ID ; read AKV_SPAUTH_SP_CLIENT_SECRET ; } < <(az ad sp create-for-rbac --name ${AKV_SPAUTH_SP_NAME} --skip-assignment --years 1 --query "[appId,password]" -otsv)
{ read AKV_SPAUTH_SP_CLIENT_ID ; read AKV_SPAUTH_SP_CLIENT_SECRET ; } < <(az ad sp create-for-rbac --name ${AKV_SPAUTH_SP_NAME} --years 1 --query "[appId,password]" -otsv)
# Give the service principal read access to the KeyVault Secrets
AKV_SPAUTH_SP_OBJECTID="$(az ad sp show --id ${AKV_SPAUTH_SP_CLIENT_ID} --query objectId -otsv)"
AKV_SPAUTH_SP_OBJECTID="$(az ad sp show --id ${AKV_SPAUTH_SP_CLIENT_ID} --query id -otsv)"
az keyvault set-policy --name "${KEYVAULT_NAME}" -g "${RESOURCE_GROUP_NAME}" --secret-permissions get list --object-id "${AKV_SPAUTH_SP_OBJECTID}"
# Update service principal credentials and roles for created resources
echo "Creating ${CERT_AUTH_SP_NAME} certificate ..."
az ad sp credential reset --name "${CERT_AUTH_SP_NAME}" --create-cert --cert "${KEYVAULT_CERT_NAME}" --keyvault "${KEYVAULT_NAME}"
az ad sp credential reset --id "${CERT_AUTH_SP_ID}" --create-cert --cert "${KEYVAULT_CERT_NAME}" --keyvault "${KEYVAULT_NAME}"
# Add an EventGrid role to the SDK auth Service Principal so that it can be reused for the EventGrid binding conformance tests.
EVENT_GRID_SCOPE="/subscriptions/${SUB_ID}/resourceGroups/${RESOURCE_GROUP_NAME}/providers/Microsoft.EventGrid/topics/${EVENT_GRID_TOPIC_NAME}"
@ -499,7 +499,7 @@ echo export ${KEYVAULT_CERT_NAME}=\"${KEYVAULT_CERT_FILE}\" >> "${ENV_CONFIG_FIL
echo export ${KEYVAULT_NAME_VAR_NAME}=\"${KEYVAULT_NAME}\" >> "${ENV_CONFIG_FILENAME}"
az keyvault secret set --name "${KEYVAULT_NAME_VAR_NAME}" --vault-name "${KEYVAULT_NAME}" --value "${KEYVAULT_NAME}"
KEYVAULT_TENANT_ID="$(az ad sp list --display-name "${CERT_AUTH_SP_NAME}" --query "[].appOwnerTenantId" --output tsv)"
KEYVAULT_TENANT_ID="$(az ad sp list --display-name "${CERT_AUTH_SP_NAME}" --query "[].appOwnerOrganizationId" --output tsv)"
echo export ${KEYVAULT_TENANT_ID_VAR_NAME}=\"${KEYVAULT_TENANT_ID}\" >> "${ENV_CONFIG_FILENAME}"
az keyvault secret set --name "${KEYVAULT_TENANT_ID_VAR_NAME}" --vault-name "${KEYVAULT_NAME}" --value "${KEYVAULT_TENANT_ID}"
@ -716,8 +716,8 @@ az keyvault secret set --name "${IOT_HUB_PUBSUB_CONSUMER_GROUP_VAR_NAME}" --vaul
# CERTIFICATION TESTS: Create service principal and grant resource access
# ------------------------------------------------------------------------
CERTIFICATION_SPAUTH_SP_NAME="${PREFIX}-certification-spauth-conf-test-sp"
{ read CERTIFICATION_SPAUTH_SP_CLIENT_ID ; read CERTIFICATION_SPAUTH_SP_CLIENT_SECRET ; } < <(az ad sp create-for-rbac --name ${CERTIFICATION_SPAUTH_SP_NAME} --skip-assignment --years 1 --query "[appId,password]" -otsv)
CERTIFICATION_SPAUTH_SP_PRINCIPAL_ID="$(az ad sp list --display-name "${CERTIFICATION_SPAUTH_SP_NAME}" --query "[].objectId" --output tsv)"
{ read CERTIFICATION_SPAUTH_SP_CLIENT_ID ; read CERTIFICATION_SPAUTH_SP_CLIENT_SECRET ; } < <(az ad sp create-for-rbac --name ${CERTIFICATION_SPAUTH_SP_NAME} --years 1 --query "[appId,password]" -otsv)
CERTIFICATION_SPAUTH_SP_PRINCIPAL_ID="$(az ad sp list --display-name "${CERTIFICATION_SPAUTH_SP_NAME}" --query "[].id" --output tsv)"
# Give the service principal used for certification test access to the relevant data plane resources
# Cosmos DB
@ -735,7 +735,7 @@ ASB_ID=$(az servicebus namespace show --resource-group "${RESOURCE_GROUP_NAME}"
az role assignment create --assignee "${CERTIFICATION_SPAUTH_SP_PRINCIPAL_ID}" --role "Azure Service Bus Data Owner" --scope "${ASB_ID}"
# Now export the service principal information
CERTIFICATION_TENANT_ID="$(az ad sp list --display-name "${CERTIFICATION_SPAUTH_SP_NAME}" --query "[].appOwnerTenantId" --output tsv)"
CERTIFICATION_TENANT_ID="$(az ad sp list --display-name "${CERTIFICATION_SPAUTH_SP_NAME}" --query "[].appOwnerOrganizationId" --output tsv)"
echo export ${CERTIFICATION_SERVICE_PRINCIPAL_CLIENT_ID_VAR_NAME}=\"${CERTIFICATION_SPAUTH_SP_CLIENT_ID}\" >> "${ENV_CONFIG_FILENAME}"
az keyvault secret set --name "${CERTIFICATION_SERVICE_PRINCIPAL_CLIENT_ID_VAR_NAME}" --vault-name "${KEYVAULT_NAME}" --value "${CERTIFICATION_SPAUTH_SP_CLIENT_ID}"
echo export ${CERTIFICATION_SERVICE_PRINCIPAL_CLIENT_SECRET_VAR_NAME}=\"${CERTIFICATION_SPAUTH_SP_CLIENT_SECRET}\" >> "${ENV_CONFIG_FILENAME}"

View File

@ -134,7 +134,7 @@ jobs:
- name: Create PR comment
if: env.PR_NUMBER != ''
uses: artursouza/sticky-pull-request-comment@v2.2.0
uses: artursouza/sticky-pull-request-comment@da9e86aa2a80e4ae3b854d251add33bd6baabcba
with:
header: ${{ github.run_id }}
number: ${{ env.PR_NUMBER }}
@ -575,7 +575,7 @@ jobs:
- name: Replace PR comment
if: always() && env.PR_NUMBER != ''
uses: artursouza/sticky-pull-request-comment@v2.2.0
uses: artursouza/sticky-pull-request-comment@da9e86aa2a80e4ae3b854d251add33bd6baabcba
with:
header: ${{ github.run_id }}
number: ${{ env.PR_NUMBER }}

View File

@ -162,7 +162,7 @@ jobs:
- name: Create PR comment
if: env.PR_NUMBER != ''
uses: artursouza/sticky-pull-request-comment@v2.2.0
uses: artursouza/sticky-pull-request-comment@da9e86aa2a80e4ae3b854d251add33bd6baabcba
with:
header: ${{ github.run_id }}
number: ${{ env.PR_NUMBER }}
@ -773,7 +773,7 @@ jobs:
- name: Replace PR comment
if: env.PR_NUMBER != ''
uses: artursouza/sticky-pull-request-comment@v2.2.0
uses: artursouza/sticky-pull-request-comment@da9e86aa2a80e4ae3b854d251add33bd6baabcba
with:
header: ${{ github.run_id }}
number: ${{ env.PR_NUMBER }}

View File

@ -15,432 +15,80 @@ package eventhubs
import (
"context"
"errors"
"fmt"
"strconv"
"strings"
"time"
"github.com/Azure/azure-amqp-common-go/v4/aad"
"github.com/Azure/azure-amqp-common-go/v4/conn"
eventhub "github.com/Azure/azure-event-hubs-go/v3"
"github.com/Azure/azure-event-hubs-go/v3/eph"
"github.com/Azure/azure-event-hubs-go/v3/storage"
"github.com/Azure/azure-storage-blob-go/azblob"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs"
"github.com/dapr/components-contrib/bindings"
azauth "github.com/dapr/components-contrib/internal/authentication/azure"
impl "github.com/dapr/components-contrib/internal/component/azure/eventhubs"
"github.com/dapr/kit/logger"
"github.com/dapr/kit/ptr"
)
const (
// metadata.
connectionString = "connectionString"
// required by subscriber.
consumerGroup = "consumerGroup"
storageAccountName = "storageAccountName"
storageAccountKey = "storageAccountKey"
storageContainerName = "storageContainerName"
// optional.
partitionKeyName = "partitionKey"
partitionIDName = "partitionID"
hubName = "eventHub"
hubNamespaceName = "eventHubNamespace"
// errors.
hubConnectionInitErrorMsg = "error: creating eventHub hub client"
invalidConnectionStringErrorMsg = "error: connectionString is invalid"
missingHubNamespaceErrorMsg = "error: connectionString or eventHubNamespace is required"
missingHubNameErrorMsg = "error: connectionString or eventHub is required"
missingStorageAccountNameErrorMsg = "error: storageAccountName is a required attribute"
missingStorageAccountKeyErrorMsg = "error: storageAccountKey is a required attribute when connectionString is provided"
missingStorageContainerNameErrorMsg = "error: storageContainerName is a required attribute"
missingConsumerGroupErrorMsg = "error: consumerGroup is a required attribute"
// Event Hubs SystemProperties names for metadata passthrough.
sysPropSequenceNumber = "x-opt-sequence-number"
sysPropEnqueuedTime = "x-opt-enqueued-time"
sysPropOffset = "x-opt-offset"
sysPropPartitionID = "x-opt-partition-id"
sysPropPartitionKey = "x-opt-partition-key"
sysPropIotHubDeviceConnectionID = "iothub-connection-device-id"
sysPropIotHubAuthGenerationID = "iothub-connection-auth-generation-id"
sysPropIotHubConnectionAuthMethod = "iothub-connection-auth-method"
sysPropIotHubConnectionModuleID = "iothub-connection-module-id"
sysPropIotHubEnqueuedTime = "iothub-enqueuedtime"
sysPropMessageID = "message-id"
)
func readHandler(ctx context.Context, e *eventhub.Event, handler bindings.Handler) error {
res := bindings.ReadResponse{Data: e.Data, Metadata: map[string]string{}}
if e.SystemProperties.SequenceNumber != nil {
res.Metadata[sysPropSequenceNumber] = strconv.FormatInt(*e.SystemProperties.SequenceNumber, 10)
}
if e.SystemProperties.EnqueuedTime != nil {
res.Metadata[sysPropEnqueuedTime] = e.SystemProperties.EnqueuedTime.Format(time.RFC3339)
}
if e.SystemProperties.Offset != nil {
res.Metadata[sysPropOffset] = strconv.FormatInt(*e.SystemProperties.Offset, 10)
}
// According to azure-event-hubs-go docs, this will always be nil.
if e.SystemProperties.PartitionID != nil {
res.Metadata[sysPropPartitionID] = strconv.Itoa(int(*e.SystemProperties.PartitionID))
}
// The following metadata properties are only present if event was generated by Azure IoT Hub.
if e.SystemProperties.PartitionKey != nil {
res.Metadata[sysPropPartitionKey] = *e.SystemProperties.PartitionKey
}
if e.SystemProperties.IoTHubDeviceConnectionID != nil {
res.Metadata[sysPropIotHubDeviceConnectionID] = *e.SystemProperties.IoTHubDeviceConnectionID
}
if e.SystemProperties.IoTHubAuthGenerationID != nil {
res.Metadata[sysPropIotHubAuthGenerationID] = *e.SystemProperties.IoTHubAuthGenerationID
}
if e.SystemProperties.IoTHubConnectionAuthMethod != nil {
res.Metadata[sysPropIotHubConnectionAuthMethod] = *e.SystemProperties.IoTHubConnectionAuthMethod
}
if e.SystemProperties.IoTHubConnectionModuleID != nil {
res.Metadata[sysPropIotHubConnectionModuleID] = *e.SystemProperties.IoTHubConnectionModuleID
}
if e.SystemProperties.IoTHubEnqueuedTime != nil {
res.Metadata[sysPropIotHubEnqueuedTime] = e.SystemProperties.IoTHubEnqueuedTime.Format(time.RFC3339)
}
// azure-event-hubs-go SDK pulls out the AMQP message-id property to the Event.ID property, map it from there.
if e.ID != "" {
res.Metadata[sysPropMessageID] = e.ID
}
_, err := handler(ctx, &res)
return err
}
// AzureEventHubs allows sending/receiving Azure Event Hubs events.
type AzureEventHubs struct {
hub *eventhub.Hub
metadata *azureEventHubsMetadata
eventHubSettings azauth.EnvironmentSettings
tokenProvider *aad.TokenProvider
storageCredential azblob.Credential
azureEnvironment *azure.Environment
logger logger.Logger
userAgent string
}
type azureEventHubsMetadata struct {
connectionString string
consumerGroup string
storageAccountName string
storageAccountKey string
storageContainerName string
partitionID string
partitionKey string
eventHubName string
eventHubNamespaceName string
}
func (m azureEventHubsMetadata) partitioned() bool {
return m.partitionID != ""
*impl.AzureEventHubs
}
// NewAzureEventHubs returns a new Azure Event hubs instance.
func NewAzureEventHubs(logger logger.Logger) bindings.InputOutputBinding {
return &AzureEventHubs{logger: logger}
}
func validate(connectionString string) error {
_, err := conn.ParsedConnectionFromStr(connectionString)
return err
}
func (a *AzureEventHubs) getStoragePrefixString() (string, error) {
hubName, err := a.validateAndGetHubName()
if err != nil {
return "", err
return &AzureEventHubs{
AzureEventHubs: impl.NewAzureEventHubs(logger, true),
}
// empty string in the end of slice to have a suffix "-".
return strings.Join([]string{"dapr", hubName, a.metadata.consumerGroup, ""}, "-"), nil
}
func (a *AzureEventHubs) validateAndGetHubName() (string, error) {
hubName := a.metadata.eventHubName
if hubName == "" {
parsed, err := conn.ParsedConnectionFromStr(a.metadata.connectionString)
if err != nil {
return "", err
}
hubName = parsed.HubName
}
return hubName, nil
}
// Init performs metadata init.
func (a *AzureEventHubs) Init(metadata bindings.Metadata) error {
m, err := parseMetadata(metadata)
if err != nil {
return err
}
a.userAgent = "dapr-" + logger.DaprVersion
a.metadata = m
if a.metadata.connectionString != "" {
// Validate connectionString.
validateErr := validate(a.metadata.connectionString)
if validateErr != nil {
return errors.New(invalidConnectionStringErrorMsg)
}
var hub *eventhub.Hub
// Create partitioned sender if the partitionID is configured.
if a.metadata.partitioned() {
hub, err = eventhub.NewHubFromConnectionString(a.metadata.connectionString,
eventhub.HubWithPartitionedSender(a.metadata.partitionID),
eventhub.HubWithUserAgent(a.userAgent),
)
} else {
hub, err = eventhub.NewHubFromConnectionString(a.metadata.connectionString,
eventhub.HubWithUserAgent(a.userAgent),
)
}
if err != nil {
return fmt.Errorf("unable to connect to azure event hubs: %w", err)
}
a.hub = hub
} else {
// Connect via AAD.
settings, sErr := azauth.NewEnvironmentSettings(azauth.AzureEventHubsResourceName, metadata.Properties)
if sErr != nil {
return sErr
}
a.eventHubSettings = settings
tokenProvider, tokenErr := a.eventHubSettings.GetAMQPTokenProvider()
if tokenErr != nil {
return fmt.Errorf("%s %w", hubConnectionInitErrorMsg, err)
}
a.tokenProvider = tokenProvider
a.hub, err = eventhub.NewHub(a.metadata.eventHubNamespaceName, a.metadata.eventHubName, a.tokenProvider, eventhub.HubWithUserAgent(a.userAgent))
if err != nil {
return fmt.Errorf("unable to connect to azure event hubs: %w", err)
}
}
// connect to the storage account.
if m.storageAccountKey != "" {
metadata.Properties["accountKey"] = m.storageAccountKey
}
a.storageCredential, a.azureEnvironment, err = azauth.GetAzureStorageBlobCredentials(a.logger, m.storageAccountName, metadata.Properties)
if err != nil {
return fmt.Errorf("invalid credentials with error: %w", err)
}
return nil
}
func parseMetadata(meta bindings.Metadata) (*azureEventHubsMetadata, error) {
m := &azureEventHubsMetadata{}
if val, ok := meta.Properties[connectionString]; ok && val != "" {
m.connectionString = val
}
if val, ok := meta.Properties[storageAccountName]; ok && val != "" {
m.storageAccountName = val
} else {
return m, errors.New(missingStorageAccountNameErrorMsg)
}
if val, ok := meta.Properties[storageAccountKey]; ok && val != "" {
m.storageAccountKey = val
} else if m.connectionString != "" {
return m, errors.New(missingStorageAccountKeyErrorMsg)
}
if val, ok := meta.Properties[storageContainerName]; ok && val != "" {
m.storageContainerName = val
} else {
return m, errors.New(missingStorageContainerNameErrorMsg)
}
if val, ok := meta.Properties[consumerGroup]; ok && val != "" {
m.consumerGroup = val
} else {
return m, errors.New(missingConsumerGroupErrorMsg)
}
if val, ok := meta.Properties[partitionKeyName]; ok {
m.partitionKey = val
}
if val, ok := meta.Properties[partitionIDName]; ok {
m.partitionID = val
}
if val, ok := meta.Properties[hubName]; ok {
m.eventHubName = val
} else if m.connectionString == "" {
return m, errors.New(missingHubNameErrorMsg)
}
if val, ok := meta.Properties[hubNamespaceName]; ok {
m.eventHubNamespaceName = val
} else if m.connectionString == "" {
return m, errors.New(missingHubNamespaceErrorMsg)
}
return m, nil
return a.AzureEventHubs.Init(metadata.Properties)
}
func (a *AzureEventHubs) Operations() []bindings.OperationKind {
return []bindings.OperationKind{bindings.CreateOperation}
return []bindings.OperationKind{
bindings.CreateOperation,
}
}
// Write posts an event hubs message.
func (a *AzureEventHubs) Invoke(ctx context.Context, req *bindings.InvokeRequest) (*bindings.InvokeResponse, error) {
event := &eventhub.Event{
Data: req.Data,
// Get the partition key and content type
batchOpts := &azeventhubs.EventDataBatchOptions{}
if pk := req.Metadata["partitionKey"]; pk != "" {
batchOpts.PartitionKey = &pk
}
var contentType *string
if ct := req.Metadata["contentType"]; ct != "" {
contentType = ptr.Of(req.Metadata["contentType"])
}
// Send partitionKey in event.
if a.metadata.partitionKey != "" {
event.PartitionKey = &a.metadata.partitionKey
} else {
partitionKey, ok := req.Metadata[partitionKeyName]
if partitionKey != "" && ok {
event.PartitionKey = &partitionKey
}
// Publish the message
messages := []*azeventhubs.EventData{
{
Body: req.Data,
ContentType: contentType,
},
}
err := a.hub.Send(ctx, event)
// Publish the message
err := a.AzureEventHubs.Publish(ctx, a.AzureEventHubs.EventHubName(), messages, batchOpts)
if err != nil {
return nil, err
}
return nil, nil
}
// Read gets messages from eventhubs in a non-blocking way.
// Read gets messages from Event Hubs in a non-blocking way.
func (a *AzureEventHubs) Read(ctx context.Context, handler bindings.Handler) error {
if !a.metadata.partitioned() {
if err := a.RegisterEventProcessor(ctx, handler); err != nil {
return err
// Start the subscription
// This is non-blocking
return a.AzureEventHubs.Subscribe(ctx, a.AzureEventHubs.EventHubName(), false, func(ctx context.Context, data []byte, metadata map[string]string) error {
res := bindings.ReadResponse{
Data: data,
Metadata: metadata,
}
} else {
if err := a.RegisterPartitionedEventProcessor(ctx, handler); err != nil {
return err
}
}
go func() {
// Wait for context to be canceled then close the connection
<-ctx.Done()
a.Close()
}()
return nil
_, hErr := handler(ctx, &res)
return hErr
})
}
// RegisterPartitionedEventProcessor - receive eventhub messages by partitionID.
func (a *AzureEventHubs) RegisterPartitionedEventProcessor(ctx context.Context, handler bindings.Handler) error {
runtimeInfo, err := a.hub.GetRuntimeInformation(ctx)
if err != nil {
return err
}
callback := func(c context.Context, event *eventhub.Event) error {
if event == nil {
return nil
}
return readHandler(c, event, handler)
}
ops := []eventhub.ReceiveOption{
eventhub.ReceiveWithLatestOffset(),
}
if a.metadata.consumerGroup != "" {
a.logger.Infof("eventhubs: using consumer group %s", a.metadata.consumerGroup)
ops = append(ops, eventhub.ReceiveWithConsumerGroup(a.metadata.consumerGroup))
}
if contains(runtimeInfo.PartitionIDs, a.metadata.partitionID) {
a.logger.Infof("eventhubs: using partition id %s", a.metadata.partitionID)
_, err := a.hub.Receive(ctx, a.metadata.partitionID, callback, ops...)
if err != nil {
return err
}
}
return nil
}
// RegisterEventProcessor - receive eventhub messages by eventprocessor
// host by balancing partitions.
func (a *AzureEventHubs) RegisterEventProcessor(ctx context.Context, handler bindings.Handler) error {
storagePrefix, err := a.getStoragePrefixString()
if err != nil {
return err
}
leaserPrefixOpt := storage.WithPrefixInBlobPath(storagePrefix)
leaserCheckpointer, err := storage.NewStorageLeaserCheckpointer(a.storageCredential, a.metadata.storageAccountName, a.metadata.storageContainerName, *a.azureEnvironment, leaserPrefixOpt)
if err != nil {
return err
}
var processor *eph.EventProcessorHost
if a.metadata.connectionString != "" {
processor, err = eph.NewFromConnectionString(ctx, a.metadata.connectionString, leaserCheckpointer, leaserCheckpointer, eph.WithNoBanner(), eph.WithConsumerGroup(a.metadata.consumerGroup))
if err != nil {
return err
}
} else {
// AAD connection.
processor, err = eph.New(ctx, a.metadata.eventHubNamespaceName, a.metadata.eventHubName, a.tokenProvider, leaserCheckpointer, leaserCheckpointer, eph.WithNoBanner(), eph.WithConsumerGroup(a.metadata.consumerGroup))
if err != nil {
return err
}
a.logger.Debugf("processor initialized via AAD for eventHubName %s", a.metadata.eventHubName)
}
_, err = processor.RegisterHandler(
ctx,
func(c context.Context, event *eventhub.Event) error {
return readHandler(c, event, handler)
},
)
if err != nil {
return err
}
err = processor.StartNonBlocking(ctx)
if err != nil {
return err
}
return nil
}
func contains(arr []string, str string) bool {
for _, a := range arr {
if a == str {
return true
}
}
return false
}
func (a *AzureEventHubs) Close() (err error) {
// Use a background context because the connection context may be canceled already
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
err = a.hub.Close(ctx)
cancel()
return err
func (a *AzureEventHubs) Close() error {
return a.AzureEventHubs.Close()
}

View File

@ -26,6 +26,7 @@ import (
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/dapr/components-contrib/bindings"
"github.com/dapr/components-contrib/metadata"
@ -61,11 +62,11 @@ func createIotHubBindingsMetadata() bindings.Metadata {
metadata := bindings.Metadata{
Base: metadata.Base{
Properties: map[string]string{
connectionString: os.Getenv(iotHubConnectionStringEnvKey),
consumerGroup: os.Getenv(iotHubConsumerGroupEnvKey),
storageAccountName: os.Getenv(storageAccountNameEnvKey),
storageAccountKey: os.Getenv(storageAccountKeyEnvKey),
storageContainerName: testStorageContainerName,
"connectionString": os.Getenv(iotHubConnectionStringEnvKey),
"consumerGroup": os.Getenv(iotHubConsumerGroupEnvKey),
"storageAccountName": os.Getenv(storageAccountNameEnvKey),
"storageAccountKey": os.Getenv(storageAccountKeyEnvKey),
"storageContainerName": testStorageContainerName,
},
},
}
@ -77,14 +78,14 @@ func createEventHubsBindingsAADMetadata() bindings.Metadata {
metadata := bindings.Metadata{
Base: metadata.Base{
Properties: map[string]string{
consumerGroup: os.Getenv(eventHubsBindingsConsumerGroupEnvKey),
storageAccountName: os.Getenv(azureBlobStorageAccountEnvKey),
storageContainerName: os.Getenv(eventHubsBindingsContainerEnvKey),
"eventHub": os.Getenv(eventHubBindingsHubEnvKey),
"eventHubNamespace": os.Getenv(eventHubBindingsNamespaceEnvKey),
"azureTenantId": os.Getenv(azureTenantIdEnvKey),
"azureClientId": os.Getenv(azureServicePrincipalClientIdEnvKey),
"azureClientSecret": os.Getenv(azureServicePrincipalClientSecretEnvKey),
"consumerGroup": os.Getenv(eventHubsBindingsConsumerGroupEnvKey),
"storageAccountName": os.Getenv(azureBlobStorageAccountEnvKey),
"storageContainerName": os.Getenv(eventHubsBindingsContainerEnvKey),
"eventHub": os.Getenv(eventHubBindingsHubEnvKey),
"eventHubNamespace": os.Getenv(eventHubBindingsNamespaceEnvKey),
"azureTenantId": os.Getenv(azureTenantIdEnvKey),
"azureClientId": os.Getenv(azureServicePrincipalClientIdEnvKey),
"azureClientSecret": os.Getenv(azureServicePrincipalClientSecretEnvKey),
},
},
}
@ -96,18 +97,19 @@ func testEventHubsBindingsAADAuthentication(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
logger := logger.NewLogger("bindings.azure.eventhubs.integration.test")
log := logger.NewLogger("bindings.azure.eventhubs.integration.test")
log.SetOutputLevel(logger.DebugLevel)
metadata := createEventHubsBindingsAADMetadata()
eventHubsBindings := NewAzureEventHubs(logger)
eventHubsBindings := NewAzureEventHubs(log)
err := eventHubsBindings.Init(metadata)
assert.NoError(t, err)
require.NoError(t, err)
req := &bindings.InvokeRequest{
Data: []byte("Integration test message"),
}
_, err = eventHubsBindings.Invoke(ctx, req)
assert.NoError(t, err)
require.NoError(t, err)
// Setup Read binding to capture readResponses in a closure so that test asserts can be
// performed on the main thread, including the case where the handler is never invoked.
@ -118,13 +120,13 @@ func testEventHubsBindingsAADAuthentication(t *testing.T) {
}
_, err = eventHubsBindings.Invoke(ctx, req)
assert.NoError(t, err)
require.NoError(t, err)
eventHubsBindings.Read(ctx, handler)
time.Sleep(1 * time.Second)
_, err = eventHubsBindings.Invoke(ctx, req)
assert.NoError(t, err)
require.NoError(t, err)
// Note: azure-event-hubs-go SDK defaultLeasePersistenceInterval is 5s
// Sleep long enough so that the azure event hubs SDK has time to persist updated checkpoints
@ -132,9 +134,9 @@ func testEventHubsBindingsAADAuthentication(t *testing.T) {
time.Sleep(10 * time.Second)
assert.Greater(t, len(readResponses), 0, "Failed to receive any EventHub events")
logger.Infof("Received %d messages", len(readResponses))
log.Infof("Received %d messages", len(readResponses))
for _, r := range readResponses {
logger.Infof("Message metadata: %v", r.Metadata)
log.Infof("Message metadata: %v", r.Metadata)
assert.Contains(t, string(r.Data), "Integration test message")
}
}
@ -145,14 +147,14 @@ func testReadIotHubEvents(t *testing.T) {
logger := logger.NewLogger("bindings.azure.eventhubs.integration.test")
eh := NewAzureEventHubs(logger)
err := eh.Init(createIotHubBindingsMetadata())
assert.Nil(t, err)
require.NoError(t, err)
// Invoke az CLI via bash script to send test IoT device events
// Requires the AZURE_CREDENTIALS environment variable to be already set (output of `az ad sp create-for-rbac`)
cmd := exec.Command("/bin/bash", "../../../tests/scripts/send-iot-device-events.sh")
cmd.Env = append(os.Environ(), fmt.Sprintf("IOT_HUB_NAME=%s", os.Getenv(iotHubNameEnvKey)))
out, err := cmd.CombinedOutput()
assert.Nil(t, err, "Error in send-iot-device-events.sh:\n%s", out)
assert.NoError(t, err, "Error in send-iot-device-events.sh:\n%s", out)
// Setup Read binding to capture readResponses in a closure so that test asserts can be
// performed on the main thread, including the case where the handler is never invoked.
@ -177,14 +179,14 @@ func testReadIotHubEvents(t *testing.T) {
// Verify expected IoT Hub device event metadata exists
// TODO: add device messages than can populate the sysPropPartitionKey and sysPropIotHubConnectionModuleID metadata
assert.Contains(t, r.Metadata, sysPropSequenceNumber, "IoT device event missing: %s", sysPropSequenceNumber)
assert.Contains(t, r.Metadata, sysPropEnqueuedTime, "IoT device event missing: %s", sysPropEnqueuedTime)
assert.Contains(t, r.Metadata, sysPropOffset, "IoT device event missing: %s", sysPropOffset)
assert.Contains(t, r.Metadata, sysPropIotHubDeviceConnectionID, "IoT device event missing: %s", sysPropIotHubDeviceConnectionID)
assert.Contains(t, r.Metadata, sysPropIotHubAuthGenerationID, "IoT device event missing: %s", sysPropIotHubAuthGenerationID)
assert.Contains(t, r.Metadata, sysPropIotHubConnectionAuthMethod, "IoT device event missing: %s", sysPropIotHubConnectionAuthMethod)
assert.Contains(t, r.Metadata, sysPropIotHubEnqueuedTime, "IoT device event missing: %s", sysPropIotHubEnqueuedTime)
assert.Contains(t, r.Metadata, sysPropMessageID, "IoT device event missing: %s", sysPropMessageID)
assert.Contains(t, r.Metadata, "x-opt-sequence-number", "IoT device event missing: x-opt-sequence-number")
assert.Contains(t, r.Metadata, "x-opt-enqueued-time", "IoT device event missing: x-opt-enqueued-time")
assert.Contains(t, r.Metadata, "x-opt-offset", "IoT device event missing: x-opt-offset")
assert.Contains(t, r.Metadata, "iothub-connection-device-id", "IoT device event missing: iothub-connection-device-id")
assert.Contains(t, r.Metadata, "iothub-connection-auth-generation-id", "IoT device event missing: iothub-connection-auth-generation-id")
assert.Contains(t, r.Metadata, "iothub-connection-auth-method", "IoT device event missing: iothub-connection-auth-method")
assert.Contains(t, r.Metadata, "iothub-enqueuedtime", "IoT device event missing: iothub-enqueuedtime")
assert.Contains(t, r.Metadata, "message-id", "IoT device event missing: message-id")
}
cancel()

View File

@ -1,122 +0,0 @@
/*
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 eventhubs
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/dapr/components-contrib/bindings"
"github.com/dapr/components-contrib/metadata"
"github.com/dapr/kit/logger"
)
var testLogger = logger.NewLogger("test")
func TestGetStoragePrefixString(t *testing.T) {
props := map[string]string{"storageAccountName": "fake", "storageAccountKey": "fake", "consumerGroup": "default", "storageContainerName": "test", "eventHub": "hubName", "eventHubNamespace": "fake"}
metadata := bindings.Metadata{Base: metadata.Base{Properties: props}}
m, err := parseMetadata(metadata)
require.NoError(t, err)
aeh := &AzureEventHubs{logger: testLogger, metadata: m}
actual, _ := aeh.getStoragePrefixString()
assert.Equal(t, "dapr-hubName-default-", actual)
}
func TestGetStoragePrefixStringWithHubNameFromConnectionString(t *testing.T) {
connectionString := "Endpoint=sb://fake.servicebus.windows.net/;SharedAccessKeyName=fakeKey;SharedAccessKey=key;EntityPath=hubName"
props := map[string]string{"storageAccountName": "fake", "storageAccountKey": "fake", "consumerGroup": "default", "storageContainerName": "test", "connectionString": connectionString}
metadata := bindings.Metadata{Base: metadata.Base{Properties: props}}
m, err := parseMetadata(metadata)
require.NoError(t, err)
aeh := &AzureEventHubs{logger: testLogger, metadata: m}
actual, _ := aeh.getStoragePrefixString()
assert.Equal(t, "dapr-hubName-default-", actual)
}
func TestParseMetadata(t *testing.T) {
t.Run("test valid configuration", func(t *testing.T) {
props := map[string]string{connectionString: "fake", consumerGroup: "mygroup", storageAccountName: "account", storageAccountKey: "key", storageContainerName: "container"}
bindingsMetadata := bindings.Metadata{Base: metadata.Base{Properties: props}}
m, err := parseMetadata(bindingsMetadata)
assert.NoError(t, err)
assert.Equal(t, m.connectionString, "fake")
assert.Equal(t, m.storageAccountName, "account")
assert.Equal(t, m.storageAccountKey, "key")
assert.Equal(t, m.storageContainerName, "container")
assert.Equal(t, m.consumerGroup, "mygroup")
})
type invalidConfigTestCase struct {
name string
config map[string]string
errMsg string
}
invalidConfigTestCases := []invalidConfigTestCase{
{
"missing consumerGroup",
map[string]string{connectionString: "fake", storageAccountName: "account", storageAccountKey: "key", storageContainerName: "container"},
missingConsumerGroupErrorMsg,
},
{
"no connectionString requires AAD specific params",
map[string]string{consumerGroup: "fake", storageAccountName: "account", storageAccountKey: "key", storageContainerName: "container"},
missingHubNameErrorMsg,
},
{
"only some required params for AAD specified",
map[string]string{consumerGroup: "fakeConsumer", storageAccountName: "account", storageAccountKey: "key", hubName: "namespace", storageContainerName: "fakeContainer"},
missingHubNamespaceErrorMsg,
},
{
"missing storageAccountName",
map[string]string{consumerGroup: "fake", connectionString: "fake", storageAccountKey: "key", storageContainerName: "container"},
missingStorageAccountNameErrorMsg,
},
{
"missing storageAccountKey",
map[string]string{consumerGroup: "fake", connectionString: "fake", storageAccountName: "name", storageContainerName: "container"},
missingStorageAccountKeyErrorMsg,
},
{
"missing storageContainerName",
map[string]string{consumerGroup: "fake", connectionString: "fake", storageAccountName: "name", storageAccountKey: "key"},
missingStorageContainerNameErrorMsg,
},
}
for _, c := range invalidConfigTestCases {
t.Run(c.name, func(t *testing.T) {
bindingsMetadata := bindings.Metadata{Base: metadata.Base{Properties: c.config}}
_, err := parseMetadata(bindingsMetadata)
assert.Error(t, err)
assert.Equal(t, err.Error(), c.errMsg)
})
}
}

View File

@ -22,12 +22,12 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
"github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig"
"github.com/Azure/go-autorest/autorest/to"
"github.com/stretchr/testify/assert"
"github.com/dapr/components-contrib/configuration"
mdata "github.com/dapr/components-contrib/metadata"
"github.com/dapr/kit/logger"
"github.com/dapr/kit/ptr"
)
type MockConfigurationStore struct {
@ -38,8 +38,8 @@ func (m *MockConfigurationStore) GetSetting(ctx context.Context, key string, opt
if key == "testKey" || key == "test_sentinel_key" {
settings := azappconfig.Setting{}
settings.Key = to.StringPtr("testKey")
settings.Value = to.StringPtr("testValue")
settings.Key = ptr.Of("testKey")
settings.Value = ptr.Of("testValue")
resp := azappconfig.GetSettingResponse{}
resp.Setting = settings
@ -53,12 +53,12 @@ func (m *MockConfigurationStore) NewListSettingsPager(selector azappconfig.Setti
settings := make([]azappconfig.Setting, 2)
setting1 := azappconfig.Setting{}
setting1.Key = to.StringPtr("testKey-1")
setting1.Value = to.StringPtr("testValue-1")
setting1.Key = ptr.Of("testKey-1")
setting1.Value = ptr.Of("testValue-1")
setting2 := azappconfig.Setting{}
setting2.Key = to.StringPtr("testKey-2")
setting2.Value = to.StringPtr("testValue-2")
setting2.Key = ptr.Of("testKey-2")
setting2.Value = ptr.Of("testValue-2")
settings[0] = setting1
settings[1] = setting2

10
go.mod
View File

@ -9,23 +9,22 @@ require (
cloud.google.com/go/storage v1.28.1
dubbo.apache.org/dubbo-go/v3 v3.0.3-0.20230118042253-4f159a2b38f3
github.com/Azure/azure-amqp-common-go/v4 v4.0.0
github.com/Azure/azure-event-hubs-go/v3 v3.4.0
github.com/Azure/azure-sdk-for-go v65.0.0+incompatible
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0
github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig v0.5.0
github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos v0.3.3
github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.0.1
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.11.0
github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs v0.4.0
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.4
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/eventhub/armeventhub v1.0.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1
github.com/Azure/azure-storage-blob-go v0.15.0
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd
github.com/Azure/go-amqp v0.18.0
github.com/Azure/go-autorest/autorest v0.11.28
github.com/Azure/go-autorest/autorest/adal v0.9.21
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11
github.com/Azure/go-autorest/autorest/to v0.4.0
github.com/DATA-DOG/go-sqlmock v1.5.0
github.com/Shopify/sarama v1.38.0
github.com/aerospike/aerospike-client-go/v6 v6.9.1
@ -145,6 +144,7 @@ require (
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
@ -184,7 +184,6 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
github.com/deepmap/oapi-codegen v1.3.6 // indirect
github.com/devigned/tab v0.1.1 // indirect
github.com/dghubble/sling v1.4.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
@ -260,7 +259,6 @@ require (
github.com/jinzhu/copier v0.3.5 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/k0kubun/pp v3.0.1+incompatible // indirect
github.com/kataras/go-errors v0.0.3 // indirect
github.com/kataras/go-serializer v0.0.4 // indirect

19
go.sum
View File

@ -406,16 +406,14 @@ github.com/AthenZ/athenz v1.10.39 h1:mtwHTF/v62ewY2Z5KWhuZgVXftBej1/Tn80zx4DcawY
github.com/AthenZ/athenz v1.10.39/go.mod h1:3Tg8HLsiQZp81BJY58JBeU2BR6B/H4/0MQGfCwhHNEA=
github.com/Azure/azure-amqp-common-go/v4 v4.0.0 h1:mV5O74KYmonn0ZXtwfMjGUtZ9Z+Hv7AIFVS1s03sRvo=
github.com/Azure/azure-amqp-common-go/v4 v4.0.0/go.mod h1:4+qRvizIo4+CbGG552O6a8ONkEwRgWXqes3SUt1Ftrc=
github.com/Azure/azure-event-hubs-go/v3 v3.4.0 h1:LtH0nHkXivyV/GajOu5ZFC5sb/5KZ8j+9U8UsfHVTOo=
github.com/Azure/azure-event-hubs-go/v3 v3.4.0/go.mod h1:ODgt5C1/c73FToYj+mWojUJLXF877ALc6G4XnfRFlAY=
github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
github.com/Azure/azure-sdk-for-go v65.0.0+incompatible h1:HzKLt3kIwMm4KeJYTdx9EbjRYTySD/t8i1Ee/W5EGXw=
github.com/Azure/azure-sdk-for-go v65.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 h1:sVW/AFBTGyJxDaMYlq0ct3jUXTtj12tQ6zE2GZUgVQw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 h1:VuHAcMq8pU1IWNT/m5yRaGqbK0BiQKHT8X4DTp9CHdI=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0/go.mod h1:tZoQYdDZNOiIjdSn0dVWVfl0NEPGOJqVLzSrcFk4Is0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 h1:t/W5MYAuQy81cvM8VUNfRLzhtKpXhVUAN7Cd7KVbTyc=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0/go.mod h1:NBanQUfSWiWn3QEpWDTCU0IjBECKOYvl2R8xdRtMtiM=
@ -432,12 +430,14 @@ github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.11.0 h1:82w8tzLcOwDP
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.11.0/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/azeventhubs v0.4.0 h1:X/ePaAG8guM7j5WORT5eEIw7cGUxe9Ah1jEQJKLmmSo=
github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs v0.4.0/go.mod h1:5dog28UP3dd1BnCPFYvyHfsmA+Phmoezt+KWT5cZnyc=
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.4 h1:kaZamwZwmUqnECvnPkf1LBRBIFYYCy3E0gKHn/UFSD0=
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.4/go.mod h1:uDLwkzCJMvTrHsvtiVFeAp85hi3W77zvs61wrpc+6ho=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/eventhub/armeventhub v1.0.0 h1:BWeAAEzkCnL0ABVJqs+4mYudNch7oFGPtTlSmIWL8ms=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/eventhub/armeventhub v1.0.0/go.mod h1:Y3gnVwfaz8h6L1YHar+NfWORtBoVUSB5h4GlGkdeF7Q=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1 h1:YvQv9Mz6T8oR5ypQOL6erY0Z5t71ak1uHV4QFokCOZk=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1/go.mod h1:c6WvOhtmjNUWbLfOG1qxM/q0SPvQNSVJvolm+C52dIU=
github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk=
github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd h1:b3wyxBl3vvr15tUAziPBPK354y+LSdfPCpex5oBttHo=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd/go.mod h1:K6am8mT+5iFXgingS9LUc7TmbsW6XBw3nxaRyaMyWc8=
github.com/Azure/go-amqp v0.18.0 h1:95bTiJq0oxjK1RUlt5T3HF/THj6jWTRZpSXMPSOJLz8=
@ -447,7 +447,6 @@ github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM=
github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk=
github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U=
@ -726,8 +725,6 @@ github.com/deepmap/oapi-codegen v1.3.6 h1:Wj44p9A0V0PJ+AUg0BWdyGcsS1LY18U+0rCuPQ
github.com/deepmap/oapi-codegen v1.3.6/go.mod h1:aBozjEveG+33xPiP55Iw/XbVkhtZHEGLq3nxlX0+hfU=
github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw=
github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo=
github.com/devigned/tab v0.1.1 h1:3mD6Kb1mUOYeLpJvTVSDwSg5ZsfSxfvxGRTxRsJsITA=
github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=
github.com/dghubble/go-twitter v0.0.0-20221104224141-912508c3888b h1:XQu6o3AwJx/jsg9LZ41uIeUdXK5be099XFfFn6H9ikk=
github.com/dghubble/go-twitter v0.0.0-20221104224141-912508c3888b/go.mod h1:B0/qdW5XUupJvcsx40hnVbfjzz9He5YpYXx6eVVdiSY=
github.com/dghubble/oauth1 v0.7.2 h1:pwcinOZy8z6XkNxvPmUDY52M7RDPxt0Xw1zgZ6Cl5JA=
@ -1045,7 +1042,6 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
@ -1241,13 +1237,12 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=

View File

@ -17,7 +17,6 @@ import (
"fmt"
"time"
"github.com/Azure/azure-storage-blob-go/azblob"
"github.com/Azure/azure-storage-queue-go/azqueue"
"github.com/Azure/go-autorest/autorest/azure"
@ -34,50 +33,6 @@ var (
StorageEndpointKeys = []string{"endpoint", "storageEndpoint", "storageAccountEndpoint", "queueEndpointUrl"}
)
// GetAzureStorageBlobCredentials returns a azblob.Credential object that can be used to authenticate an Azure Blob Storage SDK pipeline ("track 1").
// First it tries to authenticate using shared key credentials (using an account key) if present. It falls back to attempting to use Azure AD (via a service principal or MSI).
func GetAzureStorageBlobCredentials(log logger.Logger, accountName string, metadata map[string]string) (azblob.Credential, *azure.Environment, error) {
settings, err := NewEnvironmentSettings("storage", metadata)
if err != nil {
return nil, nil, err
}
// Try using shared key credentials first
accountKey, ok := mdutils.GetMetadataProperty(metadata, StorageAccountKeyKeys...)
if ok && accountKey != "" {
credential, newSharedKeyErr := azblob.NewSharedKeyCredential(accountName, accountKey)
if err != nil {
return nil, nil, fmt.Errorf("invalid credentials with error: %s", newSharedKeyErr.Error())
}
return credential, settings.AzureEnvironment, nil
}
// Fallback to using Azure AD
spt, err := settings.GetServicePrincipalToken()
if err != nil {
return nil, nil, err
}
var tokenRefresher azblob.TokenRefresher = func(credential azblob.TokenCredential) time.Duration {
log.Debug("Refreshing Azure Storage auth token")
err := spt.Refresh()
if err != nil {
panic(err)
}
token := spt.Token()
credential.SetToken(token.AccessToken)
// Make the token expire 2 minutes earlier to get some extra buffer
exp := token.Expires().Sub(time.Now().Add(2 * time.Minute))
log.Debug("Received new token, valid for", exp)
return exp
}
credential := azblob.NewTokenCredential("", tokenRefresher)
return credential, settings.AzureEnvironment, nil
}
// GetAzureStorageQueueCredentials returns a azqueues.Credential object that can be used to authenticate an Azure Queue Storage SDK pipeline ("track 1").
// First it tries to authenticate using shared key credentials (using an account key) if present. It falls back to attempting to use Azure AD (via a service principal or MSI).
func GetAzureStorageQueueCredentials(log logger.Logger, accountName string, metadata map[string]string) (azqueue.Credential, *azure.Environment, error) {

View File

@ -83,7 +83,7 @@ func CreateContainerStorageClient(log logger.Logger, meta map[string]string) (*c
}
client, clientErr = container.NewClientWithSharedKeyCredential(URL.String(), credential, &options)
if clientErr != nil {
return nil, nil, fmt.Errorf("cannot init Blobstorage container client: %w", err)
return nil, nil, fmt.Errorf("cannot init Blobstorage container client: %w", clientErr)
}
} else {
// fallback to AAD

View File

@ -0,0 +1,214 @@
/*
Copyright 2023 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 eventhubs
import (
"context"
"errors"
"fmt"
"net/http"
"time"
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/eventhub/armeventhub"
azauth "github.com/dapr/components-contrib/internal/authentication/azure"
"github.com/dapr/kit/logger"
"github.com/dapr/kit/ptr"
"github.com/dapr/kit/retry"
)
const (
defaultMessageRetentionInDays = 1
defaultPartitionCount = 1
resourceCheckMaxRetry = 5
resourceCheckMaxRetryInterval = 5 * time.Minute
resourceCreationTimeout = 15 * time.Second
resourceGetTimeout = 5 * time.Second
// See https://docs.microsoft.com/en-us/azure/event-hubs/event-hubs-quotas for numbers.
maxMessageRetention = int32(90)
maxPartitionCount = int32(1024)
)
// Intializes the entity management capabilities. This method is invoked by Init.
func (aeh *AzureEventHubs) initEntityManagement() error {
// Validate the metadata
err := aeh.validateEnitityManagementMetadata()
if err != nil {
return err
}
// Get Azure Management plane credentials object
settings, err := azauth.NewEnvironmentSettings("azure", aeh.metadata.properties)
if err != nil {
return err
}
creds, err := settings.GetTokenCredential()
if err != nil {
return fmt.Errorf("failed to obtain Azure AD management credentials: %w", err)
}
aeh.managementCreds = creds
return nil
}
func (aeh *AzureEventHubs) validateEnitityManagementMetadata() error {
if aeh.metadata.MessageRetentionInDays <= 0 || aeh.metadata.MessageRetentionInDays > maxMessageRetention {
aeh.logger.Warnf("Property messageRetentionInDays for entity management has an empty or invalid value; using the default value %d", defaultMessageRetentionInDays)
aeh.metadata.MessageRetentionInDays = defaultMessageRetentionInDays
}
if aeh.metadata.PartitionCount <= 0 || aeh.metadata.PartitionCount > maxPartitionCount {
aeh.logger.Warnf("Property partitionCount for entity management has an empty or invalid value; using the default value %d", defaultPartitionCount)
aeh.metadata.PartitionCount = defaultPartitionCount
}
if aeh.metadata.ResourceGroupName == "" {
return errors.New("property resourceGroupName is required for entity management")
}
if aeh.metadata.SubscriptionID == "" {
return errors.New("property subscriptionID is required for entity management")
}
return nil
}
// Ensures that the Event Hub entity exists.
// This is used during the creation of both producers and consumers.
func (aeh *AzureEventHubs) ensureEventHubEntity(parentCtx context.Context, topic string) error {
client, err := armeventhub.NewEventHubsClient(aeh.metadata.SubscriptionID, aeh.managementCreds, &arm.ClientOptions{
ClientOptions: policy.ClientOptions{
Telemetry: policy.TelemetryOptions{
ApplicationID: "dapr-" + logger.DaprVersion,
},
},
})
if err != nil {
return fmt.Errorf("failed to create Event Hubs ARM client: %w", err)
}
aeh.logger.Debugf("Checking if entity %s exists on Event Hub namespace %s", topic, aeh.metadata.namespaceName)
// Check if the entity exists
ctx, cancel := context.WithTimeout(parentCtx, resourceGetTimeout)
defer cancel()
_, err = client.Get(ctx, aeh.metadata.ResourceGroupName, aeh.metadata.namespaceName, topic, nil)
if err == nil {
// If there's no error, the entity already exists, so all good
aeh.logger.Debugf("Entity %s already exists on Event Hub namespace %s", topic, aeh.metadata.namespaceName)
return nil
}
// Check if the error is NotFound or something else
resErr := &azcore.ResponseError{}
if !errors.As(err, &resErr) || resErr.StatusCode != http.StatusNotFound {
// We have another error, just return it
return fmt.Errorf("failed to retrieve Event Hub entity %s: %w", topic, err)
}
// Create the entity
aeh.logger.Infof("Will create entity %s on Event Hub namespace %s", topic, aeh.metadata.namespaceName)
params := armeventhub.Eventhub{
Properties: &armeventhub.Properties{
MessageRetentionInDays: ptr.Of(int64(aeh.metadata.MessageRetentionInDays)),
PartitionCount: ptr.Of(int64(aeh.metadata.PartitionCount)),
},
}
ctx, cancel = context.WithTimeout(parentCtx, resourceCreationTimeout)
defer cancel()
_, err = client.CreateOrUpdate(ctx, aeh.metadata.ResourceGroupName, aeh.metadata.namespaceName, topic, params, nil)
if err != nil {
return fmt.Errorf("failed to create Event Hub entity %s: %w", topic, err)
}
aeh.logger.Infof("Entity %s created on Event Hub namespace %s", topic, aeh.metadata.namespaceName)
return nil
}
// Ensures that the subscription (consumer group) exists.
// This is used during the creation of consumers only.
func (aeh *AzureEventHubs) ensureSubscription(parentCtx context.Context, hubName string) error {
client, err := armeventhub.NewConsumerGroupsClient(aeh.metadata.SubscriptionID, aeh.managementCreds, &arm.ClientOptions{
ClientOptions: policy.ClientOptions{
Telemetry: policy.TelemetryOptions{
ApplicationID: "dapr-" + logger.DaprVersion,
},
},
})
if err != nil {
return fmt.Errorf("failed to create consumer group ARM client: %w", err)
}
aeh.logger.Debugf("Checking if consumer group %s exists in entity %s", aeh.metadata.ConsumerGroup, hubName)
// Check if the consumer group exists
// We need to use a retry logic here
backOffConfig := retry.DefaultConfig()
backOffConfig.Policy = retry.PolicyExponential
backOffConfig.MaxInterval = resourceCheckMaxRetryInterval
backOffConfig.MaxRetries = resourceCheckMaxRetry
b := backOffConfig.NewBackOffWithContext(parentCtx)
create, err := retry.NotifyRecoverWithData(func() (bool, error) {
c, cErr := aeh.shouldCreateConsumerGroup(parentCtx, client, hubName)
if cErr != nil {
return false, cErr
}
return c, nil
}, b, func(_ error, _ time.Duration) {
aeh.logger.Errorf("Error checking for consumer group for Event Hub: %s. Retrying…", hubName)
}, func() {
aeh.logger.Warnf("Successfully checked for consumer group in Event Hub %s after it previously failed", hubName)
})
if err != nil {
return err
}
if !create {
// Already exists
aeh.logger.Debugf("Consumer group %s already exists in entity %s", aeh.metadata.ConsumerGroup, hubName)
return nil
}
// Need to create the consumer group
aeh.logger.Infof("Will create consumer group %s exists in entity %s", aeh.metadata.ConsumerGroup, hubName)
ctx, cancel := context.WithTimeout(parentCtx, resourceCreationTimeout)
defer cancel()
_, err = client.CreateOrUpdate(ctx, aeh.metadata.ResourceGroupName, aeh.metadata.namespaceName, hubName, aeh.metadata.ConsumerGroup, armeventhub.ConsumerGroup{}, nil)
if err != nil {
return fmt.Errorf("failed to create consumer group %s: %w", aeh.metadata.ConsumerGroup, err)
}
aeh.logger.Infof("Consumer group %s created in entity %s", aeh.metadata.ConsumerGroup, hubName)
return nil
}
func (aeh *AzureEventHubs) shouldCreateConsumerGroup(parentCtx context.Context, client *armeventhub.ConsumerGroupsClient, hubName string) (bool, error) {
ctx, cancel := context.WithTimeout(parentCtx, resourceGetTimeout)
defer cancel()
_, err := client.Get(ctx, aeh.metadata.ResourceGroupName, aeh.metadata.namespaceName, hubName, aeh.metadata.ConsumerGroup, nil)
if err == nil {
// If there's no error, the consumer group already exists, so all good
return false, nil
}
// Check if the error is NotFound or something else
resErr := &azcore.ResponseError{}
if !errors.As(err, &resErr) || resErr.StatusCode != http.StatusNotFound {
// We have another error, just return it
return false, err
}
// Consumer group doesn't exist
return true, nil
}

View File

@ -0,0 +1,646 @@
/*
Copyright 2023 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 eventhubs
import (
"context"
"errors"
"fmt"
"strconv"
"sync"
"sync/atomic"
"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/messaging/azeventhubs"
"github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs/checkpoints"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
"golang.org/x/exp/maps"
azauth "github.com/dapr/components-contrib/internal/authentication/azure"
"github.com/dapr/kit/logger"
"github.com/dapr/kit/retry"
)
// AzureEventHubs allows sending/receiving Azure Event Hubs events.
// This is an abstract class used by both the pubsub and binding components.
type AzureEventHubs struct {
metadata *azureEventHubsMetadata
logger logger.Logger
isBinding bool
backOffConfig retry.Config
producersLock *sync.RWMutex
producers map[string]*azeventhubs.ProducerClient
checkpointStoreCache azeventhubs.CheckpointStore
checkpointStoreLock *sync.RWMutex
managementCreds azcore.TokenCredential
// TODO(@ItalyPaleAle): Remove in Dapr 1.13
isFailed *atomic.Bool
}
// NewAzureEventHubs returns a new Azure Event hubs instance.
func NewAzureEventHubs(logger logger.Logger, isBinding bool) *AzureEventHubs {
return &AzureEventHubs{
logger: logger,
isBinding: isBinding,
producersLock: &sync.RWMutex{},
producers: make(map[string]*azeventhubs.ProducerClient, 1),
checkpointStoreLock: &sync.RWMutex{},
isFailed: &atomic.Bool{},
}
}
// Init connects to Azure Event Hubs.
func (aeh *AzureEventHubs) Init(metadata map[string]string) error {
m, err := parseEventHubsMetadata(metadata, aeh.isBinding, aeh.logger)
if err != nil {
return err
}
aeh.metadata = m
if aeh.metadata.ConnectionString == "" {
// If connecting via Azure AD, we need to do some more initialization
var env azauth.EnvironmentSettings
env, err = azauth.NewEnvironmentSettings("eventhubs", metadata)
if err != nil {
return fmt.Errorf("failed to initialize Azure AD credentials: %w", err)
}
aeh.metadata.aadTokenProvider, err = env.GetTokenCredential()
if err != nil {
return fmt.Errorf("failed to get Azure AD token credentials provider: %w", err)
}
aeh.logger.Info("connecting to Azure Event Hub using Azure AD; the connection will be established on first publish/subscribe and req.Topic field in incoming requests will be honored")
if aeh.metadata.EnableEntityManagement {
err = aeh.initEntityManagement()
if err != nil {
return fmt.Errorf("failed to initialize entity manager: %w", err)
}
}
}
// Default retry configuration is used if no backOff properties are set
// backOff max retry config is set to 3, which means 3 retries by default
aeh.backOffConfig = retry.DefaultConfig()
aeh.backOffConfig.MaxRetries = 3
err = retry.DecodeConfigWithPrefix(&aeh.backOffConfig, metadata, "backOff")
if err != nil {
return fmt.Errorf("failed to decode backoff configuration")
}
return nil
}
// EventHubName returns the parsed eventHub property from the metadata.
// It's used by the binding only.
func (aeh *AzureEventHubs) EventHubName() string {
return aeh.metadata.hubName
}
// Publish a batch of messages.
func (aeh *AzureEventHubs) Publish(ctx context.Context, topic string, messages []*azeventhubs.EventData, batchOpts *azeventhubs.EventDataBatchOptions) error {
// Get the producer client
client, err := aeh.getProducerClientForTopic(ctx, topic)
if err != nil {
return fmt.Errorf("error trying to establish a connection: %w", err)
}
// Build the batch of messages
batch, err := client.NewEventDataBatch(ctx, batchOpts)
if err != nil {
return fmt.Errorf("error creating event batch: %w", err)
}
// Add all messages
for _, msg := range messages {
err = batch.AddEventData(msg, nil)
if err != nil {
return fmt.Errorf("error adding messages to batch: %w", err)
}
}
// Send the message
err = client.SendEventDataBatch(ctx, batch, nil)
if err != nil {
return fmt.Errorf("error publishing batch: %w", err)
}
return nil
}
// Subscribe receives data from Azure Event Hubs in background.
func (aeh *AzureEventHubs) Subscribe(subscribeCtx context.Context, topic string, getAllProperties bool, handler SubscribeHandler) (err error) {
if aeh.metadata.ConsumerGroup == "" {
return errors.New("property consumerID is required to subscribe to an Event Hub topic")
}
// Get the processor client
processor, err := aeh.getProcessorForTopic(subscribeCtx, topic)
if err != nil {
return fmt.Errorf("error trying to establish a connection: %w", err)
}
// Ensure that no subscriber using the old "track 1" SDK is active
// TODO(@ItalyPaleAle): Remove this for Dapr 1.13
{
// If a previous topic already failed, no need to try with other topics, as we're about to panic anyways
if aeh.isFailed.Load() {
return errors.New("subscribing to another topic on this component failed and Dapr is scheduled to crash; will not try subscribing to a new topic")
}
ctx, cancel := context.WithTimeout(subscribeCtx, 2*time.Minute)
err = aeh.ensureNoTrack1Subscribers(ctx, topic)
cancel()
if err != nil {
// If there's a timeout, it means that the other client was still active after the timeout
// In this case, we return an error here so Dapr can continue the initialization and report a "healthy" status (but this subscription won't be active)
// After 2 minutes, then, we panic, which ensures that during a rollout Kubernetes will see that this pod is unhealthy and re-creates that. Hopefully, by then other instances of the app will have been updated and no more locks will be present
if errors.Is(err, context.DeadlineExceeded) {
aeh.isFailed.Store(true)
errMsg := fmt.Sprintf("Another instance is currently subscribed to the topic %s in this Event Hub using an old version of Dapr, and this is not supported. Please ensure that all applications subscribed to the same topic, with this consumer group, are using Dapr 1.10 or newer.", topic)
aeh.logger.Error(errMsg + " ⚠️⚠️⚠️ Dapr will crash in 2 minutes to force the orchestrator to restart the process after the rollout of other instances is complete.")
go func() {
time.Sleep(2 * time.Minute)
aeh.logger.Fatalf("Another instance is currently subscribed to the topic %s in this Event Hub using an old version of Dapr, and this is not supported. Please ensure that all applications subscribed to the same topic, with this consumer group, are using Dapr 1.10 or newer.", topic)
}()
return fmt.Errorf("another instance is currently subscribed to the topic %s in this Event Hub using an old version of Dapr", topic)
}
// In case of other errors, just return the error
return fmt.Errorf("failed to check for subscribers using an old version of Dapr: %w", err)
}
}
// This component has built-in retries because Event Hubs doesn't support N/ACK for messages
retryHandler := func(ctx context.Context, data []byte, metadata map[string]string) error {
b := aeh.backOffConfig.NewBackOffWithContext(subscribeCtx)
mID := metadata[sysPropMessageID]
if mID == "" {
mID = "(nil)"
}
// This method is synchronous so no risk of race conditions if using side effects
var attempts int
retryerr := retry.NotifyRecover(func() error {
attempts++
aeh.logger.Debugf("Processing EventHubs event %s/%s (attempt: %d)", topic, mID, attempts)
if attempts > 1 {
metadata["dapr-attempt"] = strconv.Itoa(attempts)
}
return handler(ctx, data, metadata)
}, b, func(_ error, _ time.Duration) {
aeh.logger.Warnf("Error processing EventHubs event: %s/%s. Retrying...", topic, mID)
}, func() {
aeh.logger.Warnf("Successfully processed EventHubs event after it previously failed: %s/%s", topic, mID)
})
if retryerr != nil {
aeh.logger.Errorf("Too many failed attempts at processing Eventhubs event: %s/%s. Error: %v", topic, mID, err)
}
return retryerr
}
// Get the subscribe handler
eventHandler := subscribeHandler(subscribeCtx, getAllProperties, retryHandler)
// Process all partition clients as they come in
go func() {
for {
// This will block until a new partition client is available
// It returns nil if processor.Run terminates or if the context is canceled
partitionClient := processor.NextPartitionClient(subscribeCtx)
if partitionClient == nil {
return
}
aeh.logger.Debugf("Received client for partition %s", partitionClient.PartitionID())
// Once we get a partition client, process the events in a separate goroutine
go func() {
processErr := aeh.processEvents(subscribeCtx, topic, partitionClient, eventHandler)
// Do not log context.Canceled which happens at shutdown
if processErr != nil && !errors.Is(processErr, context.Canceled) {
aeh.logger.Errorf("Error processing events from partition client: %v", processErr)
}
}()
}
}()
// Start the processor
go func() {
// This is a blocking call that runs until the context is canceled
err = processor.Run(subscribeCtx)
// Do not log context.Canceled which happens at shutdown
if err != nil && !errors.Is(err, context.Canceled) {
aeh.logger.Errorf("Error from event processor: %v", err)
}
}()
return nil
}
func (aeh *AzureEventHubs) processEvents(subscribeCtx context.Context, topic string, partitionClient *azeventhubs.ProcessorPartitionClient, eventHandler func(e *azeventhubs.ReceivedEventData) error) error {
// At the end of the method we need to do some cleanup and close the partition client
defer func() {
closeCtx, closeCancel := context.WithTimeout(context.Background(), resourceGetTimeout)
defer closeCancel()
closeErr := partitionClient.Close(closeCtx)
if closeErr != nil {
aeh.logger.Errorf("Error while closing partition client: %v", closeErr)
}
}()
// Loop to receive messages
var (
ctx context.Context
cancel context.CancelFunc
events []*azeventhubs.ReceivedEventData
err error
)
for {
// TODO: Support setting a batch size
const batchSize = 1
ctx, cancel = context.WithTimeout(subscribeCtx, time.Minute)
events, err = partitionClient.ReceiveEvents(ctx, batchSize, nil)
cancel()
// A DeadlineExceeded error means that the context timed out before we received the full batch of messages, and that's fine
if err != nil && !errors.Is(err, context.DeadlineExceeded) {
// If we get an error like ErrorCodeOwnershipLost, it means that the partition was rebalanced and we lost it
// We'll just stop this subscription and return
eventHubError := (*azeventhubs.Error)(nil)
if errors.As(err, &eventHubError) && eventHubError.Code == azeventhubs.ErrorCodeOwnershipLost {
aeh.logger.Debugf("Client lost ownership of partition %s for topic %s", partitionClient.PartitionID(), topic)
return nil
}
return fmt.Errorf("error receiving events: %w", err)
}
aeh.logger.Debugf("Received batch with %d events on topic %s, partition %s", len(events), topic, partitionClient.PartitionID())
if len(events) != 0 {
for _, event := range events {
// Process the event in its own goroutine
go eventHandler(event)
}
// Update the checkpoint with the last event received. If we lose ownership of this partition or have to restart the next owner will start from this point.
// This context inherits from the background one in case subscriptionCtx gets canceled
ctx, cancel = context.WithTimeout(context.Background(), resourceCreationTimeout)
err = partitionClient.UpdateCheckpoint(ctx, events[len(events)-1])
cancel()
if err != nil {
return fmt.Errorf("failed to update checkpoint: %w", err)
}
}
}
}
func (aeh *AzureEventHubs) Close() (err error) {
// Acquire locks
aeh.checkpointStoreLock.Lock()
defer aeh.checkpointStoreLock.Unlock()
aeh.producersLock.Lock()
defer aeh.producersLock.Unlock()
// Close all producers
wg := sync.WaitGroup{}
for _, producer := range aeh.producers {
if producer == nil {
continue
}
wg.Add(1)
go func(producer *azeventhubs.ProducerClient) {
closeCtx, closeCancel := context.WithTimeout(context.Background(), resourceGetTimeout)
defer closeCancel()
producer.Close(closeCtx)
wg.Done()
}(producer)
}
wg.Wait()
maps.Clear(aeh.producers)
// Remove the cached checkpoint store and metadata
aeh.checkpointStoreCache = nil
aeh.metadata = nil
return nil
}
// Returns a producer client for a given topic.
// If the client doesn't exist in the cache, it will create one.
func (aeh *AzureEventHubs) getProducerClientForTopic(ctx context.Context, topic string) (client *azeventhubs.ProducerClient, err error) {
// Check if we have the producer client in the cache
aeh.producersLock.RLock()
client = aeh.producers[topic]
aeh.producersLock.RUnlock()
if client != nil {
return client, nil
}
// After acquiring a write lock, check again if the producer exists in the cache just in case another goroutine created it in the meanwhile
aeh.producersLock.Lock()
defer aeh.producersLock.Unlock()
client = aeh.producers[topic]
if client != nil {
return client, nil
}
// Create a new entity if needed
if aeh.metadata.EnableEntityManagement {
err = aeh.ensureEventHubEntity(ctx, topic)
if err != nil {
return nil, fmt.Errorf("failed to create Event Hub entity %s: %w", topic, err)
}
}
clientOpts := &azeventhubs.ProducerClientOptions{
ApplicationID: "dapr-" + logger.DaprVersion,
}
// Check if we're authenticating using a connection string
if aeh.metadata.ConnectionString != "" {
var connString string
connString, err = aeh.constructConnectionStringFromTopic(topic)
if err != nil {
return nil, err
}
client, err = azeventhubs.NewProducerClientFromConnectionString(connString, "", clientOpts)
if err != nil {
return nil, fmt.Errorf("unable to connect to Azure Event Hub using a connection string: %w", err)
}
} else {
// Use Azure AD
client, err = azeventhubs.NewProducerClient(aeh.metadata.EventHubNamespace, topic, aeh.metadata.aadTokenProvider, clientOpts)
if err != nil {
return nil, fmt.Errorf("unable to connect to Azure Event Hub using Azure AD: %w", err)
}
}
// Store in the cache before returning it
aeh.producers[topic] = client
return client, nil
}
// Creates a processor for a given topic.
func (aeh *AzureEventHubs) getProcessorForTopic(ctx context.Context, topic string) (*azeventhubs.Processor, error) {
// Get the checkpoint store
checkpointStore, err := aeh.getCheckpointStore(ctx)
if err != nil {
return nil, fmt.Errorf("unable to connect to the checkpoint store: %w", err)
}
// Create a new entity if needed
if aeh.metadata.EnableEntityManagement {
// First ensure that the Event Hub entity exists
// We need to acquire a lock on producers, as creating a producer can perform the same operations
aeh.producersLock.Lock()
err = aeh.ensureEventHubEntity(ctx, topic)
aeh.producersLock.Unlock()
if err != nil {
return nil, fmt.Errorf("failed to create Event Hub entity %s: %w", topic, err)
}
// Abuse on the lock on checkpoints which are used by all tasks creating processors
aeh.checkpointStoreLock.Lock()
err = aeh.ensureSubscription(ctx, topic)
aeh.checkpointStoreLock.Unlock()
if err != nil {
return nil, fmt.Errorf("failed to create Event Hub subscription to entity %s: %w", topic, err)
}
}
// Create a consumer client
var consumerClient *azeventhubs.ConsumerClient
clientOpts := &azeventhubs.ConsumerClientOptions{
ApplicationID: "dapr-" + logger.DaprVersion,
}
// Check if we're authenticating using a connection string
if aeh.metadata.ConnectionString != "" {
var connString string
connString, err = aeh.constructConnectionStringFromTopic(topic)
if err != nil {
return nil, err
}
consumerClient, err = azeventhubs.NewConsumerClientFromConnectionString(connString, "", aeh.metadata.ConsumerGroup, clientOpts)
if err != nil {
return nil, fmt.Errorf("unable to connect to Azure Event Hub using a connection string: %w", err)
}
} else {
// Use Azure AD
consumerClient, err = azeventhubs.NewConsumerClient(aeh.metadata.EventHubNamespace, topic, aeh.metadata.ConsumerGroup, aeh.metadata.aadTokenProvider, clientOpts)
if err != nil {
return nil, fmt.Errorf("unable to connect to Azure Event Hub using Azure AD: %w", err)
}
}
// Create the processor from the consumer client and checkpoint store
processor, err := azeventhubs.NewProcessor(consumerClient, checkpointStore, nil)
if err != nil {
return nil, fmt.Errorf("unable to create the processor: %w", err)
}
return processor, nil
}
// Returns the checkpoint store from the object. If it doesn't exist, it lazily initializes it.
func (aeh *AzureEventHubs) getCheckpointStore(ctx context.Context) (azeventhubs.CheckpointStore, error) {
// Check if we have the checkpoint store
aeh.checkpointStoreLock.RLock()
if aeh.checkpointStoreCache != nil {
aeh.checkpointStoreLock.RUnlock()
return aeh.checkpointStoreCache, nil
}
aeh.checkpointStoreLock.RUnlock()
// After acquiring a write lock, check again if the checkpoint store exists in case another goroutine created it in the meanwhile
aeh.checkpointStoreLock.Lock()
defer aeh.checkpointStoreLock.Unlock()
if aeh.checkpointStoreCache != nil {
return aeh.checkpointStoreCache, nil
}
// Init the checkpoint store and store it in the object
var err error
aeh.checkpointStoreCache, err = aeh.createCheckpointStore(ctx)
if err != nil {
return nil, fmt.Errorf("unable to connect to the checkpoint store: %w", err)
}
return aeh.checkpointStoreCache, nil
}
// Initializes a new checkpoint store
func (aeh *AzureEventHubs) createCheckpointStore(ctx context.Context) (checkpointStore azeventhubs.CheckpointStore, err error) {
if aeh.metadata.StorageAccountName == "" {
return nil, errors.New("property storageAccountName is required to subscribe to an Event Hub topic")
}
if aeh.metadata.StorageContainerName == "" {
return nil, errors.New("property storageContainerName is required to subscribe to an Event Hub topic")
}
// Ensure the container exists
err = aeh.ensureStorageContainer(ctx)
if err != nil {
return nil, err
}
// Create the checkpoint store
checkpointStoreOpts := &checkpoints.BlobStoreOptions{
ClientOptions: policy.ClientOptions{
Telemetry: policy.TelemetryOptions{
ApplicationID: "dapr-" + logger.DaprVersion,
},
},
}
if aeh.metadata.StorageConnectionString != "" {
// Authenticate with a connection string
checkpointStore, err = checkpoints.NewBlobStoreFromConnectionString(aeh.metadata.StorageConnectionString, aeh.metadata.StorageContainerName, checkpointStoreOpts)
if err != nil {
return nil, fmt.Errorf("error creating checkpointer from connection string: %w", err)
}
} else if aeh.metadata.StorageAccountKey != "" {
// Authenticate with a shared key
// TODO: This is a workaround in which we assemble a connection string until https://github.com/Azure/azure-sdk-for-go/issues/19842 is fixed
connString := fmt.Sprintf("DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s;EndpointSuffix=core.windows.net", aeh.metadata.StorageAccountName, aeh.metadata.StorageAccountKey)
checkpointStore, err = checkpoints.NewBlobStoreFromConnectionString(connString, aeh.metadata.StorageContainerName, checkpointStoreOpts)
if err != nil {
return nil, fmt.Errorf("error creating checkpointer from storage account credentials: %w", err)
}
} else {
// Use Azure AD
// If Event Hub is authenticated using a connection string, we can't use Azure AD here
if aeh.metadata.ConnectionString != "" {
return nil, errors.New("either one of storageConnectionString or storageAccountKey is required when subscribing to an Event Hub topic without using Azure AD")
}
// Use the global URL for Azure Storage
containerURL := fmt.Sprintf("https://%s.blob.%s/%s", aeh.metadata.StorageAccountName, "core.windows.net", aeh.metadata.StorageContainerName)
checkpointStore, err = checkpoints.NewBlobStore(containerURL, aeh.metadata.aadTokenProvider, checkpointStoreOpts)
if err != nil {
return nil, fmt.Errorf("error creating checkpointer from Azure AD credentials: %w", err)
}
}
return checkpointStore, nil
}
// Ensures that the container exists in the Azure Storage Account.
// This is done to preserve backwards-compatibility with Dapr 1.9, as the old checkpoint SDK created them automatically.
func (aeh *AzureEventHubs) ensureStorageContainer(parentCtx context.Context) error {
// Get a client to Azure Blob Storage
client, err := aeh.createStorageClient()
if err != nil {
return err
}
// Create the container
// This will return an error if it already exists
ctx, cancel := context.WithTimeout(parentCtx, resourceCreationTimeout)
defer cancel()
_, err = client.CreateContainer(ctx, aeh.metadata.StorageContainerName, &container.CreateOptions{
// Default is private
Access: nil,
})
if err != nil {
// Check if it's an Azure Storage error
resErr := &azcore.ResponseError{}
// If the container already exists, return no error
if errors.As(err, &resErr) && (resErr.ErrorCode == "ContainerAlreadyExists" || resErr.ErrorCode == "ResourceAlreadyExists") {
return nil
}
return fmt.Errorf("failed to create Azure Storage container %s: %w", aeh.metadata.StorageContainerName, err)
}
return nil
}
// Creates a client to access Azure Blob Storage
func (aeh *AzureEventHubs) createStorageClient() (*azblob.Client, error) {
options := azblob.ClientOptions{
ClientOptions: azcore.ClientOptions{
Telemetry: policy.TelemetryOptions{
ApplicationID: "dapr-" + logger.DaprVersion,
},
},
}
var (
err error
client *azblob.Client
)
// Use the global URL for Azure Storage
accountURL := fmt.Sprintf("https://%s.blob.%s", aeh.metadata.StorageAccountName, "core.windows.net")
if aeh.metadata.StorageConnectionString != "" {
// Authenticate with a connection string
client, err = azblob.NewClientFromConnectionString(aeh.metadata.StorageConnectionString, &options)
if err != nil {
return nil, fmt.Errorf("error creating Azure Storage client from connection string: %w", err)
}
} else if aeh.metadata.StorageAccountKey != "" {
// Authenticate with a shared key
credential, newSharedKeyErr := azblob.NewSharedKeyCredential(aeh.metadata.StorageAccountName, aeh.metadata.StorageAccountKey)
if newSharedKeyErr != nil {
return nil, fmt.Errorf("invalid Azure Storage shared key credentials with error: %w", newSharedKeyErr)
}
client, err = azblob.NewClientWithSharedKeyCredential(accountURL, credential, &options)
if err != nil {
return nil, fmt.Errorf("error creating Azure Storage client from shared key credentials: %w", err)
}
} else {
// Use Azure AD
var (
settings azauth.EnvironmentSettings
credential azcore.TokenCredential
)
settings, err = azauth.NewEnvironmentSettings("storage", aeh.metadata.properties)
if err != nil {
return nil, fmt.Errorf("error getting Azure environment settings: %w", err)
}
credential, err = settings.GetTokenCredential()
if err != nil {
return nil, fmt.Errorf("invalid Azure Storage token credentials with error: %w", err)
}
client, err = azblob.NewClient(accountURL, credential, &options)
if err != nil {
return nil, fmt.Errorf("error creating Azure Storage client from token credentials: %w", err)
}
}
return client, nil
}
// Returns a connection string with the Event Hub name (entity path) set if not present.
func (aeh *AzureEventHubs) constructConnectionStringFromTopic(topic string) (string, error) {
if aeh.metadata.hubName != "" {
if aeh.metadata.hubName != topic {
return "", fmt.Errorf("the requested topic '%s' does not match the Event Hub name in the connection string", topic)
}
return aeh.metadata.ConnectionString, nil
}
connString := aeh.metadata.ConnectionString + ";EntityPath=" + topic
return connString, nil
}

View File

@ -0,0 +1,126 @@
/*
Copyright 2023 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 eventhubs
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/dapr/kit/logger"
)
var testLogger = logger.NewLogger("test")
func TestParseEventHubsMetadata(t *testing.T) {
t.Run("test valid connectionString configuration", func(t *testing.T) {
metadata := map[string]string{"connectionString": "fake"}
m, err := parseEventHubsMetadata(metadata, false, testLogger)
require.NoError(t, err)
assert.Equal(t, "fake", m.ConnectionString)
})
t.Run("test namespace given", func(t *testing.T) {
metadata := map[string]string{"eventHubNamespace": "fake.servicebus.windows.net"}
m, err := parseEventHubsMetadata(metadata, false, testLogger)
require.NoError(t, err)
assert.Equal(t, "fake.servicebus.windows.net", m.EventHubNamespace)
})
t.Run("test namespace adds FQDN", func(t *testing.T) {
metadata := map[string]string{"eventHubNamespace": "fake"}
m, err := parseEventHubsMetadata(metadata, false, testLogger)
require.NoError(t, err)
assert.Equal(t, "fake.servicebus.windows.net", m.EventHubNamespace)
})
t.Run("test both connectionString and eventHubNamespace given", func(t *testing.T) {
metadata := map[string]string{
"connectionString": "fake",
"eventHubNamespace": "fake",
}
_, err := parseEventHubsMetadata(metadata, false, testLogger)
require.Error(t, err)
assert.ErrorContains(t, err, "only one of connectionString or eventHubNamespace should be passed")
})
t.Run("test missing metadata", func(t *testing.T) {
metadata := map[string]string{}
_, err := parseEventHubsMetadata(metadata, false, testLogger)
require.Error(t, err)
assert.ErrorContains(t, err, "one of connectionString or eventHubNamespace is required")
})
}
func TestConstructConnectionStringFromTopic(t *testing.T) {
t.Run("valid connectionString without hub name", func(t *testing.T) {
connectionString := "Endpoint=sb://fake.servicebus.windows.net/;SharedAccessKeyName=fakeKey;SharedAccessKey=key"
topic := "testHub"
metadata := map[string]string{
"connectionString": connectionString,
}
aeh := &AzureEventHubs{logger: testLogger}
err := aeh.Init(metadata)
require.NoError(t, err)
c, err := aeh.constructConnectionStringFromTopic(topic)
require.NoError(t, err)
assert.Equal(t, connectionString+";EntityPath=testHub", c)
})
t.Run("valid connectionString with hub name", func(t *testing.T) {
connectionString := "Endpoint=sb://fake.servicebus.windows.net/;SharedAccessKeyName=fakeKey;SharedAccessKey=key;EntityPath=testHub"
topic := "testHub"
metadata := map[string]string{
"connectionString": connectionString,
}
aeh := &AzureEventHubs{logger: testLogger}
err := aeh.Init(metadata)
require.NoError(t, err)
c, err := aeh.constructConnectionStringFromTopic(topic)
require.NoError(t, err)
assert.Equal(t, connectionString, c)
})
t.Run("valid connectionString with different hub name", func(t *testing.T) {
connectionString := "Endpoint=sb://fake.servicebus.windows.net/;SharedAccessKeyName=fakeKey;SharedAccessKey=key;EntityPath=testHub"
topic := "differentHub"
metadata := map[string]string{
"connectionString": connectionString,
}
aeh := &AzureEventHubs{logger: testLogger}
err := aeh.Init(metadata)
require.NoError(t, err)
c, err := aeh.constructConnectionStringFromTopic(topic)
require.Error(t, err)
assert.ErrorContains(t, err, "does not match the Event Hub name in the connection string")
assert.Equal(t, "", c)
})
}

View File

@ -0,0 +1,105 @@
/*
Copyright 2023 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 eventhubs
import (
"context"
"strconv"
"time"
"github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs"
"github.com/spf13/cast"
)
// Type for the handler for messages coming in from the subscriptions.
type SubscribeHandler func(ctx context.Context, data []byte, metadata map[string]string) error
const (
// Event Hubs SystemProperties names for metadata passthrough.
sysPropSequenceNumber = "x-opt-sequence-number"
sysPropEnqueuedTime = "x-opt-enqueued-time"
sysPropOffset = "x-opt-offset"
sysPropPartitionID = "x-opt-partition-id"
sysPropPartitionKey = "x-opt-partition-key"
sysPropIotHubDeviceConnectionID = "iothub-connection-device-id"
sysPropIotHubAuthGenerationID = "iothub-connection-auth-generation-id"
sysPropIotHubConnectionAuthMethod = "iothub-connection-auth-method"
sysPropIotHubConnectionModuleID = "iothub-connection-module-id"
sysPropIotHubEnqueuedTime = "iothub-enqueuedtime"
sysPropMessageID = "message-id"
)
func subscribeHandler(ctx context.Context, getAllProperties bool, handler SubscribeHandler) func(e *azeventhubs.ReceivedEventData) error {
return func(e *azeventhubs.ReceivedEventData) error {
// Allocate with an initial capacity of 10 which covers the common properties, also from IoT Hub
md := make(map[string]string, 10)
md[sysPropSequenceNumber] = strconv.FormatInt(e.SequenceNumber, 10)
if e.EnqueuedTime != nil {
md[sysPropEnqueuedTime] = e.EnqueuedTime.Format(time.RFC3339)
}
if e.Offset != nil {
md[sysPropOffset] = strconv.FormatInt(*e.Offset, 10)
}
if e.PartitionKey != nil {
md[sysPropPartitionKey] = *e.PartitionKey
}
if e.MessageID != nil && *e.MessageID != "" {
md[sysPropMessageID] = *e.MessageID
}
// Iterate through the system properties looking for those coming from IoT Hub
for k, v := range e.SystemProperties {
switch k {
// The following metadata properties are only present if event was generated by Azure IoT Hub.
case sysPropIotHubDeviceConnectionID,
sysPropIotHubAuthGenerationID,
sysPropIotHubConnectionAuthMethod,
sysPropIotHubConnectionModuleID,
sysPropIotHubEnqueuedTime:
addPropertyToMetadata(k, v, md)
default:
// nop
}
}
// Added properties if any (includes application properties from Azure IoT Hub)
if getAllProperties && len(e.Properties) > 0 {
for k, v := range e.Properties {
addPropertyToMetadata(k, v, md)
}
}
return handler(ctx, e.Body, md)
}
}
// Adds a property to the response metadata
func addPropertyToMetadata(key string, value any, md map[string]string) {
switch v := value.(type) {
case *time.Time:
if v != nil {
md[key] = v.Format(time.RFC3339)
}
case time.Time:
md[key] = v.Format(time.RFC3339)
default:
str, err := cast.ToStringE(value)
if err == nil {
md[key] = str
}
}
}

View File

@ -0,0 +1,154 @@
/*
Copyright 2023 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 eventhubs
import (
"errors"
"fmt"
"regexp"
"strings"
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/dapr/components-contrib/metadata"
"github.com/dapr/kit/logger"
)
type azureEventHubsMetadata struct {
ConnectionString string `json:"connectionString" mapstructure:"connectionString"`
EventHubNamespace string `json:"eventHubNamespace" mapstructure:"eventHubNamespace"`
ConsumerID string `json:"consumerID" mapstructure:"consumerID"`
StorageConnectionString string `json:"storageConnectionString" mapstructure:"storageConnectionString"`
StorageAccountName string `json:"storageAccountName" mapstructure:"storageAccountName"`
StorageAccountKey string `json:"storageAccountKey" mapstructure:"storageAccountKey"`
StorageContainerName string `json:"storageContainerName" mapstructure:"storageContainerName"`
EnableEntityManagement bool `json:"enableEntityManagement,string" mapstructure:"enableEntityManagement"`
MessageRetentionInDays int32 `json:"messageRetentionInDays,string" mapstructure:"messageRetentionInDays"`
PartitionCount int32 `json:"partitionCount,string" mapstructure:"partitionCount"`
SubscriptionID string `json:"subscriptionID" mapstructure:"subscriptionID"`
ResourceGroupName string `json:"resourceGroupName" mapstructure:"resourceGroupName"`
// Binding only
EventHub string `json:"eventHub" mapstructure:"eventHub"`
ConsumerGroup string `json:"consumerGroup" mapstructure:"consumerGroup"` // Alias for ConsumerID
PartitionID string `json:"partitionID" mapstructure:"partitionID"` // Deprecated
// Internal properties
namespaceName string
hubName string
aadTokenProvider azcore.TokenCredential
properties map[string]string
}
func parseEventHubsMetadata(meta map[string]string, isBinding bool, log logger.Logger) (*azureEventHubsMetadata, error) {
var m azureEventHubsMetadata
err := metadata.DecodeMetadata(meta, &m)
if err != nil {
return nil, fmt.Errorf("failed to decode metada: %w", err)
}
// Store the raw properties in the object
m.properties = meta
// One and only one of connectionString and eventHubNamespace is required
if m.ConnectionString == "" && m.EventHubNamespace == "" {
return nil, errors.New("one of connectionString or eventHubNamespace is required")
}
if m.ConnectionString != "" && m.EventHubNamespace != "" {
return nil, errors.New("only one of connectionString or eventHubNamespace should be passed")
}
// ConsumerGroup is an alias for ConsumerID
if m.ConsumerID != "" && m.ConsumerGroup == "" {
m.ConsumerGroup = m.ConsumerID
}
// For the binding, we need to have a property "eventHub" which is the topic name unless it's included in the connection string
if isBinding {
if m.ConnectionString == "" {
if m.EventHub == "" {
return nil, errors.New("property 'eventHub' is required when connecting with Azure AD")
}
m.hubName = m.EventHub
} else {
hubName := hubNameFromConnString(m.ConnectionString)
if hubName != "" {
m.hubName = hubName
} else if m.EventHub != "" {
m.hubName = m.EventHub
} else {
return nil, errors.New("the provided connection string does not contain a value for 'EntityPath' and no 'eventHub' property was passed")
}
}
// Property partitionID is deprecated
if m.PartitionID != "" {
log.Info("Property partitionID is deprecated and will be ignored")
m.PartitionID = ""
}
} else {
// Ignored when not a binding
m.EventHub = ""
m.PartitionID = ""
// If connecting using a connection string, parse hubName
if m.ConnectionString != "" {
hubName := hubNameFromConnString(m.ConnectionString)
if hubName != "" {
log.Infof(`The provided connection string is specific to the Event Hub ("entity path") '%s'; publishing or subscribing to a topic that does not match this Event Hub will fail when attempted`, hubName)
} else {
log.Infof(`The provided connection string does not contain an Event Hub name ("entity path"); the connection will be established on first publish/subscribe and req.Topic field in incoming requests will be honored`)
}
m.hubName = hubName
}
}
// If both storageConnectionString and storageAccountKey are specified, show a warning because the connection string will take priority
if m.StorageConnectionString != "" && m.StorageAccountName != "" {
log.Warn("Property storageAccountKey is ignored when storageConnectionString is present")
}
// Entity management is only possible when using Azure AD
if m.EnableEntityManagement && m.ConnectionString != "" {
m.EnableEntityManagement = false
log.Warn("Entity management support is not available when connecting with a connection string")
}
if m.EventHubNamespace != "" {
// Older versions of Dapr required the namespace name to be just the name and not a FQDN
// Automatically append ".servicebus.windows.net" to make them a FQDN if not present, but show a log
if !strings.ContainsRune(m.EventHubNamespace, '.') {
m.EventHubNamespace += ".servicebus.windows.net"
log.Info("Property eventHubNamespace is not a FQDN; the suffix '.servicebus.windows.net' will be added automatically")
}
// The namespace name is the first part of the FQDN, until the first dot
m.namespaceName = m.EventHubNamespace[0:strings.IndexRune(m.EventHubNamespace, '.')]
}
return &m, nil
}
var hubNameMatch = regexp.MustCompile(`(?i)(^|;)EntityPath=([^;]+)(;|$)`)
// Returns the hub name (topic) from the connection string.
// TODO: Temporary until https://github.com/Azure/azure-sdk-for-go/issues/19840 is fixed - then use `conn.ParsedConnectionFromStr(aeh.metadata.ConnectionString)` and look at the `HubName` property.
func hubNameFromConnString(connString string) string {
match := hubNameMatch.FindStringSubmatch(connString)
if len(match) < 3 {
return ""
}
return match[2]
}

View File

@ -0,0 +1,92 @@
/*
Copyright 2023 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 eventhubs
import (
"context"
"errors"
"fmt"
"net/http"
"time"
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
"github.com/cenkalti/backoff/v4"
"github.com/dapr/kit/retry"
)
// This method ensures that there are currently no active subscribers to the same Event Hub topic that are using the old ("track 1") SDK of Azure Event Hubs. This is the SDK that was in use until Dapr 1.9.
// Because the new SDK stores checkpoints in a different way, clients using the new ("track 2") and the old SDK cannot coexist.
// To ensure this doesn't happen, when we create a new subscription to the same topic and with the same consumer group, we check if there's a file in Azure Storage with the checkpoint created by the old SDK and with a still-active lease. If that's true, we wait (until the context expires) before we crash Dapr with a log message describing what's happening.
// These conflicts should be transient anyways, as mixed versions of Dapr should only happen during a rollout of a new version of Dapr.
// TODO(@ItalyPaleAle): Remove this for Dapr 1.13
func (aeh *AzureEventHubs) ensureNoTrack1Subscribers(parentCtx context.Context, topic string) error {
// Get a client to Azure Blob Storage
client, err := aeh.createStorageClient()
if err != nil {
return err
}
// In the old version of the SDK, checkpoints were stored in the root of the storage account and were named like:
// `dapr-(topic)-(consumer-group)-(partition-key)`
// We need to list those up and check if they have an active lease
prefix := fmt.Sprintf("dapr-%s-%s-", topic, aeh.metadata.ConsumerGroup)
// Retry until we find no active leases - or the context expires
backOffConfig := retry.DefaultConfig()
backOffConfig.Policy = retry.PolicyExponential
backOffConfig.MaxInterval = time.Minute
backOffConfig.MaxElapsedTime = 0
backOffConfig.MaxRetries = -1
b := backOffConfig.NewBackOffWithContext(parentCtx)
err = backoff.Retry(func() error {
pager := client.NewListBlobsFlatPager(aeh.metadata.StorageContainerName, &container.ListBlobsFlatOptions{
Prefix: &prefix,
})
for pager.More() {
ctx, cancel := context.WithTimeout(parentCtx, resourceGetTimeout)
resp, innerErr := pager.NextPage(ctx)
cancel()
if innerErr != nil {
// Treat these errors as permanent
resErr := &azcore.ResponseError{}
if !errors.As(err, &resErr) || resErr.StatusCode != http.StatusNotFound {
// A "not-found" error means that the storage container doesn't exist, so let's not handle it here
// Just return no error
return nil
}
return backoff.Permanent(fmt.Errorf("failed to list blobs: %w", innerErr))
}
for _, blob := range resp.Segment.BlobItems {
if blob == nil || blob.Name == nil || blob.Properties == nil || blob.Properties.LeaseState == nil {
continue
}
aeh.logger.Debugf("Found checkpoint from an older Dapr version %s", *blob.Name)
// If the blob is locked, it means that there's another Dapr process with an old version of the SDK running, so we need to wait
if *blob.Properties.LeaseStatus == "locked" {
aeh.logger.Warnf("Found active lease on checkpoint %s from an older Dapr version - waiting for lease to expire", *blob.Name)
return fmt.Errorf("found active lease on checkpoint %s from an old Dapr version", *blob.Name)
}
}
}
return nil
}, b)
// If the error is a DeadlineExceeded on the operation and not on parentCtx, handle that separately to avoid crashing Dapr needlessly
if err != nil && errors.Is(err, context.DeadlineExceeded) && parentCtx.Err() != context.DeadlineExceeded {
err = errors.New("failed to list blobs: request timed out")
}
return err
}

View File

@ -66,7 +66,7 @@ func NewClient(metadata *Metadata, rawMetadata map[string]string) (*Client, erro
}
}
} else {
settings, err := azauth.NewEnvironmentSettings(azauth.AzureServiceBusResourceName, rawMetadata)
settings, err := azauth.NewEnvironmentSettings("servicebus", rawMetadata)
if err != nil {
return nil, err
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2021 The Dapr Authors
Copyright 2023 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
@ -15,592 +15,101 @@ package eventhubs
import (
"context"
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
"time"
"github.com/Azure/azure-amqp-common-go/v4/aad"
"github.com/Azure/azure-amqp-common-go/v4/conn"
eventhub "github.com/Azure/azure-event-hubs-go/v3"
"github.com/Azure/azure-event-hubs-go/v3/eph"
"github.com/Azure/azure-event-hubs-go/v3/storage"
mgmt "github.com/Azure/azure-sdk-for-go/services/eventhub/mgmt/2017-04-01/eventhub"
"github.com/Azure/azure-storage-blob-go/azblob"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs"
azauth "github.com/dapr/components-contrib/internal/authentication/azure"
impl "github.com/dapr/components-contrib/internal/component/azure/eventhubs"
"github.com/dapr/components-contrib/internal/utils"
contribMetadata "github.com/dapr/components-contrib/metadata"
"github.com/dapr/components-contrib/pubsub"
"github.com/dapr/kit/logger"
"github.com/dapr/kit/retry"
"github.com/dapr/kit/ptr"
)
const (
// connection string entity path key.
entityPathKey = "EntityPath"
// metadata partitionKey key.
partitionKeyMetadataKey = "partitionKey"
// errors.
hubManagerCreationErrorMsg = "error: creating eventHub manager client"
invalidConnectionStringErrorMsg = "error: connectionString is invalid"
missingConnectionStringNamespaceErrorMsg = "error: connectionString or eventHubNamespace is required"
missingStorageAccountNameErrorMsg = "error: storageAccountName is a required attribute for subscribe"
missingStorageAccountKeyErrorMsg = "error: storageAccountKey is required for subscribe when connectionString is provided"
missingStorageContainerNameErrorMsg = "error: storageContainerName is a required attribute for subscribe"
missingConsumerIDErrorMsg = "error: missing consumerID attribute for subscribe"
bothConnectionStringNamespaceErrorMsg = "error: both connectionString and eventHubNamespace are given, only one should be given"
missingResourceGroupNameMsg = "error: missing resourceGroupName attribute required for entityManagement"
missingSubscriptionIDMsg = "error: missing subscriptionID attribute required for entityManagement"
entityManagementConnectionStrMsg = "error: entity management support is not available with connectionString"
differentTopicConnectionStringErrorTmpl = "error: specified topic %s does not match the event hub name in the provided connectionString"
// Event Hubs SystemProperties names for metadata passthrough.
sysPropSequenceNumber = "x-opt-sequence-number"
sysPropEnqueuedTime = "x-opt-enqueued-time"
sysPropOffset = "x-opt-offset"
sysPropPartitionID = "x-opt-partition-id"
sysPropPartitionKey = "x-opt-partition-key"
sysPropIotHubDeviceConnectionID = "iothub-connection-device-id"
sysPropIotHubAuthGenerationID = "iothub-connection-auth-generation-id"
sysPropIotHubConnectionAuthMethod = "iothub-connection-auth-method"
sysPropIotHubConnectionModuleID = "iothub-connection-module-id"
sysPropIotHubEnqueuedTime = "iothub-enqueuedtime"
sysPropMessageID = "message-id"
// Metadata field to ensure all Event Hub properties pass through
requireAllProperties = "requireAllProperties"
defaultMessageRetentionInDays = 1
defaultPartitionCount = 1
resourceCheckMaxRetry = 5
resourceCheckMaxRetryInterval time.Duration = 5 * time.Minute
resourceCreationTimeout time.Duration = 15 * time.Second
resourceGetTimeout time.Duration = 5 * time.Second
// See https://docs.microsoft.com/en-us/azure/event-hubs/event-hubs-quotas for numbers.
maxMessageRetention = int32(90)
maxPartitionCount = int32(1024)
)
func subscribeHandler(ctx context.Context, topic string, getAllProperties bool, e *eventhub.Event, handler pubsub.Handler) error {
res := pubsub.NewMessage{Data: e.Data, Topic: topic, Metadata: map[string]string{}}
if e.SystemProperties.SequenceNumber != nil {
res.Metadata[sysPropSequenceNumber] = strconv.FormatInt(*e.SystemProperties.SequenceNumber, 10)
}
if e.SystemProperties.EnqueuedTime != nil {
res.Metadata[sysPropEnqueuedTime] = e.SystemProperties.EnqueuedTime.Format(time.RFC3339)
}
if e.SystemProperties.Offset != nil {
res.Metadata[sysPropOffset] = strconv.FormatInt(*e.SystemProperties.Offset, 10)
}
// According to azure-event-hubs-go docs, this will always be nil.
if e.SystemProperties.PartitionID != nil {
res.Metadata[sysPropPartitionID] = strconv.Itoa(int(*e.SystemProperties.PartitionID))
}
// The following metadata properties are only present if event was generated by Azure IoT Hub.
if e.SystemProperties.PartitionKey != nil {
res.Metadata[sysPropPartitionKey] = *e.SystemProperties.PartitionKey
}
if e.SystemProperties.IoTHubDeviceConnectionID != nil {
res.Metadata[sysPropIotHubDeviceConnectionID] = *e.SystemProperties.IoTHubDeviceConnectionID
}
if e.SystemProperties.IoTHubAuthGenerationID != nil {
res.Metadata[sysPropIotHubAuthGenerationID] = *e.SystemProperties.IoTHubAuthGenerationID
}
if e.SystemProperties.IoTHubConnectionAuthMethod != nil {
res.Metadata[sysPropIotHubConnectionAuthMethod] = *e.SystemProperties.IoTHubConnectionAuthMethod
}
if e.SystemProperties.IoTHubConnectionModuleID != nil {
res.Metadata[sysPropIotHubConnectionModuleID] = *e.SystemProperties.IoTHubConnectionModuleID
}
if e.SystemProperties.IoTHubEnqueuedTime != nil {
res.Metadata[sysPropIotHubEnqueuedTime] = e.SystemProperties.IoTHubEnqueuedTime.Format(time.RFC3339)
}
// azure-event-hubs-go SDK pulls out the AMQP message-id property to the Event.ID property, map it from there.
if e.ID != "" {
res.Metadata[sysPropMessageID] = e.ID
}
// added properties if any ( includes application properties from iot-hub)
if getAllProperties {
if e.Properties != nil && len(e.Properties) > 0 {
for key, value := range e.Properties {
if str, ok := value.(string); ok {
res.Metadata[key] = str
}
}
}
}
return handler(ctx, &res)
}
// AzureEventHubs allows sending/receiving Azure Event Hubs events.
type AzureEventHubs struct {
metadata *azureEventHubsMetadata
logger logger.Logger
backOffConfig retry.Config
hubClients map[string]*eventhub.Hub
eventProcessors map[string]*eph.EventProcessorHost
hubManager *eventhub.HubManager
eventHubSettings azauth.EnvironmentSettings
managementSettings azauth.EnvironmentSettings
cgClient *mgmt.ConsumerGroupsClient
tokenProvider *aad.TokenProvider
storageCredential azblob.Credential
azureEnvironment *azure.Environment
}
type azureEventHubsMetadata struct {
ConnectionString string `json:"connectionString,omitempty"`
EventHubNamespace string `json:"eventHubNamespace,omitempty"`
ConsumerGroup string `json:"consumerID"`
StorageAccountName string `json:"storageAccountName,omitempty"`
StorageAccountKey string `json:"storageAccountKey,omitempty"`
StorageContainerName string `json:"storageContainerName,omitempty"`
EnableEntityManagement bool `json:"enableEntityManagement,omitempty,string"`
MessageRetentionInDays int32 `json:"messageRetentionInDays,omitempty,string"`
PartitionCount int32 `json:"partitionCount,omitempty,string"`
SubscriptionID string `json:"subscriptionID,omitempty"`
ResourceGroupName string `json:"resourceGroupName,omitempty"`
*impl.AzureEventHubs
}
// NewAzureEventHubs returns a new Azure Event hubs instance.
func NewAzureEventHubs(logger logger.Logger) pubsub.PubSub {
return &AzureEventHubs{logger: logger}
return &AzureEventHubs{
AzureEventHubs: impl.NewAzureEventHubs(logger, false),
}
}
func parseEventHubsMetadata(meta pubsub.Metadata) (*azureEventHubsMetadata, error) {
b, err := json.Marshal(meta.Properties)
if err != nil {
return nil, err
}
m := azureEventHubsMetadata{}
err = json.Unmarshal(b, &m)
if err != nil {
return nil, err
}
if m.ConnectionString == "" && m.EventHubNamespace == "" {
return &m, errors.New(missingConnectionStringNamespaceErrorMsg)
}
if m.ConnectionString != "" && m.EventHubNamespace != "" {
return &m, errors.New(bothConnectionStringNamespaceErrorMsg)
}
return &m, nil
}
func validateAndGetHubName(connectionString string) (string, error) {
parsed, err := conn.ParsedConnectionFromStr(connectionString)
if err != nil {
return "", err
}
return parsed.HubName, nil
}
func (aeh *AzureEventHubs) ensureEventHub(ctx context.Context, hubName string) error {
if aeh.hubManager == nil {
aeh.logger.Errorf("hubManager client not initialized properly.")
return fmt.Errorf("hubManager client not initialized properly")
}
entity, err := aeh.getHubEntity(ctx, hubName)
if err != nil {
return err
}
if entity == nil {
if err := aeh.createHubEntity(ctx, hubName); err != nil {
return err
}
}
return nil
}
func (aeh *AzureEventHubs) ensureSubscription(ctx context.Context, hubName string) error {
err := aeh.ensureEventHub(ctx, hubName)
if err != nil {
return err
}
_, err = aeh.getConsumerGroupsClient()
if err != nil {
return err
}
return aeh.createConsumerGroup(ctx, hubName)
}
func (aeh *AzureEventHubs) getConsumerGroupsClient() (*mgmt.ConsumerGroupsClient, error) {
if aeh.cgClient != nil {
return aeh.cgClient, nil
}
client := mgmt.NewConsumerGroupsClientWithBaseURI(aeh.managementSettings.AzureEnvironment.ResourceManagerEndpoint,
aeh.metadata.SubscriptionID)
a, err := aeh.managementSettings.GetAuthorizer()
if err != nil {
return nil, err
}
client.Authorizer = a
aeh.cgClient = &client
return aeh.cgClient, nil
}
func (aeh *AzureEventHubs) createConsumerGroup(parentCtx context.Context, hubName string) error {
create := false
backOffConfig := retry.DefaultConfig()
backOffConfig.Policy = retry.PolicyExponential
backOffConfig.MaxInterval = resourceCheckMaxRetryInterval
backOffConfig.MaxRetries = resourceCheckMaxRetry
b := backOffConfig.NewBackOffWithContext(parentCtx)
err := retry.NotifyRecover(func() error {
c, err := aeh.shouldCreateConsumerGroup(parentCtx, hubName)
if err == nil {
create = c
return nil
}
return err
}, b, func(_ error, _ time.Duration) {
aeh.logger.Errorf("Error checking for consumer group for EventHub : %s. Retrying...", hubName)
}, func() {
aeh.logger.Warnf("Successfully checked for consumer group in EventHub %s after it previously failed.", hubName)
})
if err != nil {
return err
}
if create {
ctx, cancel := context.WithTimeout(parentCtx, resourceCreationTimeout)
_, err = aeh.cgClient.CreateOrUpdate(ctx, aeh.metadata.ResourceGroupName, aeh.metadata.EventHubNamespace, hubName, aeh.metadata.ConsumerGroup, mgmt.ConsumerGroup{})
cancel()
if err != nil {
return err
}
}
return nil
}
func (aeh *AzureEventHubs) shouldCreateConsumerGroup(parentCtx context.Context, hubName string) (bool, error) {
ctx, cancel := context.WithTimeout(parentCtx, resourceGetTimeout)
g, err := aeh.cgClient.Get(ctx, aeh.metadata.ResourceGroupName, aeh.metadata.EventHubNamespace, hubName, aeh.metadata.ConsumerGroup)
cancel()
if err != nil {
if g.HasHTTPStatus(404) {
return true, nil
}
return false, err
}
if *g.Name == aeh.metadata.ConsumerGroup {
aeh.logger.Infof("consumer group %s exists for the requested topic/eventHub %s", aeh.metadata.ConsumerGroup, hubName)
}
return false, nil
}
func (aeh *AzureEventHubs) getHubEntity(parentCtx context.Context, hubName string) (*eventhub.HubEntity, error) {
ctx, cancel := context.WithTimeout(parentCtx, resourceGetTimeout)
defer cancel()
return aeh.hubManager.Get(ctx, hubName)
}
func (aeh *AzureEventHubs) createHubEntity(parentCtx context.Context, hubName string) error {
ctx, cancel := context.WithTimeout(parentCtx, resourceCreationTimeout)
_, err := aeh.hubManager.Put(ctx, hubName,
eventhub.HubWithMessageRetentionInDays(aeh.metadata.MessageRetentionInDays),
eventhub.HubWithPartitionCount(aeh.metadata.PartitionCount))
cancel()
if err != nil {
aeh.logger.Errorf("error creating event hub %s: %s", hubName, err)
return fmt.Errorf("error creating event hub %s: %s", hubName, err)
}
return nil
}
func (aeh *AzureEventHubs) ensurePublisherClient(ctx context.Context, hubName string) error {
if aeh.metadata.EnableEntityManagement {
if err := aeh.ensureEventHub(ctx, hubName); err != nil {
return err
}
}
userAgent := "dapr-" + logger.DaprVersion
if aeh.metadata.ConnectionString != "" {
// Connect with connection string.
newConnectionString, err := aeh.constructConnectionStringFromTopic(hubName)
if err != nil {
return err
}
hub, err := eventhub.NewHubFromConnectionString(newConnectionString,
eventhub.HubWithUserAgent(userAgent))
if err != nil {
aeh.logger.Debugf("unable to connect to azure event hubs: %v", err)
return fmt.Errorf("unable to connect to azure event hubs: %v", err)
}
aeh.hubClients[hubName] = hub
} else {
if hubName == "" {
return errors.New("error: missing topic/hubName attribute with AAD connection")
}
hub, err := eventhub.NewHub(aeh.metadata.EventHubNamespace, hubName, aeh.tokenProvider, eventhub.HubWithUserAgent(userAgent))
if err != nil {
return fmt.Errorf("unable to connect to azure event hubs: %v", err)
}
aeh.hubClients[hubName] = hub
}
return nil
}
func (aeh *AzureEventHubs) ensureSubscriberClient(ctx context.Context, topic string, leaserCheckpointer *storage.LeaserCheckpointer) (*eph.EventProcessorHost, error) {
// connectionString given.
if aeh.metadata.ConnectionString != "" {
hubName, err := validateAndGetHubName(aeh.metadata.ConnectionString)
if err != nil {
return nil, fmt.Errorf("error parsing connection string %s", err)
}
if hubName != "" && hubName != topic {
return nil, fmt.Errorf("error: component cannot subscribe to requested topic %s with the given connectionString", topic)
}
if hubName == "" {
aeh.logger.Debugf("eventhub namespace connection string given. using topic as event hub entity path")
}
connectionString, err := aeh.constructConnectionStringFromTopic(topic)
if err != nil {
return nil, err
}
processor, err := eph.NewFromConnectionString(
ctx, connectionString,
leaserCheckpointer,
leaserCheckpointer,
eph.WithNoBanner(),
eph.WithConsumerGroup(aeh.metadata.ConsumerGroup),
)
if err != nil {
return nil, err
}
aeh.logger.Debugf("processor initialized via connection string for topic %s", topic)
return processor, nil
}
// AAD connection.
processor, err := eph.New(ctx,
aeh.metadata.EventHubNamespace,
topic,
aeh.tokenProvider,
leaserCheckpointer,
leaserCheckpointer,
eph.WithNoBanner(),
eph.WithConsumerGroup(aeh.metadata.ConsumerGroup),
)
if err != nil {
return nil, err
}
aeh.logger.Debugf("processor initialized via AAD for topic %s", topic)
return processor, nil
}
func (aeh *AzureEventHubs) createHubManager() error {
// Only AAD based authentication supported.
hubManager, err := eventhub.NewHubManagerFromAzureEnvironment(aeh.metadata.EventHubNamespace, aeh.tokenProvider, *aeh.eventHubSettings.AzureEnvironment)
if err != nil {
return fmt.Errorf("%s %s", hubManagerCreationErrorMsg, err)
}
aeh.hubManager = hubManager
return nil
}
func (aeh *AzureEventHubs) constructConnectionStringFromTopic(requestedTopic string) (string, error) {
hubName, err := validateAndGetHubName(aeh.metadata.ConnectionString)
if err != nil {
return "", err
}
if hubName != "" && hubName == requestedTopic {
return aeh.metadata.ConnectionString, nil
} else if hubName != "" {
return "", fmt.Errorf(differentTopicConnectionStringErrorTmpl, requestedTopic)
}
return aeh.metadata.ConnectionString + ";" + entityPathKey + "=" + requestedTopic, nil
}
func (aeh *AzureEventHubs) validateEnitityManagementMetadata() error {
if aeh.metadata.MessageRetentionInDays <= 0 || aeh.metadata.MessageRetentionInDays > maxMessageRetention {
aeh.logger.Warnf("invalid/no message retention time period is given with entity management enabled, default value of %d is used", defaultMessageRetentionInDays)
aeh.metadata.MessageRetentionInDays = defaultMessageRetentionInDays
}
if aeh.metadata.PartitionCount <= 0 || aeh.metadata.PartitionCount > maxPartitionCount {
aeh.logger.Warnf("invalid/no partition count is given with entity management enabled, default value of %d is used", defaultPartitionCount)
aeh.metadata.PartitionCount = defaultPartitionCount
}
if aeh.metadata.ResourceGroupName == "" {
return errors.New(missingResourceGroupNameMsg)
}
if aeh.metadata.SubscriptionID == "" {
return errors.New(missingSubscriptionIDMsg)
}
return nil
}
func (aeh *AzureEventHubs) validateSubscriptionAttributes() error {
m := *aeh.metadata
if m.StorageAccountName == "" {
return errors.New(missingStorageAccountNameErrorMsg)
}
if m.StorageAccountKey == "" && m.ConnectionString != "" {
return errors.New(missingStorageAccountKeyErrorMsg)
}
if m.StorageContainerName == "" {
return errors.New(missingStorageContainerNameErrorMsg)
}
if m.ConsumerGroup == "" {
return errors.New(missingConsumerIDErrorMsg)
}
return nil
}
func (aeh *AzureEventHubs) getStoragePrefixString(topic string) string {
// empty string in the end of slice to have a suffix "-".
return strings.Join([]string{"dapr", topic, aeh.metadata.ConsumerGroup, ""}, "-")
}
// Init connects to Azure Event Hubs.
// Init the object.
func (aeh *AzureEventHubs) Init(metadata pubsub.Metadata) error {
m, parseErr := parseEventHubsMetadata(metadata)
if parseErr != nil {
return parseErr
}
aeh.metadata = m
aeh.eventProcessors = map[string]*eph.EventProcessorHost{}
aeh.hubClients = map[string]*eventhub.Hub{}
if aeh.metadata.ConnectionString != "" {
// Validate connectionString.
hubName, validateErr := validateAndGetHubName(aeh.metadata.ConnectionString)
if validateErr != nil {
return errors.New(invalidConnectionStringErrorMsg)
}
if hubName != "" {
aeh.logger.Infof("connectionString provided is specific to event hub %q. Publishing or subscribing to a topic that does not match this event hub will fail when attempted.", hubName)
} else {
aeh.logger.Infof("hubName not given in connectionString. connection established on first publish/subscribe")
aeh.logger.Debugf("req.Topic field in incoming requests honored")
}
if aeh.metadata.EnableEntityManagement {
// See https://docs.microsoft.com/en-us/azure/event-hubs/event-hubs-management-libraries
return errors.New(entityManagementConnectionStrMsg)
}
} else {
// Connect via AAD.
settings, sErr := azauth.NewEnvironmentSettings(azauth.AzureEventHubsResourceName, metadata.Properties)
if sErr != nil {
return sErr
}
aeh.eventHubSettings = settings
tokenProvider, err := aeh.eventHubSettings.GetAMQPTokenProvider()
if err != nil {
return fmt.Errorf("%s %s", hubManagerCreationErrorMsg, err)
}
aeh.tokenProvider = tokenProvider
aeh.logger.Info("connecting to Azure EventHubs via AAD. connection established on first publish/subscribe")
aeh.logger.Debugf("req.Topic field in incoming requests honored")
if aeh.metadata.EnableEntityManagement {
if err := aeh.validateEnitityManagementMetadata(); err != nil {
return err
}
// Create hubManager for eventHub management with AAD.
if managerCreateErr := aeh.createHubManager(); managerCreateErr != nil {
return managerCreateErr
}
// Get Azure Management plane settings for creating consumer groups using event hubs management client.
settings, err := azauth.NewEnvironmentSettings("azure", metadata.Properties)
if err != nil {
return err
}
aeh.managementSettings = settings
}
}
// connect to the storage account.
if m.StorageAccountKey != "" {
metadata.Properties["accountKey"] = m.StorageAccountKey
}
var storageCredsErr error
aeh.storageCredential, aeh.azureEnvironment, storageCredsErr = azauth.GetAzureStorageBlobCredentials(aeh.logger, m.StorageAccountName, metadata.Properties)
if storageCredsErr != nil {
return fmt.Errorf("invalid storage credentials with error: %w", storageCredsErr)
}
// Default retry configuration is used if no backOff properties are set.
if err := retry.DecodeConfigWithPrefix(
&aeh.backOffConfig,
metadata.Properties,
"backOff"); err != nil {
return err
}
return aeh.AzureEventHubs.Init(metadata.Properties)
}
func (aeh *AzureEventHubs) Features() []pubsub.Feature {
return nil
}
// Publish sends data to Azure Event Hubs.
// Publish sends a message to Azure Event Hubs.
func (aeh *AzureEventHubs) Publish(ctx context.Context, req *pubsub.PublishRequest) error {
if _, ok := aeh.hubClients[req.Topic]; !ok {
if err := aeh.ensurePublisherClient(ctx, req.Topic); err != nil {
return fmt.Errorf("error on establishing hub connection: %s", err)
}
}
event := &eventhub.Event{Data: req.Data}
val, ok := req.Metadata[partitionKeyMetadataKey]
if ok {
event.PartitionKey = &val
}
err := aeh.hubClients[req.Topic].Send(ctx, event)
if err != nil {
return fmt.Errorf("error from publish: %s", err)
if req.Topic == "" {
return errors.New("parameter 'topic' is required")
}
return nil
// Get the partition key and create the batch of messages
batchOpts := &azeventhubs.EventDataBatchOptions{}
if pk := req.Metadata["partitionKey"]; pk != "" {
batchOpts.PartitionKey = &pk
}
messages := []*azeventhubs.EventData{
{
Body: req.Data,
ContentType: req.ContentType,
},
}
// Publish the message
return aeh.AzureEventHubs.Publish(ctx, req.Topic, messages, batchOpts)
}
// BulkPublish sends data to Azure Event Hubs in bulk.
func (aeh *AzureEventHubs) BulkPublish(ctx context.Context, req *pubsub.BulkPublishRequest) (pubsub.BulkPublishResponse, error) {
if _, ok := aeh.hubClients[req.Topic]; !ok {
if err := aeh.ensurePublisherClient(ctx, req.Topic); err != nil {
err = fmt.Errorf("error on establishing hub connection: %s", err)
return pubsub.NewBulkPublishResponse(req.Entries, err), err
var err error
if req.Topic == "" {
err = errors.New("parameter 'topic' is required")
return pubsub.NewBulkPublishResponse(req.Entries, err), err
}
// Batch options
batchOpts := &azeventhubs.EventDataBatchOptions{}
if val := req.Metadata[contribMetadata.MaxBulkPubBytesKey]; val != "" {
var maxBytes uint64
maxBytes, err = strconv.ParseUint(val, 10, 63)
if err == nil && maxBytes > 0 {
batchOpts.MaxBytes = maxBytes
}
}
// Create a slice of events to send.
events := make([]*eventhub.Event, len(req.Entries))
// Build the batch of messages
messages := make([]*azeventhubs.EventData, len(req.Entries))
for i, entry := range req.Entries {
events[i] = &eventhub.Event{Data: entry.Event}
if val, ok := entry.Metadata[partitionKeyMetadataKey]; ok {
events[i].PartitionKey = &val
messages[i] = &azeventhubs.EventData{
Body: entry.Event,
}
if entry.ContentType != "" {
messages[i].ContentType = ptr.Of(entry.ContentType)
}
if val := entry.Metadata["partitionKey"]; val != "" {
if batchOpts.PartitionKey != nil && *batchOpts.PartitionKey != val {
err = errors.New("cannot send messages to different partitions")
return pubsub.NewBulkPublishResponse(req.Entries, err), err
}
batchOpts.PartitionKey = &val
}
}
// Configure options for sending events.
opts := []eventhub.BatchOption{
eventhub.BatchWithMaxSizeInBytes(utils.GetElemOrDefaultFromMap(
req.Metadata, contribMetadata.MaxBulkPubBytesKey, int(eventhub.DefaultMaxMessageSizeInBytes))),
}
// Send events.
err := aeh.hubClients[req.Topic].SendBatch(ctx, eventhub.NewEventBatchIterator(events...), opts...)
// Publish the message
err = aeh.AzureEventHubs.Publish(ctx, req.Topic, messages, batchOpts)
if err != nil {
// Partial success is not supported by Azure Event Hubs.
// If an error occurs, all events are considered failed.
@ -611,113 +120,27 @@ func (aeh *AzureEventHubs) BulkPublish(ctx context.Context, req *pubsub.BulkPubl
}
// Subscribe receives data from Azure Event Hubs.
func (aeh *AzureEventHubs) Subscribe(subscribeCtx context.Context, req pubsub.SubscribeRequest, handler pubsub.Handler) error {
err := aeh.validateSubscriptionAttributes()
if err != nil {
return fmt.Errorf("error : error on subscribe %s", err)
func (aeh *AzureEventHubs) Subscribe(ctx context.Context, req pubsub.SubscribeRequest, handler pubsub.Handler) error {
topic := req.Topic
if topic == "" {
return errors.New("parameter 'topic' is required")
}
if aeh.metadata.EnableEntityManagement {
if err = aeh.ensureSubscription(subscribeCtx, req.Topic); err != nil {
return err
// Check if requireAllProperties is set and is truthy
getAllProperties := utils.IsTruthy(req.Metadata["requireAllProperties"])
// Start the subscription
// This is non-blocking
return aeh.AzureEventHubs.Subscribe(ctx, topic, getAllProperties, func(ctx context.Context, data []byte, metadata map[string]string) error {
res := pubsub.NewMessage{
Data: data,
Topic: topic,
Metadata: metadata,
}
}
// Set topic name, consumerID prefix for partition checkpoint lease blob path.
// This is needed to support multiple consumers for the topic using the same storage container.
leaserPrefixOpt := storage.WithPrefixInBlobPath(aeh.getStoragePrefixString(req.Topic))
leaserCheckpointer, err := storage.NewStorageLeaserCheckpointer(aeh.storageCredential, aeh.metadata.StorageAccountName, aeh.metadata.StorageContainerName, *aeh.azureEnvironment, leaserPrefixOpt)
if err != nil {
return err
}
processor, err := aeh.ensureSubscriberClient(subscribeCtx, req.Topic, leaserCheckpointer)
if err != nil {
return err
}
getAllProperties := false
if req.Metadata[requireAllProperties] != "" {
getAllProperties, err = strconv.ParseBool(req.Metadata[requireAllProperties])
if err != nil {
aeh.logger.Errorf("invalid value for metadata : %s . Error: %v.", requireAllProperties, err)
}
}
aeh.logger.Debugf("registering handler for topic %s", req.Topic)
_, err = processor.RegisterHandler(subscribeCtx,
func(_ context.Context, e *eventhub.Event) error {
// This component has built-in retries because Event Hubs doesn't support N/ACK for messages
b := aeh.backOffConfig.NewBackOffWithContext(subscribeCtx)
retryerr := retry.NotifyRecover(func() error {
aeh.logger.Debugf("Processing EventHubs event %s/%s", req.Topic, e.ID)
return subscribeHandler(subscribeCtx, req.Topic, getAllProperties, e, handler)
}, b, func(_ error, _ time.Duration) {
aeh.logger.Warnf("Error processing EventHubs event: %s/%s. Retrying...", req.Topic, e.ID)
}, func() {
aeh.logger.Warnf("Successfully processed EventHubs event after it previously failed: %s/%s", req.Topic, e.ID)
})
if retryerr != nil {
aeh.logger.Errorf("Too many failed attempts at processing Eventhubs event: %s/%s. Error: %v.", req.Topic, e.ID, err)
}
return retryerr
})
if err != nil {
return err
}
err = processor.StartNonBlocking(subscribeCtx)
if err != nil {
return err
}
aeh.eventProcessors[req.Topic] = processor
// Listen for context cancelation and stop processing messages
// This seems to be necessary because otherwise the processor isn't automatically closed on context cancelation
go func() {
<-subscribeCtx.Done()
stopCtx, stopCancel := context.WithTimeout(context.Background(), resourceGetTimeout)
stopErr := processor.Close(stopCtx)
stopCancel()
if stopErr != nil {
aeh.logger.Warnf("Error closing subscribe processor: %v", stopErr)
}
}()
return nil
return handler(ctx, &res)
})
}
func (aeh *AzureEventHubs) Close() (err error) {
flag := false
var ctx context.Context
var cancel context.CancelFunc
for topic, client := range aeh.hubClients {
ctx, cancel = context.WithTimeout(context.Background(), resourceGetTimeout)
err = client.Close(ctx)
cancel()
if err != nil {
flag = true
aeh.logger.Warnf("error closing publish client properly for topic/eventHub %s: %s", topic, err)
}
}
aeh.hubClients = map[string]*eventhub.Hub{}
for topic, client := range aeh.eventProcessors {
ctx, cancel = context.WithTimeout(context.Background(), resourceGetTimeout)
err = client.Close(ctx)
cancel()
if err != nil {
flag = true
aeh.logger.Warnf("error closing event processor host client properly for topic/eventHub %s: %s", topic, err)
}
}
aeh.eventProcessors = map[string]*eph.EventProcessorHost{}
if flag {
return errors.New("error closing event hub clients in a proper fashion")
}
return nil
}
func (aeh *AzureEventHubs) Features() []pubsub.Feature {
return nil
return aeh.AzureEventHubs.Close()
}

View File

@ -2,7 +2,7 @@
// +build integration_test
/*
Copyright 2021 The Dapr Authors
Copyright 2023 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
@ -28,7 +28,7 @@ import (
"github.com/dapr/components-contrib/metadata"
"github.com/dapr/components-contrib/pubsub"
"github.com/dapr/kit/logger"
kitLogger "github.com/dapr/kit/logger"
)
const (
@ -36,7 +36,7 @@ const (
// iotHubConnectionStringEnvKey defines the key containing the integration test connection string
// For the default EventHub endpoint for an Azure IoT Hub, it will resemble:
// Endpoint=sb://<iotHubGeneratedNamespace>.servicebus.windows.net/;SharedAccessKeyName=service;SharedAccessKey=<key>;EntityPath=<iotHubGeneratedPath>
// Endpoint=sb://<iotHubGeneratedNamespace>.servicebus.windows.net/;SharedAccessKeyName=service;SharedAccessKey=<key>;EntityPath=integration-test-topic
iotHubConnectionStringEnvKey = "AzureIotHubEventHubConnectionString"
iotHubConsumerGroupEnvKey = "AzureIotHubPubsubConsumerGroup"
iotHubNameEnvKey = "AzureIotHubName"
@ -66,17 +66,18 @@ func createIotHubPubsubMetadata() pubsub.Metadata {
}
func testReadIotHubEvents(t *testing.T) {
logger := logger.NewLogger("pubsub.azure.eventhubs.integration.test")
logger := kitLogger.NewLogger("pubsub.azure.eventhubs.integration.test")
logger.SetOutputLevel(kitLogger.DebugLevel)
eh := NewAzureEventHubs(logger).(*AzureEventHubs)
err := eh.Init(createIotHubPubsubMetadata())
assert.Nil(t, err)
assert.NoError(t, err)
// Invoke az CLI via bash script to send test IoT device events
// Requires the AZURE_CREDENTIALS environment variable to be already set (output of `az ad sp create-for-rbac`)
cmd := exec.Command("/bin/bash", "../../../tests/scripts/send-iot-device-events.sh")
cmd.Env = append(os.Environ(), fmt.Sprintf("IOT_HUB_NAME=%s", os.Getenv(iotHubNameEnvKey)))
out, err := cmd.CombinedOutput()
assert.Nil(t, err, "Error in send-iot-device-events.sh:\n%s", out)
assert.NoError(t, err, "Error in send-iot-device-events.sh:\n%s", string(out))
// Setup subscription to capture messages in a closure so that test asserts can be
// performed on the main thread, including the case where the handler is never invoked.
@ -87,13 +88,13 @@ func testReadIotHubEvents(t *testing.T) {
}
req := pubsub.SubscribeRequest{
Topic: testTopic, // TODO: Handle Topic configuration after EventHubs pubsub rewrite #951
Topic: testTopic,
Metadata: map[string]string{
"requireAllProperties": "true",
},
}
err = eh.Subscribe(context.Background(), req, handler)
assert.Nil(t, err)
assert.NoError(t, err)
// Note: azure-event-hubs-go SDK defaultLeasePersistenceInterval is 5s
// Sleep long enough so that the azure event hubs SDK has time to persist updated checkpoints
@ -109,14 +110,14 @@ func testReadIotHubEvents(t *testing.T) {
// Verify expected IoT Hub device event metadata exists
// TODO: add device messages than can populate the sysPropPartitionKey and sysPropIotHubConnectionModuleID metadata
assert.Contains(t, r.Metadata, sysPropSequenceNumber, "IoT device event missing: %s", sysPropSequenceNumber)
assert.Contains(t, r.Metadata, sysPropEnqueuedTime, "IoT device event missing: %s", sysPropEnqueuedTime)
assert.Contains(t, r.Metadata, sysPropOffset, "IoT device event missing: %s", sysPropOffset)
assert.Contains(t, r.Metadata, sysPropIotHubDeviceConnectionID, "IoT device event missing: %s", sysPropIotHubDeviceConnectionID)
assert.Contains(t, r.Metadata, sysPropIotHubAuthGenerationID, "IoT device event missing: %s", sysPropIotHubAuthGenerationID)
assert.Contains(t, r.Metadata, sysPropIotHubConnectionAuthMethod, "IoT device event missing: %s", sysPropIotHubConnectionAuthMethod)
assert.Contains(t, r.Metadata, sysPropIotHubEnqueuedTime, "IoT device event missing: %s", sysPropIotHubEnqueuedTime)
assert.Contains(t, r.Metadata, sysPropMessageID, "IoT device event missing: %s", sysPropMessageID)
assert.Contains(t, r.Metadata, "x-opt-sequence-number", "IoT device event missing: %s", "x-opt-sequence-number")
assert.Contains(t, r.Metadata, "x-opt-enqueued-time", "IoT device event missing: %s", "x-opt-enqueued-time")
assert.Contains(t, r.Metadata, "x-opt-offset", "IoT device event missing: %s", "x-opt-offset")
assert.Contains(t, r.Metadata, "iothub-connection-device-id", "IoT device event missing: %s", "iothub-connection-device-id")
assert.Contains(t, r.Metadata, "iothub-connection-auth-generation-id", "IoT device event missing: %s", "iothub-connection-auth-generation-id")
assert.Contains(t, r.Metadata, "iothub-connection-auth-method", "IoT device event missing: %s", "iothub-connection-auth-method")
assert.Contains(t, r.Metadata, "iothub-enqueuedtime", "IoT device event missing: %s", "iothub-enqueuedtime")
assert.Contains(t, r.Metadata, "message-id", "IoT device event missing: %s", "message-id")
// Verify sent custom application property is received in IoT Hub device event metadata
assert.Contains(t, r.Metadata, applicationProperty, "IoT device event missing: %s", applicationProperty)

View File

@ -1,324 +0,0 @@
/*
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 eventhubs
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/dapr/components-contrib/metadata"
"github.com/dapr/components-contrib/pubsub"
"github.com/dapr/kit/logger"
)
var testLogger = logger.NewLogger("test")
func TestParseEventHubsMetadata(t *testing.T) {
t.Run("test valid connectionString configuration", func(t *testing.T) {
props := map[string]string{"connectionString": "fake"}
metadata := pubsub.Metadata{Base: metadata.Base{Properties: props}}
m, err := parseEventHubsMetadata(metadata)
assert.NoError(t, err)
assert.Equal(t, "fake", m.ConnectionString)
})
t.Run("test namespace given", func(t *testing.T) {
props := map[string]string{"eventHubNamespace": "fake"}
metadata := pubsub.Metadata{Base: metadata.Base{Properties: props}}
m, err := parseEventHubsMetadata(metadata)
assert.NoError(t, err)
assert.Equal(t, "fake", m.EventHubNamespace)
})
t.Run("test both connectionString and eventHubNamespace given", func(t *testing.T) {
props := map[string]string{"connectionString": "fake", "eventHubNamespace": "fake"}
metadata := pubsub.Metadata{Base: metadata.Base{Properties: props}}
_, err := parseEventHubsMetadata(metadata)
assert.Error(t, err)
assert.Equal(t, bothConnectionStringNamespaceErrorMsg, err.Error())
})
t.Run("test missing metadata", func(t *testing.T) {
props := map[string]string{}
metadata := pubsub.Metadata{Base: metadata.Base{Properties: props}}
_, err := parseEventHubsMetadata(metadata)
assert.Error(t, err)
assert.Equal(t, missingConnectionStringNamespaceErrorMsg, err.Error())
})
}
func TestValidateSubscriptionAttributes(t *testing.T) {
t.Run("test valid configuration", func(t *testing.T) {
props := map[string]string{"connectionString": "fake", "consumerID": "fake", "storageAccountName": "account", "storageAccountKey": "key", "storageContainerName": "container"}
metadata := pubsub.Metadata{Base: metadata.Base{Properties: props}}
m, err := parseEventHubsMetadata(metadata)
assert.NoError(t, err)
aeh := &AzureEventHubs{logger: testLogger, metadata: m}
assert.Equal(t, m.ConnectionString, "fake")
assert.Equal(t, m.StorageAccountName, "account")
assert.Equal(t, m.StorageAccountKey, "key")
assert.Equal(t, m.StorageContainerName, "container")
assert.Equal(t, m.ConsumerGroup, "fake")
err = aeh.validateSubscriptionAttributes()
assert.NoError(t, err)
})
type invalidConfigTestCase struct {
name string
config map[string]string
errMsg string
}
invalidConfigTestCases := []invalidConfigTestCase{
{
"missing consumerID",
map[string]string{"connectionString": "fake", "storageAccountName": "account", "storageAccountKey": "key", "storageContainerName": "container"},
missingConsumerIDErrorMsg,
},
{
"missing storageAccountName",
map[string]string{"consumerID": "fake", "connectionString": "fake", "storageAccountKey": "key", "storageContainerName": "container"},
missingStorageAccountNameErrorMsg,
},
{
"missing storageAccountKey",
map[string]string{"consumerID": "fake", "connectionString": "fake", "storageAccountName": "name", "storageContainerName": "container"},
missingStorageAccountKeyErrorMsg,
},
{
"missing storageContainerName",
map[string]string{"consumerID": "fake", "connectionString": "fake", "storageAccountName": "name", "storageAccountKey": "key"},
missingStorageContainerNameErrorMsg,
},
}
for _, c := range invalidConfigTestCases {
t.Run(c.name, func(t *testing.T) {
metadata := pubsub.Metadata{Base: metadata.Base{Properties: c.config}}
m, err := parseEventHubsMetadata(metadata)
aeh := &AzureEventHubs{logger: testLogger, metadata: m}
require.NoError(t, err)
err = aeh.validateSubscriptionAttributes()
assert.Error(t, err)
assert.Equal(t, c.errMsg, err.Error())
})
}
}
func TestValidateEnitityManagementMetadata(t *testing.T) {
t.Run("test valid configuration", func(t *testing.T) {
props := map[string]string{"eventHubNamespace": "fake", "messageRetentionInDays": "2", "partitionCount": "3", "resourceGroupName": "rg", "subscriptionID": "id"}
metadata := pubsub.Metadata{Base: metadata.Base{Properties: props}}
m, err := parseEventHubsMetadata(metadata)
require.NoError(t, err)
aeh := &AzureEventHubs{logger: testLogger, metadata: m}
assert.Equal(t, "fake", m.EventHubNamespace)
assert.Equal(t, int32(2), m.MessageRetentionInDays)
assert.Equal(t, int32(3), m.PartitionCount)
err = aeh.validateEnitityManagementMetadata()
assert.NoError(t, err)
assert.Equal(t, int32(2), m.MessageRetentionInDays)
assert.Equal(t, int32(3), m.PartitionCount)
assert.Equal(t, "rg", m.ResourceGroupName)
assert.Equal(t, "id", m.SubscriptionID)
})
t.Run("test valid configuration", func(t *testing.T) {
props := map[string]string{"eventHubNamespace": "fake", "resourceGroupName": "rg", "subscriptionID": "id"}
metadata := pubsub.Metadata{Base: metadata.Base{Properties: props}}
m, err := parseEventHubsMetadata(metadata)
require.NoError(t, err)
aeh := &AzureEventHubs{logger: testLogger, metadata: m}
assert.Equal(t, "fake", m.EventHubNamespace)
assert.Equal(t, int32(0), m.MessageRetentionInDays)
assert.Equal(t, int32(0), m.PartitionCount)
err = aeh.validateEnitityManagementMetadata()
assert.NoError(t, err)
assert.Equal(t, int32(1), m.MessageRetentionInDays)
assert.Equal(t, int32(1), m.PartitionCount)
assert.Equal(t, "rg", m.ResourceGroupName)
assert.Equal(t, "id", m.SubscriptionID)
})
type invalidConfigTestCase struct {
name string
config map[string]string
messageRetentionInDays int32
partitionCount int32
errMsg string
}
invalidConfigTestCases := []invalidConfigTestCase{
{
"negative message rentention days",
map[string]string{"eventHubNamespace": "fake", "messageRetentionInDays": "-2", "resourceGroupName": "rg", "subscriptionID": "id"},
defaultMessageRetentionInDays,
defaultPartitionCount,
"",
},
{
"more than max message rentention days",
map[string]string{"eventHubNamespace": "fake", "messageRetentionInDays": "91", "resourceGroupName": "rg", "subscriptionID": "id"},
defaultMessageRetentionInDays,
defaultPartitionCount,
"",
},
{
"negative partition count",
map[string]string{"eventHubNamespace": "fake", "partitionCount": "-2", "resourceGroupName": "rg", "subscriptionID": "id"},
defaultMessageRetentionInDays,
defaultPartitionCount,
"",
},
{
"more than max partition count",
map[string]string{"eventHubNamespace": "fake", "partitionCount": "1030", "resourceGroupName": "rg", "subscriptionID": "id"},
defaultMessageRetentionInDays,
defaultPartitionCount,
"",
},
{
"missingResourceGroupName",
map[string]string{"eventHubNamespace": "fake", "subscriptionID": "id"},
defaultMessageRetentionInDays,
defaultPartitionCount,
missingResourceGroupNameMsg,
},
{
"missingSubscriptionID",
map[string]string{"eventHubNamespace": "fake", "resourceGroupName": "id"},
defaultMessageRetentionInDays,
defaultPartitionCount,
missingSubscriptionIDMsg,
},
}
for _, c := range invalidConfigTestCases {
t.Run(c.name, func(t *testing.T) {
metadata := pubsub.Metadata{Base: metadata.Base{Properties: c.config}}
m, err := parseEventHubsMetadata(metadata)
aeh := &AzureEventHubs{logger: testLogger, metadata: m}
require.NoError(t, err)
err = aeh.validateEnitityManagementMetadata()
if c.errMsg != "" {
assert.Error(t, err)
assert.Equal(t, c.errMsg, err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, c.messageRetentionInDays, aeh.metadata.MessageRetentionInDays)
assert.Equal(t, c.partitionCount, aeh.metadata.PartitionCount)
})
}
}
func TestGetStoragePrefixString(t *testing.T) {
props := map[string]string{"connectionString": "fake", "consumerID": "test"}
metadata := pubsub.Metadata{Base: metadata.Base{Properties: props}}
m, err := parseEventHubsMetadata(metadata)
require.NoError(t, err)
aeh := &AzureEventHubs{logger: testLogger, metadata: m}
actual := aeh.getStoragePrefixString("topic")
assert.Equal(t, "dapr-topic-test-", actual)
}
func TestValidateAndGetHubName(t *testing.T) {
t.Run("valid connectionString with hub name", func(t *testing.T) {
connectionString := "Endpoint=sb://fake.servicebus.windows.net/;SharedAccessKeyName=fakeKey;SharedAccessKey=key;EntityPath=testHub"
h, err := validateAndGetHubName(connectionString)
assert.NoError(t, err)
assert.Equal(t, "testHub", h)
})
t.Run("valid connectionString without hub name", func(t *testing.T) {
connectionString := "Endpoint=sb://fake.servicebus.windows.net/;SharedAccessKeyName=fakeKey;SharedAccessKey=key"
h, err := validateAndGetHubName(connectionString)
assert.NoError(t, err)
assert.Empty(t, h)
})
t.Run("invalid connectionString ", func(t *testing.T) {
connectionString := "Endpoint=sb://fake.servicebus.windows.net/;ShareKeyName=fakeKey;SharedAccessKey=key"
_, err := validateAndGetHubName(connectionString)
assert.Error(t, err)
})
}
func TestConstructConnectionStringFromTopic(t *testing.T) {
t.Run("valid connectionString without hub name", func(t *testing.T) {
connectionString := "Endpoint=sb://fake.servicebus.windows.net/;SharedAccessKeyName=fakeKey;SharedAccessKey=key"
topic := "testHub"
aeh := &AzureEventHubs{logger: testLogger, metadata: &azureEventHubsMetadata{ConnectionString: connectionString}}
c, err := aeh.constructConnectionStringFromTopic(topic)
assert.NoError(t, err)
assert.Equal(t, connectionString+";EntityPath=testHub", c)
})
t.Run("valid connectionString with hub name", func(t *testing.T) {
connectionString := "Endpoint=sb://fake.servicebus.windows.net/;SharedAccessKeyName=fakeKey;SharedAccessKey=key;EntityPath=testHub"
topic := "testHub"
aeh := &AzureEventHubs{logger: testLogger, metadata: &azureEventHubsMetadata{ConnectionString: connectionString}}
c, err := aeh.constructConnectionStringFromTopic(topic)
assert.NoError(t, err)
assert.Equal(t, connectionString, c)
})
t.Run("invalid connectionString with hub name", func(t *testing.T) {
connectionString := "Endpoint=sb://fake.servicebus.windows.net/;SharedAccessKeyName=fakeKey;ShareKey=key;EntityPath=testHub"
topic := "testHub"
aeh := &AzureEventHubs{logger: testLogger, metadata: &azureEventHubsMetadata{ConnectionString: connectionString}}
c, err := aeh.constructConnectionStringFromTopic(topic)
assert.Error(t, err)
assert.Equal(t, "", c)
})
t.Run("valid connectionString with different hub name", func(t *testing.T) {
connectionString := "Endpoint=sb://fake.servicebus.windows.net/;SharedAccessKeyName=fakeKey;SharedAccessKey=key;EntityPath=testHub"
topic := "differentHub"
aeh := &AzureEventHubs{logger: testLogger, metadata: &azureEventHubsMetadata{ConnectionString: connectionString}}
c, err := aeh.constructConnectionStringFromTopic(topic)
assert.Error(t, err)
assert.Equal(t, (fmt.Sprintf(differentTopicConnectionStringErrorTmpl, topic)), err.Error())
assert.Equal(t, "", c)
})
}

View File

@ -13,6 +13,12 @@ limitations under the License.
package pubsub
import (
"encoding/json"
"fmt"
"strings"
)
// PublishRequest is the request to publish a message.
type PublishRequest struct {
Data []byte `json:"data"`
@ -45,6 +51,16 @@ type NewMessage struct {
ContentType *string `json:"contentType,omitempty"`
}
// String implements fmt.Stringer and it's useful for debugging.
func (m NewMessage) String() string {
ct := "(nil)"
if m.ContentType != nil {
ct = *m.ContentType
}
md, _ := json.Marshal(m.Metadata)
return fmt.Sprintf("[NewMessage] topic='%s' data='%s' content-type='%s' metadata=%s", m.Topic, string(m.Data), ct, md)
}
// BulkMessage represents bulk message arriving from a message bus instance.
type BulkMessage struct {
Entries []BulkMessageEntry `json:"entries"`
@ -52,6 +68,18 @@ type BulkMessage struct {
Metadata map[string]string `json:"metadata"`
}
// String implements fmt.Stringer and it's useful for debugging.
func (m BulkMessage) String() string {
md, _ := json.Marshal(m.Metadata)
b := strings.Builder{}
b.WriteString(fmt.Sprintf("[BulkMessage] topic='%s' metadata=%s entries=%d", m.Topic, md, len(m.Entries)))
for i, e := range m.Entries {
b.WriteString(fmt.Sprintf("\n%d: ", i))
b.WriteString(e.String())
}
return b.String()
}
// BulkMessageEntry represents a single message inside a bulk request.
type BulkMessageEntry struct {
EntryId string `json:"entryId"` //nolint:stylecheck
@ -60,6 +88,12 @@ type BulkMessageEntry struct {
Metadata map[string]string `json:"metadata"`
}
// String implements fmt.Stringer and it's useful for debugging.
func (m BulkMessageEntry) String() string {
md, _ := json.Marshal(m.Metadata)
return fmt.Sprintf("[BulkMessageEntry] entryId='%s' data='%s' content-type='%s' metadata=%s", m.EntryId, string(m.Event), m.ContentType, md)
}
// BulkSubscribeConfig represents the configuration for bulk subscribe.
// It depends on specific componets to support these.
type BulkSubscribeConfig struct {

View File

@ -17,10 +17,9 @@ require (
github.com/AdhityaRamadhanus/fasthttpcors v0.0.0-20170121111917-d4c07198763a // indirect
github.com/Azure/azure-amqp-common-go/v4 v4.0.0 // indirect
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect
github.com/Azure/azure-storage-blob-go v0.15.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
github.com/Azure/go-autorest/autorest v0.11.28 // indirect

View File

@ -40,16 +40,14 @@ github.com/Azure/azure-amqp-common-go/v4 v4.0.0/go.mod h1:4+qRvizIo4+CbGG552O6a8
github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 h1:sVW/AFBTGyJxDaMYlq0ct3jUXTtj12tQ6zE2GZUgVQw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 h1:VuHAcMq8pU1IWNT/m5yRaGqbK0BiQKHT8X4DTp9CHdI=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0/go.mod h1:tZoQYdDZNOiIjdSn0dVWVfl0NEPGOJqVLzSrcFk4Is0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 h1:t/W5MYAuQy81cvM8VUNfRLzhtKpXhVUAN7Cd7KVbTyc=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0/go.mod h1:NBanQUfSWiWn3QEpWDTCU0IjBECKOYvl2R8xdRtMtiM=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1 h1:YvQv9Mz6T8oR5ypQOL6erY0Z5t71ak1uHV4QFokCOZk=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1/go.mod h1:c6WvOhtmjNUWbLfOG1qxM/q0SPvQNSVJvolm+C52dIU=
github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk=
github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd h1:b3wyxBl3vvr15tUAziPBPK354y+LSdfPCpex5oBttHo=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd/go.mod h1:K6am8mT+5iFXgingS9LUc7TmbsW6XBw3nxaRyaMyWc8=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
@ -57,7 +55,6 @@ github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM=
github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk=
github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U=
@ -162,7 +159,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
@ -275,7 +271,6 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@ -364,7 +359,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@ -575,8 +569,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@ -654,7 +646,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@ -923,7 +914,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=

View File

@ -18,12 +18,11 @@ require (
github.com/AdhityaRamadhanus/fasthttpcors v0.0.0-20170121111917-d4c07198763a // indirect
github.com/Azure/azure-amqp-common-go/v4 v4.0.0 // indirect
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/Azure/azure-sdk-for-go v67.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos v0.3.3 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect
github.com/Azure/azure-storage-blob-go v0.15.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
github.com/Azure/go-autorest/autorest v0.11.28 // indirect

View File

@ -40,18 +40,16 @@ github.com/Azure/azure-amqp-common-go/v4 v4.0.0/go.mod h1:4+qRvizIo4+CbGG552O6a8
github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
github.com/Azure/azure-sdk-for-go v67.0.0+incompatible h1:SVBwznSETB0Sipd0uyGJr7khLhJOFRUEUb+0JgkCvDo=
github.com/Azure/azure-sdk-for-go v67.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 h1:sVW/AFBTGyJxDaMYlq0ct3jUXTtj12tQ6zE2GZUgVQw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 h1:VuHAcMq8pU1IWNT/m5yRaGqbK0BiQKHT8X4DTp9CHdI=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0/go.mod h1:tZoQYdDZNOiIjdSn0dVWVfl0NEPGOJqVLzSrcFk4Is0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 h1:t/W5MYAuQy81cvM8VUNfRLzhtKpXhVUAN7Cd7KVbTyc=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0/go.mod h1:NBanQUfSWiWn3QEpWDTCU0IjBECKOYvl2R8xdRtMtiM=
github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos v0.3.3 h1:x1shk+tVZ6kLwIQMn4r+pdz8szo3mA0jd8STmgh+aRk=
github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos v0.3.3/go.mod h1:Fy3bbChFm4cZn6oIxYYqKB2FG3rBDxk3NZDLDJCHl+Q=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk=
github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd h1:b3wyxBl3vvr15tUAziPBPK354y+LSdfPCpex5oBttHo=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd/go.mod h1:K6am8mT+5iFXgingS9LUc7TmbsW6XBw3nxaRyaMyWc8=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
@ -59,7 +57,6 @@ github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM=
github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk=
github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U=
@ -166,7 +163,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
@ -279,7 +275,6 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@ -369,7 +364,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@ -580,8 +574,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@ -659,7 +651,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@ -928,7 +919,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=

View File

@ -7,11 +7,9 @@ This project aims to test the Azure Event Hubs bindings component under various
- Test sending /receiving data between single partition
- Start an app with 1 sender and 1 receiver
- Provide multiple partitions but store data in one partition
- Receiver should only receive message from one partition
- Sends 100+ unique messages
- Simulates periodic errors
- Confirm that all expected messages were received
- Confirm that receiver does not receive messages from other than one partition
- Test sending /receiving data multiple partitions/sender and receivers
- Start an app with 1 sender and 1 receiver
@ -36,4 +34,4 @@ This project aims to test the Azure Event Hubs bindings component under various
### Running the tests
This must be run in the GitHub Actions Workflow configured for test infrastructure setup.
This must be run in the GitHub Actions Workflow configured for test infrastructure setup.

View File

@ -24,10 +24,6 @@ spec:
name: AzureBlobStorageAccessKey
value: AzureBlobStorageAccessKey
- name: storageContainerName # Azure Storage Container Name
secretKeyRef:
name: AzureEventHubsBindingsContainer
value: AzureEventHubsBindingsContainer
- name: partitionID
value: 0
value: eventhubs-bindings-container-c1
auth:
secretStore: envvar-secret-store

View File

@ -17,17 +17,6 @@ spec:
secretKeyRef:
name: AzureEventHubsBindingsConsumerGroup
value: AzureEventHubsBindingsConsumerGroup
- name: storageAccountName # Azure Storage Account Name
secretKeyRef:
name: AzureBlobStorageAccount
value: AzureBlobStorageAccount
- name: storageAccountKey # Azure Storage Account Key
secretKeyRef:
name: AzureBlobStorageAccessKey
value: AzureBlobStorageAccessKey
- name: storageContainerName # Azure Storage Container Name
secretKeyRef:
name: AzureEventHubsBindingsContainer
value: AzureEventHubsBindingsContainer
# No storage account configuration here since this is only an output binding
auth:
secretStore: envvar-secret-store

View File

@ -1,7 +1,7 @@
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: azure-partition0-binding
name: azure-partitioned-binding
namespace: default
spec:
type: bindings.azure.eventhubs
@ -26,10 +26,6 @@ spec:
name: AzureBlobStorageAccessKey
value: AzureBlobStorageAccessKey
- name: storageContainerName # Azure Storage Container Name
secretKeyRef:
name: AzureEventHubsBindingsContainer
value: AzureEventHubsBindingsContainer
- name: partitionID
value: 0
value: eventhubs-bindings-container-c3
auth:
secretStore: envvar-secret-store

View File

@ -1,36 +0,0 @@
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: azure-partition1-binding
namespace: default
spec:
type: bindings.azure.eventhubs
version: v1
metadata:
- name: consumerID
value: ehcertification2
- name: connectionString # Azure EventHubs connection string
secretKeyRef:
name: AzureEventHubsBindingsConnectionString
value: AzureEventHubsBindingsConnectionString
- name: consumerGroup # EventHubs consumer group
secretKeyRef:
name: AzureEventHubsBindingsConsumerGroup
value: AzureEventHubsBindingsConsumerGroup
- name: storageAccountName # Azure Storage Account Name
secretKeyRef:
name: AzureBlobStorageAccount
value: AzureBlobStorageAccount
- name: storageAccountKey # Azure Storage Account Key
secretKeyRef:
name: AzureBlobStorageAccessKey
value: AzureBlobStorageAccessKey
- name: storageContainerName # Azure Storage Container Name
secretKeyRef:
name: AzureEventHubsBindingsContainer
value: AzureEventHubsBindingsContainer
- name: partitionID
value: 1
auth:
secretStore: envvar-secret-store

View File

@ -26,10 +26,6 @@ spec:
name: AzureBlobStorageAccessKey
key: AzureBlobStorageAccessKey
- name: storageContainerName
secretKeyRef:
name: AzureEventHubsBindingsContainer
key: AzureEventHubsBindingsContainer
- name: PartitionID
value: 0
value: eventhubs-bindings-container-iot
auth:
secretStore: envvar-secret-store

View File

@ -10,19 +10,19 @@ spec:
- name: eventHubNamespace
secretKeyRef:
name: AzureEventHubsBindingsNamespace
key: AzureEventHubsBindingsNamespace
- name: connectionString
value: AzureEventHubsBindingsNamespace
- name: eventHub
secretKeyRef:
name: AzureEventHubsBindingsConnectionString
value: AzureEventHubsBindingsConnectionString
name: AzureEventHubsBindingsHub
value: AzureEventHubsBindingsHub
- name: azureTenantId
secretKeyRef:
name: AzureCertificationTenantId
key: AzureCertificationTenantId
key: AzureCertificationTenantId
- name: azureClientId
secretKeyRef:
name: AzureCertificationServicePrincipalClientId
key: AzureCertificationServicePrincipalClientId
key: AzureCertificationServicePrincipalClientId
- name: azureClientSecret
secretKeyRef:
name: AzureCertificationServicePrincipalClientSecret
@ -43,15 +43,12 @@ spec:
secretKeyRef:
name: AzureBlobStorageAccount
key: AzureBlobStorageAccount
- name: storageAccountKey
secretKeyRef:
name: AzureBlobStorageAccessKey
key: AzureBlobStorageAccessKey
# Use Azure AD for the storage account too
#- name: storageAccountKey
# secretKeyRef:
# name: AzureBlobStorageAccessKey
# key: AzureBlobStorageAccessKey
- name: storageContainerName
secretKeyRef:
name: AzureEventHubsBindingsContainer
value: AzureEventHubsBindingsContainer
- name: partitionID
value: 0
value: eventhubs-bindings-container-sp
auth:
secretStore: envvar-secret-store

View File

@ -11,8 +11,19 @@
# limitations under the License.
# ------------------------------------------------------------
set -e
CONTAINER_NAME=${1}
if [ -z "$CONTAINER_NAME" ]; then
echo "Usage: ./deleteeventhub.sh [container-name]"
exit 1
fi
echo "Deleting container $CONTAINER_NAME"
# login to azure
az login --service-principal -u $AzureCertificationServicePrincipalClientId -p $AzureCertificationServicePrincipalClientSecret --tenant $AzureCertificationTenantId
# delete container used by the consumer
az storage container delete --account-key $AzureBlobStorageAccessKey --account-name $AzureBlobStorageAccount --name $AzureEventHubsBindingsContainer
az storage container delete --account-key $AzureBlobStorageAccessKey --account-name $AzureBlobStorageAccount --name "$CONTAINER_NAME"

View File

@ -18,6 +18,7 @@ import (
"fmt"
"os"
"os/exec"
"strconv"
"testing"
"time"
@ -27,16 +28,8 @@ import (
"go.uber.org/multierr"
"github.com/dapr/components-contrib/bindings"
bindings_loader "github.com/dapr/dapr/pkg/components/bindings"
"github.com/dapr/dapr/pkg/runtime"
dapr_testing "github.com/dapr/dapr/pkg/testing"
"github.com/dapr/kit/logger"
secretstore_env "github.com/dapr/components-contrib/secretstores/local/env"
secretstores_loader "github.com/dapr/dapr/pkg/components/secretstores"
"github.com/dapr/components-contrib/bindings/azure/eventhubs"
secretstore_env "github.com/dapr/components-contrib/secretstores/local/env"
"github.com/dapr/components-contrib/tests/certification/embedded"
"github.com/dapr/components-contrib/tests/certification/flow"
"github.com/dapr/components-contrib/tests/certification/flow/app"
@ -44,8 +37,13 @@ import (
"github.com/dapr/components-contrib/tests/certification/flow/sidecar"
"github.com/dapr/components-contrib/tests/certification/flow/simulate"
"github.com/dapr/components-contrib/tests/certification/flow/watcher"
bindings_loader "github.com/dapr/dapr/pkg/components/bindings"
secretstores_loader "github.com/dapr/dapr/pkg/components/secretstores"
"github.com/dapr/dapr/pkg/runtime"
dapr_testing "github.com/dapr/dapr/pkg/testing"
dapr "github.com/dapr/go-sdk/client"
"github.com/dapr/go-sdk/service/common"
"github.com/dapr/kit/logger"
)
const (
@ -62,7 +60,7 @@ func TestSinglePartition(t *testing.T) {
httpPort := ports[1]
appPort := ports[2]
consumerGroup1 := watcher.NewUnordered()
received := watcher.NewUnordered()
metadata := map[string]string{
messageKey: "test",
@ -71,7 +69,7 @@ func TestSinglePartition(t *testing.T) {
sendAndReceive := func(metadata map[string]string, messages ...*watcher.Watcher) flow.Runnable {
_, hasKey := metadata[messageKey]
return func(ctx flow.Context) error {
client, err := dapr.NewClientWithPort(fmt.Sprintf("%d", grpcPort))
client, err := dapr.NewClientWithPort(strconv.Itoa(grpcPort))
require.NoError(t, err, "dapr init failed")
// Define what is expected
@ -79,7 +77,7 @@ func TestSinglePartition(t *testing.T) {
for i := 0; i < numMessages; i++ {
outputmsg[i] = fmt.Sprintf("output binding: Message %03d", i)
}
consumerGroup1.ExpectStrings(outputmsg...)
received.ExpectStrings(outputmsg...)
time.Sleep(20 * time.Second)
if !hasKey {
metadata[messageKey] = uuid.NewString()
@ -99,7 +97,7 @@ func TestSinglePartition(t *testing.T) {
}
// Assert the observed messages
consumerGroup1.Assert(ctx, time.Minute)
received.Assert(ctx, time.Minute)
return nil
}
}
@ -111,22 +109,22 @@ func TestSinglePartition(t *testing.T) {
// Setup the binding endpoints
err = multierr.Combine(err,
s.AddBindingInvocationHandler("azure-single-partition-binding", func(_ context.Context, in *common.BindingEvent) ([]byte, error) {
consumerGroup1.Observe(string(in.Data))
if err := sim(); err != nil {
return nil, err
}
received.Observe(string(in.Data))
ctx.Logf("Receiving eventhubs message: %s", string(in.Data))
return []byte("{}"), nil
}))
return err
}
deleteEventhub := func(ctx flow.Context) error {
output, err := exec.Command("/bin/sh", "deleteeventhub.sh").Output()
output, err := exec.Command("/bin/sh", "deleteeventhub.sh", "eventhubs-bindings-container-c1").Output()
assert.Nil(t, err, "Error in deleteeventhub.sh.:\n%s", string(output))
return nil
}
// Flow of events: Start app, sidecar, interrupt network to check reconnection, send and receive
flow.New(t, "eventhubs binding authentication using connection string single partition").
flow.New(t, "eventhubs binding authentication using connection string").
Step(app.Run("app", fmt.Sprintf(":%d", appPort), application)).
Step(sidecar.Run("sidecar",
embedded.WithAppProtocol(runtime.HTTPProtocol, appPort),
@ -135,9 +133,11 @@ func TestSinglePartition(t *testing.T) {
embedded.WithComponentsPath("./components/binding/consumer1"),
componentRuntimeOptions(),
)).
Step("wait", flow.Sleep(15*time.Second)).
Step("interrupt network", network.InterruptNetwork(30*time.Second, nil, nil, "443", "5671", "5672")).
Step("send and wait", sendAndReceive(metadata)).
Step("delete containers", deleteEventhub).
Step("delete container", deleteEventhub).
Step("wait", flow.Sleep(5*time.Second)).
Run()
}
@ -147,7 +147,7 @@ func TestEventhubBindingSerivcePrincipalAuth(t *testing.T) {
httpPort := ports[1]
appPort := ports[2]
consumerGroup1 := watcher.NewUnordered()
received := watcher.NewUnordered()
metadata := map[string]string{
messageKey: "test",
@ -156,7 +156,7 @@ func TestEventhubBindingSerivcePrincipalAuth(t *testing.T) {
sendAndReceive := func(metadata map[string]string, messages ...*watcher.Watcher) flow.Runnable {
_, hasKey := metadata[messageKey]
return func(ctx flow.Context) error {
client, err := dapr.NewClientWithPort(fmt.Sprintf("%d", grpcPort))
client, err := dapr.NewClientWithPort(strconv.Itoa(grpcPort))
require.NoError(t, err, "dapr init failed")
// Define what is expected
@ -164,7 +164,7 @@ func TestEventhubBindingSerivcePrincipalAuth(t *testing.T) {
for i := 0; i < numMessages; i++ {
outputmsg[i] = fmt.Sprintf("output binding: Message %03d", i)
}
consumerGroup1.ExpectStrings(outputmsg...)
received.ExpectStrings(outputmsg...)
time.Sleep(20 * time.Second)
if !hasKey {
metadata[messageKey] = uuid.NewString()
@ -184,7 +184,7 @@ func TestEventhubBindingSerivcePrincipalAuth(t *testing.T) {
}
// Assert the observed messages
consumerGroup1.Assert(ctx, time.Minute)
received.Assert(ctx, time.Minute)
return nil
}
}
@ -196,10 +196,10 @@ func TestEventhubBindingSerivcePrincipalAuth(t *testing.T) {
// Setup the binding endpoints
err = multierr.Combine(err,
s.AddBindingInvocationHandler("azure-eventhubs-binding", func(_ context.Context, in *common.BindingEvent) ([]byte, error) {
consumerGroup1.Observe(string(in.Data))
if err := sim(); err != nil {
return nil, err
}
received.Observe(string(in.Data))
ctx.Logf("Receiving eventhubs message: %s", string(in.Data))
return []byte("{}"), nil
}))
@ -207,7 +207,7 @@ func TestEventhubBindingSerivcePrincipalAuth(t *testing.T) {
}
deleteEventhub := func(ctx flow.Context) error {
output, err := exec.Command("/bin/sh", "deleteeventhub.sh").Output()
output, err := exec.Command("/bin/sh", "deleteeventhub.sh", "eventhubs-bindings-container-sp").Output()
assert.Nil(t, err, "Error in deleteeventhub.sh.:\n%s", string(output))
return nil
}
@ -221,8 +221,10 @@ func TestEventhubBindingSerivcePrincipalAuth(t *testing.T) {
embedded.WithComponentsPath("./components/binding/serviceprincipal"),
componentRuntimeOptions(),
)).
Step("wait", flow.Sleep(15*time.Second)).
Step("send and wait", sendAndReceive(metadata)).
Step("delete containers", deleteEventhub).
Step("wait", flow.Sleep(5*time.Second)).
Run()
}
@ -232,7 +234,7 @@ func TestEventhubBindingIOTHub(t *testing.T) {
httpPort := ports[1]
appPort := ports[2]
consumerGroup1 := watcher.NewUnordered()
received := watcher.NewUnordered()
// Application logic that tracks messages from eventhub.
application := func(ctx flow.Context, s common.Service) (err error) {
@ -241,10 +243,10 @@ func TestEventhubBindingIOTHub(t *testing.T) {
// Setup the binding endpoints
err = multierr.Combine(err,
s.AddBindingInvocationHandler("azure-eventhubs-binding", func(_ context.Context, in *common.BindingEvent) ([]byte, error) {
consumerGroup1.Observe(string(in.Data))
if err := sim(); err != nil {
return nil, err
}
received.Observe(string(in.Data))
ctx.Logf("Receiving eventhubs message: %s", string(in.Data))
return []byte("{}"), nil
}))
@ -269,7 +271,7 @@ func TestEventhubBindingIOTHub(t *testing.T) {
}
}
deleteEventhub := func(ctx flow.Context) error {
output, err := exec.Command("/bin/sh", "deleteeventhub.sh").Output()
output, err := exec.Command("/bin/sh", "deleteeventhub.sh", "eventhubs-bindings-container-iot").Output()
assert.Nil(t, err, "Error in deleteeventhub.sh.:\n%s", string(output))
return nil
}
@ -282,8 +284,10 @@ func TestEventhubBindingIOTHub(t *testing.T) {
embedded.WithComponentsPath("./components/binding/iothub"),
componentRuntimeOptions(),
)).
Step("wait", flow.Sleep(15*time.Second)).
Step("Send messages to IoT", sendIOTDevice(consumerGroup3)).
Step("delete containers", deleteEventhub).
Step("wait", flow.Sleep(5*time.Second)).
Run()
}
@ -293,101 +297,78 @@ func TestEventhubBindingMultiplePartition(t *testing.T) {
httpPort := ports[1]
appPort := ports[2]
consumerGroup1 := watcher.NewUnordered()
consumerGroup2 := watcher.NewUnordered()
received := watcher.NewUnordered()
metadata0 := map[string]string{
messageKey: partition0,
}
metadata1 := map[string]string{
messageKey: partition1,
}
sendAndReceive := func(metadata0 map[string]string, metadata1 map[string]string) flow.Runnable {
return func(ctx flow.Context) error {
client, err := dapr.NewClientWithPort(fmt.Sprintf("%d", grpcPort))
require.NoError(t, err, "dapr init failed")
// Define what is expected
outputmsg := make([]string, 50)
for i := 0; i < 50; i++ {
outputmsg[i] = fmt.Sprintf("output binding: Message %d, partitionkey: %s", i, metadata0[messageKey])
sendAndReceive := func(ctx flow.Context) error {
client, err := dapr.NewClientWithPort(strconv.Itoa(grpcPort))
require.NoError(t, err, "dapr init failed")
// Define what is expected
outputmsg := make([]string, 100)
for i := 0; i < 100; i++ {
var md map[string]string
if i < 50 {
md = metadata0
} else {
md = metadata1
}
consumerGroup1.ExpectStrings(outputmsg...)
time.Sleep(40 * time.Second)
// Send events from output binding
for _, msg := range outputmsg {
ctx.Logf("Sending eventhub message: %q", msg)
err := client.InvokeOutputBinding(
ctx, &dapr.InvokeBindingRequest{
Name: "azure-partition0-binding",
Operation: "create",
Data: []byte(msg),
Metadata: metadata0,
})
require.NoError(ctx, err, "error publishing message")
}
// Define what is expected
outputmsg2 := make([]string, 50)
for i := 0; i < 50; i++ {
outputmsg2[i] = fmt.Sprintf("output binding: Message %d, partitionkey: %s", i+50, metadata1[messageKey])
}
consumerGroup2.ExpectStrings(outputmsg2...)
time.Sleep(120 * time.Second)
// Send events from output binding
for _, msg2 := range outputmsg2 {
ctx.Logf("Sending eventhub message: %q", msg2)
err := client.InvokeOutputBinding(
ctx, &dapr.InvokeBindingRequest{
Name: "azure-partition1-binding",
Operation: "create",
Data: []byte(msg2),
Metadata: metadata1,
})
require.NoError(ctx, err, "error publishing message")
}
// Assert the observed messages
consumerGroup1.Assert(ctx, time.Minute)
consumerGroup2.Assert(ctx, time.Minute)
return nil
outputmsg[i] = fmt.Sprintf("output binding: Message %d, partitionkey: %s", i, md[messageKey])
}
received.ExpectStrings(outputmsg...)
// Send events from output binding
time.Sleep(20 * time.Second)
for i, msg := range outputmsg {
var md map[string]string
if i < 50 {
md = metadata0
} else {
md = metadata1
}
ctx.Logf("Sending eventhub message '%s' on partition '%s'", msg, md[messageKey])
err := client.InvokeOutputBinding(
ctx, &dapr.InvokeBindingRequest{
Name: "azure-partitioned-binding",
Operation: "create",
Data: []byte(msg),
Metadata: md,
})
require.NoError(ctx, err, "error publishing message")
}
// Assert the observed messages
received.Assert(ctx, time.Minute)
return nil
}
// Application logic that tracks messages from eventhub.
application := func(ctx flow.Context, s common.Service) (err error) {
// Simulate periodic errors.
sim := simulate.PeriodicError(ctx, 100)
// Setup the binding endpoints
err = multierr.Combine(err,
s.AddBindingInvocationHandler("azure-partition0-binding", func(_ context.Context, in *common.BindingEvent) ([]byte, error) {
consumerGroup1.Observe(string(in.Data))
if err := sim(); err != nil {
return nil, err
}
consumerGroup1.FailIfNotExpected(t, string(in.Data))
ctx.Logf("Receiving eventhubs message: %s", string(in.Data))
return []byte("{}"), nil
}),
s.AddBindingInvocationHandler("azure-partition1-binding", func(_ context.Context, in *common.BindingEvent) ([]byte, error) {
consumerGroup2.Observe(string(in.Data))
if err := sim(); err != nil {
return nil, err
}
consumerGroup2.FailIfNotExpected(t, string(in.Data))
ctx.Logf("Receiving eventhubs message: %s", string(in.Data))
return []byte("{}"), nil
}))
err = s.AddBindingInvocationHandler("azure-partitioned-binding", func(_ context.Context, in *common.BindingEvent) ([]byte, error) {
if err := sim(); err != nil {
return nil, err
}
ctx.Logf("Receiving eventhubs message: %s", string(in.Data))
received.FailIfNotExpected(t, string(in.Data))
received.Observe(string(in.Data))
return []byte("{}"), nil
})
return err
}
deleteEventhub := func(ctx flow.Context) error {
output, err := exec.Command("/bin/sh", "deleteeventhub.sh").Output()
output, err := exec.Command("/bin/sh", "deleteeventhub.sh", "eventhubs-bindings-container-c3").Output()
assert.Nil(t, err, "Error in deleteeventhub.sh.:\n%s", string(output))
return nil
}
@ -402,13 +383,16 @@ func TestEventhubBindingMultiplePartition(t *testing.T) {
embedded.WithComponentsPath("./components/binding/consumer3"),
componentRuntimeOptions(),
)).
Step("send and wait", sendAndReceive(metadata0, metadata1)).
Step("wait", flow.Sleep(15*time.Second)).
Step("send and wait", sendAndReceive).
Step("delete containers", deleteEventhub).
Step("wait", flow.Sleep(5*time.Second)).
Run()
}
func componentRuntimeOptions() []runtime.Option {
log := logger.NewLogger("dapr.components")
log.SetOutputLevel(logger.DebugLevel)
bindingsRegistry := bindings_loader.NewRegistry()
bindingsRegistry.Logger = log

View File

@ -17,23 +17,20 @@ require (
contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect
github.com/AdhityaRamadhanus/fasthttpcors v0.0.0-20170121111917-d4c07198763a // indirect
github.com/Azure/azure-amqp-common-go/v4 v4.0.0 // indirect
github.com/Azure/azure-event-hubs-go/v3 v3.4.0 // indirect
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/Azure/azure-sdk-for-go v67.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect
github.com/Azure/azure-storage-blob-go v0.15.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs v0.4.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/eventhub/armeventhub v1.0.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1 // indirect
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd // indirect
github.com/Azure/go-amqp v0.18.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.28 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.21 // indirect
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0 // indirect
@ -47,7 +44,6 @@ require (
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/devigned/tab v0.1.1 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
@ -87,7 +83,6 @@ require (
github.com/imdario/mergo v0.3.12 // indirect
github.com/jhump/protoreflect v1.13.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.15.12 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect

View File

@ -37,31 +37,28 @@ github.com/AdhityaRamadhanus/fasthttpcors v0.0.0-20170121111917-d4c07198763a h1:
github.com/AdhityaRamadhanus/fasthttpcors v0.0.0-20170121111917-d4c07198763a/go.mod h1:C0A1KeiVHs+trY6gUTPhhGammbrZ30ZfXRW/nuT7HLw=
github.com/Azure/azure-amqp-common-go/v4 v4.0.0 h1:mV5O74KYmonn0ZXtwfMjGUtZ9Z+Hv7AIFVS1s03sRvo=
github.com/Azure/azure-amqp-common-go/v4 v4.0.0/go.mod h1:4+qRvizIo4+CbGG552O6a8ONkEwRgWXqes3SUt1Ftrc=
github.com/Azure/azure-event-hubs-go/v3 v3.4.0 h1:LtH0nHkXivyV/GajOu5ZFC5sb/5KZ8j+9U8UsfHVTOo=
github.com/Azure/azure-event-hubs-go/v3 v3.4.0/go.mod h1:ODgt5C1/c73FToYj+mWojUJLXF877ALc6G4XnfRFlAY=
github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
github.com/Azure/azure-sdk-for-go v67.0.0+incompatible h1:SVBwznSETB0Sipd0uyGJr7khLhJOFRUEUb+0JgkCvDo=
github.com/Azure/azure-sdk-for-go v67.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 h1:sVW/AFBTGyJxDaMYlq0ct3jUXTtj12tQ6zE2GZUgVQw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 h1:VuHAcMq8pU1IWNT/m5yRaGqbK0BiQKHT8X4DTp9CHdI=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0/go.mod h1:tZoQYdDZNOiIjdSn0dVWVfl0NEPGOJqVLzSrcFk4Is0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 h1:t/W5MYAuQy81cvM8VUNfRLzhtKpXhVUAN7Cd7KVbTyc=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0/go.mod h1:NBanQUfSWiWn3QEpWDTCU0IjBECKOYvl2R8xdRtMtiM=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk=
github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58=
github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs v0.4.0 h1:X/ePaAG8guM7j5WORT5eEIw7cGUxe9Ah1jEQJKLmmSo=
github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs v0.4.0/go.mod h1:5dog28UP3dd1BnCPFYvyHfsmA+Phmoezt+KWT5cZnyc=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/eventhub/armeventhub v1.0.0 h1:BWeAAEzkCnL0ABVJqs+4mYudNch7oFGPtTlSmIWL8ms=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/eventhub/armeventhub v1.0.0/go.mod h1:Y3gnVwfaz8h6L1YHar+NfWORtBoVUSB5h4GlGkdeF7Q=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1 h1:YvQv9Mz6T8oR5ypQOL6erY0Z5t71ak1uHV4QFokCOZk=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1/go.mod h1:c6WvOhtmjNUWbLfOG1qxM/q0SPvQNSVJvolm+C52dIU=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd h1:b3wyxBl3vvr15tUAziPBPK354y+LSdfPCpex5oBttHo=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd/go.mod h1:K6am8mT+5iFXgingS9LUc7TmbsW6XBw3nxaRyaMyWc8=
github.com/Azure/go-amqp v0.18.0 h1:95bTiJq0oxjK1RUlt5T3HF/THj6jWTRZpSXMPSOJLz8=
github.com/Azure/go-amqp v0.18.0/go.mod h1:+bg0x3ce5+Q3ahCEXnCsGG3ETpDQe3MEVnOuT2ywPwc=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM=
github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk=
github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U=
@ -74,10 +71,6 @@ github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSY
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw=
github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU=
github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac=
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
@ -146,8 +139,6 @@ github.com/dapr/kit v0.0.4-0.20230105202559-fcb09958bfb0/go.mod h1:RFN6r5pZzhrel
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/devigned/tab v0.1.1 h1:3mD6Kb1mUOYeLpJvTVSDwSg5ZsfSxfvxGRTxRsJsITA=
github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c=
@ -172,8 +163,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
@ -286,7 +275,6 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@ -354,10 +342,9 @@ github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7
github.com/jhump/protoreflect v1.11.0/go.mod h1:U7aMIjN0NWq9swDP7xDdoMfRHb35uiuTd3Z9nFXJf5E=
github.com/jhump/protoreflect v1.13.0 h1:zrrZqa7JAc2YGgPSzZZkmUXJ5G6NRPdxOg/9t7ISImA=
github.com/jhump/protoreflect v1.13.0/go.mod h1:JytZfP5d0r8pVNLZvai7U/MCuTWITgrI4tTg7puQFKI=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@ -379,7 +366,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@ -595,8 +581,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@ -674,7 +658,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@ -943,7 +926,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=

View File

@ -17,11 +17,10 @@ require (
github.com/AdhityaRamadhanus/fasthttpcors v0.0.0-20170121111917-d4c07198763a // indirect
github.com/Azure/azure-amqp-common-go/v4 v4.0.0 // indirect
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.4 // indirect
github.com/Azure/azure-storage-blob-go v0.15.0 // indirect
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd // indirect
github.com/Azure/go-amqp v0.18.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect

View File

@ -40,16 +40,14 @@ github.com/Azure/azure-amqp-common-go/v4 v4.0.0/go.mod h1:4+qRvizIo4+CbGG552O6a8
github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 h1:sVW/AFBTGyJxDaMYlq0ct3jUXTtj12tQ6zE2GZUgVQw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 h1:VuHAcMq8pU1IWNT/m5yRaGqbK0BiQKHT8X4DTp9CHdI=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0/go.mod h1:tZoQYdDZNOiIjdSn0dVWVfl0NEPGOJqVLzSrcFk4Is0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 h1:t/W5MYAuQy81cvM8VUNfRLzhtKpXhVUAN7Cd7KVbTyc=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0/go.mod h1:NBanQUfSWiWn3QEpWDTCU0IjBECKOYvl2R8xdRtMtiM=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.4 h1:kaZamwZwmUqnECvnPkf1LBRBIFYYCy3E0gKHn/UFSD0=
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.4/go.mod h1:uDLwkzCJMvTrHsvtiVFeAp85hi3W77zvs61wrpc+6ho=
github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk=
github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd h1:b3wyxBl3vvr15tUAziPBPK354y+LSdfPCpex5oBttHo=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd/go.mod h1:K6am8mT+5iFXgingS9LUc7TmbsW6XBw3nxaRyaMyWc8=
github.com/Azure/go-amqp v0.18.0 h1:95bTiJq0oxjK1RUlt5T3HF/THj6jWTRZpSXMPSOJLz8=
@ -59,7 +57,6 @@ github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM=
github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk=
github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U=
@ -166,7 +163,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
@ -280,7 +276,6 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@ -372,7 +367,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@ -590,8 +584,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@ -669,7 +661,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@ -938,7 +929,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=

View File

@ -17,10 +17,9 @@ require (
github.com/AdhityaRamadhanus/fasthttpcors v0.0.0-20170121111917-d4c07198763a // indirect
github.com/Azure/azure-amqp-common-go/v4 v4.0.0 // indirect
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect
github.com/Azure/azure-storage-blob-go v0.15.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
github.com/Azure/go-autorest/autorest v0.11.28 // indirect

View File

@ -40,14 +40,12 @@ github.com/Azure/azure-amqp-common-go/v4 v4.0.0/go.mod h1:4+qRvizIo4+CbGG552O6a8
github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 h1:sVW/AFBTGyJxDaMYlq0ct3jUXTtj12tQ6zE2GZUgVQw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 h1:VuHAcMq8pU1IWNT/m5yRaGqbK0BiQKHT8X4DTp9CHdI=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0/go.mod h1:tZoQYdDZNOiIjdSn0dVWVfl0NEPGOJqVLzSrcFk4Is0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 h1:t/W5MYAuQy81cvM8VUNfRLzhtKpXhVUAN7Cd7KVbTyc=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0/go.mod h1:NBanQUfSWiWn3QEpWDTCU0IjBECKOYvl2R8xdRtMtiM=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk=
github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd h1:b3wyxBl3vvr15tUAziPBPK354y+LSdfPCpex5oBttHo=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd/go.mod h1:K6am8mT+5iFXgingS9LUc7TmbsW6XBw3nxaRyaMyWc8=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
@ -55,7 +53,6 @@ github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM=
github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk=
github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U=
@ -160,7 +157,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
@ -273,7 +269,6 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@ -364,7 +359,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@ -580,8 +574,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@ -659,7 +651,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@ -928,7 +919,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=

View File

@ -27,8 +27,8 @@ import (
"go.uber.org/multierr"
// Pub-Sub.
pubsub_evethubs "github.com/dapr/components-contrib/pubsub/azure/eventhubs"
"github.com/dapr/components-contrib/pubsub"
pubsub_eventhubs "github.com/dapr/components-contrib/pubsub/azure/eventhubs"
secretstore_env "github.com/dapr/components-contrib/secretstores/local/env"
pubsub_loader "github.com/dapr/dapr/pkg/components/pubsub"
secretstores_loader "github.com/dapr/dapr/pkg/components/secretstores"
@ -75,6 +75,7 @@ const (
)
func TestEventhubs(t *testing.T) {
// Tests that simulate failures are un-ordered
consumerGroup1 := watcher.NewUnordered()
consumerGroup2 := watcher.NewUnordered()
consumerGroup4 := watcher.NewOrdered()
@ -90,7 +91,7 @@ func TestEventhubs(t *testing.T) {
}
// subscriber of the given topic
subscriberApplication := func(appID string, topicName string, messagesWatcher *watcher.Watcher) app.SetupFn {
subscriberApplication := func(appID string, topicName string, messagesWatcher *watcher.Watcher, withFailures bool) app.SetupFn {
return func(ctx flow.Context, s common.Service) error {
// Simulate periodic errors.
sim := simulate.PeriodicError(ctx, 100)
@ -101,8 +102,10 @@ func TestEventhubs(t *testing.T) {
Topic: topicName,
Route: "/orders",
}, func(_ context.Context, e *common.TopicEvent) (retry bool, err error) {
if err := sim(); err != nil {
return true, err
if withFailures {
if err := sim(); err != nil {
return true, err
}
}
// Track/Observe the data of the event.
@ -131,7 +134,7 @@ func TestEventhubs(t *testing.T) {
client := sidecar.GetClient(ctx, sidecarName)
// publish messages
ctx.Logf("Publishing messages. sidecarName: %s, topicName: %s", sidecarName, topicName)
logs := fmt.Sprintf("Published messages. sidecarName: %s, topicName: %s", sidecarName, topicName)
var publishOptions dapr.PublishEventOption
@ -139,8 +142,8 @@ func TestEventhubs(t *testing.T) {
publishOptions = dapr.PublishEventWithMetadata(metadata)
}
for _, message := range messages {
ctx.Logf("Publishing: %q", message)
for i, message := range messages {
logs += fmt.Sprintf("\nMessage %d: %s", i, message)
var err error
if publishOptions != nil {
@ -149,8 +152,11 @@ func TestEventhubs(t *testing.T) {
err = client.PublishEvent(ctx, pubsubName, topicName, message)
}
require.NoError(ctx, err, "error publishing message")
require.NoErrorf(ctx, err, "error publishing message %s", message)
}
ctx.Log(logs)
return nil
}
}
@ -159,16 +165,22 @@ func TestEventhubs(t *testing.T) {
return func(ctx flow.Context) error {
// assert for messages
for _, m := range messageWatchers {
m.Assert(ctx, 25*timeout)
m.Assert(ctx, 15*timeout)
}
return nil
}
}
deleteEventhub := func(ctx flow.Context) error {
flowDoneCh := make(chan struct{}, 1)
flowDone := func(ctx flow.Context) error {
close(flowDoneCh)
return nil
}
deleteEventhub := func() error {
output, err := exec.Command("/bin/sh", "delete-eventhub.sh", topicToBeCreated).Output()
assert.Nil(t, err, "Error in delete-eventhub.sh.:\n%s", string(output))
assert.NoErrorf(t, err, "Error in delete-eventhub.sh.:\n%s", string(output))
return nil
}
@ -181,19 +193,30 @@ func TestEventhubs(t *testing.T) {
messageWatchers.ExpectStrings(messages...)
output, err := exec.Command("/bin/sh", "send-iot-device-events.sh", topicToBeCreated).Output()
assert.Nil(t, err, "Error in send-iot-device-events.sh.:\n%s", string(output))
assert.NoErrorf(t, err, "Error in send-iot-device-events.sh.:\n%s", string(output))
return nil
}
}
// Topic name for a IOT device is same as IOTHubName
iotHubName := os.Getenv(iotHubNameEnvKey)
// Here so we can comment out tests as needed
_ = consumerGroup1
_ = consumerGroup2
_ = consumerGroup4
_ = consumerGroup5
_ = publishMessageAsDevice
_ = iotHubName
_ = metadata
_ = metadata1
_ = publishMessages
flow.New(t, "eventhubs certification").
// Test : single publisher, multiple subscriber with their own consumerID
// Run subscriberApplication app1
Step(app.Run(appID1, fmt.Sprintf(":%d", appPort),
subscriberApplication(appID1, topicActiveName, consumerGroup1))).
subscriberApplication(appID1, topicActiveName, consumerGroup1, true))).
// Run the Dapr sidecar with the eventhubs component 1, with permission at namespace level
Step(sidecar.Run(sidecarName1,
@ -201,12 +224,13 @@ func TestEventhubs(t *testing.T) {
embedded.WithAppProtocol(runtime.HTTPProtocol, appPort),
embedded.WithDaprGRPCPort(runtime.DefaultDaprAPIGRPCPort),
embedded.WithDaprHTTPPort(runtime.DefaultDaprHTTPPort),
componentRuntimeOptions(),
embedded.WithProfilePort(runtime.DefaultProfilePort),
componentRuntimeOptions(1),
)).
// Run subscriberApplication app2
Step(app.Run(appID2, fmt.Sprintf(":%d", appPort+portOffset),
subscriberApplication(appID2, topicActiveName, consumerGroup2))).
subscriberApplication(appID2, topicActiveName, consumerGroup2, true))).
// Run the Dapr sidecar with the component 2.
Step(sidecar.Run(sidecarName2,
@ -215,8 +239,9 @@ func TestEventhubs(t *testing.T) {
embedded.WithDaprGRPCPort(runtime.DefaultDaprAPIGRPCPort+portOffset),
embedded.WithDaprHTTPPort(runtime.DefaultDaprHTTPPort+portOffset),
embedded.WithProfilePort(runtime.DefaultProfilePort+portOffset),
componentRuntimeOptions(),
componentRuntimeOptions(2),
)).
Step("wait", flow.Sleep(15*time.Second)).
Step("publish messages to topic1", publishMessages(nil, sidecarName1, topicActiveName, consumerGroup1, consumerGroup2)).
Step("publish messages to unUsedTopic", publishMessages(nil, sidecarName1, topicPassiveName)).
Step("verify if app1 has recevied messages published to topic1", assertMessages(10*time.Second, consumerGroup1)).
@ -226,7 +251,7 @@ func TestEventhubs(t *testing.T) {
// Test : multiple publisher with different partitionkey, multiple subscriber with same consumer ID
// Run subscriberApplication app3
Step(app.Run(appID3, fmt.Sprintf(":%d", appPort+portOffset*2),
subscriberApplication(appID3, topicActiveName, consumerGroup2))).
subscriberApplication(appID3, topicActiveName, consumerGroup2, true))).
// Run the Dapr sidecar with the component 3.
Step(sidecar.Run(sidecarName3,
@ -235,17 +260,19 @@ func TestEventhubs(t *testing.T) {
embedded.WithDaprGRPCPort(runtime.DefaultDaprAPIGRPCPort+portOffset*2),
embedded.WithDaprHTTPPort(runtime.DefaultDaprHTTPPort+portOffset*2),
embedded.WithProfilePort(runtime.DefaultProfilePort+portOffset*2),
componentRuntimeOptions(),
componentRuntimeOptions(3),
)).
Step("wait", flow.Sleep(15*time.Second)).
// publish message in topic1 from two publisher apps, however there are two subscriber apps (app2,app3) with same consumerID
Step("publish messages to topic1", publishMessages(metadata, sidecarName1, topicActiveName, consumerGroup2)).
Step("publish messages to topic1", publishMessages(metadata1, sidecarName2, topicActiveName, consumerGroup2)).
Step("publish messages to topic1 from app1", publishMessages(metadata, sidecarName1, topicActiveName, consumerGroup2)).
Step("publish messages to topic1 from app2", publishMessages(metadata1, sidecarName2, topicActiveName, consumerGroup2)).
Step("verify if app2, app3 together have recevied messages published to topic1", assertMessages(10*time.Second, consumerGroup2)).
// Test : Entitymanagement , Test partition key, in order processing with single publisher/subscriber
// Run subscriberApplication app4
Step(app.Run(appID4, fmt.Sprintf(":%d", appPort+portOffset*3),
subscriberApplication(appID4, topicToBeCreated, consumerGroup4))).
subscriberApplication(appID4, topicToBeCreated, consumerGroup4, false))).
// Run the Dapr sidecar with the component entitymanagement
Step(sidecar.Run(sidecarName4,
@ -254,15 +281,16 @@ func TestEventhubs(t *testing.T) {
embedded.WithDaprGRPCPort(runtime.DefaultDaprAPIGRPCPort+portOffset*3),
embedded.WithDaprHTTPPort(runtime.DefaultDaprHTTPPort+portOffset*3),
embedded.WithProfilePort(runtime.DefaultProfilePort+portOffset*3),
componentRuntimeOptions(),
componentRuntimeOptions(4),
)).
Step("wait", flow.Sleep(15*time.Second)).
Step(fmt.Sprintf("publish messages to topicToBeCreated: %s", topicToBeCreated), publishMessages(metadata, sidecarName4, topicToBeCreated, consumerGroup4)).
Step("verify if app4 has recevied messages published to newly created topic", assertMessages(10*time.Second, consumerGroup4)).
// Test : IOT hub
// Run subscriberApplication app5
Step(app.Run(appID5, fmt.Sprintf(":%d", appPort+portOffset*4),
subscriberApplication(appID5, iotHubName, consumerGroup5))).
subscriberApplication(appID5, iotHubName, consumerGroup5, true))).
// Run the Dapr sidecar with the iot component
Step(sidecar.Run(sidecarName5,
embedded.WithComponentsPath("./components/iotconsumer"),
@ -270,22 +298,37 @@ func TestEventhubs(t *testing.T) {
embedded.WithDaprGRPCPort(runtime.DefaultDaprAPIGRPCPort+portOffset*4),
embedded.WithDaprHTTPPort(runtime.DefaultDaprHTTPPort+portOffset*4),
embedded.WithProfilePort(runtime.DefaultProfilePort+portOffset*4),
componentRuntimeOptions(),
componentRuntimeOptions(5),
)).
Step("add expected IOT messages (simulate add message to iot)", publishMessageAsDevice(consumerGroup5)).
Step("verify if app5 has recevied messages published to iot topic", assertMessages(40*time.Second, consumerGroup5)).
Step("wait", flow.Sleep(5*time.Second)).
// cleanup azure assets created as part of tests
Step("delete eventhub created as part of the eventhub management test", deleteEventhub).
Step("wait", flow.Sleep(20*time.Second)).
Step("add expected IoT messages (simulate add message to IoT Hub)", publishMessageAsDevice(consumerGroup5)).
Step("verify if app5 has recevied messages published to IoT topic", assertMessages(10*time.Second, consumerGroup5)).
// Run the flow
Step("mark as complete", flowDone).
Run()
// Cleanup Azure assets created as part of tests
<-flowDoneCh
time.Sleep(5 * time.Second)
fmt.Println("Deleting EventHub resources created as part of the management test…")
deleteEventhub()
}
func componentRuntimeOptions() []runtime.Option {
func componentRuntimeOptions(instance int) []runtime.Option {
log := logger.NewLogger("dapr.components")
log.SetOutputLevel(logger.DebugLevel)
pubsubRegistry := pubsub_loader.NewRegistry()
pubsubRegistry.Logger = log
pubsubRegistry.RegisterComponent(pubsub_evethubs.NewAzureEventHubs, "azure.eventhubs")
pubsubRegistry.RegisterComponent(func(l logger.Logger) pubsub.PubSub {
l = l.WithFields(map[string]any{
"component": "pubsub.azure.eventhubs",
"instance": instance,
})
l.Infof("Instantiated log for instance %d", instance)
return pubsub_eventhubs.NewAzureEventHubs(l)
}, "azure.eventhubs")
secretstoreRegistry := secretstores_loader.NewRegistry()
secretstoreRegistry.Logger = log

View File

@ -17,23 +17,20 @@ require (
contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect
github.com/AdhityaRamadhanus/fasthttpcors v0.0.0-20170121111917-d4c07198763a // indirect
github.com/Azure/azure-amqp-common-go/v4 v4.0.0 // indirect
github.com/Azure/azure-event-hubs-go/v3 v3.4.0 // indirect
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/Azure/azure-sdk-for-go v67.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect
github.com/Azure/azure-storage-blob-go v0.15.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs v0.4.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/eventhub/armeventhub v1.0.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1 // indirect
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd // indirect
github.com/Azure/go-amqp v0.18.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.28 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.21 // indirect
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0 // indirect
@ -47,7 +44,6 @@ require (
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/devigned/tab v0.1.1 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
@ -87,7 +83,6 @@ require (
github.com/imdario/mergo v0.3.12 // indirect
github.com/jhump/protoreflect v1.13.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.15.12 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect

View File

@ -37,31 +37,28 @@ github.com/AdhityaRamadhanus/fasthttpcors v0.0.0-20170121111917-d4c07198763a h1:
github.com/AdhityaRamadhanus/fasthttpcors v0.0.0-20170121111917-d4c07198763a/go.mod h1:C0A1KeiVHs+trY6gUTPhhGammbrZ30ZfXRW/nuT7HLw=
github.com/Azure/azure-amqp-common-go/v4 v4.0.0 h1:mV5O74KYmonn0ZXtwfMjGUtZ9Z+Hv7AIFVS1s03sRvo=
github.com/Azure/azure-amqp-common-go/v4 v4.0.0/go.mod h1:4+qRvizIo4+CbGG552O6a8ONkEwRgWXqes3SUt1Ftrc=
github.com/Azure/azure-event-hubs-go/v3 v3.4.0 h1:LtH0nHkXivyV/GajOu5ZFC5sb/5KZ8j+9U8UsfHVTOo=
github.com/Azure/azure-event-hubs-go/v3 v3.4.0/go.mod h1:ODgt5C1/c73FToYj+mWojUJLXF877ALc6G4XnfRFlAY=
github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
github.com/Azure/azure-sdk-for-go v67.0.0+incompatible h1:SVBwznSETB0Sipd0uyGJr7khLhJOFRUEUb+0JgkCvDo=
github.com/Azure/azure-sdk-for-go v67.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 h1:sVW/AFBTGyJxDaMYlq0ct3jUXTtj12tQ6zE2GZUgVQw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 h1:VuHAcMq8pU1IWNT/m5yRaGqbK0BiQKHT8X4DTp9CHdI=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0/go.mod h1:tZoQYdDZNOiIjdSn0dVWVfl0NEPGOJqVLzSrcFk4Is0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 h1:t/W5MYAuQy81cvM8VUNfRLzhtKpXhVUAN7Cd7KVbTyc=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0/go.mod h1:NBanQUfSWiWn3QEpWDTCU0IjBECKOYvl2R8xdRtMtiM=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk=
github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58=
github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs v0.4.0 h1:X/ePaAG8guM7j5WORT5eEIw7cGUxe9Ah1jEQJKLmmSo=
github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs v0.4.0/go.mod h1:5dog28UP3dd1BnCPFYvyHfsmA+Phmoezt+KWT5cZnyc=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/eventhub/armeventhub v1.0.0 h1:BWeAAEzkCnL0ABVJqs+4mYudNch7oFGPtTlSmIWL8ms=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/eventhub/armeventhub v1.0.0/go.mod h1:Y3gnVwfaz8h6L1YHar+NfWORtBoVUSB5h4GlGkdeF7Q=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1 h1:YvQv9Mz6T8oR5ypQOL6erY0Z5t71ak1uHV4QFokCOZk=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1/go.mod h1:c6WvOhtmjNUWbLfOG1qxM/q0SPvQNSVJvolm+C52dIU=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd h1:b3wyxBl3vvr15tUAziPBPK354y+LSdfPCpex5oBttHo=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd/go.mod h1:K6am8mT+5iFXgingS9LUc7TmbsW6XBw3nxaRyaMyWc8=
github.com/Azure/go-amqp v0.18.0 h1:95bTiJq0oxjK1RUlt5T3HF/THj6jWTRZpSXMPSOJLz8=
github.com/Azure/go-amqp v0.18.0/go.mod h1:+bg0x3ce5+Q3ahCEXnCsGG3ETpDQe3MEVnOuT2ywPwc=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM=
github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk=
github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U=
@ -74,10 +71,6 @@ github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSY
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw=
github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU=
github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac=
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
@ -146,8 +139,6 @@ github.com/dapr/kit v0.0.4-0.20230105202559-fcb09958bfb0/go.mod h1:RFN6r5pZzhrel
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/devigned/tab v0.1.1 h1:3mD6Kb1mUOYeLpJvTVSDwSg5ZsfSxfvxGRTxRsJsITA=
github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c=
@ -172,8 +163,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
@ -286,7 +275,6 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@ -354,10 +342,9 @@ github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7
github.com/jhump/protoreflect v1.11.0/go.mod h1:U7aMIjN0NWq9swDP7xDdoMfRHb35uiuTd3Z9nFXJf5E=
github.com/jhump/protoreflect v1.13.0 h1:zrrZqa7JAc2YGgPSzZZkmUXJ5G6NRPdxOg/9t7ISImA=
github.com/jhump/protoreflect v1.13.0/go.mod h1:JytZfP5d0r8pVNLZvai7U/MCuTWITgrI4tTg7puQFKI=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@ -379,7 +366,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@ -593,8 +579,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@ -672,7 +656,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@ -941,7 +924,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=

View File

@ -18,11 +18,10 @@ require (
github.com/AdhityaRamadhanus/fasthttpcors v0.0.0-20170121111917-d4c07198763a // indirect
github.com/Azure/azure-amqp-common-go/v4 v4.0.0 // indirect
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.4 // indirect
github.com/Azure/azure-storage-blob-go v0.15.0 // indirect
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd // indirect
github.com/Azure/go-amqp v0.18.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect

View File

@ -40,16 +40,14 @@ github.com/Azure/azure-amqp-common-go/v4 v4.0.0/go.mod h1:4+qRvizIo4+CbGG552O6a8
github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 h1:sVW/AFBTGyJxDaMYlq0ct3jUXTtj12tQ6zE2GZUgVQw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 h1:VuHAcMq8pU1IWNT/m5yRaGqbK0BiQKHT8X4DTp9CHdI=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0/go.mod h1:tZoQYdDZNOiIjdSn0dVWVfl0NEPGOJqVLzSrcFk4Is0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 h1:t/W5MYAuQy81cvM8VUNfRLzhtKpXhVUAN7Cd7KVbTyc=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0/go.mod h1:NBanQUfSWiWn3QEpWDTCU0IjBECKOYvl2R8xdRtMtiM=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.4 h1:kaZamwZwmUqnECvnPkf1LBRBIFYYCy3E0gKHn/UFSD0=
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.1.4/go.mod h1:uDLwkzCJMvTrHsvtiVFeAp85hi3W77zvs61wrpc+6ho=
github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk=
github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd h1:b3wyxBl3vvr15tUAziPBPK354y+LSdfPCpex5oBttHo=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd/go.mod h1:K6am8mT+5iFXgingS9LUc7TmbsW6XBw3nxaRyaMyWc8=
github.com/Azure/go-amqp v0.18.0 h1:95bTiJq0oxjK1RUlt5T3HF/THj6jWTRZpSXMPSOJLz8=
@ -59,7 +57,6 @@ github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM=
github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk=
github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U=
@ -166,7 +163,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
@ -280,7 +276,6 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@ -372,7 +367,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@ -590,8 +584,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@ -669,7 +661,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@ -938,7 +929,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=

View File

@ -16,12 +16,11 @@ require (
github.com/AdhityaRamadhanus/fasthttpcors v0.0.0-20170121111917-d4c07198763a // indirect
github.com/Azure/azure-amqp-common-go/v4 v4.0.0 // indirect
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.11.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.0 // indirect
github.com/Azure/azure-storage-blob-go v0.15.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
github.com/Azure/go-autorest/autorest v0.11.28 // indirect

View File

@ -40,8 +40,8 @@ github.com/Azure/azure-amqp-common-go/v4 v4.0.0/go.mod h1:4+qRvizIo4+CbGG552O6a8
github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 h1:sVW/AFBTGyJxDaMYlq0ct3jUXTtj12tQ6zE2GZUgVQw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 h1:VuHAcMq8pU1IWNT/m5yRaGqbK0BiQKHT8X4DTp9CHdI=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0/go.mod h1:tZoQYdDZNOiIjdSn0dVWVfl0NEPGOJqVLzSrcFk4Is0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 h1:t/W5MYAuQy81cvM8VUNfRLzhtKpXhVUAN7Cd7KVbTyc=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0/go.mod h1:NBanQUfSWiWn3QEpWDTCU0IjBECKOYvl2R8xdRtMtiM=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8=
@ -50,8 +50,6 @@ github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.11.0 h1:82w8tzLcOwDP
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.11.0/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-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk=
github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd h1:b3wyxBl3vvr15tUAziPBPK354y+LSdfPCpex5oBttHo=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd/go.mod h1:K6am8mT+5iFXgingS9LUc7TmbsW6XBw3nxaRyaMyWc8=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
@ -59,7 +57,6 @@ github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM=
github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk=
github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U=
@ -164,7 +161,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
@ -277,7 +273,6 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@ -366,7 +361,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@ -577,8 +571,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@ -656,7 +648,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@ -925,7 +916,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=

View File

@ -16,11 +16,10 @@ require (
github.com/AdhityaRamadhanus/fasthttpcors v0.0.0-20170121111917-d4c07198763a // indirect
github.com/Azure/azure-amqp-common-go/v4 v4.0.0 // indirect
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1 // indirect
github.com/Azure/azure-storage-blob-go v0.15.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
github.com/Azure/go-autorest/autorest v0.11.28 // indirect

View File

@ -40,16 +40,14 @@ github.com/Azure/azure-amqp-common-go/v4 v4.0.0/go.mod h1:4+qRvizIo4+CbGG552O6a8
github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 h1:sVW/AFBTGyJxDaMYlq0ct3jUXTtj12tQ6zE2GZUgVQw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 h1:VuHAcMq8pU1IWNT/m5yRaGqbK0BiQKHT8X4DTp9CHdI=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0/go.mod h1:tZoQYdDZNOiIjdSn0dVWVfl0NEPGOJqVLzSrcFk4Is0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 h1:t/W5MYAuQy81cvM8VUNfRLzhtKpXhVUAN7Cd7KVbTyc=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0/go.mod h1:NBanQUfSWiWn3QEpWDTCU0IjBECKOYvl2R8xdRtMtiM=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1 h1:YvQv9Mz6T8oR5ypQOL6erY0Z5t71ak1uHV4QFokCOZk=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1/go.mod h1:c6WvOhtmjNUWbLfOG1qxM/q0SPvQNSVJvolm+C52dIU=
github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk=
github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd h1:b3wyxBl3vvr15tUAziPBPK354y+LSdfPCpex5oBttHo=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd/go.mod h1:K6am8mT+5iFXgingS9LUc7TmbsW6XBw3nxaRyaMyWc8=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
@ -57,7 +55,6 @@ github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM=
github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk=
github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U=
@ -162,7 +159,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
@ -275,7 +271,6 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@ -364,7 +359,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@ -575,8 +569,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@ -654,7 +646,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@ -923,7 +914,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=

View File

@ -16,12 +16,11 @@ require (
github.com/AdhityaRamadhanus/fasthttpcors v0.0.0-20170121111917-d4c07198763a // indirect
github.com/Azure/azure-amqp-common-go/v4 v4.0.0 // indirect
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/Azure/azure-sdk-for-go v67.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos v0.3.3 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect
github.com/Azure/azure-storage-blob-go v0.15.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
github.com/Azure/go-autorest/autorest v0.11.28 // indirect

View File

@ -40,18 +40,16 @@ github.com/Azure/azure-amqp-common-go/v4 v4.0.0/go.mod h1:4+qRvizIo4+CbGG552O6a8
github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
github.com/Azure/azure-sdk-for-go v67.0.0+incompatible h1:SVBwznSETB0Sipd0uyGJr7khLhJOFRUEUb+0JgkCvDo=
github.com/Azure/azure-sdk-for-go v67.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 h1:sVW/AFBTGyJxDaMYlq0ct3jUXTtj12tQ6zE2GZUgVQw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 h1:VuHAcMq8pU1IWNT/m5yRaGqbK0BiQKHT8X4DTp9CHdI=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0/go.mod h1:tZoQYdDZNOiIjdSn0dVWVfl0NEPGOJqVLzSrcFk4Is0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 h1:t/W5MYAuQy81cvM8VUNfRLzhtKpXhVUAN7Cd7KVbTyc=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0/go.mod h1:NBanQUfSWiWn3QEpWDTCU0IjBECKOYvl2R8xdRtMtiM=
github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos v0.3.3 h1:x1shk+tVZ6kLwIQMn4r+pdz8szo3mA0jd8STmgh+aRk=
github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos v0.3.3/go.mod h1:Fy3bbChFm4cZn6oIxYYqKB2FG3rBDxk3NZDLDJCHl+Q=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk=
github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd h1:b3wyxBl3vvr15tUAziPBPK354y+LSdfPCpex5oBttHo=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd/go.mod h1:K6am8mT+5iFXgingS9LUc7TmbsW6XBw3nxaRyaMyWc8=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
@ -59,7 +57,6 @@ github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM=
github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk=
github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U=
@ -164,7 +161,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
@ -277,7 +273,6 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@ -366,7 +361,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@ -577,8 +571,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@ -656,7 +648,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@ -925,7 +916,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=

View File

@ -16,11 +16,10 @@ require (
github.com/AdhityaRamadhanus/fasthttpcors v0.0.0-20170121111917-d4c07198763a // indirect
github.com/Azure/azure-amqp-common-go/v4 v4.0.0 // indirect
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.0.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect
github.com/Azure/azure-storage-blob-go v0.15.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
github.com/Azure/go-autorest/autorest v0.11.28 // indirect

View File

@ -40,16 +40,14 @@ github.com/Azure/azure-amqp-common-go/v4 v4.0.0/go.mod h1:4+qRvizIo4+CbGG552O6a8
github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 h1:sVW/AFBTGyJxDaMYlq0ct3jUXTtj12tQ6zE2GZUgVQw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 h1:VuHAcMq8pU1IWNT/m5yRaGqbK0BiQKHT8X4DTp9CHdI=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0/go.mod h1:tZoQYdDZNOiIjdSn0dVWVfl0NEPGOJqVLzSrcFk4Is0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 h1:t/W5MYAuQy81cvM8VUNfRLzhtKpXhVUAN7Cd7KVbTyc=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0/go.mod h1:NBanQUfSWiWn3QEpWDTCU0IjBECKOYvl2R8xdRtMtiM=
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 v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk=
github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd h1:b3wyxBl3vvr15tUAziPBPK354y+LSdfPCpex5oBttHo=
github.com/Azure/azure-storage-queue-go v0.0.0-20191125232315-636801874cdd/go.mod h1:K6am8mT+5iFXgingS9LUc7TmbsW6XBw3nxaRyaMyWc8=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
@ -57,7 +55,6 @@ github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM=
github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk=
github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U=
@ -162,7 +159,6 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
@ -275,7 +271,6 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@ -364,7 +359,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@ -575,8 +569,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@ -654,7 +646,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
@ -923,7 +914,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=

View File

@ -29,6 +29,9 @@ components:
outputData: '{"id": "$((uuid))", "orderid": "abcdef-test", "partitionKey": "partitionValue", "nestedproperty": {"subproperty": "something of value for testing"}, "description": "conformance test item"}'
- component: azure.eventhubs
operations: ["create", "operations", "read"]
config:
# Need to wait for Event Hubs to acquire a lock, which can take up to 1m
readBindingWait: 60s
- component: azure.eventgrid
operations: ["create", "operations", "read"]
config:

View File

@ -19,6 +19,7 @@ import (
"io"
"strconv"
"strings"
"sync/atomic"
"testing"
"time"
@ -146,23 +147,23 @@ func ConformanceTests(t *testing.T, props map[string]string, inputBinding bindin
if config.HasOperation("read") {
errInp := bindings.PingInpBinding(inputBinding)
// TODO: Ideally, all stable components should implenment ping function,
// so will only assert assert.Nil(t, err) finally, i.e. when current implementation
// so will only assert assert.NoError(t, err) finally, i.e. when current implementation
// implements ping in existing stable components
if errInp != nil {
assert.EqualError(t, errInp, "ping is not implemented by this input binding")
} else {
assert.Nil(t, errInp)
assert.NoError(t, errInp)
}
}
if config.HasOperation("operations") {
errOut := bindings.PingOutBinding(outputBinding)
// TODO: Ideally, all stable components should implenment ping function,
// so will only assert assert.Nil(t, err) finally, i.e. when current implementation
// so will only assert assert.NoError(t, err) finally, i.e. when current implementation
// implements ping in existing stable components
if errOut != nil {
assert.EqualError(t, errOut, "ping is not implemented by this output binding")
} else {
assert.Nil(t, errOut)
assert.NoError(t, errOut)
}
}
})
@ -189,25 +190,27 @@ func ConformanceTests(t *testing.T, props map[string]string, inputBinding bindin
})
}
inputBindingCall := 0
readChan := make(chan int)
inputBindingCall := atomic.Int32{}
readChan := make(chan int, 1)
readCtx, readCancel := context.WithCancel(context.Background())
defer readCancel()
if config.HasOperation("read") {
t.Run("read", func(t *testing.T) {
testLogger.Info("Read test running ...")
err := inputBinding.Read(readCtx, func(ctx context.Context, r *bindings.ReadResponse) ([]byte, error) {
inputBindingCall++
readChan <- inputBindingCall
t.Logf("Read message: %s", string(r.Data))
v := inputBindingCall.Add(1)
readChan <- int(v)
return nil, nil
})
assert.True(t, err == nil || errors.Is(err, context.Canceled), "expected Read canceled on Close")
assert.Truef(t, err == nil || errors.Is(err, context.Canceled), "expected Read canceled on Close, got: %v", err)
})
// Special case for message brokers that are also bindings
// Need a small wait here because with brokers like MQTT
// if you publish before there is a consumer, the message is thrown out
// Currently, there is no way to know when Read is successfully subscribed.
t.Logf("Sleeping for %v", config.ReadBindingWait)
time.Sleep(config.ReadBindingWait)
}
@ -233,7 +236,7 @@ func ConformanceTests(t *testing.T, props map[string]string, inputBinding bindin
req := config.createInvokeRequest()
req.Operation = bindings.GetOperation
resp, err := outputBinding.Invoke(context.Background(), &req)
assert.Nil(t, err, "expected no error invoking output binding")
assert.NoError(t, err, "expected no error invoking output binding")
if createPerformed {
assert.Equal(t, req.Data, resp.Data)
}
@ -259,10 +262,10 @@ func ConformanceTests(t *testing.T, props map[string]string, inputBinding bindin
// To stop the test from hanging if there's no response, we can setup a simple timeout.
select {
case <-readChan:
assert.Greater(t, inputBindingCall, 0)
assert.Greater(t, inputBindingCall.Load(), int32(0))
testLogger.Info("Read channel signalled.")
case <-time.After(config.ReadBindingTimeout):
assert.Greater(t, inputBindingCall, 0)
assert.Greaterf(t, inputBindingCall.Load(), int32(0), "Timed out after %v while reading", config.ReadBindingTimeout)
testLogger.Info("Read timeout.")
}
testLogger.Info("Verify Read test done.")
@ -276,7 +279,7 @@ func ConformanceTests(t *testing.T, props map[string]string, inputBinding bindin
req := config.createInvokeRequest()
req.Operation = bindings.DeleteOperation
_, err := outputBinding.Invoke(context.Background(), &req)
assert.Nil(t, err, "expected no error invoking output binding")
assert.NoError(t, err, "expected no error invoking output binding")
if createPerformed && config.HasOperation(string(bindings.GetOperation)) {
req.Operation = bindings.GetOperation

View File

@ -114,7 +114,12 @@ const (
)
//nolint:gochecknoglobals
var testLogger = logger.NewLogger("testLogger")
var testLogger logger.Logger
func init() {
testLogger = logger.NewLogger("testLogger")
testLogger.SetOutputLevel(logger.DebugLevel)
}
type TestConfiguration struct {
ComponentType string `yaml:"componentType,omitempty"`

0
tests/scripts/send-iot-device-events.sh Normal file → Executable file
View File