Merge branch 'master' into deps
This commit is contained in:
commit
a53102ed04
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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}"
|
||||
|
|
|
@ -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 }}
|
||||
|
|
|
@ -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 }}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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
10
go.mod
|
@ -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
19
go.sum
|
@ -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=
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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]
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"`
|
||||
|
|
Loading…
Reference in New Issue