Choose suitable API.

This commit is contained in:
Michael Lipp 2023-07-23 17:13:58 +02:00
parent ddf412302b
commit f1b1b2c059
6 changed files with 141 additions and 52 deletions

View file

@ -0,0 +1,30 @@
/*
* VM-Operator
* Copyright (C) 2023 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;
/**
* Some constants.
*/
public class Constants {
static final String VM_OP_GROUP = "vmoperator.jdrupes.org";
static final String VM_OP_VERSION = "v1";
static final String VM_OP_KIND_VM = "VirtualMachine";
}

View file

@ -47,7 +47,7 @@ public class Manager extends Component {
public Manager() throws IOException { public Manager() throws IOException {
// Prepare component tree // Prepare component tree
attach(new NioDispatcher()); attach(new NioDispatcher());
attach(new VmDefinitionWatcher(channel())); attach(new VmWatcher(channel()));
attach(new Reconciler(channel())); attach(new Reconciler(channel()));
} }

View file

@ -1,18 +1,80 @@
/*
* VM-Operator
* Copyright (C) 2023 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; package org.jdrupes.vmoperator.manager;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesApi;
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_GROUP;
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_VERSION;
import org.jgrapes.core.Channel; import org.jgrapes.core.Channel;
import org.jgrapes.core.Component; import org.jgrapes.core.Component;
import org.jgrapes.core.annotation.Handler; import org.jgrapes.core.annotation.Handler;
/**
* Adapts Kubenetes resources to changes in VM definitions (CRs).
*/
public class Reconciler extends Component { public class Reconciler extends Component {
/**
* Instantiates a new reconciler.
*
* @param componentChannel the component channel
*/
public Reconciler(Channel componentChannel) { public Reconciler(Channel componentChannel) {
super(componentChannel); super(componentChannel);
} }
/**
* Handles the change event.
*
* @param event the event
* @param channel the channel
* @throws ApiException the api exception
*/
@Handler @Handler
public void onVmChanged(VmChangedEvent event, WatchChannel channel) { public void onVmDefChanged(VmDefChanged event, WatchChannel channel)
throws ApiException {
DynamicKubernetesApi vmDefApi = new DynamicKubernetesApi(VM_OP_GROUP,
VM_OP_VERSION, event.crd().getName(), channel.client());
var defMeta = event.metadata();
var vmDef = vmDefApi.get(defMeta.getNamespace(), defMeta.getName());
// DynamicKubernetesApi cmApi = new DynamicKubernetesApi("", "v1",
// "configmaps", channel.client());
// var cm = new DynamicKubernetesObject();
// cm.setApiVersion("v1");
// cm.setKind("ConfigMap");
// V1ObjectMeta metadata = new V1ObjectMeta();
// metadata.setNamespace("default");
// metadata.setName("test");
// cm.setMetadata(metadata);
// JsonObject data = new JsonObject();
// data.addProperty("test", "value");
// cm.getRaw().add("data", data);
//
// var response = cmApi.create("default", cm, new CreateOptions())
// .throwsApiException();
// var obj = channel.coa().getNamespacedCustomObject(VM_OP_GROUP, VM_OP_VERSION,
// md.getNamespace(), event.crd().getName(), md.getName());
event = null; event = null;
} }
} }

View file

@ -18,8 +18,8 @@
package org.jdrupes.vmoperator.manager; package org.jdrupes.vmoperator.manager;
import io.kubernetes.client.openapi.models.V1APIResource;
import io.kubernetes.client.openapi.models.V1ObjectMeta; import io.kubernetes.client.openapi.models.V1ObjectMeta;
import org.jgrapes.core.Channel; import org.jgrapes.core.Channel;
import org.jgrapes.core.Components; import org.jgrapes.core.Components;
import org.jgrapes.core.Event; import org.jgrapes.core.Event;
@ -27,7 +27,7 @@ import org.jgrapes.core.Event;
/** /**
* Indicates a change in a VM definition. * Indicates a change in a VM definition.
*/ */
public class VmChangedEvent extends Event<Void> { public class VmDefChanged extends Event<Void> {
/** /**
* The type of change. * The type of change.
@ -37,6 +37,7 @@ public class VmChangedEvent extends Event<Void> {
} }
private final Type type; private final Type type;
private final V1APIResource crd;
private final V1ObjectMeta metadata; private final V1ObjectMeta metadata;
/** /**
@ -45,8 +46,9 @@ public class VmChangedEvent extends Event<Void> {
* @param type the type * @param type the type
* @param metadata the metadata * @param metadata the metadata
*/ */
public VmChangedEvent(Type type, V1ObjectMeta metadata) { public VmDefChanged(Type type, V1APIResource crd, V1ObjectMeta metadata) {
this.type = type; this.type = type;
this.crd = crd;
this.metadata = metadata; this.metadata = metadata;
} }
@ -59,6 +61,15 @@ public class VmChangedEvent extends Event<Void> {
return type; return type;
} }
/**
* Returns the Crd.
*
* @return the v 1 API resource
*/
public V1APIResource crd() {
return crd;
}
/** /**
* Returns the metadata. * Returns the metadata.
* *

View file

@ -22,7 +22,6 @@ import com.google.gson.reflect.TypeToken;
import io.kubernetes.client.openapi.ApiClient; import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.Configuration; import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.apis.CustomObjectsApi; import io.kubernetes.client.openapi.apis.CustomObjectsApi;
import io.kubernetes.client.openapi.models.V1APIResource; import io.kubernetes.client.openapi.models.V1APIResource;
import io.kubernetes.client.openapi.models.V1Namespace; import io.kubernetes.client.openapi.models.V1Namespace;
@ -34,7 +33,9 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level; import java.util.logging.Level;
import okhttp3.Call; import okhttp3.Call;
import org.jdrupes.vmoperator.manager.VmChangedEvent.Type; import static org.jdrupes.vmoperator.manager.Constants.VM_OP_GROUP;
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_VERSION;
import org.jdrupes.vmoperator.manager.VmDefChanged.Type;
import org.jgrapes.core.Channel; import org.jgrapes.core.Channel;
import org.jgrapes.core.Component; import org.jgrapes.core.Component;
import org.jgrapes.core.annotation.Handler; import org.jgrapes.core.annotation.Handler;
@ -45,13 +46,9 @@ import org.jgrapes.core.events.Stop;
* Watches for changes of VM definitions. * Watches for changes of VM definitions.
*/ */
@SuppressWarnings("PMD.DataflowAnomalyAnalysis") @SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class VmDefinitionWatcher extends Component { public class VmWatcher extends Component {
private static final String CR_GROUP = "vmoperator.jdrupes.org"; private ApiClient client;
private static final String CR_VERSION = "v1";
private static final String CR_KIND = "VirtualMachine";
private CoreV1Api api;
private CustomObjectsApi coa;
private V1APIResource vmsCrd; private V1APIResource vmsCrd;
private String managedNamespace = "default"; private String managedNamespace = "default";
private final Map<String, WatchChannel> channels private final Map<String, WatchChannel> channels
@ -62,7 +59,7 @@ public class VmDefinitionWatcher extends Component {
* *
* @param componentChannel the component channel * @param componentChannel the component channel
*/ */
public VmDefinitionWatcher(Channel componentChannel) { public VmWatcher(Channel componentChannel) {
super(componentChannel); super(componentChannel);
} }
@ -75,44 +72,44 @@ public class VmDefinitionWatcher extends Component {
*/ */
@Handler @Handler
public void onStart(Start event) throws IOException, ApiException { public void onStart(Start event) throws IOException, ApiException {
ApiClient client = Config.defaultClient(); client = Config.defaultClient();
Configuration.setDefaultApiClient(client); Configuration.setDefaultApiClient(client);
// Get access to APIs // Get access to API
api = new CoreV1Api(); var coa = new CustomObjectsApi(client);
coa = new CustomObjectsApi();
// Derive all information from the CRD // Derive all information from the CRD
var resources = coa.getAPIResources(CR_GROUP, CR_VERSION); var resources
= coa.getAPIResources(VM_OP_GROUP, VM_OP_VERSION);
vmsCrd = resources.getResources().stream() vmsCrd = resources.getResources().stream()
.filter(r -> CR_KIND.equals(r.getKind())).findFirst().get(); .filter(r -> Constants.VM_OP_KIND_VM.equals(r.getKind()))
.findFirst().get();
// Watch the resources (vm definitions) // Watch the resources (vm definitions)
Call call = coa.listNamespacedCustomObjectCall( Call call = coa.listNamespacedCustomObjectCall(
CR_GROUP, CR_VERSION, managedNamespace, vmsCrd.getName(), null, VM_OP_GROUP, VM_OP_VERSION, managedNamespace, vmsCrd.getName(),
false, null, null, null, null, null, null, null, true, null); null, false, null, null, null, null, null, null, null, true, null);
new Thread(() -> { new Thread(() -> {
try (Watch<V1Namespace> watch = Watch.createWatch(client, try (Watch<V1Namespace> watch = Watch.createWatch(client, call,
call, new TypeToken<Watch.Response<V1Namespace>>() { new TypeToken<Watch.Response<V1Namespace>>() {
}.getType())) { }.getType())) {
for (Watch.Response<V1Namespace> item : watch) { for (Watch.Response<V1Namespace> item : watch) {
handleCrEvent(item); handleVmDefinitionEvent(item);
} }
} catch (IOException | ApiException e) { } catch (IOException | ApiException e) {
logger.log(Level.FINE, e, () -> "Probem while watching: " logger.log(Level.FINE, e, () -> "Probem while watching: "
+ e.getMessage()); + e.getMessage());
} }
fire(new Stop()); fire(new Stop());
}).start(); }).start();
} }
private void handleCrEvent(Watch.Response<V1Namespace> item) { private void handleVmDefinitionEvent(Watch.Response<V1Namespace> item) {
V1ObjectMeta metadata = item.object.getMetadata(); V1ObjectMeta metadata = item.object.getMetadata();
WatchChannel channel = channels.computeIfAbsent(metadata.getName(), WatchChannel channel = channels.computeIfAbsent(metadata.getName(),
k -> new WatchChannel(channel(), newEventPipeline(), api, coa)); k -> new WatchChannel(channel(), newEventPipeline(), client));
channel.pipeline().fire(new VmChangedEvent( channel.pipeline().fire(new VmDefChanged(
VmChangedEvent.Type.valueOf(item.type), metadata), channel); VmDefChanged.Type.valueOf(item.type), vmsCrd, metadata), channel);
} }
/** /**
@ -122,7 +119,7 @@ public class VmDefinitionWatcher extends Component {
* @param channel the channel * @param channel the channel
*/ */
@Handler(priority = -10_000) @Handler(priority = -10_000)
public void onVmChanged(VmChangedEvent event, WatchChannel channel) { public void onVmDefChanged(VmDefChanged event, WatchChannel channel) {
if (event.type() == Type.DELETED) { if (event.type() == Type.DELETED) {
channels.remove(event.metadata().getName()); channels.remove(event.metadata().getName());
} }

View file

@ -18,8 +18,8 @@
package org.jdrupes.vmoperator.manager; package org.jdrupes.vmoperator.manager;
import io.kubernetes.client.openapi.apis.CoreV1Api; import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.apis.CustomObjectsApi;
import org.jgrapes.core.Channel; import org.jgrapes.core.Channel;
import org.jgrapes.core.EventPipeline; import org.jgrapes.core.EventPipeline;
import org.jgrapes.core.Subchannel.DefaultSubchannel; import org.jgrapes.core.Subchannel.DefaultSubchannel;
@ -31,21 +31,20 @@ import org.jgrapes.core.Subchannel.DefaultSubchannel;
public class WatchChannel extends DefaultSubchannel { public class WatchChannel extends DefaultSubchannel {
private final EventPipeline pipeline; private final EventPipeline pipeline;
private final CoreV1Api api; private final ApiClient client;
private final CustomObjectsApi coa;
/** /**
* Instantiates a new watch channel. * Instantiates a new watch channel.
* *
* @param mainChannel the main channel * @param mainChannel the main channel
* @param pipeline the pipeline * @param pipeline the pipeline
* @param client
*/ */
public WatchChannel(Channel mainChannel, EventPipeline pipeline, public WatchChannel(Channel mainChannel, EventPipeline pipeline,
CoreV1Api api, CustomObjectsApi coa) { ApiClient client) {
super(mainChannel); super(mainChannel);
this.pipeline = pipeline; this.pipeline = pipeline;
this.api = api; this.client = client;
this.coa = coa;
} }
/** /**
@ -58,21 +57,11 @@ public class WatchChannel extends DefaultSubchannel {
} }
/** /**
* Returns the API object for invoking kubernetes functions. * Returns the API client.
* *
* @return the API object * @return the API client
*/ */
public CoreV1Api api() { public ApiClient client() {
return api; return client;
}
/**
* Returns the API object for invoking kubernetes custom object
* functions.
*
* @return the API object
*/
public CustomObjectsApi coa() {
return coa;
} }
} }