github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/pkg/transportrequest/cts/upload.go (about)

     1  package cts
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/SAP/jenkins-library/pkg/command"
     6  	"github.com/SAP/jenkins-library/pkg/log"
     7  	"github.com/SAP/jenkins-library/pkg/piperutils"
     8  	"strings"
     9  )
    10  
    11  type fileUtils interface {
    12  	FileExists(string) (bool, error)
    13  }
    14  
    15  var files fileUtils = piperutils.Files{}
    16  
    17  // Connection Everything wee need for connecting to CTS
    18  type Connection struct {
    19  	// The endpoint in for form <protocol>://<host>:<port>, no path
    20  	Endpoint string
    21  	// The ABAP client, like e.g. "001"
    22  	Client   string
    23  	User     string
    24  	Password string
    25  }
    26  
    27  // Application The details of the application
    28  type Application struct {
    29  	// Name of the application
    30  	Name string
    31  	// The ABAP package
    32  	Pack string
    33  	// A description. Only taken into account for initial upload, not
    34  	// in case of a re-deployment.
    35  	Desc string
    36  }
    37  
    38  // Node The details for configuring the node image
    39  type Node struct {
    40  	// The dependencies which are installed on a basic node image in order
    41  	// to enable it for fiori deployment. If left empty we assume the
    42  	// provided base image has already everything installed.
    43  	DeployDependencies []string
    44  	// Additional options for the npm install command. Useful e.g.
    45  	// for providing additional registries or for triggering verbose mode
    46  	InstallOpts []string
    47  }
    48  
    49  // UploadAction Collects all the properties we need for the deployment
    50  type UploadAction struct {
    51  	Connection         Connection
    52  	Application        Application
    53  	Node               Node
    54  	TransportRequestID string
    55  	ConfigFile         string
    56  	DeployUser         string
    57  }
    58  
    59  const (
    60  	abapUserKey           = "ABAP_USER"
    61  	abapPasswordKey       = "ABAP_PASSWORD"
    62  	defaultConfigFileName = "ui5-deploy.yaml"
    63  )
    64  
    65  // WithConnection ...
    66  func (action *UploadAction) WithConnection(connection Connection) {
    67  	action.Connection = connection
    68  }
    69  
    70  // WithApplication ...
    71  func (action *UploadAction) WithApplication(app Application) {
    72  	action.Application = app
    73  }
    74  
    75  // WithNodeProperties ...
    76  func (action *UploadAction) WithNodeProperties(node Node) {
    77  	action.Node = node
    78  }
    79  
    80  // WithTransportRequestID ...
    81  func (action *UploadAction) WithTransportRequestID(id string) {
    82  	action.TransportRequestID = id
    83  }
    84  
    85  // WithConfigFile ...
    86  func (action *UploadAction) WithConfigFile(configFile string) {
    87  	action.ConfigFile = configFile
    88  }
    89  
    90  // WithDeployUser ...
    91  func (action *UploadAction) WithDeployUser(deployUser string) {
    92  	action.DeployUser = deployUser
    93  }
    94  
    95  // Perform Performs the upload
    96  func (action *UploadAction) Perform(command command.ShellRunner) error {
    97  
    98  	command.AppendEnv(
    99  		[]string{
   100  			fmt.Sprintf("%s=%s", abapUserKey, action.Connection.User),
   101  			fmt.Sprintf("%s=%s", abapPasswordKey, action.Connection.Password),
   102  		})
   103  
   104  	cmd := []string{"#!/bin/sh -e"}
   105  
   106  	noInstall := len(action.Node.DeployDependencies) == 0
   107  	if !noInstall {
   108  		cmd = append(cmd, "echo \"Current user is '$(whoami)'\"")
   109  		cmd = append(cmd, getPrepareFioriEnvironmentStatement(action.Node.DeployDependencies, action.Node.InstallOpts))
   110  		cmd = append(cmd, getSwitchUserStatement(action.DeployUser))
   111  	} else {
   112  		log.Entry().Info("No deploy dependencies provided. Skipping npm install call. Assuming current docker image already contains the dependencies for performing the deployment.")
   113  	}
   114  
   115  	deployStatement, err := getFioriDeployStatement(action.TransportRequestID, action.ConfigFile, action.Application, action.Connection)
   116  	if err != nil {
   117  		return err
   118  	}
   119  
   120  	cmd = append(cmd, deployStatement)
   121  
   122  	return command.RunShell("/bin/sh", strings.Join(cmd, "\n"))
   123  }
   124  
   125  func getPrepareFioriEnvironmentStatement(deps []string, npmInstallOpts []string) string {
   126  	cmd := []string{
   127  		"npm",
   128  		"install",
   129  		"--global",
   130  	}
   131  	cmd = append(cmd, npmInstallOpts...)
   132  	cmd = append(cmd, deps...)
   133  	return strings.Join(cmd, " ")
   134  }
   135  
   136  func getFioriDeployStatement(
   137  	transportRequestID string,
   138  	configFile string,
   139  	app Application,
   140  	cts Connection,
   141  ) (string, error) {
   142  	desc := app.Desc
   143  	if len(desc) == 0 {
   144  		desc = "Deployed with Piper based on SAP Fiori tools"
   145  	}
   146  
   147  	useConfigFileOptionInCommandInvocation, useNoConfigFileOptionInCommandInvocation, err := handleConfigFileOptions(configFile)
   148  	if err != nil {
   149  		return "", err
   150  	}
   151  	cmd := []string{
   152  		"fiori",
   153  		"deploy",
   154  		"--failfast", // provide return code != 0 in case of any failure
   155  		"--yes",      // autoconfirm --> no need to press 'y' key in order to confirm the params and trigger the deployment
   156  		"--username", abapUserKey,
   157  		"--password", abapPasswordKey,
   158  		"--description", fmt.Sprintf("\"%s\"", desc),
   159  	}
   160  
   161  	if useNoConfigFileOptionInCommandInvocation {
   162  		cmd = append(cmd, "--noConfig") // no config file, but we will provide our parameters
   163  	}
   164  	if useConfigFileOptionInCommandInvocation {
   165  		cmd = append(cmd, "--config", fmt.Sprintf("\"%s\"", configFile))
   166  	}
   167  	if len(cts.Endpoint) > 0 {
   168  		log.Entry().Debugf("Endpoint '%s' used from piper config", cts.Endpoint)
   169  		cmd = append(cmd, "--url", cts.Endpoint)
   170  	} else {
   171  		log.Entry().Debug("No endpoint found in piper config.")
   172  	}
   173  	if len(cts.Client) > 0 {
   174  		log.Entry().Debugf("Client '%s' used from piper config", cts.Client)
   175  		cmd = append(cmd, "--client", cts.Client)
   176  	} else {
   177  		log.Entry().Debug("No client found in piper config.")
   178  	}
   179  	if len(transportRequestID) > 0 {
   180  		log.Entry().Debugf("TransportRequestID '%s' used from piper config", transportRequestID)
   181  		cmd = append(cmd, "--transport", transportRequestID)
   182  	} else {
   183  		log.Entry().Debug("No transportRequestID found in piper config.")
   184  	}
   185  	if len(app.Pack) > 0 {
   186  		log.Entry().Debugf("application package '%s' used from piper config", app.Pack)
   187  		cmd = append(cmd, "--package", app.Pack)
   188  	} else {
   189  		log.Entry().Debug("No application package found in piper config.")
   190  	}
   191  	if len(app.Name) > 0 {
   192  		log.Entry().Debugf("application name '%s' used from piper config", app.Name)
   193  		cmd = append(cmd, "--name", app.Name)
   194  	} else {
   195  		log.Entry().Debug("No application name found in piper config.")
   196  	}
   197  
   198  	return strings.Join(cmd, " "), nil
   199  }
   200  
   201  func getSwitchUserStatement(user string) string {
   202  	return fmt.Sprintf("su %s", user)
   203  }
   204  
   205  func handleConfigFileOptions(path string) (useConfigFileOptionInCommandInvocation, useNoConfigFileOptionInCommandInvoction bool, err error) {
   206  
   207  	exists := false
   208  	if len(path) == 0 {
   209  		exists, err = files.FileExists(defaultConfigFileName)
   210  		if err != nil {
   211  			return
   212  		}
   213  		useConfigFileOptionInCommandInvocation = false
   214  		useNoConfigFileOptionInCommandInvoction = !exists
   215  		return
   216  	}
   217  	exists, err = files.FileExists(path)
   218  	if err != nil {
   219  		return
   220  	}
   221  	if exists {
   222  		useConfigFileOptionInCommandInvocation = true
   223  		useNoConfigFileOptionInCommandInvoction = false
   224  	} else {
   225  		if path != defaultConfigFileName {
   226  			err = fmt.Errorf("Configured deploy config file '%s' does not exists", path)
   227  			return
   228  		}
   229  		// in this case this is most likely provided by the piper default config and
   230  		// it was not explicitly configured. Hence we assume not having a config file
   231  		useConfigFileOptionInCommandInvocation = false
   232  		useNoConfigFileOptionInCommandInvoction = true
   233  	}
   234  	return
   235  }