diff --git a/README.md b/README.md index 8ff71570..7a33a765 100644 --- a/README.md +++ b/README.md @@ -234,6 +234,21 @@ The following one-time filters are available: - `cl.For("gofish").GetPowerState(ctx)` - This removes any provider from the registry that is not the `gofish` provider. - `cl.PreferProtocol("redfish").GetPowerState(ctx)` - This moves any provider that implements the `redfish` protocol to the beginning of the registry. +### Tracing + +To collect trace telemetry, set the `WithTraceProvider()` option on the client +which results in trace spans being collected for each client method. + +```go +cl := bmclib.NewClient( + host, + user, + pass, + bmclib.WithLogger(log), + bmclib.WithTracerProvider(otel.GetTracerProvider()), + ) +``` + ## Versions The current bmclib version is `v2` and is being developed on the `main` branch. diff --git a/bmc/bmc.go b/bmc/bmc.go index fdbd7329..5fbd275d 100644 --- a/bmc/bmc.go +++ b/bmc/bmc.go @@ -1,5 +1,12 @@ package bmc +import ( + "strings" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + // Metadata represents details about a bmc method type Metadata struct { // SuccessfulProvider is the name of the provider that successfully executed @@ -13,3 +20,33 @@ type Metadata struct { // FailedProviderDetail holds the failed providers error messages for called methods FailedProviderDetail map[string]string } + +func newMetadata() Metadata { + return Metadata{ + FailedProviderDetail: make(map[string]string), + } +} + +func (m *Metadata) RegisterSpanAttributes(host string, span trace.Span) { + span.SetAttributes(attribute.String("host", host)) + + span.SetAttributes(attribute.String("successful-provider", m.SuccessfulProvider)) + + span.SetAttributes( + attribute.String("successful-open-conns", strings.Join(m.SuccessfulOpenConns, ",")), + ) + + span.SetAttributes( + attribute.String("successful-close-conns", strings.Join(m.SuccessfulCloseConns, ",")), + ) + + span.SetAttributes( + attribute.String("attempted-providers", strings.Join(m.ProvidersAttempted, ",")), + ) + + for p, e := range m.FailedProviderDetail { + span.SetAttributes( + attribute.String("provider-errs-"+p, e), + ) + } +} diff --git a/bmc/connection.go b/bmc/connection.go index f861bd13..947fc1c1 100644 --- a/bmc/connection.go +++ b/bmc/connection.go @@ -30,9 +30,7 @@ type connectionProviders struct { // The reason failed ones need to be removed is so that when other methods are called (like powerstate) // implementations that have connections wont nil pointer error when their connection fails. func OpenConnectionFromInterfaces(ctx context.Context, timeout time.Duration, providers []interface{}) (opened []interface{}, metadata Metadata, err error) { - metadata = Metadata{ - FailedProviderDetail: make(map[string]string), - } + metadata = newMetadata() // Return immediately if the context is done. select { @@ -110,10 +108,8 @@ func OpenConnectionFromInterfaces(ctx context.Context, timeout time.Duration, pr } // closeConnection closes a connection to a BMC, trying all interface implementations passed in -func closeConnection(ctx context.Context, c []connectionProviders) (_ Metadata, err error) { - var metadata = Metadata{ - FailedProviderDetail: make(map[string]string), - } +func closeConnection(ctx context.Context, c []connectionProviders) (metadata Metadata, err error) { + metadata = newMetadata() var connClosed bool for _, elem := range c { @@ -138,6 +134,8 @@ func closeConnection(ctx context.Context, c []connectionProviders) (_ Metadata, // CloseConnectionFromInterfaces identifies implementations of the Closer() interface and and passes the found implementations to the closeConnection() wrapper func CloseConnectionFromInterfaces(ctx context.Context, generic []interface{}) (metadata Metadata, err error) { + metadata = newMetadata() + closers := make([]connectionProviders, 0) for _, elem := range generic { temp := connectionProviders{name: getProviderName(elem)} diff --git a/bmc/firmware.go b/bmc/firmware.go index e235bf20..b139e488 100644 --- a/bmc/firmware.go +++ b/bmc/firmware.go @@ -67,6 +67,8 @@ func firmwareInstall(ctx context.Context, component, operationApplyTime string, // FirmwareInstallFromInterfaces identifies implementations of the FirmwareInstaller interface and passes the found implementations to the firmwareInstall() wrapper func FirmwareInstallFromInterfaces(ctx context.Context, component, operationApplyTime string, forceInstall bool, reader io.Reader, generic []interface{}) (taskID string, metadata Metadata, err error) { + metadata = newMetadata() + implementations := make([]firmwareInstallerProvider, 0) for _, elem := range generic { temp := firmwareInstallerProvider{name: getProviderName(elem)} @@ -146,6 +148,8 @@ func firmwareInstallStatus(ctx context.Context, installVersion, component, taskI // FirmwareInstallStatusFromInterfaces identifies implementations of the FirmwareInstallVerifier interface and passes the found implementations to the firmwareInstallStatus() wrapper. func FirmwareInstallStatusFromInterfaces(ctx context.Context, installVersion, component, taskID string, generic []interface{}) (status string, metadata Metadata, err error) { + metadata = newMetadata() + implementations := make([]firmwareInstallVerifierProvider, 0) for _, elem := range generic { temp := firmwareInstallVerifierProvider{name: getProviderName(elem)} @@ -223,6 +227,8 @@ func firmwareInstallUploaded(ctx context.Context, component, uploadTaskID string // FirmwareInstallerUploadedFromInterfaces identifies implementations of the FirmwareInstallUploaded interface and passes the found implementations to the firmwareInstallUploaded() wrapper func FirmwareInstallerUploadedFromInterfaces(ctx context.Context, component, uploadTaskID string, generic []interface{}) (installTaskID string, metadata Metadata, err error) { + metadata = newMetadata() + implementations := make([]firmwareInstallerWithOptionsProvider, 0) for _, elem := range generic { temp := firmwareInstallerWithOptionsProvider{name: getProviderName(elem)} @@ -260,6 +266,8 @@ type firmwareInstallStepsGetterProvider struct { // FirmwareInstallStepsFromInterfaces identifies implementations of the FirmwareInstallStepsGetter interface and passes the found implementations to the firmwareInstallSteps() wrapper. func FirmwareInstallStepsFromInterfaces(ctx context.Context, component string, generic []interface{}) (steps []constants.FirmwareInstallStep, metadata Metadata, err error) { + metadata = newMetadata() + implementations := make([]firmwareInstallStepsGetterProvider, 0) for _, elem := range generic { temp := firmwareInstallStepsGetterProvider{name: getProviderName(elem)} @@ -326,6 +334,8 @@ type firmwareUploaderProvider struct { // FirmwareUploaderFromInterfaces identifies implementations of the FirmwareUploader interface and passes the found implementations to the firmwareUpload() wrapper. func FirmwareUploadFromInterfaces(ctx context.Context, component string, file *os.File, generic []interface{}) (taskID string, metadata Metadata, err error) { + metadata = newMetadata() + implementations := make([]firmwareUploaderProvider, 0) for _, elem := range generic { temp := firmwareUploaderProvider{name: getProviderName(elem)} @@ -437,6 +447,8 @@ func firmwareTaskStatus(ctx context.Context, kind bconsts.FirmwareInstallStep, c // FirmwareTaskStatusFromInterfaces identifies implementations of the FirmwareTaskVerifier interface and passes the found implementations to the firmwareTaskStatus() wrapper. func FirmwareTaskStatusFromInterfaces(ctx context.Context, kind bconsts.FirmwareInstallStep, component, taskID, installVersion string, generic []interface{}) (state, status string, metadata Metadata, err error) { + metadata = newMetadata() + implementations := make([]firmwareTaskVerifierProvider, 0) for _, elem := range generic { temp := firmwareTaskVerifierProvider{name: getProviderName(elem)} diff --git a/bmc/inventory.go b/bmc/inventory.go index 44a90117..7ce067ea 100644 --- a/bmc/inventory.go +++ b/bmc/inventory.go @@ -53,6 +53,8 @@ func inventory(ctx context.Context, generic []inventoryGetterProvider) (device * // GetInventoryFromInterfaces identifies implementations of the InventoryGetter interface and passes the found implementations to the inventory() wrapper method func GetInventoryFromInterfaces(ctx context.Context, generic []interface{}) (device *common.Device, metadata Metadata, err error) { + metadata = newMetadata() + implementations := make([]inventoryGetterProvider, 0) for _, elem := range generic { temp := inventoryGetterProvider{name: getProviderName(elem)} diff --git a/bmc/power.go b/bmc/power.go index 1f5ca512..1b24edfa 100644 --- a/bmc/power.go +++ b/bmc/power.go @@ -75,6 +75,8 @@ func setPowerState(ctx context.Context, timeout time.Duration, state string, p [ // SetPowerStateFromInterfaces identifies implementations of the PostStateSetter interface and passes the found implementations to the setPowerState() wrapper. func SetPowerStateFromInterfaces(ctx context.Context, timeout time.Duration, state string, generic []interface{}) (ok bool, metadata Metadata, err error) { + metadata = newMetadata() + powerSetter := make([]powerProviders, 0) for _, elem := range generic { temp := powerProviders{name: getProviderName(elem)} @@ -126,6 +128,8 @@ func getPowerState(ctx context.Context, timeout time.Duration, p []powerProvider // GetPowerStateFromInterfaces identifies implementations of the PostStateGetter interface and passes the found implementations to the getPowerState() wrapper. func GetPowerStateFromInterfaces(ctx context.Context, timeout time.Duration, generic []interface{}) (state string, metadata Metadata, err error) { + metadata = newMetadata() + powerStateGetter := make([]powerProviders, 0) for _, elem := range generic { temp := powerProviders{name: getProviderName(elem)} diff --git a/bmc/reset.go b/bmc/reset.go index b0833491..b7c2d706 100644 --- a/bmc/reset.go +++ b/bmc/reset.go @@ -57,6 +57,8 @@ func resetBMC(ctx context.Context, timeout time.Duration, resetType string, b [] // ResetBMCFromInterfaces identifies implementations of the BMCResetter interface and passes them to the resetBMC() wrapper method. func ResetBMCFromInterfaces(ctx context.Context, timeout time.Duration, resetType string, generic []interface{}) (ok bool, metadata Metadata, err error) { + metadata = newMetadata() + bmcSetters := make([]bmcProviders, 0) for _, elem := range generic { temp := bmcProviders{name: getProviderName(elem)} diff --git a/client.go b/client.go index a1917543..0bac7eae 100644 --- a/client.go +++ b/client.go @@ -8,6 +8,7 @@ import ( "io" "net/http" "os" + "strings" "sync" "time" @@ -25,11 +26,16 @@ import ( "github.com/bmc-toolbox/common" "github.com/go-logr/logr" "github.com/jacobweinstock/registrar" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + oteltrace "go.opentelemetry.io/otel/trace" + tracenoop "go.opentelemetry.io/otel/trace/noop" ) const ( // default connection timeout defaultConnectTimeout = 30 * time.Second + pkgName = "github.com/bmc-toolbox/bmclib" ) // Client for BMC interactions @@ -46,6 +52,7 @@ type Client struct { oneTimeRegistry *registrar.Registry oneTimeRegistryEnabled bool providerConfig providerConfig + traceprovider oteltrace.TracerProvider } // Auth details for connecting to a BMC @@ -74,6 +81,7 @@ func NewClient(host, user, pass string, opts ...Option) *Client { oneTimeRegistryEnabled: false, oneTimeRegistry: registrar.NewRegistry(), httpClient: httpclient.Build(), + traceprovider: tracenoop.NewTracerProvider(), providerConfig: providerConfig{ ipmitool: ipmitool.Config{ Port: "623", @@ -290,12 +298,40 @@ func (c *Client) registry() *registrar.Registry { return c.Registry } +func (c *Client) RegisterSpanAttributes(m bmc.Metadata, span trace.Span) { + span.SetAttributes(attribute.String("host", c.Auth.Host)) + + span.SetAttributes(attribute.String("successful-provider", m.SuccessfulProvider)) + + span.SetAttributes( + attribute.String("successful-open-conns", strings.Join(m.SuccessfulOpenConns, ",")), + ) + + span.SetAttributes( + attribute.String("successful-close-conns", strings.Join(m.SuccessfulCloseConns, ",")), + ) + + span.SetAttributes( + attribute.String("attempted-providers", strings.Join(m.ProvidersAttempted, ",")), + ) + + for p, e := range m.FailedProviderDetail { + span.SetAttributes( + attribute.String("provider-errs-"+p, e), + ) + } +} + // Open calls the OpenConnectionFromInterfaces library function // Any providers/drivers that do not successfully connect are removed // from the client.Registry.Drivers. If client.Registry.Drivers ends up // being empty then we error. func (c *Client) Open(ctx context.Context) error { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "Open") + defer span.End() + ifs, metadata, err := bmc.OpenConnectionFromInterfaces(ctx, c.perProviderTimeout(ctx), c.registry().GetDriverInterfaces()) + metadata.RegisterSpanAttributes(c.Auth.Host, span) defer c.setMetadata(metadata) if err != nil { return err @@ -316,6 +352,10 @@ func (c *Client) Open(ctx context.Context) error { // Close pass through to library function func (c *Client) Close(ctx context.Context) (err error) { + + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "Close") + defer span.End() + // Generally, we always want the close function to run. // We don't want a context timeout or cancellation to prevent this. // But because the current model is to pass just a single context to all @@ -328,6 +368,8 @@ func (c *Client) Close(ctx context.Context) (err error) { } metadata, err := bmc.CloseConnectionFromInterfaces(ctx, c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + return err } @@ -343,50 +385,85 @@ func (c *Client) FilterForCompatible(ctx context.Context) { // GetPowerState pass through to library function func (c *Client) GetPowerState(ctx context.Context) (state string, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "GetPowerState") + defer span.End() + state, metadata, err := bmc.GetPowerStateFromInterfaces(ctx, c.perProviderTimeout(ctx), c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + return state, err } // SetPowerState pass through to library function func (c *Client) SetPowerState(ctx context.Context, state string) (ok bool, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "SetPowerState") + defer span.End() + ok, metadata, err := bmc.SetPowerStateFromInterfaces(ctx, c.perProviderTimeout(ctx), state, c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + return ok, err } // CreateUser pass through to library function func (c *Client) CreateUser(ctx context.Context, user, pass, role string) (ok bool, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "CreateUser") + defer span.End() + ok, metadata, err := bmc.CreateUserFromInterfaces(ctx, c.perProviderTimeout(ctx), user, pass, role, c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + return ok, err } // UpdateUser pass through to library function func (c *Client) UpdateUser(ctx context.Context, user, pass, role string) (ok bool, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "UpdateUser") + defer span.End() + ok, metadata, err := bmc.UpdateUserFromInterfaces(ctx, c.perProviderTimeout(ctx), user, pass, role, c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + return ok, err } // DeleteUser pass through to library function func (c *Client) DeleteUser(ctx context.Context, user string) (ok bool, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "DeleteUser") + defer span.End() + ok, metadata, err := bmc.DeleteUserFromInterfaces(ctx, c.perProviderTimeout(ctx), user, c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + return ok, err } // ReadUsers pass through to library function func (c *Client) ReadUsers(ctx context.Context) (users []map[string]string, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "ReadUsers") + defer span.End() + users, metadata, err := bmc.ReadUsersFromInterfaces(ctx, c.perProviderTimeout(ctx), c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + return users, err } // SetBootDevice pass through to library function func (c *Client) SetBootDevice(ctx context.Context, bootDevice string, setPersistent, efiBoot bool) (ok bool, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "SetBootDevice") + defer span.End() + ok, metadata, err := bmc.SetBootDeviceFromInterfaces(ctx, c.perProviderTimeout(ctx), bootDevice, setPersistent, efiBoot, c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + return ok, err } @@ -395,35 +472,58 @@ func (c *Client) SetBootDevice(ctx context.Context, bootDevice string, setPersis // mediaURL isn't empty, attaches a virtual media device of type kind whose contents are // streamed from the indicated URL. func (c *Client) SetVirtualMedia(ctx context.Context, kind string, mediaURL string) (ok bool, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "SetVirtualMedia") + defer span.End() + ok, metadata, err := bmc.SetVirtualMediaFromInterfaces(ctx, kind, mediaURL, c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + return ok, err } // ResetBMC pass through to library function func (c *Client) ResetBMC(ctx context.Context, resetType string) (ok bool, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "ResetBMC") + defer span.End() + ok, metadata, err := bmc.ResetBMCFromInterfaces(ctx, c.perProviderTimeout(ctx), resetType, c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + return ok, err } // Inventory pass through library function to collect hardware and firmware inventory func (c *Client) Inventory(ctx context.Context) (device *common.Device, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "Inventory") + defer span.End() + device, metadata, err := bmc.GetInventoryFromInterfaces(ctx, c.registry().GetDriverInterfaces()) c.setMetadata(metadata) return device, err } func (c *Client) GetBiosConfiguration(ctx context.Context) (biosConfig map[string]string, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "GetBiosConfiguration") + defer span.End() + biosConfig, metadata, err := bmc.GetBiosConfigurationInterfaces(ctx, c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + return biosConfig, err } // FirmwareInstall pass through library function to upload firmware and install firmware func (c *Client) FirmwareInstall(ctx context.Context, component string, operationApplyTime string, forceInstall bool, reader io.Reader) (taskID string, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "FirmwareInstall") + defer span.End() + taskID, metadata, err := bmc.FirmwareInstallFromInterfaces(ctx, component, operationApplyTime, forceInstall, reader, c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + return taskID, err } @@ -431,71 +531,117 @@ func (c *Client) FirmwareInstall(ctx context.Context, component string, operatio // // FirmwareInstallStatus pass through library function to check firmware install status func (c *Client) FirmwareInstallStatus(ctx context.Context, installVersion, component, taskID string) (status string, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "FirmwareInstallStatus") + defer span.End() + status, metadata, err := bmc.FirmwareInstallStatusFromInterfaces(ctx, installVersion, component, taskID, c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + return status, err } // PostCodeGetter pass through library function to return the BIOS/UEFI POST code func (c *Client) PostCode(ctx context.Context) (status string, code int, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "PostCode") + defer span.End() + status, code, metadata, err := bmc.GetPostCodeInterfaces(ctx, c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + return status, code, err } func (c *Client) Screenshot(ctx context.Context) (image []byte, fileType string, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "Screenshot") + defer span.End() + image, fileType, metadata, err := bmc.ScreenshotFromInterfaces(ctx, c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) return image, fileType, err } func (c *Client) ClearSystemEventLog(ctx context.Context) (err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "ClearSystemEventLog") + defer span.End() + metadata, err := bmc.ClearSystemEventLogFromInterfaces(ctx, c.perProviderTimeout(ctx), c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) return err } func (c *Client) MountFloppyImage(ctx context.Context, image io.Reader) (err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "MountFloppyImage") + defer span.End() + metadata, err := bmc.MountFloppyImageFromInterfaces(ctx, image, c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) return err } func (c *Client) UnmountFloppyImage(ctx context.Context) (err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "UnmountFloppyImage") + defer span.End() + metadata, err := bmc.UnmountFloppyImageFromInterfaces(ctx, c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) return err } // FirmwareInstallSteps return the order of actions required install firmware for a component. func (c *Client) FirmwareInstallSteps(ctx context.Context, component string) (actions []constants.FirmwareInstallStep, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "FirmwareInstallSteps") + defer span.End() + status, metadata, err := bmc.FirmwareInstallStepsFromInterfaces(ctx, component, c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + return status, err } // FirmwareUpload just uploads the firmware for install, it returns a task ID to verify the upload status. func (c *Client) FirmwareUpload(ctx context.Context, component string, file *os.File) (uploadVerifyTaskID string, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "FirmwareUpload") + defer span.End() + uploadVerifyTaskID, metadata, err := bmc.FirmwareUploadFromInterfaces(ctx, component, file, c.Registry.GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + return uploadVerifyTaskID, err } // FirmwareTaskStatus pass through library function to check firmware task statuses func (c *Client) FirmwareTaskStatus(ctx context.Context, kind constants.FirmwareInstallStep, component, taskID, installVersion string) (state, status string, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "FirmwareTaskStatus") + defer span.End() + state, status, metadata, err := bmc.FirmwareTaskStatusFromInterfaces(ctx, kind, component, taskID, installVersion, c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + return state, status, err } // FirmwareInstallUploaded kicks off firmware install for a firmware uploaded with FirmwareUpload. func (c *Client) FirmwareInstallUploaded(ctx context.Context, component, uploadVerifyTaskID string) (installTaskID string, err error) { + ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "FirmwareInstallUploaded") + defer span.End() + installTaskID, metadata, err := bmc.FirmwareInstallerUploadedFromInterfaces(ctx, component, uploadVerifyTaskID, c.registry().GetDriverInterfaces()) c.setMetadata(metadata) + metadata.RegisterSpanAttributes(c.Auth.Host, span) + return installTaskID, err } diff --git a/go.mod b/go.mod index 33cbe024..2403a48b 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/ghodss/yaml v1.0.0 github.com/go-logr/logr v1.3.0 github.com/go-logr/zerologr v1.2.3 - github.com/google/go-cmp v0.5.9 + github.com/google/go-cmp v0.6.0 github.com/hashicorp/go-multierror v1.1.1 github.com/jacobweinstock/iamt v0.0.0-20230502042727-d7cdbe67d9ef github.com/jacobweinstock/registrar v0.4.7 @@ -18,11 +18,13 @@ require ( github.com/rs/zerolog v1.31.0 github.com/sirupsen/logrus v1.9.3 github.com/stmcginnis/gofish v0.14.1-0.20231018151402-dddaff9168fb - github.com/stretchr/testify v1.8.0 + github.com/stretchr/testify v1.8.4 + go.opentelemetry.io/otel v1.20.0 + go.opentelemetry.io/otel/trace v1.20.0 go.uber.org/goleak v1.2.1 - golang.org/x/crypto v0.14.0 - golang.org/x/exp v0.0.0-20231006140011-7918f672742d - golang.org/x/net v0.17.0 + golang.org/x/crypto v0.15.0 + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa + golang.org/x/net v0.18.0 gopkg.in/go-playground/assert.v1 v1.2.1 ) @@ -35,7 +37,7 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/satori/go.uuid v1.2.0 // indirect - golang.org/x/sys v0.13.0 // indirect + golang.org/x/sys v0.14.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 85dcade2..c3e42e8c 100644 --- a/go.sum +++ b/go.sum @@ -23,8 +23,8 @@ github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4 github.com/go-logr/zerologr v1.2.3 h1:up5N9vcH9Xck3jJkXzgyOxozT14R47IyDODz8LM1KSs= github.com/go-logr/zerologr v1.2.3/go.mod h1:BxwGo7y5zgSHYR1BjbnHPyF/5ZjVKfKxAZANVu6E8Ho= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -63,20 +63,28 @@ github.com/stmcginnis/gofish v0.14.1-0.20231018151402-dddaff9168fb h1:+BpzUuFIEA github.com/stmcginnis/gofish v0.14.1-0.20231018151402-dddaff9168fb/go.mod h1:BLDSFTp8pDlf/xDbLZa+F7f7eW0E/CHCboggsu8CznI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +go.opentelemetry.io/otel v1.20.0 h1:vsb/ggIY+hUjD/zCAQHpzTmndPqv/ml2ArbsbfBYTAc= +go.opentelemetry.io/otel v1.20.0/go.mod h1:oUIGj3D77RwJdM6PPZImDpSZGDvkD9fhesHny69JFrs= +go.opentelemetry.io/otel/trace v1.20.0 h1:+yxVAPZPbQhbC3OfAkeIVTky6iTFpcr4SiY9om7mXSQ= +go.opentelemetry.io/otel/trace v1.20.0/go.mod h1:HJSK7F/hA5RlzpZ0zKDCHCDHm556LCDtKaAo6JmBFUU= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210608053332-aa57babbf139/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -85,7 +93,10 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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= diff --git a/option.go b/option.go index 79447e40..307024a3 100644 --- a/option.go +++ b/option.go @@ -10,6 +10,7 @@ import ( "github.com/bmc-toolbox/bmclib/v2/providers/rpc" "github.com/go-logr/logr" "github.com/jacobweinstock/registrar" + oteltrace "go.opentelemetry.io/otel/trace" ) // Option for setting optional Client values @@ -150,3 +151,13 @@ func WithRPCOpt(opt rpc.Provider) Option { args.providerConfig.rpc = opt } } + +// WithTracerProvider specifies a tracer provider to use for creating a tracer. +// If none is specified a noop tracerprovider is used. +func WithTracerProvider(provider oteltrace.TracerProvider) Option { + return func(args *Client) { + if provider != nil { + args.traceprovider = provider + } + } +}