mirror of
https://github.com/xpipe-io/xpipe.git
synced 2026-05-03 11:20:34 +00:00
Rework container shells
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
package io.xpipe.app.ext;
|
||||
|
||||
import io.xpipe.app.process.ShellControl;
|
||||
import io.xpipe.app.process.ShellDialect;
|
||||
import io.xpipe.app.process.ShellDialects;
|
||||
import io.xpipe.app.process.ShellStoreState;
|
||||
|
||||
import io.xpipe.core.OsType;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
@@ -16,10 +20,47 @@ import lombok.extern.jackson.Jacksonized;
|
||||
@Jacksonized
|
||||
public class ContainerStoreState extends ShellStoreState {
|
||||
|
||||
public static ShellDialect findSuitableDialect(ShellControl sc) throws Exception {
|
||||
if (!sc.getShellDialect().getDumbMode().supportsAnyPossibleInteraction()) {
|
||||
return sc.getOsType() == OsType.WINDOWS ? ShellDialects.CMD : ShellDialects.SH;
|
||||
}
|
||||
|
||||
if (sc.getOsType() != OsType.WINDOWS) {
|
||||
if (sc.view().findProgram("bash").isPresent()) {
|
||||
return ShellDialects.BASH;
|
||||
}
|
||||
|
||||
if (sc.view().findProgram("zsh").isPresent()) {
|
||||
return ShellDialects.ZSH;
|
||||
}
|
||||
|
||||
return ShellDialects.SH;
|
||||
} else {
|
||||
if (sc.view().findProgram("pwsh").isPresent()) {
|
||||
return ShellDialects.POWERSHELL_CORE;
|
||||
}
|
||||
|
||||
if (sc.view().findProgram("powershell").isPresent()) {
|
||||
return ShellDialects.POWERSHELL;
|
||||
}
|
||||
|
||||
return ShellDialects.CMD;
|
||||
}
|
||||
}
|
||||
|
||||
String imageName;
|
||||
String containerState;
|
||||
ShellDialect availableShellDialect;
|
||||
Boolean shellMissing;
|
||||
|
||||
public ShellDialect getEffectiveDialect(ShellControl sc) {
|
||||
if (availableShellDialect != null) {
|
||||
return availableShellDialect;
|
||||
}
|
||||
|
||||
return sc.getOsType() != OsType.WINDOWS ? ShellDialects.SH : ShellDialects.CMD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStoreState mergeCopy(DataStoreState newer) {
|
||||
var n = (ContainerStoreState) newer;
|
||||
@@ -32,6 +73,7 @@ public class ContainerStoreState extends ShellStoreState {
|
||||
super.mergeBuilder(css, b);
|
||||
b.containerState(useNewer(containerState, css.getContainerState()));
|
||||
b.imageName(useNewer(imageName, css.getImageName()));
|
||||
b.availableShellDialect(useNewer(availableShellDialect, css.getAvailableShellDialect()));
|
||||
b.shellMissing(useNewer(shellMissing, css.getShellMissing()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,8 @@ public class ServiceRefreshHubProvider
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(DataStoreEntryRef<FixedServiceCreatorStore> o) {
|
||||
return o.getStore().allowManualServicesRefresh();
|
||||
return o.getStore().allowManualServicesRefresh() && DataStorage.get().getStoreChildren(o.get()).stream()
|
||||
.noneMatch(e -> e.getStore() instanceof AbstractServiceGroupStore<?>);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class PodmanCommandView extends CommandViewBase {
|
||||
|
||||
@@ -23,6 +24,12 @@ public class PodmanCommandView extends CommandViewBase {
|
||||
}
|
||||
|
||||
private static <T extends Throwable> T convertException(T s) {
|
||||
if (s instanceof ProcessOutputException pex) {
|
||||
if (pex.getOutput().contains("OCI runtime exec failed")) {
|
||||
ErrorEventFactory.preconfigure(ErrorEventFactory.fromThrowable(s).description("Container does not contain a usable shell"));
|
||||
}
|
||||
}
|
||||
|
||||
return ErrorEventFactory.expectedIfContains(
|
||||
s,
|
||||
"Error: unable to connect to Podman.",
|
||||
@@ -124,20 +131,20 @@ public class PodmanCommandView extends CommandViewBase {
|
||||
}
|
||||
}
|
||||
|
||||
public ShellControl exec(String container) {
|
||||
public ShellControl exec(String container, Function<ShellControl, ShellDialect> dialect) {
|
||||
var sub = shellControl.subShell();
|
||||
sub.setDumbOpen(createOpenFunction(container, false));
|
||||
sub.setTerminalOpen(createOpenFunction(container, true));
|
||||
sub.setDumbOpen(createOpenFunction(container, dialect, false));
|
||||
sub.setTerminalOpen(createOpenFunction(container, dialect, true));
|
||||
return sub.withExceptionConverter(PodmanCommandView::convertException);
|
||||
}
|
||||
|
||||
private ShellOpenFunction createOpenFunction(String containerName, boolean terminal) {
|
||||
private ShellOpenFunction createOpenFunction(String containerName, Function<ShellControl, ShellDialect> dialect, boolean terminal) {
|
||||
return new ShellOpenFunction() {
|
||||
@Override
|
||||
public CommandBuilder prepareWithoutInitCommand() {
|
||||
return execCommand(terminal)
|
||||
.addQuoted(containerName)
|
||||
.add(ShellDialects.SH.getLaunchCommand().loginCommand());
|
||||
.add(sc -> dialect.apply(sc).getLaunchCommand().loginCommand());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -136,9 +136,17 @@ public class PodmanContainerStore
|
||||
@Override
|
||||
public ShellControl control(ShellControl parent) throws Exception {
|
||||
refreshContainerState(getCmd().getStore().getHost().getStore().getOrStartSession());
|
||||
var pc = new PodmanCommandView(parent).container().exec(containerName);
|
||||
var pc = new PodmanCommandView(parent).container().exec(containerName, getState()::getEffectiveDialect);
|
||||
pc.withSourceStore(PodmanContainerStore.this);
|
||||
pc.withShellStateInit(PodmanContainerStore.this);
|
||||
pc
|
||||
.onInit(sc -> {
|
||||
var s = getState();
|
||||
if (s.getAvailableShellDialect() == null) {
|
||||
setState(s.toBuilder().availableShellDialect(ContainerStoreState.findSuitableDialect(sc)).build());
|
||||
}
|
||||
sc.setOriginalShellDialect(s.getEffectiveDialect(sc));
|
||||
});
|
||||
pc.onStartupFail(throwable -> {
|
||||
if (throwable instanceof LicenseRequiredException) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user