Skip to content

Commit

Permalink
add start/stop recording proxy and remove useless policy (Azure#22968)
Browse files Browse the repository at this point in the history
* add start/stop recording proxy and remove useless policy

* update version

* update ci config

* fix

* resolve comment and update recording

* disable location sanitizer to avoid lro playback fail

* Update CHANGELOG.md

* Update sdk/resourcemanager/internal/testutil/recording.go

Co-authored-by: Charles Lowell <10964656+chlowell@users.noreply.github.com>

* remove skip

---------

Co-authored-by: Charles Lowell <10964656+chlowell@users.noreply.github.com>
Co-authored-by: Alancere <804873052@qq.com>
  • Loading branch information
3 people authored Jun 4, 2024
1 parent cd497f0 commit 9f7b083
Show file tree
Hide file tree
Showing 13 changed files with 74 additions and 127 deletions.
8 changes: 4 additions & 4 deletions sdk/resourcemanager/internal/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
# Release History

## 2.0.1 (Unreleased)
## 3.0.0 (2024-05-31)

### Features Added
* Add `StartProxy` to help start and stop build-in test proxy for each module's test.

### Breaking Changes

### Bugs Fixed
* Remove `NewRecordingPolicy`, use `GetCredAndClientOptions` directly.

### Other Changes
* Updated dependencies.

## 2.0.0 (2023-11-16)

### Breaking Changes
* Removed `testutil.GetEnv`, use `github.com/Azure/azure-sdk-for-go/sdk/internal/recording.GetEnvVariable` instead.
* Remove `testutil.GetEnv`, use `github.com/Azure/azure-sdk-for-go/sdk/internal/recording.GetEnvVariable` instead.
* Remove `testutil.GenerateAlphaNumericID`, use `github.com/Azure/azure-sdk-for-go/sdk/internal/recording.GenerateAlphaNumericID` instead.

### Other Changes
Expand Down
2 changes: 1 addition & 1 deletion sdk/resourcemanager/internal/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "go",
"TagPrefix": "go/resourcemanager/internal",
"Tag": "go/resourcemanager/internal_227105d301"
"Tag": "go/resourcemanager/internal_94fa3b908d"
}
1 change: 1 addition & 0 deletions sdk/resourcemanager/internal/ci.resourcemanager.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ extends:
parameters:
IncludeRelease: true
ServiceDirectory: resourcemanager/internal
UsePipelineProxy: false
2 changes: 1 addition & 1 deletion sdk/resourcemanager/internal/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.18
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2
github.com/Azure/azure-sdk-for-go/sdk/internal v1.7.0
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0
github.com/stretchr/testify v1.9.0
)
Expand Down
4 changes: 2 additions & 2 deletions sdk/resourcemanager/internal/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqb
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2 h1:FDif4R1+UUR+00q6wquyX90K7A8dN+R5E8GEadoP7sU=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2/go.mod h1:aiYBYui4BJ/BJCAIKs92XiPyQfTaBWqvHujDwKb6CBU=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.7.0 h1:rTfKOCZGy5ViVrlA74ZPE99a+SgoEE2K/yg3RyW9dFA=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.7.0/go.mod h1:4OG6tQ9EOP/MT0NMjDlRzWoVFxfu9rN9B2X+tlSVktg=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 h1:jBQA3cKT4L2rWMpgE7Yt3Hwh2aUj8KXjIGLxjHeYNNo=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0/go.mod h1:4OG6tQ9EOP/MT0NMjDlRzWoVFxfu9rN9B2X+tlSVktg=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0 h1:pPvTJ1dY0sA35JOeFq6TsY2xj6Z85Yo23Pj4wCCvu4o=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE=
Expand Down
8 changes: 3 additions & 5 deletions sdk/resourcemanager/internal/testutil/credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,14 @@ func (c *FakeCredential) GetToken(ctx context.Context, opts policy.TokenRequestO
// In the record mode, the credential will be a DefaultAzureCredential which combines several common credentials.
// In the playback mode, the credential will be a fake credential which will bypass truly authorization.
func GetCredAndClientOptions(t *testing.T) (azcore.TokenCredential, *arm.ClientOptions) {
p := NewRecordingPolicy(t, &recording.RecordingOptions{UseHTTPS: true})
client, err := recording.GetHTTPClient(t)
transport, err := recording.NewRecordingHTTPClient(t, nil)
if err != nil {
t.Fatalf("Failed to create recording client: %v", err)
t.Fatalf("Failed to create recording transport: %v", err)
}

options := &arm.ClientOptions{
ClientOptions: policy.ClientOptions{
PerCallPolicies: []policy.Policy{p},
Transport: client,
Transport: transport,
},
}

Expand Down
3 changes: 2 additions & 1 deletion sdk/resourcemanager/internal/testutil/credential_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func (r *testBody) Seek(offset int64, whence int) (int64, error) {
}

func TestGetCredAndClientOptions(t *testing.T) {
testEndpoint := "http://test"
testEndpoint := "https://test"
cred, options := GetCredAndClientOptions(t)
pl, err := armruntime.NewPipeline("testmodule", "v0.1.0", cred, runtime.PipelineOptions{}, options)
require.NoError(t, err)
Expand All @@ -50,4 +50,5 @@ func TestGetCredAndClientOptions(t *testing.T) {
require.Equal(t, "Bearer FakeToken", resp.Request.Header.Get("Authorization"))
}
require.Equal(t, testEndpoint, resp.Request.URL.String())

}
12 changes: 0 additions & 12 deletions sdk/resourcemanager/internal/testutil/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ package testutil

import (
"context"
"strings"
"testing"

"github.com/Azure/azure-sdk-for-go/sdk/internal/recording"
Expand All @@ -18,30 +17,19 @@ import (
"github.com/stretchr/testify/require"
)

const (
pathToPackage = "sdk/resourcemanager/internal/testdata"
)

func TestCreateDeleteResourceGroup(t *testing.T) {
if recording.GetRecordMode() == recording.PlaybackMode {
t.Skip("https://github.com/Azure/azure-sdk-for-go/issues/22869")
}
ctx := context.Background()
cred, options := GetCredAndClientOptions(t)
subscriptionID := recording.GetEnvVariable("AZURE_SUBSCRIPTION_ID", "00000000-0000-0000-0000-000000000000")
stop := StartRecording(t, pathToPackage)
defer stop()
resourceGroup, _, err := CreateResourceGroup(ctx, subscriptionID, cred, options, "eastus")
require.NoError(t, err)
require.True(t, strings.HasPrefix(*resourceGroup.Name, "go-sdk-test-"))
_, err = DeleteResourceGroup(ctx, subscriptionID, cred, options, *resourceGroup.Name)
require.NoError(t, err)
}

func TestCreateDeployment(t *testing.T) {
if recording.GetRecordMode() == recording.PlaybackMode {
t.Skip("https://github.com/Azure/azure-sdk-for-go/issues/22869")
}
ctx := context.Background()
cred, options := GetCredAndClientOptions(t)
subscriptionID := recording.GetEnvVariable("AZURE_SUBSCRIPTION_ID", "00000000-0000-0000-0000-000000000000")
Expand Down
108 changes: 32 additions & 76 deletions sdk/resourcemanager/internal/testutil/recording.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,95 +8,51 @@ package testutil

import (
"fmt"
"net/http"
"strconv"
"testing"

"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/internal/recording"
)

const recordingRandomSeedVariableName = "recordingRandomSeed"

var (
recordingSeed int64
)

type recordingPolicy struct {
options recording.RecordingOptions
t *testing.T
}

// Host of the test proxy.
func (r *recordingPolicy) Host() string {
if r.options.UseHTTPS {
return "localhost:5001"
}
return "localhost:5000"
}

// Scheme of the test proxy.
func (r *recordingPolicy) Scheme() string {
if r.options.UseHTTPS {
return "https"
}
return "http"
}

// NewRecordingPolicy will create a recording policy which can be used in pipeline.
// The policy will change the destination of the request to the proxy server and add required header for the recording test.
func NewRecordingPolicy(t *testing.T, o *recording.RecordingOptions) policy.Policy {
if o == nil {
o = &recording.RecordingOptions{UseHTTPS: true}
}
p := &recordingPolicy{options: *o, t: t}
return p
}

// Do with recording mode.
// When handling live request, the policy will do nothing.
// Otherwise, the policy will replace the URL of the request with the test proxy endpoint.
// After request, the policy will change back to the original URL for the request to prevent wrong polling URL for LRO.
func (r *recordingPolicy) Do(req *policy.Request) (resp *http.Response, err error) {
if recording.GetRecordMode() != "live" && !recording.IsLiveOnly(r.t) {
oriSchema := req.Raw().URL.Scheme
oriHost := req.Raw().URL.Host
req.Raw().URL.Scheme = r.Scheme()
req.Raw().URL.Host = r.Host()
req.Raw().Host = r.Host()
// StartProxy starts the test proxy with the path to store test recording file.
// It should be used in the module test preparation stage only once.
// It will return a delegate function to stop test proxy.
func StartProxy(pathToPackage string) func() {
if recording.GetRecordMode() == recording.PlaybackMode || recording.GetRecordMode() == recording.RecordingMode {
proxy, err := recording.StartTestProxy(pathToPackage, nil)
if err != nil {
panic(fmt.Sprintf("Failed to start recording proxy: %v", err))
}

// replace request target to use test proxy
req.Raw().Header.Set(recording.UpstreamURIHeader, fmt.Sprintf("%v://%v", oriSchema, oriHost))
req.Raw().Header.Set(recording.ModeHeader, recording.GetRecordMode())
req.Raw().Header.Set(recording.IDHeader, recording.GetRecordingId(r.t))
// sanitizer for any uuid string, e.g., subscriptionID
err = recording.AddGeneralRegexSanitizer("00000000-0000-0000-0000-000000000000", `[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}`, proxy.Options)
if err != nil {
panic(fmt.Sprintf("Failed to add uuid sanitizer: %v", err))
}
// consolidate resource group name for recording and playback
err = recording.AddGeneralRegexSanitizer(recording.SanitizedValue, `go-sdk-test-\d+`, proxy.Options)
if err != nil {
panic(fmt.Sprintf("Failed to add resource group name sanitizer: %v", err))
}
// disable location header sanitizer
err = recording.RemoveRegisteredSanitizers([]string{"AZSDK2003", "AZSDK2030"}, proxy.Options)
if err != nil {
panic(fmt.Sprintf("Failed to remove location header sanitizer: %v", err))
}

resp, err = req.Next()
// for any lro operation, need to change back to the original target to prevent
if resp != nil {
resp.Request.URL.Scheme = oriSchema
resp.Request.URL.Host = oriHost
return func() {
err := recording.StopTestProxy(proxy)
if err != nil {
panic(fmt.Sprintf("Failed to stop recording proxy: %v", err))
}
}
return resp, err
} else {
return req.Next()
}
return func() {}
}

// StartRecording starts the recording with the path to store recording file.
// It will return a delegate function to stop recording.
func StartRecording(t *testing.T, pathToPackage string) func() {
option := &recording.RecordingOptions{UseHTTPS: true}
// sanitizer for any uuid string, e.g., subscriptionID
err := recording.AddGeneralRegexSanitizer("00000000-0000-0000-0000-000000000000", `[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}`, option)
if err != nil {
t.Fatalf("Failed to add uuid sanitizer: %v", err)
}
// consolidate resource group name for recording and playback
err = recording.AddGeneralRegexSanitizer("go-sdk-test-rg", `go-sdk-test-\d+`, option)
if err != nil {
t.Fatalf("Failed to add resource group name sanitizer: %v", err)
}
err = recording.Start(t, pathToPackage, option)
err := recording.Start(t, pathToPackage, nil)
if err != nil {
t.Fatalf("Failed to start recording: %v", err)
}
Expand All @@ -105,7 +61,7 @@ func StartRecording(t *testing.T, pathToPackage string) func() {

// StopRecording stops the recording.
func StopRecording(t *testing.T) {
err := recording.Stop(t, &recording.RecordingOptions{Variables: map[string]interface{}{recordingRandomSeedVariableName: strconv.FormatInt(recordingSeed, 10)}})
err := recording.Stop(t, nil)
if err != nil {
t.Fatalf("Failed to stop recording: %v", err)
}
Expand Down
21 changes: 0 additions & 21 deletions sdk/resourcemanager/internal/testutil/recording_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,9 @@
package testutil

import (
"context"
"net/http"
"strings"
"testing"

"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
"github.com/Azure/azure-sdk-for-go/sdk/internal/recording"
"github.com/stretchr/testify/require"
)

func TestRecordingPolicy(t *testing.T) {
testEndpoint := "http://test"
pl := runtime.NewPipeline("testmodule", "v0.1.0", runtime.PipelineOptions{}, &policy.ClientOptions{PerCallPolicies: []policy.Policy{NewRecordingPolicy(t, &recording.RecordingOptions{UseHTTPS: false})}})
req, err := runtime.NewRequest(context.Background(), http.MethodGet, testEndpoint)
require.NoError(t, err)
err = req.SetBody(&testBody{body: strings.NewReader("test")}, "text/plain")
require.NoError(t, err)
resp, err := pl.Do(req)
require.NoError(t, err)
require.Equal(t, http.StatusBadRequest, resp.StatusCode)
require.Equal(t, testEndpoint, resp.Request.URL.String())
}

func TestStartStopRecording(t *testing.T) {
stop := StartRecording(t, pathToPackage)
defer stop()
Expand Down
3 changes: 0 additions & 3 deletions sdk/resourcemanager/internal/testutil/testsuite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ func (testsuite *ExampleTestSuite) TearDownSuite() {
}

func TestExampleTestSuite(t *testing.T) {
if recording.GetRecordMode() == recording.PlaybackMode {
t.Skip("https://github.com/Azure/azure-sdk-for-go/issues/22869")
}
suite.Run(t, new(ExampleTestSuite))
}

Expand Down
27 changes: 27 additions & 0 deletions sdk/resourcemanager/internal/testutil/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//go:build go1.18
// +build go1.18

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

package testutil

import (
"os"
"testing"
)

const (
pathToPackage = "sdk/resourcemanager/internal/testdata"
)

func TestMain(m *testing.M) {
code := run(m)
os.Exit(code)
}

func run(m *testing.M) int {
f := StartProxy(pathToPackage)
defer f()
return m.Run()
}
2 changes: 1 addition & 1 deletion sdk/resourcemanager/internal/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ const (
Module = "internal"

// Version is the semantic version (see http://semver.org) of this module.
Version = "v2.0.1"
Version = "v3.0.0"
)

0 comments on commit 9f7b083

Please sign in to comment.