Skip to content

Commit 84148f0

Browse files
committed
Add notifycommand and test
1 parent 7790f01 commit 84148f0

File tree

5 files changed

+229
-4
lines changed

5 files changed

+229
-4
lines changed

java-components/build-request-processor/pom.xml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,13 @@
108108
<artifactId>pom-manipulation-common</artifactId>
109109
</dependency>
110110

111+
<dependency>
112+
<groupId>org.projectlombok</groupId>
113+
<artifactId>lombok</artifactId>
114+
<version>1.18.34</version>
115+
<scope>provided</scope>
116+
</dependency>
117+
111118
<dependency>
112119
<groupId>io.quarkus</groupId>
113120
<artifactId>quarkus-junit5</artifactId>
@@ -129,10 +136,9 @@
129136
<scope>test</scope>
130137
</dependency>
131138
<dependency>
132-
<groupId>org.projectlombok</groupId>
133-
<artifactId>lombok</artifactId>
134-
<version>1.18.34</version>
135-
<scope>provided</scope>
139+
<groupId>org.wiremock</groupId>
140+
<artifactId>wiremock</artifactId>
141+
<scope>test</scope>
136142
</dependency>
137143
</dependencies>
138144
<build>
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package com.redhat.hacbs.container.notification;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import io.quarkus.logging.Log;
5+
import jakarta.inject.Inject;
6+
import org.eclipse.microprofile.config.inject.ConfigProperty;
7+
import org.jboss.pnc.api.dto.Request;
8+
import picocli.CommandLine;
9+
10+
import java.net.http.HttpClient;
11+
import java.net.http.HttpRequest;
12+
import java.net.http.HttpResponse;
13+
import java.time.Duration;
14+
import java.util.Optional;
15+
16+
import static org.apache.commons.lang3.StringUtils.isEmpty;
17+
18+
@CommandLine.Command(name = "notify")
19+
public class NotifyCommand implements Runnable {
20+
21+
@CommandLine.Option(names = "--build-id", required = true)
22+
String buildId;
23+
24+
@CommandLine.Option(names = "--status", required = true)
25+
String status;
26+
27+
@CommandLine.Option(names = "--context", required = true)
28+
String context;
29+
30+
@CommandLine.Option(names = "--request-timeout")
31+
int requestTimeout = 15;
32+
33+
// TODO: do we need this...
34+
@ConfigProperty(name = "access.token")
35+
Optional<String> accessToken;
36+
37+
@Inject
38+
ObjectMapper objectMapper;
39+
40+
public void run() {
41+
try {
42+
43+
if (isEmpty(context)) {
44+
Log.infof("No callback configured ; unable to notify.");
45+
return;
46+
}
47+
48+
Request callback = objectMapper.readValue(context, Request.class );
49+
50+
Log.infof("Notification for build %s with status %s and callback %s", buildId, status, callback);
51+
PipelineNotification notification = PipelineNotification.builder().buildId(buildId).status(status).completionCallback((Request) callback.getAttachment()).build();
52+
String body = objectMapper.writeValueAsString(notification);
53+
54+
HttpRequest.Builder builder = HttpRequest.newBuilder()
55+
.uri(callback.getUri())
56+
.method(callback.getMethod().name(), HttpRequest.BodyPublishers.ofString(body))
57+
.timeout(Duration.ofSeconds(requestTimeout));
58+
callback.getHeaders().forEach(h -> builder.header(h.getName(), h.getValue()));
59+
60+
61+
HttpRequest request = builder.build();
62+
// TODO: Retry? Send async? Some useful mutiny examples from quarkus in https://gist.github.com/cescoffier/e9abce907a1c3d05d70bea3dae6dc3d5
63+
HttpResponse<String> response;
64+
try (HttpClient httpClient = HttpClient.newHttpClient()) {
65+
response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
66+
}
67+
Log.infof("Response %s", response);
68+
69+
} catch (Exception e) {
70+
Log.error("Notification failed", e);
71+
throw new RuntimeException(e);
72+
}
73+
}
74+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.redhat.hacbs.container.notification;
2+
3+
import org.jboss.pnc.api.dto.Request;
4+
5+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
6+
7+
import lombok.Builder;
8+
9+
// TODO: This is a direct copy of the same class in konflux-build-driver. Both need moved to pnc-api to
10+
// avoid clashes and duplication. For instance, we can't depend upon konflux-build-driver as that
11+
// then leads to oidc client issues.
12+
@Builder(builderClassName = "Builder")
13+
@JsonIgnoreProperties(ignoreUnknown = true)
14+
public record PipelineNotification(
15+
String status,
16+
String buildId,
17+
Request completionCallback) {
18+
19+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.redhat.hacbs.container.notification;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.github.tomakehurst.wiremock.WireMockServer;
5+
import com.redhat.hacbs.container.deploy.DeployCommand;
6+
import io.quarkus.test.LogCollectingTestResource;
7+
import io.quarkus.test.common.QuarkusTestResource;
8+
import io.quarkus.test.common.ResourceArg;
9+
import io.quarkus.test.junit.QuarkusTest;
10+
import org.apache.commons.lang3.StringUtils;
11+
import org.jboss.pnc.api.constants.HttpHeaders;
12+
import org.jboss.pnc.api.dto.Request;
13+
import org.junit.jupiter.api.BeforeEach;
14+
import org.junit.jupiter.api.Test;
15+
16+
import javax.ws.rs.core.MediaType;
17+
import java.io.File;
18+
import java.io.IOException;
19+
import java.net.URI;
20+
import java.net.URISyntaxException;
21+
import java.nio.file.Files;
22+
import java.nio.file.Path;
23+
import java.nio.file.Paths;
24+
import java.util.Collections;
25+
import java.util.List;
26+
import java.util.Map;
27+
import java.util.Set;
28+
import java.util.logging.LogRecord;
29+
30+
import static org.junit.jupiter.api.Assertions.assertEquals;
31+
import static org.junit.jupiter.api.Assertions.assertNotNull;
32+
import static org.junit.jupiter.api.Assertions.assertTrue;
33+
34+
@QuarkusTest
35+
@QuarkusTestResource(value = LogCollectingTestResource.class, restrictToAnnotatedClass = true, initArgs = @ResourceArg(name = LogCollectingTestResource.LEVEL, value = "FINE"))
36+
@QuarkusTestResource(WireMockExtensions.class)
37+
public class NotificationTest {
38+
39+
private WireMockServer wireMockServer;
40+
41+
@BeforeEach
42+
public void clearLogs() {
43+
LogCollectingTestResource.current().clear();
44+
}
45+
46+
@Test
47+
public void testNoNotify() {
48+
NotifyCommand notifyCommand = new NotifyCommand();
49+
notifyCommand.status = "Succeeded";
50+
notifyCommand.buildId = "1234";
51+
notifyCommand.run();
52+
List<LogRecord> logRecords = LogCollectingTestResource.current().getRecords();
53+
assertTrue(logRecords.stream()
54+
.anyMatch(r -> LogCollectingTestResource.format(r).contains("No callback configured ; unable to notify")));
55+
}
56+
57+
@Test
58+
public void testNotify() throws IOException, URISyntaxException {
59+
60+
Request request = Request.builder()
61+
.method(Request.Method.PUT)
62+
.header(new Request.Header(HttpHeaders.CONTENT_TYPE_STRING, MediaType.APPLICATION_JSON))
63+
.attachment(null)
64+
.uri(new URI(wireMockServer.baseUrl() + "/internal/completed"))
65+
.build();
66+
67+
System.err.println("### wiremock uri: " + wireMockServer.baseUrl());
68+
// {"method":"PUT","uri":"http://localhost:8081/internal/completed","headers":[{"name":"Content-Type","value":"application/json"}],"attachment":null}
69+
70+
NotifyCommand notifyCommand = new NotifyCommand();
71+
notifyCommand.status = "Succeeded";
72+
notifyCommand.buildId = "1234";
73+
notifyCommand.objectMapper = new ObjectMapper();
74+
notifyCommand.context = notifyCommand.objectMapper.writeValueAsString(request);
75+
notifyCommand.run();
76+
77+
List<LogRecord> logRecords = LogCollectingTestResource.current().getRecords();
78+
assertTrue(logRecords.stream()
79+
.anyMatch(r -> LogCollectingTestResource.format(r).contains("Response (PUT http://localhost:8080/internal/completed) 200")));
80+
}
81+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.redhat.hacbs.container.notification;
2+
3+
import com.github.tomakehurst.wiremock.WireMockServer;
4+
import com.github.tomakehurst.wiremock.common.ConsoleNotifier;
5+
import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
6+
7+
import java.util.Map;
8+
9+
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
10+
import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson;
11+
import static com.github.tomakehurst.wiremock.client.WireMock.get;
12+
import static com.github.tomakehurst.wiremock.client.WireMock.put;
13+
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
14+
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
15+
16+
public class WireMockExtensions implements QuarkusTestResourceLifecycleManager {
17+
private WireMockServer wireMockServer;
18+
19+
@Override
20+
public Map<String, String> start() {
21+
wireMockServer = new WireMockServer(wireMockConfig().notifier(new ConsoleNotifier(true)));
22+
wireMockServer.start();
23+
24+
wireMockServer.stubFor(
25+
put(urlEqualTo("/internal/completed"))
26+
.withRequestBody(
27+
equalToJson("{\"status\":\"Succeeded\",\"buildId\":\"1234\",\"completionCallback\":null}"))
28+
.willReturn(aResponse()
29+
.withStatus(200)));
30+
31+
return Map.of("quarkus.rest-client.wiremockextensions.url", wireMockServer.baseUrl());
32+
}
33+
34+
@Override
35+
public void stop() {
36+
if (wireMockServer != null) {
37+
wireMockServer.stop();
38+
}
39+
}
40+
41+
@Override
42+
public void inject(TestInjector testInjector) {
43+
testInjector.injectIntoFields(wireMockServer, new TestInjector.MatchesType(WireMockServer.class));
44+
}
45+
}

0 commit comments

Comments
 (0)