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 })