github.com/Jeffail/benthos/v3@v3.65.0/lib/test/integration/azure_test.go (about)

     1  package integration
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net/http"
     7  	"os"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/Azure/azure-sdk-for-go/storage"
    13  	"github.com/Jeffail/benthos/v3/internal/integration"
    14  	"github.com/ory/dockertest/v3"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  type AzuriteTransport struct {
    20  	Host string
    21  }
    22  
    23  func (t AzuriteTransport) RoundTrip(req *http.Request) (*http.Response, error) {
    24  	// Intercept all requests made to 127.0.0.1:10000 and substitute the port
    25  	// with the actual one that dockertest allocates for the Azurite container.
    26  	// azure-sdk-for-go doesn't let us change this port when adding
    27  	// `UseDevelopmentStorage=true;` to the connection string and using the
    28  	// default credentials. If we use custom credentials (see the
    29  	// `AZURITE_ACCOUNTS` env var) and don't pass `UseDevelopmentStorage=true;`
    30  	// in the connection string, then azure-sdk-for-go will try to reach a
    31  	// custom domain instead of localhost, so we'd have to use a similar hack
    32  	// to point the request to localhost instead.
    33  	if req.URL.Host == "127.0.0.1:10000" {
    34  		req.URL.Host = t.Host
    35  	}
    36  
    37  	resp, err := http.DefaultTransport.RoundTrip(req)
    38  	if err != nil {
    39  		return resp, err
    40  	}
    41  
    42  	reqURL := req.URL.String()
    43  
    44  	// Ugly hack: Detect API calls to storage.Container.ListBlobs and delete the
    45  	// empty `<Snapshot/>` node from the XML response because azure-sdk-for-go
    46  	// fails to deserialise an empty string to a valid timestamp.
    47  	// Details here: https://github.com/Azure/Azurite/issues/663
    48  	if strings.Contains(reqURL, "comp=list") &&
    49  		strings.Contains(reqURL, "restype=container") {
    50  		bodyBytes, err := io.ReadAll(resp.Body)
    51  		if err != nil {
    52  			return resp, fmt.Errorf("failed to read response body: %w", err)
    53  		}
    54  		newBody := strings.ReplaceAll(string(bodyBytes), "<Snapshot/>", "")
    55  		resp.Body = io.NopCloser(strings.NewReader(newBody))
    56  		resp.ContentLength = int64(len(newBody))
    57  	}
    58  
    59  	return resp, err
    60  }
    61  
    62  var _ = registerIntegrationTest("azure", func(t *testing.T) {
    63  	// Don't run this test by default, because it messes around with the
    64  	// http.DefaultClient
    65  	t.Skip()
    66  
    67  	t.Parallel()
    68  
    69  	pool, err := dockertest.NewPool("")
    70  	require.NoError(t, err)
    71  
    72  	pool.MaxWait = 30 * time.Second
    73  	if deadline, ok := t.Deadline(); ok {
    74  		pool.MaxWait = time.Until(deadline) - 100*time.Millisecond
    75  	}
    76  
    77  	resource, err := pool.RunWithOptions(&dockertest.RunOptions{
    78  		Repository: "mcr.microsoft.com/azure-storage/azurite",
    79  		Tag:        "3.9.0",
    80  		// Expose Azurite ports in the random port range, so we don't clash with
    81  		// other apps.
    82  		ExposedPorts: []string{"10000/tcp", "10001/tcp"},
    83  	})
    84  	require.NoError(t, err)
    85  	t.Cleanup(func() {
    86  		assert.NoError(t, pool.Purge(resource))
    87  	})
    88  
    89  	resource.Expire(900)
    90  
    91  	blobServicePort := resource.GetPort("10000/tcp")
    92  	origDefaultClientTransport := http.DefaultClient.Transport
    93  	http.DefaultClient.Transport = AzuriteTransport{Host: "localhost:" + blobServicePort}
    94  	t.Cleanup(func() {
    95  		http.DefaultClient.Transport = origDefaultClientTransport
    96  	})
    97  
    98  	// Wait for Azurite to properly start up
    99  	// Copied from https://github.com/mfamador/data-webhooks/blob/2dca9b0fa36bcbadf38884fb1a2e8a3614e6135e/lib/docker_containers.go#L225-L236
   100  	err = pool.Retry(func() error {
   101  		client, eerr := storage.NewEmulatorClient()
   102  		if eerr != nil {
   103  			return eerr
   104  		}
   105  		s := client.GetBlobService()
   106  		c := s.GetContainerReference("cont")
   107  		if _, err = c.Exists(); err != nil {
   108  			return err
   109  		}
   110  		return nil
   111  	})
   112  	require.NoError(t, err, "Failed to start Azurite")
   113  
   114  	dummyContainer := "jotunheim"
   115  	dummyPrefix := "kvenn"
   116  	t.Run("blob_storage", func(t *testing.T) {
   117  		template := `
   118  output:
   119    azure_blob_storage:
   120      blob_type: BLOCK
   121      container: $VAR1-$ID
   122      max_in_flight: 1
   123      path: $VAR2/${!count("$ID")}.txt
   124      public_access_level: PRIVATE
   125      storage_connection_string: "UseDevelopmentStorage=true;"
   126  
   127  input:
   128    azure_blob_storage:
   129      container: $VAR1-$ID
   130      prefix: $VAR2
   131      storage_connection_string: "UseDevelopmentStorage=true;"
   132  `
   133  		integration.StreamTests(
   134  			integration.StreamTestOpenCloseIsolated(),
   135  			integration.StreamTestStreamIsolated(10),
   136  		).Run(
   137  			t, template,
   138  			integration.StreamTestOptVarOne(dummyContainer),
   139  			integration.StreamTestOptVarTwo(dummyPrefix),
   140  		)
   141  	})
   142  
   143  	// TODO: Re-enable this after https://github.com/Azure/Azurite/issues/682 is fixed
   144  	// 	t.Run("blob_storage_append", func(t *testing.T) {
   145  	// 		template := `
   146  	// output:
   147  	//   azure_blob_storage:
   148  	//     blob_type: APPEND
   149  	//     container: $VAR1
   150  	//     max_in_flight: 1
   151  	//     path: $VAR2/data.txt
   152  	//     public_access_level: PRIVATE
   153  	//     storage_connection_string: "UseDevelopmentStorage=true;"
   154  
   155  	// input:
   156  	//   azure_blob_storage:
   157  	//     container: $VAR1
   158  	//     prefix: $VAR2/data.txt
   159  	//     storage_connection_string: "UseDevelopmentStorage=true;"
   160  	// `
   161  	// 		integration.StreamTests(
   162  	// 			integration.StreamTestOpenCloseIsolated(),
   163  	// 		).Run(
   164  	// 			t, template,
   165  	// 			integration.StreamTestOptVarOne(dummyContainer),
   166  	// 			integration.StreamTestOptVarTwo(dummyPrefix),
   167  	// 		)
   168  	// 	})
   169  
   170  	os.Setenv("AZURITE_QUEUE_ENDPOINT_PORT", resource.GetPort("10001/tcp"))
   171  	dummyQueue := "foo"
   172  	t.Run("queue_storage", func(t *testing.T) {
   173  		template := `
   174  output:
   175   azure_queue_storage:
   176     queue_name: $VAR1$ID
   177     storage_connection_string: "UseDevelopmentStorage=true;"
   178  
   179  input:
   180   azure_queue_storage:
   181     queue_name: $VAR1$ID
   182     storage_connection_string: "UseDevelopmentStorage=true;"
   183  `
   184  		integration.StreamTests(
   185  			integration.StreamTestOpenCloseIsolated(),
   186  			integration.StreamTestStreamIsolated(10),
   187  		).Run(
   188  			t, template,
   189  			integration.StreamTestOptVarOne(dummyQueue),
   190  		)
   191  	})
   192  })