diff --git a/prospero-cli/pom.xml b/prospero-cli/pom.xml
index 058f73e52..b4c05eced 100644
--- a/prospero-cli/pom.xml
+++ b/prospero-cli/pom.xml
@@ -13,6 +13,12 @@
jar
+
+
+ com.google.cloud.tools
+ jib-core
+ 0.27.1
+
com.fasterxml.jackson.dataformat
jackson-dataformat-yaml
diff --git a/prospero-cli/src/main/java/org/wildfly/prospero/cli/CliMain.java b/prospero-cli/src/main/java/org/wildfly/prospero/cli/CliMain.java
index 7b07049d5..1ec78021d 100644
--- a/prospero-cli/src/main/java/org/wildfly/prospero/cli/CliMain.java
+++ b/prospero-cli/src/main/java/org/wildfly/prospero/cli/CliMain.java
@@ -22,6 +22,7 @@
import org.jboss.logmanager.Level;
import org.jboss.logmanager.PropertyConfigurator;
import org.jboss.logmanager.config.LogContextConfiguration;
+import org.wildfly.prospero.cli.commands.BuildImageCommand;
import org.wildfly.prospero.cli.commands.ChannelCommand;
import org.wildfly.prospero.cli.commands.CliConstants;
import org.wildfly.prospero.cli.commands.CloneCommand;
@@ -86,6 +87,8 @@ public static CommandLine createCommandLine(CliConsole console, String[] args, A
commandLine.addSubcommand(new ChannelCommand(console, actionFactory));
commandLine.addSubcommand(new CompletionCommand());
+ commandLine.addSubcommand(new BuildImageCommand(console, actionFactory));
+
CommandLine channelCmd = commandLine.getSubcommands().get(CliConstants.Commands.CHANNEL);
channelCmd.addSubcommand(new ChannelAddCommand(console, actionFactory));
channelCmd.addSubcommand(new ChannelRemoveCommand(console, actionFactory));
diff --git a/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/BuildImageCommand.java b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/BuildImageCommand.java
new file mode 100644
index 000000000..e13f403cb
--- /dev/null
+++ b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/BuildImageCommand.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2022 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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 org.wildfly.prospero.cli.commands;
+
+import static com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer.DEFAULT_MODIFICATION_TIME_PROVIDER;
+
+import java.nio.file.Path;
+import java.util.Optional;
+
+import com.google.cloud.tools.jib.api.Containerizer;
+import com.google.cloud.tools.jib.api.Jib;
+import com.google.cloud.tools.jib.api.RegistryImage;
+import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath;
+import com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer;
+import com.google.cloud.tools.jib.api.buildplan.FilePermissions;
+import com.google.cloud.tools.jib.api.buildplan.FilePermissionsProvider;
+import com.google.cloud.tools.jib.api.buildplan.OwnershipProvider;
+import org.jboss.logging.Logger;
+import org.wildfly.prospero.cli.ActionFactory;
+import org.wildfly.prospero.cli.CliConsole;
+import org.wildfly.prospero.cli.ReturnCodes;
+import picocli.CommandLine;
+
+@CommandLine.Command(
+ name = CliConstants.Commands.BUILD_IMAGE,
+ sortOptions = false
+)
+public class BuildImageCommand extends AbstractCommand {
+
+ private static final Logger log = Logger.getLogger(BuildImageCommand.class);
+
+ @CommandLine.Option(names = CliConstants.NAME)
+ String name;
+
+ @CommandLine.Option(names = CliConstants.DIR)
+ Optional directory;
+
+ @CommandLine.Option(names = CliConstants.USER)
+ Optional user;
+
+ @CommandLine.Option(names = CliConstants.PASSWORD)
+ Optional password;
+
+ @CommandLine.Option(names = CliConstants.RUNTIME_VERSION)
+ Optional runtimeVersion;
+
+ private static final FilePermissionsProvider PERMISSIONS_PROVIDER = (sourcePath, destinationPath) -> FilePermissions.fromOctalString("775");
+
+ private static final OwnershipProvider JBOSS_ROOT_OWNER = (sourcePath, destinationPath) -> "jboss:root";
+
+ public BuildImageCommand(CliConsole console, ActionFactory actionFactory) {
+ super(console, actionFactory);
+ }
+
+ @Override
+ public Integer call() throws Exception {
+ Path installationDirectory = determineInstallationDirectory(directory);
+
+ String baseImage = "quay.io/wildfly/wildfly-runtime:" + runtimeVersion.orElse("latest");
+ System.out.println("Creating image with installation at " + installationDirectory + " from " + baseImage);
+
+ RegistryImage registryImage = RegistryImage.named(name);
+ if (user.isPresent() && password.isPresent()) {
+ registryImage.addCredential(user.get(), password.get());
+ }
+
+ FileEntriesLayer layer = FileEntriesLayer.builder()
+ .setName("wildfly")
+ .addEntryRecursive(installationDirectory, AbsoluteUnixPath.get("/opt/server/"),
+ PERMISSIONS_PROVIDER,
+ DEFAULT_MODIFICATION_TIME_PROVIDER,
+ JBOSS_ROOT_OWNER)
+ .build();
+
+ Jib.from(baseImage)
+ .addFileEntriesLayer(layer)
+ .containerize(Containerizer.to(registryImage));
+
+ return ReturnCodes.SUCCESS;
+ }
+
+}
diff --git a/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/CliConstants.java b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/CliConstants.java
index 6fa3b30e4..3f6659874 100644
--- a/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/CliConstants.java
+++ b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/CliConstants.java
@@ -38,6 +38,7 @@ private Commands() {
public static final String APPLY = "apply";
public static final String CHANNEL = "channel";
public static final String CLONE = "clone";
+ public static final String BUILD_IMAGE = "build-image";
public static final String CUSTOMIZATION_INIT_CHANNEL = "init";
public static final String CUSTOMIZATION_INITIALIZE_CHANNEL = "initialize";
public static final String CUSTOMIZATION_PROMOTE = "promote";
@@ -80,8 +81,10 @@ private Commands() {
public static final String LAYERS = "--layers";
public static final String LIST_PROFILES = "--list-profiles";
public static final String LOCAL_CACHE = "--local-cache";
+ public static final String NAME = "--name";
public static final String OFFLINE = "--offline";
public static final String PACKAGE_STABILITY_LEVEL = "--package-stability-level";
+ public static final String PASSWORD = "--password";
public static final String PATH = "";
public static final String PRODUCT = "--product";
public static final String PROFILE = "--profile";
@@ -90,10 +93,12 @@ private Commands() {
public static final String REPO_URL = "";
public static final String REPOSITORIES = "--repositories";
public static final String REVISION = "--revision";
+ public static final String RUNTIME_VERSION = "--runtime-version";
public static final String SELF = "--self";
public static final String SHADE_REPOSITORIES = "--shade-repositories";
public static final String STABILITY_LEVEL = "--stability-level";
public static final String USE_LOCAL_MAVEN_CACHE = "--use-default-local-cache";
+ public static final String USER = "--user";
public static final String TARGET_CONFIG = "--target-config";
public static final String V = "-v";
public static final String VERBOSE = "--verbose";