Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Container network configuration support #3983

Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.Objects;
import java.util.Optional;

import org.eclipse.kura.container.orchestration.ContainerNetworkConfiguration.ContainerNetworkConfigurationBuilder;
import org.eclipse.kura.container.orchestration.ImageConfiguration.ImageConfigurationBuilder;
import org.osgi.annotation.versioning.ProviderType;

Expand All @@ -48,6 +49,7 @@ public class ContainerConfiguration {
private Map<String, String> containerLoggerParameters;
private String containerLoggingType;
private ImageConfiguration imageConfig;
private ContainerNetworkConfiguration networkConfiguration;
private List<String> entryPoint;

private ContainerConfiguration() {
Expand Down Expand Up @@ -201,11 +203,22 @@ public int getImageDownloadTimeoutSeconds() {
return this.imageConfig.getimageDownloadTimeoutSeconds();
}

/**
* return the container's network configuration as a
* {@link ContainerNetworkConfiguration}.
*
* @return
* @since 2.4
*/
public ContainerNetworkConfiguration getContainerNetworkConfiguration() {
return this.networkConfiguration;
}

/**
* Returns a List<String> of container entry points. An empty list can be returned if no entrypoints are specified.
*
* @return
* @since 2.6
* @since 2.4
*/
public List<String> getEntryPoint() {
return this.entryPoint;
Expand Down Expand Up @@ -264,6 +277,7 @@ public static final class ContainerConfigurationBuilder {
private Map<String, String> containerLoggerParameters;
private String containerLoggingType;
private ImageConfigurationBuilder imageConfigBuilder = new ImageConfiguration.ImageConfigurationBuilder();
private ContainerNetworkConfigurationBuilder networkConfigurationBuilder = new ContainerNetworkConfigurationBuilder();
private List<String> entryPoint = new LinkedList<>();

public ContainerConfigurationBuilder setContainerName(String serviceName) {
Expand Down Expand Up @@ -341,6 +355,17 @@ public ContainerConfigurationBuilder setEntryPoint(List<String> entryPoint) {
return this;
}

/**
* Set the {@link NetworkConfiguration}
*
* @since 2.4
*/
public ContainerConfigurationBuilder setContainerNetowrkConfiguration(
ContainerNetworkConfiguration networkConfiguration) {
this.networkConfigurationBuilder.setNetworkMode(networkConfiguration.getNetworkMode());
return this;
}

/**
* Set the {@link ImageConfiguration}
*
Expand Down Expand Up @@ -368,6 +393,7 @@ public ContainerConfiguration build() {
result.containerLoggerParameters = this.containerLoggerParameters;
result.containerLoggingType = this.containerLoggingType;
result.imageConfig = this.imageConfigBuilder.build();
result.networkConfiguration = this.networkConfigurationBuilder.build();
result.entryPoint = requireNonNull(this.entryPoint, "Container EntryPoint list must not be null");

return result;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*******************************************************************************
* Copyright (c) 2022 Eurotech and/or its affiliates and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Eurotech
*******************************************************************************/

package org.eclipse.kura.container.orchestration;

import static java.util.Objects.requireNonNull;

import java.util.Objects;
import java.util.Optional;

import org.osgi.annotation.versioning.ProviderType;

/**
* Object which represents a container network configuration used to when
* requesting the generation of a new container instance.
*
* @noimplement This interface is not intended to be implemented by clients.
* @since 2.4
*
*/
@ProviderType
public class ContainerNetworkConfiguration {

private Optional<String> networkMode;

private ContainerNetworkConfiguration() {
}

/**
*
* Returns the network mode a container will be created with (e.g. 'bridge',
* 'none', 'container:', 'host').
*
* @return
*/
public Optional<String> getNetworkMode() {
return this.networkMode;
}

/**
* Creates a builder for creating a new {@link ContainerNetworkConfiguration}
* instance.
*
* @return the builder.
*/
public static ContainerNetworkConfigurationBuilder builder() {
return new ContainerNetworkConfigurationBuilder();
}

@Override
public int hashCode() {
return Objects.hash(this.networkMode);
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ContainerNetworkConfiguration)) {
return false;
}
ContainerNetworkConfiguration other = (ContainerNetworkConfiguration) obj;
return Objects.equals(this.networkMode, other.networkMode);
}

public static final class ContainerNetworkConfigurationBuilder {

private Optional<String> networkMode = Optional.empty();

public ContainerNetworkConfigurationBuilder setNetworkMode(Optional<String> networkMode) {
this.networkMode = networkMode;
return this;
}

public ContainerNetworkConfiguration build() {
ContainerNetworkConfiguration result = new ContainerNetworkConfiguration();

result.networkMode = requireNonNull(this.networkMode,
"Requested Container Network Mode Name cannot be null");

return result;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,8 @@ private String createContainer(ContainerConfiguration containerDescription) thro

configuration = containerLogConfigurationHandler(containerDescription, configuration);

configuration = containerNetworkConfigurationHandler(containerDescription, configuration);

if (containerDescription.isContainerPrivileged()) {
configuration = configuration.withPrivileged(containerDescription.isContainerPrivileged());
}
Expand Down Expand Up @@ -529,6 +531,18 @@ private HostConfig containerLogConfigurationHandler(ContainerConfiguration conta
return configuration;
}

private HostConfig containerNetworkConfigurationHandler(ContainerConfiguration containerDescription,
HostConfig configuration) {

if (containerDescription.getContainerNetworkConfiguration().getNetworkMode().isPresent()
&& !containerDescription.getContainerNetworkConfiguration().getNetworkMode().get().trim().isEmpty()) {
configuration.withNetworkMode(containerDescription.getContainerNetworkConfiguration().getNetworkMode().get()
.toLowerCase().trim());
}

return configuration;
}

private HostConfig containerPortManagementHandler(ContainerConfiguration containerDescription,
HostConfig commandBuilder) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,12 @@

<AD id="container.ports.internal" name="Internal ports"
description="A comma separated list of ports. Note, the number of internal ports must be equal to the number of external ports. Example: 80, 443."
type="String" cardinality="1" required="true"
type="String" cardinality="1" required="false"
default="80" />

<AD id="container.ports.external" name="External ports"
description="A comma separated list of ports. Note, the number of external ports must be equal to the number of internal ports. Example: 8080, 443."
type="String" cardinality="1" required="true"
type="String" cardinality="1" required="false"
default="8080" />

<AD id="container.privileged" name="Privileged Mode" type="Boolean" cardinality="1" required="true" default="false"
Expand All @@ -107,6 +107,13 @@
type="String" cardinality="1"
required="false" default="" />

<AD id="container.networkMode"
name="Networking Mode"
description="Used to specify what networking mode the container will use. Possible Drivers: bridge, none, container:{container id}, host" type="String" cardinality="0"
required="false"
default="">
</AD>

<AD id="container.loggingType"
name="Logger Type"
description="Used to specify what logging driver the container will use. By default, containers will log to a JSON-FILE on the gateway." type="String" cardinality="0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import org.eclipse.kura.configuration.Password;
import org.eclipse.kura.container.orchestration.ContainerConfiguration;
import org.eclipse.kura.container.orchestration.ContainerNetworkConfiguration;
import org.eclipse.kura.container.orchestration.ImageConfiguration;
import org.eclipse.kura.container.orchestration.PasswordRegistryCredentials;
import org.eclipse.kura.container.orchestration.RegistryCredentials;
Expand Down Expand Up @@ -54,6 +55,7 @@ public class ContainerInstanceOptions {
private static final Property<String> REGISTRY_PASSWORD = new Property<>("registry.password", "");
private static final Property<Integer> IMAGES_DOWNLOAD_TIMEOUT = new Property<>("container.image.download.timeout",
500);
private static final Property<String> CONTAINER_NETWORKING_MODE = new Property<>("container.networkMode", "");
private static final Property<String> CONTAINER_ENTRY_POINT = new Property<>("container.entrypoint", "");

private final boolean enabled;
Expand All @@ -75,6 +77,7 @@ public class ContainerInstanceOptions {
private final Optional<String> registryUsername;
private final Optional<String> registryPassword;
private final int imageDownloadTimeout;
private final Optional<String> containerNetworkingMode;
private final List<String> containerEntryPoint;

public ContainerInstanceOptions(final Map<String, Object> properties) {
Expand All @@ -101,6 +104,7 @@ public ContainerInstanceOptions(final Map<String, Object> properties) {
this.registryUsername = REGISTRY_USERNAME.getOptional(properties);
this.registryPassword = REGISTRY_PASSWORD.getOptional(properties);
this.imageDownloadTimeout = IMAGES_DOWNLOAD_TIMEOUT.get(properties);
this.containerNetworkingMode = CONTAINER_NETWORKING_MODE.getOptional(properties);
this.containerEntryPoint = parseStringListSplitByComma(CONTAINER_ENTRY_POINT.get(properties));
}

Expand Down Expand Up @@ -162,7 +166,7 @@ private List<String> parseStringListSplitByComma(String stringToSplit) {

for (String entry : stringToSplit.trim().split(",")) {
if (entry.trim().length() > 0) {
stringList.add(entry.trim());
stringList.add(entry.trim());
}
}

Expand Down Expand Up @@ -229,6 +233,10 @@ public Map<String, String> getLoggerParameters() {
return this.containerLoggingParameters;
}

public Optional<String> getContainerNetworkingMode() {
return this.containerNetworkingMode;
}

public Optional<RegistryCredentials> getRegistryCredentials() {
if (this.registryUsername.isPresent() && this.registryPassword.isPresent()) {
return Optional.of(new PasswordRegistryCredentials(this.registryURL, this.registryUsername.get(),
Expand All @@ -242,6 +250,11 @@ public int getImageDownloadTimeout() {
return this.imageDownloadTimeout;
}

private ContainerNetworkConfiguration buildContainerNetworkConfig() {
return new ContainerNetworkConfiguration.ContainerNetworkConfigurationBuilder()
.setNetworkMode(getContainerNetworkingMode()).build();
}

public List<String> getEntryPoint() {
return this.containerEntryPoint;
}
Expand All @@ -258,6 +271,7 @@ public ContainerConfiguration getContainerConfiguration() {
.setInternalPorts(getContainerPortsInternal()).setEnvVars(getContainerEnvList())
.setVolumes(getContainerVolumeList()).setPrivilegedMode(this.privilegedMode)
.setDeviceList(getContainerDeviceList()).setFrameworkManaged(true).setLoggingType(getLoggingType())
.setContainerNetowrkConfiguration(buildContainerNetworkConfig())
.setLoggerParameters(getLoggerParameters()).setEntryPoint(getEntryPoint()).build();
}

Expand All @@ -280,7 +294,8 @@ public int hashCode() {
this.containerLoggingParameters, this.containerName, this.containerVolumeString, this.containerVolumes,
this.enabled, this.externalPorts, this.image, this.imageDownloadTimeout, this.imageTag,
this.internalPorts, this.maxDownloadRetries, this.privilegedMode, this.registryPassword,
this.registryURL, this.registryUsername, this.retryInterval, this.containerEntryPoint);
this.registryURL, this.registryUsername, this.retryInterval, this.containerEntryPoint,
this.containerNetworkingMode);
}

@Override
Expand All @@ -307,6 +322,7 @@ public boolean equals(Object obj) {
&& Objects.equals(this.registryPassword, other.registryPassword)
&& Objects.equals(this.registryURL, other.registryURL)
&& Objects.equals(this.registryUsername, other.registryUsername)
&& Objects.equals(this.containerNetworkingMode, other.containerNetworkingMode)
&& Objects.equals(this.containerEntryPoint, other.containerEntryPoint)
&& this.retryInterval == other.retryInterval;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.eclipse.kura.configuration.Password;
import org.eclipse.kura.container.orchestration.ContainerConfiguration;
import org.eclipse.kura.container.orchestration.ContainerConfiguration.ContainerConfigurationBuilder;
import org.eclipse.kura.container.orchestration.ContainerNetworkConfiguration;
import org.eclipse.kura.container.orchestration.ImageConfiguration;
import org.eclipse.kura.container.orchestration.PasswordRegistryCredentials;
import org.junit.Test;
Expand Down Expand Up @@ -132,7 +133,9 @@ REGISTRY_USERNAME, new Password(REGISTRY_PASSWORD))))
.setInternalPorts(CONTAINER_PORTS_INTERNAL).setEnvVars(CONTAINER_ENV_VARS)
.setDeviceList(CONTAINER_DEVICE_LIST).setVolumes(CONTAINER_VOLUMES).setPrivilegedMode(false)
.setFrameworkManaged(false).setLoggerParameters(CONTAINER_LOGGER_PARAMETERS)
.setLoggingType(CONTAINER_LOGGER_TYPE);
.setLoggingType(CONTAINER_LOGGER_TYPE).setContainerNetowrkConfiguration(
new ContainerNetworkConfiguration.ContainerNetworkConfigurationBuilder()
.setNetworkMode(Optional.of("bridge")).build());
}

private void givenContainerOne() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public class ContainerInstanceOptionsTest {
private static final String DEFAULT_REGISTRY_USERNAME = "";
private static final String DEFAULT_REGISTRY_PASSWORD = "";
private static final int DEFAULT_IMAGES_DOWNLOAD_TIMEOUT = 500;
private static final String DEFAULT_CONTAINER_NETWORKING_MODE = "";
private static final String DEFAULT_CONTAINER_ENTRY_POINT = "";

private static final String CONTAINER_ENV = "container.env";
Expand All @@ -71,6 +72,7 @@ public class ContainerInstanceOptionsTest {
private static final String REGISTRY_USERNAME = "registry.username";
private static final String REGISTRY_PASSWORD = "registry.password";
private static final String IMAGES_DOWNLOAD_TIMEOUT = "container.image.download.timeout";
private static final String CONTAINER_NETWORKING_MODE = "container.networkMode";
private static final String CONTAINER_ENTRY_POINT = "container.entrypoint";

private Map<String, Object> properties;
Expand Down Expand Up @@ -650,6 +652,7 @@ private void givenDefaultProperties() {
this.properties.put(REGISTRY_USERNAME, DEFAULT_REGISTRY_USERNAME);
this.properties.put(REGISTRY_PASSWORD, DEFAULT_REGISTRY_PASSWORD);
this.properties.put(IMAGES_DOWNLOAD_TIMEOUT, DEFAULT_IMAGES_DOWNLOAD_TIMEOUT);
this.properties.put(CONTAINER_NETWORKING_MODE, DEFAULT_CONTAINER_NETWORKING_MODE);
this.properties.put(CONTAINER_ENTRY_POINT, DEFAULT_CONTAINER_ENTRY_POINT);
}

Expand All @@ -671,6 +674,7 @@ private void givenDifferentProperties() {
this.newProperties.put(REGISTRY_USERNAME, "test");
this.newProperties.put(REGISTRY_PASSWORD, "test");
this.newProperties.put(IMAGES_DOWNLOAD_TIMEOUT, 100);
this.newProperties.put(CONTAINER_NETWORKING_MODE, "none");
this.newProperties.put(CONTAINER_ENTRY_POINT, "./test.py,-v,-m,--human-readable,,,");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public class ContainerInstanceTest {
private static final String CONTAINER_DEVICE = "container.Device";
private static final String CONTAINER_LOGGER_PARAMETERS = "container.loggerParameters";
private static final String CONTAINER_LOGGING_TYPE = "container.loggingType";
private static final String CONTAINER_NETWORKING_MODE = "container.networkMode";

private ContainerOrchestrationService dockerService;
private Map<String, Object> properties;
Expand Down Expand Up @@ -199,6 +200,7 @@ private void givenFullProperties(boolean enabled) {
this.properties.put(CONTAINER_DEVICE, "");
this.properties.put(CONTAINER_LOGGER_PARAMETERS, "");
this.properties.put(CONTAINER_LOGGING_TYPE, "default");
this.properties.put(CONTAINER_NETWORKING_MODE, "");
}

private void givenConfigurableGenericDockerService() {
Expand Down