Skip to content

Commit

Permalink
Add support for shallow cloning azcore.Client instances (Azure#21065)
Browse files Browse the repository at this point in the history
* Add support for shallow cloning azcore.Client instances

This allows for multiple clients to share the same underlying pipeline
while having the correct client name string in traces.

* improved field names
  • Loading branch information
jhendrixMSFT committed Jul 12, 2023
1 parent 47286b0 commit f8e1a3e
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 2 deletions.
45 changes: 44 additions & 1 deletion sdk/azcore/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,54 @@
# Release History

## 1.7.0 (Unreleased)

### Features Added
* Added method `WithClientName()` to type `azcore.Client` to support shallow cloning of a client with a new name used for tracing.

### Breaking Changes
> These changes affect only code written against beta versions v1.7.0-beta.1 or v1.7.0-beta.2
* The beta features for CAE, tracing, and fakes have been omitted for this release.

### Bugs Fixed

### Other Changes

## 1.7.0-beta.2 (2023-06-06)

### Breaking Changes
> These changes affect only code written against beta version v1.7.0-beta.1
* Method `SpanFromContext()` on type `tracing.Tracer` had the `bool` return value removed.
* This includes the field `SpanFromContext` in supporting type `tracing.TracerOptions`.
* Method `AddError()` has been removed from type `tracing.Span`.
* Method `Span.End()` now requires an argument of type `*tracing.SpanEndOptions`.

## 1.6.1 (2023-06-06)

### Bugs Fixed
* Fixed an issue in `azcore.NewClient()` and `arm.NewClient()` that could cause an incorrect module name to be used in telemetry.

### Other Changes
* This version contains all bug fixes from `v1.7.0-beta.1`

## 1.7.0-beta.1 (2023-05-24)

### Features Added
* Restored CAE support for ARM clients.
* Added supporting features to enable distributed tracing.
* Added func `runtime.StartSpan()` for use by SDKs to start spans.
* Added method `WithContext()` to `runtime.Request` to support shallow cloning with a new context.
* Added field `TracingNamespace` to `runtime.PipelineOptions`.
* Added field `Tracer` to `runtime.NewPollerOptions` and `runtime.NewPollerFromResumeTokenOptions` types.
* Added field `SpanFromContext` to `tracing.TracerOptions`.
* Added methods `Enabled()`, `SetAttributes()`, and `SpanFromContext()` to `tracing.Tracer`.
* Added supporting pipeline policies to include HTTP spans when creating clients.
* Added package `fake` to support generated fakes packages in SDKs.
* The package contains public surface area exposed by fake servers and supporting APIs intended only for use by the fake server implementations.
* Added an internal fake poller implementation.

### Bugs Fixed
* Retry policy always clones the underlying `*http.Request` before invoking the next policy.
* Added some non-standard error codes to the list of error codes for unregistered resource providers.
* Fixed an issue in `azcore.NewClient()` and `arm.NewClient()` that could cause an incorrect module name to be used in telemetry.

## 1.6.0 (2023-05-04)

Expand Down
20 changes: 19 additions & 1 deletion sdk/azcore/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ type ClientOptions = policy.ClientOptions
type Client struct {
pl runtime.Pipeline
tr tracing.Tracer

// cached on the client to support shallow copying with new values
tp tracing.Provider
modVer string
}

// NewClient creates a new Client instance with the provided values.
Expand Down Expand Up @@ -100,7 +104,13 @@ func NewClient(clientName, moduleVersion string, plOpts runtime.PipelineOptions,
pl := runtime.NewPipeline(mod, moduleVersion, plOpts, options)

tr := options.TracingProvider.NewTracer(client, moduleVersion)
return &Client{pl: pl, tr: tr}, nil

return &Client{
pl: pl,
tr: tr,
tp: options.TracingProvider,
modVer: moduleVersion,
}, nil
}

// Pipeline returns the pipeline for this client.
Expand All @@ -112,3 +122,11 @@ func (c *Client) Pipeline() runtime.Pipeline {
func (c *Client) Tracer() tracing.Tracer {
return c.tr
}

// WithClientName returns a shallow copy of the Client with its tracing client name changed to clientName.
// Note that the values for module name and version will be preserved from the source Client.
// - clientName - the fully qualified name of the client ("package.Client"); this is used by the tracing provider when creating spans
func (c *Client) WithClientName(clientName string) *Client {
tr := c.tp.NewTracer(clientName, c.modVer)
return &Client{pl: c.pl, tr: tr, tp: c.tp, modVer: c.modVer}
}
43 changes: 43 additions & 0 deletions sdk/azcore/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@
package azcore

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

"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
"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/azcore/tracing"
"github.com/Azure/azure-sdk-for-go/sdk/internal/mock"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -131,3 +136,41 @@ func TestNewClientError(t *testing.T) {
require.Error(t, err)
require.Nil(t, client)
}

func TestClientWithClientName(t *testing.T) {
srv, close := mock.NewServer()
defer close()

var clientName string
var modVersion string
client, err := NewClient("module/package.Client", "v1.0.0", runtime.PipelineOptions{}, &policy.ClientOptions{
TracingProvider: tracing.NewProvider(func(name, version string) tracing.Tracer {
clientName = name
modVersion = version
return tracing.NewTracer(func(ctx context.Context, spanName string, options *tracing.SpanOptions) (context.Context, tracing.Span) {
return ctx, tracing.Span{}
}, nil)
}, nil),
Transport: srv,
})
require.NoError(t, err)
require.NotNil(t, client)
require.NotZero(t, client.Pipeline())
require.NotZero(t, client.Tracer())
require.EqualValues(t, "package.Client", clientName)
require.EqualValues(t, "v1.0.0", modVersion)

const requestEndpoint = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/fakeResourceGroupo/providers/Microsoft.Storage/storageAccounts/fakeAccountName"
req, err := exported.NewRequest(context.Background(), http.MethodGet, srv.URL()+requestEndpoint)
require.NoError(t, err)
srv.SetResponse()
_, err = client.Pipeline().Do(req)
require.NoError(t, err)

newClient := client.WithClientName("other.Client")
require.EqualValues(t, "other.Client", clientName)
require.EqualValues(t, "v1.0.0", modVersion)
require.EqualValues(t, client.Pipeline(), newClient.Pipeline())
_, err = newClient.Pipeline().Do(req)
require.NoError(t, err)
}

0 comments on commit f8e1a3e

Please sign in to comment.