Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3/4] redfish/GetBiosConfiguration: move tests and fixtures #371

Merged
merged 12 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 96 additions & 21 deletions bmc/firmware.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type firmwareInstallerProvider struct {

// firmwareInstall uploads and initiates firmware update for the component
func firmwareInstall(ctx context.Context, component, operationApplyTime string, forceInstall bool, reader io.Reader, generic []firmwareInstallerProvider) (taskID string, metadata Metadata, err error) {
var metadataLocal Metadata
metadata = newMetadata()

for _, elem := range generic {
if elem.FirmwareInstaller == nil {
Expand All @@ -49,20 +49,20 @@ func firmwareInstall(ctx context.Context, component, operationApplyTime string,

return taskID, metadata, err
default:
metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name)
metadata.ProvidersAttempted = append(metadata.ProvidersAttempted, elem.name)
taskID, vErr := elem.FirmwareInstall(ctx, component, operationApplyTime, forceInstall, reader)
if vErr != nil {
err = multierror.Append(err, errors.WithMessagef(vErr, "provider: %v", elem.name))
metadata.FailedProviderDetail[elem.name] = err.Error()
continue

}
metadataLocal.SuccessfulProvider = elem.name
return taskID, metadataLocal, nil
metadata.SuccessfulProvider = elem.name
return taskID, metadata, nil
}
}

return taskID, metadataLocal, multierror.Append(err, errors.New("failure in FirmwareInstall"))
return taskID, metadata, multierror.Append(err, errors.New("failure in FirmwareInstall"))
}

// FirmwareInstallFromInterfaces identifies implementations of the FirmwareInstaller interface and passes the found implementations to the firmwareInstall() wrapper
Expand Down Expand Up @@ -118,7 +118,7 @@ type firmwareInstallVerifierProvider struct {

// firmwareInstallStatus returns the status of the firmware install process
func firmwareInstallStatus(ctx context.Context, installVersion, component, taskID string, generic []firmwareInstallVerifierProvider) (status string, metadata Metadata, err error) {
var metadataLocal Metadata
metadata = newMetadata()

for _, elem := range generic {
if elem.FirmwareInstallVerifier == nil {
Expand All @@ -130,20 +130,20 @@ func firmwareInstallStatus(ctx context.Context, installVersion, component, taskI

return status, metadata, err
default:
metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name)
metadata.ProvidersAttempted = append(metadata.ProvidersAttempted, elem.name)
status, vErr := elem.FirmwareInstallStatus(ctx, installVersion, component, taskID)
if vErr != nil {
err = multierror.Append(err, errors.WithMessagef(vErr, "provider: %v", elem.name))
metadata.FailedProviderDetail[elem.name] = err.Error()
continue

}
metadataLocal.SuccessfulProvider = elem.name
return status, metadataLocal, nil
metadata.SuccessfulProvider = elem.name
return status, metadata, nil
}
}

return status, metadataLocal, multierror.Append(err, errors.New("failure in FirmwareInstallStatus"))
return status, metadata, multierror.Append(err, errors.New("failure in FirmwareInstallStatus"))
}

// FirmwareInstallStatusFromInterfaces identifies implementations of the FirmwareInstallVerifier interface and passes the found implementations to the firmwareInstallStatus() wrapper.
Expand Down Expand Up @@ -175,7 +175,82 @@ func FirmwareInstallStatusFromInterfaces(ctx context.Context, installVersion, co
return firmwareInstallStatus(ctx, installVersion, component, taskID, implementations)
}

// FirmwareInstallerWithOpts defines an interface to install firmware that was previously uploaded with FirmwareUpload
// FirmwareInstallProvider defines an interface to upload and initiate a firmware install in the same implementation method
//
// Its intended to deprecate the FirmwareInstall interface
type FirmwareInstallProvider interface {
// FirmwareInstallUploadAndInitiate uploads _and_ initiates the firmware install process.
//
// return values:
// taskID - A taskID is returned if the update process on the BMC returns an identifier for the update process.
FirmwareInstallUploadAndInitiate(ctx context.Context, component string, file *os.File) (taskID string, err error)
}

// firmwareInstallProvider is an internal struct to correlate an implementation/provider and its name
type firmwareInstallProvider struct {
name string
FirmwareInstallProvider
}

// firmwareInstall uploads and initiates firmware update for the component
func firmwareInstallUploadAndInitiate(ctx context.Context, component string, file *os.File, generic []firmwareInstallProvider) (taskID string, metadata Metadata, err error) {
metadata = newMetadata()

for _, elem := range generic {
if elem.FirmwareInstallProvider == nil {
continue
}
select {
case <-ctx.Done():
err = multierror.Append(err, ctx.Err())

return taskID, metadata, err
default:
metadata.ProvidersAttempted = append(metadata.ProvidersAttempted, elem.name)
taskID, vErr := elem.FirmwareInstallUploadAndInitiate(ctx, component, file)
if vErr != nil {
err = multierror.Append(err, errors.WithMessagef(vErr, "provider: %v", elem.name))
metadata.FailedProviderDetail[elem.name] = err.Error()
continue
}
metadata.SuccessfulProvider = elem.name
return taskID, metadata, nil
}
}

return taskID, metadata, multierror.Append(err, errors.New("failure in FirmwareInstallUploadAndInitiate"))
}

// FirmwareInstallUploadAndInitiateFromInterfaces identifies implementations of the FirmwareInstallProvider interface and passes the found implementations to the firmwareInstallUploadAndInitiate() wrapper
func FirmwareInstallUploadAndInitiateFromInterfaces(ctx context.Context, component string, file *os.File, generic []interface{}) (taskID string, metadata Metadata, err error) {
metadata = newMetadata()

implementations := make([]firmwareInstallProvider, 0)
for _, elem := range generic {
temp := firmwareInstallProvider{name: getProviderName(elem)}
switch p := elem.(type) {
case FirmwareInstallProvider:
temp.FirmwareInstallProvider = p
implementations = append(implementations, temp)
default:
e := fmt.Sprintf("not a FirmwareInstallProvider implementation: %T", p)
err = multierror.Append(err, errors.New(e))
}
}
if len(implementations) == 0 {
return taskID, metadata, multierror.Append(
err,
errors.Wrap(
bmclibErrs.ErrProviderImplementation,
("no FirmwareInstallProvider implementations found"),
),
)
}

return firmwareInstallUploadAndInitiate(ctx, component, file, implementations)
}

// FirmwareInstallerUploaded defines an interface to install firmware that was previously uploaded with FirmwareUpload
type FirmwareInstallerUploaded interface {
// FirmwareInstallUploaded uploads firmware update payload to the BMC returning the firmware install task ID
//
Expand All @@ -196,7 +271,7 @@ type firmwareInstallerWithOptionsProvider struct {

// firmwareInstallUploaded uploads and initiates firmware update for the component
func firmwareInstallUploaded(ctx context.Context, component, uploadTaskID string, generic []firmwareInstallerWithOptionsProvider) (installTaskID string, metadata Metadata, err error) {
var metadataLocal Metadata
metadata = newMetadata()

for _, elem := range generic {
if elem.FirmwareInstallerUploaded == nil {
Expand All @@ -208,7 +283,7 @@ func firmwareInstallUploaded(ctx context.Context, component, uploadTaskID string

return installTaskID, metadata, err
default:
metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name)
metadata.ProvidersAttempted = append(metadata.ProvidersAttempted, elem.name)
var vErr error
installTaskID, vErr = elem.FirmwareInstallUploaded(ctx, component, uploadTaskID)
if vErr != nil {
Expand All @@ -217,12 +292,12 @@ func firmwareInstallUploaded(ctx context.Context, component, uploadTaskID string
continue

}
metadataLocal.SuccessfulProvider = elem.name
return installTaskID, metadataLocal, nil
metadata.SuccessfulProvider = elem.name
return installTaskID, metadata, nil
}
}

return installTaskID, metadataLocal, multierror.Append(err, errors.New("failure in FirmwareInstallUploaded"))
return installTaskID, metadata, multierror.Append(err, errors.New("failure in FirmwareInstallUploaded"))
}

// FirmwareInstallerUploadedFromInterfaces identifies implementations of the FirmwareInstallUploaded interface and passes the found implementations to the firmwareInstallUploaded() wrapper
Expand Down Expand Up @@ -294,7 +369,7 @@ func FirmwareInstallStepsFromInterfaces(ctx context.Context, component string, g
}

func firmwareInstallSteps(ctx context.Context, component string, generic []firmwareInstallStepsGetterProvider) (steps []constants.FirmwareInstallStep, metadata Metadata, err error) {
var metadataLocal Metadata
metadata = newMetadata()

for _, elem := range generic {
if elem.FirmwareInstallStepsGetter == nil {
Expand All @@ -306,20 +381,20 @@ func firmwareInstallSteps(ctx context.Context, component string, generic []firmw

return steps, metadata, err
default:
metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name)
metadata.ProvidersAttempted = append(metadata.ProvidersAttempted, elem.name)
steps, vErr := elem.FirmwareInstallSteps(ctx, component)
if vErr != nil {
err = multierror.Append(err, errors.WithMessagef(vErr, "provider: %v", elem.name))
metadata.FailedProviderDetail[elem.name] = err.Error()
continue

}
metadataLocal.SuccessfulProvider = elem.name
return steps, metadataLocal, nil
metadata.SuccessfulProvider = elem.name
return steps, metadata, nil
}
}

return steps, metadataLocal, multierror.Append(err, errors.New("failure in FirmwareInstallSteps"))
return steps, metadata, multierror.Append(err, errors.New("failure in FirmwareInstallSteps"))
}

type FirmwareUploader interface {
Expand Down
94 changes: 94 additions & 0 deletions bmc/firmware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/bmc-toolbox/bmclib/v2/constants"
bmclibErrs "github.com/bmc-toolbox/bmclib/v2/errors"
"github.com/bmc-toolbox/common"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -204,6 +205,99 @@ func TestFirmwareInstallStatusFromInterfaces(t *testing.T) {
}
}

type firmwareInstallUploadAndInitiateTester struct {
returnTaskID string
returnError error
}

func (f *firmwareInstallUploadAndInitiateTester) FirmwareInstallUploadAndInitiate(ctx context.Context, component string, file *os.File) (taskID string, err error) {
return f.returnTaskID, f.returnError
}

func (r *firmwareInstallUploadAndInitiateTester) Name() string {
return "foo"
}

func TestFirmwareInstallUploadAndInitiate(t *testing.T) {
testCases := []struct {
testName string
component string
file *os.File
returnTaskID string
returnError error
ctxTimeout time.Duration
providerName string
providersAttempted int
}{
{"success with metadata", "componentA", &os.File{}, "1234", nil, 5 * time.Second, "foo", 1},
{"failure with metadata", "componentB", &os.File{}, "1234", errors.New("failed to upload and initiate"), 5 * time.Second, "foo", 1},
{"failure with context timeout", "componentC", &os.File{}, "", context.DeadlineExceeded, 1 * time.Nanosecond, "foo", 1},
}

for _, tc := range testCases {
t.Run(tc.testName, func(t *testing.T) {
testImplementation := &firmwareInstallUploadAndInitiateTester{returnTaskID: tc.returnTaskID, returnError: tc.returnError}
if tc.ctxTimeout == 0 {
tc.ctxTimeout = time.Second * 3
}
ctx, cancel := context.WithTimeout(context.Background(), tc.ctxTimeout)
defer cancel()
taskID, metadata, err := firmwareInstallUploadAndInitiate(ctx, tc.component, tc.file, []firmwareInstallProvider{{tc.providerName, testImplementation}})
if tc.returnError != nil {
assert.ErrorIs(t, err, tc.returnError)
return
}

if err != nil {
t.Fatal(err)
}
assert.Equal(t, tc.returnTaskID, taskID)
assert.Equal(t, tc.providerName, metadata.SuccessfulProvider)
assert.Equal(t, tc.providersAttempted, len(metadata.ProvidersAttempted))
})
}
}

func TestFirmwareInstallUploadAndInitiateFromInterfaces(t *testing.T) {
testCases := []struct {
testName string
component string
file *os.File
returnTaskID string
returnError error
providerName string
badImplementation bool
}{
{"success with metadata", "componentA", &os.File{}, "1234", nil, "foo", false},
{"failure with bad implementation", "componentB", &os.File{}, "1234", bmclibErrs.ErrProviderImplementation, "foo", true},
}

for _, tc := range testCases {
t.Run(tc.testName, func(t *testing.T) {
var generic []interface{}
if tc.badImplementation {
badImplementation := struct{}{}
generic = []interface{}{&badImplementation}
} else {
testImplementation := &firmwareInstallUploadAndInitiateTester{returnTaskID: tc.returnTaskID, returnError: tc.returnError}
generic = []interface{}{testImplementation}
}
taskID, metadata, err := FirmwareInstallUploadAndInitiateFromInterfaces(context.Background(), tc.component, tc.file, generic)
if tc.returnError != nil {
assert.ErrorIs(t, err, tc.returnError)
return
}

if err != nil {
t.Fatal(err)
}

assert.Equal(t, tc.returnTaskID, taskID)
assert.Equal(t, tc.providerName, metadata.SuccessfulProvider)
})
}
}

type firmwareInstallUploadTester struct {
TaskID string
Err error
Expand Down
11 changes: 11 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -656,3 +656,14 @@ func (c *Client) FirmwareInstallUploaded(ctx context.Context, component, uploadV

return installTaskID, err
}

func (c *Client) FirmwareInstallUploadAndInitiate(ctx context.Context, component string, file *os.File) (taskID string, err error) {
ctx, span := c.traceprovider.Tracer(pkgName).Start(ctx, "FirmwareInstallUploadAndInitiate")
defer span.End()

taskID, metadata, err := bmc.FirmwareInstallUploadAndInitiateFromInterfaces(ctx, component, file, c.registry().GetDriverInterfaces())
c.setMetadata(metadata)
metadata.RegisterSpanAttributes(c.Auth.Host, span)

return taskID, err
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ require (
github.com/pkg/errors v0.9.1
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/stmcginnis/gofish v0.15.1-0.20231121142100-22a60a77be91
github.com/stretchr/testify v1.8.4
go.opentelemetry.io/otel v1.20.0
go.opentelemetry.io/otel/trace v1.20.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stmcginnis/gofish v0.14.1-0.20231018151402-dddaff9168fb h1:+BpzUuFIEAs71bTshedsUHAAq21VZWvuokbN9ABEQeQ=
github.com/stmcginnis/gofish v0.14.1-0.20231018151402-dddaff9168fb/go.mod h1:BLDSFTp8pDlf/xDbLZa+F7f7eW0E/CHCboggsu8CznI=
github.com/stmcginnis/gofish v0.15.1-0.20231121142100-22a60a77be91 h1:WmABtU8y6kTgzoVUn3FWCQGAfyodve3uz3xno28BrRs=
github.com/stmcginnis/gofish v0.15.1-0.20231121142100-22a60a77be91/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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
Expand Down
Loading