mirror of
https://github.com/SAP/jenkins-library.git
synced 2024-12-12 10:55:20 +02:00
transport request create solman go (#2583)
* Introduce transport request create solman * log the transport request id also in case creation failed maybe there are some rare cases where a transport request id is returned nevertheless. * report exit code always * inline parameter * Provide more parameters in log message, might help with troubleshooting Co-authored-by: Roland Stengel <r.stengel@sap.com>
This commit is contained in:
parent
0adca357da
commit
0186989593
128
pkg/transportrequest/solman/create.go
Normal file
128
pkg/transportrequest/solman/create.go
Normal file
@ -0,0 +1,128 @@
|
||||
package solman
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/SAP/jenkins-library/pkg/config/validation"
|
||||
"github.com/SAP/jenkins-library/pkg/log"
|
||||
"github.com/pkg/errors"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CreateAction Collects all the properties we need for creating a transport request
|
||||
type CreateAction struct {
|
||||
Connection Connection
|
||||
ChangeDocumentID string
|
||||
DevelopmentSystemID string
|
||||
CMOpts []string
|
||||
}
|
||||
|
||||
// Create collects everything which is needed for creating a transport request
|
||||
type Create interface {
|
||||
WithConnection(Connection)
|
||||
WithChangeDocumentID(string)
|
||||
WithDevelopmentSystemID(string)
|
||||
WithCMOpts([]string)
|
||||
Perform(command Exec) (string, error)
|
||||
}
|
||||
|
||||
// WithConnection specifies all the connection details which
|
||||
// are required in order to connect so SOLMAN
|
||||
func (a *CreateAction) WithConnection(c Connection) {
|
||||
a.Connection = c
|
||||
}
|
||||
|
||||
// WithChangeDocumentID specifies the change document for that
|
||||
// the transport request is created.
|
||||
func (a *CreateAction) WithChangeDocumentID(id string) {
|
||||
a.ChangeDocumentID = id
|
||||
}
|
||||
|
||||
// WithDevelopmentSystemID specifies the development system ID.
|
||||
func (a *CreateAction) WithDevelopmentSystemID(id string) {
|
||||
a.DevelopmentSystemID = id
|
||||
}
|
||||
|
||||
// WithCMOpts sets additional options for calling the
|
||||
// cm client tool. E.g. -D options. Useful for troubleshooting
|
||||
func (a *CreateAction) WithCMOpts(opts []string) {
|
||||
a.CMOpts = opts
|
||||
}
|
||||
|
||||
// Perform creates a new transport request
|
||||
func (a *CreateAction) Perform(command Exec) (string, error) {
|
||||
|
||||
log.Entry().Infof("Creating new transport request via '%s'.", a.Connection.Endpoint)
|
||||
|
||||
missingParameters, err := validation.FindEmptyStringsInConfigStruct(*a)
|
||||
|
||||
if err == nil {
|
||||
if len(missingParameters) != 0 {
|
||||
err = fmt.Errorf("the following parameters are not available %s", missingParameters)
|
||||
}
|
||||
}
|
||||
|
||||
var transportRequestID string
|
||||
|
||||
if err == nil {
|
||||
if len(a.CMOpts) > 0 {
|
||||
command.SetEnv([]string{fmt.Sprintf("CMCLIENT_OPTS=%s", strings.Join(a.CMOpts, " "))})
|
||||
}
|
||||
|
||||
oldStdout := command.GetStdout()
|
||||
defer func() {
|
||||
command.Stdout(oldStdout)
|
||||
}()
|
||||
|
||||
var cmClientStdout bytes.Buffer
|
||||
w := io.MultiWriter(&cmClientStdout, oldStdout)
|
||||
command.Stdout(w)
|
||||
|
||||
err = command.RunExecutable("cmclient",
|
||||
"--endpoint", a.Connection.Endpoint,
|
||||
"--user", a.Connection.User,
|
||||
"--password", a.Connection.Password,
|
||||
"--backend-type", "SOLMAN",
|
||||
"create-transport",
|
||||
"-cID", a.ChangeDocumentID,
|
||||
"-dID", a.DevelopmentSystemID,
|
||||
)
|
||||
|
||||
exitCode := command.GetExitCode()
|
||||
if exitCode != 0 {
|
||||
message := fmt.Sprintf("create transport request command returned with exit code '%d'", exitCode)
|
||||
if err != nil {
|
||||
// Using the wrapping here is to some extend an abuse, since it is not really
|
||||
// error chaining (the other error is not necessaryly a "predecessor" of this one).
|
||||
// But it is a pragmatic approach for not loosing information for trouble shooting. There
|
||||
// is no possibility to have something like suppressed errors.
|
||||
err = errors.Wrap(err, message)
|
||||
} else {
|
||||
err = errors.New(message)
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
transportRequestID = strings.TrimSpace(cmClientStdout.String())
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
log.Entry().Infof("Created transport request '%s' at '%s'. ChangeDocumentId: '%s', DevelopmentSystemId: '%s'",
|
||||
transportRequestID,
|
||||
a.Connection.Endpoint,
|
||||
a.ChangeDocumentID,
|
||||
a.DevelopmentSystemID,
|
||||
)
|
||||
} else {
|
||||
log.Entry().WithError(err).Warnf("Creating transport request '%s' at '%s' failed. ChangeDocumentId: '%s', DevelopmentSystemId: '%s'",
|
||||
transportRequestID,
|
||||
a.Connection.Endpoint,
|
||||
a.ChangeDocumentID,
|
||||
a.DevelopmentSystemID,
|
||||
)
|
||||
}
|
||||
|
||||
return transportRequestID, errors.Wrap(err, "cannot create transport request")
|
||||
}
|
99
pkg/transportrequest/solman/create_test.go
Normal file
99
pkg/transportrequest/solman/create_test.go
Normal file
@ -0,0 +1,99 @@
|
||||
package solman
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/SAP/jenkins-library/pkg/mock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSolmanCreateTransportRequest(t *testing.T) {
|
||||
|
||||
a := CreateAction{
|
||||
Connection: Connection{
|
||||
Endpoint: "https://example.org/solman",
|
||||
User: "me",
|
||||
Password: "******",
|
||||
},
|
||||
ChangeDocumentID: "123",
|
||||
DevelopmentSystemID: "XXX~EXT_SRV",
|
||||
CMOpts: []string{"-Dprop1=abc", "-Dprop2=123"},
|
||||
}
|
||||
|
||||
t.Run("straight forward", func(t *testing.T) {
|
||||
|
||||
e := getExecMock()
|
||||
e.StdoutReturn = map[string]string{"^cmclient.*": "ABCK123456"}
|
||||
|
||||
examinee := a
|
||||
transportRequestId, err := examinee.Perform(e)
|
||||
|
||||
if assert.NoError(t, err) {
|
||||
assert.Equal(t, []mock.ExecCall{mock.ExecCall{
|
||||
Exec: "cmclient",
|
||||
Params: []string{
|
||||
"--endpoint", "https://example.org/solman",
|
||||
"--user", "me",
|
||||
"--password", "******",
|
||||
"--backend-type", "SOLMAN",
|
||||
"create-transport",
|
||||
"-cID", "123",
|
||||
"-dID", "XXX~EXT_SRV",
|
||||
},
|
||||
}}, e.Calls)
|
||||
assert.Equal(t, "ABCK123456", transportRequestId)
|
||||
assert.Equal(t, []string{"CMCLIENT_OPTS=-Dprop1=abc -Dprop2=123"}, e.Env)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("fail with error", func(t *testing.T) {
|
||||
|
||||
e := getExecMock()
|
||||
e.ShouldFailOnCommand = map[string]error{"^cmclient.*": fmt.Errorf("creating transport request failed")}
|
||||
|
||||
examinee := a
|
||||
_, err := examinee.Perform(e)
|
||||
|
||||
assert.EqualError(t, err, "cannot create transport request: creating transport request failed")
|
||||
})
|
||||
|
||||
t.Run("fail via return code", func(t *testing.T) {
|
||||
|
||||
e := getExecMock()
|
||||
e.ExitCode = 42
|
||||
|
||||
examinee := a
|
||||
_, err := examinee.Perform(e)
|
||||
|
||||
assert.EqualError(t, err, "cannot create transport request: create transport request command returned with exit code '42'")
|
||||
})
|
||||
|
||||
t.Run("input missing", func(t *testing.T) {
|
||||
|
||||
e := getExecMock()
|
||||
|
||||
examinee := a
|
||||
examinee.Connection = Connection{}
|
||||
examinee.ChangeDocumentID = ""
|
||||
|
||||
_, err := examinee.Perform(e)
|
||||
|
||||
if assert.Error(t, err) {
|
||||
// I don't want to rely on the order of the parameters
|
||||
assert.Contains(t, err.Error(), "cannot create transport request: the following parameters are not available")
|
||||
assert.Contains(t, err.Error(), "Connection.Endpoint")
|
||||
assert.Contains(t, err.Error(), "Connection.User")
|
||||
assert.Contains(t, err.Error(), "Connection.Password")
|
||||
assert.Contains(t, err.Error(), "ChangeDocumentID")
|
||||
assert.Empty(t, e.Calls)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func getExecMock() *mock.ExecMockRunner {
|
||||
var out bytes.Buffer
|
||||
e := &mock.ExecMockRunner{}
|
||||
e.Stdout(&out)
|
||||
return e
|
||||
}
|
Loading…
Reference in New Issue
Block a user