diff --git a/java-components/cli/src/main/java/com/redhat/hacbs/cli/driver/Pipeline.java b/java-components/cli/src/main/java/com/redhat/hacbs/cli/driver/Pipeline.java index a9d9bb854..877bc93e2 100644 --- a/java-components/cli/src/main/java/com/redhat/hacbs/cli/driver/Pipeline.java +++ b/java-components/cli/src/main/java/com/redhat/hacbs/cli/driver/Pipeline.java @@ -8,6 +8,7 @@ import com.redhat.hacbs.driver.Driver; import com.redhat.hacbs.driver.dto.BuildRequest; +import com.redhat.hacbs.driver.dto.BuildResponse; import picocli.CommandLine; @@ -19,7 +20,7 @@ public class Pipeline extends Base implements Runnable { @Inject Driver driver; - @CommandLine.Option(names = "--quay", description = "Quay repo", defaultValue = "quay.io/redhat-user-workloads-stage/pnc-devel-tenant/pnc") + @CommandLine.Option(names = "--quay", description = "Quay repo", defaultValue = "quay.io/redhat-user-workloads-stage/pnc-devel-tenant/pnc-konflux") String quayRepo; @CommandLine.Option(names = "--processor", description = "Request Process Image", defaultValue = "quay.io/redhat-user-workloads/konflux-jbs-pnc-tenant/jvm-build-service/build-request-processor:latest") @@ -47,6 +48,8 @@ public void run() { // Just use default from buildah-oci-ta for now. .podMemoryOverride("4Gi") .build(); - driver.create(request); + BuildResponse b = driver.create(request); + + logger.info("Got response {}", b); } } diff --git a/java-components/driver/pom.xml b/java-components/driver/pom.xml index 0bb3ca50b..0e03db8d2 100644 --- a/java-components/driver/pom.xml +++ b/java-components/driver/pom.xml @@ -1,6 +1,5 @@ - + 4.0.0 io.github.redhat-appstudio.jvmbuild @@ -45,6 +44,11 @@ commons-text ${version.commons-text} + + org.jboss.pnc + pnc-api + 3.0.0 + @@ -58,6 +62,30 @@ ${version.lombok} provided + + io.quarkus + quarkus-info + + + io.quarkus + quarkus-junit5 + test + + + io.quarkus + quarkus-junit5-mockito + test + + + io.rest-assured + rest-assured + test + + + io.quarkus + quarkus-test-kubernetes-client + test + @@ -86,6 +114,17 @@ + + org.apache.maven.plugins + maven-jar-plugin + + + + ${maven.build.timestamp} + + + + @@ -109,11 +148,8 @@ - - ${project.build.directory}/${project.build.finalName}-runner - - org.jboss.logmanager.LogManager - + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager ${maven.home} diff --git a/java-components/driver/src/main/java/com/redhat/hacbs/driver/Driver.java b/java-components/driver/src/main/java/com/redhat/hacbs/driver/Driver.java index 788a20272..c838682eb 100644 --- a/java-components/driver/src/main/java/com/redhat/hacbs/driver/Driver.java +++ b/java-components/driver/src/main/java/com/redhat/hacbs/driver/Driver.java @@ -4,7 +4,9 @@ import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -22,7 +24,10 @@ import com.redhat.hacbs.driver.clients.IndyTokenRequestDTO; import com.redhat.hacbs.driver.clients.IndyTokenResponseDTO; import com.redhat.hacbs.driver.dto.BuildRequest; +import com.redhat.hacbs.driver.dto.BuildResponse; +import com.redhat.hacbs.driver.dto.CancelRequest; +import io.fabric8.knative.internal.pkg.apis.Condition; import io.fabric8.kubernetes.api.model.Quantity; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.tekton.client.TektonClient; @@ -49,15 +54,20 @@ public class Driver { private String accessToken; @Setter - private String quayRepo = "quay.io/redhat-user-workloads/konflux-jbs-pnc-tenant/jvm-build-service/build-request-processor:latest"; + @ConfigProperty(name = "konflux-build-driver.konflux-processor") + String processor; @Setter - private String processor = "quay.io/redhat-user-workloads-stage/pnc-devel-tenant/pnc"; + @ConfigProperty(name = "konflux-build-driver.quay-repo") + String quayRepo; + + @ConfigProperty(name = "konflux-build-driver.pipeline-resolver") + String resolverTarget; @ConfigProperty(name = "build-driver.pipeline") Optional customPipeline; - public void create(BuildRequest buildRequest) { + public BuildResponse create(BuildRequest buildRequest) { IndyTokenResponseDTO tokenResponseDTO = new IndyTokenResponseDTO(accessToken); if (isEmpty(accessToken)) { @@ -95,10 +105,13 @@ public void create(BuildRequest buildRequest) { pipelineRun = tc.v1().pipelineRuns().load(Path.of(customPipeline.get()).toFile()).item(); } } catch (IOException e) { - e.printStackTrace(); - // TODO: process + throw new RuntimeException(e); } pipelineRun = pipelineRun.edit().editOrNewSpec() + .editPipelineRef() + .editFirstParam().editOrNewValue().withStringVal(resolverTarget).endValue() + .endParam() + .endPipelineRef() .addAllToParams(templateProperties.entrySet().stream() .map(t -> new ParamBuilder().withName(t.getKey()).withNewValue(t.getValue()).build()).toList()) .editFirstTaskRunSpec() @@ -111,14 +124,37 @@ public void create(BuildRequest buildRequest) { .endTaskRunSpec() .endSpec().build(); - System.err.println("### Got p " + pipelineRun); var created = client.resource(pipelineRun).inNamespace(buildRequest.namespace()).create(); - System.err.println("### Got c " + created); + + return BuildResponse.builder().namespace(buildRequest.namespace()).pipelineId(created.getMetadata().getName()).build(); + } + + public void cancel(CancelRequest request) { + var tc = client.adapt(TektonClient.class); + var pipeline = tc.v1beta1().pipelineRuns().inNamespace(request.namespace()).withName(request.pipelineId()).get(); + + logger.info("Retrieved pipeline {}", pipeline); + + List conditions = new ArrayList<>(); + // https://tekton.dev/docs/pipelines/pipelineruns/#monitoring-execution-status + Condition cancelCondition = new Condition(); + cancelCondition.setType("Succeeded"); + cancelCondition.setStatus("False"); + cancelCondition.setReason("CancelledRunFinally"); + cancelCondition.setMessage("The PipelineRun was cancelled"); + conditions.add(cancelCondition); + + pipeline.getStatus().setConditions(conditions); + + tc.v1beta1().pipelineRuns().inNamespace(request.namespace()).resource(pipeline).updateStatus(); + // https://tekton.dev/docs/pipelines/pipelineruns/#gracefully-cancelling-a-pipelinerun + // tc.v1beta1().pipelineRuns().updateStatus().inNamespace(request.namespace()).withName(request.pipelineId()).patch() + // .edit(p -> p.edit().editOrNewSpec().withStatus("CancelledRunFinally").endSpec().build()); } /** - * Get a fresh access token for the service account. This is done because we want to get a super-new token to be - * used since we're not entirely sure when the http request will be done inside the completablefuture. + * Get a fresh access token for the service account. This is done because we want to get a + * super-new token to be used since we're not entirely sure when the http request will be done. * * @return fresh access token */ diff --git a/java-components/driver/src/main/java/com/redhat/hacbs/driver/dto/BuildResponse.java b/java-components/driver/src/main/java/com/redhat/hacbs/driver/dto/BuildResponse.java new file mode 100644 index 000000000..208f44796 --- /dev/null +++ b/java-components/driver/src/main/java/com/redhat/hacbs/driver/dto/BuildResponse.java @@ -0,0 +1,11 @@ +package com.redhat.hacbs.driver.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import lombok.Builder; + +@Builder(builderClassName = "Builder") +@JsonIgnoreProperties(ignoreUnknown = true) +public record BuildResponse(String pipelineId, String namespace) { + +} diff --git a/java-components/driver/src/main/java/com/redhat/hacbs/driver/dto/CancelRequest.java b/java-components/driver/src/main/java/com/redhat/hacbs/driver/dto/CancelRequest.java new file mode 100644 index 000000000..36aa37320 --- /dev/null +++ b/java-components/driver/src/main/java/com/redhat/hacbs/driver/dto/CancelRequest.java @@ -0,0 +1,11 @@ +package com.redhat.hacbs.driver.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import lombok.Builder; + +@Builder(builderClassName = "Builder") +@JsonIgnoreProperties(ignoreUnknown = true) +public record CancelRequest(String pipelineId, String namespace) { + +} diff --git a/java-components/driver/src/main/java/com/redhat/hacbs/driver/endpoints/Public.java b/java-components/driver/src/main/java/com/redhat/hacbs/driver/endpoints/Public.java new file mode 100644 index 000000000..5a3002c11 --- /dev/null +++ b/java-components/driver/src/main/java/com/redhat/hacbs/driver/endpoints/Public.java @@ -0,0 +1,89 @@ +/** + * JBoss, Home of Professional Open Source. + * Copyright 2021 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.redhat.hacbs.driver.endpoints; + +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import org.jboss.pnc.api.dto.ComponentVersion; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.redhat.hacbs.driver.Driver; +import com.redhat.hacbs.driver.dto.BuildRequest; +import com.redhat.hacbs.driver.dto.BuildResponse; +import com.redhat.hacbs.driver.util.Info; + +import io.smallrye.common.annotation.RunOnVirtualThread; + +/** + * Endpoint to start/cancel the build. + * + * @author Matej Lazar + */ +@Path("/") +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +public class Public { + + private static final Logger logger = LoggerFactory.getLogger(Public.class); + + @Inject + Driver driver; + + @Inject + Info info; + + @POST + @Path("/build") + @RunOnVirtualThread + // public CompletionStage build(BuildRequest buildRequest) { + public BuildResponse build(BuildRequest buildRequest) { + logger.info("Requested project build: {}", buildRequest.projectName()); + var result = driver.create(buildRequest); + logger.info("### Got {}", result); + return result; + } + + // TODO: Is delete possible in konflux? + // + // /** + // * Cancel the build execution. + // */ + // @PUT + // @Path("/cancel") + // public CompletionStage cancel(BuildCancelRequest buildCancelRequest) { + // logger.info("Requested cancel: {}", buildCancelRequest.getBuildExecutionId()); + // return driver.cancel(buildCancelRequest).thenApply((r) -> Response.status(r.getCode()).build()); + // } + + @Path("/version") + @GET + @RunOnVirtualThread + public ComponentVersion getVersion() { + var r = info.getVersion(); + logger.info("Requested version {}", r); + return r; + } +} diff --git a/java-components/driver/src/main/java/com/redhat/hacbs/driver/util/Info.java b/java-components/driver/src/main/java/com/redhat/hacbs/driver/util/Info.java new file mode 100644 index 000000000..c7957b555 --- /dev/null +++ b/java-components/driver/src/main/java/com/redhat/hacbs/driver/util/Info.java @@ -0,0 +1,33 @@ +package com.redhat.hacbs.driver.util; + +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; + +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.jboss.pnc.api.dto.ComponentVersion; + +import io.quarkus.info.BuildInfo; +import io.quarkus.info.GitInfo; + +@RequestScoped +public class Info { + + @ConfigProperty(name = "quarkus.application.name") + String name; + + @Inject + GitInfo gitInfo; + + @Inject + BuildInfo buildInfo; + + public ComponentVersion getVersion() { + return ComponentVersion.builder() + .name(name) + .builtOn(buildInfo.time().toZonedDateTime()) + .commit(gitInfo.latestCommitId()) + .version(buildInfo.version()) + .build(); + } + +} diff --git a/java-components/driver/src/main/resources/application.yaml b/java-components/driver/src/main/resources/application.yaml index 76662e94b..25b24dad2 100644 --- a/java-components/driver/src/main/resources/application.yaml +++ b/java-components/driver/src/main/resources/application.yaml @@ -1,3 +1,8 @@ +konflux-build-driver: + quay-repo: "quay.io/redhat-user-workloads-stage/pnc-devel-tenant/pnc-konflux" + konflux-processor: "quay.io/redhat-user-workloads/konflux-jbs-pnc-tenant/jvm-build-service/build-request-processor:latest" + # TODO: This will eventually be build-definitions repository + pipeline-resolver: "https://raw.githubusercontent.com/rnc/jvm-build-service/refs/heads/NCL8774/deploy/pipeline/mw-pipeline-v0.1.yaml" quarkus: application: name: konflux-build-driver diff --git a/java-components/driver/src/main/resources/pipeline.yaml b/java-components/driver/src/main/resources/pipeline.yaml index 023d3d0fc..0ebc272b6 100644 --- a/java-components/driver/src/main/resources/pipeline.yaml +++ b/java-components/driver/src/main/resources/pipeline.yaml @@ -4,11 +4,11 @@ metadata: generateName: run-mw-pipeline- spec: pipelineRef: + # TODO: Might want in future to change this to OCI bundle reference? resolver: http params: - # TODO: This will eventually be build-definitions repository - name: url - value: https://raw.githubusercontent.com/rnc/jvm-build-service/refs/heads/NCL8774/deploy/pipeline/mw-pipeline-v0.1.yaml + value: "" workspaces: - name: source # TODO: If we have a custom git step we can share this with prebuild thereby eliminating the need for a volumeClaimTemplate diff --git a/java-components/driver/src/test/java/com/redhat/hacbs/driver/EndpointTest.java b/java-components/driver/src/test/java/com/redhat/hacbs/driver/EndpointTest.java new file mode 100644 index 000000000..79e8db882 --- /dev/null +++ b/java-components/driver/src/test/java/com/redhat/hacbs/driver/EndpointTest.java @@ -0,0 +1,68 @@ +package com.redhat.hacbs.driver; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import jakarta.inject.Inject; + +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.jboss.pnc.api.dto.ComponentVersion; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.redhat.hacbs.driver.clients.IndyService; +import com.redhat.hacbs.driver.clients.IndyTokenRequestDTO; +import com.redhat.hacbs.driver.clients.IndyTokenResponseDTO; +import com.redhat.hacbs.driver.dto.BuildRequest; + +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.KubernetesServer; +import io.quarkus.test.InjectMock; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.kubernetes.client.KubernetesTestServer; +import io.quarkus.test.kubernetes.client.WithKubernetesTestServer; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; + +@WithKubernetesTestServer +@QuarkusTest +public class EndpointTest { + + @KubernetesTestServer + KubernetesServer mockServer; + + @Inject + KubernetesClient client; + + @InjectMock + @RestClient + IndyService indyService; + + @BeforeEach + public void setup() { + when(indyService.getAuthToken(any(IndyTokenRequestDTO.class), any(String.class))) + .thenReturn(new IndyTokenResponseDTO("token-for-builder-pod")); + } + + @Test + void verify() { + + BuildRequest request = BuildRequest.builder().namespace("default").podMemoryOverride("1Gi").build(); + RestAssured.given().contentType(ContentType.JSON) + .body(request) + .when() + .post("/build") + .then() + .statusCode(200); + } + + @Test + void version() { + var result = RestAssured.given() + .when() + .get("/version") + .as(ComponentVersion.class); + Assertions.assertEquals("konflux-build-driver", result.getName()); + } +} diff --git a/java-components/driver/src/test/java/com/redhat/hacbs/driver/MockOidcClient.java b/java-components/driver/src/test/java/com/redhat/hacbs/driver/MockOidcClient.java new file mode 100644 index 000000000..9f8bc88a1 --- /dev/null +++ b/java-components/driver/src/test/java/com/redhat/hacbs/driver/MockOidcClient.java @@ -0,0 +1,41 @@ +package com.redhat.hacbs.driver; + +import java.io.IOException; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.Map; + +import io.quarkus.oidc.client.OidcClient; +import io.quarkus.oidc.client.Tokens; +import io.quarkus.test.Mock; +import io.smallrye.mutiny.Uni; + +/** + * From PNC + * BuildDriver + */ +@Mock +public class MockOidcClient implements OidcClient { + + @Override + public Uni getTokens(Map additionalGrantParameters) { + return Uni.createFrom() + .item(new Tokens("accessToken", 1L, Duration.of(5, ChronoUnit.MINUTES), "refreshToken", 1L, null, null)); + } + + @Override + public Uni refreshTokens(String refreshToken, Map additionalGrantParameters) { + return null; + } + + @Override + public Uni revokeAccessToken(String accessToken, Map additionalParameters) { + return null; + } + + @Override + public void close() throws IOException { + + } +} diff --git a/java-components/driver/src/test/resources/application.yaml b/java-components/driver/src/test/resources/application.yaml new file mode 100644 index 000000000..5b08310b0 --- /dev/null +++ b/java-components/driver/src/test/resources/application.yaml @@ -0,0 +1,9 @@ +quarkus: + oidc: + enabled: false + oidc-client: + enabled: false + log: + category: + "com.redhat.hacbs": + level: DEBUG diff --git a/java-components/pom.xml b/java-components/pom.xml index 584904736..8bb23cef5 100644 --- a/java-components/pom.xml +++ b/java-components/pom.xml @@ -4,6 +4,12 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 + + org.jboss + jboss-parent + 47 + + io.github.redhat-appstudio.jvmbuild jvm-build-service-parent 999-SNAPSHOT @@ -40,6 +46,8 @@ UTF-8 UTF-8 + 21 + 21 21 io.quarkus.platform