Fix incus project handling

This commit is contained in:
crschnick
2026-04-01 20:59:46 +00:00
parent a61835c17e
commit 21b17455c9
4 changed files with 149 additions and 115 deletions

View File

@@ -22,6 +22,133 @@ public class IncusCommandView extends CommandViewBase {
super(shellControl);
}
public Project project(String project) {
return new Project(project);
}
public class Project extends CommandView {
private final String project;
public Project(String project) {
this.project = project;
}
@Override
protected CommandControl build(Consumer<CommandBuilder> builder) {
var cmd = CommandBuilder.of().add("incus").add("--project", project != null ? project : "default");
builder.accept(cmd);
return shellControl
.command(cmd)
.withErrorFormatter(IncusCommandView::formatErrorMessage)
.withExceptionConverter(IncusCommandView::convertException)
.elevated(requiresElevation());
}
@Override
protected ShellControl getShellControl() {
return IncusCommandView.this.getShellControl();
}
@Override
public Project start() throws Exception {
shellControl.start();
return this;
}
public void start(String containerName) throws Exception {
build(commandBuilder -> commandBuilder
.add("start")
.addQuoted(containerName))
.execute();
}
public void stop(String containerName) throws Exception {
build(commandBuilder -> commandBuilder
.add("stop")
.addQuoted(containerName))
.execute();
}
public void pause(String containerName) throws Exception {
build(commandBuilder -> commandBuilder
.add("pause")
.addQuoted(containerName))
.execute();
}
public CommandControl console(String containerName) {
return build(commandBuilder -> commandBuilder
.add("console").addQuoted(containerName));
}
public CommandControl configEdit(String containerName) {
return build(commandBuilder -> commandBuilder
.add("config", "edit").addQuoted(containerName));
}
public Optional<ContainerEntry> queryContainerState(String containerName) throws Exception {
var l = listContainers();
var found = l.stream()
.filter(containerEntry -> (containerEntry.getProject().equals(project)
|| project == null
&& containerEntry.getProject().equals("default"))
&& containerEntry.getName().equals(containerName))
.findFirst();
return found;
}
public ShellControl exec(String container, String user, Supplier<Boolean> busybox) {
var sub = shellControl.subShell();
sub.setDumbOpen(createOpenFunction(container, user, false, busybox));
sub.setTerminalOpen(createOpenFunction(container, user, true, busybox));
return sub.withExceptionConverter(IncusCommandView::convertException).elevated(requiresElevation());
}
private ShellOpenFunction createOpenFunction(String containerName, String user, boolean terminal, Supplier<Boolean> busybox) {
return new ShellOpenFunction() {
@Override
public CommandBuilder prepareWithoutInitCommand() {
var b = execCommand(containerName, terminal).add("su", "-l");
if (user != null) {
b.addQuoted(user);
}
return b;
}
@Override
public CommandBuilder prepareWithInitCommand(@NonNull String command) {
var b = execCommand(containerName, terminal).add("su", "-l");
if (user != null) {
b.addQuoted(user);
}
return b.add(sc -> {
var suType = busybox.get();
if (suType) {
return "-c";
} else {
return "--session-command";
}
})
.addLiteral(command);
}
};
}
private CommandBuilder execCommand(String containerName, boolean terminal) {
var c = CommandBuilder.of()
.add("incus")
.add("--project", project != null ? project : "default")
.add("exec", terminal ? "-t" : "-T");
return c.addQuoted(containerName)
.add("--");
}
}
private static ElevationFunction requiresElevation() {
return ElevationFunction.cached("incusRequiresElevation", new ElevationFunction() {
@Override
@@ -86,47 +213,6 @@ public class IncusCommandView extends CommandViewBase {
return build(commandBuilder -> commandBuilder.add("version")).readStdoutOrThrow();
}
public void start(String projectName, String containerName) throws Exception {
build(commandBuilder -> commandBuilder
.add("start")
.addQuoted(containerName)
.add("--project")
.addQuoted(projectName))
.execute();
}
public void stop(String projectName, String containerName) throws Exception {
build(commandBuilder -> commandBuilder
.add("stop")
.addQuoted(containerName)
.add("--project")
.addQuoted(projectName))
.execute();
}
public void pause(String projectName, String containerName) throws Exception {
build(commandBuilder -> commandBuilder
.add("pause")
.addQuoted(containerName)
.add("--project")
.addQuoted(projectName))
.execute();
}
public CommandControl console(String projectName, String containerName) {
return build(commandBuilder -> commandBuilder
.add("--project")
.addQuoted(projectName)
.add("console").addQuoted(containerName));
}
public CommandControl configEdit(String projectName, String containerName) {
return build(commandBuilder -> commandBuilder
.add("--project")
.addQuoted(projectName)
.add("config", "edit").addQuoted(containerName));
}
public List<DataStoreEntryRef<IncusContainerStore>> listContainers(DataStoreEntryRef<IncusInstallStore> store)
throws Exception {
return listContainers().stream()
@@ -147,17 +233,6 @@ public class IncusCommandView extends CommandViewBase {
.toList();
}
public Optional<ContainerEntry> queryContainerState(String projectName, String containerName) throws Exception {
var l = listContainers();
var found = l.stream()
.filter(containerEntry -> (containerEntry.getProject().equals(projectName)
|| projectName == null
&& containerEntry.getProject().equals("default"))
&& containerEntry.getName().equals(containerName))
.findFirst();
return found;
}
@Value
public static class ContainerEntry {
String project;
@@ -168,8 +243,7 @@ public class IncusCommandView extends CommandViewBase {
}
private List<ContainerEntry> listContainers() throws Exception {
try (var c = build(commandBuilder -> commandBuilder.add("list", "--all-projects", "-f", "json"))
.start()) {
try (var c = build(commandBuilder -> commandBuilder.add("list", "--all-projects", "-f", "json")).start()) {
var output = c.readStdoutOrThrow();
var json = JacksonMapper.getDefault().readTree(output);
var l = new ArrayList<ContainerEntry>();
@@ -214,50 +288,4 @@ public class IncusCommandView extends CommandViewBase {
return l;
}
}
public ShellControl exec(String projectName, String container, String user, Supplier<Boolean> busybox) {
var sub = shellControl.subShell();
sub.setDumbOpen(createOpenFunction(projectName, container, user, false, busybox));
sub.setTerminalOpen(createOpenFunction(projectName, container, user, true, busybox));
return sub.withExceptionConverter(IncusCommandView::convertException).elevated(requiresElevation());
}
private ShellOpenFunction createOpenFunction(
String projectName, String containerName, String user, boolean terminal, Supplier<Boolean> busybox) {
return new ShellOpenFunction() {
@Override
public CommandBuilder prepareWithoutInitCommand() {
var b = execCommand(projectName, containerName, terminal).add("su", "-l");
if (user != null) {
b.addQuoted(user);
}
return b;
}
@Override
public CommandBuilder prepareWithInitCommand(@NonNull String command) {
var b = execCommand(projectName, containerName, terminal).add("su", "-l");
if (user != null) {
b.addQuoted(user);
}
return b.add(sc -> {
var suType = busybox.get();
if (suType) {
return "-c";
} else {
return "--session-command";
}
})
.addLiteral(command);
}
};
}
public CommandBuilder execCommand(String projectName, String containerName, boolean terminal) {
var c = CommandBuilder.of().add("incus", "exec", terminal ? "-t" : "-T");
return c.addQuoted(containerName)
.add("--project")
.addQuoted(projectName)
.add("--");
}
}

View File

@@ -52,12 +52,11 @@ public class IncusContainerConsoleActionProvider implements HubLeafProvider<Incu
@Override
public void executeImpl() throws Exception {
var d = ref.getStore();
var view = new IncusCommandView(
d.getInstall().getStore().getHost().getStore().getOrStartSession());
var view = d.view();
TerminalLaunch.builder()
.entry(ref.get())
.title("Console")
.command(view.console(d.getProjectName(), d.getName()))
.command(view.console(d.getName()))
.launch();
}
}

View File

@@ -52,12 +52,11 @@ public class IncusContainerEditConfigActionProvider implements HubLeafProvider<I
@Override
public void executeImpl() throws Exception {
var d = ref.getStore();
var view = new IncusCommandView(
d.getInstall().getStore().getHost().getStore().getOrStartSession());
var view = d.view();
TerminalLaunch.builder()
.entry(ref.get())
.title("Config")
.command(view.configEdit(d.getProjectName(), d.getName()))
.command(view.configEdit(d.getName()))
.launch();
}

View File

@@ -44,6 +44,14 @@ public class IncusContainerStore
String containerName;
IdentityValue identity;
public IncusCommandView.Project view() throws Exception {
return view(getInstall().getStore().getHost().getStore().getOrStartSession());
}
public IncusCommandView.Project view(ShellControl sc) {
return new IncusCommandView(sc).project(projectName);
}
@Override
public List<DataStoreEntryRef<?>> getDependencies() {
return DataStoreDependencies.of(install, identity != null ? identity.getDependencies() : null);
@@ -93,7 +101,7 @@ public class IncusContainerStore
getInstall().getStore().getHost().getStore().getOrStartSession());
var user = identity != null ? identity.unwrap().getUsername().retrieveUsername() : null;
var sc = new IncusCommandView(parent).exec(projectName, containerName, user, () -> {
var sc = view(parent).exec(containerName, user, () -> {
var state = getState();
var alpine = state.getOsName() != null
&& state.getOsName().toLowerCase().contains("alpine");
@@ -130,8 +138,8 @@ public class IncusContainerStore
private void refreshContainerState(ShellControl sc) throws Exception {
var state = getState();
var view = new IncusCommandView(sc);
var displayState = view.queryContainerState(projectName, containerName);
var view = view(sc);
var displayState = view.queryContainerState(containerName);
if (displayState.isEmpty()) {
return;
}
@@ -149,8 +157,8 @@ public class IncusContainerStore
@Override
public void start() throws Exception {
var sc = getInstall().getStore().getHost().getStore().getOrStartSession();
var view = new IncusCommandView(sc);
view.start(projectName, containerName);
var view = view();
view.start(containerName);
refreshContainerState(sc);
}
@@ -158,16 +166,16 @@ public class IncusContainerStore
public void stop() throws Exception {
stopSessionIfNeeded();
var sc = getInstall().getStore().getHost().getStore().getOrStartSession();
var view = new IncusCommandView(sc);
view.stop(projectName, containerName);
var view = view();
view.stop(containerName);
refreshContainerState(sc);
}
@Override
public void pause() throws Exception {
var sc = getInstall().getStore().getHost().getStore().getOrStartSession();
var view = new IncusCommandView(sc);
view.pause(projectName, containerName);
var view = view();
view.pause(containerName);
refreshContainerState(sc);
}