From 1734e38714f6bd5ce9981e4866398f38e5b4f769 Mon Sep 17 00:00:00 2001 From: Nick Cross Date: Fri, 6 Dec 2024 09:04:32 +0000 Subject: [PATCH 1/4] Port implementation code from JBS --- .github/workflows/maven.yml | 2 +- README.md | 4 +- pom.xml | 113 +++++++++++++++ .../jboss/pnc/konfluxbuilddriver/Driver.java | 135 ++++++++++++++++++ .../clients/IndyService.java | 1 + .../clients/IndyTokenRequestDTO.java | 1 + .../konfluxbuilddriver/dto/BuildRequest.java | 14 ++ .../konfluxbuilddriver/dto/BuildResponse.java | 11 ++ .../konfluxbuilddriver/dto/CancelRequest.java | 11 ++ .../konfluxbuilddriver/endpoints/Public.java | 64 +++++++++ .../util/Configuration.java | 16 +++ .../pnc/konfluxbuilddriver/util/Info.java | 33 +++++ src/main/resources/META-INF/beans.xml | 0 src/main/resources/application.yaml | 27 ++++ src/main/resources/pipeline.yaml | 43 ++++++ .../pnc/konfluxbuilddriver/EndpointTest.java | 69 +++++++++ .../konfluxbuilddriver/MockOidcClient.java | 41 ++++++ .../pnc/konfluxbuilddriver/UtilTest.java | 23 +++ src/test/resources/application.yaml | 7 + 19 files changed, 613 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/jboss/pnc/konfluxbuilddriver/Driver.java create mode 100644 src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/BuildRequest.java create mode 100644 src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/BuildResponse.java create mode 100644 src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/CancelRequest.java create mode 100644 src/main/java/org/jboss/pnc/konfluxbuilddriver/endpoints/Public.java create mode 100644 src/main/java/org/jboss/pnc/konfluxbuilddriver/util/Configuration.java create mode 100644 src/main/java/org/jboss/pnc/konfluxbuilddriver/util/Info.java create mode 100644 src/main/resources/META-INF/beans.xml create mode 100644 src/main/resources/application.yaml create mode 100644 src/main/resources/pipeline.yaml create mode 100644 src/test/java/org/jboss/pnc/konfluxbuilddriver/EndpointTest.java create mode 100644 src/test/java/org/jboss/pnc/konfluxbuilddriver/MockOidcClient.java create mode 100644 src/test/java/org/jboss/pnc/konfluxbuilddriver/UtilTest.java create mode 100644 src/test/resources/application.yaml diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 346164f..64b27e9 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -76,4 +76,4 @@ jobs: "password": "${{ secrets.SONATYPE_PASSWORD }}" }] - name: Deploy Snapshot - run: mvn -B -V -Pjboss-release org.apache.maven.plugins:maven-source-plugin:jar-no-fork deploy + run: mvn -B -V -Pjboss-release deploy diff --git a/README.md b/README.md index 06d3a35..852c91d 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ -# konflux-build-driver \ No newline at end of file +# konflux-build-driver + +Uses the same pattern as https://github.com/project-ncl/build-driver/ or https://github.com/project-ncl/environment-driver/ but targeted towards a Konflux deployment. diff --git a/pom.xml b/pom.xml index d132849..d1a58ce 100644 --- a/pom.xml +++ b/pom.xml @@ -73,6 +73,10 @@ + + io.quarkus + quarkus-info + io.quarkus quarkus-oidc @@ -81,10 +85,29 @@ io.quarkus quarkus-oidc-client + + io.quarkus + quarkus-openshift-client + + + io.quarkus + quarkus-rest-jackson + io.quarkus quarkus-rest-client-jackson + + io.quarkiverse.tektonclient + quarkus-tekton-client + 1.0.1 + + + + org.jboss.pnc + pnc-api + 3.0.0 + org.projectlombok @@ -98,11 +121,26 @@ quarkus-junit5 test + + io.quarkus + quarkus-junit5-component + test + + + io.quarkus + quarkus-junit5-mockito + test + io.rest-assured rest-assured test + + io.quarkus + quarkus-test-kubernetes-client + test + @@ -156,6 +194,7 @@ + org.apache.maven.plugins maven-surefire-plugin @@ -179,5 +218,79 @@ true + + format + + true + + !validate-format + + + + + + net.revelc.code.formatter + formatter-maven-plugin + + + process-sources + + format + + + + + + net.revelc.code + impsort-maven-plugin + + + sort-imports + + sort + + + + + + + + + validate + + false + + validate-format + + + + + + net.revelc.code.formatter + formatter-maven-plugin + + + process-sources + + validate + + + + + + net.revelc.code + impsort-maven-plugin + + + check-imports + + check + + + + + + + diff --git a/src/main/java/org/jboss/pnc/konfluxbuilddriver/Driver.java b/src/main/java/org/jboss/pnc/konfluxbuilddriver/Driver.java new file mode 100644 index 0000000..5996364 --- /dev/null +++ b/src/main/java/org/jboss/pnc/konfluxbuilddriver/Driver.java @@ -0,0 +1,135 @@ +package org.jboss.pnc.konfluxbuilddriver; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import org.apache.commons.io.IOUtils; +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.jboss.pnc.konfluxbuilddriver.clients.IndyService; +import org.jboss.pnc.konfluxbuilddriver.clients.IndyTokenRequestDTO; +import org.jboss.pnc.konfluxbuilddriver.clients.IndyTokenResponseDTO; +import org.jboss.pnc.konfluxbuilddriver.dto.BuildRequest; +import org.jboss.pnc.konfluxbuilddriver.dto.BuildResponse; +import org.jboss.pnc.konfluxbuilddriver.dto.CancelRequest; +import org.jboss.pnc.konfluxbuilddriver.util.Configuration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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; +import io.fabric8.tekton.pipeline.v1.ParamBuilder; +import io.fabric8.tekton.pipeline.v1.PipelineRun; +import io.quarkus.oidc.client.OidcClient; + +@ApplicationScoped +public class Driver { + + private static final Logger logger = LoggerFactory.getLogger(Driver.class); + + @Inject + OidcClient oidcClient; + + @RestClient + IndyService indyService; + + @Inject + KubernetesClient client; + + @Inject + Configuration config; + + public BuildResponse create(BuildRequest buildRequest) { + + logger.info("Establishing token from Indy using clientId {}", + ConfigProvider.getConfig().getConfigValue("quarkus.oidc.client-id").getValue()); + IndyTokenResponseDTO tokenResponseDTO = indyService.getAuthToken( + new IndyTokenRequestDTO(buildRequest.repositoryBuildContentId()), + "Bearer " + getFreshAccessToken()); + + Map templateProperties = new HashMap<>(); + templateProperties.put("ACCESS_TOKEN", tokenResponseDTO.token()); + templateProperties.put("BUILD_ID", buildRequest.repositoryBuildContentId()); + templateProperties.put("BUILD_SCRIPT", buildRequest.buildScript()); + templateProperties.put("BUILD_TOOL", buildRequest.buildTool()); + templateProperties.put("BUILD_TOOL_VERSION", buildRequest.buildToolVersion()); + templateProperties.put("JAVA_VERSION", buildRequest.javaVersion()); + templateProperties.put("MVN_REPO_DEPENDENCIES_URL", buildRequest.repositoryDependencyUrl()); + templateProperties.put("MVN_REPO_DEPLOY_URL", buildRequest.repositoryDeployUrl()); + templateProperties.put("QUAY_REPO", config.quayRepo()); + templateProperties.put("RECIPE_IMAGE", buildRequest.recipeImage()); + templateProperties.put("JVM_BUILD_SERVICE_REQPROCESSOR_IMAGE", config.processor()); + templateProperties.put("REVISION", buildRequest.scmRevision()); + templateProperties.put("URL", buildRequest.scmUrl()); + + PipelineRun pipelineRun; + try { + var tc = client.adapt(TektonClient.class); + // Various ways to create the initial PipelineRun object. We can use an objectmapper, + // client.getKubernetesSerialization() or the load calls on the Fabric8 objects. + pipelineRun = tc.v1().pipelineRuns() + .load(IOUtils.resourceToURL("pipeline.yaml", Thread.currentThread().getContextClassLoader())).item(); + } catch (IOException e) { + throw new RuntimeException(e); + } + pipelineRun = pipelineRun.edit().editOrNewSpec() + .editPipelineRef() + .editFirstParam().editOrNewValue().withStringVal(config.resolverTarget()).endValue() + .endParam() + .endPipelineRef() + .addAllToParams(templateProperties.entrySet().stream() + .map(t -> new ParamBuilder().withName(t.getKey()).withNewValue(t.getValue()).build()).toList()) + .editFirstTaskRunSpec() + .editFirstStepSpec() + .editComputeResources() + .addToLimits("memory", new Quantity(buildRequest.podMemoryOverride())) + .addToRequests("memory", new Quantity(buildRequest.podMemoryOverride())) + .endComputeResources() + .endStepSpec() + .endTaskRunSpec() + .endSpec().build(); + + var created = client.resource(pipelineRun).inNamespace(buildRequest.namespace()).create(); + + 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.getMetadata().getName()); + + List conditions = new ArrayList<>(); + // https://tekton.dev/docs/pipelines/pipelineruns/#monitoring-execution-status + Condition cancelCondition = new Condition(); + cancelCondition.setType("Succeeded"); + cancelCondition.setStatus("False"); + // https://github.com/tektoncd/community/blob/main/teps/0058-graceful-pipeline-run-termination.md + 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(); + } + + /** + * 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 + */ + public String getFreshAccessToken() { + return oidcClient.getTokens().await().indefinitely().getAccessToken(); + } +} diff --git a/src/main/java/org/jboss/pnc/konfluxbuilddriver/clients/IndyService.java b/src/main/java/org/jboss/pnc/konfluxbuilddriver/clients/IndyService.java index 0b4f9d3..1dbce3e 100644 --- a/src/main/java/org/jboss/pnc/konfluxbuilddriver/clients/IndyService.java +++ b/src/main/java/org/jboss/pnc/konfluxbuilddriver/clients/IndyService.java @@ -6,6 +6,7 @@ import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; + import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; /** diff --git a/src/main/java/org/jboss/pnc/konfluxbuilddriver/clients/IndyTokenRequestDTO.java b/src/main/java/org/jboss/pnc/konfluxbuilddriver/clients/IndyTokenRequestDTO.java index 10c954e..f260a95 100644 --- a/src/main/java/org/jboss/pnc/konfluxbuilddriver/clients/IndyTokenRequestDTO.java +++ b/src/main/java/org/jboss/pnc/konfluxbuilddriver/clients/IndyTokenRequestDTO.java @@ -1,6 +1,7 @@ package org.jboss.pnc.konfluxbuilddriver.clients; import com.fasterxml.jackson.annotation.JsonProperty; + import lombok.Builder; /** diff --git a/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/BuildRequest.java b/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/BuildRequest.java new file mode 100644 index 0000000..683e92e --- /dev/null +++ b/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/BuildRequest.java @@ -0,0 +1,14 @@ +package org.jboss.pnc.konfluxbuilddriver.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import lombok.Builder; + +@Builder(builderClassName = "Builder") +@JsonIgnoreProperties(ignoreUnknown = true) +public record BuildRequest(String recipeImage, String buildTool, String buildToolVersion, String javaVersion, + String projectName, String scmUrl, String scmRevision, String buildScript, + String repositoryDependencyUrl, String repositoryDeployUrl, String repositoryBuildContentId, + String namespace, String podMemoryOverride) { + +} diff --git a/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/BuildResponse.java b/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/BuildResponse.java new file mode 100644 index 0000000..e73120b --- /dev/null +++ b/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/BuildResponse.java @@ -0,0 +1,11 @@ +package org.jboss.pnc.konfluxbuilddriver.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/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/CancelRequest.java b/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/CancelRequest.java new file mode 100644 index 0000000..9d197f0 --- /dev/null +++ b/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/CancelRequest.java @@ -0,0 +1,11 @@ +package org.jboss.pnc.konfluxbuilddriver.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/src/main/java/org/jboss/pnc/konfluxbuilddriver/endpoints/Public.java b/src/main/java/org/jboss/pnc/konfluxbuilddriver/endpoints/Public.java new file mode 100644 index 0000000..127c71e --- /dev/null +++ b/src/main/java/org/jboss/pnc/konfluxbuilddriver/endpoints/Public.java @@ -0,0 +1,64 @@ +package org.jboss.pnc.konfluxbuilddriver.endpoints; + +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +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.jboss.pnc.konfluxbuilddriver.Driver; +import org.jboss.pnc.konfluxbuilddriver.dto.BuildRequest; +import org.jboss.pnc.konfluxbuilddriver.dto.BuildResponse; +import org.jboss.pnc.konfluxbuilddriver.dto.CancelRequest; +import org.jboss.pnc.konfluxbuilddriver.util.Info; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.smallrye.common.annotation.RunOnVirtualThread; + +/** + * Endpoint to start/cancel the build. + */ +@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 BuildResponse build(BuildRequest buildRequest) { + logger.info("Requested project build: {}", buildRequest.projectName()); + var result = driver.create(buildRequest); + logger.info("### Got {}", result); + return result; + } + + @PUT + @Path("/cancel") + @RunOnVirtualThread + public void cancel(CancelRequest cancelRequest) { + logger.info("Requested cancel: {}", cancelRequest.pipelineId()); + driver.cancel(cancelRequest); + } + + @Path("/version") + @GET + public ComponentVersion getVersion() { + var r = info.getVersion(); + logger.info("Requested version {}", r); + return r; + } +} diff --git a/src/main/java/org/jboss/pnc/konfluxbuilddriver/util/Configuration.java b/src/main/java/org/jboss/pnc/konfluxbuilddriver/util/Configuration.java new file mode 100644 index 0000000..a57db7e --- /dev/null +++ b/src/main/java/org/jboss/pnc/konfluxbuilddriver/util/Configuration.java @@ -0,0 +1,16 @@ +package org.jboss.pnc.konfluxbuilddriver.util; + +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithName; + +@ConfigMapping(prefix = "konflux-build-driver") +public interface Configuration { + + @WithName("konflux-processor") + String processor(); + + String quayRepo(); + + @WithName("pipeline-resolver") + String resolverTarget(); +} diff --git a/src/main/java/org/jboss/pnc/konfluxbuilddriver/util/Info.java b/src/main/java/org/jboss/pnc/konfluxbuilddriver/util/Info.java new file mode 100644 index 0000000..f2b70a2 --- /dev/null +++ b/src/main/java/org/jboss/pnc/konfluxbuilddriver/util/Info.java @@ -0,0 +1,33 @@ +package org.jboss.pnc.konfluxbuilddriver.util; + +import jakarta.enterprise.context.ApplicationScoped; +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; + +@ApplicationScoped +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/src/main/resources/META-INF/beans.xml b/src/main/resources/META-INF/beans.xml new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml new file mode 100644 index 0000000..b6c9eed --- /dev/null +++ b/src/main/resources/application.yaml @@ -0,0 +1,27 @@ +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/redhat-appstudio/jvm-build-service/refs/heads/main/deploy/pipeline/mw-pipeline-v0.1.yaml" +quarkus: + application: + name: konflux-build-driver + shutdown: + timeout: 300 + log: + category: + "org.jboss.pnc.konfluxbuilddriver": + level: DEBUG + oidc: + auth-server-url: https://keycloak-host/auth/realms/pncredhat + client-id: client + credentials: + secret: secret + oidc-client: + auth-server-url: https://keycloak-host/auth/realms/pncredhat + client-id: service-account + credentials: + secret: secret-of-service-account + rest-client: + indy-service: + url: "http://localhost:8080" diff --git a/src/main/resources/pipeline.yaml b/src/main/resources/pipeline.yaml new file mode 100644 index 0000000..0ebc272 --- /dev/null +++ b/src/main/resources/pipeline.yaml @@ -0,0 +1,43 @@ +apiVersion: tekton.dev/v1 +kind: PipelineRun +metadata: + generateName: run-mw-pipeline- +spec: + pipelineRef: + # TODO: Might want in future to change this to OCI bundle reference? + resolver: http + params: + - name: url + 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 + # + # emptyDir: {} - does not share the data between tasks + # When the volume is created from a template in a PipelineRun or TaskRun it will be deleted when the PipelineRun or TaskRun is deleted. + volumeClaimTemplate: + metadata: + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + status: {} + params: + - name: caTrustConfigMapName + value: custom-ca + - name: ENABLE_INDY_PROXY + value: "false" + # TODO: Should PNC set both limits and requests? See + # https://home.robusta.dev/blog/kubernetes-memory-limit + # https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + taskRunSpecs: + - pipelineTaskName: buildah-oci-ta + stepSpecs: + - name: build + computeResources: + # limits/requests memory are configured dynamically in Driver. + taskRunTemplate: + podTemplate: + env: diff --git a/src/test/java/org/jboss/pnc/konfluxbuilddriver/EndpointTest.java b/src/test/java/org/jboss/pnc/konfluxbuilddriver/EndpointTest.java new file mode 100644 index 0000000..042e0c1 --- /dev/null +++ b/src/test/java/org/jboss/pnc/konfluxbuilddriver/EndpointTest.java @@ -0,0 +1,69 @@ +package org.jboss.pnc.konfluxbuilddriver; + +import static org.junit.jupiter.api.Assertions.assertEquals; +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.jboss.pnc.konfluxbuilddriver.clients.IndyService; +import org.jboss.pnc.konfluxbuilddriver.clients.IndyTokenRequestDTO; +import org.jboss.pnc.konfluxbuilddriver.clients.IndyTokenResponseDTO; +import org.jboss.pnc.konfluxbuilddriver.dto.BuildRequest; +import org.jboss.pnc.konfluxbuilddriver.dto.BuildResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +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; +import io.restassured.response.Response; + +@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(); + Response res = RestAssured.given().contentType(ContentType.JSON) + .body(request) + .when() + .post("/build"); + + assertEquals(200, res.statusCode()); + assertEquals("default", res.as(BuildResponse.class).namespace()); + } + + @Test + void version() { + var result = RestAssured.given() + .when() + .get("/version") + .as(ComponentVersion.class); + assertEquals("konflux-build-driver", result.getName()); + } +} diff --git a/src/test/java/org/jboss/pnc/konfluxbuilddriver/MockOidcClient.java b/src/test/java/org/jboss/pnc/konfluxbuilddriver/MockOidcClient.java new file mode 100644 index 0000000..ae69f66 --- /dev/null +++ b/src/test/java/org/jboss/pnc/konfluxbuilddriver/MockOidcClient.java @@ -0,0 +1,41 @@ +package org.jboss.pnc.konfluxbuilddriver; + +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/src/test/java/org/jboss/pnc/konfluxbuilddriver/UtilTest.java b/src/test/java/org/jboss/pnc/konfluxbuilddriver/UtilTest.java new file mode 100644 index 0000000..b04450f --- /dev/null +++ b/src/test/java/org/jboss/pnc/konfluxbuilddriver/UtilTest.java @@ -0,0 +1,23 @@ +package org.jboss.pnc.konfluxbuilddriver; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import jakarta.inject.Inject; + +import org.jboss.pnc.konfluxbuilddriver.util.Configuration; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class UtilTest { + + @Inject + Configuration configTest; + + @Test + public void testMapping() { + assertEquals("quay.io/redhat-user-workloads-stage/pnc-devel-tenant/pnc-konflux", configTest.quayRepo()); + assertEquals("foobar", configTest.processor()); + } +} diff --git a/src/test/resources/application.yaml b/src/test/resources/application.yaml new file mode 100644 index 0000000..686c1ab --- /dev/null +++ b/src/test/resources/application.yaml @@ -0,0 +1,7 @@ +quarkus: + oidc: + enabled: false + oidc-client: + enabled: false +konflux-build-driver: + konflux-processor: "foobar" From 4a970637ade609c2e191ce5b44797102f1b4c894 Mon Sep 17 00:00:00 2001 From: Nick Cross Date: Fri, 6 Dec 2024 13:10:06 +0000 Subject: [PATCH 2/4] Improve testing --- pom.xml | 5 ++ .../jboss/pnc/konfluxbuilddriver/Driver.java | 15 ++-- .../clients/IndyService.java | 5 +- .../konfluxbuilddriver/dto/BuildRequest.java | 18 ++++- .../konfluxbuilddriver/dto/BuildResponse.java | 4 +- .../konfluxbuilddriver/dto/CancelRequest.java | 4 +- .../konfluxbuilddriver/endpoints/Public.java | 4 +- .../pnc/konfluxbuilddriver/DriverTest.java | 81 +++++++++++++++++++ .../pnc/konfluxbuilddriver/EndpointTest.java | 6 -- 9 files changed, 115 insertions(+), 27 deletions(-) create mode 100644 src/test/java/org/jboss/pnc/konfluxbuilddriver/DriverTest.java diff --git a/pom.xml b/pom.xml index d1a58ce..2a0d1f7 100644 --- a/pom.xml +++ b/pom.xml @@ -126,6 +126,11 @@ quarkus-junit5-component test + + io.quarkus + quarkus-junit5-internal + test + io.quarkus quarkus-junit5-mockito diff --git a/src/main/java/org/jboss/pnc/konfluxbuilddriver/Driver.java b/src/main/java/org/jboss/pnc/konfluxbuilddriver/Driver.java index 5996364..f107239 100644 --- a/src/main/java/org/jboss/pnc/konfluxbuilddriver/Driver.java +++ b/src/main/java/org/jboss/pnc/konfluxbuilddriver/Driver.java @@ -1,9 +1,7 @@ package org.jboss.pnc.konfluxbuilddriver; import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; import jakarta.enterprise.context.ApplicationScoped; @@ -28,6 +26,7 @@ import io.fabric8.tekton.client.TektonClient; import io.fabric8.tekton.pipeline.v1.ParamBuilder; import io.fabric8.tekton.pipeline.v1.PipelineRun; +import io.fabric8.tekton.pipeline.v1.PipelineRunStatusBuilder; import io.quarkus.oidc.client.OidcClient; @ApplicationScoped @@ -104,11 +103,10 @@ public BuildResponse create(BuildRequest buildRequest) { public void cancel(CancelRequest request) { var tc = client.adapt(TektonClient.class); - var pipeline = tc.v1beta1().pipelineRuns().inNamespace(request.namespace()).withName(request.pipelineId()).get(); + var pipeline = tc.v1().pipelineRuns().inNamespace(request.namespace()).withName(request.pipelineId()).get(); logger.info("Retrieved pipeline {}", pipeline.getMetadata().getName()); - List conditions = new ArrayList<>(); // https://tekton.dev/docs/pipelines/pipelineruns/#monitoring-execution-status Condition cancelCondition = new Condition(); cancelCondition.setType("Succeeded"); @@ -116,16 +114,15 @@ public void cancel(CancelRequest request) { // https://github.com/tektoncd/community/blob/main/teps/0058-graceful-pipeline-run-termination.md cancelCondition.setReason("CancelledRunFinally"); cancelCondition.setMessage("The PipelineRun was cancelled"); - conditions.add(cancelCondition); - pipeline.getStatus().setConditions(conditions); + pipeline.setStatus(new PipelineRunStatusBuilder(pipeline.getStatus()).withConditions(cancelCondition).build()); - tc.v1beta1().pipelineRuns().inNamespace(request.namespace()).resource(pipeline).updateStatus(); + tc.v1().pipelineRuns().inNamespace(request.namespace()).resource(pipeline).updateStatus(); } /** - * 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. + * 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/src/main/java/org/jboss/pnc/konfluxbuilddriver/clients/IndyService.java b/src/main/java/org/jboss/pnc/konfluxbuilddriver/clients/IndyService.java index 1dbce3e..0003681 100644 --- a/src/main/java/org/jboss/pnc/konfluxbuilddriver/clients/IndyService.java +++ b/src/main/java/org/jboss/pnc/konfluxbuilddriver/clients/IndyService.java @@ -16,12 +16,11 @@ public interface IndyService { /** - * Ask Indy to give us the token that we will use for Maven communication with Indy, in the builder pod for the - * particular buildId + * Ask Indy to give us the token that we will use for Maven communication with Indy, in the builder pod for the particular + * buildId * * @param indyTokenRequestDTO the DTO to send to Indy * @param accessToken accessToken required to send data. Note that it should include "Bearer " - * * @return Token DTO */ @Path("/api/security/auth/token") diff --git a/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/BuildRequest.java b/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/BuildRequest.java index 683e92e..10b3ce5 100644 --- a/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/BuildRequest.java +++ b/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/BuildRequest.java @@ -6,9 +6,19 @@ @Builder(builderClassName = "Builder") @JsonIgnoreProperties(ignoreUnknown = true) -public record BuildRequest(String recipeImage, String buildTool, String buildToolVersion, String javaVersion, - String projectName, String scmUrl, String scmRevision, String buildScript, - String repositoryDependencyUrl, String repositoryDeployUrl, String repositoryBuildContentId, - String namespace, String podMemoryOverride) { +public record BuildRequest( + String recipeImage, + String buildTool, + String buildToolVersion, + String javaVersion, + String projectName, + String scmUrl, + String scmRevision, + String buildScript, + String repositoryDependencyUrl, + String repositoryDeployUrl, + String repositoryBuildContentId, + String namespace, + String podMemoryOverride) { } diff --git a/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/BuildResponse.java b/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/BuildResponse.java index e73120b..54a7956 100644 --- a/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/BuildResponse.java +++ b/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/BuildResponse.java @@ -6,6 +6,8 @@ @Builder(builderClassName = "Builder") @JsonIgnoreProperties(ignoreUnknown = true) -public record BuildResponse(String pipelineId, String namespace) { +public record BuildResponse( + String pipelineId, + String namespace) { } diff --git a/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/CancelRequest.java b/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/CancelRequest.java index 9d197f0..772beaa 100644 --- a/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/CancelRequest.java +++ b/src/main/java/org/jboss/pnc/konfluxbuilddriver/dto/CancelRequest.java @@ -6,6 +6,8 @@ @Builder(builderClassName = "Builder") @JsonIgnoreProperties(ignoreUnknown = true) -public record CancelRequest(String pipelineId, String namespace) { +public record CancelRequest( + String pipelineId, + String namespace) { } diff --git a/src/main/java/org/jboss/pnc/konfluxbuilddriver/endpoints/Public.java b/src/main/java/org/jboss/pnc/konfluxbuilddriver/endpoints/Public.java index 127c71e..d5a6ddb 100644 --- a/src/main/java/org/jboss/pnc/konfluxbuilddriver/endpoints/Public.java +++ b/src/main/java/org/jboss/pnc/konfluxbuilddriver/endpoints/Public.java @@ -41,9 +41,7 @@ public class Public { @RunOnVirtualThread public BuildResponse build(BuildRequest buildRequest) { logger.info("Requested project build: {}", buildRequest.projectName()); - var result = driver.create(buildRequest); - logger.info("### Got {}", result); - return result; + return driver.create(buildRequest); } @PUT diff --git a/src/test/java/org/jboss/pnc/konfluxbuilddriver/DriverTest.java b/src/test/java/org/jboss/pnc/konfluxbuilddriver/DriverTest.java new file mode 100644 index 0000000..7b1fa23 --- /dev/null +++ b/src/test/java/org/jboss/pnc/konfluxbuilddriver/DriverTest.java @@ -0,0 +1,81 @@ +package org.jboss.pnc.konfluxbuilddriver; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.logging.LogRecord; + +import jakarta.inject.Inject; + +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.jboss.pnc.konfluxbuilddriver.clients.IndyService; +import org.jboss.pnc.konfluxbuilddriver.clients.IndyTokenRequestDTO; +import org.jboss.pnc.konfluxbuilddriver.clients.IndyTokenResponseDTO; +import org.jboss.pnc.konfluxbuilddriver.dto.BuildRequest; +import org.jboss.pnc.konfluxbuilddriver.dto.BuildResponse; +import org.jboss.pnc.konfluxbuilddriver.dto.CancelRequest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.KubernetesServer; +import io.fabric8.tekton.client.TektonClient; +import io.quarkus.test.InjectMock; +import io.quarkus.test.LogCollectingTestResource; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.common.ResourceArg; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.kubernetes.client.KubernetesTestServer; +import io.quarkus.test.kubernetes.client.WithKubernetesTestServer; + +@WithKubernetesTestServer +@QuarkusTest +@QuarkusTestResource(value = LogCollectingTestResource.class, restrictToAnnotatedClass = true, initArgs = @ResourceArg(name = LogCollectingTestResource.LEVEL, value = "FINE")) +public class DriverTest { + + private static final String namespace = "test-namespace"; + + @KubernetesTestServer + KubernetesServer mockServer; + + @Inject + KubernetesClient client; + + @InjectMock + @RestClient + IndyService indyService; + + @Inject + Driver driver; + + @BeforeEach + public void setup() { + when(indyService.getAuthToken(any(IndyTokenRequestDTO.class), any(String.class))) + .thenReturn(new IndyTokenResponseDTO("token-for-builder-pod")); + + driver.client = this.client; + } + + @Test + void verify() { + BuildRequest request = BuildRequest.builder().namespace(namespace).podMemoryOverride("1Gi").build(); + BuildResponse response = driver.create(request); + + var pipelineRuns = client.adapt(TektonClient.class).v1().pipelineRuns() + .inNamespace(response.namespace()).list().getItems(); + + assertEquals(1, pipelineRuns.size()); + assertEquals(response.pipelineId(), pipelineRuns.getFirst().getMetadata().getName()); + + driver.cancel(CancelRequest.builder().namespace(namespace).pipelineId(response.pipelineId()).build()); + pipelineRuns = client.adapt(TektonClient.class).v1().pipelineRuns() + .inNamespace(response.namespace()).list().getItems(); + assertEquals("False", pipelineRuns.getFirst().getStatus().getConditions().getFirst().getStatus()); + List logRecords = LogCollectingTestResource.current().getRecords(); + assertTrue(logRecords.stream().anyMatch(r -> LogCollectingTestResource.format(r) + .contains("Retrieved pipeline run-mw-pipeline--00000000-0000-0000-0000-000000000005"))); + } +} diff --git a/src/test/java/org/jboss/pnc/konfluxbuilddriver/EndpointTest.java b/src/test/java/org/jboss/pnc/konfluxbuilddriver/EndpointTest.java index 042e0c1..030ee1d 100644 --- a/src/test/java/org/jboss/pnc/konfluxbuilddriver/EndpointTest.java +++ b/src/test/java/org/jboss/pnc/konfluxbuilddriver/EndpointTest.java @@ -4,8 +4,6 @@ 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.jboss.pnc.konfluxbuilddriver.clients.IndyService; @@ -16,7 +14,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -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; @@ -33,9 +30,6 @@ public class EndpointTest { @KubernetesTestServer KubernetesServer mockServer; - @Inject - KubernetesClient client; - @InjectMock @RestClient IndyService indyService; From 1aa827fb872b3fa839d29d3a48c18b4a05c14bb3 Mon Sep 17 00:00:00 2001 From: Nick Cross Date: Mon, 9 Dec 2024 11:38:35 +0000 Subject: [PATCH 3/4] Rename pipeline template. Remove all params from template --- src/main/java/org/jboss/pnc/konfluxbuilddriver/Driver.java | 5 ++++- src/main/resources/{pipeline.yaml => pipeline-run.yaml} | 7 ------- 2 files changed, 4 insertions(+), 8 deletions(-) rename src/main/resources/{pipeline.yaml => pipeline-run.yaml} (88%) diff --git a/src/main/java/org/jboss/pnc/konfluxbuilddriver/Driver.java b/src/main/java/org/jboss/pnc/konfluxbuilddriver/Driver.java index f107239..deef41a 100644 --- a/src/main/java/org/jboss/pnc/konfluxbuilddriver/Driver.java +++ b/src/main/java/org/jboss/pnc/konfluxbuilddriver/Driver.java @@ -68,6 +68,9 @@ public BuildResponse create(BuildRequest buildRequest) { templateProperties.put("JVM_BUILD_SERVICE_REQPROCESSOR_IMAGE", config.processor()); templateProperties.put("REVISION", buildRequest.scmRevision()); templateProperties.put("URL", buildRequest.scmUrl()); + templateProperties.put("caTrustConfigMapName", "custom-ca"); + // TODO: This should be changed to true eventually. + templateProperties.put("ENABLE_INDY_PROXY", "false"); PipelineRun pipelineRun; try { @@ -75,7 +78,7 @@ public BuildResponse create(BuildRequest buildRequest) { // Various ways to create the initial PipelineRun object. We can use an objectmapper, // client.getKubernetesSerialization() or the load calls on the Fabric8 objects. pipelineRun = tc.v1().pipelineRuns() - .load(IOUtils.resourceToURL("pipeline.yaml", Thread.currentThread().getContextClassLoader())).item(); + .load(IOUtils.resourceToURL("pipeline-run.yaml", Thread.currentThread().getContextClassLoader())).item(); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/src/main/resources/pipeline.yaml b/src/main/resources/pipeline-run.yaml similarity index 88% rename from src/main/resources/pipeline.yaml rename to src/main/resources/pipeline-run.yaml index 0ebc272..aecbf5e 100644 --- a/src/main/resources/pipeline.yaml +++ b/src/main/resources/pipeline-run.yaml @@ -25,10 +25,6 @@ spec: storage: 1Gi status: {} params: - - name: caTrustConfigMapName - value: custom-ca - - name: ENABLE_INDY_PROXY - value: "false" # TODO: Should PNC set both limits and requests? See # https://home.robusta.dev/blog/kubernetes-memory-limit # https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ @@ -38,6 +34,3 @@ spec: - name: build computeResources: # limits/requests memory are configured dynamically in Driver. - taskRunTemplate: - podTemplate: - env: From 32a82488bd4503a31c35e5532b3a65d6d699222a Mon Sep 17 00:00:00 2001 From: Nick Cross Date: Mon, 9 Dec 2024 12:19:21 +0000 Subject: [PATCH 4/4] Allow indyProxy to be configured. Simplify pipelinerun template loading --- .../jboss/pnc/konfluxbuilddriver/Driver.java | 29 ++++++++++++------- .../util/Configuration.java | 2 ++ src/main/resources/application.yaml | 3 +- src/test/resources/application.yaml | 4 +++ 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/jboss/pnc/konfluxbuilddriver/Driver.java b/src/main/java/org/jboss/pnc/konfluxbuilddriver/Driver.java index deef41a..3889a1f 100644 --- a/src/main/java/org/jboss/pnc/konfluxbuilddriver/Driver.java +++ b/src/main/java/org/jboss/pnc/konfluxbuilddriver/Driver.java @@ -1,10 +1,12 @@ package org.jboss.pnc.konfluxbuilddriver; import java.io.IOException; +import java.net.URL; import java.util.HashMap; import java.util.Map; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; import jakarta.inject.Inject; import org.apache.commons.io.IOUtils; @@ -28,6 +30,7 @@ import io.fabric8.tekton.pipeline.v1.PipelineRun; import io.fabric8.tekton.pipeline.v1.PipelineRunStatusBuilder; import io.quarkus.oidc.client.OidcClient; +import io.quarkus.runtime.StartupEvent; @ApplicationScoped public class Driver { @@ -46,6 +49,17 @@ public class Driver { @Inject Configuration config; + URL pipelineRunTemplate; + + void onStart(@Observes StartupEvent ev) { + try { + pipelineRunTemplate = IOUtils.resourceToURL("pipeline-run.yaml", Thread.currentThread().getContextClassLoader()); + logger.debug("Driver creating with {}", pipelineRunTemplate); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + public BuildResponse create(BuildRequest buildRequest) { logger.info("Establishing token from Indy using clientId {}", @@ -70,18 +84,11 @@ public BuildResponse create(BuildRequest buildRequest) { templateProperties.put("URL", buildRequest.scmUrl()); templateProperties.put("caTrustConfigMapName", "custom-ca"); // TODO: This should be changed to true eventually. - templateProperties.put("ENABLE_INDY_PROXY", "false"); + templateProperties.put("ENABLE_INDY_PROXY", config.indyProxyEnabled()); - PipelineRun pipelineRun; - try { - var tc = client.adapt(TektonClient.class); - // Various ways to create the initial PipelineRun object. We can use an objectmapper, - // client.getKubernetesSerialization() or the load calls on the Fabric8 objects. - pipelineRun = tc.v1().pipelineRuns() - .load(IOUtils.resourceToURL("pipeline-run.yaml", Thread.currentThread().getContextClassLoader())).item(); - } catch (IOException e) { - throw new RuntimeException(e); - } + // Various ways to create the initial PipelineRun object. We can use an objectmapper, + // client.getKubernetesSerialization() or the load calls on the Fabric8 objects. + PipelineRun pipelineRun = client.adapt(TektonClient.class).v1().pipelineRuns().load(pipelineRunTemplate).item(); pipelineRun = pipelineRun.edit().editOrNewSpec() .editPipelineRef() .editFirstParam().editOrNewValue().withStringVal(config.resolverTarget()).endValue() diff --git a/src/main/java/org/jboss/pnc/konfluxbuilddriver/util/Configuration.java b/src/main/java/org/jboss/pnc/konfluxbuilddriver/util/Configuration.java index a57db7e..3fd6ff8 100644 --- a/src/main/java/org/jboss/pnc/konfluxbuilddriver/util/Configuration.java +++ b/src/main/java/org/jboss/pnc/konfluxbuilddriver/util/Configuration.java @@ -13,4 +13,6 @@ public interface Configuration { @WithName("pipeline-resolver") String resolverTarget(); + + String indyProxyEnabled(); } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index b6c9eed..2809627 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -3,6 +3,7 @@ konflux-build-driver: 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/redhat-appstudio/jvm-build-service/refs/heads/main/deploy/pipeline/mw-pipeline-v0.1.yaml" + indy-proxy-enabled: false quarkus: application: name: konflux-build-driver @@ -11,7 +12,7 @@ quarkus: log: category: "org.jboss.pnc.konfluxbuilddriver": - level: DEBUG + level: INFO oidc: auth-server-url: https://keycloak-host/auth/realms/pncredhat client-id: client diff --git a/src/test/resources/application.yaml b/src/test/resources/application.yaml index 686c1ab..edf002a 100644 --- a/src/test/resources/application.yaml +++ b/src/test/resources/application.yaml @@ -3,5 +3,9 @@ quarkus: enabled: false oidc-client: enabled: false + log: + category: + "org.jboss.pnc.konfluxbuilddriver": + level: DEBUG konflux-build-driver: konflux-processor: "foobar"