/** * POST TITLE: Tekton Pipelines Integration in Java * * Complete Java client for Tekton CI/CD pipelines with Kubernetes integration */ import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientBuilder; import io.fabric8.tekton.client.TektonClient; import io.fabric8.tekton.client.DefaultTektonClient; import io.fabric8.tekton.pipeline.v1.*; import io.fabric8.kubernetes.api.model.*; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.core.JsonProcessingException; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; public class TektonPipelineManager { private static final Logger logger = Logger.getLogger(TektonPipelineManager.class.getName()); private TektonClient tektonClient; private KubernetesClient kubernetesClient; private ObjectMapper objectMapper; public TektonPipelineManager() { this.kubernetesClient = new KubernetesClientBuilder().build(); this.tektonClient = new DefaultTektonClient(kubernetesClient); this.objectMapper = new ObjectMapper(); } /** * Pipeline Definition Builder */ public static class PipelineBuilder { private String name; private String namespace = "default"; private String description; private List<TaskRef> tasks = new ArrayList<>(); private Map<String, Param> params = new HashMap<>(); private Map<String, Workspace> workspaces = new HashMap<>(); public PipelineBuilder(String name) { this.name = name; } public PipelineBuilder namespace(String namespace) { this.namespace = namespace; return this; } public PipelineBuilder description(String description) { this.description = description; return this; } public PipelineBuilder addParam(String name, String description, String defaultValue) { this.params.put(name, new Param(name, description, defaultValue)); return this; } public PipelineBuilder addWorkspace(String name, String description) { this.workspaces.put(name, new Workspace(name, description)); return this; } public PipelineBuilder addTask(String name, String taskRef, Map<String, String> params) { this.tasks.add(new TaskRef(name, taskRef, params)); return this; } public Pipeline build() { Pipeline pipeline = new Pipeline(); pipeline.setMetadata(new ObjectMeta()); pipeline.getMetadata().setName(name); pipeline.getMetadata().setNamespace(namespace); if (description != null) { pipeline.getMetadata().setAnnotations(Map.of("description", description)); } // Set parameters if (!params.isEmpty()) { List<io.fabric8.tekton.pipeline.v1.ParamSpec> paramSpecs = new ArrayList<>(); for (Param param : params.values()) { io.fabric8.tekton.pipeline.v1.ParamSpec paramSpec = new io.fabric8.tekton.pipeline.v1.ParamSpec(); paramSpec.setName(param.name); paramSpec.setDescription(param.description); paramSpec.setType("string"); paramSpec.setDefault(new ParamValue(param.defaultValue)); paramSpecs.add(paramSpec); } pipeline.setSpec(new PipelineSpec()); pipeline.getSpec().setParams(paramSpecs); } // Set workspaces if (!workspaces.isEmpty()) { List<io.fabric8.tekton.pipeline.v1.PipelineWorkspaceDeclaration> workspaceDeclarations = new ArrayList<>(); for (Workspace workspace : workspaces.values()) { io.fabric8.tekton.pipeline.v1.PipelineWorkspaceDeclaration workspaceDecl = new io.fabric8.tekton.pipeline.v1.PipelineWorkspaceDeclaration(); workspaceDecl.setName(workspace.name); workspaceDecl.setDescription(workspace.description); workspaceDeclarations.add(workspaceDecl); } pipeline.getSpec().setWorkspaces(workspaceDeclarations); } // Set tasks List<PipelineTask> pipelineTasks = new ArrayList<>(); for (TaskRef taskRef : tasks) { PipelineTask pipelineTask = new PipelineTask(); pipelineTask.setName(taskRef.name); TaskRefBuilder taskRefBuilder = new TaskRefBuilder(); taskRefBuilder.withName(taskRef.taskRef); pipelineTask.setTaskRef(taskRefBuilder.build()); if (taskRef.params != null && !taskRef.params.isEmpty()) { List<Param> taskParams = new ArrayList<>(); for (Map.Entry<String, String> entry : taskRef.params.entrySet()) { Param param = new Param(); param.setName(entry.getKey()); param.setValue(new ParamValue(entry.getValue())); taskParams.add(param); } pipelineTask.setParams(taskParams); } pipelineTasks.add(pipelineTask); } pipeline.getSpec().setTasks(pipelineTasks); return pipeline; } private static class Param { String name; String description; String defaultValue; Param(String name, String description, String defaultValue) { this.name = name; this.description = description; this.defaultValue = defaultValue; } } private static class Workspace { String name; String description; Workspace(String name, String description) { this.name = name; this.description = description; } } private static class TaskRef { String name; String taskRef; Map<String, String> params; TaskRef(String name, String taskRef, Map<String, String> params) { this.name = name; this.taskRef = taskRef; this.params = params; } } } /** * Create a new Pipeline */ public Pipeline createPipeline(PipelineBuilder builder) { Pipeline pipeline = builder.build(); return tektonClient.v1().pipelines().inNamespace(builder.namespace).create(pipeline); } /** * Create and run a PipelineRun */ public PipelineRun runPipeline(String pipelineName, String namespace, Map<String, String> params, Map<String, String> workspaces) { String runName = pipelineName + "-run-" + System.currentTimeMillis(); PipelineRun pipelineRun = new PipelineRun(); pipelineRun.setMetadata(new ObjectMeta()); pipelineRun.getMetadata().setName(runName); pipelineRun.getMetadata().setNamespace(namespace); PipelineRunSpec spec = new PipelineRunSpec(); spec.setPipelineRef(new PipelineRefBuilder().withName(pipelineName).build()); // Set parameters if (params != null && !params.isEmpty()) { List<io.fabric8.tekton.pipeline.v1.Param> runParams = new ArrayList<>(); for (Map.Entry<String, String> entry : params.entrySet()) { io.fabric8.tekton.pipeline.v1.Param param = new io.fabric8.tekton.pipeline.v1.Param(); param.setName(entry.getKey()); param.setValue(new ParamValue(entry.getValue())); runParams.add(param); } spec.setParams(runParams); } // Set workspaces if (workspaces != null && !workspaces.isEmpty()) { List<WorkspaceBinding> workspaceBindings = new ArrayList<>(); for (Map.Entry<String, String> entry : workspaces.entrySet()) { WorkspaceBinding workspaceBinding = new WorkspaceBinding(); workspaceBinding.setName(entry.getKey()); PersistentVolumeClaimVolumeSource pvc = new PersistentVolumeClaimVolumeSource(); pvc.setClaimName(entry.getValue()); Volume volume = new Volume(); volume.setPersistentVolumeClaim(pvc); workspaceBinding.setVolume(volume); workspaceBindings.add(workspaceBinding); } spec.setWorkspaces(workspaceBindings); } pipelineRun.setSpec(spec); return tektonClient.v1().pipelineRuns().inNamespace(namespace).create(pipelineRun); } /** * Monitor PipelineRun status */ public PipelineRunStatus monitorPipelineRun(String runName, String namespace, long timeoutMinutes) throws InterruptedException { long startTime = System.currentTimeMillis(); long timeoutMs = timeoutMinutes * 60 * 1000; while (System.currentTimeMillis() - startTime < timeoutMs) { PipelineRun pipelineRun = tektonClient.v1().pipelineRuns() .inNamespace(namespace) .withName(runName) .get(); if (pipelineRun == null) { throw new RuntimeException("PipelineRun not found: " + runName); } PipelineRunStatus status = pipelineRun.getStatus(); if (status != null) { String completionTime = status.getCompletionTime(); if (completionTime != null) { logger.info("PipelineRun " + runName + " completed"); return status; } List<Condition> conditions = status.getConditions(); if (conditions != null && !conditions.isEmpty()) { Condition condition = conditions.get(0); if ("False".equals(condition.getStatus())) { throw new RuntimeException("PipelineRun failed: " + condition.getMessage()); } } } logger.info("PipelineRun " + runName + " still running..."); TimeUnit.SECONDS.sleep(30); } throw new RuntimeException("PipelineRun timeout after " + timeoutMinutes + " minutes"); } /** * Get PipelineRun logs */ public String getPipelineRunLogs(String runName, String namespace) { // This would integrate with Kubernetes log retrieval // Implementation depends on your logging setup return "Logs for PipelineRun: " + runName; } /** * Task Definition Builder */ public static class TaskBuilder { private String name; private String namespace = "default"; private String description; private List<Step> steps = new ArrayList<>(); private Map<String, Param> params = new HashMap<>(); private Map<String, Workspace> workspaces = new HashMap<>(); public TaskBuilder(String name) { this.name = name; } public TaskBuilder namespace(String namespace) { this.namespace = namespace; return this; } public TaskBuilder description(String description) { this.description = description; return this; } public TaskBuilder addParam(String name, String description, String defaultValue) { this.params.put(name, new Param(name, description, defaultValue)); return this; } public TaskBuilder addWorkspace(String name, String description) { this.workspaces.put(name, new Workspace(name, description)); return this; } public TaskBuilder addStep(String name, String image, List<String> command, List<String> args) { this.steps.add(new Step(name, image, command, args)); return this; } public Task build() { Task task = new Task(); task.setMetadata(new ObjectMeta()); task.getMetadata().setName(name); task.getMetadata().setNamespace(namespace); if (description != null) { task.getMetadata().setAnnotations(Map.of("description", description)); } TaskSpec spec = new TaskSpec(); // Set parameters if (!params.isEmpty()) { List<io.fabric8.tekton.pipeline.v1.ParamSpec> paramSpecs = new ArrayList<>(); for (Param param : params.values()) { io.fabric8.tekton.pipeline.v1.ParamSpec paramSpec = new io.fabric8.tekton.pipeline.v1.ParamSpec(); paramSpec.setName(param.name); paramSpec.setDescription(param.description); paramSpec.setType("string"); paramSpec.setDefault(new ParamValue(param.defaultValue)); paramSpecs.add(paramSpec); } spec.setParams(paramSpecs); } // Set workspaces if (!workspaces.isEmpty()) { List<io.fabric8.tekton.pipeline.v1.WorkspaceDeclaration> workspaceDeclarations = new ArrayList<>(); for (Workspace workspace : workspaces.values()) { io.fabric8.tekton.pipeline.v1.WorkspaceDeclaration workspaceDecl = new io.fabric8.tekton.pipeline.v1.WorkspaceDeclaration(); workspaceDecl.setName(workspace.name); workspaceDecl.setDescription(workspace.description); workspaceDeclarations.add(workspaceDecl); } spec.setWorkspaces(workspaceDeclarations); } // Set steps List<io.fabric8.tekton.pipeline.v1.Step> taskSteps = new ArrayList<>(); for (Step step : steps) { io.fabric8.tekton.pipeline.v1.Step taskStep = new io.fabric8.tekton.pipeline.v1.Step(); taskStep.setName(step.name); taskStep.setImage(step.image); taskStep.setCommand(step.command); taskStep.setArgs(step.args); taskSteps.add(taskStep); } spec.setSteps(taskSteps); task.setSpec(spec); return task; } private static class Step { String name; String image; List<String> command; List<String> args; Step(String name, String image, List<String> command, List<String> args) { this.name = name; this.image = image; this.command = command; this.args = args; } } } /** * Create a Task */ public Task createTask(TaskBuilder builder) { Task task = builder.build(); return tektonClient.v1().tasks().inNamespace(builder.namespace).create(task); } /** * Pipeline Service for common CI/CD workflows */ public static class PipelineService { private TektonPipelineManager manager; public PipelineService(TektonPipelineManager manager) { this.manager = manager; } /** * Create a Java Maven build pipeline */ public Pipeline createJavaMavenPipeline(String name, String namespace) { // First create tasks createMavenBuildTask(namespace); createDockerBuildTask(namespace); createKubernetesDeployTask(namespace); // Then create pipeline PipelineBuilder builder = new PipelineBuilder(name) .namespace(namespace) .description("Java Maven Build and Deploy Pipeline") .addParam("git-url", "Git repository URL", "") .addParam("image-tag", "Docker image tag", "latest") .addWorkspace("source", "Source code workspace") .addWorkspace("maven-cache", "Maven cache workspace") .addTask("clone", "git-clone", Map.of("url", "$(params.git-url)", "subdirectory", "source")) .addTask("test", "maven-test", Map.of()) .addTask("build", "maven-build", Map.of()) .addTask("docker-build", "docker-build", Map.of("IMAGE", "$(params.image-tag)")) .addTask("deploy", "kubernetes-deploy", Map.of("image", "$(params.image-tag)")); return manager.createPipeline(builder); } private void createMavenBuildTask(String namespace) { TaskBuilder builder = new TaskBuilder("maven-build") .namespace(namespace) .description("Maven build task for Java applications") .addWorkspace("source", "Source code") .addWorkspace("maven-cache", "Maven cache") .addStep("maven-test", "maven:3.8.4-openjdk-17", List.of("mvn", "clean", "test"), List.of()) .addStep("maven-package", "maven:3.8.4-openjdk-17", List.of("mvn", "clean", "package", "-DskipTests"), List.of()); manager.createTask(builder); } private void createDockerBuildTask(String namespace) { TaskBuilder builder = new TaskBuilder("docker-build") .namespace(namespace) .description("Docker build task") .addParam("IMAGE", "Image name", "") .addWorkspace("source", "Source code with Dockerfile") .addStep("build", "gcr.io/kaniko-project/executor:latest", List.of("/kaniko/executor"), List.of("--dockerfile=$(workspaces.source.path)/Dockerfile", "--destination=$(params.IMAGE)", "--context=$(workspaces.source.path)")); manager.createTask(builder); } } /** * Demo usage */ public static void main(String[] args) { try { TektonPipelineManager manager = new TektonPipelineManager(); PipelineService pipelineService = new PipelineService(manager); // Create a Java pipeline Pipeline pipeline = pipelineService.createJavaMavenPipeline("java-app-pipeline", "default"); logger.info("Created pipeline: " + pipeline.getMetadata().getName()); // Run the pipeline Map<String, String> params = Map.of( "git-url", "https://github.com/example/java-app.git", "image-tag", "myapp:v1.0.0" ); Map<String, String> workspaces = Map.of( "source", "pipeline-source-pvc", "maven-cache", "maven-cache-pvc" ); PipelineRun pipelineRun = manager.runPipeline("java-app-pipeline", "default", params, workspaces); logger.info("Started PipelineRun: " + pipelineRun.getMetadata().getName()); // Monitor the pipeline run (commented out for demo) // PipelineRunStatus status = manager.monitorPipelineRun( // pipelineRun.getMetadata().getName(), "default", 30); // logger.info("PipelineRun completed with status: " + status.getConditions()); } catch (Exception e) { logger.severe("Error in Tekton pipeline demo: " + e.getMessage()); e.printStackTrace(); } } } Maven Dependencies
<!-- pom.xml --> <dependencies> <dependency> <groupId>io.fabric8</groupId> <artifactId>tekton-client</artifactId> <version>6.3.0</version> </dependency> <dependency> <groupId>io.fabric8</groupId> <artifactId>kubernetes-client</artifactId> <version>6.3.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.14.2</version> </dependency> </dependencies>
Spring Boot Integration
/** * Spring Boot Configuration for Tekton */ @Configuration public class TektonConfig { @Value("${kubernetes.namespace:default}") private String namespace; @Bean public TektonPipelineManager tektonPipelineManager() { return new TektonPipelineManager(); } @Bean public PipelineService pipelineService(TektonPipelineManager manager) { return new PipelineService(manager); } } /** * REST Controller for Pipeline Management */ @RestController @RequestMapping("/api/pipelines") public class PipelineController { @Autowired private TektonPipelineManager pipelineManager; @PostMapping public ResponseEntity<Pipeline> createPipeline(@RequestBody PipelineRequest request) { PipelineBuilder builder = new PipelineBuilder(request.getName()) .namespace(request.getNamespace()) .description(request.getDescription()); for (PipelineRequest.Task task : request.getTasks()) { builder.addTask(task.getName(), task.getTaskRef(), task.getParams()); } Pipeline pipeline = pipelineManager.createPipeline(builder); return ResponseEntity.ok(pipeline); } @PostMapping("/{pipelineName}/run") public ResponseEntity<PipelineRun> runPipeline( @PathVariable String pipelineName, @RequestParam String namespace, @RequestBody Map<String, String> params) { PipelineRun pipelineRun = pipelineManager.runPipeline(pipelineName, namespace, params, null); return ResponseEntity.ok(pipelineRun); } @GetMapping("/runs/{runName}") public ResponseEntity<PipelineRun> getPipelineRun( @PathVariable String runName, @RequestParam String namespace) { PipelineRun pipelineRun = pipelineManager.tektonClient.v1().pipelineRuns() .inNamespace(namespace) .withName(runName) .get(); return ResponseEntity.ok(pipelineRun); } } /** * Request DTOs */ class PipelineRequest { private String name; private String namespace; private String description; private List<Task> tasks; // Getters and setters public static class Task { private String name; private String taskRef; private Map<String, String> params; // Getters and setters } } This Tekton Pipelines Java integration provides:
- Complete Tekton Client for pipeline and task management
- Builder Patterns for easy pipeline/task creation
- PipelineRun Management with monitoring capabilities
- Common CI/CD Templates for Java applications
- Spring Boot Integration for web applications
- Error Handling and status monitoring
- Workspace and Parameter management
The implementation follows Kubernetes/Tekton best practices and can be extended for complex CI/CD workflows.