diff --git a/.editorconfig b/.editorconfig index b9217495..b19fe258 100644 --- a/.editorconfig +++ b/.editorconfig @@ -31,6 +31,6 @@ indent_style = space indent_size = 2 # 2 spaces for md, yaml, ttl, etc.. -[*.{md,yml,iml,json,ttl,ts,js,html,css}] +[*.{md,yml,iml,json,ttl,ts,js,html,css,yaml}] indent_style = space indent_size = 2 diff --git a/Makefile b/Makefile index fa5a9e08..a44227c7 100644 --- a/Makefile +++ b/Makefile @@ -109,3 +109,39 @@ lc-run: dev: docker-compose -f docker-compose-dev.yml build docker-compose -f docker-compose-dev.yml -f docker-compose.override.yml up + +#kubernetes configurations +#- +create-cluster: + sudo kubeadm init --pod-network-cidr=192.168.0.0/16 + +configure-kubectl: + mkdir -p $HOME/.kube + sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config + sudo chown $(id -u):$(id -g) $HOME/.kube/config + +schedule-on-master: + kubectl taint nodes --all node-role.kubernetes.io/master- + +start-cni: + kubectl apply -f ./resource/calico.yaml && kubectl apply -f - - + + + + + + + + + + @@ -33,35 +42,43 @@ - - + + + + + + + + + + + + + + + - - - - - - - + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/analysis-deployment.yaml b/analysis-deployment.yaml new file mode 100644 index 00000000..e69de29b diff --git a/gui-deployment.yaml b/gui-deployment.yaml new file mode 100644 index 00000000..38c3e44a --- /dev/null +++ b/gui-deployment.yaml @@ -0,0 +1,51 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 () + creationTimestamp: null + labels: + io.kompose.service: gui + name: gui +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: gui + strategy: {} + template: + metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 () + creationTimestamp: null + labels: + io.kompose.network/hobbit: "true" + io.kompose.network/hobbit-core: "true" + io.kompose.service: gui + spec: + containers: + - env: + - name: CHECK_REALM_URL + value: "false" + - name: ELASTICSEARCH_HOST + value: elasticsearch + - name: ELASTICSEARCH_HTTP_PORT + value: "9200" + - name: HOBBIT_RABBIT_HOST + value: rabbit + - name: KEYCLOAK_AUTH_URL + value: http://localhost:8181/auth + - name: KEYCLOAK_DIRECT_URL + value: http://keycloak:8080/auth + image: hobbitproject/hobbit-gui:latest + imagePullPolicy: "" + name: gui + ports: + - containerPort: 8080 + resources: {} + restartPolicy: Always + serviceAccountName: "" + volumes: null +status: {} diff --git a/gui-service.yaml b/gui-service.yaml new file mode 100644 index 00000000..e69de29b diff --git a/hobbit-core-networkpolicy.yaml b/hobbit-core-networkpolicy.yaml new file mode 100644 index 00000000..c46a45da --- /dev/null +++ b/hobbit-core-networkpolicy.yaml @@ -0,0 +1,14 @@ +apiVersion: extensions/v1beta1 +kind: NetworkPolicy +metadata: + creationTimestamp: null + name: hobbit-core +spec: + ingress: + - from: + - podSelector: + matchLabels: + io.kompose.network/hobbit-core: "true" + podSelector: + matchLabels: + io.kompose.network/hobbit-core: "true" diff --git a/hobbit-networkpolicy.yaml b/hobbit-networkpolicy.yaml new file mode 100644 index 00000000..7eb36b7e --- /dev/null +++ b/hobbit-networkpolicy.yaml @@ -0,0 +1,14 @@ +apiVersion: extensions/v1beta1 +kind: NetworkPolicy +metadata: + creationTimestamp: null + name: hobbit +spec: + ingress: + - from: + - podSelector: + matchLabels: + io.kompose.network/hobbit: "true" + podSelector: + matchLabels: + io.kompose.network/hobbit: "true" diff --git a/keycloak-claim0-persistentvolumeclaim.yaml b/keycloak-claim0-persistentvolumeclaim.yaml new file mode 100644 index 00000000..e69de29b diff --git a/keycloak-deployment.yaml b/keycloak-deployment.yaml new file mode 100644 index 00000000..42a87426 --- /dev/null +++ b/keycloak-deployment.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 () + creationTimestamp: null + labels: + io.kompose.service: keycloak + name: keycloak +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: keycloak + strategy: + type: Recreate + template: + metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 () + creationTimestamp: null + labels: + io.kompose.network/hobbit: "true" + io.kompose.service: keycloak + spec: + containers: + - image: hobbitproject/hobbit-keycloak:latest + imagePullPolicy: "" + name: keycloak + ports: + - containerPort: 8080 + resources: {} + volumeMounts: + - mountPath: /opt/jboss/keycloak/standalone/data/db + name: keycloak-claim0 + restartPolicy: Always + serviceAccountName: "" + volumes: + - name: keycloak-claim0 + persistentVolumeClaim: + claimName: keycloak-claim0 +status: {} diff --git a/keycloak-service.yaml b/keycloak-service.yaml new file mode 100644 index 00000000..e69de29b diff --git a/kompose b/kompose new file mode 100644 index 00000000..81ace3dc Binary files /dev/null and b/kompose differ diff --git a/platform-controller-claim0-persistentvolumeclaim.yaml b/platform-controller-claim0-persistentvolumeclaim.yaml new file mode 100644 index 00000000..e69de29b diff --git a/platform-controller-deployment.yaml b/platform-controller-deployment.yaml new file mode 100644 index 00000000..76c3c5ab --- /dev/null +++ b/platform-controller-deployment.yaml @@ -0,0 +1,56 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 () + creationTimestamp: null + labels: + io.kompose.service: platform-controller + name: platform-controller +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: platform-controller + strategy: + type: Recreate + template: + metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 () + creationTimestamp: null + labels: + io.kompose.network/hobbit-core: "true" + io.kompose.service: platform-controller + spec: + containers: + - env: + - name: DEPLOY_ENV + value: testing + - name: GITLAB_EMAIL + - name: GITLAB_TOKEN + - name: GITLAB_USER + - name: HOBBIT_RABBIT_EXPERIMENTS_HOST + value: rabbit + - name: HOBBIT_RABBIT_HOST + value: rabbit + - name: HOBBIT_REDIS_HOST + value: redis + - name: SWARM_NODE_NUMBER + value: "1" + image: hobbitproject/hobbit-platform-controller:latest + imagePullPolicy: "" + name: platform-controller + resources: {} + volumeMounts: + - mountPath: /var/run/docker.sock + name: platform-controller-claim0 + restartPolicy: Always + serviceAccountName: "" + volumes: + - name: platform-controller-claim0 + persistentVolumeClaim: + claimName: platform-controller-claim0 +status: {} diff --git a/platform-controller/Dockerfile b/platform-controller/Dockerfile index 5209c5dd..7bd3bcd9 100644 --- a/platform-controller/Dockerfile +++ b/platform-controller/Dockerfile @@ -1,5 +1,9 @@ FROM maven +LABEL maintainer="Hobbit Team" \ + name="platform-controller" \ + version="0.1" + RUN mkdir -p /usr/src/app # Create an empty metadata directory (it will be used as default by the file-based image manager) RUN mkdir -p /usr/src/app/metadata diff --git a/platform-controller/pom.xml b/platform-controller/pom.xml index cffe1ec5..d7121532 100644 --- a/platform-controller/pom.xml +++ b/platform-controller/pom.xml @@ -65,6 +65,20 @@ docker-client 8.11.2 + + + io.kubernetes + client-java + 8.0.2 + compile + + + + + io.fabric8 + kubernetes-client + 4.10.1 + com.google.code.gson @@ -135,7 +149,37 @@ system-rules test - + + org.junit.jupiter + junit-jupiter + RELEASE + test + + + io.fabric8 + kubernetes-server-mock + 4.9.0 + test + + + org.hobbit + platform-controller + 2.0.13-SNAPSHOT + test + + + io.fabric8 + kubernetes-model-apps + 4.10.1 + test + + + io.fabric8 + kubernetes-model + 4.10.1 + test + + diff --git a/platform-controller/src/main/java/org/hobbit/controller/ExperimentManager.java b/platform-controller/src/main/java/org/hobbit/controller/ExperimentManager.java index 76625ee5..a4181d17 100644 --- a/platform-controller/src/main/java/org/hobbit/controller/ExperimentManager.java +++ b/platform-controller/src/main/java/org/hobbit/controller/ExperimentManager.java @@ -38,7 +38,7 @@ import org.hobbit.controller.data.ExperimentStatus; import org.hobbit.controller.data.ExperimentStatus.States; import org.hobbit.controller.data.SetupHardwareInformation; -import org.hobbit.controller.docker.ClusterManager; +import org.hobbit.controller.orchestration.ClusterManager; import org.hobbit.controller.docker.MetaDataFactory; import org.hobbit.controller.execute.ExperimentAbortTimerTask; import org.hobbit.core.Commands; @@ -407,10 +407,8 @@ private synchronized void handleExperimentTermination_unsecured() { experimentStatus.addError(HobbitErrors.ClusterNotHealthy); } - } catch (DockerException e) { + } catch (Exception e) { LOGGER.error("Could not get cluster health status. ", e); - } catch (InterruptedException e) { - LOGGER.error("Interrupted. Could not get cluster health status. ", e); } Model resultModel = experimentStatus.getResultModel(); diff --git a/platform-controller/src/main/java/org/hobbit/controller/PlatformController.java b/platform-controller/src/main/java/org/hobbit/controller/PlatformController.java index 9bb723f1..dadbb31f 100644 --- a/platform-controller/src/main/java/org/hobbit/controller/PlatformController.java +++ b/platform-controller/src/main/java/org/hobbit/controller/PlatformController.java @@ -16,50 +16,29 @@ */ package org.hobbit.controller; -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.text.SimpleDateFormat; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.HashSet; -import java.util.List; -import java.util.Properties; -import java.util.Set; -import java.util.Timer; -import java.util.TimerTask; -import java.util.concurrent.Semaphore; - +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.rabbitmq.client.*; +import com.rabbitmq.client.AMQP.BasicProperties; import org.apache.commons.io.Charsets; import org.apache.commons.io.IOUtils; import org.apache.jena.query.Dataset; import org.apache.jena.query.DatasetFactory; import org.apache.jena.query.QueryExecution; import org.apache.jena.query.QueryExecutionFactory; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.NodeIterator; -import org.apache.jena.rdf.model.RDFNode; -import org.apache.jena.rdf.model.ResIterator; -import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.*; import org.apache.jena.vocabulary.RDF; import org.hobbit.controller.analyze.ExperimentAnalyzer; import org.hobbit.controller.data.ExperimentConfiguration; -import org.hobbit.controller.docker.ClusterManager; -import org.hobbit.controller.docker.ClusterManagerImpl; -import org.hobbit.controller.docker.ContainerManager; -import org.hobbit.controller.docker.ContainerManagerImpl; -import org.hobbit.controller.docker.ContainerStateObserver; -import org.hobbit.controller.docker.ContainerStateObserverImpl; -import org.hobbit.controller.docker.ContainerTerminationCallback; -import org.hobbit.controller.docker.FileBasedImageManager; -import org.hobbit.controller.docker.GitlabBasedImageManager; -import org.hobbit.controller.docker.ImageManager; -import org.hobbit.controller.docker.ImageManagerFacade; -import org.hobbit.controller.docker.ResourceInformationCollector; -import org.hobbit.controller.docker.ResourceInformationCollectorImpl; +import org.hobbit.controller.docker.*; import org.hobbit.controller.front.FrontEndApiHandler; +import org.hobbit.controller.kubernetes.K8sClusterManager; +import org.hobbit.controller.kubernetes.K8sClusterManagerImpl; +import org.hobbit.controller.kubernetes.K8sContainerManagerImpl; +import org.hobbit.controller.kubernetes.K8sResourceInformationCollectorImpl; +import org.hobbit.controller.orchestration.ClusterManager; +import org.hobbit.controller.orchestration.ContainerManager; +import org.hobbit.controller.orchestration.ResourceInformationCollector; import org.hobbit.controller.queue.ExperimentQueue; import org.hobbit.controller.queue.ExperimentQueueImpl; import org.hobbit.core.Commands; @@ -80,23 +59,20 @@ import org.hobbit.core.rabbit.RabbitQueueFactoryImpl; import org.hobbit.storage.client.StorageServiceClient; import org.hobbit.storage.queries.SparqlQueries; -import org.hobbit.utils.EnvVariables; import org.hobbit.utils.rdf.RdfHelper; import org.hobbit.vocab.HOBBIT; import org.hobbit.vocab.HobbitExperiments; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.rabbitmq.client.AMQP; -import com.rabbitmq.client.AMQP.BasicProperties; -import com.rabbitmq.client.Channel; -import com.rabbitmq.client.ConnectionFactory; -import com.rabbitmq.client.Consumer; -import com.rabbitmq.client.DefaultConsumer; -import com.rabbitmq.client.Envelope; -import com.rabbitmq.client.MessageProperties; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.text.SimpleDateFormat; +import java.time.Duration; +import java.util.*; +import java.util.concurrent.Semaphore; /** * This class implements the functionality of the central platform controller. @@ -105,7 +81,7 @@ * */ public class PlatformController extends AbstractCommandReceivingComponent - implements ContainerTerminationCallback, ExperimentAnalyzer { + implements ContainerTerminationCallback, ExperimentAnalyzer { private static final Logger LOGGER = LoggerFactory.getLogger(PlatformController.class); @@ -135,8 +111,8 @@ public class PlatformController extends AbstractCommandReceivingComponent * newly created containers is necessary or not. */ private static final boolean CONTAINER_PARENT_CHECK = System.getenv().containsKey(CONTAINER_PARENT_CHECK_ENV_KEY) - ? System.getenv().get(CONTAINER_PARENT_CHECK_ENV_KEY) == "1" - : true; + ? System.getenv().get(CONTAINER_PARENT_CHECK_ENV_KEY) == "1" + : true; /** * Environmental variable key for the RabbitMQ broker host name used for experiments. */ @@ -246,38 +222,38 @@ public void init() throws Exception { LOGGER.info("Using {} as message broker for experiments.", rabbitMQExperimentsHostName); } else { LOGGER.warn( - "The message broker {} will be used for both - the platform internal communication as well as the experiment communication. It is suggested to have two separated message brokers.", - rabbitMQHostName); + "The message broker {} will be used for both - the platform internal communication as well as the experiment communication. It is suggested to have two separated message brokers.", + rabbitMQHostName); } } else { // the platform and its experiment are sharing a single RabbitMQ instance rabbitMQExperimentsHostName = rabbitMQHostName; LOGGER.warn( - "The message broker {} will be used for both - the platform internal communication as well as the experiment communication. It is suggested to have two separated message brokers.", - rabbitMQHostName); + "The message broker {} will be used for both - the platform internal communication as well as the experiment communication. It is suggested to have two separated message brokers.", + rabbitMQHostName); } // Set task history limit for swarm cluster to 0 (will remove all terminated // containers) // Only for prod mode - clusterManager = new ClusterManagerImpl(); + clusterManager = new K8sClusterManagerImpl(); if (DEPLOY_ENV.equals(DEPLOY_ENV_TESTING) || DEPLOY_ENV.equals(DEPLOY_ENV_DEVELOP)) { LOGGER.debug("Ignoring task history limit parameter. Will remain default (run 'docker info' for details)."); } else { LOGGER.debug( - "Production mode. Setting task history limit to 0. All terminated containers will be removed."); + "Production mode. Setting task history limit to 0. All terminated containers will be removed."); clusterManager.setTaskHistoryLimit(0); } // create container manager - containerManager = new ContainerManagerImpl(); + containerManager = new K8sContainerManagerImpl(); LOGGER.debug("Container manager initialized."); // Create container observer (polls status every 5s) containerObserver = new ContainerStateObserverImpl(containerManager, 5 * 1000); containerObserver.addTerminationCallback(this); // Tell the manager to add container to the observer containerManager.addContainerObserver(containerObserver); - resInfoCollector = new ResourceInformationCollectorImpl(containerManager); + resInfoCollector = new K8sResourceInformationCollectorImpl(containerManager); containerObserver.startObserving(); LOGGER.debug("Container observer initialized."); @@ -297,10 +273,10 @@ public void init() throws Exception { frontEnd2Controller = incomingDataQueueFactory.getConnection().createChannel(); frontEndApiHandler = (new FrontEndApiHandler.Builder()).platformController(this) - .queue(incomingDataQueueFactory, Constants.FRONT_END_2_CONTROLLER_QUEUE_NAME).build(); + .queue(incomingDataQueueFactory, Constants.FRONT_END_2_CONTROLLER_QUEUE_NAME).build(); sender2Analysis = DataSenderImpl.builder() - .queue(outgoingDataQueuefactory, Constants.CONTROLLER_2_ANALYSIS_QUEUE_NAME).build(); + .queue(outgoingDataQueuefactory, Constants.CONTROLLER_2_ANALYSIS_QUEUE_NAME).build(); queue = new ExperimentQueueImpl(); @@ -360,7 +336,7 @@ private void switchCmdToExpRabbit() throws Exception { Consumer consumer = new DefaultConsumer(cmdChannel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, - byte[] body) throws IOException { + byte[] body) throws IOException { try { handleCmd(body, properties); } catch (Exception e) { @@ -401,99 +377,99 @@ public void receiveCommand(byte command, byte[] data, String sessionId, AMQP.Bas if (LOGGER.isDebugEnabled()) { LOGGER.info("received command: session={}, command={}, data={}", sessionId, Commands.toString(command), - data != null ? RabbitMQUtils.readString(data) : "null"); + data != null ? RabbitMQUtils.readString(data) : "null"); } else { LOGGER.info("received command: session={}, command={}", sessionId, Commands.toString(command)); } // This command will receive data from Rabbit // determine the command switch (command) { - case Commands.DOCKER_CONTAINER_START: { - StartCommandData startParams = null; - String containerName = ""; - if (expManager.isExpRunning(sessionId)) { - // Convert data byte array to config data structure - startParams = deserializeStartCommandData(data); - // trigger creation - containerName = createContainer(startParams); - } else { - LOGGER.error( + case Commands.DOCKER_CONTAINER_START: { + StartCommandData startParams = null; + String containerName = ""; + if (expManager.isExpRunning(sessionId)) { + // Convert data byte array to config data structure + startParams = deserializeStartCommandData(data); + // trigger creation + containerName = createContainer(startParams); + } else { + LOGGER.error( "Got a request to start a container for experiment \"{}\" which is either not running or was already stopped. Returning null.", sessionId); - } + } - if (replyTo != null) { - try { - AMQP.BasicProperties.Builder propsBuilder = new AMQP.BasicProperties.Builder(); - propsBuilder.deliveryMode(2); - propsBuilder.correlationId(props.getCorrelationId()); - AMQP.BasicProperties replyProps = propsBuilder.build(); - cmdChannel.basicPublish("", replyTo, replyProps, + if (replyTo != null) { + try { + AMQP.BasicProperties.Builder propsBuilder = new AMQP.BasicProperties.Builder(); + propsBuilder.deliveryMode(2); + propsBuilder.correlationId(props.getCorrelationId()); + AMQP.BasicProperties replyProps = propsBuilder.build(); + cmdChannel.basicPublish("", replyTo, replyProps, RabbitMQUtils.writeString(containerName)); - } catch (IOException e) { - StringBuilder errMsgBuilder = new StringBuilder(); - errMsgBuilder.append("Error, couldn't sent response after creation of container ("); - if (startParams != null) { - errMsgBuilder.append(startParams.toString()); + } catch (IOException e) { + StringBuilder errMsgBuilder = new StringBuilder(); + errMsgBuilder.append("Error, couldn't sent response after creation of container ("); + if (startParams != null) { + errMsgBuilder.append(startParams.toString()); + } + errMsgBuilder.append(") to replyTo="); + errMsgBuilder.append(replyTo); + errMsgBuilder.append("."); + LOGGER.error(errMsgBuilder.toString(), e); } - errMsgBuilder.append(") to replyTo="); - errMsgBuilder.append(replyTo); - errMsgBuilder.append("."); - LOGGER.error(errMsgBuilder.toString(), e); } + break; } - break; - } - case Commands.DOCKER_CONTAINER_STOP: { - // get containerId from params - StopCommandData stopParams = deserializeStopCommandData(data); - // trigger stop - stopContainer(stopParams.containerName); - break; - } - case Commands.BENCHMARK_READY_SIGNAL: { - expManager.systemOrBenchmarkReady(false, sessionId); - break; - } - case Commands.SYSTEM_READY_SIGNAL: { - expManager.systemOrBenchmarkReady(true, sessionId); - break; - } - case Commands.TASK_GENERATION_FINISHED: { - expManager.taskGenFinished(sessionId); - break; - } - case Commands.BENCHMARK_FINISHED_SIGNAL: { - if ((data == null) || (data.length == 0)) { - LOGGER.error("Got no result model from the benchmark controller."); - } else { - expManager.setResultModel(sessionId, data, RabbitMQUtils::readModel); + case Commands.DOCKER_CONTAINER_STOP: { + // get containerId from params + StopCommandData stopParams = deserializeStopCommandData(data); + // trigger stop + stopContainer(stopParams.containerName); + break; } - break; - } - case Commands.REQUEST_SYSTEM_RESOURCES_USAGE: { - // FIXME use the session id to make sure that only containers of this session - // are observed - ResourceUsageInformation resUsage = resInfoCollector.getSystemUsageInformation(); - LOGGER.info("Returning usage information: {}", resUsage != null ? resUsage.toString() : "null"); - if (replyTo != null) { - byte[] response; - if (resUsage != null) { - response = RabbitMQUtils.writeString(gson.toJson(resUsage)); + case Commands.BENCHMARK_READY_SIGNAL: { + expManager.systemOrBenchmarkReady(false, sessionId); + break; + } + case Commands.SYSTEM_READY_SIGNAL: { + expManager.systemOrBenchmarkReady(true, sessionId); + break; + } + case Commands.TASK_GENERATION_FINISHED: { + expManager.taskGenFinished(sessionId); + break; + } + case Commands.BENCHMARK_FINISHED_SIGNAL: { + if ((data == null) || (data.length == 0)) { + LOGGER.error("Got no result model from the benchmark controller."); } else { - response = new byte[0]; + expManager.setResultModel(sessionId, data, RabbitMQUtils::readModel); } - try { - cmdChannel.basicPublish("", replyTo, MessageProperties.PERSISTENT_BASIC, response); - } catch (IOException e) { - StringBuilder errMsgBuilder = new StringBuilder(); - errMsgBuilder.append("Error, couldn't sent the request resource usage statistics to replyTo="); - errMsgBuilder.append(replyTo); - errMsgBuilder.append("."); - LOGGER.error(errMsgBuilder.toString(), e); + break; + } + case Commands.REQUEST_SYSTEM_RESOURCES_USAGE: { + // FIXME use the session id to make sure that only containers of this session + // are observed + ResourceUsageInformation resUsage = resInfoCollector.getSystemUsageInformation(); + LOGGER.info("Returning usage information: {}", resUsage != null ? resUsage.toString() : "null"); + if (replyTo != null) { + byte[] response; + if (resUsage != null) { + response = RabbitMQUtils.writeString(gson.toJson(resUsage)); + } else { + response = new byte[0]; + } + try { + cmdChannel.basicPublish("", replyTo, MessageProperties.PERSISTENT_BASIC, response); + } catch (IOException e) { + StringBuilder errMsgBuilder = new StringBuilder(); + errMsgBuilder.append("Error, couldn't sent the request resource usage statistics to replyTo="); + errMsgBuilder.append(replyTo); + errMsgBuilder.append("."); + LOGGER.error(errMsgBuilder.toString(), e); + } } } } - } } private StopCommandData deserializeStopCommandData(byte[] data) { @@ -534,7 +510,7 @@ private String createContainer(StartCommandData data) { } String containerId = containerManager.startContainer(data.image, data.type, parentId, data.environmentVariables, - data.networkAliases, null, pullImage); + data.networkAliases, null, pullImage); if (containerId == null) { return null; } else { @@ -672,7 +648,7 @@ protected void sendToCmdQueue(String address, byte command, byte data[], BasicPr /** * The controller overrides the super method because it does not need to check * for the leading hobbit id and delegates the command handling to the - * {@link #receiveCommand(byte, byte[], String, String)} method. + * method. */ protected void handleCmd(byte bytes[], AMQP.BasicProperties props) { ByteBuffer buffer = ByteBuffer.wrap(bytes); @@ -700,100 +676,100 @@ public void handleFrontEndCmd(byte bytes[], String replyTo, BasicProperties repl try { // The first byte is the command switch (buffer.get()) { - case FrontEndApiCommands.LIST_CURRENT_STATUS: { - String userName = RabbitMQUtils.readString(buffer); - ControllerStatus status = getStatus(userName); - response = RabbitMQUtils.writeString(gson.toJson(status)); - break; - } - case FrontEndApiCommands.LIST_AVAILABLE_BENCHMARKS: { - response = RabbitMQUtils.writeString(gson.toJson(imageManager.getBenchmarks())); - break; - } - case FrontEndApiCommands.GET_BENCHMARK_DETAILS: { - // get benchmarkUri - String benchmarkUri = RabbitMQUtils.readString(buffer); - LOGGER.debug("Loading details for benchmark \"{}\"", benchmarkUri); - // Get the benchmark - BenchmarkMetaData benchmark = imageManager.getBenchmark(benchmarkUri); - List systems4Benchmark = imageManager.getSystemsForBenchmark(benchmarkUri); - // If there is a username based on that the systems should - // be filtered - if (buffer.hasRemaining()) { + case FrontEndApiCommands.LIST_CURRENT_STATUS: { String userName = RabbitMQUtils.readString(buffer); - LOGGER.debug("Fitlering systems for user \"{}\"", userName); - Set userSystems = new HashSet( + ControllerStatus status = getStatus(userName); + response = RabbitMQUtils.writeString(gson.toJson(status)); + break; + } + case FrontEndApiCommands.LIST_AVAILABLE_BENCHMARKS: { + response = RabbitMQUtils.writeString(gson.toJson(imageManager.getBenchmarks())); + break; + } + case FrontEndApiCommands.GET_BENCHMARK_DETAILS: { + // get benchmarkUri + String benchmarkUri = RabbitMQUtils.readString(buffer); + LOGGER.debug("Loading details for benchmark \"{}\"", benchmarkUri); + // Get the benchmark + BenchmarkMetaData benchmark = imageManager.getBenchmark(benchmarkUri); + List systems4Benchmark = imageManager.getSystemsForBenchmark(benchmarkUri); + // If there is a username based on that the systems should + // be filtered + if (buffer.hasRemaining()) { + String userName = RabbitMQUtils.readString(buffer); + LOGGER.debug("Fitlering systems for user \"{}\"", userName); + Set userSystems = new HashSet( imageManager.getSystemsOfUser(userName)); - List filteredSystems = new ArrayList<>(systems4Benchmark.size()); - for (SystemMetaData s : systems4Benchmark) { - if (userSystems.contains(s)) { - filteredSystems.add(s); + List filteredSystems = new ArrayList<>(systems4Benchmark.size()); + for (SystemMetaData s : systems4Benchmark) { + if (userSystems.contains(s)) { + filteredSystems.add(s); + } } + systems4Benchmark = filteredSystems; } - systems4Benchmark = filteredSystems; - } - response = RabbitMQUtils.writeByteArrays(new byte[][] { RabbitMQUtils.writeModel(benchmark.rdfModel), + response = RabbitMQUtils.writeByteArrays(new byte[][] { RabbitMQUtils.writeModel(benchmark.rdfModel), RabbitMQUtils.writeString(gson.toJson(systems4Benchmark)) }); - break; - } - case FrontEndApiCommands.ADD_EXPERIMENT_CONFIGURATION: { - // get the benchmark URI - String benchmarkUri = RabbitMQUtils.readString(buffer); - String systemUri = RabbitMQUtils.readString(buffer); - String serializedBenchParams = RabbitMQUtils.readString(buffer); - String userName = RabbitMQUtils.readString(buffer); - String experimentId = addExperimentToQueue(benchmarkUri, systemUri, userName, serializedBenchParams, + break; + } + case FrontEndApiCommands.ADD_EXPERIMENT_CONFIGURATION: { + // get the benchmark URI + String benchmarkUri = RabbitMQUtils.readString(buffer); + String systemUri = RabbitMQUtils.readString(buffer); + String serializedBenchParams = RabbitMQUtils.readString(buffer); + String userName = RabbitMQUtils.readString(buffer); + String experimentId = addExperimentToQueue(benchmarkUri, systemUri, userName, serializedBenchParams, null, null, null); - response = RabbitMQUtils.writeString(experimentId); - break; - } - case FrontEndApiCommands.GET_SYSTEMS_OF_USER: { - // get the user name - String email = RabbitMQUtils.readString(buffer); - LOGGER.info("Loading systems of user \"{}\"", email); - response = RabbitMQUtils.writeString(gson.toJson(imageManager.getSystemsOfUser(email))); - break; - } - case FrontEndApiCommands.CLOSE_CHALLENGE: { - // get the challenge URI - String challengeUri = RabbitMQUtils.readString(buffer); - closeChallenge(challengeUri); - break; - } - case FrontEndApiCommands.REMOVE_EXPERIMENT: { - // get the experiment ID - String experimentId = RabbitMQUtils.readString(buffer); - // get the user name - String userName = RabbitMQUtils.readString(buffer); - // Get the experiment from the queue - ExperimentConfiguration config = queue.getExperiment(experimentId); - if (config == null) { - // The experiment is not known - response = new byte[] { 1 }; + response = RabbitMQUtils.writeString(experimentId); + break; } - // Check whether the use has the right to terminate the experiment - if ((config != null) && (config.userName != null) && (config.userName.equals(userName))) { - // Remove the experiment from the queue - if (queue.remove(config)) { - // call the Experiment Manager to cancel the experiment if it is running - expManager.stopExperimentIfRunning(experimentId); - // The experiment has been terminated + case FrontEndApiCommands.GET_SYSTEMS_OF_USER: { + // get the user name + String email = RabbitMQUtils.readString(buffer); + LOGGER.info("Loading systems of user \"{}\"", email); + response = RabbitMQUtils.writeString(gson.toJson(imageManager.getSystemsOfUser(email))); + break; + } + case FrontEndApiCommands.CLOSE_CHALLENGE: { + // get the challenge URI + String challengeUri = RabbitMQUtils.readString(buffer); + closeChallenge(challengeUri); + break; + } + case FrontEndApiCommands.REMOVE_EXPERIMENT: { + // get the experiment ID + String experimentId = RabbitMQUtils.readString(buffer); + // get the user name + String userName = RabbitMQUtils.readString(buffer); + // Get the experiment from the queue + ExperimentConfiguration config = queue.getExperiment(experimentId); + if (config == null) { + // The experiment is not known response = new byte[] { 1 }; + } + // Check whether the use has the right to terminate the experiment + if ((config != null) && (config.userName != null) && (config.userName.equals(userName))) { + // Remove the experiment from the queue + if (queue.remove(config)) { + // call the Experiment Manager to cancel the experiment if it is running + expManager.stopExperimentIfRunning(experimentId); + // The experiment has been terminated + response = new byte[] { 1 }; + } else { + // The experiment is not known + response = new byte[] { 0 }; + } } else { - // The experiment is not known + // The user does not have the right to remove the experiment response = new byte[] { 0 }; } - } else { - // The user does not have the right to remove the experiment - response = new byte[] { 0 }; + break; } - break; - } - default: { - LOGGER.error("Got a request from the front end with an unknown command code {}. It will be ignored.", + default: { + LOGGER.error("Got a request from the front end with an unknown command code {}. It will be ignored.", bytes[0]); - break; - } + break; + } } } catch (Exception e) { LOGGER.error("Exception while hadling front end request.", e); @@ -802,7 +778,7 @@ public void handleFrontEndCmd(byte bytes[], String replyTo, BasicProperties repl LOGGER.trace("Replying to " + replyTo); try { frontEnd2Controller.basicPublish("", replyTo, replyProperties, - response != null ? response : new byte[0]); + response != null ? response : new byte[0]); } catch (IOException e) { LOGGER.error("Exception while trying to send response to the front end.", e); } @@ -826,7 +802,7 @@ protected Model getChallengeFromUri(String challengeUri, String graphUri) { String query = SparqlQueries.getChallengeGraphQuery(challengeUri, graphUri); if (query == null) { LOGGER.error("Couldn't get challenge {} because the needed SPARQL query couldn't be loaded. Aborting.", - challengeUri); + challengeUri); return null; } return storage.sendConstructQuery(query); @@ -851,7 +827,7 @@ private List getChallengeTasksFromUri(String challengeU String experimentId, systemUri, serializedBenchParams; // iterate participating system instances NodeIterator systemInstanceIterator = model.listObjectsOfProperty(challengeTask, - HOBBIT.involvesSystemInstance); + HOBBIT.involvesSystemInstance); RDFNode sysInstance; while (systemInstanceIterator.hasNext()) { sysInstance = systemInstanceIterator.next(); @@ -859,12 +835,12 @@ private List getChallengeTasksFromUri(String challengeU systemUri = sysInstance.asResource().getURI(); experimentId = generateExperimentId(); serializedBenchParams = RabbitMQUtils - .writeModel2String(createExpModelForChallengeTask(model, challengeTaskUri, systemUri)); + .writeModel2String(createExpModelForChallengeTask(model, challengeTaskUri, systemUri)); experiments.add(new ExperimentConfiguration(experimentId, benchmarkUri, serializedBenchParams, - systemUri, organizer, challengeUri, challengeTaskUri, executionDate)); + systemUri, organizer, challengeUri, challengeTaskUri, executionDate)); } else { LOGGER.error("Couldn't get the benchmark for challenge task \"{}\". This task will be ignored.", - challengeTaskUri); + challengeTaskUri); } } } @@ -887,7 +863,7 @@ private void executeChallengeExperiments(String challengeUri) { // add to queue for (ExperimentConfiguration ex : experiments) { LOGGER.info("Adding experiment " + ex.id + " with benchmark " + ex.benchmarkUri + " and system " - + ex.systemUri + " to the queue."); + + ex.systemUri + " to the queue."); queue.add(ex); } } @@ -904,10 +880,10 @@ private void executeChallengeExperiments(String challengeUri) { * time to use as current when scheduling */ protected static synchronized void scheduleDateOfNextExecution(StorageServiceClient storage, String challengeUri, - Calendar now) { + Calendar now) { LOGGER.info("Scheduling dateOfNextExecution for challenge {}...", challengeUri); String query = SparqlQueries.getRepeatableChallengeInfoQuery(challengeUri, - Constants.CHALLENGE_DEFINITION_GRAPH_URI); + Constants.CHALLENGE_DEFINITION_GRAPH_URI); Model challengeModel = storage.sendConstructQuery(query); if (challengeModel == null) { LOGGER.error("Couldn't retrieve challenge {}. Aborting.", challengeUri); @@ -921,7 +897,7 @@ protected static synchronized void scheduleDateOfNextExecution(StorageServiceCli Resource challenge = challengeIterator.next(); Calendar registrationCutoffDate = RdfHelper.getDateTimeValue(challengeModel, challenge, - HOBBIT.registrationCutoffDate); + HOBBIT.registrationCutoffDate); if (registrationCutoffDate == null) { LOGGER.error("Couldn't retrieve registration cutoff date for challenge " + challengeUri + ". Aborting."); return; @@ -934,7 +910,7 @@ protected static synchronized void scheduleDateOfNextExecution(StorageServiceCli } Calendar dateOfNextExecution = RdfHelper.getDateTimeValue(challengeModel, challenge, - HOBBIT.dateOfNextExecution); + HOBBIT.dateOfNextExecution); if (dateOfNextExecution == null) { dateOfNextExecution = RdfHelper.getDateTimeValue(challengeModel, challenge, HOBBIT.executionDate); if (dateOfNextExecution == null) { @@ -955,9 +931,9 @@ protected static synchronized void scheduleDateOfNextExecution(StorageServiceCli // set dateOfNextExecution += executionPeriod SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); LOGGER.info("Next execution date for challenge {} is now set to {}", challengeUri, - dateFormat.format(dateOfNextExecution.getTime())); + dateFormat.format(dateOfNextExecution.getTime())); if (!storage.sendUpdateQuery(SparqlQueries.getUpdateDateOfNextExecutionQuery(challengeUri, - dateOfNextExecution, Constants.CHALLENGE_DEFINITION_GRAPH_URI))) { + dateOfNextExecution, Constants.CHALLENGE_DEFINITION_GRAPH_URI))) { LOGGER.error("Couldn't update dateOfNextExecution for challenge {}", challengeUri); } } else { @@ -965,7 +941,7 @@ protected static synchronized void scheduleDateOfNextExecution(StorageServiceCli // already LOGGER.info("Removing dateOfNextExecution for challenge {} because it reached cutoff date", challengeUri); if (!storage.sendUpdateQuery(SparqlQueries.getUpdateDateOfNextExecutionQuery(challengeUri, null, - Constants.CHALLENGE_DEFINITION_GRAPH_URI))) { + Constants.CHALLENGE_DEFINITION_GRAPH_URI))) { LOGGER.error("Couldn't remove dateOfNextExecution for challenge {}", challengeUri); } } @@ -980,10 +956,10 @@ protected static synchronized void scheduleDateOfNextExecution(StorageServiceCli * challenge URI */ protected static synchronized boolean copyChallengeToPublicResultGraph(StorageServiceClient storage, - String challengeUri) { + String challengeUri) { // get the challenge model Model challengeModel = storage.sendConstructQuery( - SparqlQueries.getChallengeGraphQuery(challengeUri, Constants.CHALLENGE_DEFINITION_GRAPH_URI)); + SparqlQueries.getChallengeGraphQuery(challengeUri, Constants.CHALLENGE_DEFINITION_GRAPH_URI)); // insert the challenge into the public graph return storage.sendInsertQuery(challengeModel, Constants.PUBLIC_RESULT_GRAPH_URI); } @@ -1001,13 +977,13 @@ private void closeChallenge(String challengeUri) { String query = SparqlQueries.getCloseChallengeQuery(challengeUri, Constants.CHALLENGE_DEFINITION_GRAPH_URI); if (query == null) { LOGGER.error( - "Couldn't close the challenge {} because the needed SPARQL query couldn't be loaded. Aborting.", - challengeUri); + "Couldn't close the challenge {} because the needed SPARQL query couldn't be loaded. Aborting.", + challengeUri); return; } if (!storage.sendUpdateQuery(query)) { LOGGER.error("Couldn't close the challenge {} because the SPARQL query didn't had any effect. Aborting.", - challengeUri); + challengeUri); return; } executeChallengeExperiments(challengeUri); @@ -1035,7 +1011,7 @@ protected synchronized void checkRepeatableChallenges() { LOGGER.info("Processing repeatable challenge {}...", challenge); registrationCutoffDate = RdfHelper.getDateTimeValue(challengesModel, challenge, - HOBBIT.registrationCutoffDate); + HOBBIT.registrationCutoffDate); if ((registrationCutoffDate != null) && (now.after(registrationCutoffDate))) { // registration cutoff date has been reached, close the challenge (it will run // remaining experiments) @@ -1049,7 +1025,7 @@ protected synchronized void checkRepeatableChallenges() { Calendar executionDate = RdfHelper.getDateTimeValue(challengesModel, challenge, HOBBIT.executionDate); if ((executionDate != null) && (now.after(executionDate))) { LOGGER.info("Starting repeatable challenge {} with execution date {}...", challenge, - dateFormat.format(executionDate.getTime())); + dateFormat.format(executionDate.getTime())); // executionDate has been reached, copy challenge to public graph and set // dateOfNextExecution if (!copyChallengeToPublicResultGraph(storage, challenge.getURI())) { @@ -1058,7 +1034,7 @@ protected synchronized void checkRepeatableChallenges() { } } else { LOGGER.info("Repeatable challenge {} will start at {}", challenge, - dateFormat.format(executionDate.getTime())); + dateFormat.format(executionDate.getTime())); continue; } } @@ -1071,10 +1047,10 @@ protected synchronized void checkRepeatableChallenges() { // move the [challengeTask hobbit:involvesSystem system] triples from the // challenge def graph to the public result graph String moveQuery = SparqlQueries.getMoveChallengeSystemQuery(challenge.getURI(), - Constants.CHALLENGE_DEFINITION_GRAPH_URI, Constants.PUBLIC_RESULT_GRAPH_URI); + Constants.CHALLENGE_DEFINITION_GRAPH_URI, Constants.PUBLIC_RESULT_GRAPH_URI); if (!storage.sendUpdateQuery(moveQuery)) { LOGGER.error("Couldn't move the [task :involvesSystem system] triple to the public graph", - challenge); + challenge); } scheduleDateOfNextExecution(storage, challenge.getURI(), now); @@ -1086,12 +1062,12 @@ protected synchronized void checkRepeatableChallenges() { * The method is static for an easier JUnit test implementation */ protected static synchronized void republishChallenges(StorageServiceClient storage, ExperimentQueue queue, - ExperimentAnalyzer analyzer) { + ExperimentAnalyzer analyzer) { LOGGER.info("Checking for challenges to publish..."); // Get list of all UNPUBLISHED, closed challenges, their tasks and // publication dates Model challengesModel = storage.sendConstructQuery( - SparqlQueries.getChallengePublishInfoQuery(null, Constants.CHALLENGE_DEFINITION_GRAPH_URI)); + SparqlQueries.getChallengePublishInfoQuery(null, Constants.CHALLENGE_DEFINITION_GRAPH_URI)); if (challengesModel == null) { LOGGER.error("Couldn't retrieve challenges to publish. Aborting."); return; @@ -1109,7 +1085,7 @@ protected static synchronized void republishChallenges(StorageServiceClient stor // If the challenge results should be published if ((publishDate != null) && (now.after(publishDate))) { List taskResources = RdfHelper.getSubjectResources(challengesModel, HOBBIT.isTaskOf, - challenge); + challenge); Set tasks = new HashSet<>(); for (Resource taskResource : taskResources) { tasks.add(taskResource.getURI()); @@ -1137,12 +1113,12 @@ protected static synchronized void republishChallenges(StorageServiceClient stor List experiments = new ArrayList<>(); for (String taskUri : tasks) { Model taskExperimentModel = storage.sendConstructQuery(SparqlQueries - .getExperimentOfTaskQuery(null, taskUri, Constants.PRIVATE_RESULT_GRAPH_URI)); + .getExperimentOfTaskQuery(null, taskUri, Constants.PRIVATE_RESULT_GRAPH_URI)); experiments.addAll(RdfHelper.getSubjectResources(taskExperimentModel, HOBBIT.isPartOf, - taskExperimentModel.getResource(taskUri))); + taskExperimentModel.getResource(taskUri))); if (!storage.sendInsertQuery(taskExperimentModel, Constants.PUBLIC_RESULT_GRAPH_URI)) { LOGGER.error("Couldn't copy experiment results for challenge task \"{}\". Aborting.", - taskUri); + taskUri); return; } // Send the experiment to the analysis component @@ -1156,15 +1132,15 @@ protected static synchronized void republishChallenges(StorageServiceClient stor } // Remove challenge from challenge graph storage.sendUpdateQuery(SparqlQueries.deleteChallengeGraphQuery(challenge.getURI(), - Constants.CHALLENGE_DEFINITION_GRAPH_URI)); + Constants.CHALLENGE_DEFINITION_GRAPH_URI)); // Remove experiments for (Resource experiment : experiments) { storage.sendUpdateQuery(SparqlQueries.deleteExperimentGraphQuery(experiment.getURI(), - Constants.PRIVATE_RESULT_GRAPH_URI)); + Constants.PRIVATE_RESULT_GRAPH_URI)); } // Clean up the remaining graph String queries[] = SparqlQueries - .cleanUpChallengeGraphQueries(Constants.CHALLENGE_DEFINITION_GRAPH_URI); + .cleanUpChallengeGraphQueries(Constants.CHALLENGE_DEFINITION_GRAPH_URI); for (int i = 0; i < queries.length; ++i) { storage.sendUpdateQuery(queries[i]); } @@ -1181,7 +1157,7 @@ private Model createExpModelForChallengeTask(Model model, String challengeTaskUr Dataset dataset = DatasetFactory.create(); dataset.addNamedModel("http://temp.org/challenge", model); String query = SparqlQueries.getCreateExperimentFromTaskQuery(HobbitExperiments.New.getURI(), challengeTaskUri, - systemUri, "http://temp.org/challenge"); + systemUri, "http://temp.org/challenge"); if (query == null) { LOGGER.error("Couldn't load SPARQL query to create an RDF model for a new experiment. Returning null."); return null; @@ -1209,12 +1185,12 @@ private Model createExpModelForChallengeTask(Model model, String challengeTaskUr * @return the Id of the created experiment */ protected String addExperimentToQueue(String benchmarkUri, String systemUri, String userName, - String serializedBenchParams, String challengUri, String challengTaskUri, Calendar executionDate) { + String serializedBenchParams, String challengUri, String challengTaskUri, Calendar executionDate) { String experimentId = generateExperimentId(); LOGGER.info("Adding experiment {} with benchmark {}, system {} and user {} to the queue.", experimentId, - benchmarkUri, systemUri, userName); + benchmarkUri, systemUri, userName); queue.add(new ExperimentConfiguration(experimentId, benchmarkUri, serializedBenchParams, systemUri, userName, - challengUri, challengTaskUri, executionDate)); + challengUri, challengTaskUri, executionDate)); return experimentId; } @@ -1231,14 +1207,14 @@ private ControllerStatus getStatus(String userName) { Model model = imageManager.getSystemModel(runningExperiment.systemUri); if (model != null) { runningExperiment.systemName = RdfHelper.getLabel(model, - model.getResource(runningExperiment.systemUri)); + model.getResource(runningExperiment.systemUri)); } else { runningExperiment.systemName = runningExperiment.systemUri; } model = imageManager.getBenchmarkModel(runningExperiment.benchmarkUri); if (model != null) { runningExperiment.benchmarkName = RdfHelper.getLabel(model, - model.getResource(runningExperiment.benchmarkUri)); + model.getResource(runningExperiment.benchmarkUri)); } else { runningExperiment.benchmarkName = runningExperiment.benchmarkUri; } @@ -1257,8 +1233,8 @@ private ControllerStatus getStatus(String userName) { queuedExp.challengeUri = experiment.challengeUri; queuedExp.challengeTaskUri = experiment.challengeTaskUri; queuedExp.dateOfExecution = experiment.executionDate != null - ? experiment.executionDate.getTimeInMillis() - : 0; + ? experiment.executionDate.getTimeInMillis() + : 0; queuedExp.canBeCanceled = userName != null && userName.equals(experiment.userName); tempQueue.add(queuedExp); } @@ -1312,7 +1288,7 @@ public String rabbitMQHostName() { /** * @deprecated Not used inside the controller. Use - * {@link #receiveCommand(byte, byte[], String, String)} instead. + * instead. */ @Deprecated @Override diff --git a/platform-controller/src/main/java/org/hobbit/controller/docker/ClusterManager.java b/platform-controller/src/main/java/org/hobbit/controller/docker/ClusterManager.java deleted file mode 100644 index 9f8b93dc..00000000 --- a/platform-controller/src/main/java/org/hobbit/controller/docker/ClusterManager.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.hobbit.controller.docker; - -import com.spotify.docker.client.exceptions.DockerException; -import com.spotify.docker.client.messages.Info; - -/** - * This interface is implemented by classes that can be used to manage and - * collect info about Docker Cluster. - * - * @author Ivan Ermilov (iermilov@informatik.uni-leipzig.de) - * - */ -public interface ClusterManager { - /** - * Get cluster info - * - * @return com.spotify.docker.client.messages.Info - */ - public Info getClusterInfo() throws DockerException, InterruptedException; - - /** - * Get number of nodes in the cluster - * - * @return number of nodes - */ - public long getNumberOfNodes() throws DockerException, InterruptedException; - - /** - * Get number of nodes in the cluster - * - * @param label - * the label to filter nodes - * @return number of nodes with the specified label - */ - public long getNumberOfNodes(String label) throws DockerException, InterruptedException; - - /** - * Get the health status of the cluster - * - * @return boolean (is cluster healthy?) - */ - public boolean isClusterHealthy() throws DockerException, InterruptedException; - - /** - * Get expected number of nodes in the cluster - * Set externally by SWARM_NODE_NUMBER env variable - * - * @return expected number of nodes - */ - public long getExpectedNumberOfNodes(); - - /** - * Set task history limit for the swarm cluster - * For production history limit should be set to 0 - * Then the cluster will not keep containers after the services are removed - */ - public void setTaskHistoryLimit(Integer taskHistoryLimit) throws DockerException, InterruptedException; - - /** - * Get task history limit for the swarm cluster - * - * @return int (task history limit) - */ - - public int getTaskHistoryLimit() throws DockerException, InterruptedException; -} diff --git a/platform-controller/src/main/java/org/hobbit/controller/docker/ClusterManagerImpl.java b/platform-controller/src/main/java/org/hobbit/controller/docker/ClusterManagerImpl.java index d628800a..da7df9da 100644 --- a/platform-controller/src/main/java/org/hobbit/controller/docker/ClusterManagerImpl.java +++ b/platform-controller/src/main/java/org/hobbit/controller/docker/ClusterManagerImpl.java @@ -2,6 +2,8 @@ import java.util.stream.Stream; +import org.hobbit.controller.orchestration.ClusterManager; +import org.hobbit.controller.orchestration.objects.ClusterInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,11 +29,14 @@ public class ClusterManagerImpl implements ClusterManager { * Docker client instance */ private DockerClient dockerClient; + private long expectedNumberOfNodes = 0; private String SWARM_NODE_NUMBER = null; public ClusterManagerImpl() throws DockerCertificateException { dockerClient = DockerUtility.getDockerClient(); + + SWARM_NODE_NUMBER = System.getenv("SWARM_NODE_NUMBER"); if(SWARM_NODE_NUMBER == null) { expectedNumberOfNodes = 1; @@ -40,24 +45,58 @@ public ClusterManagerImpl() throws DockerCertificateException { } } - public Info getClusterInfo() throws DockerException, InterruptedException { - return dockerClient.info(); + public ClusterInfo getClusterInfo(){ + Info info = null; + try { + info = dockerClient.info(); + } catch (DockerException e) { + LOGGER.error(e.getMessage(), e); + } catch (InterruptedException e) { + LOGGER.error(e.getMessage(), e); + } + + ClusterInfo clusterInfo = new ClusterInfo(info.architecture(), info.clusterStore(), info.cgroupDriver(), info.containers(), info.containersRunning(), + info.containersStopped(), info.containersPaused(), info.cpuCfsPeriod(), info.cpuCfsQuota(), info.debug(), info.dockerRootDir(), info.storageDriver(), + info.driverStatus(), info.experimentalBuild(), info.httpProxy(), info.httpsProxy(), info.id(), info.ipv4Forwarding(), + info.images(), info.indexServerAddress(), info.initPath(), info.initSha1(), info.kernelMemory(), info.kernelVersion(), info.labels(), info.memTotal(), + info.memoryLimit(), info.cpus(), info.eventsListener(), info.fileDescriptors(), info.goroutines(), info.name(), info.noProxy(), info.oomKillDisable(), info.operatingSystem(), + info.osType(), info.systemStatus(), info.systemTime()); + + + return clusterInfo; } private Stream streamReadyNodes() throws DockerException, InterruptedException { return dockerClient.listNodes().stream().filter(n->n.status().state().equalsIgnoreCase("ready")); } - public long getNumberOfNodes() throws DockerException, InterruptedException { - return streamReadyNodes().count(); + public long getNumberOfNodes() { + Long nodes = 0L; + try { + nodes = streamReadyNodes().count(); + } catch (DockerException e) { + LOGGER.error(e.getMessage(), e); + } catch (InterruptedException e) { + LOGGER.error(e.getMessage(), e); + } + + return nodes; } - public long getNumberOfNodes(String label) throws DockerException, InterruptedException { + public long getNumberOfNodes(String label) { + Long nodes = 0L; final String[] parts = label.split("="); - return streamReadyNodes().filter(n->parts[1].equals(n.spec().labels().get(parts[0]))).count(); + try { + nodes = streamReadyNodes().filter(n->parts[1].equals(n.spec().labels().get(parts[0]))).count(); + } catch (DockerException e) { + LOGGER.error(e.getMessage(), e); + } catch (InterruptedException e) { + LOGGER.error(e.getMessage(), e); + } + return nodes; } - public boolean isClusterHealthy() throws DockerException, InterruptedException { + public boolean isClusterHealthy(){ long numberOfNodes = getNumberOfNodes(); if(numberOfNodes >= expectedNumberOfNodes) { return true; @@ -70,12 +109,14 @@ public long getExpectedNumberOfNodes() { return expectedNumberOfNodes; } - public void setTaskHistoryLimit(Integer taskHistoryLimit) throws DockerException, InterruptedException { + public void setTaskHistoryLimit(Integer taskHistoryLimit){ OrchestrationConfig orchestrationConfig = OrchestrationConfig.builder() .taskHistoryRetentionLimit(0) .build(); - SwarmSpec currentSwarmSpec = dockerClient.inspectSwarm().swarmSpec(); - SwarmSpec updatedSwarmSpec = SwarmSpec.builder() + SwarmSpec currentSwarmSpec = null; + try { + currentSwarmSpec = dockerClient.inspectSwarm().swarmSpec(); + SwarmSpec updatedSwarmSpec = SwarmSpec.builder() .orchestration(orchestrationConfig) .caConfig(currentSwarmSpec.caConfig()) .dispatcher(currentSwarmSpec.dispatcher()) @@ -85,12 +126,27 @@ public void setTaskHistoryLimit(Integer taskHistoryLimit) throws DockerException .raft(currentSwarmSpec.raft()) .taskDefaults(currentSwarmSpec.taskDefaults()) .build(); - Version swarmVersion = dockerClient.inspectSwarm().version(); - dockerClient.updateSwarm(swarmVersion.index(), updatedSwarmSpec); + Version swarmVersion = null; + swarmVersion = dockerClient.inspectSwarm().version(); + + dockerClient.updateSwarm(swarmVersion.index(), updatedSwarmSpec); + } catch (DockerException e) { + LOGGER.error(e.getMessage(), e); + } catch (InterruptedException e) { + LOGGER.error(e.getMessage(), e); + } + } - public int getTaskHistoryLimit() throws DockerException, InterruptedException { - SwarmSpec currentSwarmSpec = dockerClient.inspectSwarm().swarmSpec(); + public int getTaskHistoryLimit(){ + SwarmSpec currentSwarmSpec = null; + try { + currentSwarmSpec = dockerClient.inspectSwarm().swarmSpec(); + } catch (DockerException e) { + LOGGER.error(e.getMessage(), e); + } catch (InterruptedException e) { + LOGGER.error(e.getMessage(), e); + } return currentSwarmSpec.orchestration().taskHistoryRetentionLimit(); } } diff --git a/platform-controller/src/main/java/org/hobbit/controller/docker/ContainerManager.java b/platform-controller/src/main/java/org/hobbit/controller/docker/ContainerManager.java deleted file mode 100644 index 930ee059..00000000 --- a/platform-controller/src/main/java/org/hobbit/controller/docker/ContainerManager.java +++ /dev/null @@ -1,329 +0,0 @@ -/** - * This file is part of platform-controller. - * - * platform-controller is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * platform-controller 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with platform-controller. If not, see . - */ -package org.hobbit.controller.docker; - -import java.util.List; - -import com.spotify.docker.client.exceptions.DockerException; -import com.spotify.docker.client.messages.ContainerStats; -import com.spotify.docker.client.messages.swarm.Service; - -/** - * This interface is implemented by classes that can be used to manage Docker - * containers. - * - * @author Michael Röder (roeder@informatik.uni-leipzig.de) - * - */ -public interface ContainerManager { - - /** - * Exit code of containers - * where process was terminated with SIGKILL (number 9). - */ - public static int DOCKER_EXITCODE_SIGKILL = 128 + 9; - - /** - * Label that denotes container type - */ - public static final String LABEL_TYPE = "org.hobbit.type"; - /** - * Label that denotes container parent - */ - public static final String LABEL_PARENT = "org.hobbit.parent"; - - /** - * Start container with container type Benchmark and no parent - * - * @param imageName - * Name of the image to start - * - * @return container id - * @deprecated because the method tries to create a container with type=null and - * parent="" which does not work without a predefined default type - * for all containers that are created in that way. Use - * {@link #startContainer(String, String, String)} instead. - */ - @Deprecated - public String startContainer(String imageName); - - /** - * Start container with container type Benchmark and no parent - * - * @param imageName - * name of the image to start - * @param command - * command to be executed - * - * @return container id - * @deprecated because the method tries to create a container with type=null and - * parent="" which does not work without a predefined default type - * for all containers that are created in that way. Use - * {@link #startContainer(String, String, String, String[])} - * instead. - */ - public String startContainer(String imageName, String[] command); - - /** - * Start container with given image, type and parent - * - * @param imageName - * name of the image to start - * @param type - * container type - * @param parent - * parent id - * - * - * @return container id - */ - public String startContainer(String imageName, String type, String parent); - - /** - * Starts the container with the given image name. - * - * @param imageName - * name of the image to be started - * @param containerType - * type to be assigned to container - * @param parentId - * id of the parent container - * @param command - * commands that should be executed - * - * @return container Id or null if an error occurred. - */ - public String startContainer(String imageName, String containerType, String parentId, String[] command); - - /** - * Starts the container with the given image name. - * - * @param imageName - * name of the image to be started - * @param containerType - * type to be assigned to container - * @param parentId - * id of the parent container - * @param env - * environment variables of the schema "key=value" - * @param command - * commands that should be executed - * - * @return container Id or null if an error occurred. - */ - public String startContainer(String imageName, String containerType, String parentId, String[] env, - String[] command); - - /** - * Starts the container with the given image name. - * - * @param imageName - * name of the image to be started - * @param containerType - * type to be assigned to container - * @param parentId - * id of the parent container - * @param env - * environment variables of the schema "key=value" - * @param netAliases - * network aliases for this container - * @param command - * commands that should be executed - * - * @return container Id or null if an error occurred. - */ - public String startContainer(String imageName, String containerType, String parentId, String[] env, - String[] netAliases, String[] command); - - /** - * Starts the container with the given image name. - * - * @param imageName - * name of the image to be started - * @param containerType - * type to be assigned to container - * @param parentId - * id of the parent container - * @param env - * environment variables of the schema "key=value" - * @param command - * commands that should be executed - * @param pullImage - * whether the image needs to be prefetched - * - * @return container Id or null if an error occurred. - */ - public String startContainer(String imageName, String containerType, String parentId, String[] env, - String[] command, boolean pullImage); - - /** - * Starts the container with the given image name. - * - * @param imageName - * name of the image to be started - * @param containerType - * type to be assigned to container - * @param parentId - * id of the parent container - * @param env - * environment variables of the schema "key=value" - * @param netAliases - * network aliases for this container - * @param command - * commands that should be executed - * @param pullImage - * whether the image needs to be prefetched - * - * @return container Id or null if an error occurred. - */ - public String startContainer(String imageName, String containerType, String parentId, String[] env, - String[] netAliases, String[] command, boolean pullImage); - - /** - * Starts the container with the given image name. - * - * @param imageName - * name of the image to be started - * @param containerType - * type to be assigned to container - * @param parentId - * id of the parent container - * @param env - * environment variables of the schema "key=value" - * @param command - * commands that should be executed - * @param experimentId - * experimentId to add to GELF tag - * - * @return container Id or null if an error occurred. - */ - public String startContainer(String imageName, String containerType, String parentId, String[] env, - String[] netAliases, String[] command, String experimentId); - - /** - * Stops the container with the given container Id. - * - * @param containerId - * id of the container that should be stopped - * @deprecated use {@link #removeContainer(String)} instead. - */ - @Deprecated - public void stopContainer(String containerId); - - /** - * Removes the container with the given container Id. - * - * @param containerId - * id of the container that should be removed - */ - public void removeContainer(String serviceName); - - /** - * Stops the parent container and all its children given the parent id - * - * @param parentId - * id of the parent container - * @deprecated use {@link #removeParentAndChildren(String)} instead. - */ - @Deprecated - public void stopParentAndChildren(String parentId); - - /** - * Removes the parent container and all its children given the parent id - * - * @param parent - * id of the parent container - */ - public void removeParentAndChildren(String parent); - - /** - * Returns container's exit code or null if container is still running. - * - * @param container - */ - public Integer getContainerExitCode(String serviceName) throws DockerException, InterruptedException; - - /** - * Returns container info - * - * @param containerId - */ - public Service getContainerInfo(String serviceName) throws InterruptedException, DockerException; - - /** - * Get a list of services - */ - public default List getContainers() { - return getContainers(Service.Criteria.builder().build()); - } - - /** - * Get a list of services which fulfill the given filter criteria. - * - * @Service.Criteria criteria - * service criteria for filtering the list of services - */ - public List getContainers(Service.Criteria criteria); - - /** - * @deprecated Platform uses names as IDs. - * Retrieves the container Id for the container with the given name or null if - * no such container could be found. - */ - @Deprecated - public String getContainerId(String name); - - /** - * @deprecated Platform uses names as IDs. - * Returns the name of the container with the given Id or {@code null} if such a - * container can not be found - * - * @param containerId - * the Id of the container for which the name should be retrieved - * @return the name of the container with the given Id or {@code null} if such a - * container can not be found - */ - @Deprecated - public String getContainerName(String containerId); - - /** - * Adds the given observer to the list of internal observers. - * - * @param containerObserver - * the observer that should be added to the internal list - */ - public void addContainerObserver(ContainerStateObserver containerObserver); - - /** - * Pulls the image with the given name. - * - * @param imageName - * the name of the image that should be pulled - */ - public void pullImage(String imageName); - - /** - * Returns statistics of the container with the given Id or {@code null} if the - * container can not be found or an error occurs. - * - * @param containerId - * the Id of the container for which statistics should be requested - * @return statistics of the container with the given Id or {@code null} if the - * container can not be found or an error occurs. - */ - public ContainerStats getStats(String containerId); -} diff --git a/platform-controller/src/main/java/org/hobbit/controller/docker/ContainerManagerImpl.java b/platform-controller/src/main/java/org/hobbit/controller/docker/ContainerManagerImpl.java index c972a541..2287337e 100644 --- a/platform-controller/src/main/java/org/hobbit/controller/docker/ContainerManagerImpl.java +++ b/platform-controller/src/main/java/org/hobbit/controller/docker/ContainerManagerImpl.java @@ -16,54 +16,34 @@ */ package org.hobbit.controller.docker; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import org.hobbit.controller.gitlab.GitlabControllerImpl; -import org.hobbit.controller.utils.Waiting; -import org.hobbit.core.Constants; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.google.common.collect.ImmutableMap; import com.spotify.docker.client.DockerClient; import com.spotify.docker.client.exceptions.DockerCertificateException; import com.spotify.docker.client.exceptions.DockerException; import com.spotify.docker.client.exceptions.ServiceNotFoundException; import com.spotify.docker.client.exceptions.TaskNotFoundException; -import com.spotify.docker.client.messages.ContainerStats; import com.spotify.docker.client.messages.Network; -import com.spotify.docker.client.messages.NetworkConfig; -import com.spotify.docker.client.messages.RegistryAuth; -import com.spotify.docker.client.messages.ServiceCreateResponse; -import com.spotify.docker.client.messages.swarm.ContainerSpec; -import com.spotify.docker.client.messages.swarm.Driver; -import com.spotify.docker.client.messages.swarm.NetworkAttachmentConfig; -import com.spotify.docker.client.messages.swarm.Placement; -import com.spotify.docker.client.messages.swarm.RestartPolicy; -import com.spotify.docker.client.messages.swarm.Service; -import com.spotify.docker.client.messages.swarm.ServiceMode; -import com.spotify.docker.client.messages.swarm.ServiceSpec; -import com.spotify.docker.client.messages.swarm.Task; -import com.spotify.docker.client.messages.swarm.TaskSpec; -import com.spotify.docker.client.messages.swarm.TaskStatus; +import com.spotify.docker.client.messages.*; +import com.spotify.docker.client.messages.swarm.*; +import org.hobbit.controller.gitlab.GitlabControllerImpl; +import org.hobbit.controller.orchestration.ClusterManager; +import org.hobbit.controller.orchestration.ContainerManager; +import org.hobbit.controller.utils.Waiting; +import org.hobbit.core.Constants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * Created by Timofey Ermilov on 31/08/16 * * @author Michael Röder (roeder@informatik.uni-leipzig.de) */ -public class ContainerManagerImpl implements ContainerManager { +public class ContainerManagerImpl implements ContainerManager { private static final Logger LOGGER = LoggerFactory.getLogger(ContainerManagerImpl.class); @@ -78,16 +58,16 @@ public class ContainerManagerImpl implements ContainerManager { private static final int DOCKER_MAX_NAME_LENGTH = 63; private static final String DEPLOY_ENV = System.getenv().containsKey(DEPLOY_ENV_KEY) - ? System.getenv().get(DEPLOY_ENV_KEY) - : "production"; + ? System.getenv().get(DEPLOY_ENV_KEY) + : "production"; private static final String DEPLOY_ENV_TESTING = "testing"; private static final String DEPLOY_ENV_DEVELOP = "develop"; private static final String LOGGING_DRIVER_GELF = "gelf"; private static final Pattern PORT_PATTERN = Pattern.compile(":[0-9]+/"); private static final Boolean DOCKER_AUTOPULL = System.getenv().containsKey(DOCKER_AUTOPULL_ENV_KEY) - ? System.getenv().get(DOCKER_AUTOPULL_ENV_KEY) == "1" - : true; + ? System.getenv().get(DOCKER_AUTOPULL_ENV_KEY) == "1" + : true; private static final long DOCKER_POLL_INTERVAL = 100; private static final long DOCKER_IMAGE_PULL_MAX_WAITING_TIME = 1200000; // 20 min @@ -104,16 +84,16 @@ public class ContainerManagerImpl implements ContainerManager { * Task states which are considered as not running yet. */ public static final Set NEW_TASKS_STATES = Collections.unmodifiableSet( - new HashSet<>(Arrays.asList(new String[] { TaskStatus.TASK_STATE_NEW, TaskStatus.TASK_STATE_ALLOCATED, - TaskStatus.TASK_STATE_PENDING, TaskStatus.TASK_STATE_ASSIGNED, TaskStatus.TASK_STATE_ACCEPTED, - TaskStatus.TASK_STATE_PREPARING, TaskStatus.TASK_STATE_READY, TaskStatus.TASK_STATE_STARTING, }))); + new HashSet<>(Arrays.asList(new String[] { TaskStatus.TASK_STATE_NEW, TaskStatus.TASK_STATE_ALLOCATED, + TaskStatus.TASK_STATE_PENDING, TaskStatus.TASK_STATE_ASSIGNED, TaskStatus.TASK_STATE_ACCEPTED, + TaskStatus.TASK_STATE_PREPARING, TaskStatus.TASK_STATE_READY, TaskStatus.TASK_STATE_STARTING, }))); /** * Task states which are considered as not finished yet. */ public static final Set UNFINISHED_TASK_STATES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( - new String[] { TaskStatus.TASK_STATE_NEW, TaskStatus.TASK_STATE_ALLOCATED, TaskStatus.TASK_STATE_PENDING, - TaskStatus.TASK_STATE_ASSIGNED, TaskStatus.TASK_STATE_ACCEPTED, TaskStatus.TASK_STATE_PREPARING, - TaskStatus.TASK_STATE_READY, TaskStatus.TASK_STATE_STARTING, TaskStatus.TASK_STATE_RUNNING, }))); + new String[] { TaskStatus.TASK_STATE_NEW, TaskStatus.TASK_STATE_ALLOCATED, TaskStatus.TASK_STATE_PENDING, + TaskStatus.TASK_STATE_ASSIGNED, TaskStatus.TASK_STATE_ACCEPTED, TaskStatus.TASK_STATE_PREPARING, + TaskStatus.TASK_STATE_READY, TaskStatus.TASK_STATE_STARTING, TaskStatus.TASK_STATE_RUNNING, }))); /** * Logging separator for type/experiment id. */ @@ -151,21 +131,21 @@ public ContainerManagerImpl() throws Exception { String email = System.getenv(USER_EMAIL_KEY); String password = System.getenv(USER_PASSWORD_KEY); String registryUrl = System.getenv().containsKey(REGISTRY_URL_KEY) ? System.getenv(REGISTRY_URL_KEY) - : "git.project-hobbit.eu:4567"; + : "git.project-hobbit.eu:4567"; if ((username != null) && (password != null)) { gitlabAuth = RegistryAuth.builder().serverAddress(registryUrl).username(username).password(password) - .email(email).build(); + .email(email).build(); } else { LOGGER.warn( - "Couldn't load a username ({}), email ({}) and a security token ({}) to access private repositories. This platform won't be able to pull protected or private images.", - USER_NAME_KEY, USER_EMAIL_KEY, USER_PASSWORD_KEY); + "Couldn't load a username ({}), email ({}) and a security token ({}) to access private repositories. This platform won't be able to pull protected or private images.", + USER_NAME_KEY, USER_EMAIL_KEY, USER_PASSWORD_KEY); gitlabAuth = null; } gelfAddress = System.getenv(LOGGING_GELF_ADDRESS_KEY); if (gelfAddress == null) { LOGGER.info( - "Didn't find a gelf address ({}). Containers created by this platform will use the default logging.", - LOGGING_GELF_ADDRESS_KEY); + "Didn't find a gelf address ({}). Containers created by this platform will use the default logging.", + LOGGING_GELF_ADDRESS_KEY); } // try to find hobbit network in existing ones List networks = dockerClient.listNetworks(); @@ -180,7 +160,7 @@ public ContainerManagerImpl() throws Exception { if (hobbitNetwork == null) { LOGGER.warn("Could not find hobbit docker network, creating a new one"); final NetworkConfig networkConfig = NetworkConfig.builder().name(HOBBIT_DOCKER_NETWORK).driver("overlay") - .build(); + .build(); dockerClient.createNetwork(networkConfig); } } @@ -312,13 +292,13 @@ public void pullImage(String imageName) { try { Waiting.waitFor(() -> { List pullingTasks = dockerClient - .listTasks(Task.Criteria.builder().serviceName(serviceId).build()); + .listTasks(Task.Criteria.builder().serviceName(serviceId).build()); for (Task pullingTask : pullingTasks) { String state = pullingTask.status().state(); if (!UNFINISHED_TASK_STATES.contains(state)) { if (state.equals(TaskStatus.TASK_STATE_REJECTED)) { LOGGER.error("Couldn't pull image {} on node {}. {}", imageName, pullingTask.nodeId(), - pullingTask.status().err()); + pullingTask.status().err()); throw new Exception("Couldn't pull image on node " + pullingTask.nodeId() + ": " + pullingTask.status().err()); } finshedTaskIds.add(pullingTask.id()); @@ -326,7 +306,7 @@ public void pullImage(String imageName) { } if (finshedTaskIds.size() >= totalNodes) { LOGGER.info("Swarm pulled the image \"{}\" ({})", imageName, - pullingTasks.stream().map(t -> t.status().state()).collect(Collectors.joining(", "))); + pullingTasks.stream().map(t -> t.status().state()).collect(Collectors.joining(", "))); return true; } else { return false; @@ -334,8 +314,8 @@ public void pullImage(String imageName) { }, DOCKER_POLL_INTERVAL, DOCKER_IMAGE_PULL_MAX_WAITING_TIME); } catch (InterruptedException e) { LOGGER.warn( - "Interrupted while waiting for the image {} to be pulled. Assuming that pulling was successful. Exception: {}", - imageName, e.getLocalizedMessage()); + "Interrupted while waiting for the image {} to be pulled. Assuming that pulling was successful. Exception: {}", + imageName, e.getLocalizedMessage()); } dockerClient.removeService(serviceId); @@ -361,7 +341,7 @@ public void pullImage(String imageName) { * @return String the container Id or null if an error occurs */ private String createContainer(String imageName, String containerType, String parentId, String[] env, - String[] netAliases, String[] command) { + String[] netAliases, String[] command) { ServiceSpec.Builder serviceCfgBuilder = ServiceSpec.builder(); TaskSpec.Builder taskCfgBuilder = TaskSpec.builder(); @@ -390,8 +370,8 @@ private String createContainer(String imageName, String containerType, String pa containerType = parentType; } else { LOGGER.error( - "Can't create container using image {} without a container type (either a given type or one that can be derived from the parent). Returning null.", - imageName); + "Can't create container using image {} without a container type (either a given type or one that can be derived from the parent). Returning null.", + imageName); return null; } } @@ -407,7 +387,7 @@ private String createContainer(String imageName, String containerType, String pa numberOfBenchmarkSwarmNodes = clusterManager.getNumberOfNodes("org.hobbit.workergroup=benchmark"); } catch (DockerCertificateException e) { LOGGER.error("Could not initialize Cluster Manager, will use container placement constraints by default. ", - e); + e); } catch (Exception e) { LOGGER.error("Could not get number of swarm nodes. ", e); } @@ -418,28 +398,28 @@ private String createContainer(String imageName, String containerType, String pa // benchmark (in case of the benchmark controller) and the container has // type "system" if ((((parentType == null) || Constants.CONTAINER_TYPE_BENCHMARK.equals(parentType)) - && Constants.CONTAINER_TYPE_SYSTEM.equals(containerType)) - || Constants.CONTAINER_TYPE_SYSTEM.equals(parentType)) { + && Constants.CONTAINER_TYPE_SYSTEM.equals(containerType)) + || Constants.CONTAINER_TYPE_SYSTEM.equals(parentType)) { taskCfgBuilder.placement(Placement - .create(new ArrayList(Arrays.asList("node.labels.org.hobbit.workergroup==system")))); + .create(new ArrayList(Arrays.asList("node.labels.org.hobbit.workergroup==system")))); containerType = Constants.CONTAINER_TYPE_SYSTEM; } else if (Constants.CONTAINER_TYPE_DATABASE.equals(containerType) - && ((parentType == null) || Constants.CONTAINER_TYPE_BENCHMARK.equals(parentType) - || Constants.CONTAINER_TYPE_DATABASE.equals(parentType))) { + && ((parentType == null) || Constants.CONTAINER_TYPE_BENCHMARK.equals(parentType) + || Constants.CONTAINER_TYPE_DATABASE.equals(parentType))) { // defaultEnv.add("constraint:org.hobbit.workergroup==" + // Constants.CONTAINER_TYPE_DATABASE); // defaultEnv.add("constraint:org.hobbit.type==data"); // database containers have to be deployed on the benchmark nodes (see // https://github.com/hobbit-project/platform/issues/170) taskCfgBuilder.placement(Placement - .create(new ArrayList(Arrays.asList("node.labels.org.hobbit.workergroup==benchmark")))); + .create(new ArrayList(Arrays.asList("node.labels.org.hobbit.workergroup==benchmark")))); } else if (Constants.CONTAINER_TYPE_BENCHMARK.equals(containerType) - && ((parentType == null) || Constants.CONTAINER_TYPE_BENCHMARK.equals(parentType))) { + && ((parentType == null) || Constants.CONTAINER_TYPE_BENCHMARK.equals(parentType))) { taskCfgBuilder.placement(Placement - .create(new ArrayList(Arrays.asList("node.labels.org.hobbit.workergroup==benchmark")))); + .create(new ArrayList(Arrays.asList("node.labels.org.hobbit.workergroup==benchmark")))); } else { LOGGER.error("Got a request to create a container with type={} and parentType={}. " - + "Got no rule to determine its type. Returning null.", containerType, parentType); + + "Got no rule to determine its type. Returning null.", containerType, parentType); return null; } } else { @@ -557,25 +537,25 @@ public String startContainer(String imageName, String containerType, String pare @Override public String startContainer(String imageName, String containerType, String parentId, String[] env, - String[] command) { + String[] command) { return startContainer(imageName, containerType, parentId, env, null, command); } @Override public String startContainer(String imageName, String containerType, String parentId, String[] env, - String[] netAliases, String[] command) { + String[] netAliases, String[] command) { return startContainer(imageName, containerType, parentId, env, netAliases, command, true); } @Override public String startContainer(String imageName, String containerType, String parentId, String[] env, - String[] command, boolean pullImage) { + String[] command, boolean pullImage) { return startContainer(imageName, containerType, parentId, env, null, command, true); } @Override public String startContainer(String imageName, String containerType, String parentId, String[] env, - String[] netAliases, String[] command, boolean pullImage) { + String[] netAliases, String[] command, boolean pullImage) { if (pullImage) { pullImage(imageName); } @@ -594,7 +574,7 @@ public String startContainer(String imageName, String containerType, String pare @Override public String startContainer(String imageName, String containerType, String parentId, String[] env, - String[] netAliases, String[] command, String experimentId) { + String[] netAliases, String[] command, String experimentId) { this.experimentId = experimentId; return startContainer(imageName, containerType, parentId, env, netAliases, command); } @@ -605,15 +585,15 @@ public void removeContainer(String serviceName) { Integer exitCode = getContainerExitCode(serviceName); if (DEPLOY_ENV.equals(DEPLOY_ENV_DEVELOP)) { LOGGER.info( - "Will not remove container {}. " + "Will not remove container {}. " + "Development mode is enabled.", - serviceName); + serviceName); } else if (DEPLOY_ENV.equals(DEPLOY_ENV_TESTING) && (exitCode == null || exitCode != 0)) { // In testing - do not remove containers if they returned non-zero exit code LOGGER.info( - "Will not remove container {}. " + "Will not remove container {}. " + "ExitCode: {} != 0 and testing mode is enabled.", - serviceName, exitCode); + serviceName, exitCode); } else { LOGGER.info("Removing container {}. ", serviceName); dockerClient.removeService(serviceName); @@ -669,7 +649,7 @@ public void removeParentAndChildren(String parent) { } @Override - public Service getContainerInfo(String serviceName) throws InterruptedException, DockerException { + public Service getContainerInfo(String serviceName){ if (serviceName == null) { return null; } @@ -677,13 +657,18 @@ public Service getContainerInfo(String serviceName) throws InterruptedException, try { info = dockerClient.inspectService(serviceName); } catch (ServiceNotFoundException e) { - // return null + LOGGER.error(e.toString(), e); + } catch (InterruptedException e) { + LOGGER.error(e.toString(), e); + } catch (DockerException e) { + LOGGER.error(e.toString(), e); } return info; } @Override - public List getContainers(Service.Criteria criteria) { + public List getContainers(String label, String value) { + Service.Criteria criteria = Service.Criteria.builder().labels(ImmutableMap.of(label, value)).build(); try { return dockerClient.listServices(criteria); } catch (Exception e) { @@ -691,15 +676,24 @@ public List getContainers(Service.Criteria criteria) { } } + + @Override - public Integer getContainerExitCode(String serviceName) throws DockerException, InterruptedException { + public Integer getContainerExitCode(String serviceName){ if (getContainerInfo(serviceName) == null) { LOGGER.warn("Couldn't get the exit code for container {}. Service doesn't exist. Assuming it was stopped by the platform.", serviceName); return DOCKER_EXITCODE_SIGKILL; } // Service exists, but no tasks are observed. - List tasks = dockerClient.listTasks(Task.Criteria.builder().serviceName(serviceName).build()); + List tasks = null; + try { + tasks = dockerClient.listTasks(Task.Criteria.builder().serviceName(serviceName).build()); + } catch (DockerException e) { + LOGGER.error(e.toString(), e); + } catch (InterruptedException e) { + LOGGER.error(e.toString(), e); + } if (tasks.size() == 0) { LOGGER.warn("Couldn't get the exit code for container {}. Service has no tasks. Returning null.", serviceName); return null; @@ -756,7 +750,7 @@ public ContainerStats getStats(String containerId) { stats = dockerClient.stats(containerId); } catch (Exception e) { LOGGER.warn("Error while requesting usage stats for {}. Returning null. Error: {}", containerId, - e.getLocalizedMessage()); + e.getLocalizedMessage()); } return stats; } diff --git a/platform-controller/src/main/java/org/hobbit/controller/docker/ContainerStateObserverImpl.java b/platform-controller/src/main/java/org/hobbit/controller/docker/ContainerStateObserverImpl.java index 3f590917..de5986d6 100644 --- a/platform-controller/src/main/java/org/hobbit/controller/docker/ContainerStateObserverImpl.java +++ b/platform-controller/src/main/java/org/hobbit/controller/docker/ContainerStateObserverImpl.java @@ -21,6 +21,7 @@ import java.util.Timer; import java.util.TimerTask; +import org.hobbit.controller.orchestration.ContainerManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -106,7 +107,7 @@ public void run() { } } } - } catch (DockerException | InterruptedException e) { + } catch (Exception e) { LOGGER.error("Couldn't get the status of container " + id + ". It will be ignored during this run but will be checked again during the next run."); } diff --git a/platform-controller/src/main/java/org/hobbit/controller/docker/ResourceInformationCollector.java b/platform-controller/src/main/java/org/hobbit/controller/docker/ResourceInformationCollector.java deleted file mode 100644 index 6969542c..00000000 --- a/platform-controller/src/main/java/org/hobbit/controller/docker/ResourceInformationCollector.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.hobbit.controller.docker; - -import org.hobbit.controller.data.SetupHardwareInformation; -import org.hobbit.core.data.usage.ResourceUsageInformation; -import com.spotify.docker.client.messages.swarm.Service.Criteria; - -/** - * A class that can collect resource usage information. - * - * @author Michael Röder (michael.roeder@uni-paderborn.de) - * - */ -public interface ResourceInformationCollector { - - public ResourceUsageInformation getSystemUsageInformation(); - - public ResourceUsageInformation getUsageInformation(Criteria criteria); - - public SetupHardwareInformation getHardwareInformation(); - -} diff --git a/platform-controller/src/main/java/org/hobbit/controller/docker/ResourceInformationCollectorImpl.java b/platform-controller/src/main/java/org/hobbit/controller/docker/ResourceInformationCollectorImpl.java index a4384067..27e3ab1f 100644 --- a/platform-controller/src/main/java/org/hobbit/controller/docker/ResourceInformationCollectorImpl.java +++ b/platform-controller/src/main/java/org/hobbit/controller/docker/ResourceInformationCollectorImpl.java @@ -13,6 +13,8 @@ import javax.ws.rs.core.UriBuilder; import org.apache.commons.io.IOUtils; +import org.hobbit.controller.orchestration.ContainerManager; +import org.hobbit.controller.orchestration.ResourceInformationCollector; import org.hobbit.core.Constants; import org.hobbit.core.data.usage.CpuStats; import org.hobbit.core.data.usage.DiskStats; @@ -44,7 +46,7 @@ * @author Michael Röder (michael.roeder@uni-paderborn.de) * */ -public class ResourceInformationCollectorImpl implements ResourceInformationCollector { +public class ResourceInformationCollectorImpl implements ResourceInformationCollector { private static final Logger LOGGER = LoggerFactory.getLogger(ResourceInformationCollectorImpl.class); @@ -96,9 +98,7 @@ public ResourceInformationCollectorImpl(ContainerManager manager, String prometh @Override public ResourceUsageInformation getSystemUsageInformation() { - return getUsageInformation(Service.Criteria.builder() - .labels(ImmutableMap.of(ContainerManager.LABEL_TYPE, Constants.CONTAINER_TYPE_SYSTEM)) - .build()); + return getUsageInformation(); } private long countRunningTasks(String serviceName) { @@ -115,9 +115,8 @@ private long countRunningTasks(String serviceName) { } @Override - public ResourceUsageInformation getUsageInformation(Service.Criteria criteria) { - List services = manager.getContainers(criteria); - + public ResourceUsageInformation getUsageInformation() { + List services = manager.getContainers(ContainerManager.LABEL_TYPE, Constants.CONTAINER_TYPE_SYSTEM); Map containerMapping = new HashMap<>(); for (Service c : services) { containerMapping.put(c.spec().name(), c); diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/ELK Stack Final/Elasticsearch.yaml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/ELK Stack Final/Elasticsearch.yaml new file mode 100644 index 00000000..260f07ce --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/ELK Stack Final/Elasticsearch.yaml @@ -0,0 +1,102 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: es-cluster +spec: + serviceName: elasticsearch # provides association with our previously created elasticsearch Service. + replicas: 3 + selector: + matchLabels: + app: elasticsearch + template: + metadata: + labels: + app: elasticsearch + spec: + containers: + - name: elasticsearch + image: docker.elastic.co/elasticsearch/elasticsearch:7.5.0 + resources: + limits: + cpu: 1000m + memory: "1Gi" + requests: + cpu: 100m + memory: "1Gi" + ports: + - containerPort: 9200 # for REST API. + name: rest + protocol: TCP + - containerPort: 9300 # for inter-node communication. + name: inter-node + protocol: TCP + volumeMounts: + - name: node + mountPath: /usr/share/elasticsearch/data + env: + - name: cluster.name + value: k8s-logs + - name: node + valueFrom: + fieldRef: + fieldPath: metadata.name + # sets a list of master-eligible nodes in the cluster. + - name: discovery.seed_hosts + value: "es-cluster-0.elasticsearch, es-cluster-1.elasticsearch,es-cluster-2.elasticsearch" + # specifies a list of master-eligible nodes that will participate in the master election process. + - name: cluster.initial_master_nodes + value: "es-cluster-0,es-cluster-1,es-cluster-2" + - name: ES_JAVA_OPTS + value: "-Xms256m -Xmx256m" + # Each init containers run to completion in the specified order. + initContainers: + # By default k8s mounts the data directory as root, which renders it inaccessible to Elasticsearch. + - name: fix-permissions + image: busybox + command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"] + securityContext: + privileged: true + volumeMounts: + - name: node + mountPath: /usr/share/elasticsearch/data + # To prevent OOM errors. + - name: increase-vm-max-map + image: busybox + command: ["sysctl", "-w", "vm.max_map_count=262144"] + securityContext: + privileged: true + # Increase the max number of open file descriptors. + - name: increase-fd-ulimit + image: busybox + command: ["sh", "-c", "ulimit -n 65536"] + securityContext: + privileged: true + volumeClaimTemplates: + - metadata: + name: node + labels: + app: elasticsearch + spec: + accessModes: [ "ReadWriteOnce" ] + storageClassName: default + resources: + requests: + storage: 10Gi +--- +kind: Service +apiVersion: v1 +metadata: + name: elasticsearch + labels: + app: elasticsearch +spec: + selector: + app: elasticsearch + type: NodePort + ports: + - port: 9200 + name: rest + nodePort: 31920 + - port: 9300 + nodePort: 31930 + name: inter-node \ No newline at end of file diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/ELK Stack Final/Filebeat.yaml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/ELK Stack Final/Filebeat.yaml new file mode 100644 index 00000000..8910e9b6 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/ELK Stack Final/Filebeat.yaml @@ -0,0 +1,166 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: filebeat-config + labels: + k8s-app: filebeat +data: + filebeat.yml: |- + filebeat.inputs: + - type: log + paths: + - /var/log/messages + fields: + index_prefix: k8s-logs + #- type: container + # paths: + # - /var/log/containers/*.log + # fields: + # index_prefix: k8s-logs-container + # processors: + # - add_kubernetes_metadata: + # host: ${NODE_NAME} + # matchers: + # - logs_path: + # logs_path: "/var/log/containers/" + + # To enable hints based autodiscover, remove `filebeat.inputs` configuration and uncomment this: + #filebeat.autodiscover: + # providers: + # - type: kubernetes + # host: ${NODE_NAME} + # hints.enabled: true + # hints.default_config: + # type: container + # paths: + # - /var/log/containers/*${data.kubernetes.container.id}.log + + processors: + - add_cloud_metadata: + - add_host_metadata: + + output.logstash: + hosts: ["logstash:5044"] + + setup.kibana: + host: "kibana:5601" +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: filebeat + labels: + k8s-app: filebeat +spec: + selector: + matchLabels: + k8s-app: filebeat + template: + metadata: + labels: + k8s-app: filebeat + spec: + serviceAccountName: filebeat + terminationGracePeriodSeconds: 30 + hostNetwork: true + dnsPolicy: ClusterFirstWithHostNet + containers: + - name: filebeat + image: docker.elastic.co/beats/filebeat:7.10.2 + args: [ + "-c", "/etc/filebeat.yml", + "-e", + ] + env: + - name: ELASTICSEARCH_HOST + value: elasticsearch + - name: ELASTICSEARCH_PORT + value: "9200" + - name: ELASTICSEARCH_USERNAME + value: elastic + - name: ELASTICSEARCH_PASSWORD + value: changeme + - name: ELASTIC_CLOUD_ID + value: + - name: ELASTIC_CLOUD_AUTH + value: + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + securityContext: + runAsUser: 0 + # If using Red Hat OpenShift uncomment this: + #privileged: true + resources: + limits: + memory: 200Mi + requests: + cpu: 100m + memory: 100Mi + volumeMounts: + - name: config + mountPath: /etc/filebeat.yml + readOnly: true + subPath: filebeat.yml + - name: data + mountPath: /usr/share/filebeat/data + - name: varlibdockercontainers + mountPath: /var/lib/docker/containers + readOnly: true + - name: varlog + mountPath: /var/log + readOnly: true + volumes: + - name: config + configMap: + defaultMode: 0600 + name: filebeat-config + - name: varlibdockercontainers + hostPath: + path: /var/lib/docker/containers + - name: varlog + hostPath: + path: /var/log + # data folder stores a registry of read status for all files, so we don't send everything again on a Filebeat pod restart + - name: data + hostPath: + path: /var/lib/filebeat-data + type: DirectoryOrCreate +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: filebeat +subjects: +- kind: ServiceAccount + name: filebeat + namespace: kube-system +roleRef: + kind: ClusterRole + name: filebeat + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: filebeat + labels: + k8s-app: filebeat +rules: +- apiGroups: [""] # "" indicates the core API group + resources: + - namespaces + - pods + verbs: + - get + - watch + - list +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: filebeat + labels: + k8s-app: filebeat +--- \ No newline at end of file diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/ELK Stack Final/Kibana.yaml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/ELK Stack Final/Kibana.yaml new file mode 100644 index 00000000..38d91a55 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/ELK Stack Final/Kibana.yaml @@ -0,0 +1,46 @@ +apiVersion: v1 +kind: Service +metadata: + name: kibana + labels: + k8s-app: kibana +spec: + type: NodePort + ports: + - port: 5601 + nodePort: 31601 + protocol: TCP + targetPort: ui + selector: + k8s-app: kibana +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kibana + labels: + k8s-app: kibana +spec: + selector: + matchLabels: + k8s-app: kibana + template: + metadata: + labels: + k8s-app: kibana + spec: + containers: + - name: kibana + image: docker.elastic.co/kibana/kibana:7.5.0 + resources: + requests: + cpu: 100m + limits: + cpu: 1000m + env: + - name: ELASTICSEARCH_URL + value: http://elasticsearch.operations:9200 + ports: + - containerPort: 5601 + name: ui + protocol: TCP \ No newline at end of file diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/ELK Stack Final/Logstash.yaml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/ELK Stack Final/Logstash.yaml new file mode 100644 index 00000000..6aa8be73 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/ELK Stack Final/Logstash.yaml @@ -0,0 +1,73 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: logstash-config +data: + logstash.conf: |- + input { + beats { + port => "5044" + } + } + + output { + # You can uncomment this line to investigate the generated events by the logstash. + stdout { codec => rubydebug } + elasticsearch { + hosts => "elasticsearch:9200" + # The events will be stored in elasticsearch under previously defined index_prefix value. + index => "filebeat-%{+YYYY.MM.dd}" + } + } + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: logstash + labels: + k8s-app: logstash +spec: + selector: + matchLabels: + k8s-app: logstash + template: + metadata: + labels: + k8s-app: logstash + spec: + hostname: logstash + containers: + - name: logstash + ports: + - containerPort: 5044 + name: logstash + image: docker.elastic.co/logstash/logstash:7.5.0 + volumeMounts: + - name: logstash-config + mountPath: /usr/share/logstash/pipeline/ + command: + - logstash + volumes: + # Previously defined ConfigMap object. + - name: logstash-config + configMap: + name: logstash-config + items: + - key: logstash.conf + path: logstash.conf +--- +kind: Service +apiVersion: v1 +metadata: + name: logstash +spec: + type: NodePort + selector: + k8s-app: logstash + ports: + - protocol: TCP + port: 5044 + targetPort: 5044 + name: logstash + nodePort: 31960 \ No newline at end of file diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/ELK Stack Final/Storage.yaml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/ELK Stack Final/Storage.yaml new file mode 100644 index 00000000..9db64df4 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/ELK Stack Final/Storage.yaml @@ -0,0 +1,45 @@ +kind: PersistentVolume +apiVersion: v1 +metadata: + name: node-es-cluster-0 + labels: + app: elasticsearch +spec: + storageClassName: default + capacity: + storage: 20Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/data/elastic/elastic0" + +--- +kind: PersistentVolume +apiVersion: v1 +metadata: + name: node-es-cluster-1 + labels: + app: elasticsearch +spec: + storageClassName: default + capacity: + storage: 20Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/data/elastic/elastic1" +--- +kind: PersistentVolume +apiVersion: v1 +metadata: + name: node-es-cluster-2 + labels: + app: elasticsearch +spec: + storageClassName: default + capacity: + storage: 20Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/data/elastic/elastic2" \ No newline at end of file diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/K8sClusterManagerImplTest.java b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/K8sClusterManagerImplTest.java new file mode 100644 index 00000000..e934748e --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/K8sClusterManagerImplTest.java @@ -0,0 +1,85 @@ +package org.hobbit.controller.kubernetes; + + +import io.fabric8.kubernetes.api.model.NodeListBuilder; +import io.fabric8.kubernetes.api.model.apps.DeploymentListBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.KubernetesServer; +import org.hobbit.controller.orchestration.objects.ClusterInfo; +import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + + +import static org.junit.jupiter.api.Assertions.*; + +class K8sClusterManagerImplTest { + + @Rule + KubernetesServer server = new KubernetesServer(); + + @Rule + K8sClusterManagerImpl clusterManager = new K8sClusterManagerImpl(); + + KubernetesClient client = null; + + @BeforeEach + public void setUp(){ + server.before(); + server.expect().withPath("/api/v1/nodes").andReturn(200, new NodeListBuilder().addNewItem().withNewMetadata() + .addToLabels("org.hobbit.workergroup","system").endMetadata().and() + .addNewItem().and() + .addNewItem().and() + .build()).once(); + clusterManager.setK8sClient(server.getClient()); + client = clusterManager.getK8sClient(); + + } + + @Test + void getClusterInfo() { + final ClusterInfo info = clusterManager.getClusterInfo(); + assertNotNull(info); + } + + @Test + void getNumberOfNodes() { + long numberOfNodes = clusterManager.getNumberOfNodes(); + assertEquals(3, numberOfNodes); + } + + @Test + void testGetNumberOfNodes() { + // System.out.println(client.nodes().withLabelIn("org.hobbit.workergroup","system" ).list().getItems()); + long numberOfNodes = clusterManager.getNumberOfNodes("org.hobbit.workergroup=system"); + assertEquals(1, numberOfNodes); + } + + @Test + void isClusterHealthy() { + boolean isHealthy = clusterManager.isClusterHealthy(); + assertTrue(isHealthy); + } + + @Test + void setTaskHistoryLimit() { + server.expect().withPath("/apis/apps/v1/deployments").andReturn(200, new DeploymentListBuilder() + .addNewItem().editOrNewSpec().withRevisionHistoryLimit(5).endSpec().and() + .addNewItem().editOrNewSpec().withRevisionHistoryLimit(5).endSpec().and() + .addNewItem().editOrNewSpec().withRevisionHistoryLimit(5).endSpec().and() + .build()).once(); + + clusterManager.setTaskHistoryLimit(0); + server.expect().withPath("/apis/apps/v1/deployments").andReturn(200, new DeploymentListBuilder() + .addNewItem().editOrNewSpec().withRevisionHistoryLimit(0).endSpec().and() + .addNewItem().editOrNewSpec().withRevisionHistoryLimit(0).endSpec().and() + .addNewItem().editOrNewSpec().withRevisionHistoryLimit(0).endSpec().and() + .build()).times(2); + Integer taskHistoryLimit = clusterManager.getTaskHistoryLimit(); + assertEquals(0, (long) taskHistoryLimit); + + //set back to default + clusterManager.setTaskHistoryLimit(5); + } + +} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch-claim0-persistentvolumeclaim.yaml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch-claim0-persistentvolumeclaim.yaml new file mode 100644 index 00000000..4f8366dc --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch-claim0-persistentvolumeclaim.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: elasticsearch-claim0 + name: elasticsearch-claim0 +spec: + storageClassName: manual + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +status: {} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch-claim1-persistentvolumeclaim.yaml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch-claim1-persistentvolumeclaim.yaml new file mode 100644 index 00000000..19ead725 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch-claim1-persistentvolumeclaim.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: elasticsearch-claim1 + name: elasticsearch-claim1 +spec: + storageClassName: manual + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 2Gi +status: {} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch-claim2-persistentvolumeclaim.yaml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch-claim2-persistentvolumeclaim.yaml new file mode 100644 index 00000000..32a9bba9 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch-claim2-persistentvolumeclaim.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: elasticsearch-claim2 + name: elasticsearch-claim2 +spec: + storageClassName: manual + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 2Gi +status: {} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch-deployment.yaml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch-deployment.yaml new file mode 100644 index 00000000..f3cae223 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch-deployment.yaml @@ -0,0 +1,65 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose -f docker-compose-elk.yml convert + kompose.version: 1.22.0 (955b78124) + creationTimestamp: null + labels: + io.kompose.service: elasticsearch + name: elasticsearch +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: elasticsearch + strategy: + type: Recreate + template: + metadata: + annotations: + kompose.cmd: kompose -f docker-compose-elk.yml convert + kompose.version: 1.22.0 (955b78124) + creationTimestamp: null + labels: + io.kompose.network/hobbit: "true" + io.kompose.network/hobbit-core: "true" + io.kompose.network/hobbit-services: "true" + io.kompose.service: elasticsearch + spec: + containers: + - args: + - elasticsearch + - -E + - network.host=0.0.0.0 + image: 'docker.elastic.co/elasticsearch/elasticsearch:7.9.3' + name: elasticsearch + ports: + - containerPort: 9200 + - containerPort: 9300 + resources: + limits: + memory: "8589934592" + volumeMounts: + - mountPath: /usr/share/elasticsearch/config/elasticsearch.yml + name: elasticsearch-claim0 + readOnly: true + - mountPath: /usr/share/elasticsearch/config/jvm.options + name: elasticsearch-claim1 + readOnly: true + - mountPath: /usr/share/elasticsearch/data + name: elasticsearch-claim2 + restartPolicy: Always + volumes: + - name: elasticsearch-claim0 + persistentVolumeClaim: + claimName: elasticsearch-claim0 + readOnly: true + - name: elasticsearch-claim1 + persistentVolumeClaim: + claimName: elasticsearch-claim1 + readOnly: true + - name: elasticsearch-claim2 + persistentVolumeClaim: + claimName: elasticsearch-claim2 +status: {} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch-new.yaml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch-new.yaml new file mode 100644 index 00000000..9a3e6e78 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch-new.yaml @@ -0,0 +1,132 @@ +# RBAC authn and authz +apiVersion: v1 +kind: ServiceAccount +metadata: + name: elasticsearch-logging + namespace: kube-system + labels: + k8s-app: elasticsearch-logging + kubernetes.io/cluster-service: "true" + addonmanager.kubernetes.io/mode: Reconcile +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: elasticsearch-logging + labels: + k8s-app: elasticsearch-logging + kubernetes.io/cluster-service: "true" + addonmanager.kubernetes.io/mode: Reconcile +rules: +- apiGroups: + - "" + resources: + - "services" + - "namespaces" + - "endpoints" + verbs: + - "get" +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + namespace: kube-system + name: elasticsearch-logging + labels: + k8s-app: elasticsearch-logging + kubernetes.io/cluster-service: "true" + addonmanager.kubernetes.io/mode: Reconcile +subjects: +- kind: ServiceAccount + name: elasticsearch-logging + namespace: kube-system + apiGroup: "" +roleRef: + kind: ClusterRole + name: elasticsearch-logging + apiGroup: "" +--- +# Elasticsearch deployment itself +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: elasticsearch-logging + namespace: kube-system + labels: + k8s-app: elasticsearch-logging + version: v6.2.4 + kubernetes.io/cluster-service: "true" + addonmanager.kubernetes.io/mode: Reconcile +spec: + serviceName: elasticsearch-logging + replicas: 2 + updateStrategy: + type: RollingUpdate + selector: + matchLabels: + k8s-app: elasticsearch-logging + version: v6.2.4 + template: + metadata: + labels: + k8s-app: elasticsearch-logging + version: v6.2.4 + kubernetes.io/cluster-service: "true" + spec: + serviceAccountName: elasticsearch-logging + containers: + - image: k8s.gcr.io/elasticsearch:v6.2.4 + name: elasticsearch-logging + resources: + # need more cpu upon initialization, therefore burstable class + limits: + cpu: 1000m + requests: + cpu: 100m + ports: + - containerPort: 9200 + name: db + protocol: TCP + - containerPort: 9300 + name: transport + protocol: TCP + volumeMounts: + - name: elasticsearch-logging + mountPath: /data + env: + - name: "NAMESPACE" + valueFrom: + fieldRef: + fieldPath: metadata.namespace + volumes: + - name: elasticsearch-logging + emptyDir: {} + # Elasticsearch requires vm.max_map_count to be at least 262144. + # If your OS already sets up this number to a higher value, feel free + # to remove this init container. + initContainers: + - image: alpine:3.6 + command: ["/sbin/sysctl", "-w", "vm.max_map_count=262144"] + name: elasticsearch-logging-init + securityContext: + privileged: true +--- +apiVersion: v1 +kind: Service +metadata: + name: elasticsearch-logging + namespace: kube-system + labels: + k8s-app: elasticsearch-logging + kubernetes.io/cluster-service: "true" + addonmanager.kubernetes.io/mode: Reconcile + kubernetes.io/name: "Elasticsearch" +spec: + type: NodePort + ports: + - port: 9200 + protocol: TCP + targetPort: db + nodePort: 31335 + selector: + k8s-app: elasticsearch-logging diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch-service.yaml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch-service.yaml new file mode 100644 index 00000000..1f8f5599 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch-service.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose -f docker-compose-elk.yml convert + kompose.version: 1.22.0 (955b78124) + creationTimestamp: null + labels: + io.kompose.service: elasticsearch + name: elasticsearch +spec: + ports: + - name: "9200" + port: 9200 + targetPort: 9200 + - name: "9300" + port: 9300 + targetPort: 9300 + selector: + io.kompose.service: elasticsearch +status: + loadBalancer: {} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch.yaml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch.yaml new file mode 100755 index 00000000..4774b49a --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/elasticsearch.yaml @@ -0,0 +1,113 @@ +apiVersion: v1 +kind: Service +metadata: + name: elasticsearch +spec: + clusterIP: None + selector: + app: es-cluster + ports: + - name: transport + port: 9300 + - name: rest + port: 9200 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: es-config +data: + elasticsearch.yml: | + cluster.name: elasticsearch-cluster + node.name: ${HOSTNAME} + node.master: true + node.data: true + network.host: 0.0.0.0 + http.port: 9200 + discovery.zen.ping.unicast.hosts: elasticsearch + discovery.zen.minimum_master_nodes: 1 + cluster.initial_master_nodes: esnode-0 + ES_JAVA_OPTS: -Xms512m -Xmx512m +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: esnode +spec: + serviceName: elasticsearch + replicas: 1 + selector: + matchLabels: + app: es-cluster + template: + metadata: + labels: + app: es-cluster + spec: + securityContext: + fsGroup: 1000 + initContainers: + - name: init-sysctl + image: busybox + imagePullPolicy: IfNotPresent + securityContext: + privileged: true + command: ["sysctl", "-w", "vm.max_map_count=262144"] + containers: + - name: elasticsearch + # resources: + # requests: + # memory: 1Gi + securityContext: + privileged: true + runAsUser: 1000 + capabilities: + add: + - IPC_LOCK + - SYS_RESOURCE + image: elasticsearch:7.10.1 + env: + - name: ES_JAVA_OPTS + valueFrom: + configMapKeyRef: + name: es-config + key: ES_JAVA_OPTS + resources: + limits: + cpu: "1024m" + memory: 1024Mi + requests: + cpu: "1024m" + memory: 512Mi + readinessProbe: + httpGet: + scheme: HTTP + path: /_cluster/health?local=true + port: 9200 + initialDelaySeconds: 5 + ports: + - containerPort: 9200 + name: es-http + - containerPort: 9300 + name: es-transport + volumeMounts: +# - name: es-data +# mountPath: /usr/share/elasticsearch/data + - name: elasticsearch-config + mountPath: /usr/share/elasticsearch/config/elasticsearch.yml + subPath: elasticsearch.yml + volumes: + - name: elasticsearch-config + configMap: + name: es-config + items: + - key: elasticsearch.yml + path: elasticsearch.yml +# volumeClaimTemplates: +# - metadata: +# name: es-data +# spec: +# accessModes: [ "ReadWriteOnce" ] +# resources: +# requests: +# storage: 25Gi \ No newline at end of file diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/es.yaml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/es.yaml new file mode 100644 index 00000000..acea28e4 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/es.yaml @@ -0,0 +1,72 @@ +apiVersion: elasticsearch.k8s.elastic.co/v1 +kind: Elasticsearch +metadata: + name: quickstart +spec: + version: 7.6.2 + nodeSets: + - name: master-nodes + count: 2 + config: + node.master: true + node.data: true + node.ml: false + node.store.allow_mmap: false + node.attr.temp: hot + podTemplate: + spec: + containers: + - name: elasticsearch + env: + - name: ES_JAVA_OPTS + value: -Xms500m -Xmx500m + resources: + requests: + cpu: 0.5 + volumeClaimTemplates: + - metadata: + name: elasticsearch-data + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + storageClassName: manual + - name: data-nodes + count: 2 + config: + node.master: false + node.data: true + node.ml: false + node.store.allow_mmap: false + node.attr.temp: warm + podTemplate: + spec: + containers: + - name: elasticsearch + env: + - name: ES_JAVA_OPTS + value: -Xms500m -Xmx500m + resources: + requests: + cpu: 0.5 + volumeClaimTemplates: + - metadata: + name: elasticsearch-data + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + storageClassName: manual + http: + service: + spec: + type: NodePort + ports: + - port: 9200 + targetPort: 9200 + protocol: TCP + nodePort: 9200 diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/filebeat.yml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/filebeat.yml new file mode 100755 index 00000000..35a2c774 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/filebeat.yml @@ -0,0 +1,150 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: filebeat + labels: + k8s-app: filebeat +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: filebeat + labels: + k8s-app: filebeat +rules: +- apiGroups: [""] # "" indicates the core API group + resources: + - namespaces + - pods + verbs: + - get + - watch + - list +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: filebeat +subjects: +- kind: ServiceAccount + name: filebeat + namespace: default +roleRef: + kind: ClusterRole + name: filebeat + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: filebeat-config + labels: + k8s-app: filebeat +data: + filebeat.yml: |- + + filebeat.inputs: + - type: container + enabled: true + paths: + - /var/log/containers/*.log + # If you setup helm for your cluster and want to investigate its logs, comment out this section. + + # To be used by Logstash for distinguishing index names while writing to elasticsearch. + fields_under_root: true + fields: + index_prefix: k8s-logs + + # Enrich events with k8s, cloud metadata + processors: + - add_cloud_metadata: + - add_host_metadata: + - add_kubernetes_metadata: + host: ${NODE_NAME} + matchers: + - logs_path: + logs_path: "/var/log/containers/" + # Send events to Logstash. + output.logstash: + enabled: true + hosts: ["logstash:9600"] + + # You can set logging.level to debug to see the generated events by the running filebeat instance. + logging.level: info + logging.to_files: false + logging.files: + path: /var/log/filebeat + name: filebeat + keepfiles: 7 + permissions: 0644 +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: filebeat + labels: + k8s-app: filebeat +spec: + selector: + matchLabels: + k8s-app: filebeat + template: + metadata: + labels: + k8s-app: filebeat + spec: + # Refers to our previously defined ServiceAccount. + serviceAccountName: filebeat + terminationGracePeriodSeconds: 30 + hostNetwork: true + dnsPolicy: ClusterFirstWithHostNet + containers: + - name: filebeat + image: docker.elastic.co/beats/filebeat:7.5.0 + args: [ + "-c", "/etc/filebeat.yml", + "-e", + ] + env: + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + securityContext: + runAsUser: 0 + # If using Red Hat OpenShift uncomment this: + #privileged: true + resources: # comment out for using full speed + limits: + memory: 200Mi + requests: + cpu: 500m + memory: 100Mi + volumeMounts: + - name: config + mountPath: /etc/filebeat.yml + readOnly: true + subPath: filebeat.yml +# - name: data +# mountPath: /usr/share/filebeat/data +# - name: varlibdockercontainers +# mountPath: /var/lib/docker/containers +# readOnly: true + volumes: + # Bind previously defined ConfigMap + - name: config + configMap: + defaultMode: 0600 + name: filebeat-config +# - name: varlibdockercontainers +# hostPath: +# path: /var/lib/docker/containers +# - name: varlog +# hostPath: +# path: /var/log + # data folder stores a registry of read status for all files, so we don't send everything again on a Filebeat pod restart +# - name: data +# hostPath: +# path: /var/lib/filebeat-data +# type: DirectoryOrCreate \ No newline at end of file diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/kibana-deployment-new.yaml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/kibana-deployment-new.yaml new file mode 100644 index 00000000..d2e42f57 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/kibana-deployment-new.yaml @@ -0,0 +1,76 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kibana-logging + namespace: kube-system + labels: + k8s-app: kibana-logging + kubernetes.io/cluster-service: "true" + addonmanager.kubernetes.io/mode: Reconcile +spec: + replicas: 1 + selector: + matchLabels: + k8s-app: kibana-logging + template: + metadata: + labels: + k8s-app: kibana-logging + annotations: + seccomp.security.alpha.kubernetes.io/pod: 'docker/kube-system' + spec: + containers: + - name: kibana-logging + image: docker.elastic.co/kibana/kibana-oss:6.2.4 + resources: + # need more cpu upon initialization, therefore burstable class + limits: + cpu: 1000m + requests: + cpu: 100m + env: + - name: ELASTICSEARCH_URL + value: http://elasticsearch-logging:9200 + ports: + - containerPort: 5601 + name: ui + protocol: TCP +--- +apiVersion: v1 +kind: Service +metadata: + name: kibana-logging + namespace: kube-system + labels: + k8s-app: kibana-logging + kubernetes.io/cluster-service: "true" + addonmanager.kubernetes.io/mode: Reconcile + kubernetes.io/name: "Kibana" +spec: + type: NodePort + ports: + - port: 5601 + protocol: TCP + targetPort: ui + nodePort: 31336 + selector: + k8s-app: kibana-logging +# --- +# apiVersion: extensions/v1beta1 +# kind: Ingress +# metadata: +# name: logs-ingress +# namespace: kube-system +# labels: +# app: logs +# annotations: +# kubernetes.io/ingress.class: traefik +# spec: +# rules: +# - host: logs.example.com +# http: +# paths: +# - path: / +# backend: +# serviceName: kibana-logging +# servicePort: 5601 diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/kibana.yaml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/kibana.yaml new file mode 100755 index 00000000..7dc10308 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/kibana.yaml @@ -0,0 +1,45 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kibana + labels: + k8s-app: kibana +spec: + selector: + matchLabels: + k8s-app: kibana + template: + metadata: + labels: + k8s-app: kibana + spec: + containers: + - name: kibana + image: docker.elastic.co/kibana/kibana:6.5.0 + resources: + requests: + cpu: 100m + limits: + cpu: 1000m + env: + - name: ELASTICSEARCH_URL + value: http://elasticsearch:9200 + ports: + - containerPort: 5601 + name: ui + protocol: TCP +--- +apiVersion: v1 +kind: Service +metadata: + name: kibana + labels: + k8s-app: kibana +spec: + ports: + - port: 5601 + protocol: TCP + targetPort: ui + selector: + k8s-app: kibana \ No newline at end of file diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/logstash-deployment.yaml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/logstash-deployment.yaml new file mode 100644 index 00000000..009ef588 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/logstash-deployment.yaml @@ -0,0 +1,86 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: logstash-configmap + namespace: kube-system +data: + logstash.yml: | + http.host: "0.0.0.0" + path.config: /usr/share/logstash/pipeline + logstash.conf: | + # all input will come from filebeat, no local logs + input { + beats { + port => 5044 + } + } + filter { + if [message] =~ /^\{.*\}$/ { + json { + source => "message" + } + } + if [ClientHost] { + geoip { + source => "ClientHost" + } + } + } + output { + elasticsearch { + hosts => [ "elasticsearch-logging:9200" ] + } + } +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: logstash-deployment + namespace: kube-system +spec: + replicas: 1 + selector: + matchLabels: + app: logstash + template: + metadata: + labels: + app: logstash + spec: + containers: + - name: logstash + image: docker.elastic.co/logstash/logstash:6.3.0 + ports: + - containerPort: 5044 + volumeMounts: + - name: config-volume + mountPath: /usr/share/logstash/config + - name: logstash-pipeline-volume + mountPath: /usr/share/logstash/pipeline + volumes: + - name: config-volume + configMap: + name: logstash-configmap + items: + - key: logstash.yml + path: logstash.yml + - name: logstash-pipeline-volume + configMap: + name: logstash-configmap + items: + - key: logstash.conf + path: logstash.conf +--- +kind: Service +apiVersion: v1 +metadata: + name: logstash-service + namespace: kube-system +spec: + selector: + app: logstash + ports: + - protocol: TCP + port: 5044 + targetPort: 5044 + type: ClusterIP diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/logstash.yaml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/logstash.yaml new file mode 100755 index 00000000..9c3efbec --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/logstash.yaml @@ -0,0 +1,108 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: logstash-config +data: + logstash.conf: |- + input { + beats { + port => "9600" + } + } + + filter { + + # Container logs are received with variable named index_prefix + # Since it is in json format, we can decode it via json filter plugin. + if [index_prefix] == "k8s-logs" { + + if [message] =~ /^\{.*\}$/ { + json { + source => "message" + skip_on_invalid_json => true + } + } + + } + + # do not expose index_prefix field to kibana + mutate { + # @metadata is not exposed outside of Logstash by default. + add_field => { "[@metadata][index_prefix]" => "%{index_prefix}-%{+YYYY.MM.dd}" } + # since we added index_prefix to metadata, we no longer need ["index_prefix"] field. + remove_field => ["index_prefix"] + } + } + output { + # You can uncomment this line to investigate the generated events by the logstash. + # stdout { codec => rubydebug } + elasticsearch { + hosts => "elasticsearch:9200" + template_overwrite => false + manage_template => false + # The events will be stored in elasticsearch under previously defined index_prefix value. + index => "%{[@metadata][index_prefix]}" + sniffing => false + } + } + logstash.yml: |- + http.host: "0.0.0.0" + xpack.monitoring.elasticsearch.hosts: [ "http://elasticsearch:9200/" ] +--- +kind: Deployment +apiVersion: apps/v1 +metadata: + name: logstash +spec: + replicas: 1 + selector: + matchLabels: + app: logstash + template: + metadata: + labels: + app: logstash + spec: + hostname: logstash + containers: + - name: logstash + ports: + - containerPort: 9600 + name: logstash + image: docker.elastic.co/logstash/logstash:7.5.0 + volumeMounts: + - name: logstash-config + mountPath: /usr/share/logstash/pipeline/ +# - name: logstash-config-yml +# mountPath: /usr/share/logstash/config/ + command: + - logstash + volumes: + # Previously defined ConfigMap object. + - name: logstash-config + configMap: + name: logstash-config + items: + - key: logstash.conf + path: logstash.conf +# - name: logstash-config-yml +# configMap: +# name: logstash-config +# items: +# - key: logstash.yml +# path: logstash.yml +--- +kind: Service +apiVersion: v1 +metadata: + name: logstash +spec: + type: NodePort + selector: + app: logstash + ports: + - protocol: TCP + port: 9600 + targetPort: 9600 + name: logstash diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/pv 2.yaml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/pv 2.yaml new file mode 100644 index 00000000..178d7867 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/pv 2.yaml @@ -0,0 +1,29 @@ +kind: PersistentVolume +apiVersion: v1 +metadata: + name: es-data-holder-01 + labels: + type: local +spec: + storageClassName: manual + capacity: + storage: 5Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/elkdata" +--- +kind: PersistentVolume +apiVersion: v1 +metadata: + name: es-data-holder-02 + labels: + type: local +spec: + storageClassName: manual + capacity: + storage: 5Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/elkdata" \ No newline at end of file diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/pv.yaml b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/pv.yaml new file mode 100644 index 00000000..29807584 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/ELK Stack/pv.yaml @@ -0,0 +1,44 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: elastic-volume-00 + labels: + type: local +spec: + storageClassName: manual + capacity: + storage: 4Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/elkdata" +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: elastic-volume-01 + labels: + type: local +spec: + storageClassName: manual + capacity: + storage: 4Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/elkdata" +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: elastic-volume-02 + labels: + type: local +spec: + storageClassName: manual + capacity: + storage: 4Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/elkdata" diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/K8sClusterManager.java b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/K8sClusterManager.java new file mode 100644 index 00000000..e3693b68 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/K8sClusterManager.java @@ -0,0 +1,22 @@ +package org.hobbit.controller.kubernetes; + +//import io.kubernetes.client.ApiException; +//import io.kubernetes.client.models.V1PodList; +import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.models.V1PodList; + +public interface K8sClusterManager { + + //sujay + public V1PodList getPodsInfo() throws ApiException; + + //sujay + public long getNumberOfNodes(); + + //Sam + public int getNumberOfNodes(String label); + + //Olu + public boolean isClusterHealthy(); + +} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/K8sClusterManagerImpl.java b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/K8sClusterManagerImpl.java new file mode 100644 index 00000000..e45e10da --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/K8sClusterManagerImpl.java @@ -0,0 +1,105 @@ +package org.hobbit.controller.kubernetes; + +import io.kubernetes.client.informer.SharedIndexInformer; +import io.kubernetes.client.informer.SharedInformerFactory; +import io.kubernetes.client.informer.cache.Lister; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.Configuration; +import io.kubernetes.client.openapi.apis.CoreV1Api; +import io.kubernetes.client.openapi.models.V1Node; +import io.kubernetes.client.openapi.models.V1NodeList; +import io.kubernetes.client.openapi.models.V1PodList; +import io.kubernetes.client.util.CallGeneratorParams; +import io.kubernetes.client.util.ClientBuilder; +import org.hobbit.controller.docker.ClusterManagerImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +public class K8sClusterManagerImpl implements K8sClusterManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(ClusterManagerImpl.class); + + private ApiClient k8sclient; + private CoreV1Api api; + private SharedInformerFactory factory; + + private long expectedNumberOfPods = 0; + private SharedIndexInformer nodeInformer; + + private String K8S_PODS_NUMBER = null; + + public K8sClusterManagerImpl() throws IOException, ApiException { + k8sclient = ClientBuilder.cluster().build(); + Configuration.setDefaultApiClient(k8sclient); + api = new CoreV1Api(); + + K8S_PODS_NUMBER = System.getenv("K8S_PODS_NUMBER"); + + factory = new SharedInformerFactory(); + + nodeInformer = + factory.sharedIndexInformerFor( + (CallGeneratorParams params) -> { + return api.listNodeCall( + null, + null, + null, + null, + null, + null, + params.resourceVersion, + params.timeoutSeconds, + params.watch, + null); + }, + V1Node.class, + V1NodeList.class); + + + expectedNumberOfPods = Integer.parseInt(K8S_PODS_NUMBER); + } + + + @Override + public V1PodList getPodsInfo() throws ApiException { + + V1PodList list = api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null); + // k8sclient = ClientBuilder.kubeconfig(KubeConfig.loadKubeConfig(new FileReader(kubeConfigPath))).build(); + // Parameters are currently commented because I do not know the right values yet + + return list; + } + + @Override + public long getNumberOfNodes() { + // Node informer + + + Lister nodeLister = new Lister(nodeInformer.getIndexer()); + + return nodeLister.list().size(); + } + + @Override + public int getNumberOfNodes(String label) { + + Lister nodeLister = new Lister(nodeInformer.getIndexer()); + V1Node node = nodeLister.get(label); + + if (node != null) + return 1; + return 0; + } + + public boolean isClusterHealthy() { + long numberOfPods = getNumberOfNodes(); + if(numberOfPods >= expectedNumberOfPods) { + return true; + } + LOGGER.debug("Cluster is not healthy ({}/{})",numberOfPods, expectedNumberOfPods); + return false; + } +} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/K8sContainerManagerImpl.java b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/K8sContainerManagerImpl.java new file mode 100644 index 00000000..1ced8704 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/K8sContainerManagerImpl.java @@ -0,0 +1,463 @@ +package org.hobbit.controller.kubernetes; + +import com.spotify.docker.client.messages.RegistryAuth; +import com.spotify.docker.client.messages.swarm.*; +import io.fabric8.kubernetes.api.model.*; +import io.fabric8.kubernetes.api.model.Service; +import io.fabric8.kubernetes.api.model.apps.*; +import io.fabric8.kubernetes.api.model.batch.Job; +import io.fabric8.kubernetes.api.model.batch.JobBuilder; +import io.fabric8.kubernetes.api.model.batch.JobList; +import io.fabric8.kubernetes.api.model.batch.JobStatus; +import io.fabric8.kubernetes.api.model.metrics.v1beta1.PodMetrics; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientException; +import io.fabric8.kubernetes.client.Watch; +import io.fabric8.kubernetes.client.Watcher; +import org.hobbit.controller.docker.ContainerStateObserver; +import org.hobbit.controller.gitlab.GitlabControllerImpl; +import org.hobbit.controller.orchestration.ClusterManager; +import org.hobbit.controller.orchestration.ContainerManager; +import org.hobbit.core.Constants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class K8sContainerManagerImpl implements ContainerManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(K8sContainerManagerImpl.class); + + public static final String DEPLOY_ENV_KEY = "DEPLOY_ENV"; + public static final String DOCKER_AUTOPULL_ENV_KEY = "DOCKER_AUTOPULL"; + public static final String LOGGING_GELF_ADDRESS_KEY = "LOGGING_GELF_ADDRESS"; + public static final String USER_NAME_KEY = "GITLAB_USER"; + public static final String USER_EMAIL_KEY = "GITLAB_EMAIL"; + public static final String USER_PASSWORD_KEY = GitlabControllerImpl.GITLAB_TOKEN_KEY; + public static final String REGISTRY_URL_KEY = "REGISTRY_URL"; + + private static final int DOCKER_MAX_NAME_LENGTH = 63; + + private static final String DEPLOY_ENV = System.getenv().containsKey(DEPLOY_ENV_KEY) + ? System.getenv().get(DEPLOY_ENV_KEY) + : "production"; + private static final String DEPLOY_ENV_TESTING = "testing"; + private static final String DEPLOY_ENV_DEVELOP = "develop"; + private static final String LOGGING_DRIVER_GELF = "gelf"; + private static final Pattern PORT_PATTERN = Pattern.compile(":[0-9]+/"); + + private static final Boolean DOCKER_AUTOPULL = System.getenv().containsKey(DOCKER_AUTOPULL_ENV_KEY) + ? System.getenv().get(DOCKER_AUTOPULL_ENV_KEY) == "1" + : true; + + private static final long DOCKER_POLL_INTERVAL = 100; + private static final long DOCKER_IMAGE_PULL_MAX_WAITING_TIME = 1200000; // 20 min + + + /** + * Default network for new containers + */ + public static final String HOBBIT_DOCKER_NETWORK = "hobbit"; + /** + * Logging pattern. + */ + public static final String LOGGING_TAG = "{{.ImageName}}/{{.Name}}/{{.ID}}"; + /** + * Task states which are considered as not running yet. + */ + public static final Set NEW_TASKS_STATES = Collections.unmodifiableSet( + new HashSet<>(Arrays.asList(new String[] { TaskStatus.TASK_STATE_NEW, TaskStatus.TASK_STATE_ALLOCATED, + TaskStatus.TASK_STATE_PENDING, TaskStatus.TASK_STATE_ASSIGNED, TaskStatus.TASK_STATE_ACCEPTED, + TaskStatus.TASK_STATE_PREPARING, TaskStatus.TASK_STATE_READY, TaskStatus.TASK_STATE_STARTING, }))); + /** + * Task states which are considered as not finished yet. + */ + public static final Set UNFINISHED_TASK_STATES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( + new String[] { TaskStatus.TASK_STATE_NEW, TaskStatus.TASK_STATE_ALLOCATED, TaskStatus.TASK_STATE_PENDING, + TaskStatus.TASK_STATE_ASSIGNED, TaskStatus.TASK_STATE_ACCEPTED, TaskStatus.TASK_STATE_PREPARING, + TaskStatus.TASK_STATE_READY, TaskStatus.TASK_STATE_STARTING, TaskStatus.TASK_STATE_RUNNING, }))); + /** + * Logging separator for type/experiment id. + */ + private static final String LOGGING_SEPARATOR = "_sep_"; + /** + * Docker client instance + */ + private KubernetesClient k8sClient; + /** + * Authentication configuration for accessing private repositories. + */ + private final RegistryAuth gitlabAuth; + /** + * Empty authentication configuration. Docker client's createService() uses + * ConfigFileRegistryAuthSupplier by default (if auth is omitted) and warns + * about not being able to use it with swarm each time. + */ + private final RegistryAuth nullAuth = RegistryAuth.builder().build(); + /** + * Observers that should be notified if a container terminates. + */ + private List containerObservers = new ArrayList<>(); + + private String gelfAddress = null; + private String experimentId = null; + + public K8sContainerManagerImpl() { + LOGGER.info("Deployed as \"{}\".", DEPLOY_ENV); + this.k8sClient = K8sUtility.getK8sClient(); + + String username = System.getenv(USER_NAME_KEY); + String email = System.getenv(USER_EMAIL_KEY); + String password = System.getenv(USER_PASSWORD_KEY); + String registryUrl = System.getenv().containsKey(REGISTRY_URL_KEY) ? System.getenv(REGISTRY_URL_KEY) + : "git.project-hobbit.eu:4567"; + if ((username != null) && (password != null)) { + gitlabAuth = RegistryAuth.builder().serverAddress(registryUrl).username(username).password(password) + .email(email).build(); + } else { + LOGGER.warn( + "Couldn't load a username ({}), email ({}) and a security token ({}) to access private repositories. This platform won't be able to pull protected or private images.", + USER_NAME_KEY, USER_EMAIL_KEY, USER_PASSWORD_KEY); + gitlabAuth = null; + } + gelfAddress = System.getenv(LOGGING_GELF_ADDRESS_KEY); + if (gelfAddress == null) { + LOGGER.info( + "Didn't find a gelf address ({}). Containers created by this platform will use the default logging.", + LOGGING_GELF_ADDRESS_KEY); + } + + } + + @Override + public String startContainer(String imageName) { + return startContainer(imageName, null, "", null); + } + + @Override + public String startContainer(String imageName, String[] command) { + return startContainer(imageName, null, "", command); + } + + @Override + public String startContainer(String imageName, String type, String parent) { + return startContainer(imageName, type, parent, null); + } + + @Override + public String startContainer(String imageName, String containerType, String parentId, String[] command) { + return startContainer(imageName, containerType, parentId, null, command); + } + + @Override + public String startContainer(String imageName, String containerType, String parentId, String[] env, String[] command) { + return startContainer(imageName, containerType, parentId, env, null, command); + } + + @Override + public String startContainer(String imageName, String containerType, String parentId, String[] env, String[] netAliases, String[] command) { + return startContainer(imageName, containerType, parentId, env, netAliases, command, true); + } + + @Override + public String startContainer(String imageName, String containerType, String parentId, String[] env, String[] command, boolean pullImage) { + return startContainer(imageName, containerType, parentId, env, null, command, true); + } + + @Override + public String startContainer(String imageName, String containerType, String parentId, String[] env, String[] netAliases, String[] command, boolean pullImage) { + /* + if (pullImage) { + pullImage(imageName); + } + * + */ + String containerId = createContainer(imageName, containerType, parentId, env, netAliases, command); + // if the creation was successful + if (containerId != null) { + for (ContainerStateObserver observer : containerObservers) { + observer.addObservedContainer(containerId); + } + return containerId; + } + return null; + } + + + private String createContainer(String imageName, String containerType, String parentId, String[] env, String[] netAliases, String[] command) { + // generate unique container name + String serviceName = getInstanceName(imageName); + EnvVar envContainer = new EnvVarBuilder().withNewName(Constants.CONTAINER_NAME_KEY).withNewValue(serviceName).build(); + List defaultEnv = new ArrayList<>(); + defaultEnv.add(envContainer); + // create env vars to pass + if (env != null) { + for(String en : env){ + String[] en_key_val = en.split("="); + EnvVar envVar = new EnvVarBuilder().withNewName(en_key_val[0]).withNewValue(en_key_val[1]).build(); + defaultEnv.add(envVar); + } + } + // create container labels + Map labels = new HashMap<>(); + labels.put(LABEL_TYPE, containerType); + if (parentId != null) { + labels.put(LABEL_PARENT, parentId); + } + + Job parent = getContainerInfo(parentId); + String parentType = (parent == null) ? null : parent.getMetadata().getLabels().get(LABEL_TYPE); + // If there is no container type try to use the parent type or return null + if (containerType == null || containerType.isEmpty()) { + if ((parentType != null) && (!parentType.isEmpty())) { + containerType = parentType; + } else { + LOGGER.error( + "Can't create container using image {} without a container type (either a given type or one that can be derived from the parent). Returning null.", + imageName); + return null; + } + } + + if (gelfAddress != null) { + Map logOptions = new HashMap<>(); + logOptions.put("gelf-address", gelfAddress); + String tag = LOGGING_TAG; + if (experimentId != null) { + tag = containerType + LOGGING_SEPARATOR + experimentId + LOGGING_SEPARATOR + LOGGING_TAG; + } + logOptions.put("tag", tag); + // Not fully understood + // taskCfgBuilder.logDriver(Driver.builder().name(LOGGING_DRIVER_GELF).options(logOptions).build()); + } + + // trigger creation + Job job = new JobBuilder() + .withApiVersion("batch/v1") + .withNewMetadata() + .withName(serviceName) + .withLabels(labels) + .endMetadata() + .withNewSpec() + .withNewTemplate() + .withNewSpec() + .addNewContainer() + .withName(serviceName) + .withImage(imageName) + .withArgs(command) + .withEnv(defaultEnv) + .endContainer() + .withRestartPolicy("OnFailure") + .endSpec() + .endTemplate() + .endSpec() + .build(); + + k8sClient.batch().jobs().inNamespace("default").create(job); + + try{ + final CountDownLatch closeLatch = new CountDownLatch(1); + k8sClient.batch().jobs().inNamespace("default").create(job); + k8sClient.batch().jobs().inNamespace("default").withName(serviceName).watch(new Watcher() { + @Override + public void eventReceived(Action action, Job resource) { + if (action.name().equals("ADDED")){ + LOGGER.info("Container {} created", serviceName); + } + else if (action.name().equals("ERROR")){ + LOGGER.error("Error creating container {}", serviceName); + } + } + @Override + public void onClose(KubernetesClientException cause) { + LOGGER.error(cause.getMessage(), cause); + closeLatch.countDown(); + } + }); + closeLatch.await(10, TimeUnit.SECONDS); + return serviceName; + }catch (InterruptedException e){ + LOGGER.error(e.getMessage()); + return null; + } + } + + @Override + public String startContainer(String imageName, String containerType, String parentId, String[] env, String[] netAliases, String[] command, String experimentId) { + this.experimentId = experimentId; + return startContainer(imageName, containerType, parentId, env, netAliases, command); + } + + @Override + public void stopContainer(String containerId) { + removeContainer(containerId); + } + + @Override + public void removeContainer(String serviceName) { + + Integer exitCode = getContainerExitCode(serviceName); + if (DEPLOY_ENV.equals(DEPLOY_ENV_DEVELOP)) { + LOGGER.info( + "Will not remove container {}. " + + "Development mode is enabled.", + serviceName); + } else if (DEPLOY_ENV.equals(DEPLOY_ENV_TESTING) && (exitCode == null || exitCode != 0)) { + // In testing - do not remove containers if they returned non-zero exit code + LOGGER.info( + "Will not remove container {}. " + + "ExitCode: {} != 0 and testing mode is enabled.", + serviceName, exitCode); + } else { + LOGGER.info("Removing container {}. ", serviceName); + k8sClient.batch().jobs().inNamespace("default").withName(serviceName).delete(); + // wait for the service to disappear + // ... + } + + } + + @Override + public void stopParentAndChildren(String parentId) { + LOGGER.error("ContainerManager.stopParentAndChildren() is deprecated! Will remove them instead"); + removeParentAndChildren(parentId); + } + + @Override + public void removeParentAndChildren(String parent) { + // stop parent + removeContainer(parent); + // find children + try { + JobList services = k8sClient.batch().jobs().inNamespace("default").withLabel(LABEL_PARENT, parent).list(); + services.getItems().forEach(c -> { + if (c != null) { + removeParentAndChildren(c.getMetadata().getName()); + } + }); + } catch (Exception e) { + LOGGER.error("Error while removing containers: " + e.toString()); + } + } + + @Override + public Integer getContainerExitCode(String serviceName) { + if (getContainerInfo(serviceName) == null) { + LOGGER.warn("Couldn't get the exit code for container {}. Service doesn't exist. Assuming it was stopped by the platform.", serviceName); + return DOCKER_EXITCODE_SIGKILL; + } + JobStatus status = k8sClient.batch().jobs().inNamespace("default") + .withName(serviceName).get().getStatus(); + if (status.getActive() == 0){ + LOGGER.warn("Couldn't get the exit code for container {}. Service has no tasks. Returning null.", serviceName); + return null; + } + if (status.getSucceeded() == 0) { + LOGGER.warn("Couldn't get the exit code for container {}. Service has no tasks. Returning null.", serviceName); + return null; + }if (status.getActive() >=1) + return 1; + return null; + } + + @Override + public Job getContainerInfo(String serviceName) { + if (serviceName == null) return null; + Job info = null; + info = k8sClient.batch().jobs().inNamespace("default").withName(serviceName).get(); + return info; + } + + @Override + public List getContainers(String label, String value) { + try { + JobList services = k8sClient.batch().jobs().inNamespace("default").withLabel(label, value).list(); + return services.getItems(); + }catch (Exception e){ + return new ArrayList<>(); + } + } + + @Override + public String getContainerId(String name) { + return name; + } + + @Override + public String getContainerName(String containerId) { + return containerId; + } + + @Override + public void addContainerObserver(ContainerStateObserver containerObserver) { + containerObservers.add(containerObserver); + } + + @Override + public void pullImage(String imageName) { + + } + + @Override + public PodMetrics getStats(String containerId) { + PodMetrics stats = null; + try { + stats = k8sClient.top().pods().metrics("default", containerId);; + } catch (Exception e) { + LOGGER.warn("Error while requesting usage stats for {}. Returning null. Error: {}", containerId, + e.getLocalizedMessage()); + } + return stats; + } + + private String getInstanceName(String imageName) { + return getInstanceName(imageName, ""); + } + + private String getInstanceName(String imageName, String prefix) { + String baseName = imageName; + // If there is a tag it has to be removed + if (containsVersionTag(baseName)) { + int pos = imageName.lastIndexOf(':'); + baseName = baseName.substring(0, pos - 1); + } + int posSlash = baseName.lastIndexOf('/'); + int posColon = baseName.lastIndexOf(':'); + if (posSlash > posColon) { + baseName = baseName.substring(posSlash + 1); + } else if (posSlash < posColon) { + baseName = baseName.substring(posColon + 1); + } + final String uuid = UUID.randomUUID().toString().replaceAll("-", ""); + StringBuilder builder = new StringBuilder(prefix.length() + baseName.length() + uuid.length() + 2); + if (prefix.length() != 0) { + builder.append(prefix); + builder.append('-'); + } + builder.append(baseName.replaceAll("[^-a-z0-9]", "-")); + int maxLength = DOCKER_MAX_NAME_LENGTH - 1 - uuid.length(); + if (builder.length() > maxLength) { + builder.setLength(maxLength); + } + builder.append('-'); + builder.append(uuid); + return builder.toString(); + } + + public static boolean containsVersionTag(String imageName) { + int pos = 0; + // Check whether the given image name contains a host with a port + Matcher matcher = PORT_PATTERN.matcher(imageName); + while (matcher.find()) { + pos = matcher.end(); + } + // Check whether there is a ':' in the remaining part of the image name + return imageName.indexOf(':', pos) >= 0; + } +} + + diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/K8sResourceInformationCollectorImpl.java b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/K8sResourceInformationCollectorImpl.java new file mode 100644 index 00000000..9a692c04 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/K8sResourceInformationCollectorImpl.java @@ -0,0 +1,292 @@ +package org.hobbit.controller.kubernetes; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.client.KubernetesClient; +import org.apache.commons.io.IOUtils; +import org.apache.jena.ext.com.google.common.collect.Streams; +import org.hobbit.controller.data.NodeHardwareInformation; +import org.hobbit.controller.data.SetupHardwareInformation; +import org.hobbit.controller.orchestration.ContainerManager; +import org.hobbit.controller.orchestration.ResourceInformationCollector; +import org.hobbit.core.Constants; +import org.hobbit.core.data.usage.CpuStats; +import org.hobbit.core.data.usage.DiskStats; +import org.hobbit.core.data.usage.MemoryStats; +import org.hobbit.core.data.usage.ResourceUsageInformation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.core.UriBuilder; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class K8sResourceInformationCollectorImpl implements ResourceInformationCollector { + + private static final Logger LOGGER = LoggerFactory.getLogger(K8sResourceInformationCollectorImpl.class); + + + public static final String PROMETHEUS_HOST_KEY = "PROMETHEUS_HOST"; + public static final String PROMETHEUS_PORT_KEY = "PROMETHEUS_PORT"; + + public static final String PROMETHEUS_HOST_DEFAULT = "localhost"; + public static final String PROMETHEUS_PORT_DEFAULT = "9090"; + + private static final String PROMETHEUS_METRIC_CPU_CORES = "machine_cpu_cores"; + private static final String PROMETHEUS_METRIC_CPU_FREQUENCY = "node_cpu_frequency_max_hertz"; + private static final String PROMETHEUS_METRIC_CPU_USAGE = "container_cpu_usage_seconds_total"; + private static final String PROMETHEUS_METRIC_FS_USAGE = "container_fs_usage_bytes"; + private static final String PROMETHEUS_METRIC_MEMORY = "node_memory_MemTotal_bytes"; + private static final String PROMETHEUS_METRIC_MEMORY_USAGE = "container_memory_usage_bytes"; + private static final String PROMETHEUS_METRIC_SWAP = "node_memory_SwapTotal_bytes"; + private static final String PROMETHEUS_METRIC_UNAME = "node_uname_info"; + + + private ContainerManager manager; + private KubernetesClient k8sClient; + private String prometheusHost; + private String prometheusPort; + + public K8sResourceInformationCollectorImpl(ContainerManager manager) { + this(manager, null, null); + } + + public K8sResourceInformationCollectorImpl(ContainerManager manager, String prometheusHost, String prometheusPort) { + this.manager = manager; + this.prometheusHost = prometheusHost; + if ((this.prometheusHost == null) && System.getenv().containsKey(PROMETHEUS_HOST_KEY)) { + this.prometheusHost = System.getenv().get(PROMETHEUS_HOST_KEY); + } + if (this.prometheusHost == null) { + LOGGER.info("Prometheus host env {} is not set. Using default {}.", PROMETHEUS_HOST_KEY, PROMETHEUS_HOST_DEFAULT); + this.prometheusHost = PROMETHEUS_HOST_DEFAULT; + } + this.prometheusPort = prometheusPort; + if ((this.prometheusPort == null) && System.getenv().containsKey(PROMETHEUS_PORT_KEY)) { + this.prometheusPort = System.getenv().get(PROMETHEUS_PORT_KEY); + } + if (this.prometheusPort == null) { + LOGGER.info("Prometheus port env {} is not set. Using default {}.", PROMETHEUS_PORT_KEY, PROMETHEUS_PORT_DEFAULT); + this.prometheusPort = PROMETHEUS_PORT_DEFAULT; + } + k8sClient = K8sUtility.getK8sClient(); + } + + @Override + public ResourceUsageInformation getSystemUsageInformation() { + return getUsageInformation(); + } + + @Override + public ResourceUsageInformation getUsageInformation() { + List services = manager.getContainers(ContainerManager.LABEL_TYPE, Constants.CONTAINER_TYPE_SYSTEM); + Map podMapping = new HashMap<>(); + for (Deployment c: services){ + podMapping.put(c.getMetadata().getName(), c); + } + ResourceUsageInformation resourceInfo = podMapping.keySet().parallelStream() + // filter all pods that are not running + .filter(d -> countRunningTasks(d) !=0) + // get the stats for the single deployment + .map(id -> requestCpuAndMemoryStats(id)) + // sum up the stats + .collect(Collectors.reducing(ResourceUsageInformation::staticMerge)).orElse(null); + return resourceInfo; + } + + protected ResourceUsageInformation requestCpuAndMemoryStats(String serviceName) { + ResourceUsageInformation resInfo = new ResourceUsageInformation(); + try { + Double value = requestAveragePrometheusValue(PROMETHEUS_METRIC_CPU_USAGE, serviceName); + if (value != null) { + resInfo.setCpuStats(new CpuStats(Math.round(value * 1000))); + } + } catch (Exception e) { + LOGGER.error("Could not get cpu usage stats for container {}", serviceName, e); + } + try { + String value = requestSamplePrometheusValue(PROMETHEUS_METRIC_MEMORY_USAGE, serviceName); + if (value != null) { + resInfo.setMemoryStats(new MemoryStats(Long.parseLong(value))); + } + } catch (Exception e) { + LOGGER.error("Could not get memory usage stats for container {}", serviceName, e); + } + try { + String value = requestSamplePrometheusValue(PROMETHEUS_METRIC_FS_USAGE, serviceName); + if (value != null) { + resInfo.setDiskStats(new DiskStats(Long.parseLong(value))); + } + } catch (Exception e) { + LOGGER.error("Could not get disk usage stats for container {}", serviceName, e); + } + return resInfo; + } + private Double requestAveragePrometheusValue(String metric, String serviceName) { + Map>> instances = requestPrometheusMetrics(new String[] {metric}, serviceName); + if (instances.size() == 0) { + return null; + } + return instances.values().stream().map(item -> item.get(metric).get(0)) + .collect( + Collectors.averagingDouble(Double::parseDouble) + ); + } + + private String requestSamplePrometheusValue(String metric, String deploymentName) { + Map>> instances = requestPrometheusMetrics(new String[] {metric}, deploymentName); + if (instances.size() == 0) { + return null; + } + return instances.values().iterator().next().get(metric).get(0); + } + + + // metrics should not contain regular expression special characters + private Map>> requestPrometheusMetrics(String[] metrics, String deploymentName) { + StringBuilder query = new StringBuilder(); + query.append('{'); + query.append("__name__=~").append('"') + .append("^").append(String.join("|", metrics)).append("$") + .append('"'); + if (deploymentName != null) { + query.append(", "); + query.append("pod_label_com_kubernetes_deployment_name=").append('"') + .append(deploymentName) + .append('"'); + } + query.append('}'); + JsonArray result = queryPrometheus(query.toString()); + // result is an array of data from all metrics from all instances + return Streams.stream(result).map(JsonElement::getAsJsonObject).collect( + // group by "instance" in the outer map + // NOTE: Prometheus must be configured + // so all extractors use the same instance value for the same swarm node + Collectors.groupingBy( + obj -> obj.getAsJsonObject("metric").get("instance").getAsString(), + // group by "__name__" (metric name) in the inner map + Collectors.groupingBy( + obj -> obj.getAsJsonObject("metric").get("__name__").getAsString(), + Collectors.mapping( + this::prometheusMetricValue, + Collectors.toList() + ) + ) + ) + ); + } + + private String prometheusMetricValue(JsonObject obj) { + JsonObject metricObj = obj.getAsJsonObject("metric"); + switch (metricObj.get("__name__").getAsString()) { + case PROMETHEUS_METRIC_UNAME: + StringBuilder builder = new StringBuilder(); + builder.append(metricObj.get("sysname").getAsString()); + builder.append(metricObj.get("release").getAsString()); + builder.append(metricObj.get("version").getAsString()); + builder.append(metricObj.get("machine").getAsString()); + return builder.toString(); + default: + return obj.get("value").getAsJsonArray().get(1).getAsString(); + } + } + + + private JsonArray queryPrometheus(String query) { + LOGGER.debug("Prometheus query: {}", query); + UriBuilder uri = UriBuilder.fromPath("/api/v1/query"); + uri.host(prometheusHost); + uri.port(Integer.parseInt(prometheusPort)); + uri.queryParam("query", "{query}"); + URL url; + try { + url = new URI("http:///").resolve(uri.build(query)).toURL(); + } catch (MalformedURLException | URISyntaxException e) { + LOGGER.error("Error while building Prometheus URL", e); + return null; + } + LOGGER.debug("Prometheus URL: {}", url); + String content = null; + try { + content = IOUtils.toString(url.openConnection().getInputStream()); + } catch (IOException e) { + LOGGER.error("Error while requesting Prometheus", e); + return null; + } + LOGGER.debug("Prometheus response: {}", content); + JsonObject root = new JsonParser().parse(content).getAsJsonObject(); + return root.getAsJsonObject("data").getAsJsonArray("result"); + } + + + + private long countRunningTasks(String serviceName) { + Deployment d = k8sClient.apps().deployments().inNamespace("default").withName(serviceName).get(); + return d.getStatus().getAvailableReplicas(); + } + + @Override + public SetupHardwareInformation getHardwareInformation() { + SetupHardwareInformation setupInfo = new SetupHardwareInformation(); + + Map>> instances = requestPrometheusMetrics(new String[] { + PROMETHEUS_METRIC_CPU_CORES, + PROMETHEUS_METRIC_CPU_FREQUENCY, + PROMETHEUS_METRIC_MEMORY, + PROMETHEUS_METRIC_SWAP, + PROMETHEUS_METRIC_UNAME, + }, null); + + for (Map.Entry>> entry : instances.entrySet()) { + NodeHardwareInformation nodeInfo = new NodeHardwareInformation(); + nodeInfo.setInstance(entry.getKey()); + + Map> metrics = entry.getValue(); + + nodeInfo.setCpu( + metrics.getOrDefault(PROMETHEUS_METRIC_CPU_CORES, new ArrayList()) + .stream().map(Long::parseLong).findAny().orElse(null), + metrics.getOrDefault(PROMETHEUS_METRIC_CPU_FREQUENCY, new ArrayList()) + .stream().map(Long::parseLong).collect(Collectors.toList()) + ); + + nodeInfo.setMemory( + metrics.getOrDefault(PROMETHEUS_METRIC_MEMORY, new ArrayList()) + .stream().map(Long::parseLong).findAny().orElse(null), + metrics.getOrDefault(PROMETHEUS_METRIC_SWAP, new ArrayList()) + .stream().map(Long::parseLong).findAny().orElse(null) + ); + + nodeInfo.setOs( + metrics.getOrDefault(PROMETHEUS_METRIC_UNAME, new ArrayList()) + .stream().findAny().orElse(null) + ); + + setupInfo.addNode(nodeInfo); + } + return setupInfo; + } +} + + + + + + + + + + + + diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/K8sUtility.java b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/K8sUtility.java new file mode 100644 index 00000000..a26b83df --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/K8sUtility.java @@ -0,0 +1,30 @@ +package org.hobbit.controller.kubernetes; + +import io.fabric8.kubernetes.client.DefaultKubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.internal.KubernetesDeserializer; +import org.hobbit.controller.kubernetes.networkAttachmentDefinitionCustomResources.NetworkAttachmentDefinition; + +public class K8sUtility { + + + private static KubernetesClient k8sClient = null; + + protected K8sUtility() { + // Exists only to defeat instantiation. + } + + public static KubernetesClient getK8sClient(){ + if(k8sClient == null) { + k8sClient = new DefaultKubernetesClient(); + } +// KubernetesDeserializer.registerCustomKind("k8s.cni.cncf.io/v1", "NetworkAttachmentDefinition", NetworkAttachmentDefinition.class); + return k8sClient; + } + + public static String defaultNamespace(String namespace){ + if (namespace == null) + namespace = "default"; + return namespace; + } +} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/NamespaceManager.java b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/NamespaceManager.java new file mode 100644 index 00000000..1daf7ae3 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/NamespaceManager.java @@ -0,0 +1,14 @@ +package org.hobbit.controller.kubernetes; + +import io.fabric8.kubernetes.api.model.NamespaceList; + +public interface NamespaceManager { + + static final String APP_CATEGORY_KEY = "app_category"; + static final String APP_CATEGORY_VALUE = "platform_controller"; + + NamespaceList getNamespaces(); + + Boolean deleteNamespaceResources(); + +} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/NamespaceManagerImpl.java b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/NamespaceManagerImpl.java new file mode 100644 index 00000000..0f4f68bb --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/NamespaceManagerImpl.java @@ -0,0 +1,28 @@ +package org.hobbit.controller.kubernetes; + +import io.fabric8.kubernetes.api.model.NamespaceList; +import io.fabric8.kubernetes.client.KubernetesClient; + + +public class NamespaceManagerImpl implements NamespaceManager { + + private KubernetesClient k8sClient; + + public NamespaceManagerImpl() { + this.k8sClient = K8sUtility.getK8sClient();; + } + + @Override + public NamespaceList getNamespaces() { + NamespaceList namespaceList = k8sClient.namespaces().withLabel(APP_CATEGORY_KEY, APP_CATEGORY_VALUE).list(); + return namespaceList; + } + + @Override + public Boolean deleteNamespaceResources() { + Boolean isDeleted = k8sClient.namespaces().withLabel(APP_CATEGORY_KEY, APP_CATEGORY_VALUE).delete(); + return isDeleted; + } + + +} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/fabric8/Deployer.java b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/fabric8/Deployer.java new file mode 100644 index 00000000..cb2003ae --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/fabric8/Deployer.java @@ -0,0 +1,35 @@ +package org.hobbit.controller.kubernetes.fabric8; + +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.api.model.apps.DeploymentList; + + +public interface Deployer { + + Deployment loadDeployment(String yaml_file); + + Deployment loadDeployment(String name, String namespace); + + Deployment createDeployment(String name, String deployLabel1, String deployLabel2, + String container, String image, String specLabel1, String specLabel2, + String namespace, int replicaCount); + + Deployment createOrReplace(Deployment deployObj, String namespace); + + + //Deployment createOrReplace(Deployment deployObj, String namespace, int replicas, List labels, List specLabels); + + DeploymentList getDeployments(String namespace); + + DeploymentList getDeployments(String namespace, String label1, String label2); + + Deployment scaleReplicas(String name, String namespace, int replicas); + + Boolean deleteDeployment(String namespace, String name ); + + void scaleDeployment(String namespace, String name, int scale); + + void getDeploymentLogs(String namespace, String name); + + +} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/fabric8/DeployerImpl.java b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/fabric8/DeployerImpl.java new file mode 100644 index 00000000..98d040b1 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/fabric8/DeployerImpl.java @@ -0,0 +1,140 @@ +package org.hobbit.controller.kubernetes.fabric8; + +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder; +import io.fabric8.kubernetes.api.model.apps.DeploymentList; +import io.fabric8.kubernetes.client.KubernetesClient; +import org.hobbit.controller.docker.ClusterManagerImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.util.List; + +public class DeployerImpl implements Deployer { + + private static final Logger LOGGER = LoggerFactory.getLogger(ClusterManagerImpl.class); + + private KubernetesClient kubeClient; + + public DeployerImpl() { + this.kubeClient = K8sUtility.kubeClient; + } + + @Override + public Deployment loadDeployment(String yaml_file) { + try { + Deployment deployment = kubeClient.apps().deployments().load(new FileInputStream(yaml_file)).get(); + return deployment; + } catch (FileNotFoundException e) { + LOGGER.debug(e.getMessage()); + e.printStackTrace(); + return null; + } + } + + @Override + public Deployment loadDeployment(String name, String namespace) { + namespace = K8sUtility.defaultNamespace(namespace); + Deployment deployment = kubeClient.apps().deployments() + .inNamespace(namespace) + .withName(name).get(); + return deployment; + } + + @Override + public Deployment createDeployment(String name, String deployLabel1, String deployLabel2, + String container, String image, String specLabel1, String specLabel2, + String namespace, int replicaCount) { + namespace = K8sUtility.defaultNamespace(namespace); + Deployment deployment = new DeploymentBuilder() + .withNewMetadata() + .withName(name) + .addToLabels(deployLabel1, deployLabel2) + .endMetadata() + .withNewSpec() + .withReplicas(replicaCount) + .withNewTemplate() + .withNewMetadata() + .addToLabels(specLabel1, specLabel2) + .endMetadata() + .withNewSpec() + .addNewContainer() + .withName(container) + .withImage(image) + .withCommand("sleep","36000") + .endContainer() + .endSpec() + .endTemplate() + .withNewSelector() + .addToMatchLabels(specLabel1,specLabel1) + .endSelector() + .endSpec() + .build(); + + kubeClient.apps().deployments().inNamespace(namespace).create(deployment); + + return deployment; + } + + @Override + public Deployment createOrReplace(Deployment deployObj, String namespace) { + namespace = K8sUtility.defaultNamespace(namespace); + Deployment deployment = kubeClient.apps().deployments().inNamespace(namespace).createOrReplace(deployObj); + return deployment; + } + + /* + @Override + public Deployment createOrReplace(Deployment deployObj, String namespace, int replicas, List labels, List specLabels) { + return null; + } + */ + + @Override + public DeploymentList getDeployments(String namespace) { + namespace = K8sUtility.defaultNamespace(namespace); + DeploymentList deployments = kubeClient.apps().deployments().inNamespace(namespace).list(); + return deployments; + } + + @Override + public DeploymentList getDeployments(String namespace, String label1, String label2) { + namespace = K8sUtility.defaultNamespace(namespace); + DeploymentList deployments = kubeClient.apps().deployments().inNamespace(namespace).withLabel(label1, label2).list(); + return deployments; + } + + @Override + public Deployment scaleReplicas(String name, String namespace, int replicas) { + namespace = K8sUtility.defaultNamespace(namespace); + Deployment scaledDeployment = kubeClient.apps().deployments().inNamespace(namespace) + .withName(name).edit() + .editSpec().withReplicas(replicas).endSpec().done(); + + return scaledDeployment; + } + + @Override + public Boolean deleteDeployment(String namespace, String name) { + namespace = K8sUtility.defaultNamespace(namespace); + Boolean delete = kubeClient.apps().deployments().inNamespace(namespace).withName(name).delete(); + return delete; + } + + + @Override + public void scaleDeployment(String namespace, String name, int scale) { + namespace = K8sUtility.defaultNamespace(namespace); + kubeClient.apps().deployments().inNamespace(namespace).withName(name).scale(scale); + } + + @Override + public void getDeploymentLogs(String namespace, String name) { + namespace = K8sUtility.defaultNamespace(namespace); + kubeClient.apps().deployments().inNamespace(namespace).withName(name).watchLog(System.out); + } + + +} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/fabric8/K8sUtility.java b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/fabric8/K8sUtility.java new file mode 100644 index 00000000..eeb35220 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/fabric8/K8sUtility.java @@ -0,0 +1,25 @@ +package org.hobbit.controller.kubernetes.fabric8; + +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.ConfigBuilder; +import io.fabric8.kubernetes.client.DefaultKubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClient; + +public class K8sUtility { + // current placeholder dummy URL to be changed + public static final String MASTER_URL = "https://192.168.42.20:8443/"; + + public static Config kubeConfig = new ConfigBuilder() + .withMasterUrl(K8sUtility.MASTER_URL) + .build(); + + public static KubernetesClient kubeClient =new DefaultKubernetesClient(kubeConfig); + + public static String defaultNamespace(String namespace){ + if (namespace == null) + namespace = "default"; + return namespace; + } + + +} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/Config.java b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/Config.java new file mode 100644 index 00000000..4ff148f9 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/Config.java @@ -0,0 +1,67 @@ +package org.hobbit.controller.kubernetes.networkAttachmentDefinitionCustomResources; + +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import io.fabric8.kubernetes.api.model.KubernetesResource; + + +public class Config implements KubernetesResource { + + private String cniVersion; + private String type; + private String master; + private String mode; + private Ipam ipam; + + public String getCniVersion() { + return cniVersion; + } + + public void setCniVersion(String cniVersion) { + this.cniVersion = cniVersion; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getMaster() { + return master; + } + + public void setMaster(String master) { + this.master = master; + } + + public String getMode() { + return mode; + } + + public void setMode(String mode) { + this.mode = mode; + } + + public Ipam getIpam() { + return ipam; + } + + public void setIpam(Ipam ipam) { + this.ipam = ipam; + } + + + @Override + public String toString() { + return "Config{" + + "cniVersion='" + cniVersion + '\'' + + ", type='" + type + '\'' + + ", master='" + master + '\'' + + ", mode='" + mode + '\'' + + ", ipam=" + ipam + + '}'; + } +} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/DoneableNetworkAttachmentDefinition.java b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/DoneableNetworkAttachmentDefinition.java new file mode 100644 index 00000000..c3f93111 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/DoneableNetworkAttachmentDefinition.java @@ -0,0 +1,11 @@ +package org.hobbit.controller.kubernetes.networkAttachmentDefinitionCustomResources; + +import io.fabric8.kubernetes.api.builder.Function; +import io.fabric8.kubernetes.client.CustomResourceDoneable; + +public class DoneableNetworkAttachmentDefinition extends CustomResourceDoneable { + + public DoneableNetworkAttachmentDefinition(NetworkAttachmentDefinition resource, Function function) { + super(resource, function); + } +} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/Ipam.java b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/Ipam.java new file mode 100644 index 00000000..c89a1859 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/Ipam.java @@ -0,0 +1,75 @@ +package org.hobbit.controller.kubernetes.networkAttachmentDefinitionCustomResources; + +import io.fabric8.kubernetes.api.model.KubernetesResource; + +import java.util.List; + +public class Ipam implements KubernetesResource { + + private String type; + private String subnet; + private String rangeStart; + private String rangeEnd; + private List routes; + private String gateway; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getSubnet() { + return subnet; + } + + public void setSubnet(String subnet) { + this.subnet = subnet; + } + + public String getRangeStart() { + return rangeStart; + } + + public void setRangeStart(String rangeStart) { + this.rangeStart = rangeStart; + } + + public String getRangeEnd() { + return rangeEnd; + } + + public void setRangeEnd(String rangeEnd) { + this.rangeEnd = rangeEnd; + } + + public List getRoutes() { + return routes; + } + + public void setRoutes(List routes) { + this.routes = routes; + } + + public String getGateway() { + return gateway; + } + + public void setGateway(String gateway) { + this.gateway = gateway; + } + + @Override + public String toString() { + return "Ipam{" + + "type='" + type + '\'' + + ", subnet='" + subnet + '\'' + + ", rangeStart='" + rangeStart + '\'' + + ", rangeEnd='" + rangeEnd + '\'' + + ", routes=" + routes + + ", gateway='" + gateway + '\'' + + '}'; + } +} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/NetworkAttachmentDefinition.java b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/NetworkAttachmentDefinition.java new file mode 100644 index 00000000..d94d314a --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/NetworkAttachmentDefinition.java @@ -0,0 +1,44 @@ +package org.hobbit.controller.kubernetes.networkAttachmentDefinitionCustomResources; + +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.kubernetes.client.CustomResource; + + +public class NetworkAttachmentDefinition extends CustomResource { + + private Spec spec; + + public Spec getSpec() { + return spec; + } + + public void setSpec(Spec spec) { + this.spec = spec; + } + + @Override + public String getKind() { + return super.getKind(); + } + + @Override + public String getApiVersion() { + return "k8s.cni.cncf.io/v1"; + } + + @Override + public ObjectMeta getMetadata() { + return super.getMetadata(); + } + + + @Override + public String toString() { + return "NetworkAttachmentDefinition{" + + "apiVersion='" + getApiVersion() + "'" + + ", metadata=" + getMetadata() + + ", spec=" + spec + + //", status=" + status + + "}"; + } +} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/NetworkAttachmentDefinitionList.java b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/NetworkAttachmentDefinitionList.java new file mode 100644 index 00000000..816600a4 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/NetworkAttachmentDefinitionList.java @@ -0,0 +1,6 @@ +package org.hobbit.controller.kubernetes.networkAttachmentDefinitionCustomResources; + +import io.fabric8.kubernetes.client.CustomResourceList; + +public class NetworkAttachmentDefinitionList extends CustomResourceList { +} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/Routes.java b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/Routes.java new file mode 100644 index 00000000..1f718a8d --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/Routes.java @@ -0,0 +1,22 @@ +package org.hobbit.controller.kubernetes.networkAttachmentDefinitionCustomResources; + + +public class Routes { + + private String dst; + + public String getDst() { + return dst; + } + + public void setDst(String dst) { + this.dst = dst; + } + + @Override + public String toString() { + return "Routes{" + + "dst='" + dst + '\'' + + '}'; + } +} diff --git a/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/Spec.java b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/Spec.java new file mode 100644 index 00000000..afc45162 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/kubernetes/networkAttachmentDefinitionCustomResources/Spec.java @@ -0,0 +1,27 @@ +package org.hobbit.controller.kubernetes.networkAttachmentDefinitionCustomResources; + +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import io.fabric8.kubernetes.api.model.KubernetesResource; + +@JsonDeserialize( + using = JsonDeserializer.None.class +) +public class Spec implements KubernetesResource { + private Config config; + + public Config getConfig() { + return config; + } + + public void setConfig(Config config) { + this.config = config; + } + + @Override + public String toString() { + return "Spec{" + + "config=" + config + + '}'; + } +} diff --git a/platform-controller/src/main/java/org/hobbit/controller/orchestration/ClusterManager.java b/platform-controller/src/main/java/org/hobbit/controller/orchestration/ClusterManager.java new file mode 100644 index 00000000..2a1963da --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/orchestration/ClusterManager.java @@ -0,0 +1,20 @@ +package org.hobbit.controller.orchestration; + +import org.hobbit.controller.orchestration.objects.ClusterInfo; + +public interface ClusterManager { + + ClusterInfo getClusterInfo(); + + long getNumberOfNodes(); + + long getNumberOfNodes(String label); + + boolean isClusterHealthy(); + + long getExpectedNumberOfNodes(); + + void setTaskHistoryLimit(Integer taskHistoryLimit); + + int getTaskHistoryLimit(); +} diff --git a/platform-controller/src/main/java/org/hobbit/controller/orchestration/ContainerManager.java b/platform-controller/src/main/java/org/hobbit/controller/orchestration/ContainerManager.java new file mode 100644 index 00000000..2958dc66 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/orchestration/ContainerManager.java @@ -0,0 +1,84 @@ +package org.hobbit.controller.orchestration; + +import org.hobbit.controller.docker.ContainerStateObserver; + +import java.util.List; + +public interface ContainerManager { + + /** + * Exit code of containers + * where process was terminated with SIGKILL (number 9). + */ + public static int DOCKER_EXITCODE_SIGKILL = 128 + 9; + + /** + * Label that denotes container type + */ + public static final String LABEL_TYPE = "org.hobbit.type"; + /** + * Label that denotes container parent + */ + public static final String LABEL_PARENT = "org.hobbit.parent"; + + @Deprecated + public String startContainer(String imageName); + + String startContainer(String imageName, String[] command); + + String startContainer(String imageName, String type, String parent); + + String startContainer(String imageName, String containerType, String parentId, String[] command); + + String startContainer(String imageName, String containerType, String parentId, String[] env, + String[] command); + + String startContainer(String imageName, String containerType, String parentId, String[] env, + String[] netAliases, String[] command); + + String startContainer(String imageName, String containerType, String parentId, String[] env, + String[] command, boolean pullImage); + + String startContainer(String imageName, String containerType, String parentId, String[] env, + String[] netAliases, String[] command, boolean pullImage); + + String startContainer(String imageName, String containerType, String parentId, String[] env, + String[] netAliases, String[] command, String experimentId); + + @Deprecated + void stopContainer(String containerId); + + void removeContainer(String serviceName); + + @Deprecated + void stopParentAndChildren(String parentId); + + void removeParentAndChildren(String parent); + + Integer getContainerExitCode(String serviceName); + + ReplicationController getContainerInfo(String serviceName); + + List getContainers(String label, String value); + + @Deprecated + String getContainerId(String name); + + @Deprecated + String getContainerName(String containerId); + + void addContainerObserver(ContainerStateObserver containerObserver); + + void pullImage(String imageName); + + /** + * Returns statistics of the container with the given Id or {@code null} if the + * container can not be found or an error occurs. + * + * @param containerId + * the Id of the container for which statistics should be requested + * @return statistics of the container with the given Id or {@code null} if the + * container can not be found or an error occurs. + */ + Metrics getStats(String containerId); +} diff --git a/platform-controller/src/main/java/org/hobbit/controller/orchestration/ResourceInformationCollector.java b/platform-controller/src/main/java/org/hobbit/controller/orchestration/ResourceInformationCollector.java new file mode 100644 index 00000000..3263a7d6 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/orchestration/ResourceInformationCollector.java @@ -0,0 +1,13 @@ +package org.hobbit.controller.orchestration; + +import org.hobbit.controller.data.SetupHardwareInformation; +import org.hobbit.core.data.usage.ResourceUsageInformation; + +public interface ResourceInformationCollector { + + ResourceUsageInformation getSystemUsageInformation(); + + ResourceUsageInformation getUsageInformation(); + + SetupHardwareInformation getHardwareInformation() throws Exception; +} diff --git a/platform-controller/src/main/java/org/hobbit/controller/orchestration/objects/ClusterInfo.java b/platform-controller/src/main/java/org/hobbit/controller/orchestration/objects/ClusterInfo.java new file mode 100644 index 00000000..a91991a7 --- /dev/null +++ b/platform-controller/src/main/java/org/hobbit/controller/orchestration/objects/ClusterInfo.java @@ -0,0 +1,438 @@ +package org.hobbit.controller.orchestration.objects; + +import com.google.common.collect.ImmutableList; + +import java.util.Date; + +public class ClusterInfo { + + private String architecture; + + private String clusterStore; + + private String cgroupDriver; + + private Integer containers; + + private Integer containersRunning; + + private Integer containersStopped; + + private Integer containersPaused; + + private Boolean cpuCfsPeriod; + + private Boolean cpuCfsQuota; + + private Boolean debug; + + private String dockerRootDir; + + private String storageDriver; + + private ImmutableList> driverStatus; + + private Boolean experimentalBuild; + + private String httpProxy; + + private String httpsProxy; + + private String id; + + private Boolean ipv4Forwarding; + + private Integer images; + + private String indexServerAddress; + + private String initPath; + + private String initSha1; + + private Boolean kernelMemory; + + private String kernelVersion; + + private ImmutableList labels; + + private Long memTotal; + + private Boolean memoryLimit; + + private Integer cpus; + + private Integer eventsListener; + + private Integer fileDescriptors; + + private Integer goroutines; + + private String name; + + private String noProxy; + + private Boolean oomKillDisable; + + private String operatingSystem; + + private String osType; + + private ImmutableList> systemStatus; + + private Date systemTime; + + public ClusterInfo() { + } + + public ClusterInfo(String architecture, String clusterStore, String cgroupDriver, Integer containers, Integer containersRunning, + Integer containersStopped, Integer containersPaused, Boolean cpuCfsPeriod, Boolean cpuCfsQuota, Boolean debug, + String dockerRootDir, String storageDriver, ImmutableList> driverStatus, Boolean experimentalBuild, + String httpProxy, String httpsProxy, String id, Boolean ipv4Forwarding, Integer images, String indexServerAddress, String initPath, + String initSha1, Boolean kernelMemory, String kernelVersion, ImmutableList labels, Long memTotal, Boolean memoryLimit, Integer cpus, + Integer eventsListener, Integer fileDescriptors, Integer goroutines, String name, String noProxy, Boolean oomKillDisable, String operatingSystem, + String osType, ImmutableList> systemStatus, Date systemTime) { + this.architecture = architecture; + this.clusterStore = clusterStore; + this.cgroupDriver = cgroupDriver; + this.containers = containers; + this.containersRunning = containersRunning; + this.containersStopped = containersStopped; + this.containersPaused = containersPaused; + this.cpuCfsPeriod = cpuCfsPeriod; + this.cpuCfsQuota = cpuCfsQuota; + this.debug = debug; + this.dockerRootDir = dockerRootDir; + this.storageDriver = storageDriver; + this.driverStatus = driverStatus; + this.experimentalBuild = experimentalBuild; + this.httpProxy = httpProxy; + this.httpsProxy = httpsProxy; + this.id = id; + this.ipv4Forwarding = ipv4Forwarding; + this.images = images; + this.indexServerAddress = indexServerAddress; + this.initPath = initPath; + this.initSha1 = initSha1; + this.kernelMemory = kernelMemory; + this.kernelVersion = kernelVersion; + this.labels = labels; + this.memTotal = memTotal; + this.memoryLimit = memoryLimit; + this.cpus = cpus; + this.eventsListener = eventsListener; + this.fileDescriptors = fileDescriptors; + this.goroutines = goroutines; + this.name = name; + this.noProxy = noProxy; + this.oomKillDisable = oomKillDisable; + this.operatingSystem = operatingSystem; + this.osType = osType; + this.systemStatus = systemStatus; + this.systemTime = systemTime; + } + + public String getArchitecture() { + return architecture; + } + + public void setArchitecture(String architecture) { + this.architecture = architecture; + } + + public String getClusterStore() { + return clusterStore; + } + + public void setClusterStore(String clusterStore) { + this.clusterStore = clusterStore; + } + + public String getCgroupDriver() { + return cgroupDriver; + } + + public void setCgroupDriver(String cgroupDriver) { + this.cgroupDriver = cgroupDriver; + } + + public Integer getContainers() { + return containers; + } + + public void setContainers(Integer containers) { + this.containers = containers; + } + + public Integer getContainersRunning() { + return containersRunning; + } + + public void setContainersRunning(Integer containersRunning) { + this.containersRunning = containersRunning; + } + + public Integer getContainersStopped() { + return containersStopped; + } + + public void setContainersStopped(Integer containersStopped) { + this.containersStopped = containersStopped; + } + + public Integer getContainersPaused() { + return containersPaused; + } + + public void setContainersPaused(Integer containersPaused) { + this.containersPaused = containersPaused; + } + + public Boolean getCpuCfsPeriod() { + return cpuCfsPeriod; + } + + public void setCpuCfsPeriod(Boolean cpuCfsPeriod) { + this.cpuCfsPeriod = cpuCfsPeriod; + } + + public Boolean getCpuCfsQuota() { + return cpuCfsQuota; + } + + public void setCpuCfsQuota(Boolean cpuCfsQuota) { + this.cpuCfsQuota = cpuCfsQuota; + } + + public Boolean getDebug() { + return debug; + } + + public void setDebug(Boolean debug) { + this.debug = debug; + } + + public String getDockerRootDir() { + return dockerRootDir; + } + + public void setDockerRootDir(String dockerRootDir) { + this.dockerRootDir = dockerRootDir; + } + + public String getStorageDriver() { + return storageDriver; + } + + public void setStorageDriver(String storageDriver) { + this.storageDriver = storageDriver; + } + + public ImmutableList> getDriverStatus() { + return driverStatus; + } + + public void setDriverStatus(ImmutableList> driverStatus) { + this.driverStatus = driverStatus; + } + + public Boolean getExperimentalBuild() { + return experimentalBuild; + } + + public void setExperimentalBuild(Boolean experimentalBuild) { + this.experimentalBuild = experimentalBuild; + } + + public String getHttpProxy() { + return httpProxy; + } + + public void setHttpProxy(String httpProxy) { + this.httpProxy = httpProxy; + } + + public String getHttpsProxy() { + return httpsProxy; + } + + public void setHttpsProxy(String httpsProxy) { + this.httpsProxy = httpsProxy; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Boolean getIpv4Forwarding() { + return ipv4Forwarding; + } + + public void setIpv4Forwarding(Boolean ipv4Forwarding) { + this.ipv4Forwarding = ipv4Forwarding; + } + + public Integer getImages() { + return images; + } + + public void setImages(Integer images) { + this.images = images; + } + + public String getIndexServerAddress() { + return indexServerAddress; + } + + public void setIndexServerAddress(String indexServerAddress) { + this.indexServerAddress = indexServerAddress; + } + + public String getInitPath() { + return initPath; + } + + public void setInitPath(String initPath) { + this.initPath = initPath; + } + + public String getInitSha1() { + return initSha1; + } + + public void setInitSha1(String initSha1) { + this.initSha1 = initSha1; + } + + public Boolean getKernelMemory() { + return kernelMemory; + } + + public void setKernelMemory(Boolean kernelMemory) { + this.kernelMemory = kernelMemory; + } + + public String getKernelVersion() { + return kernelVersion; + } + + public void setKernelVersion(String kernelVersion) { + this.kernelVersion = kernelVersion; + } + + public ImmutableList getLabels() { + return labels; + } + + public void setLabels(ImmutableList labels) { + this.labels = labels; + } + + public Long getMemTotal() { + return memTotal; + } + + public void setMemTotal(Long memTotal) { + this.memTotal = memTotal; + } + + public Boolean getMemoryLimit() { + return memoryLimit; + } + + public void setMemoryLimit(Boolean memoryLimit) { + this.memoryLimit = memoryLimit; + } + + public Integer getCpus() { + return cpus; + } + + public void setCpus(Integer cpus) { + this.cpus = cpus; + } + + public Integer getEventsListener() { + return eventsListener; + } + + public void setEventsListener(Integer eventsListener) { + this.eventsListener = eventsListener; + } + + public Integer getFileDescriptors() { + return fileDescriptors; + } + + public void setFileDescriptors(Integer fileDescriptors) { + this.fileDescriptors = fileDescriptors; + } + + public Integer getGoroutines() { + return goroutines; + } + + public void setGoroutines(Integer goroutines) { + this.goroutines = goroutines; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getNoProxy() { + return noProxy; + } + + public void setNoProxy(String noProxy) { + this.noProxy = noProxy; + } + + public Boolean getOomKillDisable() { + return oomKillDisable; + } + + public void setOomKillDisable(Boolean oomKillDisable) { + this.oomKillDisable = oomKillDisable; + } + + public String getOperatingSystem() { + return operatingSystem; + } + + public void setOperatingSystem(String operatingSystem) { + this.operatingSystem = operatingSystem; + } + + public String getOsType() { + return osType; + } + + public void setOsType(String osType) { + this.osType = osType; + } + + public ImmutableList> getSystemStatus() { + return systemStatus; + } + + public void setSystemStatus(ImmutableList> systemStatus) { + this.systemStatus = systemStatus; + } + + public Date getSystemTime() { + return systemTime; + } + + public void setSystemTime(Date systemTime) { + this.systemTime = systemTime; + } +} diff --git a/platform-controller/src/test/java/org/hobbit/controller/DockerBasedTest.java b/platform-controller/src/test/java/org/hobbit/controller/DockerBasedTest.java index 9799e8a5..d4e522fa 100644 --- a/platform-controller/src/test/java/org/hobbit/controller/DockerBasedTest.java +++ b/platform-controller/src/test/java/org/hobbit/controller/DockerBasedTest.java @@ -31,8 +31,8 @@ */ public class DockerBasedTest { protected DockerClient dockerClient; - protected static final String busyboxImageName = "busybox:latest"; - protected static final String[] sleepCommand = { "sleep", "60s" }; + public static final String busyboxImageName = "busybox:latest"; + public static final String[] sleepCommand = { "sleep", "60s" }; protected boolean findImageWithTag(final String id, final List images) { if (images != null) { diff --git a/platform-controller/src/test/java/org/hobbit/controller/docker/ClusterManagerImplTest.java b/platform-controller/src/test/java/org/hobbit/controller/docker/ClusterManagerImplTest.java index c3060c51..05a0d815 100644 --- a/platform-controller/src/test/java/org/hobbit/controller/docker/ClusterManagerImplTest.java +++ b/platform-controller/src/test/java/org/hobbit/controller/docker/ClusterManagerImplTest.java @@ -1,6 +1,6 @@ package org.hobbit.controller.docker; -import com.spotify.docker.client.messages.Info; +import org.hobbit.controller.orchestration.objects.ClusterInfo; import org.junit.Before; import org.junit.Test; @@ -17,7 +17,7 @@ public void setUp() throws Exception { @Test public void getClusterInfo() throws Exception { - final Info info = clusterManager.getClusterInfo(); + final ClusterInfo info = clusterManager.getClusterInfo(); assertNotNull(info); } @@ -55,4 +55,4 @@ public void setTaskHistoryLimit() throws Exception { } -} \ No newline at end of file +} diff --git a/platform-controller/src/test/java/org/hobbit/controller/docker/ResourceInformationCollectorTest.java b/platform-controller/src/test/java/org/hobbit/controller/docker/ResourceInformationCollectorTest.java index 9adf77a9..7b69301d 100644 --- a/platform-controller/src/test/java/org/hobbit/controller/docker/ResourceInformationCollectorTest.java +++ b/platform-controller/src/test/java/org/hobbit/controller/docker/ResourceInformationCollectorTest.java @@ -8,6 +8,7 @@ import org.apache.jena.vocabulary.RDF; import org.apache.jena.vocabulary.RDFS; import org.hobbit.controller.data.SetupHardwareInformation; +import org.hobbit.controller.orchestration.ResourceInformationCollector; import org.hobbit.core.Constants; import org.hobbit.core.data.usage.ResourceUsageInformation; import org.hobbit.vocab.HOBBIT; diff --git a/platform-controller/src/test/java/org/hobbit/controller/kubernetes/K8sClusterManagerImplTest.java b/platform-controller/src/test/java/org/hobbit/controller/kubernetes/K8sClusterManagerImplTest.java new file mode 100644 index 00000000..40c75b52 --- /dev/null +++ b/platform-controller/src/test/java/org/hobbit/controller/kubernetes/K8sClusterManagerImplTest.java @@ -0,0 +1,54 @@ +package org.hobbit.controller.kubernetes; + +import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.models.V1PodList; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.*; + +public class K8sClusterManagerImplTest { + + K8sClusterManagerImpl clusterManager; + + @Before + public void setUp() { + try { + System.out.print("Into the Setup"); + //ApiClient k8sclient = ClientBuilder.standard().build(); + //Configuration.setDefaultApiClient(k8sclient); + clusterManager = new K8sClusterManagerImpl(); + } catch (IOException | ApiException e) { + e.printStackTrace(); + } + } + + @Test + public void testGetPodsInfo() { + try { + System.out.print("Into the testGetPodsInfo"); + final V1PodList info = clusterManager.getPodsInfo(); + assertNotNull(info); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + @Test + public void testGetNumberOfNodes() { + System.out.print("Into the testGetNumberOfNodes"); + long numberOfNodes = clusterManager.getNumberOfNodes(); + assertEquals(1, numberOfNodes); + } + + @Test + public void testIsClusterHealthy() { + System.out.print("Into the testIsClusterHealthy"); + boolean isHealthy = clusterManager.isClusterHealthy(); + assertTrue(isHealthy); + } + +} diff --git a/platform-controller/src/test/java/org/hobbit/controller/kubernetes/K8sContainerManagerImplTest.java b/platform-controller/src/test/java/org/hobbit/controller/kubernetes/K8sContainerManagerImplTest.java new file mode 100644 index 00000000..847a8a80 --- /dev/null +++ b/platform-controller/src/test/java/org/hobbit/controller/kubernetes/K8sContainerManagerImplTest.java @@ -0,0 +1,233 @@ +package org.hobbit.controller.kubernetes; + +import io.fabric8.kubernetes.api.model.batch.CronJob; +import io.fabric8.kubernetes.api.model.batch.CronJobBuilder; +import io.fabric8.kubernetes.api.model.batch.JobBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.KubernetesServer; +import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Collections; + +import static org.junit.Assert.*; +import static org.junit.jupiter.api.AssertFalse.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class K8sContainerManagerImplTest { + + @Rule + KubernetesServer server = new KubernetesServer(); + + @Rule + K8sContainerManagerImpl containerManager = new K8sContainerManagerImpl(); + private KubernetesClient k8sClient; + + + @BeforeEach + public void setUp(){ + server.before(); + } + + @Test + public JobBuilder startContainer(){ + + return new JobBuilder() + .withApiVersion("batch/v1") + .withNewMetadata() + .withName("job1") + .withUid("3Dc4c8746c-94fd-47a7-ac01-11047c0323b4") + .withLabels(Collections.singletonMap("label1", "maximum-length-of-63-characters")) + .withAnnotations(Collections.singletonMap("annotation1", "some-very-long-annotation")) + .endMetadata() + .withNewSpec() + .withNewTemplate() + .withNewSpec() + .addNewContainer() + .withName("pi") + .withImage("perl") + .withArgs("perl", "-Mbignum=bpi", "-wle", "print bpi(2000)") + .endContainer() + .withRestartPolicy("Never") + .endSpec() + .endTemplate() + .endSpec(); + } +} + + + @Test + void testStartContainer() { + } + + @Test + void testStartContainer1() { + + KubernetesClient k8sClient = server.getClient(); + + CronJob cronjob = k8sClient.batch().cronjobs().withName(name).get(); + assertNotNull(1,CronJob); + + + } + + @Test + void testStartContainer2() { + + CronJob cronjob = k8sClient.batch().cronjobs().withName(name).get(); + assertNull(2,CronJob); + + } + + @Test + void testStartContainer3() { + + CronJob cronjob = k8sClient.batch().cronjobs().withName(name).get(); + assertNull(3,CronJob); + + } + + @Test + void testStartContainer4() { + + CronJob cronjob = k8sClient.batch().cronjobs().withName(name).get(); + assertNull(4,CronJob); + + } + + @Test + void testStartContainer5() { + + CronJob cronjob = k8sClient.batch().cronjobs().withName(name).get(); + assertEquals(5, CronJob); + } + + + + @Test + void testStartContainer6() { + + CronJob cronjob = k8sClient.batch().cronjobs().withName(name).get(); + assertNull(6, CronJob); + + + } + + @Test + void testStartContainer7() { + + CronJob cronjob = k8sClient.batch().cronjobs().withName(name).get(); + assertNull(7, CronJob); + + + } + + @Test + void testStartContainer8() { + + CronJob cronjob = k8sClient.batch().cronjobs().withName(name).get(); + assertNull(8, CronJob); + + + } + + @Test + void stopContainer() { + } + + @Test + void removeContainer() { + + CronJob cronjob1 = new CronJobBuilder().withNewMetadata() + .withNamespace("test") + .withName("cronjob1") + .withResourceVersion("1") + .endMetadata() + .withNewSpec() + .endSpec() + .withNewStatus() + .endStatus() + .build(); + + Boolean deleted = k8sClient.batch().cronjobs().inAnyNamespace().delete(cronjob3); + assertFalse(deleted); + } + + @Test + void stopParentAndChildren() { + + + + + + } + + + + @Test + void removeParentAndChildren() { + + + Boolean deleted = k8sClient.batch().cronjobs().inAnyNamespace().delete(cronjob1, cronjob2); + assertTrue(deleted); + + deleted = k8sClient.batch().cronjobs().inAnyNamespace().delete(cronjob3); + assertFalse(deleted); + } + + + } + + @Test + void getContainerExitCode() { + } + + @Test + void getContainerInfo() { + } + + @Test + void getContainers() { + } + + @Test + void getContainerId() { + server.expect().withPath("/apis/batch/v1beta1/namespaces/test/cronjobs?labelSelector=" + Utils.toUrlEncoded("key1=value1,key2=value2")).andReturn(200, new CronJobListBuilder().addNewItem().and() + .build()).once(); + + CronJobList cronJobList = client.batch().cronjobs() + .withLabel("key1", "value1") + + assertNotNull(cronJobList); + assertEquals(0, cronJobList.getItems().size()); + + + } + + @Test + void getContainerName() { + } + + @Test + void addContainerObserver() { + } + + @Test + void pullImage() { + + kubernetesClient client = server.getClient(); + assertNotNull(client.batch().cronjobs().load(getClass().getResourceAsStream("/test-cronjob.yml")).get()); + } + + } + + @Test + void getStats() { + } + + @Test + void containsVersionTag() { + } +} diff --git a/platform-controller/src/test/java/org/hobbit/controller/kubernetes/K8sResourceInformationCollectorImplTest.java b/platform-controller/src/test/java/org/hobbit/controller/kubernetes/K8sResourceInformationCollectorImplTest.java new file mode 100644 index 00000000..bbef54ec --- /dev/null +++ b/platform-controller/src/test/java/org/hobbit/controller/kubernetes/K8sResourceInformationCollectorImplTest.java @@ -0,0 +1,238 @@ +package org.hobbit.controller.kubernetes; + +import com.spotify.docker.client.messages.ContainerStats; +import com.spotify.docker.client.messages.swarm.Service; +import io.fabric8.kubernetes.api.model.DoneableNode; +import io.fabric8.kubernetes.api.model.Node; +import io.fabric8.kubernetes.api.model.NodeList; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation; +import io.fabric8.kubernetes.client.dsl.Resource; +import io.fabric8.kubernetes.client.server.mock.KubernetesServer; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.sparql.vocabulary.DOAP; +import org.apache.jena.vocabulary.RDF; +import org.apache.jena.vocabulary.RDFS; +import org.hobbit.controller.data.SetupHardwareInformation; +import org.hobbit.controller.docker.ContainerManagerBasedTest; +import org.hobbit.controller.docker.ContainerManagerImpl; +import org.hobbit.controller.orchestration.ContainerManager; +import org.hobbit.core.Constants; +import org.hobbit.core.data.usage.ResourceUsageInformation; +import org.hobbit.vocab.HOBBIT; +import org.hobbit.vocab.MEXCORE; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.contrib.java.lang.system.EnvironmentVariables; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.hobbit.controller.DockerBasedTest.busyboxImageName; +import static org.hobbit.controller.DockerBasedTest.sleepCommand; +import static org.junit.Assert.assertNotNull; + +public class K8sResourceInformationCollectorImpl extends ContainerManagerBasedTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(K8sResourceInformationCollectorImpl.class); + + public static final String PROMETHEUS_HOST_KEY = "PROMETHEUS_HOST"; + public static final String PROMETHEUS_PORT_KEY = "PROMETHEUS_PORT"; + + public static final String PROMETHEUS_HOST_DEFAULT = "localhost"; + public static final String PROMETHEUS_PORT_DEFAULT = "9090"; + + private static final String PROMETHEUS_METRIC_CPU_CORES = "machine_cpu_cores"; + private static final String PROMETHEUS_METRIC_CPU_FREQUENCY = "node_cpu_frequency_max_hertz"; + private static final String PROMETHEUS_METRIC_CPU_USAGE = "container_cpu_usage_seconds_total"; + private static final String PROMETHEUS_METRIC_FS_USAGE = "container_fs_usage_bytes"; + private static final String PROMETHEUS_METRIC_MEMORY = "node_memory_MemTotal_bytes"; + private static final String PROMETHEUS_METRIC_MEMORY_USAGE = "container_memory_usage_bytes"; + private static final String PROMETHEUS_METRIC_SWAP = "node_memory_SwapTotal_bytes"; + private static final String PROMETHEUS_METRIC_UNAME = "node_uname_info"; + + + @Rule + public KubernetesServer server = new KubernetesServer(); + public KubernetesClient k8sClient = K8sUtility.getK8sClient(); + public final EnvironmentVariables environmentVariables = new EnvironmentVariables(); + + + + @Before + public void setEnv() { + environmentVariables.set( + K8sResourceInformationCollectorImpl.PROMETHEUS_HOST_KEY, + K8sResourceInformationCollectorImpl.PROMETHEUS_HOST_DEFAULT); + environmentVariables.set( + K8sResourceInformationCollectorImpl.PROMETHEUS_PORT_KEY, + K8sResourceInformationCollectorImpl.PROMETHEUS_PORT_DEFAULT); + } + + + + @Test + public void test() throws Exception { + LOGGER.info("Waiting to avoid failing container creation, which happens when running the full test suite..."); + Thread.sleep(10000); + + LOGGER.info("Creating first container..."); + String containerId = manager.startContainer(busyboxImageName, + Constants.CONTAINER_TYPE_SYSTEM, null, sleepCommand); + assertNotNull("Container ID", containerId); + services.add(containerId); + + LOGGER.info("Waiting..."); + Thread.sleep(10000); + + K8sResourceInformationCollectorImpl collector = new K8sResourceInformationCollectorImpl(manager); + LOGGER.info("Requesting system usage information..."); + ResourceUsageInformation usage = collector.getSystemUsageInformation(); + + Assert.assertNotNull("System usage information", usage); + LOGGER.info("Got {}", usage); + + Assert.assertNotNull("CPU stats", usage.getCpuStats()); + Assert.assertTrue(usage.getCpuStats().getTotalUsage() > 0); + Assert.assertNotNull("Memory stats", usage.getMemoryStats()); + Assert.assertTrue(usage.getMemoryStats().getUsageSum() > 0); + Assert.assertNotNull("Disk stats", usage.getDiskStats()); + Assert.assertTrue(usage.getDiskStats().getFsSizeSum() > 0); + + // Generate a second container + LOGGER.info("Creating second container..."); + containerId = manager.startContainer(busyboxImageName, + Constants.CONTAINER_TYPE_SYSTEM, null, sleepCommand); + assertNotNull("Container ID", containerId); + services.add(containerId); + + LOGGER.info("Waiting..."); + Thread.sleep(10000); + + LOGGER.info("Requesting system usage information..."); + ResourceUsageInformation usage2 = collector.getSystemUsageInformation(); + Assert.assertNotNull("System usage information", usage2); + LOGGER.info("Got {}", usage2); + + Assert.assertNotNull("CPU stats", usage2.getCpuStats()); + Assert.assertTrue(usage2.getCpuStats().getTotalUsage() > 0); + Assert.assertTrue(usage.getCpuStats().getTotalUsage() + <= usage2.getCpuStats().getTotalUsage()); + Assert.assertNotNull("Memory stats", usage2.getMemoryStats()); + Assert.assertTrue(usage.getMemoryStats().getUsageSum() + <= usage2.getMemoryStats().getUsageSum()); + Assert.assertNotNull("Disk stats", usage2.getDiskStats()); + Assert.assertTrue(usage.getDiskStats().getFsSizeSum() + <= usage2.getDiskStats().getFsSizeSum()); + } + + + + @Test + public void testIncreasingFsSize() throws Exception { + K8sResourceInformationCollectorImpl collector = new K8sResourceInformationCollectorImpl(manager); + final String[] command = { "sh", "-c", + "sleep 20s ; dd if=/dev/zero of=file.txt count=16024 bs=1048576 ; sleep 60s" }; + LOGGER.info("Creating container..."); + String containerId = manager.startContainer(busyboxImageName, + Constants.CONTAINER_TYPE_SYSTEM, null, command); + assertNotNull("Container ID", containerId); + services.add(containerId); + LOGGER.info("Waiting for the container {} to start...", containerId); + Thread.sleep(10000); + + LOGGER.info("Requesting system usage information..."); + ResourceUsageInformation usage = collector.getSystemUsageInformation(); + + Assert.assertNotNull("System usage information", usage); + LOGGER.info("Got {}", usage); + + Assert.assertNotNull("CPU stats", usage.getCpuStats()); + /* FIXME cpu usage */ + Assert.assertTrue("CPU usage > 0", usage.getCpuStats().getTotalUsage() > 0); + Assert.assertNotNull("Memory stats", usage.getMemoryStats()); + Assert.assertTrue("Memory usage > 0", usage.getMemoryStats().getUsageSum() > 0); + Assert.assertNotNull("Disk stats", usage.getDiskStats()); + Assert.assertTrue("Disk fs size > 0", usage.getDiskStats().getFsSizeSum() > 0); + + LOGGER.info("Waiting for the container {} to generate its file...", + containerId); + Thread.sleep(30000); + + LOGGER.info("Requesting system usage information..."); + ResourceUsageInformation usage2 = collector.getSystemUsageInformation(); + Assert.assertNotNull("System usage information", usage2); + LOGGER.info("Got {}", usage2); + + Assert.assertNotNull("CPU stats", usage2.getCpuStats()); + Assert.assertTrue("CPU usage (after generating the file) > 0", usage2.getCpuStats().getTotalUsage() > 0); + // Assert.assertTrue("We expected that the CPU time used to generate the file + // would increase the overall CPU time", + // usage.getCpuStats().getTotalUsage() < usage2.getCpuStats().getTotalUsage()); + Assert.assertNotNull("Memory stats", usage2.getMemoryStats()); + // Assert.assertTrue(usage.getMemoryStats().getUsageSum() < + // usage2.getMemoryStats().getUsageSum()); + Assert.assertNotNull("Disk stats", usage2.getDiskStats()); + // Assert.assertTrue("We expected that the Fssize would be increased when + // generating a huge file", + // usage.getDiskStats().getFsSizeSum() < usage2.getDiskStats().getFsSizeSum()); + Assert.assertTrue("We expected that the consumed memory would be increased when generating a huge file", + (usage.getMemoryStats().getUsageSum() + + usage.getDiskStats().getFsSizeSum()) + < (usage2.getMemoryStats().getUsageSum() + + usage2.getDiskStats().getFsSizeSum())); + } + + + @Test + public SetupHardwareInformation getHardwareInformation() throws Exception { + + LOGGER.info("Requesting hardware information..."); + K8sResourceInformationCollectorImpl collector = new K8sResourceInformationCollectorImpl(manager); + SetupHardwareInformation setupInfo = collector.getHardwareInformation(); + assertNotNull("Hardware information", setupInfo); + LOGGER.info("Got {}", setupInfo); + + Model m = ModelFactory.createDefaultModel(); + setupInfo.addToModel(m); + + NonNamespaceOperation> nodesInKubernetes = K8sUtility.getK8sClient().nodes(); + + + + Assert.assertEquals("Number of hobbit:comprises properties is equal to the number of nodes in the Kubernetes", + nodesInKubernetes, + m.listObjectsOfProperty(HOBBIT.comprises).toList().size()); + + Assert.assertEquals("Number of mexcore:HardwareConfiguration resources is equal to the number of nodes in the Kubernetes", + nodesInKubernetes, + m.listSubjectsWithProperty(RDF.type, MEXCORE.HardwareConfiguration).toList().size()); + + Assert.assertEquals("Number of rdfs:label properties is equal to the number of nodes in the Kubernetes", + nodesInKubernetes, + m.listObjectsOfProperty(RDFS.label).toList().size()); + + Assert.assertEquals("Number of mexcore:cpu properties is equal to the number of nodes in the Kubernetes", + nodesInKubernetes, + m.listObjectsOfProperty(MEXCORE.cpu).toList().size()); + + Assert.assertEquals("Number of mexcore:memory properties is equal to the number of nodes in the Kubernetes", + nodesInKubernetes, + m.listObjectsOfProperty(MEXCORE.memory).toList().size()); + + Assert.assertEquals("Number of doap:os properties is equal to the number of nodes in the Kubernetes", + nodesInKubernetes, + m.listObjectsOfProperty(DOAP.os).toList().size()); + + Assert.assertEquals("Total number of statements in hardware information model", + 1 + 6 * nodesInKubernetes, + m.listStatements().toList().size()); + return setupInfo; + } + +} + + + diff --git a/platform-controller/src/test/java/org/hobbit/controller/kubernetes/ServiceManagerImplTest.java b/platform-controller/src/test/java/org/hobbit/controller/kubernetes/ServiceManagerImplTest.java new file mode 100644 index 00000000..d9364a19 --- /dev/null +++ b/platform-controller/src/test/java/org/hobbit/controller/kubernetes/ServiceManagerImplTest.java @@ -0,0 +1,124 @@ +package org.hobbit.controller.kubernetes; + +import io.fabric8.kubernetes.api.model.IntOrString; +import io.fabric8.kubernetes.api.model.Service; +import io.fabric8.kubernetes.api.model.ServiceBuilder; +import io.fabric8.kubernetes.api.model.ServiceList; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.KubernetesServer; +import org.hobbit.controller.docker.ClusterManagerImpl; +import org.junit.Rule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.*; + +class ServiceManagerImplTest { + + @Rule; + public KubernetesServer server = new KubernetesServer(true, true); + + @Rule + ServiceManagerImpl ServiceManager = new ServiceManagerImpl(); + private static final Logger LOGGER = LoggerFactory.getLogger(ClusterManagerImpl.class); + private KubernetesClient k8sClient; + + + + @BeforeEach + void prepareService() { + + Service service = new ServiceBuilder() + .withNewMetadata() + .withName("httpbin") + .withLabels(Collections.singletonMap("app", "httpbin")) + .endMetadata() + .withNewSpec() + .addNewPort() + .withName("http") + .withPort(5511) + .withTargetPort(new IntOrString(8080)) + .endPort() + .addToSelector("deploymentconfig", "httpbin") + .endSpec() + .build(); + } + + + + @Test + void getService() { + Service svc = k8sClient.services().load(getClass().getResourceAsStream("/test-service.yml")).get(); + assertNotNull(svc); + assertEquals("httpbin", svc.getMetadata().getName()); + } + + + @Test + void testGetService() { + Service responseSvc = k8sClient.services().inNamespace("test").create(Service); + assertNotNull(responseSvc); + assertEquals("httpbin", responseSvc.getMetadata().getName()); + + } + + @Test + void createService() { + + Service service1 = new ServiceBuilder().withNewMetadata().withName("svc1").and().withNewSpec().and().build(); + Service service2 = new ServiceBuilder().withNewMetadata().withName("svc2").addToLabels("foo", "bar").and().withNewSpec().and().build(); + Service service3 = new ServiceBuilder().withNewMetadata().withName("svc3").addToLabels("foo", "bar").and().withNewSpec().and().build(); + + k8sClient.services().inNamespace("ns1").create(service1); + k8sClient.services().inNamespace("ns2").create(service2); + k8sClient.services().inNamespace("ns1").create(service3); + + } + + + @Test + ServiceList getServices() { + //When + ServiceList services = k8sClient.services().inAnyNamespace().list(); + return services; + + + //Then + ServiceList service = k8sClient.services().list(); + assertNotNull(service); + assertEquals(0, size()); + + } + + private void size() { + } + + @Test + ServiceList testGetServices() { + //When + namespace = K8sUtility.defaultNamespace(namespace); + ServiceList services = k8sClient.services().inNamespace(namespace).withLabel(label1, label2).list(); + return services; + + //Then + ServiceList = k8sClient.services().inAnyNamespace().withLabels(Collections.singletonMap("label1", "label2")).list(); + assertNotNull(services); + assertEquals(2, ServiceList.size()); + } + + + + @Test + void deleteService() { + //When + Boolean isDeleted = k8sClient.services().inNamespace(namespace).withName(name).delete(); + + //Then + assertTrue(isDeleted); + } + +} diff --git a/platform-controller/src/test/java/org/hobbit/controller/kubernetes/fabric8/DeployerImplTest.java b/platform-controller/src/test/java/org/hobbit/controller/kubernetes/fabric8/DeployerImplTest.java new file mode 100644 index 00000000..35a888b2 --- /dev/null +++ b/platform-controller/src/test/java/org/hobbit/controller/kubernetes/fabric8/DeployerImplTest.java @@ -0,0 +1,118 @@ +package org.hobbit.controller.kubernetes.fabric8; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.api.model.apps.DeploymentList; +import io.fabric8.kubernetes.api.model.autoscaling.v1.Scale; +import io.fabric8.kubernetes.api.model.extensions.DeploymentBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.KubernetesServer; +import org.hobbit.controller.docker.ClusterManagerImpl; +import org.junit.Rule; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +import static org.apache.jena.assembler.JA.namespace; +import static org.junit.jupiter.api.Assertions.*; + +class DeployerImplTest { + + @Rule + private static final Logger LOGGER = LoggerFactory.getLogger(ClusterManagerImpl.class); + private KubernetesClient kubeClient; + private String name; + + + public void DeployerImpl() { + this.kubeClient = K8sUtility.kubeClient; + } + // KubernetesClient kubeClient = K8sUtility.getK8sClient(); + public KubernetesServer server = new KubernetesServer(); + + @Test + void loadDeployment() { + + List list = kubeClient.load(getClass().getResourceAsStream("/valid-deployment-without-apiversion.json")).get(); + Deployment deployment = (Deployment) list.get(0); + + // Then + assertNotNull(deployment); + assertEquals("test", deployment.getMetadata().getName()); + assertEquals(1, deployment.getSpec().getReplicas()); + assertEquals(1, deployment.getSpec().getTemplate().getSpec().getContainers().size()); + } + + + @Test + void createDeployment() { + + namespace = K8sUtility.defaultNamespace(namespace); + Deployment deployment = new DeploymentBuilder().withNewMetadata() + .withName("deployment") + .withNamespace(namespace) + .addToLabels("label1", "label2") + .endMetadata() + .withNewSpec() + .endSpec() + .build(); + + kubeClient.apps().deployments().inNamespace(namespace).create(deployObj); + } + + @Test + void createOrReplace() { + Deployment deployment = new DeploymentBuilder().withNewMetadata().withName(name).withNamespace(namespace).build(); + KubernetesClient client = server.getClient(); + + Deployment deployObj; + Deployment result = client.apps().deployments().inNamespace(namespace).create(deployObj); + assertNotNull(result); + assertEquals("deployment1", result.getMetadata().getName()); + } + + + @Test + void testGetDeployments() { + + DeploymentList aDeploymentList = kubeClient.apps().deployments().inNamespace(namespace).list(); + assertNotNull(aDeploymentList); + assertEquals(1, aDeploymentList.getItems().size()); + } + + @Test + void scaleReplicas() { + } + + @Test + void deleteDeployment() { + + String namespace = K8sUtility.defaultNamespace(namespace); + boolean bDeleted = kubeClient.apps().deployments().inNamespace(namespace).withName(name).delete(); + assertTrue(bDeleted); + + } + + @Test + void scaleDeployment() { + + Scale scaleResponse = kubeClient.apps().deployments().inNamespace(namespace).withName(name).scale(scale); + assertEquals("bar", scaleResponse.getMetadata().getLabels().get("foo")); + } + + @Test + void getDeploymentLogs() { + + String namespace = K8sUtility.defaultNamespace(namespace); + namespace = K8sUtility.defaultNamespace(namespace); + + // When + String log = kubeClient.apps().deployments().inNamespace(namespace).withName(name).getLog(); + + // Then + assertNotNull(log); + assertEquals("hello", log); + } +} diff --git a/platform-controller/src/test/java/org/hobbit/controller/mocks/DummyContainerManager.java b/platform-controller/src/test/java/org/hobbit/controller/mocks/DummyContainerManager.java index 927f9c4a..b46caf8c 100644 --- a/platform-controller/src/test/java/org/hobbit/controller/mocks/DummyContainerManager.java +++ b/platform-controller/src/test/java/org/hobbit/controller/mocks/DummyContainerManager.java @@ -2,13 +2,12 @@ import com.spotify.docker.client.messages.ContainerStats; import com.spotify.docker.client.messages.swarm.Service; -import com.spotify.docker.client.messages.swarm.Service.Criteria; import java.util.ArrayList; import java.util.concurrent.Semaphore; import java.util.List; -import org.hobbit.controller.docker.ContainerManager; import org.hobbit.controller.docker.ContainerStateObserver; import org.hobbit.controller.docker.ContainerTerminationCallback; +import org.hobbit.controller.orchestration.ContainerManager; public class DummyContainerManager implements ContainerManager { @@ -106,7 +105,7 @@ public Service getContainerInfo(String serviceName) { } @Override - public List getContainers(Criteria criteria) { + public List getContainers(String label, String value) { return new ArrayList<>(0); } diff --git a/platform-controller/src/test/java/org/hobbit/controller/mocks/DummyResourceInformationCollector.java b/platform-controller/src/test/java/org/hobbit/controller/mocks/DummyResourceInformationCollector.java index 06bcb903..754f6acb 100644 --- a/platform-controller/src/test/java/org/hobbit/controller/mocks/DummyResourceInformationCollector.java +++ b/platform-controller/src/test/java/org/hobbit/controller/mocks/DummyResourceInformationCollector.java @@ -1,8 +1,7 @@ package org.hobbit.controller.mocks; -import com.spotify.docker.client.messages.swarm.Service; import org.hobbit.controller.data.SetupHardwareInformation; -import org.hobbit.controller.docker.ResourceInformationCollector; +import org.hobbit.controller.orchestration.ResourceInformationCollector; import org.hobbit.core.data.usage.ResourceUsageInformation; public class DummyResourceInformationCollector implements ResourceInformationCollector { @@ -13,7 +12,7 @@ public ResourceUsageInformation getSystemUsageInformation() { }; @Override - public ResourceUsageInformation getUsageInformation(Service.Criteria criteria) { + public ResourceUsageInformation getUsageInformation() { return null; }; diff --git a/rabbit-deployment.yaml b/rabbit-deployment.yaml new file mode 100644 index 00000000..1e6f5fce --- /dev/null +++ b/rabbit-deployment.yaml @@ -0,0 +1,39 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 () + creationTimestamp: null + labels: + io.kompose.service: rabbit + name: rabbit +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: rabbit + strategy: {} + template: + metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 () + creationTimestamp: null + labels: + io.kompose.network/hobbit: "true" + io.kompose.network/hobbit-core: "true" + io.kompose.service: rabbit + spec: + containers: + - image: rabbitmq:management + imagePullPolicy: "" + name: rabbit + ports: + - containerPort: 15672 + - containerPort: 5672 + resources: {} + restartPolicy: Always + serviceAccountName: "" + volumes: null +status: {} diff --git a/rabbit-service.yaml b/rabbit-service.yaml new file mode 100644 index 00000000..e69de29b diff --git a/redis-claim0-persistentvolumeclaim.yaml b/redis-claim0-persistentvolumeclaim.yaml new file mode 100644 index 00000000..e69de29b diff --git a/redis-deployment.yaml b/redis-deployment.yaml new file mode 100644 index 00000000..df1347f8 --- /dev/null +++ b/redis-deployment.yaml @@ -0,0 +1,45 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 () + creationTimestamp: null + labels: + io.kompose.service: redis + name: redis +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: redis + strategy: + type: Recreate + template: + metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 () + creationTimestamp: null + labels: + io.kompose.network/hobbit-core: "true" + io.kompose.service: redis + spec: + containers: + - args: + - redis-server + - /data/redis.conf + image: redis:4.0.7 + imagePullPolicy: "" + name: redis + resources: {} + volumeMounts: + - mountPath: /data + name: redis-claim0 + restartPolicy: Always + serviceAccountName: "" + volumes: + - name: redis-claim0 + persistentVolumeClaim: + claimName: redis-claim0 +status: {} diff --git a/resource/calico.yaml b/resource/calico.yaml new file mode 100644 index 00000000..99b33e8a --- /dev/null +++ b/resource/calico.yaml @@ -0,0 +1,3750 @@ +--- +# Source: calico/templates/calico-config.yaml +# This ConfigMap is used to configure a self-hosted Calico installation. +kind: ConfigMap +apiVersion: v1 +metadata: + name: calico-config + namespace: kube-system +data: + # Typha is disabled. + typha_service_name: "none" + # Configure the backend to use. + calico_backend: "bird" + # Configure the MTU to use for workload interfaces and tunnels. + # - If Wireguard is enabled, set to your network MTU - 60 + # - Otherwise, if VXLAN or BPF mode is enabled, set to your network MTU - 50 + # - Otherwise, if IPIP is enabled, set to your network MTU - 20 + # - Otherwise, if not using any encapsulation, set to your network MTU. + veth_mtu: "1440" + + # The CNI network configuration to install on each node. The special + # values in this config will be automatically populated. + cni_network_config: |- + { + "name": "k8s-pod-network", + "cniVersion": "0.3.1", + "plugins": [ + { + "type": "calico", + "log_level": "info", + "datastore_type": "kubernetes", + "nodename": "__KUBERNETES_NODE_NAME__", + "mtu": __CNI_MTU__, + "ipam": { + "type": "calico-ipam" + }, + "policy": { + "type": "k8s" + }, + "kubernetes": { + "kubeconfig": "__KUBECONFIG_FILEPATH__" + } + }, + { + "type": "portmap", + "snat": true, + "capabilities": {"portMappings": true} + }, + { + "type": "bandwidth", + "capabilities": {"bandwidth": true} + } + ] + } + +--- +# Source: calico/templates/kdd-crds.yaml + + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: bgpconfigurations.crd.projectcalico.org +spec: + group: crd.projectcalico.org + names: + kind: BGPConfiguration + listKind: BGPConfigurationList + plural: bgpconfigurations + singular: bgpconfiguration + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + description: BGPConfiguration contains the configuration for any BGP routing. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: BGPConfigurationSpec contains the values of the BGP configuration. + properties: + asNumber: + description: 'ASNumber is the default AS number used by a node. [Default: + 64512]' + format: int32 + type: integer + logSeverityScreen: + description: 'LogSeverityScreen is the log severity above which logs + are sent to the stdout. [Default: INFO]' + type: string + nodeToNodeMeshEnabled: + description: 'NodeToNodeMeshEnabled sets whether full node to node + BGP mesh is enabled. [Default: true]' + type: boolean + serviceClusterIPs: + description: ServiceClusterIPs are the CIDR blocks from which service + cluster IPs are allocated. If specified, Calico will advertise these + blocks, as well as any cluster IPs within them. + items: + description: ServiceClusterIPBlock represents a single whitelisted + CIDR block for ClusterIPs. + properties: + cidr: + type: string + type: object + type: array + serviceExternalIPs: + description: ServiceExternalIPs are the CIDR blocks for Kubernetes + Service External IPs. Kubernetes Service ExternalIPs will only be + advertised if they are within one of these blocks. + items: + description: ServiceExternalIPBlock represents a single whitelisted + CIDR External IP block. + properties: + cidr: + type: string + type: object + type: array + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] + +--- + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: bgppeers.crd.projectcalico.org +spec: + group: crd.projectcalico.org + names: + kind: BGPPeer + listKind: BGPPeerList + plural: bgppeers + singular: bgppeer + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: BGPPeerSpec contains the specification for a BGPPeer resource. + properties: + asNumber: + description: The AS Number of the peer. + format: int32 + type: integer + node: + description: The node name identifying the Calico node instance that + is peering with this peer. If this is not set, this represents a + global peer, i.e. a peer that peers with every node in the deployment. + type: string + nodeSelector: + description: Selector for the nodes that should have this peering. When + this is set, the Node field must be empty. + type: string + peerIP: + description: The IP address of the peer. + type: string + peerSelector: + description: Selector for the remote nodes to peer with. When this + is set, the PeerIP and ASNumber fields must be empty. For each + peering between the local node and selected remote nodes, we configure + an IPv4 peering if both ends have NodeBGPSpec.IPv4Address specified, + and an IPv6 peering if both ends have NodeBGPSpec.IPv6Address specified. The + remote AS number comes from the remote node’s NodeBGPSpec.ASNumber, + or the global default if that is not set. + type: string + required: + - asNumber + - peerIP + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] + +--- + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: blockaffinities.crd.projectcalico.org +spec: + group: crd.projectcalico.org + names: + kind: BlockAffinity + listKind: BlockAffinityList + plural: blockaffinities + singular: blockaffinity + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: BlockAffinitySpec contains the specification for a BlockAffinity + resource. + properties: + cidr: + type: string + deleted: + description: Deleted indicates that this block affinity is being deleted. + This field is a string for compatibility with older releases that + mistakenly treat this field as a string. + type: string + node: + type: string + state: + type: string + required: + - cidr + - deleted + - node + - state + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] + +--- + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: clusterinformations.crd.projectcalico.org +spec: + group: crd.projectcalico.org + names: + kind: ClusterInformation + listKind: ClusterInformationList + plural: clusterinformations + singular: clusterinformation + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + description: ClusterInformation contains the cluster specific information. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ClusterInformationSpec contains the values of describing + the cluster. + properties: + calicoVersion: + description: CalicoVersion is the version of Calico that the cluster + is running + type: string + clusterGUID: + description: ClusterGUID is the GUID of the cluster + type: string + clusterType: + description: ClusterType describes the type of the cluster + type: string + datastoreReady: + description: DatastoreReady is used during significant datastore migrations + to signal to components such as Felix that it should wait before + accessing the datastore. + type: boolean + variant: + description: Variant declares which variant of Calico should be active. + type: string + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] + +--- + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: felixconfigurations.crd.projectcalico.org +spec: + group: crd.projectcalico.org + names: + kind: FelixConfiguration + listKind: FelixConfigurationList + plural: felixconfigurations + singular: felixconfiguration + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + description: Felix Configuration contains the configuration for Felix. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: FelixConfigurationSpec contains the values of the Felix configuration. + properties: + bpfConnectTimeLoadBalancingEnabled: + description: 'BPFConnectTimeLoadBalancingEnabled when in BPF mode, + controls whether Felix installs the connection-time load balancer. The + connect-time load balancer is required for the host to be able to + reach Kubernetes services and it improves the performance of pod-to-service + connections. The only reason to disable it is for debugging purposes. [Default: + true]' + type: boolean + bpfDataIfacePattern: + description: 'BPFDataIfacePattern is a regular expression that controls + which interfaces Felix should attach BPF programs to in order to + catch traffic to/from the network. This needs to match the interfaces + that Calico workload traffic flows over as well as any interfaces + that handle incoming traffic to nodeports and services from outside + the cluster. It should not match the workload interfaces (usually + named cali...). [Default: ^(en.*|eth.*|tunl0$)]' + type: string + bpfDisableUnprivileged: + description: 'BPFDisableUnprivileged, if enabled, Felix sets the kernel.unprivileged_bpf_disabled + sysctl to disable unprivileged use of BPF. This ensures that unprivileged + users cannot access Calico''s BPF maps and cannot insert their own + BPF programs to interfere with Calico''s. [Default: true]' + type: boolean + bpfEnabled: + description: 'BPFEnabled, if enabled Felix will use the BPF dataplane. + [Default: false]' + type: boolean + bpfExternalServiceMode: + description: 'BPFExternalServiceMode in BPF mode, controls how connections + from outside the cluster to services (node ports and cluster IPs) + are forwarded to remote workloads. If set to "Tunnel" then both + request and response traffic is tunneled to the remote node. If + set to "DSR", the request traffic is tunneled but the response traffic + is sent directly from the remote node. In "DSR" mode, the remote + node appears to use the IP of the ingress node; this requires a + permissive L2 network. [Default: Tunnel]' + type: string + bpfKubeProxyEndpointSlicesEnabled: + description: BPFKubeProxyEndpointSlicesEnabled in BPF mode, controls + whether Felix's embedded kube-proxy accepts EndpointSlices or not. + type: boolean + bpfKubeProxyIptablesCleanupEnabled: + description: 'BPFKubeProxyIptablesCleanupEnabled, if enabled in BPF + mode, Felix will proactively clean up the upstream Kubernetes kube-proxy''s + iptables chains. Should only be enabled if kube-proxy is not running. [Default: + true]' + type: boolean + bpfKubeProxyMinSyncPeriod: + description: 'BPFKubeProxyMinSyncPeriod, in BPF mode, controls the + minimum time between updates to the dataplane for Felix''s embedded + kube-proxy. Lower values give reduced set-up latency. Higher values + reduce Felix CPU usage by batching up more work. [Default: 1s]' + type: string + bpfLogLevel: + description: 'BPFLogLevel controls the log level of the BPF programs + when in BPF dataplane mode. One of "Off", "Info", or "Debug". The + logs are emitted to the BPF trace pipe, accessible with the command + `tc exec bpf debug`. [Default: Off].' + type: string + chainInsertMode: + description: 'ChainInsertMode controls whether Felix hooks the kernel’s + top-level iptables chains by inserting a rule at the top of the + chain or by appending a rule at the bottom. insert is the safe default + since it prevents Calico’s rules from being bypassed. If you switch + to append mode, be sure that the other rules in the chains signal + acceptance by falling through to the Calico rules, otherwise the + Calico policy will be bypassed. [Default: insert]' + type: string + dataplaneDriver: + type: string + debugDisableLogDropping: + type: boolean + debugMemoryProfilePath: + type: string + debugSimulateCalcGraphHangAfter: + type: string + debugSimulateDataplaneHangAfter: + type: string + defaultEndpointToHostAction: + description: 'DefaultEndpointToHostAction controls what happens to + traffic that goes from a workload endpoint to the host itself (after + the traffic hits the endpoint egress policy). By default Calico + blocks traffic from workload endpoints to the host itself with an + iptables “DROP” action. If you want to allow some or all traffic + from endpoint to host, set this parameter to RETURN or ACCEPT. Use + RETURN if you have your own rules in the iptables “INPUT” chain; + Calico will insert its rules at the top of that chain, then “RETURN” + packets to the “INPUT” chain once it has completed processing workload + endpoint egress policy. Use ACCEPT to unconditionally accept packets + from workloads after processing workload endpoint egress policy. + [Default: Drop]' + type: string + deviceRouteProtocol: + description: This defines the route protocol added to programmed device + routes, by default this will be RTPROT_BOOT when left blank. + type: integer + deviceRouteSourceAddress: + description: This is the source address to use on programmed device + routes. By default the source address is left blank, leaving the + kernel to choose the source address used. + type: string + disableConntrackInvalidCheck: + type: boolean + endpointReportingDelay: + type: string + endpointReportingEnabled: + type: boolean + externalNodesList: + description: ExternalNodesCIDRList is a list of CIDR's of external-non-calico-nodes + which may source tunnel traffic and have the tunneled traffic be + accepted at calico nodes. + items: + type: string + type: array + failsafeInboundHostPorts: + description: 'FailsafeInboundHostPorts is a comma-delimited list of + UDP/TCP ports that Felix will allow incoming traffic to host endpoints + on irrespective of the security policy. This is useful to avoid + accidentally cutting off a host with incorrect configuration. Each + port should be specified as tcp: or udp:. + For back-compatibility, if the protocol is not specified, it defaults + to “tcp”. To disable all inbound host ports, use the value none. + The default value allows ssh access and DHCP. [Default: tcp:22, + udp:68, tcp:179, tcp:2379, tcp:2380, tcp:6443, tcp:6666, tcp:6667]' + items: + description: ProtoPort is combination of protocol and port, both + must be specified. + properties: + port: + type: integer + protocol: + type: string + required: + - port + - protocol + type: object + type: array + failsafeOutboundHostPorts: + description: 'FailsafeOutboundHostPorts is a comma-delimited list + of UDP/TCP ports that Felix will allow outgoing traffic from host + endpoints to irrespective of the security policy. This is useful + to avoid accidentally cutting off a host with incorrect configuration. + Each port should be specified as tcp: or udp:. + For back-compatibility, if the protocol is not specified, it defaults + to “tcp”. To disable all outbound host ports, use the value none. + The default value opens etcd’s standard ports to ensure that Felix + does not get cut off from etcd as well as allowing DHCP and DNS. + [Default: tcp:179, tcp:2379, tcp:2380, tcp:6443, tcp:6666, tcp:6667, + udp:53, udp:67]' + items: + description: ProtoPort is combination of protocol and port, both + must be specified. + properties: + port: + type: integer + protocol: + type: string + required: + - port + - protocol + type: object + type: array + genericXDPEnabled: + description: 'GenericXDPEnabled enables Generic XDP so network cards + that don''t support XDP offload or driver modes can use XDP. This + is not recommended since it doesn''t provide better performance + than iptables. [Default: false]' + type: boolean + healthEnabled: + type: boolean + healthHost: + type: string + healthPort: + type: integer + interfaceExclude: + description: 'InterfaceExclude is a comma-separated list of interfaces + that Felix should exclude when monitoring for host endpoints. The + default value ensures that Felix ignores Kubernetes'' IPVS dummy + interface, which is used internally by kube-proxy. If you want to + exclude multiple interface names using a single value, the list + supports regular expressions. For regular expressions you must wrap + the value with ''/''. For example having values ''/^kube/,veth1'' + will exclude all interfaces that begin with ''kube'' and also the + interface ''veth1''. [Default: kube-ipvs0]' + type: string + interfacePrefix: + description: 'InterfacePrefix is the interface name prefix that identifies + workload endpoints and so distinguishes them from host endpoint + interfaces. Note: in environments other than bare metal, the orchestrators + configure this appropriately. For example our Kubernetes and Docker + integrations set the ‘cali’ value, and our OpenStack integration + sets the ‘tap’ value. [Default: cali]' + type: string + ipipEnabled: + type: boolean + ipipMTU: + description: 'IPIPMTU is the MTU to set on the tunnel device. See + Configuring MTU [Default: 1440]' + type: integer + ipsetsRefreshInterval: + description: 'IpsetsRefreshInterval is the period at which Felix re-checks + all iptables state to ensure that no other process has accidentally + broken Calico’s rules. Set to 0 to disable iptables refresh. [Default: + 90s]' + type: string + iptablesBackend: + description: IptablesBackend specifies which backend of iptables will + be used. The default is legacy. + type: string + iptablesFilterAllowAction: + type: string + iptablesLockFilePath: + description: 'IptablesLockFilePath is the location of the iptables + lock file. You may need to change this if the lock file is not in + its standard location (for example if you have mapped it into Felix’s + container at a different path). [Default: /run/xtables.lock]' + type: string + iptablesLockProbeInterval: + description: 'IptablesLockProbeInterval is the time that Felix will + wait between attempts to acquire the iptables lock if it is not + available. Lower values make Felix more responsive when the lock + is contended, but use more CPU. [Default: 50ms]' + type: string + iptablesLockTimeout: + description: 'IptablesLockTimeout is the time that Felix will wait + for the iptables lock, or 0, to disable. To use this feature, Felix + must share the iptables lock file with all other processes that + also take the lock. When running Felix inside a container, this + requires the /run directory of the host to be mounted into the calico/node + or calico/felix container. [Default: 0s disabled]' + type: string + iptablesMangleAllowAction: + type: string + iptablesMarkMask: + description: 'IptablesMarkMask is the mask that Felix selects its + IPTables Mark bits from. Should be a 32 bit hexadecimal number with + at least 8 bits set, none of which clash with any other mark bits + in use on the system. [Default: 0xff000000]' + format: int32 + type: integer + iptablesNATOutgoingInterfaceFilter: + type: string + iptablesPostWriteCheckInterval: + description: 'IptablesPostWriteCheckInterval is the period after Felix + has done a write to the dataplane that it schedules an extra read + back in order to check the write was not clobbered by another process. + This should only occur if another application on the system doesn’t + respect the iptables lock. [Default: 1s]' + type: string + iptablesRefreshInterval: + description: 'IptablesRefreshInterval is the period at which Felix + re-checks the IP sets in the dataplane to ensure that no other process + has accidentally broken Calico’s rules. Set to 0 to disable IP sets + refresh. Note: the default for this value is lower than the other + refresh intervals as a workaround for a Linux kernel bug that was + fixed in kernel version 4.11. If you are using v4.11 or greater + you may want to set this to, a higher value to reduce Felix CPU + usage. [Default: 10s]' + type: string + ipv6Support: + type: boolean + kubeNodePortRanges: + description: 'KubeNodePortRanges holds list of port ranges used for + service node ports. Only used if felix detects kube-proxy running + in ipvs mode. Felix uses these ranges to separate host and workload + traffic. [Default: 30000:32767].' + items: + anyOf: + - type: integer + - type: string + pattern: ^.* + x-kubernetes-int-or-string: true + type: array + logFilePath: + description: 'LogFilePath is the full path to the Felix log. Set to + none to disable file logging. [Default: /var/log/calico/felix.log]' + type: string + logPrefix: + description: 'LogPrefix is the log prefix that Felix uses when rendering + LOG rules. [Default: calico-packet]' + type: string + logSeverityFile: + description: 'LogSeverityFile is the log severity above which logs + are sent to the log file. [Default: Info]' + type: string + logSeverityScreen: + description: 'LogSeverityScreen is the log severity above which logs + are sent to the stdout. [Default: Info]' + type: string + logSeveritySys: + description: 'LogSeveritySys is the log severity above which logs + are sent to the syslog. Set to None for no logging to syslog. [Default: + Info]' + type: string + maxIpsetSize: + type: integer + metadataAddr: + description: 'MetadataAddr is the IP address or domain name of the + server that can answer VM queries for cloud-init metadata. In OpenStack, + this corresponds to the machine running nova-api (or in Ubuntu, + nova-api-metadata). A value of none (case insensitive) means that + Felix should not set up any NAT rule for the metadata path. [Default: + 127.0.0.1]' + type: string + metadataPort: + description: 'MetadataPort is the port of the metadata server. This, + combined with global.MetadataAddr (if not ‘None’), is used to set + up a NAT rule, from 169.254.169.254:80 to MetadataAddr:MetadataPort. + In most cases this should not need to be changed [Default: 8775].' + type: integer + natOutgoingAddress: + description: NATOutgoingAddress specifies an address to use when performing + source NAT for traffic in a natOutgoing pool that is leaving the + network. By default the address used is an address on the interface + the traffic is leaving on (ie it uses the iptables MASQUERADE target) + type: string + natPortRange: + anyOf: + - type: integer + - type: string + description: NATPortRange specifies the range of ports that is used + for port mapping when doing outgoing NAT. When unset the default + behavior of the network stack is used. + pattern: ^.* + x-kubernetes-int-or-string: true + netlinkTimeout: + type: string + openstackRegion: + description: 'OpenstackRegion is the name of the region that a particular + Felix belongs to. In a multi-region Calico/OpenStack deployment, + this must be configured somehow for each Felix (here in the datamodel, + or in felix.cfg or the environment on each compute node), and must + match the [calico] openstack_region value configured in neutron.conf + on each node. [Default: Empty]' + type: string + policySyncPathPrefix: + description: 'PolicySyncPathPrefix is used to by Felix to communicate + policy changes to external services, like Application layer policy. + [Default: Empty]' + type: string + prometheusGoMetricsEnabled: + description: 'PrometheusGoMetricsEnabled disables Go runtime metrics + collection, which the Prometheus client does by default, when set + to false. This reduces the number of metrics reported, reducing + Prometheus load. [Default: true]' + type: boolean + prometheusMetricsEnabled: + description: 'PrometheusMetricsEnabled enables the Prometheus metrics + server in Felix if set to true. [Default: false]' + type: boolean + prometheusMetricsHost: + description: 'PrometheusMetricsHost is the host that the Prometheus + metrics server should bind to. [Default: empty]' + type: string + prometheusMetricsPort: + description: 'PrometheusMetricsPort is the TCP port that the Prometheus + metrics server should bind to. [Default: 9091]' + type: integer + prometheusProcessMetricsEnabled: + description: 'PrometheusProcessMetricsEnabled disables process metrics + collection, which the Prometheus client does by default, when set + to false. This reduces the number of metrics reported, reducing + Prometheus load. [Default: true]' + type: boolean + removeExternalRoutes: + description: Whether or not to remove device routes that have not + been programmed by Felix. Disabling this will allow external applications + to also add device routes. This is enabled by default which means + we will remove externally added routes. + type: boolean + reportingInterval: + description: 'ReportingInterval is the interval at which Felix reports + its status into the datastore or 0 to disable. Must be non-zero + in OpenStack deployments. [Default: 30s]' + type: string + reportingTTL: + description: 'ReportingTTL is the time-to-live setting for process-wide + status reports. [Default: 90s]' + type: string + routeRefreshInterval: + description: 'RouterefreshInterval is the period at which Felix re-checks + the routes in the dataplane to ensure that no other process has + accidentally broken Calico’s rules. Set to 0 to disable route refresh. + [Default: 90s]' + type: string + routeSource: + description: 'RouteSource configures where Felix gets its routing + information. - WorkloadIPs: use workload endpoints to construct + routes. - CalicoIPAM: the default - use IPAM data to construct routes.' + type: string + routeTableRange: + description: Calico programs additional Linux route tables for various + purposes. RouteTableRange specifies the indices of the route tables + that Calico should use. + properties: + max: + type: integer + min: + type: integer + required: + - max + - min + type: object + sidecarAccelerationEnabled: + description: 'SidecarAccelerationEnabled enables experimental sidecar + acceleration [Default: false]' + type: boolean + usageReportingEnabled: + description: 'UsageReportingEnabled reports anonymous Calico version + number and cluster size to projectcalico.org. Logs warnings returned + by the usage server. For example, if a significant security vulnerability + has been discovered in the version of Calico being used. [Default: + true]' + type: boolean + usageReportingInitialDelay: + description: 'UsageReportingInitialDelay controls the minimum delay + before Felix makes a report. [Default: 300s]' + type: string + usageReportingInterval: + description: 'UsageReportingInterval controls the interval at which + Felix makes reports. [Default: 86400s]' + type: string + useInternalDataplaneDriver: + type: boolean + vxlanEnabled: + type: boolean + vxlanMTU: + description: 'VXLANMTU is the MTU to set on the tunnel device. See + Configuring MTU [Default: 1440]' + type: integer + vxlanPort: + type: integer + vxlanVNI: + type: integer + wireguardEnabled: + description: 'WireguardEnabled controls whether Wireguard is enabled. + [Default: false]' + type: boolean + wireguardInterfaceName: + description: 'WireguardInterfaceName specifies the name to use for + the Wireguard interface. [Default: wg.calico]' + type: string + wireguardListeningPort: + description: 'WireguardListeningPort controls the listening port used + by Wireguard. [Default: 51820]' + type: integer + wireguardMTU: + description: 'WireguardMTU controls the MTU on the Wireguard interface. + See Configuring MTU [Default: 1420]' + type: integer + wireguardRoutingRulePriority: + description: 'WireguardRoutingRulePriority controls the priority value + to use for the Wireguard routing rule. [Default: 99]' + type: integer + xdpEnabled: + description: 'XDPEnabled enables XDP acceleration for suitable untracked + incoming deny rules. [Default: true]' + type: boolean + xdpRefreshInterval: + description: 'XDPRefreshInterval is the period at which Felix re-checks + all XDP state to ensure that no other process has accidentally broken + Calico''s BPF maps or attached programs. Set to 0 to disable XDP + refresh. [Default: 90s]' + type: string + required: + - bpfLogLevel + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] + +--- + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: globalnetworkpolicies.crd.projectcalico.org +spec: + group: crd.projectcalico.org + names: + kind: GlobalNetworkPolicy + listKind: GlobalNetworkPolicyList + plural: globalnetworkpolicies + singular: globalnetworkpolicy + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + properties: + applyOnForward: + description: ApplyOnForward indicates to apply the rules in this policy + on forward traffic. + type: boolean + doNotTrack: + description: DoNotTrack indicates whether packets matched by the rules + in this policy should go through the data plane's connection tracking, + such as Linux conntrack. If True, the rules in this policy are + applied before any data plane connection tracking, and packets allowed + by this policy are marked as not to be tracked. + type: boolean + egress: + description: The ordered set of egress rules. Each rule contains + a set of packet match criteria and a corresponding action to apply. + items: + description: "A Rule encapsulates a set of match criteria and an + action. Both selector-based security Policy and security Profiles + reference rules - separated out as a list of rules for both ingress + and egress packet matching. \n Each positive match criteria has + a negated version, prefixed with ”Not”. All the match criteria + within a rule must be satisfied for a packet to match. A single + rule can contain the positive and negative version of a match + and both must be satisfied for the rule to match." + properties: + action: + type: string + destination: + description: Destination contains the match criteria that apply + to destination entity. + properties: + namespaceSelector: + description: "NamespaceSelector is an optional field that + contains a selector expression. Only traffic that originates + from (or terminates at) endpoints within the selected + namespaces will be matched. When both NamespaceSelector + and Selector are defined on the same rule, then only workload + endpoints that are matched by both selectors will be selected + by the rule. \n For NetworkPolicy, an empty NamespaceSelector + implies that the Selector is limited to selecting only + workload endpoints in the same namespace as the NetworkPolicy. + \n For NetworkPolicy, `global()` NamespaceSelector implies + that the Selector is limited to selecting only GlobalNetworkSet + or HostEndpoint. \n For GlobalNetworkPolicy, an empty + NamespaceSelector implies the Selector applies to workload + endpoints across all namespaces." + type: string + nets: + description: Nets is an optional field that restricts the + rule to only apply to traffic that originates from (or + terminates at) IP addresses in any of the given subnets. + items: + type: string + type: array + notNets: + description: NotNets is the negated version of the Nets + field. + items: + type: string + type: array + notPorts: + description: NotPorts is the negated version of the Ports + field. Since only some protocols have ports, if any ports + are specified it requires the Protocol match in the Rule + to be set to "TCP" or "UDP". + items: + anyOf: + - type: integer + - type: string + pattern: ^.* + x-kubernetes-int-or-string: true + type: array + notSelector: + description: NotSelector is the negated version of the Selector + field. See Selector field for subtleties with negated + selectors. + type: string + ports: + description: "Ports is an optional field that restricts + the rule to only apply to traffic that has a source (destination) + port that matches one of these ranges/values. This value + is a list of integers or strings that represent ranges + of ports. \n Since only some protocols have ports, if + any ports are specified it requires the Protocol match + in the Rule to be set to \"TCP\" or \"UDP\"." + items: + anyOf: + - type: integer + - type: string + pattern: ^.* + x-kubernetes-int-or-string: true + type: array + selector: + description: "Selector is an optional field that contains + a selector expression (see Policy for sample syntax). + \ Only traffic that originates from (terminates at) endpoints + matching the selector will be matched. \n Note that: in + addition to the negated version of the Selector (see NotSelector + below), the selector expression syntax itself supports + negation. The two types of negation are subtly different. + One negates the set of matched endpoints, the other negates + the whole match: \n \tSelector = \"!has(my_label)\" matches + packets that are from other Calico-controlled \tendpoints + that do not have the label “my_label”. \n \tNotSelector + = \"has(my_label)\" matches packets that are not from + Calico-controlled \tendpoints that do have the label “my_label”. + \n The effect is that the latter will accept packets from + non-Calico sources whereas the former is limited to packets + from Calico-controlled endpoints." + type: string + serviceAccounts: + description: ServiceAccounts is an optional field that restricts + the rule to only apply to traffic that originates from + (or terminates at) a pod running as a matching service + account. + properties: + names: + description: Names is an optional field that restricts + the rule to only apply to traffic that originates + from (or terminates at) a pod running as a service + account whose name is in the list. + items: + type: string + type: array + selector: + description: Selector is an optional field that restricts + the rule to only apply to traffic that originates + from (or terminates at) a pod running as a service + account that matches the given label selector. If + both Names and Selector are specified then they are + AND'ed. + type: string + type: object + type: object + http: + description: HTTP contains match criteria that apply to HTTP + requests. + properties: + methods: + description: Methods is an optional field that restricts + the rule to apply only to HTTP requests that use one of + the listed HTTP Methods (e.g. GET, PUT, etc.) Multiple + methods are OR'd together. + items: + type: string + type: array + paths: + description: 'Paths is an optional field that restricts + the rule to apply to HTTP requests that use one of the + listed HTTP Paths. Multiple paths are OR''d together. + e.g: - exact: /foo - prefix: /bar NOTE: Each entry may + ONLY specify either a `exact` or a `prefix` match. The + validator will check for it.' + items: + description: 'HTTPPath specifies an HTTP path to match. + It may be either of the form: exact: : which matches + the path exactly or prefix: : which matches + the path prefix' + properties: + exact: + type: string + prefix: + type: string + type: object + type: array + type: object + icmp: + description: ICMP is an optional field that restricts the rule + to apply to a specific type and code of ICMP traffic. This + should only be specified if the Protocol field is set to "ICMP" + or "ICMPv6". + properties: + code: + description: Match on a specific ICMP code. If specified, + the Type value must also be specified. This is a technical + limitation imposed by the kernel’s iptables firewall, + which Calico uses to enforce the rule. + type: integer + type: + description: Match on a specific ICMP type. For example + a value of 8 refers to ICMP Echo Request (i.e. pings). + type: integer + type: object + ipVersion: + description: IPVersion is an optional field that restricts the + rule to only match a specific IP version. + type: integer + metadata: + description: Metadata contains additional information for this + rule + properties: + annotations: + additionalProperties: + type: string + description: Annotations is a set of key value pairs that + give extra information about the rule + type: object + type: object + notICMP: + description: NotICMP is the negated version of the ICMP field. + properties: + code: + description: Match on a specific ICMP code. If specified, + the Type value must also be specified. This is a technical + limitation imposed by the kernel’s iptables firewall, + which Calico uses to enforce the rule. + type: integer + type: + description: Match on a specific ICMP type. For example + a value of 8 refers to ICMP Echo Request (i.e. pings). + type: integer + type: object + notProtocol: + anyOf: + - type: integer + - type: string + description: NotProtocol is the negated version of the Protocol + field. + pattern: ^.* + x-kubernetes-int-or-string: true + protocol: + anyOf: + - type: integer + - type: string + description: "Protocol is an optional field that restricts the + rule to only apply to traffic of a specific IP protocol. Required + if any of the EntityRules contain Ports (because ports only + apply to certain protocols). \n Must be one of these string + values: \"TCP\", \"UDP\", \"ICMP\", \"ICMPv6\", \"SCTP\", + \"UDPLite\" or an integer in the range 1-255." + pattern: ^.* + x-kubernetes-int-or-string: true + source: + description: Source contains the match criteria that apply to + source entity. + properties: + namespaceSelector: + description: "NamespaceSelector is an optional field that + contains a selector expression. Only traffic that originates + from (or terminates at) endpoints within the selected + namespaces will be matched. When both NamespaceSelector + and Selector are defined on the same rule, then only workload + endpoints that are matched by both selectors will be selected + by the rule. \n For NetworkPolicy, an empty NamespaceSelector + implies that the Selector is limited to selecting only + workload endpoints in the same namespace as the NetworkPolicy. + \n For NetworkPolicy, `global()` NamespaceSelector implies + that the Selector is limited to selecting only GlobalNetworkSet + or HostEndpoint. \n For GlobalNetworkPolicy, an empty + NamespaceSelector implies the Selector applies to workload + endpoints across all namespaces." + type: string + nets: + description: Nets is an optional field that restricts the + rule to only apply to traffic that originates from (or + terminates at) IP addresses in any of the given subnets. + items: + type: string + type: array + notNets: + description: NotNets is the negated version of the Nets + field. + items: + type: string + type: array + notPorts: + description: NotPorts is the negated version of the Ports + field. Since only some protocols have ports, if any ports + are specified it requires the Protocol match in the Rule + to be set to "TCP" or "UDP". + items: + anyOf: + - type: integer + - type: string + pattern: ^.* + x-kubernetes-int-or-string: true + type: array + notSelector: + description: NotSelector is the negated version of the Selector + field. See Selector field for subtleties with negated + selectors. + type: string + ports: + description: "Ports is an optional field that restricts + the rule to only apply to traffic that has a source (destination) + port that matches one of these ranges/values. This value + is a list of integers or strings that represent ranges + of ports. \n Since only some protocols have ports, if + any ports are specified it requires the Protocol match + in the Rule to be set to \"TCP\" or \"UDP\"." + items: + anyOf: + - type: integer + - type: string + pattern: ^.* + x-kubernetes-int-or-string: true + type: array + selector: + description: "Selector is an optional field that contains + a selector expression (see Policy for sample syntax). + \ Only traffic that originates from (terminates at) endpoints + matching the selector will be matched. \n Note that: in + addition to the negated version of the Selector (see NotSelector + below), the selector expression syntax itself supports + negation. The two types of negation are subtly different. + One negates the set of matched endpoints, the other negates + the whole match: \n \tSelector = \"!has(my_label)\" matches + packets that are from other Calico-controlled \tendpoints + that do not have the label “my_label”. \n \tNotSelector + = \"has(my_label)\" matches packets that are not from + Calico-controlled \tendpoints that do have the label “my_label”. + \n The effect is that the latter will accept packets from + non-Calico sources whereas the former is limited to packets + from Calico-controlled endpoints." + type: string + serviceAccounts: + description: ServiceAccounts is an optional field that restricts + the rule to only apply to traffic that originates from + (or terminates at) a pod running as a matching service + account. + properties: + names: + description: Names is an optional field that restricts + the rule to only apply to traffic that originates + from (or terminates at) a pod running as a service + account whose name is in the list. + items: + type: string + type: array + selector: + description: Selector is an optional field that restricts + the rule to only apply to traffic that originates + from (or terminates at) a pod running as a service + account that matches the given label selector. If + both Names and Selector are specified then they are + AND'ed. + type: string + type: object + type: object + required: + - action + type: object + type: array + ingress: + description: The ordered set of ingress rules. Each rule contains + a set of packet match criteria and a corresponding action to apply. + items: + description: "A Rule encapsulates a set of match criteria and an + action. Both selector-based security Policy and security Profiles + reference rules - separated out as a list of rules for both ingress + and egress packet matching. \n Each positive match criteria has + a negated version, prefixed with ”Not”. All the match criteria + within a rule must be satisfied for a packet to match. A single + rule can contain the positive and negative version of a match + and both must be satisfied for the rule to match." + properties: + action: + type: string + destination: + description: Destination contains the match criteria that apply + to destination entity. + properties: + namespaceSelector: + description: "NamespaceSelector is an optional field that + contains a selector expression. Only traffic that originates + from (or terminates at) endpoints within the selected + namespaces will be matched. When both NamespaceSelector + and Selector are defined on the same rule, then only workload + endpoints that are matched by both selectors will be selected + by the rule. \n For NetworkPolicy, an empty NamespaceSelector + implies that the Selector is limited to selecting only + workload endpoints in the same namespace as the NetworkPolicy. + \n For NetworkPolicy, `global()` NamespaceSelector implies + that the Selector is limited to selecting only GlobalNetworkSet + or HostEndpoint. \n For GlobalNetworkPolicy, an empty + NamespaceSelector implies the Selector applies to workload + endpoints across all namespaces." + type: string + nets: + description: Nets is an optional field that restricts the + rule to only apply to traffic that originates from (or + terminates at) IP addresses in any of the given subnets. + items: + type: string + type: array + notNets: + description: NotNets is the negated version of the Nets + field. + items: + type: string + type: array + notPorts: + description: NotPorts is the negated version of the Ports + field. Since only some protocols have ports, if any ports + are specified it requires the Protocol match in the Rule + to be set to "TCP" or "UDP". + items: + anyOf: + - type: integer + - type: string + pattern: ^.* + x-kubernetes-int-or-string: true + type: array + notSelector: + description: NotSelector is the negated version of the Selector + field. See Selector field for subtleties with negated + selectors. + type: string + ports: + description: "Ports is an optional field that restricts + the rule to only apply to traffic that has a source (destination) + port that matches one of these ranges/values. This value + is a list of integers or strings that represent ranges + of ports. \n Since only some protocols have ports, if + any ports are specified it requires the Protocol match + in the Rule to be set to \"TCP\" or \"UDP\"." + items: + anyOf: + - type: integer + - type: string + pattern: ^.* + x-kubernetes-int-or-string: true + type: array + selector: + description: "Selector is an optional field that contains + a selector expression (see Policy for sample syntax). + \ Only traffic that originates from (terminates at) endpoints + matching the selector will be matched. \n Note that: in + addition to the negated version of the Selector (see NotSelector + below), the selector expression syntax itself supports + negation. The two types of negation are subtly different. + One negates the set of matched endpoints, the other negates + the whole match: \n \tSelector = \"!has(my_label)\" matches + packets that are from other Calico-controlled \tendpoints + that do not have the label “my_label”. \n \tNotSelector + = \"has(my_label)\" matches packets that are not from + Calico-controlled \tendpoints that do have the label “my_label”. + \n The effect is that the latter will accept packets from + non-Calico sources whereas the former is limited to packets + from Calico-controlled endpoints." + type: string + serviceAccounts: + description: ServiceAccounts is an optional field that restricts + the rule to only apply to traffic that originates from + (or terminates at) a pod running as a matching service + account. + properties: + names: + description: Names is an optional field that restricts + the rule to only apply to traffic that originates + from (or terminates at) a pod running as a service + account whose name is in the list. + items: + type: string + type: array + selector: + description: Selector is an optional field that restricts + the rule to only apply to traffic that originates + from (or terminates at) a pod running as a service + account that matches the given label selector. If + both Names and Selector are specified then they are + AND'ed. + type: string + type: object + type: object + http: + description: HTTP contains match criteria that apply to HTTP + requests. + properties: + methods: + description: Methods is an optional field that restricts + the rule to apply only to HTTP requests that use one of + the listed HTTP Methods (e.g. GET, PUT, etc.) Multiple + methods are OR'd together. + items: + type: string + type: array + paths: + description: 'Paths is an optional field that restricts + the rule to apply to HTTP requests that use one of the + listed HTTP Paths. Multiple paths are OR''d together. + e.g: - exact: /foo - prefix: /bar NOTE: Each entry may + ONLY specify either a `exact` or a `prefix` match. The + validator will check for it.' + items: + description: 'HTTPPath specifies an HTTP path to match. + It may be either of the form: exact: : which matches + the path exactly or prefix: : which matches + the path prefix' + properties: + exact: + type: string + prefix: + type: string + type: object + type: array + type: object + icmp: + description: ICMP is an optional field that restricts the rule + to apply to a specific type and code of ICMP traffic. This + should only be specified if the Protocol field is set to "ICMP" + or "ICMPv6". + properties: + code: + description: Match on a specific ICMP code. If specified, + the Type value must also be specified. This is a technical + limitation imposed by the kernel’s iptables firewall, + which Calico uses to enforce the rule. + type: integer + type: + description: Match on a specific ICMP type. For example + a value of 8 refers to ICMP Echo Request (i.e. pings). + type: integer + type: object + ipVersion: + description: IPVersion is an optional field that restricts the + rule to only match a specific IP version. + type: integer + metadata: + description: Metadata contains additional information for this + rule + properties: + annotations: + additionalProperties: + type: string + description: Annotations is a set of key value pairs that + give extra information about the rule + type: object + type: object + notICMP: + description: NotICMP is the negated version of the ICMP field. + properties: + code: + description: Match on a specific ICMP code. If specified, + the Type value must also be specified. This is a technical + limitation imposed by the kernel’s iptables firewall, + which Calico uses to enforce the rule. + type: integer + type: + description: Match on a specific ICMP type. For example + a value of 8 refers to ICMP Echo Request (i.e. pings). + type: integer + type: object + notProtocol: + anyOf: + - type: integer + - type: string + description: NotProtocol is the negated version of the Protocol + field. + pattern: ^.* + x-kubernetes-int-or-string: true + protocol: + anyOf: + - type: integer + - type: string + description: "Protocol is an optional field that restricts the + rule to only apply to traffic of a specific IP protocol. Required + if any of the EntityRules contain Ports (because ports only + apply to certain protocols). \n Must be one of these string + values: \"TCP\", \"UDP\", \"ICMP\", \"ICMPv6\", \"SCTP\", + \"UDPLite\" or an integer in the range 1-255." + pattern: ^.* + x-kubernetes-int-or-string: true + source: + description: Source contains the match criteria that apply to + source entity. + properties: + namespaceSelector: + description: "NamespaceSelector is an optional field that + contains a selector expression. Only traffic that originates + from (or terminates at) endpoints within the selected + namespaces will be matched. When both NamespaceSelector + and Selector are defined on the same rule, then only workload + endpoints that are matched by both selectors will be selected + by the rule. \n For NetworkPolicy, an empty NamespaceSelector + implies that the Selector is limited to selecting only + workload endpoints in the same namespace as the NetworkPolicy. + \n For NetworkPolicy, `global()` NamespaceSelector implies + that the Selector is limited to selecting only GlobalNetworkSet + or HostEndpoint. \n For GlobalNetworkPolicy, an empty + NamespaceSelector implies the Selector applies to workload + endpoints across all namespaces." + type: string + nets: + description: Nets is an optional field that restricts the + rule to only apply to traffic that originates from (or + terminates at) IP addresses in any of the given subnets. + items: + type: string + type: array + notNets: + description: NotNets is the negated version of the Nets + field. + items: + type: string + type: array + notPorts: + description: NotPorts is the negated version of the Ports + field. Since only some protocols have ports, if any ports + are specified it requires the Protocol match in the Rule + to be set to "TCP" or "UDP". + items: + anyOf: + - type: integer + - type: string + pattern: ^.* + x-kubernetes-int-or-string: true + type: array + notSelector: + description: NotSelector is the negated version of the Selector + field. See Selector field for subtleties with negated + selectors. + type: string + ports: + description: "Ports is an optional field that restricts + the rule to only apply to traffic that has a source (destination) + port that matches one of these ranges/values. This value + is a list of integers or strings that represent ranges + of ports. \n Since only some protocols have ports, if + any ports are specified it requires the Protocol match + in the Rule to be set to \"TCP\" or \"UDP\"." + items: + anyOf: + - type: integer + - type: string + pattern: ^.* + x-kubernetes-int-or-string: true + type: array + selector: + description: "Selector is an optional field that contains + a selector expression (see Policy for sample syntax). + \ Only traffic that originates from (terminates at) endpoints + matching the selector will be matched. \n Note that: in + addition to the negated version of the Selector (see NotSelector + below), the selector expression syntax itself supports + negation. The two types of negation are subtly different. + One negates the set of matched endpoints, the other negates + the whole match: \n \tSelector = \"!has(my_label)\" matches + packets that are from other Calico-controlled \tendpoints + that do not have the label “my_label”. \n \tNotSelector + = \"has(my_label)\" matches packets that are not from + Calico-controlled \tendpoints that do have the label “my_label”. + \n The effect is that the latter will accept packets from + non-Calico sources whereas the former is limited to packets + from Calico-controlled endpoints." + type: string + serviceAccounts: + description: ServiceAccounts is an optional field that restricts + the rule to only apply to traffic that originates from + (or terminates at) a pod running as a matching service + account. + properties: + names: + description: Names is an optional field that restricts + the rule to only apply to traffic that originates + from (or terminates at) a pod running as a service + account whose name is in the list. + items: + type: string + type: array + selector: + description: Selector is an optional field that restricts + the rule to only apply to traffic that originates + from (or terminates at) a pod running as a service + account that matches the given label selector. If + both Names and Selector are specified then they are + AND'ed. + type: string + type: object + type: object + required: + - action + type: object + type: array + namespaceSelector: + description: NamespaceSelector is an optional field for an expression + used to select a pod based on namespaces. + type: string + order: + description: Order is an optional field that specifies the order in + which the policy is applied. Policies with higher "order" are applied + after those with lower order. If the order is omitted, it may be + considered to be "infinite" - i.e. the policy will be applied last. Policies + with identical order will be applied in alphanumerical order based + on the Policy "Name". + type: number + preDNAT: + description: PreDNAT indicates to apply the rules in this policy before + any DNAT. + type: boolean + selector: + description: "The selector is an expression used to pick pick out + the endpoints that the policy should be applied to. \n Selector + expressions follow this syntax: \n \tlabel == \"string_literal\" + \ -> comparison, e.g. my_label == \"foo bar\" \tlabel != \"string_literal\" + \ -> not equal; also matches if label is not present \tlabel in + { \"a\", \"b\", \"c\", ... } -> true if the value of label X is + one of \"a\", \"b\", \"c\" \tlabel not in { \"a\", \"b\", \"c\", + ... } -> true if the value of label X is not one of \"a\", \"b\", + \"c\" \thas(label_name) -> True if that label is present \t! expr + -> negation of expr \texpr && expr -> Short-circuit and \texpr + || expr -> Short-circuit or \t( expr ) -> parens for grouping \tall() + or the empty selector -> matches all endpoints. \n Label names are + allowed to contain alphanumerics, -, _ and /. String literals are + more permissive but they do not support escape characters. \n Examples + (with made-up labels): \n \ttype == \"webserver\" && deployment + == \"prod\" \ttype in {\"frontend\", \"backend\"} \tdeployment != + \"dev\" \t! has(label_name)" + type: string + serviceAccountSelector: + description: ServiceAccountSelector is an optional field for an expression + used to select a pod based on service accounts. + type: string + types: + description: "Types indicates whether this policy applies to ingress, + or to egress, or to both. When not explicitly specified (and so + the value on creation is empty or nil), Calico defaults Types according + to what Ingress and Egress rules are present in the policy. The + default is: \n - [ PolicyTypeIngress ], if there are no Egress rules + (including the case where there are also no Ingress rules) \n + - [ PolicyTypeEgress ], if there are Egress rules but no Ingress + rules \n - [ PolicyTypeIngress, PolicyTypeEgress ], if there are + both Ingress and Egress rules. \n When the policy is read back again, + Types will always be one of these values, never empty or nil." + items: + description: PolicyType enumerates the possible values of the PolicySpec + Types field. + type: string + type: array + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] + +--- + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: globalnetworksets.crd.projectcalico.org +spec: + group: crd.projectcalico.org + names: + kind: GlobalNetworkSet + listKind: GlobalNetworkSetList + plural: globalnetworksets + singular: globalnetworkset + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + description: GlobalNetworkSet contains a set of arbitrary IP sub-networks/CIDRs + that share labels to allow rules to refer to them via selectors. The labels + of GlobalNetworkSet are not namespaced. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: GlobalNetworkSetSpec contains the specification for a NetworkSet + resource. + properties: + nets: + description: The list of IP networks that belong to this set. + items: + type: string + type: array + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] + +--- + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: hostendpoints.crd.projectcalico.org +spec: + group: crd.projectcalico.org + names: + kind: HostEndpoint + listKind: HostEndpointList + plural: hostendpoints + singular: hostendpoint + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: HostEndpointSpec contains the specification for a HostEndpoint + resource. + properties: + expectedIPs: + description: "The expected IP addresses (IPv4 and IPv6) of the endpoint. + If \"InterfaceName\" is not present, Calico will look for an interface + matching any of the IPs in the list and apply policy to that. Note: + \tWhen using the selector match criteria in an ingress or egress + security Policy \tor Profile, Calico converts the selector into + a set of IP addresses. For host \tendpoints, the ExpectedIPs field + is used for that purpose. (If only the interface \tname is specified, + Calico does not learn the IPs of the interface for use in match + \tcriteria.)" + items: + type: string + type: array + interfaceName: + description: "Either \"*\", or the name of a specific Linux interface + to apply policy to; or empty. \"*\" indicates that this HostEndpoint + governs all traffic to, from or through the default network namespace + of the host named by the \"Node\" field; entering and leaving that + namespace via any interface, including those from/to non-host-networked + local workloads. \n If InterfaceName is not \"*\", this HostEndpoint + only governs traffic that enters or leaves the host through the + specific interface named by InterfaceName, or - when InterfaceName + is empty - through the specific interface that has one of the IPs + in ExpectedIPs. Therefore, when InterfaceName is empty, at least + one expected IP must be specified. Only external interfaces (such + as “eth0”) are supported here; it isn't possible for a HostEndpoint + to protect traffic through a specific local workload interface. + \n Note: Only some kinds of policy are implemented for \"*\" HostEndpoints; + initially just pre-DNAT policy. Please check Calico documentation + for the latest position." + type: string + node: + description: The node name identifying the Calico node instance. + type: string + ports: + description: Ports contains the endpoint's named ports, which may + be referenced in security policy rules. + items: + properties: + name: + type: string + port: + type: integer + protocol: + anyOf: + - type: integer + - type: string + pattern: ^.* + x-kubernetes-int-or-string: true + required: + - name + - port + - protocol + type: object + type: array + profiles: + description: A list of identifiers of security Profile objects that + apply to this endpoint. Each profile is applied in the order that + they appear in this list. Profile rules are applied after the selector-based + security policy. + items: + type: string + type: array + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] + +--- + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: ipamblocks.crd.projectcalico.org +spec: + group: crd.projectcalico.org + names: + kind: IPAMBlock + listKind: IPAMBlockList + plural: ipamblocks + singular: ipamblock + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: IPAMBlockSpec contains the specification for an IPAMBlock + resource. + properties: + affinity: + type: string + allocations: + items: + type: integer + # TODO: This nullable is manually added in. We should update controller-gen + # to handle []*int properly itself. + nullable: true + type: array + attributes: + items: + properties: + handle_id: + type: string + secondary: + additionalProperties: + type: string + type: object + type: object + type: array + cidr: + type: string + deleted: + type: boolean + strictAffinity: + type: boolean + unallocated: + items: + type: integer + type: array + required: + - allocations + - attributes + - cidr + - deleted + - strictAffinity + - unallocated + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] + +--- + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: ipamconfigs.crd.projectcalico.org +spec: + group: crd.projectcalico.org + names: + kind: IPAMConfig + listKind: IPAMConfigList + plural: ipamconfigs + singular: ipamconfig + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: IPAMConfigSpec contains the specification for an IPAMConfig + resource. + properties: + autoAllocateBlocks: + type: boolean + strictAffinity: + type: boolean + required: + - autoAllocateBlocks + - strictAffinity + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] + +--- + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: ipamhandles.crd.projectcalico.org +spec: + group: crd.projectcalico.org + names: + kind: IPAMHandle + listKind: IPAMHandleList + plural: ipamhandles + singular: ipamhandle + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: IPAMHandleSpec contains the specification for an IPAMHandle + resource. + properties: + block: + additionalProperties: + type: integer + type: object + handleID: + type: string + required: + - block + - handleID + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] + +--- + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: ippools.crd.projectcalico.org +spec: + group: crd.projectcalico.org + names: + kind: IPPool + listKind: IPPoolList + plural: ippools + singular: ippool + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: IPPoolSpec contains the specification for an IPPool resource. + properties: + blockSize: + description: The block size to use for IP address assignments from + this pool. Defaults to 26 for IPv4 and 112 for IPv6. + type: integer + cidr: + description: The pool CIDR. + type: string + disabled: + description: When disabled is true, Calico IPAM will not assign addresses + from this pool. + type: boolean + ipip: + description: 'Deprecated: this field is only used for APIv1 backwards + compatibility. Setting this field is not allowed, this field is + for internal use only.' + properties: + enabled: + description: When enabled is true, ipip tunneling will be used + to deliver packets to destinations within this pool. + type: boolean + mode: + description: The IPIP mode. This can be one of "always" or "cross-subnet". A + mode of "always" will also use IPIP tunneling for routing to + destination IP addresses within this pool. A mode of "cross-subnet" + will only use IPIP tunneling when the destination node is on + a different subnet to the originating node. The default value + (if not specified) is "always". + type: string + type: object + ipipMode: + description: Contains configuration for IPIP tunneling for this pool. + If not specified, then this is defaulted to "Never" (i.e. IPIP tunelling + is disabled). + type: string + nat-outgoing: + description: 'Deprecated: this field is only used for APIv1 backwards + compatibility. Setting this field is not allowed, this field is + for internal use only.' + type: boolean + natOutgoing: + description: When nat-outgoing is true, packets sent from Calico networked + containers in this pool to destinations outside of this pool will + be masqueraded. + type: boolean + nodeSelector: + description: Allows IPPool to allocate for a specific node by label + selector. + type: string + vxlanMode: + description: Contains configuration for VXLAN tunneling for this pool. + If not specified, then this is defaulted to "Never" (i.e. VXLAN + tunelling is disabled). + type: string + required: + - cidr + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] + +--- + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: kubecontrollersconfigurations.crd.projectcalico.org +spec: + group: crd.projectcalico.org + names: + kind: KubeControllersConfiguration + listKind: KubeControllersConfigurationList + plural: kubecontrollersconfigurations + singular: kubecontrollersconfiguration + scope: Cluster + versions: + - name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: KubeControllersConfigurationSpec contains the values of the + Kubernetes controllers configuration. + properties: + controllers: + description: Controllers enables and configures individual Kubernetes + controllers + properties: + namespace: + description: Namespace enables and configures the namespace controller. + Enabled by default, set to nil to disable. + properties: + reconcilerPeriod: + description: 'ReconcilerPeriod is the period to perform reconciliation + with the Calico datastore. [Default: 5m]' + type: string + type: object + node: + description: Node enables and configures the node controller. + Enabled by default, set to nil to disable. + properties: + hostEndpoint: + description: HostEndpoint controls syncing nodes to host endpoints. + Disabled by default, set to nil to disable. + properties: + autoCreate: + description: 'AutoCreate enables automatic creation of + host endpoints for every node. [Default: Disabled]' + type: string + type: object + reconcilerPeriod: + description: 'ReconcilerPeriod is the period to perform reconciliation + with the Calico datastore. [Default: 5m]' + type: string + syncLabels: + description: 'SyncLabels controls whether to copy Kubernetes + node labels to Calico nodes. [Default: Enabled]' + type: string + type: object + policy: + description: Policy enables and configures the policy controller. + Enabled by default, set to nil to disable. + properties: + reconcilerPeriod: + description: 'ReconcilerPeriod is the period to perform reconciliation + with the Calico datastore. [Default: 5m]' + type: string + type: object + serviceAccount: + description: ServiceAccount enables and configures the service + account controller. Enabled by default, set to nil to disable. + properties: + reconcilerPeriod: + description: 'ReconcilerPeriod is the period to perform reconciliation + with the Calico datastore. [Default: 5m]' + type: string + type: object + workloadEndpoint: + description: WorkloadEndpoint enables and configures the workload + endpoint controller. Enabled by default, set to nil to disable. + properties: + reconcilerPeriod: + description: 'ReconcilerPeriod is the period to perform reconciliation + with the Calico datastore. [Default: 5m]' + type: string + type: object + type: object + etcdV3CompactionPeriod: + description: 'EtcdV3CompactionPeriod is the period between etcdv3 + compaction requests. Set to 0 to disable. [Default: 10m]' + type: string + healthChecks: + description: 'HealthChecks enables or disables support for health + checks [Default: Enabled]' + type: string + logSeverityScreen: + description: 'LogSeverityScreen is the log severity above which logs + are sent to the stdout. [Default: Info]' + type: string + required: + - controllers + type: object + status: + description: KubeControllersConfigurationStatus represents the status + of the configuration. It's useful for admins to be able to see the actual + config that was applied, which can be modified by environment variables + on the kube-controllers process. + properties: + environmentVars: + additionalProperties: + type: string + description: EnvironmentVars contains the environment variables on + the kube-controllers that influenced the RunningConfig. + type: object + runningConfig: + description: RunningConfig contains the effective config that is running + in the kube-controllers pod, after merging the API resource with + any environment variables. + properties: + controllers: + description: Controllers enables and configures individual Kubernetes + controllers + properties: + namespace: + description: Namespace enables and configures the namespace + controller. Enabled by default, set to nil to disable. + properties: + reconcilerPeriod: + description: 'ReconcilerPeriod is the period to perform + reconciliation with the Calico datastore. [Default: + 5m]' + type: string + type: object + node: + description: Node enables and configures the node controller. + Enabled by default, set to nil to disable. + properties: + hostEndpoint: + description: HostEndpoint controls syncing nodes to host + endpoints. Disabled by default, set to nil to disable. + properties: + autoCreate: + description: 'AutoCreate enables automatic creation + of host endpoints for every node. [Default: Disabled]' + type: string + type: object + reconcilerPeriod: + description: 'ReconcilerPeriod is the period to perform + reconciliation with the Calico datastore. [Default: + 5m]' + type: string + syncLabels: + description: 'SyncLabels controls whether to copy Kubernetes + node labels to Calico nodes. [Default: Enabled]' + type: string + type: object + policy: + description: Policy enables and configures the policy controller. + Enabled by default, set to nil to disable. + properties: + reconcilerPeriod: + description: 'ReconcilerPeriod is the period to perform + reconciliation with the Calico datastore. [Default: + 5m]' + type: string + type: object + serviceAccount: + description: ServiceAccount enables and configures the service + account controller. Enabled by default, set to nil to disable. + properties: + reconcilerPeriod: + description: 'ReconcilerPeriod is the period to perform + reconciliation with the Calico datastore. [Default: + 5m]' + type: string + type: object + workloadEndpoint: + description: WorkloadEndpoint enables and configures the workload + endpoint controller. Enabled by default, set to nil to disable. + properties: + reconcilerPeriod: + description: 'ReconcilerPeriod is the period to perform + reconciliation with the Calico datastore. [Default: + 5m]' + type: string + type: object + type: object + etcdV3CompactionPeriod: + description: 'EtcdV3CompactionPeriod is the period between etcdv3 + compaction requests. Set to 0 to disable. [Default: 10m]' + type: string + healthChecks: + description: 'HealthChecks enables or disables support for health + checks [Default: Enabled]' + type: string + logSeverityScreen: + description: 'LogSeverityScreen is the log severity above which + logs are sent to the stdout. [Default: Info]' + type: string + required: + - controllers + type: object + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] + +--- + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: networkpolicies.crd.projectcalico.org +spec: + group: crd.projectcalico.org + names: + kind: NetworkPolicy + listKind: NetworkPolicyList + plural: networkpolicies + singular: networkpolicy + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + properties: + egress: + description: The ordered set of egress rules. Each rule contains + a set of packet match criteria and a corresponding action to apply. + items: + description: "A Rule encapsulates a set of match criteria and an + action. Both selector-based security Policy and security Profiles + reference rules - separated out as a list of rules for both ingress + and egress packet matching. \n Each positive match criteria has + a negated version, prefixed with ”Not”. All the match criteria + within a rule must be satisfied for a packet to match. A single + rule can contain the positive and negative version of a match + and both must be satisfied for the rule to match." + properties: + action: + type: string + destination: + description: Destination contains the match criteria that apply + to destination entity. + properties: + namespaceSelector: + description: "NamespaceSelector is an optional field that + contains a selector expression. Only traffic that originates + from (or terminates at) endpoints within the selected + namespaces will be matched. When both NamespaceSelector + and Selector are defined on the same rule, then only workload + endpoints that are matched by both selectors will be selected + by the rule. \n For NetworkPolicy, an empty NamespaceSelector + implies that the Selector is limited to selecting only + workload endpoints in the same namespace as the NetworkPolicy. + \n For NetworkPolicy, `global()` NamespaceSelector implies + that the Selector is limited to selecting only GlobalNetworkSet + or HostEndpoint. \n For GlobalNetworkPolicy, an empty + NamespaceSelector implies the Selector applies to workload + endpoints across all namespaces." + type: string + nets: + description: Nets is an optional field that restricts the + rule to only apply to traffic that originates from (or + terminates at) IP addresses in any of the given subnets. + items: + type: string + type: array + notNets: + description: NotNets is the negated version of the Nets + field. + items: + type: string + type: array + notPorts: + description: NotPorts is the negated version of the Ports + field. Since only some protocols have ports, if any ports + are specified it requires the Protocol match in the Rule + to be set to "TCP" or "UDP". + items: + anyOf: + - type: integer + - type: string + pattern: ^.* + x-kubernetes-int-or-string: true + type: array + notSelector: + description: NotSelector is the negated version of the Selector + field. See Selector field for subtleties with negated + selectors. + type: string + ports: + description: "Ports is an optional field that restricts + the rule to only apply to traffic that has a source (destination) + port that matches one of these ranges/values. This value + is a list of integers or strings that represent ranges + of ports. \n Since only some protocols have ports, if + any ports are specified it requires the Protocol match + in the Rule to be set to \"TCP\" or \"UDP\"." + items: + anyOf: + - type: integer + - type: string + pattern: ^.* + x-kubernetes-int-or-string: true + type: array + selector: + description: "Selector is an optional field that contains + a selector expression (see Policy for sample syntax). + \ Only traffic that originates from (terminates at) endpoints + matching the selector will be matched. \n Note that: in + addition to the negated version of the Selector (see NotSelector + below), the selector expression syntax itself supports + negation. The two types of negation are subtly different. + One negates the set of matched endpoints, the other negates + the whole match: \n \tSelector = \"!has(my_label)\" matches + packets that are from other Calico-controlled \tendpoints + that do not have the label “my_label”. \n \tNotSelector + = \"has(my_label)\" matches packets that are not from + Calico-controlled \tendpoints that do have the label “my_label”. + \n The effect is that the latter will accept packets from + non-Calico sources whereas the former is limited to packets + from Calico-controlled endpoints." + type: string + serviceAccounts: + description: ServiceAccounts is an optional field that restricts + the rule to only apply to traffic that originates from + (or terminates at) a pod running as a matching service + account. + properties: + names: + description: Names is an optional field that restricts + the rule to only apply to traffic that originates + from (or terminates at) a pod running as a service + account whose name is in the list. + items: + type: string + type: array + selector: + description: Selector is an optional field that restricts + the rule to only apply to traffic that originates + from (or terminates at) a pod running as a service + account that matches the given label selector. If + both Names and Selector are specified then they are + AND'ed. + type: string + type: object + type: object + http: + description: HTTP contains match criteria that apply to HTTP + requests. + properties: + methods: + description: Methods is an optional field that restricts + the rule to apply only to HTTP requests that use one of + the listed HTTP Methods (e.g. GET, PUT, etc.) Multiple + methods are OR'd together. + items: + type: string + type: array + paths: + description: 'Paths is an optional field that restricts + the rule to apply to HTTP requests that use one of the + listed HTTP Paths. Multiple paths are OR''d together. + e.g: - exact: /foo - prefix: /bar NOTE: Each entry may + ONLY specify either a `exact` or a `prefix` match. The + validator will check for it.' + items: + description: 'HTTPPath specifies an HTTP path to match. + It may be either of the form: exact: : which matches + the path exactly or prefix: : which matches + the path prefix' + properties: + exact: + type: string + prefix: + type: string + type: object + type: array + type: object + icmp: + description: ICMP is an optional field that restricts the rule + to apply to a specific type and code of ICMP traffic. This + should only be specified if the Protocol field is set to "ICMP" + or "ICMPv6". + properties: + code: + description: Match on a specific ICMP code. If specified, + the Type value must also be specified. This is a technical + limitation imposed by the kernel’s iptables firewall, + which Calico uses to enforce the rule. + type: integer + type: + description: Match on a specific ICMP type. For example + a value of 8 refers to ICMP Echo Request (i.e. pings). + type: integer + type: object + ipVersion: + description: IPVersion is an optional field that restricts the + rule to only match a specific IP version. + type: integer + metadata: + description: Metadata contains additional information for this + rule + properties: + annotations: + additionalProperties: + type: string + description: Annotations is a set of key value pairs that + give extra information about the rule + type: object + type: object + notICMP: + description: NotICMP is the negated version of the ICMP field. + properties: + code: + description: Match on a specific ICMP code. If specified, + the Type value must also be specified. This is a technical + limitation imposed by the kernel’s iptables firewall, + which Calico uses to enforce the rule. + type: integer + type: + description: Match on a specific ICMP type. For example + a value of 8 refers to ICMP Echo Request (i.e. pings). + type: integer + type: object + notProtocol: + anyOf: + - type: integer + - type: string + description: NotProtocol is the negated version of the Protocol + field. + pattern: ^.* + x-kubernetes-int-or-string: true + protocol: + anyOf: + - type: integer + - type: string + description: "Protocol is an optional field that restricts the + rule to only apply to traffic of a specific IP protocol. Required + if any of the EntityRules contain Ports (because ports only + apply to certain protocols). \n Must be one of these string + values: \"TCP\", \"UDP\", \"ICMP\", \"ICMPv6\", \"SCTP\", + \"UDPLite\" or an integer in the range 1-255." + pattern: ^.* + x-kubernetes-int-or-string: true + source: + description: Source contains the match criteria that apply to + source entity. + properties: + namespaceSelector: + description: "NamespaceSelector is an optional field that + contains a selector expression. Only traffic that originates + from (or terminates at) endpoints within the selected + namespaces will be matched. When both NamespaceSelector + and Selector are defined on the same rule, then only workload + endpoints that are matched by both selectors will be selected + by the rule. \n For NetworkPolicy, an empty NamespaceSelector + implies that the Selector is limited to selecting only + workload endpoints in the same namespace as the NetworkPolicy. + \n For NetworkPolicy, `global()` NamespaceSelector implies + that the Selector is limited to selecting only GlobalNetworkSet + or HostEndpoint. \n For GlobalNetworkPolicy, an empty + NamespaceSelector implies the Selector applies to workload + endpoints across all namespaces." + type: string + nets: + description: Nets is an optional field that restricts the + rule to only apply to traffic that originates from (or + terminates at) IP addresses in any of the given subnets. + items: + type: string + type: array + notNets: + description: NotNets is the negated version of the Nets + field. + items: + type: string + type: array + notPorts: + description: NotPorts is the negated version of the Ports + field. Since only some protocols have ports, if any ports + are specified it requires the Protocol match in the Rule + to be set to "TCP" or "UDP". + items: + anyOf: + - type: integer + - type: string + pattern: ^.* + x-kubernetes-int-or-string: true + type: array + notSelector: + description: NotSelector is the negated version of the Selector + field. See Selector field for subtleties with negated + selectors. + type: string + ports: + description: "Ports is an optional field that restricts + the rule to only apply to traffic that has a source (destination) + port that matches one of these ranges/values. This value + is a list of integers or strings that represent ranges + of ports. \n Since only some protocols have ports, if + any ports are specified it requires the Protocol match + in the Rule to be set to \"TCP\" or \"UDP\"." + items: + anyOf: + - type: integer + - type: string + pattern: ^.* + x-kubernetes-int-or-string: true + type: array + selector: + description: "Selector is an optional field that contains + a selector expression (see Policy for sample syntax). + \ Only traffic that originates from (terminates at) endpoints + matching the selector will be matched. \n Note that: in + addition to the negated version of the Selector (see NotSelector + below), the selector expression syntax itself supports + negation. The two types of negation are subtly different. + One negates the set of matched endpoints, the other negates + the whole match: \n \tSelector = \"!has(my_label)\" matches + packets that are from other Calico-controlled \tendpoints + that do not have the label “my_label”. \n \tNotSelector + = \"has(my_label)\" matches packets that are not from + Calico-controlled \tendpoints that do have the label “my_label”. + \n The effect is that the latter will accept packets from + non-Calico sources whereas the former is limited to packets + from Calico-controlled endpoints." + type: string + serviceAccounts: + description: ServiceAccounts is an optional field that restricts + the rule to only apply to traffic that originates from + (or terminates at) a pod running as a matching service + account. + properties: + names: + description: Names is an optional field that restricts + the rule to only apply to traffic that originates + from (or terminates at) a pod running as a service + account whose name is in the list. + items: + type: string + type: array + selector: + description: Selector is an optional field that restricts + the rule to only apply to traffic that originates + from (or terminates at) a pod running as a service + account that matches the given label selector. If + both Names and Selector are specified then they are + AND'ed. + type: string + type: object + type: object + required: + - action + type: object + type: array + ingress: + description: The ordered set of ingress rules. Each rule contains + a set of packet match criteria and a corresponding action to apply. + items: + description: "A Rule encapsulates a set of match criteria and an + action. Both selector-based security Policy and security Profiles + reference rules - separated out as a list of rules for both ingress + and egress packet matching. \n Each positive match criteria has + a negated version, prefixed with ”Not”. All the match criteria + within a rule must be satisfied for a packet to match. A single + rule can contain the positive and negative version of a match + and both must be satisfied for the rule to match." + properties: + action: + type: string + destination: + description: Destination contains the match criteria that apply + to destination entity. + properties: + namespaceSelector: + description: "NamespaceSelector is an optional field that + contains a selector expression. Only traffic that originates + from (or terminates at) endpoints within the selected + namespaces will be matched. When both NamespaceSelector + and Selector are defined on the same rule, then only workload + endpoints that are matched by both selectors will be selected + by the rule. \n For NetworkPolicy, an empty NamespaceSelector + implies that the Selector is limited to selecting only + workload endpoints in the same namespace as the NetworkPolicy. + \n For NetworkPolicy, `global()` NamespaceSelector implies + that the Selector is limited to selecting only GlobalNetworkSet + or HostEndpoint. \n For GlobalNetworkPolicy, an empty + NamespaceSelector implies the Selector applies to workload + endpoints across all namespaces." + type: string + nets: + description: Nets is an optional field that restricts the + rule to only apply to traffic that originates from (or + terminates at) IP addresses in any of the given subnets. + items: + type: string + type: array + notNets: + description: NotNets is the negated version of the Nets + field. + items: + type: string + type: array + notPorts: + description: NotPorts is the negated version of the Ports + field. Since only some protocols have ports, if any ports + are specified it requires the Protocol match in the Rule + to be set to "TCP" or "UDP". + items: + anyOf: + - type: integer + - type: string + pattern: ^.* + x-kubernetes-int-or-string: true + type: array + notSelector: + description: NotSelector is the negated version of the Selector + field. See Selector field for subtleties with negated + selectors. + type: string + ports: + description: "Ports is an optional field that restricts + the rule to only apply to traffic that has a source (destination) + port that matches one of these ranges/values. This value + is a list of integers or strings that represent ranges + of ports. \n Since only some protocols have ports, if + any ports are specified it requires the Protocol match + in the Rule to be set to \"TCP\" or \"UDP\"." + items: + anyOf: + - type: integer + - type: string + pattern: ^.* + x-kubernetes-int-or-string: true + type: array + selector: + description: "Selector is an optional field that contains + a selector expression (see Policy for sample syntax). + \ Only traffic that originates from (terminates at) endpoints + matching the selector will be matched. \n Note that: in + addition to the negated version of the Selector (see NotSelector + below), the selector expression syntax itself supports + negation. The two types of negation are subtly different. + One negates the set of matched endpoints, the other negates + the whole match: \n \tSelector = \"!has(my_label)\" matches + packets that are from other Calico-controlled \tendpoints + that do not have the label “my_label”. \n \tNotSelector + = \"has(my_label)\" matches packets that are not from + Calico-controlled \tendpoints that do have the label “my_label”. + \n The effect is that the latter will accept packets from + non-Calico sources whereas the former is limited to packets + from Calico-controlled endpoints." + type: string + serviceAccounts: + description: ServiceAccounts is an optional field that restricts + the rule to only apply to traffic that originates from + (or terminates at) a pod running as a matching service + account. + properties: + names: + description: Names is an optional field that restricts + the rule to only apply to traffic that originates + from (or terminates at) a pod running as a service + account whose name is in the list. + items: + type: string + type: array + selector: + description: Selector is an optional field that restricts + the rule to only apply to traffic that originates + from (or terminates at) a pod running as a service + account that matches the given label selector. If + both Names and Selector are specified then they are + AND'ed. + type: string + type: object + type: object + http: + description: HTTP contains match criteria that apply to HTTP + requests. + properties: + methods: + description: Methods is an optional field that restricts + the rule to apply only to HTTP requests that use one of + the listed HTTP Methods (e.g. GET, PUT, etc.) Multiple + methods are OR'd together. + items: + type: string + type: array + paths: + description: 'Paths is an optional field that restricts + the rule to apply to HTTP requests that use one of the + listed HTTP Paths. Multiple paths are OR''d together. + e.g: - exact: /foo - prefix: /bar NOTE: Each entry may + ONLY specify either a `exact` or a `prefix` match. The + validator will check for it.' + items: + description: 'HTTPPath specifies an HTTP path to match. + It may be either of the form: exact: : which matches + the path exactly or prefix: : which matches + the path prefix' + properties: + exact: + type: string + prefix: + type: string + type: object + type: array + type: object + icmp: + description: ICMP is an optional field that restricts the rule + to apply to a specific type and code of ICMP traffic. This + should only be specified if the Protocol field is set to "ICMP" + or "ICMPv6". + properties: + code: + description: Match on a specific ICMP code. If specified, + the Type value must also be specified. This is a technical + limitation imposed by the kernel’s iptables firewall, + which Calico uses to enforce the rule. + type: integer + type: + description: Match on a specific ICMP type. For example + a value of 8 refers to ICMP Echo Request (i.e. pings). + type: integer + type: object + ipVersion: + description: IPVersion is an optional field that restricts the + rule to only match a specific IP version. + type: integer + metadata: + description: Metadata contains additional information for this + rule + properties: + annotations: + additionalProperties: + type: string + description: Annotations is a set of key value pairs that + give extra information about the rule + type: object + type: object + notICMP: + description: NotICMP is the negated version of the ICMP field. + properties: + code: + description: Match on a specific ICMP code. If specified, + the Type value must also be specified. This is a technical + limitation imposed by the kernel’s iptables firewall, + which Calico uses to enforce the rule. + type: integer + type: + description: Match on a specific ICMP type. For example + a value of 8 refers to ICMP Echo Request (i.e. pings). + type: integer + type: object + notProtocol: + anyOf: + - type: integer + - type: string + description: NotProtocol is the negated version of the Protocol + field. + pattern: ^.* + x-kubernetes-int-or-string: true + protocol: + anyOf: + - type: integer + - type: string + description: "Protocol is an optional field that restricts the + rule to only apply to traffic of a specific IP protocol. Required + if any of the EntityRules contain Ports (because ports only + apply to certain protocols). \n Must be one of these string + values: \"TCP\", \"UDP\", \"ICMP\", \"ICMPv6\", \"SCTP\", + \"UDPLite\" or an integer in the range 1-255." + pattern: ^.* + x-kubernetes-int-or-string: true + source: + description: Source contains the match criteria that apply to + source entity. + properties: + namespaceSelector: + description: "NamespaceSelector is an optional field that + contains a selector expression. Only traffic that originates + from (or terminates at) endpoints within the selected + namespaces will be matched. When both NamespaceSelector + and Selector are defined on the same rule, then only workload + endpoints that are matched by both selectors will be selected + by the rule. \n For NetworkPolicy, an empty NamespaceSelector + implies that the Selector is limited to selecting only + workload endpoints in the same namespace as the NetworkPolicy. + \n For NetworkPolicy, `global()` NamespaceSelector implies + that the Selector is limited to selecting only GlobalNetworkSet + or HostEndpoint. \n For GlobalNetworkPolicy, an empty + NamespaceSelector implies the Selector applies to workload + endpoints across all namespaces." + type: string + nets: + description: Nets is an optional field that restricts the + rule to only apply to traffic that originates from (or + terminates at) IP addresses in any of the given subnets. + items: + type: string + type: array + notNets: + description: NotNets is the negated version of the Nets + field. + items: + type: string + type: array + notPorts: + description: NotPorts is the negated version of the Ports + field. Since only some protocols have ports, if any ports + are specified it requires the Protocol match in the Rule + to be set to "TCP" or "UDP". + items: + anyOf: + - type: integer + - type: string + pattern: ^.* + x-kubernetes-int-or-string: true + type: array + notSelector: + description: NotSelector is the negated version of the Selector + field. See Selector field for subtleties with negated + selectors. + type: string + ports: + description: "Ports is an optional field that restricts + the rule to only apply to traffic that has a source (destination) + port that matches one of these ranges/values. This value + is a list of integers or strings that represent ranges + of ports. \n Since only some protocols have ports, if + any ports are specified it requires the Protocol match + in the Rule to be set to \"TCP\" or \"UDP\"." + items: + anyOf: + - type: integer + - type: string + pattern: ^.* + x-kubernetes-int-or-string: true + type: array + selector: + description: "Selector is an optional field that contains + a selector expression (see Policy for sample syntax). + \ Only traffic that originates from (terminates at) endpoints + matching the selector will be matched. \n Note that: in + addition to the negated version of the Selector (see NotSelector + below), the selector expression syntax itself supports + negation. The two types of negation are subtly different. + One negates the set of matched endpoints, the other negates + the whole match: \n \tSelector = \"!has(my_label)\" matches + packets that are from other Calico-controlled \tendpoints + that do not have the label “my_label”. \n \tNotSelector + = \"has(my_label)\" matches packets that are not from + Calico-controlled \tendpoints that do have the label “my_label”. + \n The effect is that the latter will accept packets from + non-Calico sources whereas the former is limited to packets + from Calico-controlled endpoints." + type: string + serviceAccounts: + description: ServiceAccounts is an optional field that restricts + the rule to only apply to traffic that originates from + (or terminates at) a pod running as a matching service + account. + properties: + names: + description: Names is an optional field that restricts + the rule to only apply to traffic that originates + from (or terminates at) a pod running as a service + account whose name is in the list. + items: + type: string + type: array + selector: + description: Selector is an optional field that restricts + the rule to only apply to traffic that originates + from (or terminates at) a pod running as a service + account that matches the given label selector. If + both Names and Selector are specified then they are + AND'ed. + type: string + type: object + type: object + required: + - action + type: object + type: array + order: + description: Order is an optional field that specifies the order in + which the policy is applied. Policies with higher "order" are applied + after those with lower order. If the order is omitted, it may be + considered to be "infinite" - i.e. the policy will be applied last. Policies + with identical order will be applied in alphanumerical order based + on the Policy "Name". + type: number + selector: + description: "The selector is an expression used to pick pick out + the endpoints that the policy should be applied to. \n Selector + expressions follow this syntax: \n \tlabel == \"string_literal\" + \ -> comparison, e.g. my_label == \"foo bar\" \tlabel != \"string_literal\" + \ -> not equal; also matches if label is not present \tlabel in + { \"a\", \"b\", \"c\", ... } -> true if the value of label X is + one of \"a\", \"b\", \"c\" \tlabel not in { \"a\", \"b\", \"c\", + ... } -> true if the value of label X is not one of \"a\", \"b\", + \"c\" \thas(label_name) -> True if that label is present \t! expr + -> negation of expr \texpr && expr -> Short-circuit and \texpr + || expr -> Short-circuit or \t( expr ) -> parens for grouping \tall() + or the empty selector -> matches all endpoints. \n Label names are + allowed to contain alphanumerics, -, _ and /. String literals are + more permissive but they do not support escape characters. \n Examples + (with made-up labels): \n \ttype == \"webserver\" && deployment + == \"prod\" \ttype in {\"frontend\", \"backend\"} \tdeployment != + \"dev\" \t! has(label_name)" + type: string + serviceAccountSelector: + description: ServiceAccountSelector is an optional field for an expression + used to select a pod based on service accounts. + type: string + types: + description: "Types indicates whether this policy applies to ingress, + or to egress, or to both. When not explicitly specified (and so + the value on creation is empty or nil), Calico defaults Types according + to what Ingress and Egress are present in the policy. The default + is: \n - [ PolicyTypeIngress ], if there are no Egress rules (including + the case where there are also no Ingress rules) \n - [ PolicyTypeEgress + ], if there are Egress rules but no Ingress rules \n - [ PolicyTypeIngress, + PolicyTypeEgress ], if there are both Ingress and Egress rules. + \n When the policy is read back again, Types will always be one + of these values, never empty or nil." + items: + description: PolicyType enumerates the possible values of the PolicySpec + Types field. + type: string + type: array + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] + +--- + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: networksets.crd.projectcalico.org +spec: + group: crd.projectcalico.org + names: + kind: NetworkSet + listKind: NetworkSetList + plural: networksets + singular: networkset + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: NetworkSet is the Namespaced-equivalent of the GlobalNetworkSet. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: NetworkSetSpec contains the specification for a NetworkSet + resource. + properties: + nets: + description: The list of IP networks that belong to this set. + items: + type: string + type: array + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] + +--- +--- +# Source: calico/templates/calico-kube-controllers-rbac.yaml + +# Include a clusterrole for the kube-controllers component, +# and bind it to the calico-kube-controllers serviceaccount. +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: calico-kube-controllers +rules: + # Nodes are watched to monitor for deletions. + - apiGroups: [""] + resources: + - nodes + verbs: + - watch + - list + - get + # Pods are queried to check for existence. + - apiGroups: [""] + resources: + - pods + verbs: + - get + # IPAM resources are manipulated when nodes are deleted. + - apiGroups: ["crd.projectcalico.org"] + resources: + - ippools + verbs: + - list + - apiGroups: ["crd.projectcalico.org"] + resources: + - blockaffinities + - ipamblocks + - ipamhandles + verbs: + - get + - list + - create + - update + - delete + # kube-controllers manages hostendpoints. + - apiGroups: ["crd.projectcalico.org"] + resources: + - hostendpoints + verbs: + - get + - list + - create + - update + - delete + # Needs access to update clusterinformations. + - apiGroups: ["crd.projectcalico.org"] + resources: + - clusterinformations + verbs: + - get + - create + - update + # KubeControllersConfiguration is where it gets its config + - apiGroups: ["crd.projectcalico.org"] + resources: + - kubecontrollersconfigurations + verbs: + # read its own config + - get + # create a default if none exists + - create + # update status + - update + # watch for changes + - watch +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: calico-kube-controllers +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: calico-kube-controllers +subjects: +- kind: ServiceAccount + name: calico-kube-controllers + namespace: kube-system +--- + +--- +# Source: calico/templates/calico-node-rbac.yaml +# Include a clusterrole for the calico-node DaemonSet, +# and bind it to the calico-node serviceaccount. +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: calico-node +rules: + # The CNI plugin needs to get pods, nodes, and namespaces. + - apiGroups: [""] + resources: + - pods + - nodes + - namespaces + verbs: + - get + - apiGroups: [""] + resources: + - endpoints + - services + verbs: + # Used to discover service IPs for advertisement. + - watch + - list + # Used to discover Typhas. + - get + # Pod CIDR auto-detection on kubeadm needs access to config maps. + - apiGroups: [""] + resources: + - configmaps + verbs: + - get + - apiGroups: [""] + resources: + - nodes/status + verbs: + # Needed for clearing NodeNetworkUnavailable flag. + - patch + # Calico stores some configuration information in node annotations. + - update + # Watch for changes to Kubernetes NetworkPolicies. + - apiGroups: ["networking.k8s.io"] + resources: + - networkpolicies + verbs: + - watch + - list + # Used by Calico for policy information. + - apiGroups: [""] + resources: + - pods + - namespaces + - serviceaccounts + verbs: + - list + - watch + # The CNI plugin patches pods/status. + - apiGroups: [""] + resources: + - pods/status + verbs: + - patch + # Calico monitors various CRDs for config. + - apiGroups: ["crd.projectcalico.org"] + resources: + - globalfelixconfigs + - felixconfigurations + - bgppeers + - globalbgpconfigs + - bgpconfigurations + - ippools + - ipamblocks + - globalnetworkpolicies + - globalnetworksets + - networkpolicies + - networksets + - clusterinformations + - hostendpoints + - blockaffinities + verbs: + - get + - list + - watch + # Calico must create and update some CRDs on startup. + - apiGroups: ["crd.projectcalico.org"] + resources: + - ippools + - felixconfigurations + - clusterinformations + verbs: + - create + - update + # Calico stores some configuration information on the node. + - apiGroups: [""] + resources: + - nodes + verbs: + - get + - list + - watch + # These permissions are only required for upgrade from v2.6, and can + # be removed after upgrade or on fresh installations. + - apiGroups: ["crd.projectcalico.org"] + resources: + - bgpconfigurations + - bgppeers + verbs: + - create + - update + # These permissions are required for Calico CNI to perform IPAM allocations. + - apiGroups: ["crd.projectcalico.org"] + resources: + - blockaffinities + - ipamblocks + - ipamhandles + verbs: + - get + - list + - create + - update + - delete + - apiGroups: ["crd.projectcalico.org"] + resources: + - ipamconfigs + verbs: + - get + # Block affinities must also be watchable by confd for route aggregation. + - apiGroups: ["crd.projectcalico.org"] + resources: + - blockaffinities + verbs: + - watch + # The Calico IPAM migration needs to get daemonsets. These permissions can be + # removed if not upgrading from an installation using host-local IPAM. + - apiGroups: ["apps"] + resources: + - daemonsets + verbs: + - get + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: calico-node +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: calico-node +subjects: +- kind: ServiceAccount + name: calico-node + namespace: kube-system + +--- +# Source: calico/templates/calico-node.yaml +# This manifest installs the calico-node container, as well +# as the CNI plugins and network config on +# each master and worker node in a Kubernetes cluster. +kind: DaemonSet +apiVersion: apps/v1 +metadata: + name: calico-node + namespace: kube-system + labels: + k8s-app: calico-node +spec: + selector: + matchLabels: + k8s-app: calico-node + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + template: + metadata: + labels: + k8s-app: calico-node + spec: + nodeSelector: + kubernetes.io/os: linux + hostNetwork: true + tolerations: + # Make sure calico-node gets scheduled on all nodes. + - effect: NoSchedule + operator: Exists + # Mark the pod as a critical add-on for rescheduling. + - key: CriticalAddonsOnly + operator: Exists + - effect: NoExecute + operator: Exists + serviceAccountName: calico-node + # Minimize downtime during a rolling upgrade or deletion; tell Kubernetes to do a "force + # deletion": https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods. + terminationGracePeriodSeconds: 0 + priorityClassName: system-node-critical + initContainers: + # This container performs upgrade from host-local IPAM to calico-ipam. + # It can be deleted if this is a fresh installation, or if you have already + # upgraded to use calico-ipam. + - name: upgrade-ipam + image: calico/cni:v3.15.0 + command: ["/opt/cni/bin/calico-ipam", "-upgrade"] + env: + - name: KUBERNETES_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: CALICO_NETWORKING_BACKEND + valueFrom: + configMapKeyRef: + name: calico-config + key: calico_backend + volumeMounts: + - mountPath: /var/lib/cni/networks + name: host-local-net-dir + - mountPath: /host/opt/cni/bin + name: cni-bin-dir + securityContext: + privileged: true + # This container installs the CNI binaries + # and CNI network config file on each node. + - name: install-cni + image: calico/cni:v3.15.0 + command: ["/install-cni.sh"] + env: + # Name of the CNI config file to create. + - name: CNI_CONF_NAME + value: "10-calico.conflist" + # The CNI network config to install on each node. + - name: CNI_NETWORK_CONFIG + valueFrom: + configMapKeyRef: + name: calico-config + key: cni_network_config + # Set the hostname based on the k8s node name. + - name: KUBERNETES_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + # CNI MTU Config variable + - name: CNI_MTU + valueFrom: + configMapKeyRef: + name: calico-config + key: veth_mtu + # Prevents the container from sleeping forever. + - name: SLEEP + value: "false" + volumeMounts: + - mountPath: /host/opt/cni/bin + name: cni-bin-dir + - mountPath: /host/etc/cni/net.d + name: cni-net-dir + securityContext: + privileged: true + # Adds a Flex Volume Driver that creates a per-pod Unix Domain Socket to allow Dikastes + # to communicate with Felix over the Policy Sync API. + - name: flexvol-driver + image: calico/pod2daemon-flexvol:v3.15.0 + volumeMounts: + - name: flexvol-driver-host + mountPath: /host/driver + securityContext: + privileged: true + containers: + # Runs calico-node container on each Kubernetes node. This + # container programs network policy and routes on each + # host. + - name: calico-node + image: calico/node:v3.15.0 + env: + # Use Kubernetes API as the backing datastore. + - name: DATASTORE_TYPE + value: "kubernetes" + # Wait for the datastore. + - name: WAIT_FOR_DATASTORE + value: "true" + # Set based on the k8s node name. + - name: NODENAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + # Choose the backend to use. + - name: CALICO_NETWORKING_BACKEND + valueFrom: + configMapKeyRef: + name: calico-config + key: calico_backend + # Cluster type to identify the deployment type + - name: CLUSTER_TYPE + value: "k8s,bgp" + # Auto-detect the BGP IP address. + - name: IP + value: "autodetect" + # Enable IPIP + - name: CALICO_IPV4POOL_IPIP + value: "Never" + # Enable or Disable VXLAN on the default IP pool. + - name: CALICO_IPV4POOL_VXLAN + value: "Always" + # Set MTU for tunnel device used if ipip is enabled + - name: FELIX_IPINIPMTU + valueFrom: + configMapKeyRef: + name: calico-config + key: veth_mtu + # Set MTU for the VXLAN tunnel device. + - name: FELIX_VXLANMTU + valueFrom: + configMapKeyRef: + name: calico-config + key: veth_mtu + # Set MTU for the Wireguard tunnel device. + - name: FELIX_WIREGUARDMTU + valueFrom: + configMapKeyRef: + name: calico-config + key: veth_mtu + # The default IPv4 pool to create on startup if none exists. Pod IPs will be + # chosen from this range. Changing this value after installation will have + # no effect. This should fall within `--cluster-cidr`. + - name: CALICO_IPV4POOL_CIDR + value: "192.168.0.0/16" + # Set MTU for the Wireguard tunnel device. + - name: FELIX_WIREGUARDMTU + valueFrom: + configMapKeyRef: + name: calico-config + key: veth_mtu + # Disable file logging so `kubectl logs` works. + - name: CALICO_DISABLE_FILE_LOGGING + value: "true" + # Set Felix endpoint to host default action to ACCEPT. + - name: FELIX_DEFAULTENDPOINTTOHOSTACTION + value: "ACCEPT" + # Disable IPv6 on Kubernetes. + - name: FELIX_IPV6SUPPORT + value: "false" + # Set Felix logging to "info" + - name: FELIX_LOGSEVERITYSCREEN + value: "info" + - name: FELIX_HEALTHENABLED + value: "true" + securityContext: + privileged: true + resources: + requests: + cpu: 250m + livenessProbe: + exec: + command: + - /bin/calico-node + - -felix-live + - -bird-live + periodSeconds: 10 + initialDelaySeconds: 10 + failureThreshold: 6 + readinessProbe: + exec: + command: + - /bin/calico-node + - -felix-ready + - -bird-ready + periodSeconds: 10 + volumeMounts: + - mountPath: /lib/modules + name: lib-modules + readOnly: true + - mountPath: /run/xtables.lock + name: xtables-lock + readOnly: false + - mountPath: /var/run/calico + name: var-run-calico + readOnly: false + - mountPath: /var/lib/calico + name: var-lib-calico + readOnly: false + - name: policysync + mountPath: /var/run/nodeagent + volumes: + # Used by calico-node. + - name: lib-modules + hostPath: + path: /lib/modules + - name: var-run-calico + hostPath: + path: /var/run/calico + - name: var-lib-calico + hostPath: + path: /var/lib/calico + - name: xtables-lock + hostPath: + path: /run/xtables.lock + type: FileOrCreate + # Used to install CNI. + - name: cni-bin-dir + hostPath: + path: /opt/cni/bin + - name: cni-net-dir + hostPath: + path: /etc/cni/net.d + # Mount in the directory for host-local IPAM allocations. This is + # used when upgrading from host-local to calico-ipam, and can be removed + # if not using the upgrade-ipam init container. + - name: host-local-net-dir + hostPath: + path: /var/lib/cni/networks + # Used to create per-pod Unix Domain Sockets + - name: policysync + hostPath: + type: DirectoryOrCreate + path: /var/run/nodeagent + # Used to install Flex Volume Driver + - name: flexvol-driver-host + hostPath: + type: DirectoryOrCreate + path: /usr/libexec/kubernetes/kubelet-plugins/volume/exec/nodeagent~uds +--- + +apiVersion: v1 +kind: ServiceAccount +metadata: + name: calico-node + namespace: kube-system + +--- +# Source: calico/templates/calico-kube-controllers.yaml +# See https://github.com/projectcalico/kube-controllers +apiVersion: apps/v1 +kind: Deployment +metadata: + name: calico-kube-controllers + namespace: kube-system + labels: + k8s-app: calico-kube-controllers +spec: + # The controllers can only have a single active instance. + replicas: 1 + selector: + matchLabels: + k8s-app: calico-kube-controllers + strategy: + type: Recreate + template: + metadata: + name: calico-kube-controllers + namespace: kube-system + labels: + k8s-app: calico-kube-controllers + spec: + nodeSelector: + kubernetes.io/os: linux + tolerations: + # Mark the pod as a critical add-on for rescheduling. + - key: CriticalAddonsOnly + operator: Exists + - key: node-role.kubernetes.io/master + effect: NoSchedule + serviceAccountName: calico-kube-controllers + priorityClassName: system-cluster-critical + containers: + - name: calico-kube-controllers + image: calico/kube-controllers:v3.15.0 + env: + # Choose which controllers to run. + - name: ENABLED_CONTROLLERS + value: node + - name: DATASTORE_TYPE + value: kubernetes + readinessProbe: + exec: + command: + - /usr/bin/check-status + - -r + +--- + +apiVersion: v1 +kind: ServiceAccount +metadata: + name: calico-kube-controllers + namespace: kube-system + +--- +# Source: calico/templates/calico-etcd-secrets.yaml + +--- +# Source: calico/templates/calico-typha.yaml + +--- +# Source: calico/templates/configure-canal.yaml + + diff --git a/resource/calicoctl.yaml b/resource/calicoctl.yaml new file mode 100644 index 00000000..29045edc --- /dev/null +++ b/resource/calicoctl.yaml @@ -0,0 +1,107 @@ +# Calico Version v3.14.1 +# https://docs.projectcalico.org/releases#v3.14.1 +# This manifest includes the following component versions: +# calico/ctl:v3.14.1 + +apiVersion: v1 +kind: ServiceAccount +metadata: + name: calicoctl + namespace: kube-system + +--- + +apiVersion: v1 +kind: Pod +metadata: + name: calicoctl + namespace: kube-system +spec: + nodeSelector: + kubernetes.io/os: linux + hostNetwork: true + serviceAccountName: calicoctl + containers: + - name: calicoctl + image: calico/ctl:v3.14.1 + command: ["/bin/sh", "-c", "while true; do sleep 3600; done"] + env: + - name: DATASTORE_TYPE + value: kubernetes + +--- + +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: calicoctl +rules: + - apiGroups: [""] + resources: + - namespaces + - nodes + verbs: + - get + - list + - update + - apiGroups: [""] + resources: + - nodes/status + verbs: + - update + - apiGroups: [""] + resources: + - pods + - serviceaccounts + verbs: + - get + - list + - apiGroups: [""] + resources: + - pods/status + verbs: + - update + - apiGroups: ["crd.projectcalico.org"] + resources: + - bgppeers + - bgpconfigurations + - clusterinformations + - felixconfigurations + - globalnetworkpolicies + - globalnetworksets + - ippools + - kubecontrollersconfigurations + - networkpolicies + - networksets + - hostendpoints + - ipamblocks + - blockaffinities + - ipamhandles + - ipamconfigs + verbs: + - create + - get + - list + - update + - delete + - apiGroups: ["networking.k8s.io"] + resources: + - networkpolicies + verbs: + - get + - list + +--- + +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: calicoctl +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: calicoctl +subjects: +- kind: ServiceAccount + name: calicoctl + namespace: kube-system diff --git a/resource/kompose-dev/analysis-deployment.yaml b/resource/kompose-dev/analysis-deployment.yaml new file mode 100644 index 00000000..cef15e3d --- /dev/null +++ b/resource/kompose-dev/analysis-deployment.yaml @@ -0,0 +1,30 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + io.kompose.service: analysis + name: analysis +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: analysis + strategy: {} + template: + metadata: + labels: + io.kompose.network/hobbit-core: "true" + io.kompose.service: analysis + spec: + containers: + - env: + - name: HOBBIT_RABBIT_HOST + value: rabbit + image: hobbitproject/hobbit-analysis-component:dev + imagePullPolicy: IfNotPresent + name: analysis + resources: {} + restartPolicy: Always + serviceAccountName: "" + volumes: null +status: {} diff --git a/resource/kompose-dev/cadvisor-claim0-persistentvolumeclaim.yaml b/resource/kompose-dev/cadvisor-claim0-persistentvolumeclaim.yaml new file mode 100644 index 00000000..4addf836 --- /dev/null +++ b/resource/kompose-dev/cadvisor-claim0-persistentvolumeclaim.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: cadvisor-claim0 + name: cadvisor-claim0 +spec: + accessModes: + - ReadOnlyMany + resources: + requests: + storage: 100Mi +status: {} diff --git a/resource/kompose-dev/cadvisor-claim1-persistentvolumeclaim.yaml b/resource/kompose-dev/cadvisor-claim1-persistentvolumeclaim.yaml new file mode 100644 index 00000000..60756821 --- /dev/null +++ b/resource/kompose-dev/cadvisor-claim1-persistentvolumeclaim.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: cadvisor-claim1 + name: cadvisor-claim1 +spec: + accessModes: + - ReadOnlyMany + resources: + requests: + storage: 100Mi +status: {} diff --git a/resource/kompose-dev/cadvisor-claim2-persistentvolumeclaim.yaml b/resource/kompose-dev/cadvisor-claim2-persistentvolumeclaim.yaml new file mode 100644 index 00000000..f53a01fb --- /dev/null +++ b/resource/kompose-dev/cadvisor-claim2-persistentvolumeclaim.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: cadvisor-claim2 + name: cadvisor-claim2 +spec: + accessModes: + - ReadOnlyMany + resources: + requests: + storage: 100Mi +status: {} diff --git a/resource/kompose-dev/cadvisor-claim3-persistentvolumeclaim.yaml b/resource/kompose-dev/cadvisor-claim3-persistentvolumeclaim.yaml new file mode 100644 index 00000000..24bd63bc --- /dev/null +++ b/resource/kompose-dev/cadvisor-claim3-persistentvolumeclaim.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: cadvisor-claim3 + name: cadvisor-claim3 +spec: + accessModes: + - ReadOnlyMany + resources: + requests: + storage: 100Mi +status: {} diff --git a/resource/kompose-dev/cadvisor-claim4-persistentvolumeclaim.yaml b/resource/kompose-dev/cadvisor-claim4-persistentvolumeclaim.yaml new file mode 100644 index 00000000..f9e15c4a --- /dev/null +++ b/resource/kompose-dev/cadvisor-claim4-persistentvolumeclaim.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: cadvisor-claim4 + name: cadvisor-claim4 +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 100Mi +status: {} diff --git a/resource/kompose-dev/cadvisor-deployment.yaml b/resource/kompose-dev/cadvisor-deployment.yaml new file mode 100644 index 00000000..1e939cdf --- /dev/null +++ b/resource/kompose-dev/cadvisor-deployment.yaml @@ -0,0 +1,62 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + io.kompose.service: cadvisor + name: cadvisor +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: cadvisor + strategy: + type: Recreate + template: + metadata: + labels: + io.kompose.network/hobbit-core: "true" + io.kompose.service: cadvisor + spec: + containers: + - image: google/cadvisor + imagePullPolicy: IfNotPresent + name: cadvisor + resources: {} + volumeMounts: + - mountPath: /rootfs + name: cadvisor-claim0 + readOnly: true + - mountPath: /dev/disk + name: cadvisor-claim1 + readOnly: true + - mountPath: /sys + name: cadvisor-claim2 + readOnly: true + - mountPath: /var/lib/docker + name: cadvisor-claim3 + readOnly: true + - mountPath: /var/run + name: cadvisor-claim4 + restartPolicy: Always + serviceAccountName: "" + volumes: + - name: cadvisor-claim0 + persistentVolumeClaim: + claimName: cadvisor-claim0 + readOnly: true + - name: cadvisor-claim1 + persistentVolumeClaim: + claimName: cadvisor-claim1 + readOnly: true + - name: cadvisor-claim2 + persistentVolumeClaim: + claimName: cadvisor-claim2 + readOnly: true + - name: cadvisor-claim3 + persistentVolumeClaim: + claimName: cadvisor-claim3 + readOnly: true + - name: cadvisor-claim4 + persistentVolumeClaim: + claimName: cadvisor-claim4 +status: {} diff --git a/resource/kompose-dev/gui-deployment.yaml b/resource/kompose-dev/gui-deployment.yaml new file mode 100644 index 00000000..3a5f9889 --- /dev/null +++ b/resource/kompose-dev/gui-deployment.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + io.kompose.service: gui + name: gui + namespace: hobbit +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: gui + strategy: {} + template: + metadata: + labels: + io.kompose.network/hobbit: "true" + io.kompose.network/hobbit-core: "true" + io.kompose.service: gui + spec: + containers: + - env: + - name: CHECK_REALM_URL + value: "false" + - name: ELASTICSEARCH_HOST + value: elasticsearch + - name: ELASTICSEARCH_HTTP_PORT + value: "9200" + - name: HOBBIT_RABBIT_HOST + value: rabbit + - name: KEYCLOAK_AUTH_URL + value: http://localhost:8181/auth + - name: KEYCLOAK_DIRECT_URL + value: http://keycloak:8080/auth + image: hobbitproject/hobbit-gui:dev + imagePullPolicy: IfNotPresent + name: gui + ports: + - containerPort: 8080 + resources: {} + restartPolicy: Always + serviceAccountName: "" + volumes: null +status: {} diff --git a/resource/kompose-dev/gui-service.yaml b/resource/kompose-dev/gui-service.yaml new file mode 100644 index 00000000..1cd543a0 --- /dev/null +++ b/resource/kompose-dev/gui-service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + io.kompose.service: gui + name: gui + namespace: hobbit +spec: + ports: + - name: "8080" + port: 8080 + targetPort: 8080 + selector: + io.kompose.service: gui +status: + loadBalancer: {} diff --git a/resource/kompose-dev/hobbit-core-networkpolicy.yaml b/resource/kompose-dev/hobbit-core-networkpolicy.yaml new file mode 100644 index 00000000..b44a6e6b --- /dev/null +++ b/resource/kompose-dev/hobbit-core-networkpolicy.yaml @@ -0,0 +1,19 @@ +apiVersion: extensions/v1beta1 +kind: NetworkPolicy +metadata: + creationTimestamp: null + name: hobbit-core +spec: + policyTypes: + - Ingress + - Egress + ingress: + - from: + - ipBlock: + cidr: 172.16.101.0/24 + - podSelector: + matchLabels: + io.kompose.network/hobbit-core: "true" + podSelector: + matchLabels: + io.kompose.network/hobbit-core: "true" diff --git a/resource/kompose-dev/hobbit-networkpolicy.yaml b/resource/kompose-dev/hobbit-networkpolicy.yaml new file mode 100644 index 00000000..541c7933 --- /dev/null +++ b/resource/kompose-dev/hobbit-networkpolicy.yaml @@ -0,0 +1,19 @@ +apiVersion: extensions/v1beta1 +kind: NetworkPolicy +metadata: + name: hobbit + namespace: hobbit +spec: + policyTypes: + - Ingress + - Egress + ingress: + - from: + - ipBlock: + cidr: 172.16.100.0/24 + - podSelector: + matchLabels: + io.kompose.network/hobbit: "true" + podSelector: + matchLabels: + io.kompose.network/hobbit: "true" diff --git a/resource/kompose-dev/keycloak-claim0-persistentvolumeclaim.yaml b/resource/kompose-dev/keycloak-claim0-persistentvolumeclaim.yaml new file mode 100644 index 00000000..9234f4a1 --- /dev/null +++ b/resource/kompose-dev/keycloak-claim0-persistentvolumeclaim.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: keycloak-claim0 + name: keycloak-claim0 +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 100Mi +status: {} diff --git a/resource/kompose-dev/keycloak-deployment.yaml b/resource/kompose-dev/keycloak-deployment.yaml new file mode 100644 index 00000000..f0f75091 --- /dev/null +++ b/resource/kompose-dev/keycloak-deployment.yaml @@ -0,0 +1,37 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + io.kompose.service: keycloak + name: keycloak + namespace: hobbit +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: keycloak + strategy: + type: Recreate + template: + metadata: + labels: + io.kompose.network/hobbit: "true" + io.kompose.service: keycloak + spec: + containers: + - image: hobbitproject/hobbit-keycloak:latest + imagePullPolicy: IfNotPresent + name: keycloak + ports: + - containerPort: 8080 + resources: {} + volumeMounts: + - mountPath: /opt/jboss/keycloak/standalone/data/db + name: keycloak-claim0 + restartPolicy: Always + serviceAccountName: "" + volumes: + - name: keycloak-claim0 + persistentVolumeClaim: + claimName: keycloak-claim0 +status: {} diff --git a/resource/kompose-dev/keycloak-service.yaml b/resource/kompose-dev/keycloak-service.yaml new file mode 100644 index 00000000..0fd862e3 --- /dev/null +++ b/resource/kompose-dev/keycloak-service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + io.kompose.service: keycloak + name: keycloak + namespace: hobbit +spec: + ports: + - name: "8181" + port: 8181 + targetPort: 8080 + selector: + io.kompose.service: keycloak +status: + loadBalancer: {} diff --git a/resource/kompose-dev/node-exporter-deployment.yaml b/resource/kompose-dev/node-exporter-deployment.yaml new file mode 100644 index 00000000..24ee8cc2 --- /dev/null +++ b/resource/kompose-dev/node-exporter-deployment.yaml @@ -0,0 +1,27 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + io.kompose.service: node-exporter + name: node-exporter +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: node-exporter + strategy: {} + template: + metadata: + labels: + io.kompose.network/hobbit-core: "true" + io.kompose.service: node-exporter + spec: + containers: + - image: prom/node-exporter + imagePullPolicy: IfNotPresent + name: node-exporter + resources: {} + restartPolicy: Always + serviceAccountName: "" + volumes: null +status: {} diff --git a/resource/kompose-dev/platform-controller-claim0-persistentvolumeclaim.yaml b/resource/kompose-dev/platform-controller-claim0-persistentvolumeclaim.yaml new file mode 100644 index 00000000..80aeb30a --- /dev/null +++ b/resource/kompose-dev/platform-controller-claim0-persistentvolumeclaim.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: platform-controller-claim0 + name: platform-controller-claim0 +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 100Mi +status: {} diff --git a/resource/kompose-dev/platform-controller-deployment.yaml b/resource/kompose-dev/platform-controller-deployment.yaml new file mode 100644 index 00000000..ac17e0e3 --- /dev/null +++ b/resource/kompose-dev/platform-controller-deployment.yaml @@ -0,0 +1,54 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + io.kompose.service: platform-controller + name: platform-controller +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: platform-controller + strategy: + type: Recreate + template: + metadata: + labels: + io.kompose.network/hobbit-core: "true" + io.kompose.service: platform-controller + spec: + containers: + - env: + - name: DEPLOY_ENV + value: testing + - name: GITLAB_EMAIL + - name: GITLAB_TOKEN + - name: GITLAB_USER + - name: HOBBIT_RABBIT_EXPERIMENTS_HOST + value: rabbit + - name: HOBBIT_RABBIT_HOST + value: rabbit + - name: HOBBIT_REDIS_HOST + value: redis + - name: LOGGING_GELF_ADDRESS + value: udp://localhost:12201 + - name: PROMETHEUS_HOST + value: prometheus + - name: PROMETHEUS_PORT + value: "9090" + - name: SWARM_NODE_NUMBER + value: "1" + image: hobbitproject/hobbit-platform-controller:dev + imagePullPolicy: IfNotPresent + name: platform-controller + resources: {} + volumeMounts: + - mountPath: /var/run/docker.sock + name: platform-controller-claim0 + restartPolicy: Always + serviceAccountName: "" + volumes: + - name: platform-controller-claim0 + persistentVolumeClaim: + claimName: platform-controller-claim0 +status: {} diff --git a/resource/kompose-dev/prometheus-claim0-persistentvolumeclaim.yaml b/resource/kompose-dev/prometheus-claim0-persistentvolumeclaim.yaml new file mode 100644 index 00000000..b79265ba --- /dev/null +++ b/resource/kompose-dev/prometheus-claim0-persistentvolumeclaim.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: prometheus-claim0 + name: prometheus-claim0 +spec: + accessModes: + - ReadOnlyMany + resources: + requests: + storage: 100Mi +status: {} diff --git a/resource/kompose-dev/prometheus-deployment.yaml b/resource/kompose-dev/prometheus-deployment.yaml new file mode 100644 index 00000000..1d3518d4 --- /dev/null +++ b/resource/kompose-dev/prometheus-deployment.yaml @@ -0,0 +1,40 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + io.kompose.service: prometheus + name: prometheus +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: prometheus + strategy: + type: Recreate + template: + metadata: + labels: + io.kompose.network/hobbit-core: "true" + io.kompose.service: prometheus + spec: + containers: + - args: + - --config.file=/config/prometheus.conf + image: prom/prometheus + imagePullPolicy: IfNotPresent + name: prometheus + ports: + - containerPort: 9090 + resources: {} + volumeMounts: + - mountPath: /config + name: prometheus-claim0 + readOnly: true + restartPolicy: Always + serviceAccountName: "" + volumes: + - name: prometheus-claim0 + persistentVolumeClaim: + claimName: prometheus-claim0 + readOnly: true +status: {} diff --git a/resource/kompose-dev/prometheus-service.yaml b/resource/kompose-dev/prometheus-service.yaml new file mode 100644 index 00000000..eb95e487 --- /dev/null +++ b/resource/kompose-dev/prometheus-service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + io.kompose.service: prometheus + name: prometheus +spec: + ports: + - name: "9090" + port: 9090 + targetPort: 9090 + selector: + io.kompose.service: prometheus +status: + loadBalancer: {} diff --git a/resource/kompose-dev/rabbit-deployment.yaml b/resource/kompose-dev/rabbit-deployment.yaml new file mode 100644 index 00000000..fc807b22 --- /dev/null +++ b/resource/kompose-dev/rabbit-deployment.yaml @@ -0,0 +1,32 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + io.kompose.service: rabbit + name: rabbit + namespace: hobbit +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: rabbit + strategy: {} + template: + metadata: + labels: + io.kompose.network/hobbit: "true" + io.kompose.network/hobbit-core: "true" + io.kompose.service: rabbit + spec: + containers: + - image: rabbitmq:management + imagePullPolicy: IfNotPresent + name: rabbit + ports: + - containerPort: 15672 + - containerPort: 5672 + resources: {} + restartPolicy: Always + serviceAccountName: "" + volumes: null +status: {} diff --git a/resource/kompose-dev/rabbit-service.yaml b/resource/kompose-dev/rabbit-service.yaml new file mode 100644 index 00000000..d69898df --- /dev/null +++ b/resource/kompose-dev/rabbit-service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + io.kompose.service: rabbit + name: rabbit + namespace: hobbit +spec: + ports: + - name: "8081" + port: 8081 + targetPort: 15672 + - name: "5672" + port: 5672 + targetPort: 5672 + selector: + io.kompose.service: rabbit +status: + loadBalancer: {} diff --git a/resource/kompose-dev/redis-claim0-persistentvolumeclaim.yaml b/resource/kompose-dev/redis-claim0-persistentvolumeclaim.yaml new file mode 100644 index 00000000..d552d346 --- /dev/null +++ b/resource/kompose-dev/redis-claim0-persistentvolumeclaim.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: redis-claim0 + name: redis-claim0 +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 100Mi +status: {} diff --git a/resource/kompose-dev/redis-deployment.yaml b/resource/kompose-dev/redis-deployment.yaml new file mode 100644 index 00000000..c3eb0485 --- /dev/null +++ b/resource/kompose-dev/redis-deployment.yaml @@ -0,0 +1,39 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + io.kompose.service: redis + name: redis +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: redis + strategy: + type: Recreate + template: + metadata: + labels: + io.kompose.network/hobbit-core: "true" + io.kompose.service: redis + spec: + containers: + - args: + - redis-server + - /data/redis.conf + image: redis:4.0.7 + imagePullPolicy: IfNotPresent + name: redis + ports: + - containerPort: 6379 + resources: {} + volumeMounts: + - mountPath: /data + name: redis-claim0 + restartPolicy: Always + serviceAccountName: "" + volumes: + - name: redis-claim0 + persistentVolumeClaim: + claimName: redis-claim0 +status: {} diff --git a/resource/kompose-dev/redis-service.yaml b/resource/kompose-dev/redis-service.yaml new file mode 100644 index 00000000..702eaab0 --- /dev/null +++ b/resource/kompose-dev/redis-service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + io.kompose.service: redis + name: redis +spec: + ports: + - name: "6379" + port: 6379 + targetPort: 6379 + selector: + io.kompose.service: redis +status: + loadBalancer: {} diff --git a/resource/kompose-dev/storage-service-deployment.yaml b/resource/kompose-dev/storage-service-deployment.yaml new file mode 100644 index 00000000..d5c060e0 --- /dev/null +++ b/resource/kompose-dev/storage-service-deployment.yaml @@ -0,0 +1,36 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + io.kompose.service: storage-service + name: storage-service +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: storage-service + strategy: {} + template: + metadata: + labels: + io.kompose.network/hobbit-core: "true" + io.kompose.service: storage-service + spec: + containers: + - env: + - name: HOBBIT_RABBIT_HOST + value: rabbit + - name: SPARQL_ENDPOINT_PASSWORD + value: Password + - name: SPARQL_ENDPOINT_URL + value: http://vos:8890/sparql + - name: SPARQL_ENDPOINT_USERNAME + value: HobbitPlatform + image: hobbitproject/hobbit-storage-service:dev + imagePullPolicy: IfNotPresent + name: storage-service + resources: {} + restartPolicy: Always + serviceAccountName: "" + volumes: null +status: {} diff --git a/resource/kompose-dev/vos-claim0-persistentvolumeclaim.yaml b/resource/kompose-dev/vos-claim0-persistentvolumeclaim.yaml new file mode 100644 index 00000000..1b2e8325 --- /dev/null +++ b/resource/kompose-dev/vos-claim0-persistentvolumeclaim.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: vos-claim0 + name: vos-claim0 +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 100Mi +status: {} diff --git a/resource/kompose-dev/vos-deployment.yaml b/resource/kompose-dev/vos-deployment.yaml new file mode 100644 index 00000000..b1eed453 --- /dev/null +++ b/resource/kompose-dev/vos-deployment.yaml @@ -0,0 +1,37 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + io.kompose.service: vos + name: vos +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: vos + strategy: + type: Recreate + template: + metadata: + labels: + io.kompose.network/hobbit-core: "true" + io.kompose.service: vos + spec: + containers: + - image: hobbitproject/virtuoso_opensource:v07.20.3217 + imagePullPolicy: IfNotPresent + name: vos + ports: + - containerPort: 8890 + resources: {} + volumeMounts: + - mountPath: /opt/virtuoso-opensource/database + name: vos-claim0 + hostname: vos + restartPolicy: Always + serviceAccountName: "" + volumes: + - name: vos-claim0 + persistentVolumeClaim: + claimName: vos-claim0 +status: {} diff --git a/resource/kompose-dev/vos-service.yaml b/resource/kompose-dev/vos-service.yaml new file mode 100644 index 00000000..9311d032 --- /dev/null +++ b/resource/kompose-dev/vos-service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + io.kompose.service: vos + name: vos +spec: + ports: + - name: "8890" + port: 8890 + targetPort: 8890 + selector: + io.kompose.service: vos +status: + loadBalancer: {} diff --git a/resource/kompose-elk/elasticsearch-claim0-persistentvolumeclaim.yaml b/resource/kompose-elk/elasticsearch-claim0-persistentvolumeclaim.yaml new file mode 100644 index 00000000..e0162fc1 --- /dev/null +++ b/resource/kompose-elk/elasticsearch-claim0-persistentvolumeclaim.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: elasticsearch-claim0 + name: elasticsearch-claim0 +spec: + accessModes: + - ReadOnlyMany + resources: + requests: + storage: 100Mi +status: {} diff --git a/resource/kompose-elk/elasticsearch-claim1-persistentvolumeclaim.yaml b/resource/kompose-elk/elasticsearch-claim1-persistentvolumeclaim.yaml new file mode 100644 index 00000000..3f034a7c --- /dev/null +++ b/resource/kompose-elk/elasticsearch-claim1-persistentvolumeclaim.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: elasticsearch-claim1 + name: elasticsearch-claim1 +spec: + accessModes: + - ReadOnlyMany + resources: + requests: + storage: 100Mi +status: {} diff --git a/resource/kompose-elk/elasticsearch-claim2-persistentvolumeclaim.yaml b/resource/kompose-elk/elasticsearch-claim2-persistentvolumeclaim.yaml new file mode 100644 index 00000000..ff5ccb66 --- /dev/null +++ b/resource/kompose-elk/elasticsearch-claim2-persistentvolumeclaim.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: elasticsearch-claim2 + name: elasticsearch-claim2 +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 100Mi +status: {} diff --git a/resource/kompose-elk/elasticsearch-deployment.yaml b/resource/kompose-elk/elasticsearch-deployment.yaml new file mode 100644 index 00000000..51dac780 --- /dev/null +++ b/resource/kompose-elk/elasticsearch-deployment.yaml @@ -0,0 +1,57 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + io.kompose.service: elasticsearch + name: elasticsearch +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: elasticsearch + strategy: + type: Recreate + template: + metadata: + labels: + io.kompose.network/hobbit: "true" + io.kompose.network/hobbit-core: "true" + io.kompose.network/hobbit-services: "true" + io.kompose.service: elasticsearch + spec: + containers: + - args: + - elasticsearch + - -E + - network.host=0.0.0.0 + image: docker.elastic.co/elasticsearch/elasticsearch:5.4.0 + imagePullPolicy: IfNotPresent + name: elasticsearch + ports: + - containerPort: 9200 + - containerPort: 9300 + resources: + limits: + memory: "8589934592" + volumeMounts: + - mountPath: /usr/share/elasticsearch/config/elasticsearch.yml + name: elasticsearch-hostpath0 + readOnly: true + - mountPath: /usr/share/elasticsearch/config/jvm.options + name: elasticsearch-hostpath1 + readOnly: true + - mountPath: /usr/share/elasticsearch/data + name: elasticsearch-hostpath2 + restartPolicy: Always + serviceAccountName: "" + volumes: + - hostPath: + path: ./resource/kompose-elk/config/elk/elasticsearch.yml + name: elasticsearch-hostpath0 + - hostPath: + path: ./resource/kompose-elk/config/elk/jvm.options + name: elasticsearch-hostpath1 + - hostPath: + path: ./resource/kompose-elk/config/elk/logs + name: elasticsearch-hostpath2 +status: {} diff --git a/resource/kompose-elk/elasticsearch-service.yaml b/resource/kompose-elk/elasticsearch-service.yaml new file mode 100644 index 00000000..5154625c --- /dev/null +++ b/resource/kompose-elk/elasticsearch-service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + io.kompose.service: elasticsearch + name: elasticsearch +spec: + ports: + - name: "9200" + port: 9200 + targetPort: 9200 + - name: "9300" + port: 9300 + targetPort: 9300 + selector: + io.kompose.service: elasticsearch +status: + loadBalancer: {} diff --git a/resource/kompose-elk/hobbit-core-networkpolicy.yaml b/resource/kompose-elk/hobbit-core-networkpolicy.yaml new file mode 100644 index 00000000..0c04cf51 --- /dev/null +++ b/resource/kompose-elk/hobbit-core-networkpolicy.yaml @@ -0,0 +1,19 @@ +apiVersion: extensions/v1beta1 +kind: NetworkPolicy +metadata: + creationTimestamp: null + name: hobbit-core +spec: + policyTypes: + - Ingress + - Egress + ingress: + - from: + - ipBlock: + cidr: 172.16.101.0/24 + - podSelector: + matchLabels: + io.kompose.network/hobbit-core: "true" + podSelector: + matchLabels: + io.kompose.network/hobbit-core: "true" diff --git a/resource/kompose-elk/hobbit-networkpolicy.yaml b/resource/kompose-elk/hobbit-networkpolicy.yaml new file mode 100644 index 00000000..6e5bc426 --- /dev/null +++ b/resource/kompose-elk/hobbit-networkpolicy.yaml @@ -0,0 +1,19 @@ +apiVersion: extensions/v1beta1 +kind: NetworkPolicy +metadata: + creationTimestamp: null + name: hobbit +spec: + policyTypes: + - Ingress + - Egress + ingress: + - from: + - ipBlock: + cidr: 172.16.100.0/24 + - podSelector: + matchLabels: + io.kompose.network/hobbit: "true" + podSelector: + matchLabels: + io.kompose.network/hobbit: "true" diff --git a/resource/kompose-elk/hobbit-services-networkpolicy.yaml b/resource/kompose-elk/hobbit-services-networkpolicy.yaml new file mode 100644 index 00000000..4aa5345a --- /dev/null +++ b/resource/kompose-elk/hobbit-services-networkpolicy.yaml @@ -0,0 +1,16 @@ +apiVersion: extensions/v1beta1 +kind: NetworkPolicy +metadata: + creationTimestamp: null + name: hobbit-services +spec: + ingress: + - from: + - ipBlock: + cidr: 172.16.102.0/24 + - podSelector: + matchLabels: + io.kompose.network/hobbit-services: "true" + podSelector: + matchLabels: + io.kompose.network/hobbit-services: "true" diff --git a/resource/kompose-elk/kibana-deployment.yaml b/resource/kompose-elk/kibana-deployment.yaml new file mode 100644 index 00000000..2a0289bf --- /dev/null +++ b/resource/kompose-elk/kibana-deployment.yaml @@ -0,0 +1,42 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + io.kompose.service: kibana + name: kibana +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: kibana + strategy: {} + template: + metadata: + labels: + io.kompose.network/hobbit-services: "true" + io.kompose.service: kibana + spec: + containers: + - env: + - name: ELASTICSEARCH_PASSWORD + value: changeme + - name: ELASTICSEARCH_URL + value: http://elasticsearch:9200 + - name: ELASTICSEARCH_USERNAME + value: elastic + - name: SERVER_NAME + value: kibana + - name: XPACK_MONITORING_ENABLED + value: "false" + - name: XPACK_SECURITY_ENABLED + value: "false" + image: docker.elastic.co/kibana/kibana:5.2.2 + imagePullPolicy: IfNotPresent + name: kibana + ports: + - containerPort: 5601 + resources: {} + restartPolicy: Always + serviceAccountName: "" + volumes: null +status: {} diff --git a/resource/kompose-elk/kibana-service.yaml b/resource/kompose-elk/kibana-service.yaml new file mode 100644 index 00000000..e63a83b0 --- /dev/null +++ b/resource/kompose-elk/kibana-service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + io.kompose.service: kibana + name: kibana +spec: + ports: + - name: "5601" + port: 5601 + targetPort: 5601 + selector: + io.kompose.service: kibana +status: + loadBalancer: {} diff --git a/resource/kompose-elk/logstash-claim0-persistentvolumeclaim.yaml b/resource/kompose-elk/logstash-claim0-persistentvolumeclaim.yaml new file mode 100644 index 00000000..051bdad5 --- /dev/null +++ b/resource/kompose-elk/logstash-claim0-persistentvolumeclaim.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: logstash-claim0 + name: logstash-claim0 +spec: + accessModes: + - ReadOnlyMany + resources: + requests: + storage: 100Mi +status: {} diff --git a/resource/kompose-elk/logstash-claim1-persistentvolumeclaim.yaml b/resource/kompose-elk/logstash-claim1-persistentvolumeclaim.yaml new file mode 100644 index 00000000..4eee134f --- /dev/null +++ b/resource/kompose-elk/logstash-claim1-persistentvolumeclaim.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: logstash-claim1 + name: logstash-claim1 +spec: + accessModes: + - ReadOnlyMany + resources: + requests: + storage: 100Mi +status: {} diff --git a/resource/kompose-elk/logstash-deployment.yaml b/resource/kompose-elk/logstash-deployment.yaml new file mode 100644 index 00000000..92ce70f6 --- /dev/null +++ b/resource/kompose-elk/logstash-deployment.yaml @@ -0,0 +1,56 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + io.kompose.service: logstash + name: logstash +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: logstash + strategy: + type: Recreate + template: + metadata: + labels: + io.kompose.network/hobbit: "true" + io.kompose.network/hobbit-core: "true" + io.kompose.network/hobbit-services: "true" + io.kompose.service: logstash + spec: + containers: + - args: + - logstash + - -f + - /usr/share/logstash/config/logstash.conf + env: + - name: LS_JAVA_OPTS + value: -Xmx256m -Xms256m + - name: constraint:org.hobbit.type + value: =data + image: docker.elastic.co/logstash/logstash:5.2.2 + imagePullPolicy: IfNotPresent + name: logstash + ports: + - containerPort: 12201 + - containerPort: 12201 + protocol: UDP + resources: {} + volumeMounts: + - mountPath: /usr/share/logstash/config/logstash.conf + name: logstash-hostpath0 + readOnly: true + - mountPath: /usr/share/logstash/config/logstash.yml + name: logstash-hostpath1 + readOnly: true + restartPolicy: Always + serviceAccountName: "" + volumes: + - hostPath: + path: ./resource/kompose-elk/config/elk/logstash.conf + name: logstash-hostpath0 + - hostPath: + path: ./resource/kompose-elk/config/elk/logstash.yml + name: logstash-hostpath1 +status: {} diff --git a/resource/kompose-elk/logstash-service.yaml b/resource/kompose-elk/logstash-service.yaml new file mode 100644 index 00000000..5a1b4fe0 --- /dev/null +++ b/resource/kompose-elk/logstash-service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + io.kompose.service: logstash + name: logstash +spec: + ports: + - name: "12201" + port: 12201 + targetPort: 12201 + - name: 12201-udp + port: 12201 + protocol: UDP + targetPort: 12201 + selector: + io.kompose.service: logstash +status: + loadBalancer: {} diff --git a/resource/kompose/analysis.yaml b/resource/kompose/analysis.yaml new file mode 100644 index 00000000..4d6e8542 --- /dev/null +++ b/resource/kompose/analysis.yaml @@ -0,0 +1,27 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: analysis + labels: + app: analysis + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit-core\"]" +spec: + replicas: 1 + selector: + matchLabels: + app: analysis + template: + metadata: + labels: + app: analysis + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit-core\"]" + spec: + containers: + - name: analysis + image: hobbitproject/hobbit-analysis-component:latest + imagePullPolicy: IfNotPresent + env: + - name: HOBBIT_RABBIT_HOST + value: "rabbit" diff --git a/resource/kompose/gui.yaml b/resource/kompose/gui.yaml new file mode 100644 index 00000000..ba6631b6 --- /dev/null +++ b/resource/kompose/gui.yaml @@ -0,0 +1,56 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: gui + labels: + app: gui + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit-core\",\"hobbit\"]" +spec: + replicas: 1 + selector: + matchLabels: + app: gui + template: + metadata: + labels: + app: gui + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit-core\",\"hobbit\"]" + spec: + containers: + - name: gui + image: hobbitproject/hobbit-gui:latest + imagePullPolicy: IfNotPresent + env: + - name: HOBBIT_RABBIT_HOST + value: "rabbit" + - name: KEYCLOAK_AUTH_URL + value: "http://localhost:8181/auth" + - name: CHECK_REALM_URL + value: "false" + - name: KEYCLOAK_DIRECT_URL + value: "http://keycloak:8080/auth" + - name: ELASTICSEARCH_HOST + value: "elasticsearch" + - name: ELASTICSEARCH_HTTP_PORT + value: "9200" + ports: + - containerPort: 8080 + +--- + +apiVersion: v1 +kind: Service +metadata: + name: gui + labels: + app: gui + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit-core\",\"hobbit\"]" +spec: + ports: + - port: 8080 + protocol: TCP + selector: + app: gui diff --git a/resource/kompose/hobbit-core-networkpolicy.yaml b/resource/kompose/hobbit-core-networkpolicy.yaml new file mode 100644 index 00000000..5991b1e9 --- /dev/null +++ b/resource/kompose/hobbit-core-networkpolicy.yaml @@ -0,0 +1,20 @@ +apiVersion: extensions/v1beta1 +kind: NetworkPolicy +metadata: + creationTimestamp: null + name: hobbit-core + namespace: hobbit-core +spec: + policyTypes: + - Ingress + - Egress + ingress: + - from: + - ipBlock: + cidr: 172.16.101.0/24 + - podSelector: + matchLabels: + io.kompose.network/hobbit-core: "true" + podSelector: + matchLabels: + io.kompose.network/hobbit-core: "true" diff --git a/resource/kompose/hobbit-networkpolicy.yaml b/resource/kompose/hobbit-networkpolicy.yaml new file mode 100644 index 00000000..c55528ee --- /dev/null +++ b/resource/kompose/hobbit-networkpolicy.yaml @@ -0,0 +1,19 @@ +apiVersion: extensions/v1beta1 +kind: NetworkPolicy +metadata: + creationTimestamp: null + name: hobbit +spec: + policyTypes: + - Ingress + - Egress + ingress: + - from: + - ipBlock: + cidr: 172.16.100.0/24 + - podSelector: + matchLabels: + io.kompose.network/hobbit: "true" + podSelector: + matchLabels: + io.kompose.network/hobbit: "true" diff --git a/resource/kompose/keycloak.yaml b/resource/kompose/keycloak.yaml new file mode 100644 index 00000000..32618b41 --- /dev/null +++ b/resource/kompose/keycloak.yaml @@ -0,0 +1,67 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: keycloak + labels: + app: keycloak + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit\"]" +spec: + replicas: 1 + selector: + matchLabels: + app: keycloak + template: + metadata: + labels: + app: keycloak + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit\"]" + spec: + containers: + - name: keycloak + image: hobbitproject/hobbit-keycloak:latest + imagePullPolicy: IfNotPresent + securityContext: + allowPrivilegeEscalation: false + runAsUser: 0 + env: + - name: HOBBIT_RABBIT_HOST + value: "rabbit" + - name: KEYCLOAK_AUTH_URL + value: "http://localhost:8181/auth" + - name: CHECK_REALM_URL + value: "false" + - name: KEYCLOAK_DIRECT_URL + value: "http://keycloak:8080/auth" + - name: ELASTICSEARCH_HOST + value: "elasticsearch" + - name: ELASTICSEARCH_HTTP_PORT + value: "9200" + ports: + - containerPort: 8080 + volumeMounts: + - name: keycloakdata + mountPath: "/opt/jboss/keycloak/standalone/data/db" + volumes: + - name: keycloakdata + hostPath: + path: /config/keycloak + +--- + +apiVersion: v1 +kind: Service +metadata: + name: keycloak + labels: + app: keycloak + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit\"]" +spec: + ports: + - port: 8181 + targetPort: 8080 + protocol: TCP + selector: + app: keycloak diff --git a/resource/kompose/platform-controller.yaml b/resource/kompose/platform-controller.yaml new file mode 100644 index 00000000..86cd00f7 --- /dev/null +++ b/resource/kompose/platform-controller.yaml @@ -0,0 +1,48 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: platform-controller + labels: + app: platform-controller + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit-core\"]" +spec: + replicas: 1 + selector: + matchLabels: + app: platform-controller + template: + metadata: + labels: + app: platform-controller + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit-core\"]" + spec: + containers: + - name: platform-controller + image: hobbitproject/hobbit-platform-controller:latest + imagePullPolicy: IfNotPresent + env: + - name: HOBBIT_RABBIT_HOST + value: "rabbit" + - name: HOBBIT_RABBIT_EXPERIMENTS_HOST + value: "rabbit" + - name: HOBBIT_REDIS_HOST + value: "redis" + - name: DEPLOY_ENV + value: "testing" + - name: GITLAB_USER + value: "${GITLAB_USER}" + - name: GITLAB_EMAIL + value: "${GITLAB_EMAIL}" + - name: GITLAB_TOKEN + value: "${GITLAB_TOKEN}" + - name: SWARM_NODE_NUMBER + value: "1" + volumeMounts: + - name: dockersock + mountPath: "/var/run/docker.sock" + volumes: + - name: dockersock + hostPath: + path: /var/run/docker.sock diff --git a/resource/kompose/rabbit.yaml b/resource/kompose/rabbit.yaml new file mode 100644 index 00000000..ddb81611 --- /dev/null +++ b/resource/kompose/rabbit.yaml @@ -0,0 +1,52 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: rabbit + labels: + app: rabbit + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit\",\"hobbit-core\"]" +spec: + replicas: 1 + selector: + matchLabels: + app: rabbit + template: + metadata: + labels: + app: rabbit + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit\",\"hobbit-core\"]" + spec: + containers: + - name: rabbit + image: rabbitmq:management + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 15672 + - name: testing + containerPort: 5672 + +--- + +apiVersion: v1 +kind: Service +metadata: + name: rabbit + labels: + app: rabbit + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit\",\"hobbit-core\"]" +spec: + ports: + - name: http + port: 8081 + targetPort: 15672 + protocol: TCP + - name: testing + port: 5672 + targetPort: 5672 + protocol: TCP + selector: + app: rabbit diff --git a/resource/kompose/redis.yaml b/resource/kompose/redis.yaml new file mode 100644 index 00000000..9654dc3c --- /dev/null +++ b/resource/kompose/redis.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis + labels: + app: redis + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit-core\"]" +spec: + replicas: 1 + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit-core\"]" + spec: + containers: + - name: redis + image: redis:4.0.7 + imagePullPolicy: IfNotPresent + volumeMounts: + - name: redis-server + mountPath: "/data" + command: + - redis-server + - "/data/redis.conf" + volumes: + - name: redis-server + hostPath: + path: /config/redis-db diff --git a/resource/kompose/storage-service.yaml b/resource/kompose/storage-service.yaml new file mode 100644 index 00000000..edf162d2 --- /dev/null +++ b/resource/kompose/storage-service.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: storage-service + labels: + app: storage-service + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit-core\"]" +spec: + replicas: 1 + selector: + matchLabels: + app: storage-service + template: + metadata: + labels: + app: storage-service + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit-core\"]" + spec: + containers: + - name: storage-service + image: hobbitproject/hobbit-storage-service:latest + imagePullPolicy: IfNotPresent + env: + - name: SPARQL_ENDPOINT_URL + value: "http://vos:8890/sparql" + - name: HOBBIT_RABBIT_HOST + value: "rabbit" + - name: SPARQL_ENDPOINT_USERNAME + value: "HobbitPlatform" + - name: SPARQL_ENDPOINT_PASSWORD + value: "Password" + diff --git a/resource/kompose/vos.yaml b/resource/kompose/vos.yaml new file mode 100644 index 00000000..392c9e77 --- /dev/null +++ b/resource/kompose/vos.yaml @@ -0,0 +1,54 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: vos + labels: + app: vos + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit-core\"]" +spec: + replicas: 1 + selector: + matchLabels: + app: vos + template: + metadata: + labels: + app: vos + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit-core\"]" + spec: + containers: + - name: vos + image: hobbitproject/virtuoso_opensource:v07.20.3217 + imagePullPolicy: IfNotPresent + securityContext: + privileged: true + allowPrivilegeEscalation: true + ports: + - name: http + containerPort: 8890 + volumeMounts: + - name: db + mountPath: "/opt/virtuoso-opensource/database" + volumes: + - name: db + hostPath: + path: /config/db + +--- + +apiVersion: v1 +kind: Service +metadata: + name: vos + labels: + app: vos + annotations: + "cni.projectcalico.org/ipv4pools": "[\"hobbit-core\"]" +spec: + ports: + - name: http + port: 8890 + selector: + app: vos diff --git a/resource/namespaces.yaml b/resource/namespaces.yaml new file mode 100644 index 00000000..3a0426cc --- /dev/null +++ b/resource/namespaces.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: hobbit + +--- + +apiVersion: v1 +kind: Namespace +metadata: + name: hobbit-core + +--- + +apiVersion: v1 +kind: Namespace +metadata: + name: hobbit-services diff --git a/resource/pools.yaml b/resource/pools.yaml new file mode 100644 index 00000000..9360580d --- /dev/null +++ b/resource/pools.yaml @@ -0,0 +1,36 @@ +apiVersion: crd.projectcalico.org/v1 +kind: IPPool +metadata: + name: hobbit +spec: + cidr: 172.16.100.0/24 + vxlanMode: Always + natOutgoing: true + disabled: false + nodeSelector: all() + +--- + +apiVersion: crd.projectcalico.org/v1 +kind: IPPool +metadata: + name: hobbit-core +spec: + cidr: 172.16.101.0/24 + vxlanMode: Always + natOutgoing: true + disabled: false + nodeSelector: all() + +--- + +apiVersion: crd.projectcalico.org/v1 +kind: IPPool +metadata: + name: hobbit-services +spec: + cidr: 172.16.102.0/24 + vxlanMode: Always + natOutgoing: true + disabled: false + nodeSelector: all() diff --git a/storage-service-deployment.yaml b/storage-service-deployment.yaml new file mode 100644 index 00000000..cd4fc22f --- /dev/null +++ b/storage-service-deployment.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 () + creationTimestamp: null + labels: + io.kompose.service: storage-service + name: storage-service +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: storage-service + strategy: {} + template: + metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 () + creationTimestamp: null + labels: + io.kompose.network/hobbit-core: "true" + io.kompose.service: storage-service + spec: + containers: + - env: + - name: HOBBIT_RABBIT_HOST + value: rabbit + - name: SPARQL_ENDPOINT_PASSWORD + value: Password + - name: SPARQL_ENDPOINT_URL + value: http://vos:8890/sparql + - name: SPARQL_ENDPOINT_USERNAME + value: HobbitPlatform + image: hobbitproject/hobbit-storage-service:latest + imagePullPolicy: "" + name: storage-service + resources: {} + restartPolicy: Always + serviceAccountName: "" + volumes: null +status: {} diff --git a/vos-claim0-persistentvolumeclaim.yaml b/vos-claim0-persistentvolumeclaim.yaml new file mode 100644 index 00000000..e69de29b diff --git a/vos-deployment.yaml b/vos-deployment.yaml new file mode 100644 index 00000000..433eef89 --- /dev/null +++ b/vos-deployment.yaml @@ -0,0 +1,45 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 () + creationTimestamp: null + labels: + io.kompose.service: vos + name: vos +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: vos + strategy: + type: Recreate + template: + metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 () + creationTimestamp: null + labels: + io.kompose.network/hobbit-core: "true" + io.kompose.service: vos + spec: + containers: + - image: hobbitproject/virtuoso_opensource:v07.20.3217 + imagePullPolicy: "" + name: vos + ports: + - containerPort: 8890 + resources: {} + volumeMounts: + - mountPath: /opt/virtuoso-opensource/database + name: vos-claim0 + hostname: vos + restartPolicy: Always + serviceAccountName: "" + volumes: + - name: vos-claim0 + persistentVolumeClaim: + claimName: vos-claim0 +status: {} diff --git a/vos-service.yaml b/vos-service.yaml new file mode 100644 index 00000000..e69de29b