Prepare release v3.4
This commit is contained in:
parent
31a3f79e2a
commit
54445ef531
12 changed files with 274 additions and 504 deletions
|
|
@ -27,14 +27,11 @@ import io.kubernetes.client.util.generic.options.ListOptions;
|
|||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
import org.jdrupes.vmoperator.common.K8s;
|
||||
import org.jdrupes.vmoperator.common.K8sClient;
|
||||
import org.jdrupes.vmoperator.common.K8sObserver;
|
||||
import org.jdrupes.vmoperator.common.K8sObserver.ResponseType;
|
||||
import org.jdrupes.vmoperator.manager.events.ChannelManager;
|
||||
import org.jdrupes.vmoperator.manager.events.Exit;
|
||||
import org.jgrapes.core.Channel;
|
||||
import org.jgrapes.core.Component;
|
||||
|
|
@ -45,7 +42,11 @@ import org.jgrapes.core.events.Stop;
|
|||
import org.jgrapes.util.events.ConfigurationUpdate;
|
||||
|
||||
/**
|
||||
* A base class for monitoring VM related resources.
|
||||
* A base class for monitoring VM related resources. When started,
|
||||
* it creates observers for all versions of the the {@link APIResource}
|
||||
* configured by {@link #context(APIResource)}. The APIResource is not
|
||||
* passed to the constructor because in some cases it has to be
|
||||
* evaluated lazily.
|
||||
*
|
||||
* @param <O> the object type for the context
|
||||
* @param <L> the object list type for the context
|
||||
|
|
@ -61,15 +62,17 @@ public abstract class AbstractMonitor<O extends KubernetesObject,
|
|||
private String namespace;
|
||||
private ListOptions options = new ListOptions();
|
||||
private final AtomicInteger observerCounter = new AtomicInteger(0);
|
||||
private ChannelManager<String, C, ?> channelManager;
|
||||
|
||||
/**
|
||||
* Initializes the instance.
|
||||
*
|
||||
* @param componentChannel the component channel
|
||||
* @param objectClass the class of the Kubernetes object to watch
|
||||
* @param objectListClass the class of the list of Kubernetes objects
|
||||
* to watch
|
||||
*/
|
||||
protected AbstractMonitor(Channel componentChannel, Class<O> objectClass,
|
||||
Class<L> objectListClass) {
|
||||
protected AbstractMonitor(Channel componentChannel,
|
||||
Class<O> objectClass, Class<L> objectListClass) {
|
||||
super(componentChannel);
|
||||
this.objectClass = objectClass;
|
||||
this.objectListClass = objectListClass;
|
||||
|
|
@ -155,27 +158,6 @@ public abstract class AbstractMonitor<O extends KubernetesObject,
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the channel manager.
|
||||
*
|
||||
* @return the context
|
||||
*/
|
||||
public ChannelManager<String, C, ?> channelManager() {
|
||||
return channelManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the channel manager.
|
||||
*
|
||||
* @param channelManager the channel manager
|
||||
* @return the abstract monitor
|
||||
*/
|
||||
public AbstractMonitor<O, L, C>
|
||||
channelManager(ChannelManager<String, C, ?> channelManager) {
|
||||
this.channelManager = channelManager;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for a key "namespace" in the configuration and, if found,
|
||||
* sets the namespace to its value.
|
||||
|
|
@ -193,7 +175,7 @@ public abstract class AbstractMonitor<O extends KubernetesObject,
|
|||
}
|
||||
|
||||
/**
|
||||
* Handle the start event. Configures the namespace invokes
|
||||
* Handle the start event. Configures the namespace, invokes
|
||||
* {@link #prepareMonitoring()} and starts the observers.
|
||||
*
|
||||
* @param event the event
|
||||
|
|
@ -239,9 +221,6 @@ public abstract class AbstractMonitor<O extends KubernetesObject,
|
|||
K8s.preferred(context, version), namespace, options)
|
||||
.handler((c, r) -> {
|
||||
handleChange(c, r);
|
||||
if (ResponseType.valueOf(r.type) == ResponseType.DELETED) {
|
||||
channelManager.remove(r.object.getMetadata().getName());
|
||||
}
|
||||
}).onTerminated((o, t) -> {
|
||||
if (observerCounter.decrementAndGet() == 0) {
|
||||
unregisterAsGenerator();
|
||||
|
|
@ -255,7 +234,8 @@ public abstract class AbstractMonitor<O extends KubernetesObject,
|
|||
|
||||
/**
|
||||
* Invoked by {@link #onStart(Start)} after the namespace has
|
||||
* been configured and before starting the observer.
|
||||
* been configured and before starting the observer. This is
|
||||
* the last opportunity to invoke {@link #context(APIResource)}.
|
||||
*
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
* @throws ApiException the api exception
|
||||
|
|
@ -272,14 +252,4 @@ public abstract class AbstractMonitor<O extends KubernetesObject,
|
|||
* @param change the change
|
||||
*/
|
||||
protected abstract void handleChange(K8sClient client, Response<O> change);
|
||||
|
||||
/**
|
||||
* Returns the {@link Channel} for the given name.
|
||||
*
|
||||
* @param name the name
|
||||
* @return the channel used for events related to the specified object
|
||||
*/
|
||||
protected Optional<C> channel(String name) {
|
||||
return channelManager.getChannel(name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,9 +100,8 @@ public class Controller extends Component {
|
|||
return null;
|
||||
}
|
||||
});
|
||||
attach(new VmMonitor(channel()).channelManager(chanMgr));
|
||||
attach(new DisplaySecretMonitor(channel())
|
||||
.channelManager(chanMgr.fixed()));
|
||||
attach(new VmMonitor(channel(), chanMgr));
|
||||
attach(new DisplaySecretMonitor(channel(), chanMgr));
|
||||
// Currently, we don't use the IP assigned by the load balancer
|
||||
// to access the VM's console. Might change in the future.
|
||||
// attach(new ServiceMonitor(channel()).channelManager(chanMgr));
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import org.jdrupes.vmoperator.common.K8sV1SecretStub;
|
|||
import static org.jdrupes.vmoperator.manager.Constants.COMP_DISPLAY_SECRET;
|
||||
import static org.jdrupes.vmoperator.manager.Constants.DATA_DISPLAY_PASSWORD;
|
||||
import static org.jdrupes.vmoperator.manager.Constants.DATA_PASSWORD_EXPIRY;
|
||||
import org.jdrupes.vmoperator.manager.events.ChannelDictionary;
|
||||
import org.jdrupes.vmoperator.manager.events.GetDisplayPassword;
|
||||
import org.jdrupes.vmoperator.manager.events.VmChannel;
|
||||
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
|
||||
|
|
@ -68,14 +69,18 @@ public class DisplaySecretMonitor
|
|||
private int passwordValidity = 10;
|
||||
private final List<PendingGet> pendingGets
|
||||
= Collections.synchronizedList(new LinkedList<>());
|
||||
private final ChannelDictionary<String, VmChannel, ?> channelDictionary;
|
||||
|
||||
/**
|
||||
* Instantiates a new display secrets monitor.
|
||||
*
|
||||
* @param componentChannel the component channel
|
||||
* @param channelDictionary the channel dictionary
|
||||
*/
|
||||
public DisplaySecretMonitor(Channel componentChannel) {
|
||||
public DisplaySecretMonitor(Channel componentChannel,
|
||||
ChannelDictionary<String, VmChannel, ?> channelDictionary) {
|
||||
super(componentChannel, V1Secret.class, V1SecretList.class);
|
||||
this.channelDictionary = channelDictionary;
|
||||
context(K8sV1SecretStub.CONTEXT);
|
||||
ListOptions options = new ListOptions();
|
||||
options.setLabelSelector("app.kubernetes.io/name=" + APP_NAME + ","
|
||||
|
|
@ -116,7 +121,7 @@ public class DisplaySecretMonitor
|
|||
if (vmName == null) {
|
||||
return;
|
||||
}
|
||||
var channel = channel(vmName).orElse(null);
|
||||
var channel = channelDictionary.channel(vmName).orElse(null);
|
||||
if (channel == null || channel.vmDefinition() == null) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -248,6 +253,7 @@ public class DisplaySecretMonitor
|
|||
* @param channel the channel
|
||||
*/
|
||||
@Handler
|
||||
@SuppressWarnings("PMD.AvoidSynchronizedStatement")
|
||||
public void onVmDefChanged(VmDefChanged event, Channel channel) {
|
||||
synchronized (pendingGets) {
|
||||
String vmName = event.vmDefinition().metadata().getName();
|
||||
|
|
|
|||
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
* VM-Operator
|
||||
* Copyright (C) 2024 Michael N. Lipp
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jdrupes.vmoperator.manager;
|
||||
|
||||
import io.kubernetes.client.openapi.ApiException;
|
||||
import io.kubernetes.client.openapi.models.V1Service;
|
||||
import io.kubernetes.client.openapi.models.V1ServiceList;
|
||||
import io.kubernetes.client.util.Watch.Response;
|
||||
import io.kubernetes.client.util.generic.options.ListOptions;
|
||||
import java.io.IOException;
|
||||
import static org.jdrupes.vmoperator.common.Constants.APP_NAME;
|
||||
import org.jdrupes.vmoperator.common.K8sClient;
|
||||
import org.jdrupes.vmoperator.common.K8sObserver.ResponseType;
|
||||
import org.jdrupes.vmoperator.common.K8sV1ServiceStub;
|
||||
import org.jdrupes.vmoperator.manager.events.ServiceChanged;
|
||||
import org.jdrupes.vmoperator.manager.events.VmChannel;
|
||||
import org.jgrapes.core.Channel;
|
||||
|
||||
/**
|
||||
* Watches for changes of services.
|
||||
*/
|
||||
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
|
||||
public class ServiceMonitor
|
||||
extends AbstractMonitor<V1Service, V1ServiceList, VmChannel> {
|
||||
|
||||
/**
|
||||
* Instantiates a new display secrets monitor.
|
||||
*
|
||||
* @param componentChannel the component channel
|
||||
*/
|
||||
public ServiceMonitor(Channel componentChannel) {
|
||||
super(componentChannel, V1Service.class, V1ServiceList.class);
|
||||
context(K8sV1ServiceStub.CONTEXT);
|
||||
ListOptions options = new ListOptions();
|
||||
options.setLabelSelector("app.kubernetes.io/name=" + APP_NAME);
|
||||
options(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareMonitoring() throws IOException, ApiException {
|
||||
client(new K8sClient());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleChange(K8sClient client, Response<V1Service> change) {
|
||||
String vmName = change.object.getMetadata().getLabels()
|
||||
.get("app.kubernetes.io/instance");
|
||||
if (vmName == null) {
|
||||
return;
|
||||
}
|
||||
var channel = channel(vmName).orElse(null);
|
||||
if (channel == null || channel.vmDefinition() == null) {
|
||||
return;
|
||||
}
|
||||
channel.pipeline().fire(new ServiceChanged(
|
||||
ResponseType.valueOf(change.type), change.object), channel);
|
||||
}
|
||||
}
|
||||
|
|
@ -43,10 +43,12 @@ import org.jdrupes.vmoperator.common.VmDefinitionStub;
|
|||
import static org.jdrupes.vmoperator.manager.Constants.APP_NAME;
|
||||
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_KIND_VM;
|
||||
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_NAME;
|
||||
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.GsonPtr;
|
||||
import org.jgrapes.core.Channel;
|
||||
import org.jgrapes.core.Event;
|
||||
|
||||
/**
|
||||
* Watches for changes of VM definitions.
|
||||
|
|
@ -55,14 +57,19 @@ import org.jgrapes.core.Channel;
|
|||
public class VmMonitor extends
|
||||
AbstractMonitor<VmDefinitionModel, VmDefinitionModels, VmChannel> {
|
||||
|
||||
private final ChannelManager<String, VmChannel, ?> channelManager;
|
||||
|
||||
/**
|
||||
* Instantiates a new VM definition watcher.
|
||||
*
|
||||
* @param componentChannel the component channel
|
||||
* @param channelManager the channel manager
|
||||
*/
|
||||
public VmMonitor(Channel componentChannel) {
|
||||
public VmMonitor(Channel componentChannel,
|
||||
ChannelManager<String, VmChannel, ?> channelManager) {
|
||||
super(componentChannel, VmDefinitionModel.class,
|
||||
VmDefinitionModels.class);
|
||||
this.channelManager = channelManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -107,10 +114,7 @@ public class VmMonitor extends
|
|||
protected void handleChange(K8sClient client,
|
||||
Watch.Response<VmDefinitionModel> response) {
|
||||
V1ObjectMeta metadata = response.object.getMetadata();
|
||||
VmChannel channel = channel(metadata.getName()).orElse(null);
|
||||
if (channel == null) {
|
||||
return;
|
||||
}
|
||||
VmChannel channel = channelManager.channelGet(metadata.getName());
|
||||
|
||||
// Get full definition and associate with channel as backup
|
||||
var vmDef = response.object;
|
||||
|
|
@ -132,13 +136,24 @@ public class VmMonitor extends
|
|||
() -> "Cannot get model for " + response.object.getMetadata());
|
||||
return;
|
||||
}
|
||||
if (ResponseType.valueOf(response.type) == ResponseType.DELETED) {
|
||||
channelManager.remove(metadata.getName());
|
||||
}
|
||||
|
||||
// Create and fire event
|
||||
// Create and fire changed event. Remove channel from channel
|
||||
// manager on completion.
|
||||
channel.pipeline()
|
||||
.fire(new VmDefChanged(ResponseType.valueOf(response.type),
|
||||
channel.setGeneration(
|
||||
response.object.getMetadata().getGeneration()),
|
||||
vmDef), channel);
|
||||
.fire(Event.onCompletion(
|
||||
new VmDefChanged(ResponseType.valueOf(response.type),
|
||||
channel.setGeneration(
|
||||
response.object.getMetadata().getGeneration()),
|
||||
vmDef),
|
||||
e -> {
|
||||
if (e.type() == ResponseType.DELETED) {
|
||||
channelManager
|
||||
.remove(e.vmDefinition().metadata().getName());
|
||||
}
|
||||
}), channel);
|
||||
}
|
||||
|
||||
private VmDefinitionModel getModel(K8sClient client,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue