Take different approach to finding CR.

Requires less privileges.
This commit is contained in:
Michael Lipp 2023-09-16 11:47:24 +02:00
parent 2210dbcae2
commit ea6751282c
2 changed files with 61 additions and 59 deletions

View file

@ -22,8 +22,10 @@ import com.google.gson.JsonObject;
import io.kubernetes.client.custom.Quantity; import io.kubernetes.client.custom.Quantity;
import io.kubernetes.client.custom.Quantity.Format; import io.kubernetes.client.custom.Quantity.Format;
import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.apis.ApiextensionsV1Api; import io.kubernetes.client.openapi.apis.ApisApi;
import io.kubernetes.client.openapi.models.V1CustomResourceDefinitionVersion; import io.kubernetes.client.openapi.apis.CustomObjectsApi;
import io.kubernetes.client.openapi.models.V1APIGroup;
import io.kubernetes.client.openapi.models.V1GroupVersionForDiscovery;
import io.kubernetes.client.util.Config; import io.kubernetes.client.util.Config;
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesApi; import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesApi;
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject; import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject;
@ -41,12 +43,13 @@ import org.jdrupes.vmoperator.runner.qemu.events.HotpluggableCpuStatus;
import org.jdrupes.vmoperator.runner.qemu.events.RunnerConfigurationUpdate; import org.jdrupes.vmoperator.runner.qemu.events.RunnerConfigurationUpdate;
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange; import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange;
import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State; import org.jdrupes.vmoperator.runner.qemu.events.RunnerStateChange.State;
import static org.jdrupes.vmoperator.util.Constants.VM_OP_CRD_NAME;
import static org.jdrupes.vmoperator.util.Constants.VM_OP_GROUP; import static org.jdrupes.vmoperator.util.Constants.VM_OP_GROUP;
import static org.jdrupes.vmoperator.util.Constants.VM_OP_KIND_VM;
import org.jdrupes.vmoperator.util.GsonPtr; import org.jdrupes.vmoperator.util.GsonPtr;
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;
import org.jgrapes.core.events.HandlingError;
import org.jgrapes.core.events.Start; import org.jgrapes.core.events.Start;
import org.jgrapes.util.events.ConfigurationUpdate; import org.jgrapes.util.events.ConfigurationUpdate;
import org.jgrapes.util.events.InitialConfiguration; import org.jgrapes.util.events.InitialConfiguration;
@ -73,6 +76,20 @@ public class StatusUpdater extends Component {
super(componentChannel); super(componentChannel);
} }
/**
* On handling error.
*
* @param event the event
*/
@Handler(channels = Channel.class)
public void onHandlingError(HandlingError event) {
if (event.throwable() instanceof ApiException exc) {
logger.log(Level.WARNING, exc,
() -> "Problem accessing kubernetes: " + exc.getResponseBody());
event.stop();
}
}
/** /**
* On configuration update. * On configuration update.
* *
@ -114,51 +131,40 @@ public class StatusUpdater extends Component {
* Handle the start event. * Handle the start event.
* *
* @param event the event * @param event the event
* @throws IOException
* @throws ApiException
*/ */
@Handler @Handler
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", @SuppressWarnings({ "PMD.DataflowAnomalyAnalysis",
"PMD.AvoidInstantiatingObjectsInLoops" }) "PMD.AvoidInstantiatingObjectsInLoops", "PMD.AvoidDuplicateLiterals" })
public void onStart(Start event) { public void onStart(Start event) throws IOException, ApiException {
try { var client = Config.defaultClient();
var client = Config.defaultClient(); var apis = new ApisApi(client).getAPIVersions();
var extsApi = new ApiextensionsV1Api(client); var crdVersions = apis.getGroups().stream()
var crds = extsApi.listCustomResourceDefinition(null, null, null, .filter(g -> g.getName().equals(VM_OP_GROUP)).findFirst()
"metadata.name=" + VM_OP_CRD_NAME, null, null, null, .map(V1APIGroup::getVersions).stream().flatMap(l -> l.stream())
null, null, null); .map(V1GroupVersionForDiscovery::getVersion).toList();
if (crds.getItems().isEmpty()) { var coa = new CustomObjectsApi(client);
logger.warning(() -> "CRD is unknown, status will not" for (var crdVersion : crdVersions) {
+ " be updated."); var crdApiRes = coa.getAPIResources(VM_OP_GROUP,
return; crdVersion).getResources().stream()
.filter(r -> VM_OP_KIND_VM.equals(r.getKind())).findFirst();
if (crdApiRes.isEmpty()) {
continue;
} }
var crd = crds.getItems().get(0); var crApi = new DynamicKubernetesApi(VM_OP_GROUP,
if (crd.getSpec().getVersions().stream() crdVersion, crdApiRes.get().getName(), client);
.filter(v -> v.getSubresources() == null).findAny() var vmCr = crApi.get(namespace, vmName).throwsApiException();
.isPresent()) { if (vmCr.isSuccess()) {
logger.warning(() -> "You are using an old version of the CRD," vmCrApi = crApi;
+ " status will not be updated."); observedGeneration
return; = vmCr.getObject().getMetadata().getGeneration();
break;
} }
var crdPlural = crd.getSpec().getNames().getPlural(); }
var vmOpApiVersions = crd.getSpec().getVersions().stream() if (vmCrApi == null) {
.map(V1CustomResourceDefinitionVersion::getName).toList(); logger.warning(() -> "Cannot find VM's CR, status will not"
for (var apiVer : vmOpApiVersions) { + " be updated.");
var api = new DynamicKubernetesApi(VM_OP_GROUP, apiVer,
crdPlural, client);
var res = api.get(namespace, vmName);
if (res.isSuccess()) {
vmCrApi = api;
observedGeneration
= res.getObject().getMetadata().getGeneration();
break;
}
}
if (vmCrApi == null) {
logger.warning(() -> "VM's CR is unknown, status will not"
+ " be updated.");
}
} catch (IOException | ApiException e) {
logger.log(Level.WARNING, e, () -> "Cannot access kubernetes: "
+ e.getMessage());
} }
} }
@ -189,9 +195,8 @@ public class StatusUpdater extends Component {
vmCrApi.updateStatus(vmCr, from -> { vmCrApi.updateStatus(vmCr, from -> {
JsonObject status = currentStatus(from); JsonObject status = currentStatus(from);
status.getAsJsonArray("conditions").asList().stream() status.getAsJsonArray("conditions").asList().stream()
.map(cond -> (JsonObject) cond) .map(cond -> (JsonObject) cond).filter(cond -> "Running"
.filter( .equals(cond.get("type").getAsString()))
cond -> "Running".equals(cond.get("type").getAsString()))
.forEach(cond -> cond.addProperty("observedGeneration", .forEach(cond -> cond.addProperty("observedGeneration",
from.getMetadata().getGeneration())); from.getMetadata().getGeneration()));
return status; return status;
@ -202,7 +207,7 @@ public class StatusUpdater extends Component {
* On runner state changed. * On runner state changed.
* *
* @param event the event * @param event the event
* @throws ApiException the api exception * @throws ApiException
*/ */
@Handler @Handler
public void onRunnerStateChanged(RunnerStateChange event) public void onRunnerStateChanged(RunnerStateChange event)
@ -210,8 +215,8 @@ public class StatusUpdater extends Component {
if (vmCrApi == null) { if (vmCrApi == null) {
return; return;
} }
var vmCr = vmCrApi.get(namespace, vmName) var vmCr = vmCrApi.get(namespace, vmName).throwsApiException()
.throwsApiException().getObject(); .getObject();
vmCrApi.updateStatus(vmCr, from -> { vmCrApi.updateStatus(vmCr, from -> {
JsonObject status = currentStatus(from); JsonObject status = currentStatus(from);
status.getAsJsonArray("conditions").asList().stream() status.getAsJsonArray("conditions").asList().stream()
@ -230,7 +235,7 @@ public class StatusUpdater extends Component {
status.addProperty("cpus", 0); status.addProperty("cpus", 0);
} }
return status; return status;
}); }).throwsApiException();
} }
private void updateRunningCondition(RunnerStateChange event, private void updateRunningCondition(RunnerStateChange event,
@ -266,15 +271,15 @@ public class StatusUpdater extends Component {
if (vmCrApi == null) { if (vmCrApi == null) {
return; return;
} }
var vmCr var vmCr = vmCrApi.get(namespace, vmName).throwsApiException()
= vmCrApi.get(namespace, vmName).throwsApiException().getObject(); .getObject();
vmCrApi.updateStatus(vmCr, from -> { vmCrApi.updateStatus(vmCr, from -> {
JsonObject status = currentStatus(from); JsonObject status = currentStatus(from);
status.addProperty("ram", status.addProperty("ram",
new Quantity(new BigDecimal(event.size()), Format.BINARY_SI) new Quantity(new BigDecimal(event.size()), Format.BINARY_SI)
.toSuffixedString()); .toSuffixedString());
return status; return status;
}); }).throwsApiException();
} }
/** /**
@ -288,12 +293,12 @@ public class StatusUpdater extends Component {
if (vmCrApi == null) { if (vmCrApi == null) {
return; return;
} }
var vmCr var vmCr = vmCrApi.get(namespace, vmName).throwsApiException()
= vmCrApi.get(namespace, vmName).throwsApiException().getObject(); .getObject();
vmCrApi.updateStatus(vmCr, from -> { vmCrApi.updateStatus(vmCr, from -> {
JsonObject status = currentStatus(from); JsonObject status = currentStatus(from);
status.addProperty("cpus", event.usedCpus().size()); status.addProperty("cpus", event.usedCpus().size());
return status; return status;
}); }).throwsApiException();
} }
} }

View file

@ -29,9 +29,6 @@ public class Constants {
/** The Constant VM_OP_GROUP. */ /** The Constant VM_OP_GROUP. */
public static final String VM_OP_GROUP = "vmoperator.jdrupes.org"; public static final String VM_OP_GROUP = "vmoperator.jdrupes.org";
/** The Constant VM_OP_CRD_NAME. */
public static final String VM_OP_CRD_NAME = "vms." + VM_OP_GROUP;
/** The Constant VM_OP_KIND_VM. */ /** The Constant VM_OP_KIND_VM. */
public static final String VM_OP_KIND_VM = "VirtualMachine"; public static final String VM_OP_KIND_VM = "VirtualMachine";
} }