Skip to content

Commit

Permalink
Merge branch 'release/1.64.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
jelemux committed Apr 17, 2023
2 parents a9c930f + c7124ce commit c428ecc
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 50 deletions.
10 changes: 7 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.64.1](https://github.com/cloudogu/ces-build-lib/releases/tag/1.64.0) - 2023-04-17
### Fixed
- HttpClient escapes now the credentials in curl command to support credentials with characters like `$`.

## [1.64.0](https://github.com/cloudogu/ces-build-lib/releases/tag/1.64.0) - 2023-04-11
## Added
### Added
- Add parameter to configure version for markdown link checker #100.

## [1.63.0](https://github.com/cloudogu/ces-build-lib/releases/tag/1.63.0) - 2023-02-16
## Fixed
### Fixed
- A bug with SonarCloud where an error was thrown because a private field was accessed (#99)

## [1.62.0](https://github.com/cloudogu/ces-build-lib/releases/tag/1.62.0) - 2023-01-30
## Added
### Added
- Function lintDockerfile to lint docker files #96.
- Function shellCheck to lint shell scripts #96.

Expand Down
31 changes: 19 additions & 12 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>
<repositories>
<repository>
<id>jenkins-ci-releases</id>
<url>https://repo.jenkins-ci.org/releases/</url>
</repository>
<repository>
<id>maven-central</id>
<url>https://repo1.maven.org/maven2</url>
</repository>
</repositories>


<groupId>com.cloudogu.ces</groupId>
<artifactId>ces-build-lib</artifactId>
<name>ces-build-lib</name>
<version>1.64.0</version>
<version>1.64.1</version>


<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jacoco.version>0.8.5</jacoco.version>
</properties>

<dependencies>
<dependency>
<groupId>com.cloudbees</groupId>
<artifactId>groovy-cps</artifactId>
<version>1.31</version>
</dependency>

<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
Expand All @@ -30,7 +37,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<version>4.13.2</version>
<scope>test</scope>
</dependency>

Expand All @@ -43,15 +50,15 @@

<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<artifactId>hamcrest</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.lesfurets</groupId>
<artifactId>jenkins-pipeline-unit</artifactId>
<version>1.1</version>
<version>1.17</version>
<scope>test</scope>
</dependency>

Expand Down
55 changes: 29 additions & 26 deletions src/com/cloudogu/ces/cesbuildlib/HttpClient.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.cloudogu.ces.cesbuildlib

/**
* An HTTP client that calls curl on the shell.
*
*
* Returns a map of
* * httpCode (String)
* * headers (Map)
Expand All @@ -22,7 +22,7 @@ class HttpClient implements Serializable {
Map get(String url, String contentType = '', def data = '') {
return httpRequest('GET', url, contentType, data)
}

Map put(String url, String contentType = '', def data = '') {
return httpRequest('PUT', url, contentType, data)
}
Expand All @@ -34,7 +34,7 @@ class HttpClient implements Serializable {
}
return httpRequest('PUT', url, contentType, filePath, command)
}

Map post(String url, String contentType = '', def data = '') {
return httpRequest('POST', url, contentType, data)
}
Expand All @@ -50,65 +50,68 @@ class HttpClient implements Serializable {
}
}

private static String escapeSingleQuotes(String toEscape) {
return toEscape.replaceAll("'", "'\"'\"'")
}

protected String getCurlAuthParam() {
"-u ${script.env.CURL_USER}:${script.env.CURL_PASSWORD}"
"-u '" + escapeSingleQuotes(script.env.CURL_USER) + ":" + escapeSingleQuotes(script.env.CURL_PASSWORD) + "' "
}

private String getCurlCommand(String httpMethod, String url, String contentType, String data) {
return "curl -i -X ${httpMethod} " +
(credentials ? "${getCurlAuthParam()} " : '') +
(contentType ? "-H 'Content-Type: ${contentType}' " : '') +
(data ? "-d '" + data + "' " : '') +
"${url}"
return "curl -i -X '" + escapeSingleQuotes(httpMethod) + "' " +
(credentials ? getCurlAuthParam() : '') +
(contentType ? "-H 'Content-Type: " + escapeSingleQuotes(contentType) + "' " : '') +
(data ? "-d '" + escapeSingleQuotes(data) + "' " : '') +
"'" + escapeSingleQuotes(url) + "'"
}

private String getUploadFileCurlCommand(String httpMethod, String url, String contentType, String filePath) {
return "curl -i -X ${httpMethod} " +
(credentials ? "${getCurlAuthParam()} " : '') +
(contentType ? "-H 'Content-Type: ${contentType}' " : '') +
(filePath ? "-T '" + filePath + "' " : '') +
"${url}"
return "curl -i -X '" + escapeSingleQuotes(httpMethod) + "' " +
(credentials ? getCurlAuthParam() : '') +
(contentType ? "-H 'Content-Type: " + escapeSingleQuotes(contentType) + "' " : '') +
(filePath ? "-T '" + escapeSingleQuotes(filePath) + "' " : '') +
"'" + escapeSingleQuotes(url) + "'"
}

protected Map httpRequest(String httpMethod, String url, String contentType, def data, String customCommand = '') {
String httpResponse
def rawHeaders
def body

executeWithCredentials {
String curlCommand
if (customCommand.isEmpty()) {
String dataStr = data.toString().replaceAll("'", "'\"'\"'")
curlCommand = getCurlCommand(httpMethod, url, contentType, dataStr)
curlCommand = getCurlCommand(httpMethod, url, contentType, data.toString())
} else {
curlCommand = customCommand
}

// Command must be run inside this closure, otherwise the credentials will not be masked (using '*') in the console
httpResponse = sh.returnStdOut curlCommand
}

String[] responseLines = httpResponse.split("\n")

// e.g. HTTP/2 301
String httpCode = responseLines[0].split(" ")[1]
String httpCode = responseLines[0].split(" ")[1]
def separatingLine = responseLines.findIndexOf { it.trim().isEmpty() }

if (separatingLine > 0) {
rawHeaders = responseLines[1..(separatingLine -1)]
body = responseLines[separatingLine+1..-1].join('\n')
rawHeaders = responseLines[1..(separatingLine - 1)]
body = responseLines[separatingLine + 1..-1].join('\n')
} else {
// No body returned
rawHeaders = responseLines[1..-1]
body = ''
}

def headers = [:]
for(String line: rawHeaders) {
for (String line : rawHeaders) {
// e.g. cache-control: no-cache
def splitLine = line.split(':', 2)
headers[splitLine[0].trim()] = splitLine[1].trim()
}
return [ httpCode: httpCode, headers: headers, body: body]
return [httpCode: httpCode, headers: headers, body: body]
}
}
3 changes: 0 additions & 3 deletions src/com/cloudogu/ces/cesbuildlib/K3d.groovy
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.cloudogu.ces.cesbuildlib

import com.cloudbees.groovy.cps.NonCPS

class K3d {
/**
* The image of the k3s version defining the targeted k8s version
Expand Down Expand Up @@ -70,7 +68,6 @@ class K3d {
*
* @return new randomized cluster name
*/
@NonCPS
static String createClusterName() {
String[] randomUUIDs = UUID.randomUUID().toString().split("-")
String uuid_snippet = randomUUIDs[randomUUIDs.length - 1]
Expand Down
2 changes: 1 addition & 1 deletion test/com/cloudogu/ces/cesbuildlib/DockerMock.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class DockerMock {
when(imageMock.mountJenkinsUser(anyBoolean())).thenReturn(imageMock)
when(imageMock.mountDockerSocket()).thenReturn(imageMock)
when(imageMock.mountDockerSocket(anyBoolean())).thenReturn(imageMock)
when(imageMock.inside(anyString(), any())).thenAnswer(new Answer<Object>() {
when(imageMock.inside(any(), any())).thenAnswer(new Answer<Object>() {
@Override
Object answer(InvocationOnMock invocation) throws Throwable {
Closure closure = invocation.getArgument(1)
Expand Down
52 changes: 47 additions & 5 deletions test/com/cloudogu/ces/cesbuildlib/HttpClientTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class HttpClientTest {
assertThat(actualResponse.httpCode).isEqualTo('201')
assertThat(actualResponse.headers['location']).isEqualTo('https://some:/url')
assertThat(actualResponse.body).isEqualTo('')
assertThat(scriptMock.actualShMapArgs[0]).isEqualTo('curl -i -X GET http://url')
assertThat(scriptMock.actualShMapArgs[0]).isEqualTo("curl -i -X 'GET' 'http://url'")
}

@Test
Expand All @@ -43,7 +43,7 @@ class HttpClientTest {
assertThat(actualResponse.body).isEqualTo('')

assertThat(scriptMock.actualShMapArgs[0])
.isEqualTo('curl -i -X POST -H \'Content-Type: input\' -d \'{"title":"t","description":"d"}\' http://some-url' )
.isEqualTo("curl -i -X 'POST' -H 'Content-Type: input' -d '{\"title\":\"t\",\"description\":\"d\"}' 'http://some-url'" )
}

@Test
Expand All @@ -60,7 +60,7 @@ class HttpClientTest {
assertThat(actualResponse.body).isEqualTo('')

assertThat(scriptMock.actualShMapArgs[0])
.isEqualTo('curl -i -X PUT -H \'Content-Type: input\' -T \'/path/to/file\' http://some-url' )
.isEqualTo("curl -i -X 'PUT' -H 'Content-Type: input' -T '/path/to/file' 'http://some-url'")
}

@Test
Expand All @@ -82,7 +82,7 @@ class HttpClientTest {
assertThat(actualResponse.body).isEqualTo(expectedBody1 + expectedBody2)

assertThat(scriptMock.actualShMapArgs[0])
.isEqualTo('curl -i -X POST http://some-url' )
.isEqualTo("curl -i -X 'POST' 'http://some-url'")
}

@Test
Expand All @@ -97,7 +97,49 @@ class HttpClientTest {

http.get('http://url')

assertThat(scriptMock.actualShMapArgs[0]).isEqualTo('curl -i -X GET -u user:pw http://url')
assertThat(scriptMock.actualShMapArgs[0]).isEqualTo("curl -i -X 'GET' -u 'user:pw' 'http://url'")
}

@Test
void "put request with single quotes"() {
http = new HttpClient(scriptMock, "credentialsID")
def expectedResponse = 'HTTP/1.1 203\n' +
'cache-control: no-cache\n' +
'content-type: output'
scriptMock.env.put("CURL_USER", "us'er")
scriptMock.env.put("CURL_PASSWORD", "p'w")
scriptMock.expectedDefaultShRetValue = expectedResponse

def actualResponse = http.putFile('http://so\'me-url', 'in\'put', "/path/t\'o/file")

assertThat(actualResponse.httpCode).isEqualTo('203')
assertThat(actualResponse.headers['content-type']).isEqualTo('output')
assertThat(actualResponse.body).isEqualTo('')

assertThat(scriptMock.actualShMapArgs[0])
.isEqualTo("curl -i -X 'PUT' -u 'us'\"'\"'er:p'\"'\"'w' -H 'Content-Type: in'\"'\"'put' -T '/path/t'\"'\"'o/file' 'http://so'\"'\"'me-url'" )
}

@Test
void "post request with single quotes"() {
def expectedResponse = 'HTTP/1.1 203\n' +
'cache-control: no-cache\n' +
'content-type: output'
scriptMock.expectedDefaultShRetValue = expectedResponse

def dataJson = JsonOutput.toJson([
title : 't',
description: 'd\'d'
])

def actualResponse = http.post('http://some-url', 'input', dataJson)

assertThat(actualResponse.httpCode).isEqualTo('203')
assertThat(actualResponse.headers['content-type']).isEqualTo('output')
assertThat(actualResponse.body).isEqualTo('')

assertThat(scriptMock.actualShMapArgs[0])
.isEqualTo("curl -i -X 'POST' -H 'Content-Type: input' -d '{\"title\":\"t\",\"description\":\"d'\"'\"'d\"}' 'http://some-url'" )
}
}

0 comments on commit c428ecc

Please sign in to comment.