Tekton is a powerful Kubernetes-native framework for creating CI/CD systems. While Tekton pipelines are typically defined using YAML, you can manage and interact with them programmatically using Java.
Core Concepts
Tekton Components
- Pipeline: Defines a series of tasks
- Task: Individual step in a pipeline
- PipelineRun: Instance of a pipeline execution
- TaskRun: Instance of a task execution
- PipelineResource: Inputs and outputs for pipelines
Java Client Setup
Dependencies
<!-- Kubernetes Java Client --> <dependency> <groupId>io.kubernetes</groupId> <artifactId>client-java</artifactId> <version>18.0.0</version> </dependency> <!-- Tekton Models --> <dependency> <groupId>io.fabric8</groupId> <artifactId>tekton-client</artifactId> <version>5.12.0</version> </dependency> <!-- YAML Processing --> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-yaml</artifactId> <version>2.15.0</version> </dependency> <!-- HTTP Client --> <dependency> <groupId>org.apache.httpcomponents.client5</groupId> <artifactId>httpclient5</artifactId> <version>5.2.1</version> </dependency>
Core Tekton Client Implementation
Example 1: Tekton Client Factory and Configuration
@Component
@Slf4j
public class TektonClientFactory {
private final TektonClient tektonClient;
private final ObjectMapper yamlMapper;
public TektonClientFactory(@Value("${kube.config.path:}") String kubeConfigPath) {
this.yamlMapper = new ObjectMapper(new YAMLFactory());
this.tektonClient = createTektonClient(kubeConfigPath);
}
private TektonClient createTektonClient(String kubeConfigPath) {
try {
Config config;
if (kubeConfigPath != null && !kubeConfigPath.isEmpty()) {
config = Config.fromKubeconfig(null,
Files.readString(Path.of(kubeConfigPath)), null);
} else {
// Use in-cluster config or default
config = new ConfigBuilder().build();
}
return new DefaultTektonClient(config);
} catch (Exception e) {
log.error("Failed to create Tekton client", e);
throw new TektonClientException("Tekton client initialization failed", e);
}
}
public TektonClient getClient() {
return tektonClient;
}
public ObjectMapper getYamlMapper() {
return yamlMapper;
}
public boolean isClusterAvailable() {
try {
tektonClient.v1().tasks().inAnyNamespace().list();
return true;
} catch (Exception e) {
log.warn("Kubernetes cluster not available", e);
return false;
}
}
}
// Custom exceptions
class TektonClientException extends RuntimeException {
public TektonClientException(String message) {
super(message);
}
public TektonClientException(String message, Throwable cause) {
super(message, cause);
}
}
// Configuration properties
@ConfigurationProperties(prefix = "tekton")
@Data
public class TektonProperties {
private String namespace = "default";
private String serviceAccount = "tekton-bot";
private int timeoutMinutes = 60;
private Retry retry = new Retry();
@Data
public static class Retry {
private int maxAttempts = 3;
private long backoffMs = 5000;
private double backoffMultiplier = 2.0;
}
}
Example 2: Pipeline Management Service
@Service
@Slf4j
public class PipelineManagementService {
private final TektonClient tektonClient;
private final TektonProperties properties;
private final ObjectMapper yamlMapper;
public PipelineManagementService(TektonClientFactory clientFactory,
TektonProperties properties) {
this.tektonClient = clientFactory.getClient();
this.properties = properties;
this.yamlMapper = clientFactory.getYamlMapper();
}
// Pipeline Definition DTOs
@Data
public static class PipelineDefinition {
private String name;
private String description;
private List<TaskDefinition> tasks;
private Map<String, String> parameters;
private Map<String, String> labels;
private Map<String, String> annotations;
public PipelineDefinition(String name) {
this.name = name;
this.tasks = new ArrayList<>();
this.parameters = new HashMap<>();
this.labels = new HashMap<>();
this.annotations = new HashMap<>();
}
public PipelineDefinition withTask(TaskDefinition task) {
this.tasks.add(task);
return this;
}
public PipelineDefinition withParameter(String name, String value) {
this.parameters.put(name, value);
return this;
}
public PipelineDefinition withLabel(String key, String value) {
this.labels.put(key, value);
return this;
}
public PipelineDefinition withAnnotation(String key, String value) {
this.annotations.put(key, value);
return this;
}
}
@Data
public static class TaskDefinition {
private String name;
private String taskRef;
private Map<String, String> parameters;
private List<String> runAfter;
private Map<String, String> workspaces;
private Map<String, String> labels;
public TaskDefinition(String name, String taskRef) {
this.name = name;
this.taskRef = taskRef;
this.parameters = new HashMap<>();
this.runAfter = new ArrayList<>();
this.workspaces = new HashMap<>();
this.labels = new HashMap<>();
}
public TaskDefinition withParameter(String name, String value) {
this.parameters.put(name, value);
return this;
}
public TaskDefinition runAfter(String taskName) {
this.runAfter.add(taskName);
return this;
}
public TaskDefinition withWorkspace(String name, String workspace) {
this.workspaces.put(name, workspace);
return this;
}
public TaskDefinition withLabel(String key, String value) {
this.labels.put(key, value);
return this;
}
}
public Pipeline createPipeline(PipelineDefinition definition) {
try {
log.info("Creating pipeline: {}", definition.getName());
Pipeline pipeline = buildPipelineFromDefinition(definition);
Pipeline created = tektonClient.v1().pipelines()
.inNamespace(properties.getNamespace())
.createOrReplace(pipeline);
log.info("Successfully created pipeline: {}", created.getMetadata().getName());
return created;
} catch (Exception e) {
log.error("Failed to create pipeline: {}", definition.getName(), e);
throw new PipelineException("Pipeline creation failed", e);
}
}
public Pipeline updatePipeline(PipelineDefinition definition) {
try {
log.info("Updating pipeline: {}", definition.getName());
Pipeline pipeline = buildPipelineFromDefinition(definition);
Pipeline updated = tektonClient.v1().pipelines()
.inNamespace(properties.getNamespace())
.withName(definition.getName())
.patch(pipeline);
log.info("Successfully updated pipeline: {}", updated.getMetadata().getName());
return updated;
} catch (Exception e) {
log.error("Failed to update pipeline: {}", definition.getName(), e);
throw new PipelineException("Pipeline update failed", e);
}
}
public boolean deletePipeline(String pipelineName) {
try {
log.info("Deleting pipeline: {}", pipelineName);
boolean deleted = tektonClient.v1().pipelines()
.inNamespace(properties.getNamespace())
.withName(pipelineName)
.delete();
if (deleted) {
log.info("Successfully deleted pipeline: {}", pipelineName);
} else {
log.warn("Pipeline not found: {}", pipelineName);
}
return deleted;
} catch (Exception e) {
log.error("Failed to delete pipeline: {}", pipelineName, e);
throw new PipelineException("Pipeline deletion failed", e);
}
}
public Pipeline getPipeline(String pipelineName) {
try {
return tektonClient.v1().pipelines()
.inNamespace(properties.getNamespace())
.withName(pipelineName)
.get();
} catch (Exception e) {
log.error("Failed to get pipeline: {}", pipelineName, e);
throw new PipelineException("Pipeline retrieval failed", e);
}
}
public List<Pipeline> listPipelines() {
try {
return tektonClient.v1().pipelines()
.inNamespace(properties.getNamespace())
.list()
.getItems();
} catch (Exception e) {
log.error("Failed to list pipelines", e);
throw new PipelineException("Pipeline listing failed", e);
}
}
public List<Pipeline> listPipelinesByLabel(String labelSelector) {
try {
return tektonClient.v1().pipelines()
.inNamespace(properties.getNamespace())
.withLabelSelector(labelSelector)
.list()
.getItems();
} catch (Exception e) {
log.error("Failed to list pipelines with selector: {}", labelSelector, e);
throw new PipelineException("Pipeline listing failed", e);
}
}
private Pipeline buildPipelineFromDefinition(PipelineDefinition definition) {
return new PipelineBuilder()
.withNewMetadata()
.withName(definition.getName())
.withNamespace(properties.getNamespace())
.withLabels(definition.getLabels())
.withAnnotations(definition.getAnnotations())
.endMetadata()
.withNewSpec()
.withParams(buildParams(definition.getParameters()))
.withTasks(buildPipelineTasks(definition.getTasks()))
.endSpec()
.build();
}
private List<ParamSpec> buildParams(Map<String, String> parameters) {
return parameters.entrySet().stream()
.map(entry -> new ParamSpecBuilder()
.withName(entry.getKey())
.withType("string")
.withDescription("Parameter: " + entry.getKey())
.build())
.collect(Collectors.toList());
}
private List<PipelineTask> buildPipelineTasks(List<TaskDefinition> taskDefinitions) {
List<PipelineTask> tasks = new ArrayList<>();
for (TaskDefinition taskDef : taskDefinitions) {
PipelineTask task = new PipelineTaskBuilder()
.withName(taskDef.getName())
.withNewTaskRef()
.withName(taskDef.getTaskRef())
.endTaskRef()
.withParams(buildTaskParams(taskDef.getParameters()))
.withWorkspaces(buildTaskWorkspaces(taskDef.getWorkspaces()))
.withRunAfter(taskDef.getRunAfter())
.build();
tasks.add(task);
}
return tasks;
}
private List<Param> buildTaskParams(Map<String, String> parameters) {
return parameters.entrySet().stream()
.map(entry -> new ParamBuilder()
.withName(entry.getKey())
.withNewValue(entry.getValue())
.build())
.collect(Collectors.toList());
}
private List<WorkspacePipelineTaskBinding> buildTaskWorkspaces(Map<String, String> workspaces) {
return workspaces.entrySet().stream()
.map(entry -> new WorkspacePipelineTaskBindingBuilder()
.withName(entry.getKey())
.withWorkspace(entry.getValue())
.build())
.collect(Collectors.toList());
}
public Pipeline createPipelineFromYaml(String yamlContent) {
try {
Pipeline pipeline = yamlMapper.readValue(yamlContent, Pipeline.class);
Pipeline created = tektonClient.v1().pipelines()
.inNamespace(properties.getNamespace())
.createOrReplace(pipeline);
log.info("Created pipeline from YAML: {}", created.getMetadata().getName());
return created;
} catch (Exception e) {
log.error("Failed to create pipeline from YAML", e);
throw new PipelineException("YAML pipeline creation failed", e);
}
}
public String exportPipelineToYaml(String pipelineName) {
try {
Pipeline pipeline = getPipeline(pipelineName);
return yamlMapper.writeValueAsString(pipeline);
} catch (Exception e) {
log.error("Failed to export pipeline to YAML: {}", pipelineName, e);
throw new PipelineException("Pipeline export failed", e);
}
}
}
class PipelineException extends RuntimeException {
public PipelineException(String message) {
super(message);
}
public PipelineException(String message, Throwable cause) {
super(message, cause);
}
}
Example 3: PipelineRun Management Service
@Service
@Slf4j
public class PipelineRunService {
private final TektonClient tektonClient;
private final TektonProperties properties;
private final ObjectMapper yamlMapper;
private final ScheduledExecutorService monitorExecutor;
public PipelineRunService(TektonClientFactory clientFactory,
TektonProperties properties) {
this.tektonClient = clientFactory.getClient();
this.properties = properties;
this.yamlMapper = clientFactory.getYamlMapper();
this.monitorExecutor = Executors.newScheduledThreadPool(3);
}
@Data
public static class PipelineRunRequest {
private String pipelineName;
private String runName;
private Map<String, String> parameters;
private Map<String, String> workspaces;
private Map<String, String> labels;
private Map<String, String> annotations;
private String serviceAccountName;
public PipelineRunRequest(String pipelineName) {
this.pipelineName = pipelineName;
this.runName = pipelineName + "-run-" + System.currentTimeMillis();
this.parameters = new HashMap<>();
this.workspaces = new HashMap<>();
this.labels = new HashMap<>();
this.annotations = new HashMap<>();
this.serviceAccountName = "default";
}
public PipelineRunRequest withParameter(String name, String value) {
this.parameters.put(name, value);
return this;
}
public PipelineRunRequest withWorkspace(String name, String pvc) {
this.workspaces.put(name, pvc);
return this;
}
public PipelineRunRequest withLabel(String key, String value) {
this.labels.put(key, value);
return this;
}
public PipelineRunRequest withAnnotation(String key, String value) {
this.annotations.put(key, value);
return this;
}
public PipelineRunRequest withServiceAccount(String serviceAccount) {
this.serviceAccountName = serviceAccount;
return this;
}
}
@Data
public static class PipelineRunResult {
private PipelineRun pipelineRun;
private PipelineRunStatus status;
private List<TaskRunInfo> taskRuns;
private Instant startTime;
private Instant completionTime;
private Duration duration;
private boolean success;
private String errorMessage;
public static PipelineRunResult fromPipelineRun(PipelineRun pipelineRun) {
PipelineRunResult result = new PipelineRunResult();
result.setPipelineRun(pipelineRun);
result.setStatus(pipelineRun.getStatus());
if (pipelineRun.getStatus() != null) {
result.setStartTime(parseTimestamp(pipelineRun.getStatus().getStartTime()));
result.setCompletionTime(parseTimestamp(pipelineRun.getStatus().getCompletionTime()));
if (result.getStartTime() != null && result.getCompletionTime() != null) {
result.setDuration(Duration.between(result.getStartTime(), result.getCompletionTime()));
}
result.setSuccess(isPipelineRunSuccessful(pipelineRun));
if (pipelineRun.getStatus().getConditions() != null) {
Optional<Condition> failedCondition = pipelineRun.getStatus().getConditions().stream()
.filter(c -> "Failed".equals(c.getType()))
.findFirst();
if (failedCondition.isPresent()) {
result.setErrorMessage(failedCondition.get().getMessage());
}
}
}
return result;
}
private static Instant parseTimestamp(String timestamp) {
if (timestamp == null) return null;
try {
return Instant.parse(timestamp);
} catch (Exception e) {
return null;
}
}
private static boolean isPipelineRunSuccessful(PipelineRun pipelineRun) {
if (pipelineRun.getStatus() == null || pipelineRun.getStatus().getConditions() == null) {
return false;
}
return pipelineRun.getStatus().getConditions().stream()
.filter(c -> "Succeeded".equals(c.getType()))
.map(Condition::getStatus)
.anyMatch("True"::equals);
}
}
@Data
public static class TaskRunInfo {
private String name;
private String taskName;
private String status;
private Instant startTime;
private Instant completionTime;
private Duration duration;
private List<StepInfo> steps;
private boolean success;
private String errorMessage;
}
@Data
public static class StepInfo {
private String name;
private String container;
private String image;
private String status;
private Instant startTime;
private Instant completionTime;
private Duration duration;
private boolean success;
private String errorMessage;
}
public PipelineRun createPipelineRun(PipelineRunRequest request) {
try {
log.info("Creating PipelineRun: {} for pipeline: {}",
request.getRunName(), request.getPipelineName());
PipelineRun pipelineRun = buildPipelineRun(request);
PipelineRun created = tektonClient.v1().pipelineRuns()
.inNamespace(properties.getNamespace())
.createOrReplace(pipelineRun);
log.info("Successfully created PipelineRun: {}", created.getMetadata().getName());
return created;
} catch (Exception e) {
log.error("Failed to create PipelineRun: {}", request.getRunName(), e);
throw new PipelineRunException("PipelineRun creation failed", e);
}
}
public PipelineRun createPipelineRunFromYaml(String yamlContent) {
try {
PipelineRun pipelineRun = yamlMapper.readValue(yamlContent, PipelineRun.class);
PipelineRun created = tektonClient.v1().pipelineRuns()
.inNamespace(properties.getNamespace())
.createOrReplace(pipelineRun);
log.info("Created PipelineRun from YAML: {}", created.getMetadata().getName());
return created;
} catch (Exception e) {
log.error("Failed to create PipelineRun from YAML", e);
throw new PipelineRunException("YAML PipelineRun creation failed", e);
}
}
private PipelineRun buildPipelineRun(PipelineRunRequest request) {
return new PipelineRunBuilder()
.withNewMetadata()
.withName(request.getRunName())
.withNamespace(properties.getNamespace())
.withLabels(request.getLabels())
.withAnnotations(request.getAnnotations())
.endMetadata()
.withNewSpec()
.withNewPipelineRef()
.withName(request.getPipelineName())
.endPipelineRef()
.withParams(buildPipelineRunParams(request.getParameters()))
.withWorkspaces(buildPipelineRunWorkspaces(request.getWorkspaces()))
.withServiceAccountName(request.getServiceAccountName())
.withTimeouts(buildTimeouts())
.endSpec()
.build();
}
private List<Param> buildPipelineRunParams(Map<String, String> parameters) {
return parameters.entrySet().stream()
.map(entry -> new ParamBuilder()
.withName(entry.getKey())
.withNewValue(entry.getValue())
.build())
.collect(Collectors.toList());
}
private List<WorkspaceBinding> buildPipelineRunWorkspaces(Map<String, String> workspaces) {
return workspaces.entrySet().stream()
.map(entry -> new WorkspaceBindingBuilder()
.withName(entry.getKey())
.withNewPersistentVolumeClaim()
.withClaimName(entry.getValue())
.endPersistentVolumeClaim()
.build())
.collect(Collectors.toList());
}
private TimeoutFields buildTimeouts() {
return new TimeoutFieldsBuilder()
.withPipeline(Duration.ofMinutes(properties.getTimeoutMinutes()).toString())
.build();
}
public PipelineRun getPipelineRun(String runName) {
try {
return tektonClient.v1().pipelineRuns()
.inNamespace(properties.getNamespace())
.withName(runName)
.get();
} catch (Exception e) {
log.error("Failed to get PipelineRun: {}", runName, e);
throw new PipelineRunException("PipelineRun retrieval failed", e);
}
}
public PipelineRunResult getPipelineRunResult(String runName) {
try {
PipelineRun pipelineRun = getPipelineRun(runName);
PipelineRunResult result = PipelineRunResult.fromPipelineRun(pipelineRun);
// Enrich with TaskRun details
result.setTaskRuns(getTaskRunInfos(pipelineRun));
return result;
} catch (Exception e) {
log.error("Failed to get PipelineRun result: {}", runName, e);
throw new PipelineRunException("PipelineRun result retrieval failed", e);
}
}
private List<TaskRunInfo> getTaskRunInfos(PipelineRun pipelineRun) {
List<TaskRunInfo> taskRunInfos = new ArrayList<>();
if (pipelineRun.getStatus() == null ||
pipelineRun.getStatus().getTaskRuns() == null) {
return taskRunInfos;
}
for (Map.Entry<String, PipelineRunTaskRunStatus> entry :
pipelineRun.getStatus().getTaskRuns().entrySet()) {
TaskRunInfo taskRunInfo = new TaskRunInfo();
taskRunInfo.setName(entry.getKey());
PipelineRunTaskRunStatus status = entry.getValue();
if (status.getStatus() != null) {
taskRunInfo.setTaskName(status.getPipelineTaskName());
taskRunInfo.setStatus(status.getStatus().getConditions().toString());
taskRunInfo.setStartTime(parseTimestamp(status.getStatus().getStartTime()));
taskRunInfo.setCompletionTime(parseTimestamp(status.getStatus().getCompletionTime()));
if (taskRunInfo.getStartTime() != null && taskRunInfo.getCompletionTime() != null) {
taskRunInfo.setDuration(Duration.between(
taskRunInfo.getStartTime(), taskRunInfo.getCompletionTime()));
}
// Extract step information
taskRunInfo.setSteps(extractStepInfos(status.getStatus()));
// Determine success
taskRunInfo.setSuccess(isTaskRunSuccessful(status.getStatus()));
// Extract error message if any
taskRunInfo.setErrorMessage(extractErrorMessage(status.getStatus()));
}
taskRunInfos.add(taskRunInfo);
}
return taskRunInfos;
}
private List<StepInfo> extractStepInfos(TaskRunStatus status) {
List<StepInfo> steps = new ArrayList<>();
if (status.getSteps() == null) return steps;
for (StepState stepState : status.getSteps()) {
StepInfo stepInfo = new StepInfo();
stepInfo.setName(stepState.getName());
stepInfo.setContainer(stepState.getContainer());
stepInfo.setImage(stepState.getImageID());
stepInfo.setStatus(stepState.getTerminated() != null ? "Terminated" : "Running");
stepInfo.setStartTime(parseTimestamp(stepState.getTerminated() != null ?
stepState.getTerminated().getStartedAt() : null));
stepInfo.setCompletionTime(parseTimestamp(stepState.getTerminated() != null ?
stepState.getTerminated().getFinishedAt() : null));
if (stepInfo.getStartTime() != null && stepInfo.getCompletionTime() != null) {
stepInfo.setDuration(Duration.between(
stepInfo.getStartTime(), stepInfo.getCompletionTime()));
}
stepInfo.setSuccess(stepState.getTerminated() != null &&
stepState.getTerminated().getExitCode() == 0);
if (stepState.getTerminated() != null && stepState.getTerminated().getExitCode() != 0) {
stepInfo.setErrorMessage("Exit code: " + stepState.getTerminated().getExitCode());
}
steps.add(stepInfo);
}
return steps;
}
private boolean isTaskRunSuccessful(TaskRunStatus status) {
if (status == null || status.getConditions() == null) return false;
return status.getConditions().stream()
.filter(c -> "Succeeded".equals(c.getType()))
.map(Condition::getStatus)
.anyMatch("True"::equals);
}
private String extractErrorMessage(TaskRunStatus status) {
if (status == null || status.getConditions() == null) return null;
Optional<Condition> failedCondition = status.getConditions().stream()
.filter(c -> "Failed".equals(c.getType()))
.findFirst();
return failedCondition.map(Condition::getMessage).orElse(null);
}
private Instant parseTimestamp(String timestamp) {
if (timestamp == null) return null;
try {
return Instant.parse(timestamp);
} catch (Exception e) {
return null;
}
}
public CompletableFuture<PipelineRunResult> runPipelineAndWait(PipelineRunRequest request) {
return runPipelineAndWait(request, Duration.ofMinutes(properties.getTimeoutMinutes()));
}
public CompletableFuture<PipelineRunResult> runPipelineAndWait(PipelineRunRequest request,
Duration timeout) {
return CompletableFuture.supplyAsync(() -> {
try {
// Create the PipelineRun
PipelineRun pipelineRun = createPipelineRun(request);
String runName = pipelineRun.getMetadata().getName();
log.info("Waiting for PipelineRun completion: {}", runName);
// Wait for completion with timeout
PipelineRun completedRun = waitForPipelineRunCompletion(runName, timeout);
// Get detailed result
PipelineRunResult result = getPipelineRunResult(runName);
log.info("PipelineRun completed: {} - Success: {}", runName, result.isSuccess());
return result;
} catch (Exception e) {
log.error("PipelineRun execution failed", e);
throw new PipelineRunException("PipelineRun execution failed", e);
}
});
}
private PipelineRun waitForPipelineRunCompletion(String runName, Duration timeout) {
long endTime = System.currentTimeMillis() + timeout.toMillis();
while (System.currentTimeMillis() < endTime) {
try {
PipelineRun pipelineRun = getPipelineRun(runName);
if (pipelineRun.getStatus() != null) {
boolean isCompleted = pipelineRun.getStatus().getCompletionTime() != null;
boolean hasConditions = pipelineRun.getStatus().getConditions() != null;
if (isCompleted && hasConditions) {
return pipelineRun;
}
}
// Wait before checking again
Thread.sleep(5000); // 5 seconds
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new PipelineRunException("PipelineRun wait interrupted", e);
} catch (Exception e) {
log.warn("Error checking PipelineRun status, retrying...", e);
try {
Thread.sleep(10000); // 10 seconds on error
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new PipelineRunException("PipelineRun wait interrupted", ie);
}
}
}
throw new PipelineRunException("PipelineRun timed out after " + timeout);
}
public List<PipelineRun> listPipelineRuns() {
try {
return tektonClient.v1().pipelineRuns()
.inNamespace(properties.getNamespace())
.list()
.getItems();
} catch (Exception e) {
log.error("Failed to list PipelineRuns", e);
throw new PipelineRunException("PipelineRun listing failed", e);
}
}
public List<PipelineRun> listPipelineRunsByPipeline(String pipelineName) {
try {
return tektonClient.v1().pipelineRuns()
.inNamespace(properties.getNamespace())
.withLabel("tekton.dev/pipeline", pipelineName)
.list()
.getItems();
} catch (Exception e) {
log.error("Failed to list PipelineRuns for pipeline: {}", pipelineName, e);
throw new PipelineRunException("PipelineRun listing failed", e);
}
}
public boolean deletePipelineRun(String runName) {
try {
log.info("Deleting PipelineRun: {}", runName);
boolean deleted = tektonClient.v1().pipelineRuns()
.inNamespace(properties.getNamespace())
.withName(runName)
.delete();
if (deleted) {
log.info("Successfully deleted PipelineRun: {}", runName);
} else {
log.warn("PipelineRun not found: {}", runName);
}
return deleted;
} catch (Exception e) {
log.error("Failed to delete PipelineRun: {}", runName, e);
throw new PipelineRunException("PipelineRun deletion failed", e);
}
}
@PreDestroy
public void cleanup() {
monitorExecutor.shutdown();
try {
if (!monitorExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
monitorExecutor.shutdownNow();
}
} catch (InterruptedException e) {
monitorExecutor.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
class PipelineRunException extends RuntimeException {
public PipelineRunException(String message) {
super(message);
}
public PipelineRunException(String message, Throwable cause) {
super(message, cause);
}
}
Example 4: Task Management Service
```java
@Service
@Slf4j
public class TaskManagementService {
private final TektonClient tektonClient;
private final TektonProperties properties;
private final ObjectMapper yamlMapper;
public TaskManagementService(TektonClientFactory clientFactory,
TektonProperties properties) {
this.tektonClient = clientFactory.getClient();
this.properties = properties;
this.yamlMapper = clientFactory.getYamlMapper();
}
@Data
public static class TaskDefinition {
private String name;
private String description;
private List<TaskStep> steps;
private List<ParamSpec> parameters;
private List<WorkspaceDeclaration> workspaces;
private Map<String, String> labels;
private Map<String, String> annotations;
public TaskDefinition(String name) {
this.name = name;
this.steps = new ArrayList<>();
this.parameters = new ArrayList<>();
this.workspaces = new ArrayList<>();
this.labels = new HashMap<>();
this.annotations = new HashMap<>();
}
public TaskDefinition withStep(TaskStep step) {
this.steps.add(step);
return this;
}
public TaskDefinition withParameter(ParamSpec parameter) {
this.parameters.add(parameter);
return this;
}
public TaskDefinition withWorkspace(WorkspaceDeclaration workspace) {
this.workspaces.add(workspace);
return this;
}
public TaskDefinition withLabel(String key, String value) {
this.labels.put(key, value);
return this;
}
public TaskDefinition withAnnotation(String key, String value) {
this.annotations.put(key, value);
return this;
}
}
@Data
public static class TaskStep {
private String name;
private String image;
private List<String> command;
private List<String> args;
private List<String> script;
private Map<String, String> env;
private String workingDir;
private Map<String, String> volumeMounts;
public TaskStep(String name, String image) {
this.name = name;
this.image = image;
this.command = new ArrayList<>();
this.args = new ArrayList<>();
this.script = new ArrayList<>();
this.env = new HashMap<>();
this.volumeMounts = new HashMap<>();
}
public TaskStep withCommand(String... commands) {
this.command.addAll(Arrays.asList(commands));
return this;
}
public TaskStep withArg(String... arguments) {
this.args.addAll(Arrays.asList(arguments));
return this;
}
public TaskStep withScriptLine(String line) {
this.script.add(line);
return this;
}
public TaskStep withEnv(String key, String value) {
this.env.put(key, value);
return this;
}
public TaskStep withVolumeMount(String name, String mountPath) {
this.volumeMounts.put(name, mountPath);
return this;
}
}
public Task createTask(TaskDefinition definition) {
try {
log.info("Creating task: {}", definition.getName());
Task task = buildTaskFromDefinition(definition);
Task created = tektonClient.v1().tasks()
.inNamespace(properties.getNamespace())
.createOrReplace(task);
log.info("Successfully created task: {}", created.getMetadata().getName());
return created;
} catch (Exception e) {
log.error("Failed to create task: {}", definition.getName(), e);
throw new TaskException("Task creation failed", e);
}
}
public Task updateTask(TaskDefinition definition) {
try {
log.info("Updating task: {}", definition.getName());
Task task = buildTaskFromDefinition(definition);
Task updated = tektonClient.v1().tasks()
.inNamespace(properties.getNamespace())
.withName(definition.getName())
.patch(task);
log.info("Successfully updated task: {}", updated.getMetadata().getName());
return updated;
} catch (Exception e) {
log.error("Failed to update task: {}", definition.getName(), e);
throw new TaskException("Task update failed", e);
}
}
public boolean deleteTask(String taskName) {
try {
log.info("Deleting task: {}", taskName);
boolean deleted = tektonClient.v1().tasks()
.inNamespace(properties.getNamespace())
.withName(taskName)
.delete();
if (deleted) {
log.info("Successfully deleted task: {}", taskName);
} else {
log.warn("Task not found: {}", taskName);
}
return deleted;
} catch (Exception e) {
log.error("Failed to delete task: {}", taskName, e);
throw new TaskException("Task deletion failed", e);
}
}
public Task getTask(String taskName) {
try {
return tektonClient.v1().tasks()
.inNamespace(properties.getNamespace())
.withName(taskName)
.get();
} catch (Exception e) {
log.error("Failed to get task: {}", taskName, e);
throw new TaskException("Task retrieval failed", e);
}
}
public List<Task> listTasks() {
try {
return tektonClient.v1().tasks()
.inNamespace(properties.getNamespace())
.list()
.getItems();
} catch (Exception e) {
log.error("Failed to list tasks", e);
throw new TaskException("Task listing failed", e);
}
}
private Task buildTaskFromDefinition(TaskDefinition definition) {
TaskBuilder builder = new TaskBuilder()
.withNewMetadata()
.withName(definition.getName())
.withNamespace(properties.getNamespace())
.withLabels(definition.getLabels())
.withAnnotations(definition.getAnnotations())
.endMetadata()
.withNewSpec();
// Add steps
for (TaskStep step : definition.getSteps()) {
builder.addNewStep()
.withName(step.getName())
.withImage(step.getImage())
.withCommand(step.getCommand())
.withArgs(step.getArgs())
.withScript(step.getScript() != null && !step.getScript().isEmpty() ?
String.join("\n", step.getScript()) : null)
.withEnv(step.getEnv().entrySet().stream()
.map(entry -> new EnvVarBuilder()
.withName(entry.getKey())
.withValue(entry.getValue())
.build())
.collect(Collectors.toList()))
.withWorkingDir(step.getWorkingDir())
.endStep();
}
// Add parameters
if (!definition.getParameters().isEmpty()) {
builder.withParams(definition.getParameters());
}
// Add workspaces
if (!definition.getWorkspaces().isEmpty()) {
builder.withWorkspaces(definition.getWorkspaces());
}
return builder.endSpec().build();
}
public Task createTaskFromYaml(String yamlContent) {
try {
Task task = yamlMapper.readValue(yamlContent, Task.class