dev.galasa.kubernetes.internal.KubernetesManagerImpl Maven / Gradle / Ivy
/*
* Licensed Materials - Property of IBM
*
* (c) Copyright IBM Corp. 2020.
*/
package dev.galasa.kubernetes.internal;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import javax.validation.constraints.NotNull;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.service.component.annotations.Component;
import dev.galasa.ManagerException;
import dev.galasa.framework.spi.AbstractManager;
import dev.galasa.framework.spi.ConfigurationPropertyStoreException;
import dev.galasa.framework.spi.DynamicStatusStoreException;
import dev.galasa.framework.spi.FrameworkException;
import dev.galasa.framework.spi.GenerateAnnotatedField;
import dev.galasa.framework.spi.IDynamicStatusStoreService;
import dev.galasa.framework.spi.IFramework;
import dev.galasa.framework.spi.IManager;
import dev.galasa.framework.spi.IRun;
import dev.galasa.framework.spi.ResourceUnavailableException;
import dev.galasa.framework.spi.SharedEnvironmentRunType;
import dev.galasa.kubernetes.IKubernetesNamespace;
import dev.galasa.kubernetes.KubernetesManagerException;
import dev.galasa.kubernetes.KubernetesNamespace;
import dev.galasa.kubernetes.internal.properties.KubernetesClusters;
import dev.galasa.kubernetes.internal.properties.KubernetesNamespaceTagSharedEnvironment;
import dev.galasa.kubernetes.internal.properties.KubernetesPropertiesSingleton;
import dev.galasa.kubernetes.spi.IKubernetesManagerSpi;
import io.kubernetes.client.openapi.models.V1ConfigMap;
import io.kubernetes.client.openapi.models.V1Deployment;
import io.kubernetes.client.openapi.models.V1PersistentVolumeClaim;
import io.kubernetes.client.openapi.models.V1Secret;
import io.kubernetes.client.openapi.models.V1Service;
import io.kubernetes.client.openapi.models.V1StatefulSet;
import io.kubernetes.client.util.Yaml;
/**
* The Kubernetes Manager implementation
*
* @author Michael Baylis
*
*/
@Component(service = { IManager.class })
public class KubernetesManagerImpl extends AbstractManager implements IKubernetesManagerSpi {
protected final static String NAMESPACE = "kubernetes";
private final Log logger = LogFactory.getLog(KubernetesManagerImpl.class);
private IDynamicStatusStoreService dss;
private HashMap taggedNamespaces = new HashMap<>();
private HashSet sharedEnvironmentNamespacesTags = new HashSet<>();
private HashMap clusters = new HashMap<>();
private boolean required = false;
/**
* Initialise the Kubernetes Manager if the Kubernetes TPI is included in the test class
*/
@Override
public void initialise(@NotNull IFramework framework,
@NotNull List allManagers,
@NotNull List activeManagers,
@NotNull Class> testClass) throws ManagerException {
super.initialise(framework, allManagers, activeManagers, testClass);
//*** Check to see if we are needed
if (!required) {
for(Field field : testClass.getFields()) {
if (field.getType() == IKubernetesNamespace.class) {
required = true;
break;
}
}
}
if (!required) {
return;
}
youAreRequired(allManagers, activeManagers);
//*** Initialise the CPS and DSS fields
try {
KubernetesPropertiesSingleton.setCps(getFramework().getConfigurationPropertyService(NAMESPACE));
} catch (ConfigurationPropertyStoreException e) {
throw new KubernetesManagerException("Failed to set the CPS with the kubernetes namespace", e);
}
try {
this.dss = this.getFramework().getDynamicStatusStoreService(NAMESPACE);
} catch(DynamicStatusStoreException e) {
throw new KubernetesManagerException("Unable to provide the DSS for the Kubernetes Manager", e);
}
//*** Load the YAML supported types so that the YAML can serialize
Yaml.addModelMap("v1", "ConfigMap", V1ConfigMap.class);
Yaml.addModelMap("v1", "PersistentVolumeClaim", V1PersistentVolumeClaim.class);
Yaml.addModelMap("v1", "Service", V1Service.class);
Yaml.addModelMap("v1", "Secret", V1Secret.class);
Yaml.addModelMap("v1", "Deployment", V1Deployment.class);
Yaml.addModelMap("v1", "StatefulSet", V1StatefulSet.class);
this.logger.info("Kubernetes Manager initialised");
}
/**
* This or another manager has indicated that this manager is required
*/
@Override
public void youAreRequired(@NotNull List allManagers, @NotNull List activeManagers)
throws ManagerException {
super.youAreRequired(allManagers, activeManagers);
if (activeManagers.contains(this)) {
return;
}
activeManagers.add(this);
this.required = true;
}
@Override
public boolean doYouSupportSharedEnvironments() {
return true;
}
/**
* Generate all the annotated fields, uses the standard generate by method mechanism
*/
@Override
public void provisionGenerate() throws ManagerException, ResourceUnavailableException {
if (getFramework().getTestRun().isSharedEnvironment()) {
logger.info("Manager running in Shared Environment setup");
}
if (this.clusters.isEmpty()) {
buildClusterMap();
}
//*** Shared Environment Discard processing
try {
if (getFramework().getSharedEnvironmentRunType() == SharedEnvironmentRunType.DISCARD) {
KubernetesNamespaceImpl.loadNamespacesFromRun(getFramework(), dss, clusters, taggedNamespaces, getFramework().getTestRun());
}
} catch (ConfigurationPropertyStoreException e) {
throw new KubernetesManagerException("Unable to determine Shared Environment phase", e);
}
generateAnnotatedFields(KubernetesManagerField.class);
}
/**
* Generate a Kubernetes Namespace
*
* @param field The test field
* @param annotations any annotations with the namespace
* @return a {@link IKubernetesNamespace} namespace
* @throws KubernetesManagerException if there is a problem generating a namespace
*/
@GenerateAnnotatedField(annotation = KubernetesNamespace.class)
public IKubernetesNamespace generateKubernetesNamespace(Field field, List annotations) throws KubernetesManagerException {
KubernetesNamespace annotation = field.getAnnotation(KubernetesNamespace.class);
String tag = annotation.kubernetesNamespaceTag().trim().toUpperCase();
if (tag.isEmpty()) {
tag = "PRIMARY";
}
//*** First check if we already have the tag
KubernetesNamespaceImpl namespace = taggedNamespaces.get(tag);
if (namespace != null) {
return namespace;
}
try {
if (getFramework().getSharedEnvironmentRunType() == SharedEnvironmentRunType.DISCARD) {
throw new KubernetesManagerException("Attempt to generate a new Namespace during Shared Environment discard");
}
} catch (ConfigurationPropertyStoreException e) {
throw new KubernetesManagerException("Unable to determine Shared Environment phase", e);
}
//*** check to see if the tag is a shared environment
String sharedEnvironmentRunName = KubernetesNamespaceTagSharedEnvironment.get(tag);
if (sharedEnvironmentRunName != null) {
try {
IRun sharedEnvironmentRun = getFramework().getFrameworkRuns().getRun(sharedEnvironmentRunName);
if (sharedEnvironmentRun == null || !sharedEnvironmentRun.isSharedEnvironment()) {
throw new KubernetesManagerException("Unable to locate Shared Environment " + sharedEnvironmentRunName + " for Namespace Tag " + tag);
}
HashMap tempSharedEnvironmentNamespaces = new HashMap<>();
KubernetesNamespaceImpl.loadNamespacesFromRun(getFramework(), dss, clusters, tempSharedEnvironmentNamespaces, sharedEnvironmentRun);
namespace = tempSharedEnvironmentNamespaces.get(tag);
if (namespace == null) {
throw new KubernetesManagerException("Unable to locate Shared Environment " + sharedEnvironmentRunName + " for Namespace Tag " + tag);
}
this.taggedNamespaces.put(tag, namespace);
this.sharedEnvironmentNamespacesTags.add(tag);
logger.info("Namespace tag " + tag + " is using Shared Environment " + sharedEnvironmentRunName);
return namespace;
} catch(FrameworkException e) {
throw new KubernetesManagerException("Problem loading Shared Environment " + sharedEnvironmentRunName + " for Namespace Tag " + tag, e);
}
}
//*** Allocate a namespace, may take a few passes through as other tests may be trying to get namespaces at the same time
ArrayList availableClusters = new ArrayList<>(this.clusters.values());
KubernetesNamespaceImpl allocatedNamespace = null;
while(allocatedNamespace == null) {
//*** Workout which cluster has the most capability
float percentageAvailable = -1.0f;
KubernetesClusterImpl selectedCluster = null;
for(KubernetesClusterImpl cluster : availableClusters) {
Float availability = cluster.getAvailability();
if (availability != null && availability > percentageAvailable) {
selectedCluster = cluster;
percentageAvailable = availability;
}
}
//*** Did we find a cluster with availability?
if (selectedCluster == null) {
throw new KubernetesManagerException("Unable to allocate a slot on any Kubernetes Cluster");
}
//*** ask cluster to allocate a namespace, if it returns null, means we have run out of available namespaces
//*** so try a different cluster
allocatedNamespace = selectedCluster.allocateNamespace(tag);
if (allocatedNamespace == null) { // Failed to allocate namespace, remove from available clusters
availableClusters.remove(selectedCluster);
}
}
logger.info("Allocated Kubernetes Namespace " + allocatedNamespace.getId() + " on Cluster " + allocatedNamespace.getCluster().getId() + " for tag " + tag);
taggedNamespaces.put(tag, allocatedNamespace);
return allocatedNamespace;
}
/**
* Delete all the supported resources in the namespaces
*/
@Override
public void provisionDiscard() {
for(KubernetesNamespaceImpl namespace : taggedNamespaces.values()) {
if (this.sharedEnvironmentNamespacesTags.contains(namespace.getTag())) {
logger.debug("Not discarding Shared Environment namespace tag " + namespace.getTag());
continue; //*** Do not discard shared environment namespaces (during test runs)
}
try {
namespace.discard(this.getFramework().getTestRunName());
} catch(KubernetesManagerException e) {
logger.error("Problem discarding namespace " + namespace.getId() + " on cluster " + namespace.getCluster().getId(), e);
}
}
super.provisionDiscard();
}
/**
* Build a map of all the defined clusters
*
* @throws KubernetesManagerException
*/
private void buildClusterMap() throws KubernetesManagerException {
List clusterIds = KubernetesClusters.get();
for(String clusterId : clusterIds) {
this.clusters.put(clusterId, new KubernetesClusterImpl(clusterId, dss, getFramework()));
}
return;
}
@Override
public IKubernetesNamespace getNamespaceByTag(@NotNull String namespaceTag) {
String tag = namespaceTag.trim().toUpperCase();
return taggedNamespaces.get(tag);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy