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 }