use of org.talend.sdk.component.remoteengine.customizer.model.DockerConfiguration in project component-runtime by Talend.
the class RemoteEngineCustomizer method registerComponents.
// CHECKSTYLE:OFF
public void registerComponents(final String remoteEngineDirConf, final String workDirConf, final String cacheDirConf, final String baseImageConf, final String targetImageConf, final Collection<String> carPaths, final ImageType fromImageType, final ImageType targetImageType, final DockerConfiguration dockerConfiguration, final RegistryConfiguration registryConfiguration, final ConnectorLoader connectorLoader, final boolean updateOriginalFile) {
// CHECKSTYLE:ON
final Path remoteEngineDir = PathFactory.get(requireNonNull(remoteEngineDirConf, "Missing remote engine folder"));
final Path workDir = PathFactory.get(workDirConf);
final Path cacheDir = cacheDirConf.startsWith("${remote.engine.dir}/") ? remoteEngineDir.resolve(cacheDirConf.substring("${remote.engine.dir}/".length())) : PathFactory.get(cacheDirConf);
final Collection<Path> cars = carPaths.stream().map(PathFactory::get).collect(toList());
final List<Path> missingCars = cars.stream().filter(it -> !Files.exists(it)).collect(toList());
if (!missingCars.isEmpty()) {
throw new IllegalArgumentException("Missing component archives: " + missingCars);
}
try {
final Properties filtering = IO.loadProperties(remoteEngineDir.resolve(".env"));
final Path compose = remoteEngineDir.resolve("docker-compose.yml");
final List<String> lines = IO.readFile(compose);
final ImageAndLine connectorsImageRef = findImage(lines, "connectors");
final String fromConnectorsImage = ofNullable(baseImageConf).filter(it -> !"auto".equals(it)).orElseGet(() -> filterPlaceholders(filtering, connectorsImageRef.image));
final String toConnectorsImage = ofNullable(targetImageConf).filter(it -> !"auto".equals(it)).orElseGet(() -> timestampImage(fromConnectorsImage));
final Containerizer targetContainer = targetImageType == ImageType.DOCKER ? Containerizer.to(dockerConfiguration.toImage(toConnectorsImage)) : Containerizer.to(registryConfiguration.toImage(toConnectorsImage));
log.info("Building image '{}' from '{}' adding {}", toConnectorsImage, fromConnectorsImage, cars);
final ExecutorService executor = Executors.newFixedThreadPool(Math.max(Runtime.getRuntime().availableProcessors(), 4));
try (final AutoCloseable ignored = IO.autoDir(workDir)) {
final Path registry = workDir.resolve("component-registry.properties");
final Path registryDigest = workDir.resolve("component-registry-digest.properties");
final AbsoluteUnixPath rootContainerPath = AbsoluteUnixPath.get("/opt/talend/connectors");
final Instant now = Instant.now();
final Collection<ConnectorLoader.ConnectorLayer> connectorsLayer = cars.stream().map(it -> connectorLoader.createConnectorLayer(rootContainerPath, workDir, it)).collect(toList());
final Path baseCache = cacheDir.resolve("base");
final Path appCache = cacheDir.resolve("application");
log.info("Looking for component-registry.properties configuration, this can be a bit long...");
final Image image;
try {
image = loadImage(fromConnectorsImage, toConnectorsImage, executor, baseCache, appCache, dockerConfiguration, fromImageType);
} catch (final ExecutionException ee) {
log.error("Please validate the connectors container image is an official one, " + "we don't support customizations on custom images or set the from image type");
throw ee;
}
final Map<String, Properties> propertiesContents = image.getLayers().reverse().stream().map(it -> extractProperties(it, Stream.of(registry, registryDigest).map(f -> "opt/talend/connectors/" + f.getFileName()).collect(toSet()))).filter(it -> !it.isEmpty()).findFirst().orElseThrow(() -> new IllegalStateException("No layer containing the component registry in '" + fromConnectorsImage + "'"));
final Properties componentProperties = requireNonNull(propertiesContents.get("/opt/talend/connectors/component-registry.properties"), "Missing component-registry.properties");
connectorsLayer.forEach(c -> componentProperties.put(c.getGav().split(":")[1], c.getGav()));
final Optional<Properties> digestProperties = ofNullable(propertiesContents.get("/opt/talend/connectors/component-registry-digest.properties"));
digestProperties.ifPresent(digests -> connectorsLayer.forEach(cl -> cl.getDependencies().forEach((key, path) -> {
try (final DigestOutputStream out = new DigestOutputStream(ByteStreams.nullOutputStream(), MessageDigest.getInstance("SHA-512"))) {
java.nio.file.Files.copy(path, out);
out.flush();
final byte[] digest = out.getMessageDigest().digest();
if (digests.put(key, Hex.hex(digest)) != null) {
log.info("'{}' digest will be overriding existing entry (entry='{}')", key, cl.getGav());
}
} catch (final NoSuchAlgorithmException | IOException e) {
throw new IllegalStateException(e);
}
})));
try (final Writer writer = Files.newBufferedWriter(registry)) {
componentProperties.store(writer, "Generated by " + getClass().getName());
}
if (digestProperties.isPresent()) {
try (final Writer writer = Files.newBufferedWriter(registryDigest)) {
digestProperties.orElseThrow(IllegalStateException::new).store(writer, "Generated by " + getClass().getName());
}
}
log.info("Building image '{}'", toConnectorsImage);
final JibContainerBuilder from = from(fromImageType, dockerConfiguration, fromConnectorsImage);
connectorsLayer.stream().map(ConnectorLoader.ConnectorLayer::getLayer).forEach(from::addLayer);
from.addLayer(LayerConfiguration.builder().addEntry(registry, rootContainerPath.resolve(registry.getFileName().toString()), FilePermissions.DEFAULT_FILE_PERMISSIONS, now).addEntry(registryDigest, rootContainerPath.resolve(registryDigest.getFileName().toString()), FilePermissions.DEFAULT_FILE_PERMISSIONS, now).build()).setCreationTime(now).containerize(targetContainer.setToolName("Talend Component Kit Remote Engine Customizer " + Versions.VERSION).setExecutorService(executor).setBaseImageLayersCache(baseCache).setApplicationLayersCache(appCache));
if (updateOriginalFile) {
rewriteCompose(remoteEngineDir, compose, lines, connectorsImageRef, toConnectorsImage);
log.info("Restart your remote engine to take into account the new connector image");
} else {
log.info("You can update '{}' connectors container with image '{}'", compose, toConnectorsImage);
}
} finally {
executor.shutdownNow();
if (!executor.awaitTermination(5, SECONDS)) {
log.warn("Executor is not terminated but exiting since it is not critical");
}
}
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
} catch (final Exception e) {
throw new IllegalStateException(e);
}
}
Aggregations