Java tutorial
/* * Copyright (C) 2015 Original Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.fabric8.kubernetes.pipeline; import com.google.inject.Inject; import hudson.EnvVars; import hudson.FilePath; import hudson.Functions; import hudson.model.TaskListener; import hudson.os.PosixException; import hudson.util.DirScanner; import hudson.util.FileVisitor; import hudson.util.IOUtils; import hudson.util.io.Archiver; import hudson.util.io.ArchiverFactory; import io.fabric8.docker.api.model.ImageInspect; import io.fabric8.docker.client.DefaultDockerClient; import io.fabric8.docker.client.DockerClient; import io.fabric8.docker.client.utils.DockerIgnorePathMatcher; import io.fabric8.docker.dsl.EventListener; import io.fabric8.docker.dsl.OutputHandle; import jenkins.security.MasterToSlaveCallable; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.tools.tar.TarEntry; import org.jenkinsci.plugins.workflow.steps.AbstractSynchronousStepExecution; import org.jenkinsci.plugins.workflow.steps.StepContextParameter; import java.io.BufferedReader; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.Serializable; import java.lang.reflect.Field; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import static io.fabric8.workflow.core.Constants.DEFAULT_IGNORE_PATTERNS; import static io.fabric8.workflow.core.Constants.DOCKER_IGNORE; import static org.apache.tools.tar.TarConstants.LF_SYMLINK; public class BuildImageStepExecution extends AbstractSynchronousStepExecution<ImageInspect> { private static final transient Logger LOGGER = Logger.getLogger(BuildImageStepExecution.class.getName()); @Inject private BuildImageStep step; @StepContextParameter private FilePath workspace; @StepContextParameter private TaskListener listener; @StepContextParameter private transient EnvVars env; @Override protected ImageInspect run() throws Exception { return workspace.getChannel().call(new MasterToSlaveCallable<ImageInspect, Exception>() { @Override public ImageInspect call() throws Exception { ExecutorService executorService = Executors.newFixedThreadPool(2); try { Future<Boolean> createTarFuture; Future<ImageInspect> buildImageFuture; try (PipedInputStream pin = new PipedInputStream(); PipedOutputStream pout = new PipedOutputStream(pin)) { createTarFuture = executorService.submit(new CreateTarTask(pout)); buildImageFuture = executorService.submit(new BuildImageTask(pin)); } //Wait for the two tasks to complete. if (!createTarFuture.get(step.getTimeout(), TimeUnit.MILLISECONDS)) { listener.getLogger().println("Failed to create docker image tarball."); } ImageInspect imageInspect = buildImageFuture.get(step.getTimeout(), TimeUnit.MILLISECONDS); if (imageInspect == null) { throw new RuntimeException("Failed to build docker image."); } else { return imageInspect; } } finally { executorService.shutdown(); if (executorService.awaitTermination(30, TimeUnit.SECONDS)) { executorService.shutdownNow(); } } } }); } private class BuildImageTask implements Callable<ImageInspect> { private final InputStream inputStream; private BuildImageTask(InputStream inputStream) { this.inputStream = inputStream; } @Override public ImageInspect call() throws Exception { OutputHandle handle = null; try (DockerClient client = new DefaultDockerClient(step.getDockerConfig())) { final BlockingQueue queue = new LinkedBlockingQueue(); listener.getLogger() .println("Building image:" + step.getName() + " from path:" + step.getPath() + "."); handle = client.image().build().withRepositoryName(step.getName()).removingIntermediateOnSuccess() .usingListener(new EventListener() { @Override public void onSuccess(String s) { listener.getLogger().println(s); queue.add(true); } @Override public void onError(String s) { queue.add(new RuntimeException("Failed to build image. Error:" + s)); } @Override public void onEvent(String s) { listener.getLogger().println(s); } }).fromTar(inputStream); Object result = queue.poll(step.getTimeout(), TimeUnit.MILLISECONDS); if (result == null) { throw new RuntimeException("Timed out (" + step.getTimeout() + "ms)building docker image."); } else if (result instanceof Throwable) { throw new RuntimeException((Throwable) result); } else { return client.image().withName(step.getName()).inspect(); } } catch (Throwable t) { t.printStackTrace(listener.getLogger()); return null; } finally { if (handle != null) { handle.close(); } } } } private class CreateTarTask implements Callable<Boolean> { private final OutputStream outputStream; private CreateTarTask(OutputStream outputStream) { this.outputStream = outputStream; } @Override public Boolean call() throws Exception { try { listener.getLogger().printf("Creating tar from path: %s.", step.getPath()); DockerIgnorePathMatcher matcher = createMatcher(workspace.child(step.getPath())); FileFilter docerIgnoreFilter = new DockerIgnoreFileFilter(matcher); workspace.child(step.getPath()).archive(new DockerArchiverFactory(matcher), outputStream, docerIgnoreFilter); outputStream.flush(); outputStream.close(); return true; } catch (Throwable t) { t.printStackTrace(listener.getLogger()); return false; } } } private class TrueFileFilter implements FileFilter, Serializable { @Override public boolean accept(File pathname) { return true; } } private class DockerIgnoreFileFilter implements FileFilter, Serializable { private final DockerIgnorePathMatcher dockerIgnorePathMatcher; private DockerIgnoreFileFilter(DockerIgnorePathMatcher dockerIgnorePathMatcher) { this.dockerIgnorePathMatcher = dockerIgnorePathMatcher; } @Override public boolean accept(File pathname) { return !dockerIgnorePathMatcher.matches(pathname.toPath().toAbsolutePath()); } } public static class MyScanner extends DirScanner { @Override public void scan(File dir, FileVisitor visitor) throws IOException { } } private class DockerArchiverFactory extends ArchiverFactory { private final DockerIgnorePathMatcher matcher; public DockerArchiverFactory(DockerIgnorePathMatcher matcher) { this.matcher = matcher; } @Override public Archiver create(OutputStream out) throws IOException { return new DockerImageArchiver(out, matcher); } } private static class DockerImageArchiver extends Archiver { private final byte[] buf = new byte[8192]; private final TarArchiveOutputStream tar; private final DockerIgnorePathMatcher matcher; DockerImageArchiver(OutputStream out, DockerIgnorePathMatcher matcher) { this.matcher = matcher; tar = new TarArchiveOutputStream(out); tar.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); } @Override public void visitSymlink(File link, String target, String relativePath) throws IOException { TarArchiveEntry e = new TarArchiveEntry(relativePath, LF_SYMLINK); try { int mode = IOUtils.mode(link); if (mode != -1) { e.setMode(mode); } } catch (PosixException x) { // ignore } try { StringBuffer linkName = (StringBuffer) LINKNAME_FIELD.get(e); linkName.setLength(0); linkName.append(target); } catch (IllegalAccessException x) { throw new IOException("Failed to set linkName", x); } tar.putArchiveEntry(e); entriesWritten++; } @Override public boolean understandsSymlink() { return true; } public void visit(File file, String relativePath) throws IOException { if (recursiveMatch(matcher, file.toPath())) { return; } if (relativePath.contains("/")) { relativePath = relativePath.substring(relativePath.indexOf("/") + 1); } else { relativePath = "."; } if (Functions.isWindows()) { relativePath = relativePath.replace('\\', '/'); } if (file.isDirectory()) { relativePath += '/'; } TarArchiveEntry te = new TarArchiveEntry(file); te.setName(relativePath); int mode = IOUtils.mode(file); if (mode != -1) { te.setMode(mode); } te.setModTime(file.lastModified()); if (!file.isDirectory()) { te.setSize(file.length()); } tar.putArchiveEntry(te); if (!file.isDirectory()) { FileInputStream in = new FileInputStream(file); try { int len; while ((len = in.read(buf)) >= 0) tar.write(buf, 0, len); } finally { in.close(); } } tar.closeArchiveEntry(); entriesWritten++; } public void close() throws IOException { tar.close(); } private static final Field LINKNAME_FIELD = getTarEntryLinkNameField(); private static Field getTarEntryLinkNameField() { try { Field f = TarEntry.class.getDeclaredField("linkName"); f.setAccessible(true); return f; } catch (SecurityException e) { throw new AssertionError(e); } catch (NoSuchFieldException e) { throw new AssertionError(e); } } } private DockerIgnorePathMatcher createMatcher(FilePath root) throws IOException, InterruptedException { FilePath dockerIgnorePath = root.child(DOCKER_IGNORE); Set<String> ignorePatterns = new LinkedHashSet<>(); if (dockerIgnorePath.exists()) { ignorePatterns.addAll(readAllLines(dockerIgnorePath)); } if (step.getIgnorePatterns() != null && !step.getIgnorePatterns().isEmpty()) { ignorePatterns.addAll(step.getIgnorePatterns()); } if (ignorePatterns.isEmpty()) { ignorePatterns.addAll(Arrays.asList(DEFAULT_IGNORE_PATTERNS)); } return new DockerIgnorePathMatcher(apply(root, ignorePatterns)); } public static List<String> readAllLines(FilePath path) throws IOException, InterruptedException { try (InputStream is = path.read(); InputStreamReader isr = new InputStreamReader(is); BufferedReader reader = new BufferedReader(isr)) { List<String> result = new ArrayList<>(); for (;;) { String line = reader.readLine(); if (line == null) break; result.add(line); } return result; } } private static List<String> apply(FilePath path, Collection<String> patterns) { return apply(path, patterns.toArray(new String[patterns.size()])); } private static List<String> apply(FilePath path, String... patterns) { List<String> result = new ArrayList<>(); for (String p : patterns) { result.add(path.getRemote().endsWith(File.separator) ? path + p : path + File.separator + p); } return result; } private static boolean recursiveMatch(DockerIgnorePathMatcher match, Path file) { if (match.matches(file)) { return true; } else if (file.getParent() != null) { return recursiveMatch(match, file.getParent()); } else { return false; } } }