1
0
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:
Marcus Holl 2021-03-15 16:44:18 +01:00 committed by GitHub
parent 0adca357da
commit 0186989593
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 227 additions and 0 deletions

View 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")
}

View 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
}