All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.metaeffekt.artifact.resolver.manager.execenv.EnvironmentManager Maven / Gradle / Ivy

There is a newer version: 0.134.0
Show newest version
/*
 * Copyright 2021-2024 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.metaeffekt.artifact.resolver.manager.execenv;

import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import io.fabric8.kubernetes.client.VersionInfo;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.metaeffekt.artifact.resolver.manager.execenv.exception.EnvironmentInitializationFailure;
import org.metaeffekt.core.container.control.kubernetesapi.NamespaceHandle;

import java.lang.reflect.InvocationTargetException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * An overarching controller for keeping environments alive for the liftime of the curent jvm.
 * 
* The point of this is to keep track of open environments, not opening hundreds and exhausting resources. *
* These can then be used by artifact resolvers. */ @Slf4j public class EnvironmentManager { /** * This will act as an LRU cache. */ private final LinkedHashMap environments = new LinkedHashMap<>(16, 0.75f, true); @Getter private final EnvironmentManagerConfig environmentManagerConfig; @Getter private final Config kubeconfig; @Getter private final NamespaceHandle namespaceHandle; /** * Creates a new object. * @param environmentManagerConfig configuration for this object to specify things such as maximum pod limits. */ public EnvironmentManager(EnvironmentManagerConfig environmentManagerConfig) { this.environmentManagerConfig = environmentManagerConfig; // TODO: get kubeconfig from environmentManagerConfig once supported this.kubeconfig = null; this.namespaceHandle = new NamespaceHandle(kubeconfig); log.info("Namespace [{}] created.", namespaceHandle.getName()); final AtomicBoolean gracefulClosureFinished = new AtomicBoolean(false); Runtime.getRuntime().addShutdownHook(new Thread(() -> { // we are closing. try to gracefully shut down all currently present environments log.info("Namespace [{}] shutdown hook called. Attempting gracefully shutdown of environments...", namespaceHandle.getName()); for (Map.Entry entry : environments.entrySet()) { // close all environments in parallel, deallocating their resources cleanly Thread newThread = new Thread(() -> { try { entry.getValue().close(); } catch (Exception e) { throw new RuntimeException("Shutdown couldn't gracefully close environment", e); } }); newThread.start(); } log.info("Namespace [{}] shutdown gracefully.", namespaceHandle.getName()); gracefulClosureFinished.set(true); })); Runtime.getRuntime().addShutdownHook(new Thread(() -> { try { for (int i = 0; i < 100; i++) { if (gracefulClosureFinished.get()) { break; } Thread.sleep(100); } } catch (InterruptedException e) { throw new RuntimeException(e); } if (!gracefulClosureFinished.get()) { log.info("Namespace [{}] shutdown timout. Killing environments...", namespaceHandle.getName()); } // kill the namespace, ungracefully closing leftover environments. should be dormant at jvm shutdown try { namespaceHandle.close(); } catch (Exception e) { throw new RuntimeException("Shutdown hook: got exception while closing namespace handle", e); } log.info("Namespace [{}] closed.", namespaceHandle.getName()); })); } private synchronized E constructNewEnvironment(EnvironmentParameters parameters) throws EnvironmentInitializationFailure { E execEnv; try { execEnv = parameters.getEnvironmentClass() .getDeclaredConstructor(Config.class, NamespaceHandle.class, parameters.getClass()) .newInstance(kubeconfig, namespaceHandle, parameters); environments.put( parameters, execEnv ); } catch (NoSuchMethodException e) { throw new IllegalStateException(e); } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { throw new EnvironmentInitializationFailure("Error while instantiating environment via reflection", e); } return execEnv; } /** * Tries to find a fitting environment implementation for the given parameters. *
* If an environment for these parameters does not exist, a new one will be constructed from the parameters * * @param parameters the parameters to look for * * @return an environment or null on failure to produce one. * * @throws EnvironmentInitializationFailure Exception thrown when initialization fails. */ public synchronized E getEnvironment(EnvironmentParameters parameters) throws EnvironmentInitializationFailure { // exit if we can't create environments at all if (environmentManagerConfig.getMaxEnvironments() == 0) { return null; } if (parameters == null) { return null; } // get environment or attempt instantiation, throwing away of an old environment if capacity is reached E execEnv = environments.get(parameters); if (execEnv == null) { log.debug("Creating new environment for parameters [{}].", parameters); if (environmentManagerConfig.getMaxEnvironments() != -1 && environments.size() >= environmentManagerConfig.getMaxEnvironments()) { // throw away the oldest environment Map.Entry oldest = environments .entrySet() .stream() .findFirst() .orElseThrow(() -> new IllegalStateException( "Impossible: should have at least one object in this situation.") ); try { environments.remove(oldest.getKey()).close(); } catch (Exception e) { // should not happen throw new RuntimeException("Environment closure failed unexpectedly", e); } } // create a new environment, add it to the manager and set execEnv for return execEnv = constructNewEnvironment(parameters); } return execEnv; } /** * Sends a query to the configured endpoint to check if kubernetes is available. * @param config kubernetes configuration to use for the request; nullable * @return true if available, false if not */ public static boolean checkKubernetesAvailable(Config config) { log.info("Checking kubernetes availability. May take a while if unavailable."); // check if kubernetes runtime is available try (KubernetesClient client = new KubernetesClientBuilder().withConfig(config).build()) { final VersionInfo versionInfo = client.getKubernetesVersion(); log.debug("Detected Kubernetes version: {}.{}", versionInfo.getMajor(), versionInfo.getMinor()); return true; } catch (Exception e) { log.debug("Check request failed. Kubernetes environment is not available.", e); return false; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy