Skip to content

Commit

Permalink
Adapt viewer to changes.
Browse files Browse the repository at this point in the history
  • Loading branch information
mnlipp committed Nov 2, 2024
1 parent 4e702e8 commit 7e456c5
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 72 deletions.
12 changes: 6 additions & 6 deletions dev-example/test-vm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ spec:
pullPolicy: Always

permissions:
- user: admin
may:
- "*"
- user: test
may:
- "accessConsole"
- user: admin
may:
- "*"
- user: test
may:
- "accessConsole"

resources:
requests:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@

import io.kubernetes.client.openapi.models.V1ObjectMeta;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.jdrupes.vmoperator.util.DataPath;

/**
Expand Down Expand Up @@ -273,19 +274,17 @@ public RequestedVmState vmState() {
*/
public Set<Permission> permissionsFor(String user,
Collection<String> roles) {
// TODO
return null;
// return GsonPtr.to(data())
// .getAsListOf(JsonObject.class, "spec", "permissions")
// .stream().filter(p -> GsonPtr.to(p).getAsString("user")
// .map(u -> u.equals(user)).orElse(false)
// || GsonPtr.to(p).getAsString("role").map(roles::contains)
// .orElse(false))
// .map(p -> GsonPtr.to(p).getAsListOf(JsonPrimitive.class, "may")
// .stream())
// .flatMap(Function.identity()).map(p -> p.getAsString())
// .map(Permission::parse).map(Set::stream)
// .flatMap(Function.identity()).collect(Collectors.toSet());
return DataPath
.<List<Map<String, Object>>> get(this, "spec", "permissions")
.orElse(Collections.emptyList()).stream()
.filter(p -> DataPath.get(p, "user").map(u -> u.equals(user))
.orElse(false)
|| DataPath.get(p, "role").map(roles::contains).orElse(false))
.map(p -> DataPath.<List<String>> get(p, "may")
.orElse(Collections.emptyList()).stream())
.flatMap(Function.identity())
.map(Permission::parse).map(Set::stream)
.flatMap(Function.identity()).collect(Collectors.toSet());
}

/**
Expand All @@ -294,10 +293,7 @@ public Set<Permission> permissionsFor(String user,
* @return the optional
*/
public Optional<Long> displayPasswordSerial() {
// TODO
return null;
// return GsonPtr.to(status())
// .get(JsonPrimitive.class, "displayPasswordSerial")
// .map(JsonPrimitive::getAsLong);
return DataPath.<Number> get(status(), "displayPasswordSerial")
.map(Number::longValue);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
package org.jdrupes.vmoperator.manager.events;

import java.util.Optional;
import org.jdrupes.vmoperator.common.VmDefinitionModel;
import org.jdrupes.vmoperator.common.VmDefinition;
import org.jgrapes.core.Event;

/**
Expand All @@ -28,14 +28,14 @@
@SuppressWarnings("PMD.DataClass")
public class GetDisplayPassword extends Event<String> {

private final VmDefinitionModel vmDef;
private final VmDefinition vmDef;

/**
* Instantiates a new returns the display secret.
*
* @param vmDef the vm name
*/
public GetDisplayPassword(VmDefinitionModel vmDef) {
public GetDisplayPassword(VmDefinition vmDef) {
this.vmDef = vmDef;
}

Expand All @@ -44,7 +44,7 @@ public GetDisplayPassword(VmDefinitionModel vmDef) {
*
* @return the vm definition
*/
public VmDefinitionModel vmDefinition() {
public VmDefinition vmDefinition() {
return vmDef;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ protected void handleChange(K8sClient client, Response<V1Secret> change) {
return;
}
var channel = channelDictionary.channel(vmName).orElse(null);
if (channel == null || channel.vmModel() == null) {
if (channel == null || channel.vmDefinition() == null) {
return;
}

Expand Down Expand Up @@ -256,11 +256,10 @@ private boolean stillValid(String expiry) {
@SuppressWarnings("PMD.AvoidSynchronizedStatement")
public void onVmDefChanged(VmDefChanged event, Channel channel) {
synchronized (pendingGets) {
String vmName = event.vmModel().metadata().getName();
String vmName = event.vmDefinition().name();
for (var pending : pendingGets) {
if (pending.event.vmDefinition().metadata().getName()
.equals(vmName)
&& event.vmModel().displayPasswordSerial()
if (pending.event.vmDefinition().name().equals(vmName)
&& event.vmDefinition().displayPasswordSerial()
.map(s -> s >= pending.expectedSerial).orElse(false)) {
pending.lock.remove();
// pending will be removed from pendingGest by
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
import io.kubernetes.client.util.Watch;
import io.kubernetes.client.util.generic.options.ListOptions;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
Expand All @@ -47,6 +51,7 @@
import org.jdrupes.vmoperator.manager.events.ChannelManager;
import org.jdrupes.vmoperator.manager.events.VmChannel;
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
import org.jdrupes.vmoperator.util.DataPath;
import org.jdrupes.vmoperator.util.GsonPtr;
import org.jgrapes.core.Channel;
import org.jgrapes.core.Event;
Expand Down Expand Up @@ -176,12 +181,58 @@ private VmDefinitionModel getModel(K8sClient client,
}
}

private void addDynamicData(K8sClient client, VmDefinition vmState,
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
private void addDynamicData(K8sClient client, VmDefinition vmDef,
VmDefinition prevState) {
// Maintain (or initialize) the resetCount
vmState.extra("resetCount",
vmDef.extra("resetCount",
Optional.ofNullable(prevState).map(d -> d.extra("resetCount"))
.orElse(0L));

// Node information
// Add defaults in case the VM is not running
vmDef.extra("nodeName", "");
vmDef.extra("nodeAddress", "");

// VM definition status changes before the pod terminates.
// This results in pod information being shown for a stopped
// VM which is irritating. So check condition first.
@SuppressWarnings("PMD.LambdaCanBeMethodReference")
var isRunning = DataPath
.<List<Map<String, Object>>> get(vmDef.status(), "conditions")
.orElse(Collections.emptyList()).stream()
.filter(cond -> DataPath.get(cond, "type")
.map(t -> "Running".equals(t)).orElse(false))
.findFirst().map(cond -> DataPath.get(cond, "status")
.map(s -> "True".equals(s)).orElse(false))
.orElse(false);
if (!isRunning) {
return;
}
var podSearch = new ListOptions();
podSearch.setLabelSelector("app.kubernetes.io/name=" + APP_NAME
+ ",app.kubernetes.io/component=" + APP_NAME
+ ",app.kubernetes.io/instance=" + vmDef.name());
try {
var podList
= K8sV1PodStub.list(client, namespace(), podSearch);
for (var podStub : podList) {
var nodeName = podStub.model().get().getSpec().getNodeName();
vmDef.extra("nodeName", nodeName);
logger.fine(() -> "Added node name " + nodeName
+ " to VM info for " + vmDef.name());
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
var addrs = new ArrayList<String>();
podStub.model().get().getStatus().getPodIPs().stream()
.map(ip -> ip.getIp()).forEach(addrs::add);
vmDef.extra("nodeAddresses", addrs);
logger.fine(() -> "Added node addresses " + addrs
+ " to VM info for " + vmDef.name());
}
} catch (ApiException e) {
logger.log(Level.WARNING, e,
() -> "Cannot access node information: " + e.getMessage());
}
}

@Deprecated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSyntaxException;
import freemarker.core.ParseException;
import freemarker.template.MalformedTemplateNameException;
Expand All @@ -48,17 +46,16 @@
import java.util.Set;
import java.util.logging.Level;
import org.bouncycastle.util.Objects;
import org.jdrupes.vmoperator.common.K8sDynamicModel;
import org.jdrupes.vmoperator.common.K8sObserver;
import org.jdrupes.vmoperator.common.VmDefinitionModel;
import org.jdrupes.vmoperator.common.VmDefinitionModel.Permission;
import org.jdrupes.vmoperator.common.VmDefinition;
import org.jdrupes.vmoperator.common.VmDefinition.Permission;
import org.jdrupes.vmoperator.manager.events.ChannelTracker;
import org.jdrupes.vmoperator.manager.events.GetDisplayPassword;
import org.jdrupes.vmoperator.manager.events.ModifyVm;
import org.jdrupes.vmoperator.manager.events.ResetVm;
import org.jdrupes.vmoperator.manager.events.VmChannel;
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
import org.jdrupes.vmoperator.util.GsonPtr;
import org.jdrupes.vmoperator.util.DataPath;
import org.jgrapes.core.Channel;
import org.jgrapes.core.Components;
import org.jgrapes.core.Event;
Expand Down Expand Up @@ -122,7 +119,7 @@ public class VmViewer extends FreeMarkerConlet<VmViewer.ViewerModel> {
private static final Set<RenderMode> MODES_FOR_GENERATED = RenderMode.asSet(
RenderMode.Preview, RenderMode.StickyPreview);
private final ChannelTracker<String, VmChannel,
VmDefinitionModel> channelTracker = new ChannelTracker<>();
VmDefinition> channelTracker = new ChannelTracker<>();
private static ObjectMapper objectMapper
= new ObjectMapper().registerModule(new JavaTimeModule());
private Class<?> preferredIpVersion = Inet4Address.class;
Expand Down Expand Up @@ -399,8 +396,7 @@ private List<String> accessibleVms(ConsoleConnection channel) {
.map(d -> d.getMetadata().getName()).sorted().toList();
}

private Set<Permission> permissions(VmDefinitionModel vmDef,
Session session) {
private Set<Permission> permissions(VmDefinition vmDef, Session session) {
var user = WebConsoleUtils.userFromSession(session)
.map(ConsoleUser::getName).orElse(null);
var roles = WebConsoleUtils.rolesFromSession(session)
Expand All @@ -421,15 +417,16 @@ private void updateVmDef(ConsoleConnection channel, ViewerModel model) {
channelTracker.value(model.vmName()).ifPresent(item -> {
try {
var vmDef = item.associated();
@SuppressWarnings("unchecked")
var def = (Map<String, Object>) item.channel().client()
.getJSON().getGson()
.fromJson(vmDef.data().toString(), Map.class);
def.put("userPermissions",
var data = Map.of("metadata",
Map.of("namespace", vmDef.namespace(),
"name", vmDef.name()),
"spec", vmDef.spec(),
"status", vmDef.getStatus(),
"userPermissions",
permissions(vmDef, channel.session()).stream()
.map(Permission::toString).toList());
channel.respond(new NotifyConletView(type(),
model.getConletId(), "updateVmDefinition", def));
model.getConletId(), "updateVmDefinition", data));
} catch (JsonSyntaxException e) {
logger.log(Level.SEVERE, e,
() -> "Failed to serialize VM definition");
Expand Down Expand Up @@ -461,11 +458,8 @@ protected void doConletDeleted(ConletDeleted event,
"PMD.ConfusingArgumentToVarargsMethod" })
public void onVmDefChanged(VmDefChanged event, VmChannel channel)
throws IOException {
var vmDef = new VmDefinitionModel(channel.client().getJSON()
.getGson(), event.vmModel().data());
GsonPtr.to(vmDef.data()).to("metadata").get(JsonObject.class)
.remove("managedFields");
var vmName = vmDef.getMetadata().getName();
var vmDef = event.vmDefinition();
var vmName = vmDef.name();
if (event.type() == K8sObserver.ResponseType.DELETED) {
channelTracker.remove(vmName);
} else {
Expand Down Expand Up @@ -567,27 +561,28 @@ private void openConsole(String vmName, ConsoleConnection connection,
logger.severe(() -> "Failed to find display IP for " + vmName);
return;
}
var port = GsonPtr.to(vmDef.data()).get(JsonPrimitive.class, "spec",
"vm", "display", "spice", "port");
var port = DataPath
.<Number> get(vmDef.spec(), "vm", "display", "spice", "port")
.map(Number::longValue);
if (port.isEmpty()) {
logger.severe(() -> "No port defined for display of " + vmName);
return;
}
var proxyUrl = GsonPtr.to(vmDef.data()).get(JsonPrimitive.class, "spec",
"vm", "display", "spice", "proxyUrl");
StringBuffer data = new StringBuffer(100)
.append("[virt-viewer]\ntype=spice\nhost=")
.append(addr.get().getHostAddress()).append("\nport=")
.append(Integer.toString(port.get().getAsInt()))
.append(port.get().toString())
.append('\n');
if (password != null) {
data.append("password=").append(password).append('\n');
}
proxyUrl.map(JsonPrimitive::getAsString).ifPresent(u -> {
if (!Strings.isNullOrEmpty(u)) {
data.append("proxy=").append(u).append('\n');
}
});
DataPath
.<String> get(vmDef.spec(), "vm", "display", "spice", "proxyUrl")
.ifPresent(u -> {
if (!Strings.isNullOrEmpty(u)) {
data.append("proxy=").append(u).append('\n');
}
});
if (deleteConnectionFile) {
data.append("delete-this-file=1\n");
}
Expand All @@ -596,11 +591,11 @@ private void openConsole(String vmName, ConsoleConnection connection,
Base64.getEncoder().encodeToString(data.toString().getBytes())));
}

private Optional<InetAddress> displayIp(K8sDynamicModel vmDef) {
var server = GsonPtr.to(vmDef.data()).get(JsonPrimitive.class, "spec",
private Optional<InetAddress> displayIp(VmDefinition vmDef) {
Optional<String> server = DataPath.get(vmDef.spec(),
"vm", "display", "spice", "server");
if (server.isPresent()) {
var srv = server.get().getAsString();
var srv = server.get();
try {
var addr = InetAddress.getByName(srv);
logger.fine(() -> "Using IP address from CRD for "
Expand All @@ -612,8 +607,8 @@ private Optional<InetAddress> displayIp(K8sDynamicModel vmDef) {
return Optional.empty();
}
}
var addrs = GsonPtr.to(vmDef.data()).getAsListOf(JsonPrimitive.class,
"nodeAddresses").stream().map(JsonPrimitive::getAsString)
var addrs = Optional.<List<String>> ofNullable(vmDef
.extra("nodeAddresses")).orElse(Collections.emptyList()).stream()
.map(a -> {
try {
return InetAddress.getByName(a);
Expand All @@ -623,7 +618,7 @@ private Optional<InetAddress> displayIp(K8sDynamicModel vmDef) {
}
}).filter(a -> a != null).toList();
logger.fine(() -> "Known IP addresses for "
+ vmDef.getMetadata().getName() + ": " + addrs);
+ vmDef.name() + ": " + addrs);
return addrs.stream()
.filter(a -> preferredIpVersion.isAssignableFrom(a.getClass()))
.findFirst().or(() -> addrs.stream().findFirst());
Expand Down

0 comments on commit 7e456c5

Please sign in to comment.