From 024e264f7de362785090ade810710c95328c86d5 Mon Sep 17 00:00:00 2001 From: sfiorani <109297780+sfiorani@users.noreply.github.com> Date: Thu, 4 Apr 2024 16:53:55 +0200 Subject: [PATCH] feat(container.provider): added container instances enforcement allowlist (#5197) * feat(container.orchestration.provider): added first implementation of enforcement allowlist Signed-off-by: SimoneFiorani * feat(container.orchestration.provider): enforcement allowlist implemented Signed-off-by: SimoneFiorani * feat(container.orchestration.provider): updated copyright and method signature Signed-off-by: SimoneFiorani * feat(container.orchestration.provider): improved implementation, tests added Signed-off-by: SimoneFiorani * feat(container.orchestration.provider): corrected typo in option description Signed-off-by: SimoneFiorani * feat(container.orchestration.provider): fixed indendation Co-authored-by: Mattia Dal Ben * feat(container.orchestration.provider): implemented suggestion and validation of already running containers Signed-off-by: SimoneFiorani * feat(container.orchestration.provider): added tests for monitor-starting phase Signed-off-by: SimoneFiorani * feat(container.orchestration.provider): refactored allowlist monitor class Signed-off-by: SimoneFiorani * fix: typo in log * style: fix copyright header * fix: copyright header year * feat(container.provider): added container instance digest enforcement Signed-off-by: SimoneFiorani * feat: added tests and some updates on orchestration service Signed-off-by: SimoneFiorani * feat: added running containers check after instance digest removing Signed-off-by: SimoneFiorani * feat: running container check after stopping added Signed-off-by: SimoneFiorani * refactor: refactor with suggestions Signed-off-by: SimoneFiorani * refactor: moved enforceAlreadyRunningContainer method * refactor: refactored some methods * refactor: refactored hashcode and equals methods * fix: fixed unit tests after rebase on develop * Update kura/org.eclipse.kura.container.provider/OSGI-INF/metatype/org.eclipse.kura.container.provider.ContainerInstance.xml Co-authored-by: Mattia Dal Ben * refactor: refactored constant names in Monitor * Update kura/org.eclipse.kura.container.orchestration.provider/src/main/java/org/eclipse/kura/container/orchestration/provider/impl/enforcement/AllowlistEnforcementMonitor.java Co-authored-by: Mattia Dal Ben --------- Signed-off-by: SimoneFiorani Co-authored-by: Mattia Dal Ben --- .../orchestration/ContainerConfiguration.java | 31 +++++- .../ContainerOrchestrationServiceImpl.java | 45 +++++++- .../AllowlistEnforcementMonitor.java | 14 ++- ...a.container.provider.ContainerInstance.xml | 8 ++ .../provider/ContainerInstanceOptions.java | 22 +++- ...ContainerOrchestrationServiceImplTest.java | 101 +++++++++++++++++- .../ContainerInstanceOptionsTest.java | 30 ++++++ 7 files changed, 235 insertions(+), 16 deletions(-) diff --git a/kura/org.eclipse.kura.api/src/main/java/org/eclipse/kura/container/orchestration/ContainerConfiguration.java b/kura/org.eclipse.kura.api/src/main/java/org/eclipse/kura/container/orchestration/ContainerConfiguration.java index e6fb6970f9f..07082afe95e 100644 --- a/kura/org.eclipse.kura.api/src/main/java/org/eclipse/kura/container/orchestration/ContainerConfiguration.java +++ b/kura/org.eclipse.kura.api/src/main/java/org/eclipse/kura/container/orchestration/ContainerConfiguration.java @@ -57,6 +57,7 @@ public class ContainerConfiguration { private Optional cpus; private Optional gpus; private Optional runtime; + private Optional enforcementDigest; private ContainerConfiguration() { } @@ -303,6 +304,16 @@ public Optional getRuntime() { return this.runtime; } + /** + * Return the enforcement digest assigned to the container. + * + * @return the optional runtime string used by the container + * @since 2.7 + */ + public Optional getEnforcementDigest() { + return this.enforcementDigest; + } + /** * Creates a builder for creating a new {@link ContainerConfiguration} instance. * @@ -316,8 +327,9 @@ public static ContainerConfigurationBuilder builder() { public int hashCode() { return Objects.hash(this.containerDevices, this.containerEnvVars, this.containerLoggerParameters, this.containerLoggingType, this.containerName, this.containerPorts, this.containerPrivileged, - this.containerVolumes, this.cpus, this.entryPoint, this.gpus, this.imageConfig, this.isFrameworkManaged, - this.memory, this.networkConfiguration, this.containerRestartOnFailure, this.runtime); + this.containerVolumes, this.cpus, this.enforcementDigest, this.entryPoint, this.gpus, this.imageConfig, + this.isFrameworkManaged, this.memory, this.networkConfiguration, this.containerRestartOnFailure, + this.runtime); } @Override @@ -337,8 +349,9 @@ public boolean equals(Object obj) { && Objects.equals(this.containerPorts, other.containerPorts) && Objects.equals(this.containerPrivileged, other.containerPrivileged) && Objects.equals(this.containerVolumes, other.containerVolumes) - && Objects.equals(this.cpus, other.cpus) && Objects.equals(this.entryPoint, other.entryPoint) - && Objects.equals(this.gpus, other.gpus) && Objects.equals(this.imageConfig, other.imageConfig) + && Objects.equals(this.cpus, other.cpus) && Objects.equals(enforcementDigest, other.enforcementDigest) + && Objects.equals(this.entryPoint, other.entryPoint) && Objects.equals(this.gpus, other.gpus) + && Objects.equals(this.imageConfig, other.imageConfig) && Objects.equals(this.isFrameworkManaged, other.isFrameworkManaged) && Objects.equals(this.memory, other.memory) && Objects.equals(this.networkConfiguration, other.networkConfiguration) @@ -367,6 +380,7 @@ public static final class ContainerConfigurationBuilder { private Optional cpus = Optional.empty(); private Optional gpus = Optional.empty(); private Optional runtime = Optional.empty(); + private Optional enforcementDigest = Optional.empty(); public ContainerConfigurationBuilder setContainerName(String serviceName) { this.containerName = serviceName; @@ -547,6 +561,14 @@ public ContainerConfigurationBuilder setRuntime(Optional runtime) { return this; } + /** + * @since 2.7 + */ + public ContainerConfigurationBuilder setEnforcementDigest(Optional digest) { + this.enforcementDigest = digest; + return this; + } + public ContainerConfiguration build() { if (this.containerPorts.isEmpty()) { @@ -577,6 +599,7 @@ public ContainerConfiguration build() { result.cpus = this.cpus; result.gpus = this.gpus; result.runtime = this.runtime; + result.enforcementDigest = this.enforcementDigest; return result; } diff --git a/kura/org.eclipse.kura.container.orchestration.provider/src/main/java/org/eclipse/kura/container/orchestration/provider/impl/ContainerOrchestrationServiceImpl.java b/kura/org.eclipse.kura.container.orchestration.provider/src/main/java/org/eclipse/kura/container/orchestration/provider/impl/ContainerOrchestrationServiceImpl.java index 197c1d06685..d0d74de9fb6 100644 --- a/kura/org.eclipse.kura.container.orchestration.provider/src/main/java/org/eclipse/kura/container/orchestration/provider/impl/ContainerOrchestrationServiceImpl.java +++ b/kura/org.eclipse.kura.container.orchestration.provider/src/main/java/org/eclipse/kura/container/orchestration/provider/impl/ContainerOrchestrationServiceImpl.java @@ -17,6 +17,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -91,6 +92,8 @@ public class ContainerOrchestrationServiceImpl implements ConfigurableComponent, private List exposedPorts; private AllowlistEnforcementMonitor allowlistEnforcementMonitor; + private Map containerInstancesDigests = new HashMap<>(); + public void setDockerClient(DockerClient dockerClient) { this.dockerClient = dockerClient; } @@ -374,6 +377,7 @@ public String startContainer(ContainerConfiguration container) throws KuraExcept logger.info("Creating new container instance"); pullImage(container.getImageConfiguration()); containerId = createContainer(container); + addContainerInstanceDigest(containerId, container.getEnforcementDigest()); startContainer(containerId); } @@ -391,7 +395,13 @@ public String startContainer(ContainerConfiguration container) throws KuraExcept public void stopContainer(String id) throws KuraException { checkRequestEnv(id); try { - this.dockerClient.stopContainerCmd(id).exec(); + + if (listContainersIds().contains(id)) { + this.dockerClient.stopContainerCmd(id).exec(); + } + + removeContainerInstanceDigest(id); + } catch (Exception e) { logger.error("Could not stop container {}. Caused by {}", id, e); throw new KuraException(KuraErrorCode.OS_COMMAND_ERROR); @@ -402,7 +412,11 @@ public void stopContainer(String id) throws KuraException { public void deleteContainer(String id) throws KuraException { checkRequestEnv(id); try { - this.dockerClient.removeContainerCmd(id).exec(); + + if (listContainersIds().contains(id)) { + this.dockerClient.removeContainerCmd(id).exec(); + } + this.frameworkManagedContainers.removeIf(c -> id.equals(c.id)); } catch (Exception e) { logger.error("Could not remove container {}. Caused by {}", id, e); @@ -1004,4 +1018,31 @@ public Set getImageDigestsByContainerId(String containerId) { return imageDigests; } + private void addContainerInstanceDigest(String containerId, Optional containerInstanceDigest) { + + if (containerInstanceDigest.isPresent()) { + logger.info( + "Container {} presented enforcement digest. Adding it to the digests allowlist: it will be used if the enforcement is enabled.", + containerId); + this.containerInstancesDigests.put(containerId, containerInstanceDigest.get()); + } else { + logger.info("Container {} doesn't contain the enforcement digest. " + + "If enforcement is enabled, be sure that the digest is included in the Orchestration Service allowlist", + containerId); + } + + } + + private void removeContainerInstanceDigest(String containerId) { + if (this.containerInstancesDigests.containsKey(containerId)) { + this.containerInstancesDigests.remove(containerId); + logger.info("Removed digest of container with ID {} from Container Instances Allowlist", containerId); + enforceAlreadyRunningContainer(); + } + } + + public Set getContainerInstancesAllowlist() { + return new HashSet<>(this.containerInstancesDigests.values()); + } + } diff --git a/kura/org.eclipse.kura.container.orchestration.provider/src/main/java/org/eclipse/kura/container/orchestration/provider/impl/enforcement/AllowlistEnforcementMonitor.java b/kura/org.eclipse.kura.container.orchestration.provider/src/main/java/org/eclipse/kura/container/orchestration/provider/impl/enforcement/AllowlistEnforcementMonitor.java index 3df848b1418..33136be6e46 100644 --- a/kura/org.eclipse.kura.container.orchestration.provider/src/main/java/org/eclipse/kura/container/orchestration/provider/impl/enforcement/AllowlistEnforcementMonitor.java +++ b/kura/org.eclipse.kura.container.orchestration.provider/src/main/java/org/eclipse/kura/container/orchestration/provider/impl/enforcement/AllowlistEnforcementMonitor.java @@ -31,8 +31,8 @@ public class AllowlistEnforcementMonitor extends ResultCallbackTemplate { private static final Logger logger = LoggerFactory.getLogger(AllowlistEnforcementMonitor.class); - private static final String ENFORCEMENT_SUCCESS = "Enforcement allowlist contains image digests {}...container {} is starting"; - private static final String ENFORCEMENT_FAILURE = "Enforcement allowlist doesn't contain image digests...container {} will be stopped"; + private static final String ENFORCEMENT_CHECK_SUCCESS = "Enforcement allowlist contains image digests {}...container {} is starting"; + private static final String ENFORCEMENT_CHECK_FAILURE = "Enforcement allowlist doesn't contain image digests...container {} will be stopped"; private final Set enforcementAllowlistContent; private final ContainerOrchestrationServiceImpl orchestrationServiceImpl; @@ -55,11 +55,17 @@ private void enforceAllowlistFor(String containerId) { Set digestIntersection = this.enforcementAllowlistContent.stream().distinct() .filter(digestsList::contains).collect(Collectors.toSet()); + + if (digestIntersection.isEmpty()) { + digestIntersection = this.orchestrationServiceImpl.getContainerInstancesAllowlist().stream().distinct() + .filter(digestsList::contains).collect(Collectors.toSet()); + } + if (!digestIntersection.isEmpty()) { - logger.info(ENFORCEMENT_SUCCESS, digestIntersection, containerId); + logger.info(ENFORCEMENT_CHECK_SUCCESS, digestIntersection, containerId); } else { - logger.error(ENFORCEMENT_FAILURE, containerId); + logger.info(ENFORCEMENT_CHECK_FAILURE, containerId); stopContainer(containerId); deleteContainer(containerId); } diff --git a/kura/org.eclipse.kura.container.provider/OSGI-INF/metatype/org.eclipse.kura.container.provider.ContainerInstance.xml b/kura/org.eclipse.kura.container.provider/OSGI-INF/metatype/org.eclipse.kura.container.provider.ContainerInstance.xml index 8daf533e546..1a87b3810a8 100644 --- a/kura/org.eclipse.kura.container.provider/OSGI-INF/metatype/org.eclipse.kura.container.provider.ContainerInstance.xml +++ b/kura/org.eclipse.kura.container.provider/OSGI-INF/metatype/org.eclipse.kura.container.provider.ContainerInstance.xml @@ -175,6 +175,14 @@ + + + diff --git a/kura/org.eclipse.kura.container.provider/src/main/java/org/eclipse/kura/container/provider/ContainerInstanceOptions.java b/kura/org.eclipse.kura.container.provider/src/main/java/org/eclipse/kura/container/provider/ContainerInstanceOptions.java index 2a8cbaea95d..4a1323292cc 100644 --- a/kura/org.eclipse.kura.container.provider/src/main/java/org/eclipse/kura/container/provider/ContainerInstanceOptions.java +++ b/kura/org.eclipse.kura.container.provider/src/main/java/org/eclipse/kura/container/provider/ContainerInstanceOptions.java @@ -72,6 +72,7 @@ public class ContainerInstanceOptions { ""); private static final Property SIGNATURE_VERIFY_TLOG = new Property<>( "container.signature.verify.transparency.log", true); + private static final Property ENFORCEMENT_DIGEST = new Property<>("enforcement.digest", ""); private boolean enabled; private final String image; @@ -104,6 +105,8 @@ public class ContainerInstanceOptions { private final Optional signatureTrustAnchor; private final Boolean signatureVerifyTransparencyLog; + private final Optional enforcementDigest; + public ContainerInstanceOptions(final Map properties) { if (isNull(properties)) { throw new IllegalArgumentException("Properties cannot be null!"); @@ -138,6 +141,7 @@ public ContainerInstanceOptions(final Map properties) { this.containerRuntime = parseOptionalString(CONTAINER_RUNTIME.getOptional(properties)); this.signatureTrustAnchor = parseOptionalString(SIGNATURE_TRUST_ANCHOR.getOptional(properties)); this.signatureVerifyTransparencyLog = SIGNATURE_VERIFY_TLOG.get(properties); + this.enforcementDigest = parseOptionalString(ENFORCEMENT_DIGEST.getOptional(properties)); } private Map parseVolume(String volumeString) { @@ -353,6 +357,10 @@ public Boolean getSignatureVerifyTransparencyLog() { return this.signatureVerifyTransparencyLog; } + public Optional getEnforcementDigest() { + return this.enforcementDigest; + } + private ImageConfiguration buildImageConfig() { return new ImageConfiguration.ImageConfigurationBuilder().setImageName(this.image).setImageTag(this.imageTag) .setImageDownloadTimeoutSeconds(this.imageDownloadTimeout) @@ -383,7 +391,7 @@ public ContainerConfiguration getContainerConfiguration() { .setContainerNetowrkConfiguration(buildContainerNetworkConfig()) .setLoggerParameters(getLoggerParameters()).setEntryPoint(getEntryPoint()) .setRestartOnFailure(getRestartOnFailure()).setMemory(getMemory()).setCpus(getCpus()).setGpus(getGpus()) - .setRuntime(getRuntime()).build(); + .setRuntime(getRuntime()).setEnforcementDigest(getEnforcementDigest()).build(); } private List parsePortString(String ports) { @@ -429,9 +437,10 @@ private List parsePortStringProtocol(String ports) { public int hashCode() { return Objects.hash(containerCpus, containerDevice, containerEntryPoint, containerEnv, containerGpus, containerLoggerType, containerLoggingParameters, containerMemory, containerName, - containerNetworkingMode, containerPortProtocol, containerVolumeString, containerVolumes, enabled, - externalPorts, image, imageDownloadTimeout, imageTag, internalPorts, maxDownloadRetries, privilegedMode, - registryPassword, registryURL, registryUsername, restartOnFailure, retryInterval, containerRuntime); + containerNetworkingMode, containerPortProtocol, containerRuntime, containerVolumeString, + containerVolumes, enabled, enforcementDigest, externalPorts, image, imageDownloadTimeout, imageTag, + internalPorts, maxDownloadRetries, privilegedMode, registryPassword, registryURL, registryUsername, + restartOnFailure, retryInterval, signatureTrustAnchor, signatureVerifyTransparencyLog); } @Override @@ -459,6 +468,7 @@ public boolean equals(Object obj) { && Objects.equals(containerPortProtocol, other.containerPortProtocol) && Objects.equals(containerVolumeString, other.containerVolumeString) && Objects.equals(containerVolumes, other.containerVolumes) && enabled == other.enabled + && Objects.equals(enforcementDigest, other.enforcementDigest) && Objects.equals(externalPorts, other.externalPorts) && Objects.equals(image, other.image) && imageDownloadTimeout == other.imageDownloadTimeout && Objects.equals(imageTag, other.imageTag) && Objects.equals(internalPorts, other.internalPorts) && maxDownloadRetries == other.maxDownloadRetries @@ -466,7 +476,9 @@ public boolean equals(Object obj) { && Objects.equals(registryURL, other.registryURL) && Objects.equals(registryUsername, other.registryUsername) && restartOnFailure == other.restartOnFailure && retryInterval == other.retryInterval - && Objects.equals(containerRuntime, other.containerRuntime); + && Objects.equals(containerRuntime, other.containerRuntime) + && Objects.equals(signatureTrustAnchor, other.signatureTrustAnchor) + && Objects.equals(signatureVerifyTransparencyLog, other.signatureVerifyTransparencyLog); } } diff --git a/kura/test/org.eclipse.kura.container.orchestration.provider.test/src/test/java/org/eclipse/kura/container/orchestration/provider/ContainerOrchestrationServiceImplTest.java b/kura/test/org.eclipse.kura.container.orchestration.provider.test/src/test/java/org/eclipse/kura/container/orchestration/provider/ContainerOrchestrationServiceImplTest.java index 65469544fb8..103d5457b87 100644 --- a/kura/test/org.eclipse.kura.container.orchestration.provider.test/src/test/java/org/eclipse/kura/container/orchestration/provider/ContainerOrchestrationServiceImplTest.java +++ b/kura/test/org.eclipse.kura.container.orchestration.provider.test/src/test/java/org/eclipse/kura/container/orchestration/provider/ContainerOrchestrationServiceImplTest.java @@ -14,13 +14,16 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -46,6 +49,7 @@ import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.CreateContainerCmd; +import com.github.dockerjava.api.command.CreateContainerResponse; import com.github.dockerjava.api.command.ListContainersCmd; import com.github.dockerjava.api.command.ListImagesCmd; import com.github.dockerjava.api.model.Container; @@ -54,6 +58,7 @@ public class ContainerOrchestrationServiceImplTest { + private static final String CONTAINER_INSTANCE_DIGEST = "sha256:test"; private static final String[] REPO_DIGESTS_ARRAY = new String[] { "ubuntu@sha256:c26ae7472d624ba1fafd296e73cecc4f93f853088e6a9c13c0d52f6ca5865107" }; private static final String[] EXPECTED_DIGESTS_ARRAY = new String[] { @@ -74,6 +79,8 @@ public class ContainerOrchestrationServiceImplTest { private static final String REPOSITORY_USERNAME = "repository.username"; private static final String REPOSITORY_PASSWORD = "repository.password"; + private static final String ENFORCEMENT_ENABLED = "enforcement.enabled"; + private static final String REGISTRY_URL = "https://test"; private static final String REGISTRY_USERNAME = "test"; private static final String REGISTRY_PASSWORD = "test1"; @@ -110,6 +117,9 @@ public class ContainerOrchestrationServiceImplTest { private Map properties; private String containerId; + CreateContainerCmd createContainerCmd = mock(CreateContainerCmd.class); + CreateContainerResponse createContainerResponse = mock(CreateContainerResponse.class); + Set digestsList; @Test @@ -337,6 +347,41 @@ public void testImageDigestsByContainerName() throws KuraException, InterruptedE thenDigestsListEqualsExpectedOne(EXPECTED_DIGESTS_ARRAY); } + @Test + public void testContainerInstanceDigestIsAddedToAllowlist() throws KuraException, InterruptedException { + + givenFullProperties(true); + givenEnforcementEnabledProperty(true); + givenDockerServiceImplSpy(); + givenDockerClient(); + + whenActivateInstance(); + whenDockerClientMockCreateContainer(); + whenMockForContainerInstancesDigestAdding(); + whenRunContainer(); + + thenContainerInstanceDigestIsAddedToAllowlist(); + thenContainerInstanceDigestIsExpectedOne(CONTAINER_INSTANCE_DIGEST); + } + + @Test + public void testContainerInstanceDigestIsAddedToAndRemovedFromAllowlist() + throws KuraException, InterruptedException { + + givenFullProperties(true); + givenEnforcementEnabledProperty(true); + givenDockerServiceImplSpy(); + givenDockerClient(); + + whenActivateInstance(); + whenDockerClientMockCreateContainer(); + whenMockForContainerInstancesDigestAdding(); + whenRunContainer(); + whenStopContainer(); + + thenContainerInstanceDigestIsNotInAllowlist(); + } + /** * givens */ @@ -398,6 +443,10 @@ private void givenAuthWithRepoAndCredentials() { this.properties.put(IMAGES_DOWNLOAD_TIMEOUT, DEFAULT_IMAGES_DOWNLOAD_TIMEOUT); } + private void givenEnforcementEnabledProperty(boolean enabled) { + this.properties.put(ENFORCEMENT_ENABLED, enabled); + } + /** * when */ @@ -462,6 +511,18 @@ private void whenDockerClientMockSomeContainers() { when(this.mockedListContainersCmd.exec()).thenReturn(containerListmock); } + private void whenDockerClientMockCreateContainer() { + + this.createContainerCmd = mock(CreateContainerCmd.class, Mockito.RETURNS_DEEP_STUBS); + this.createContainerResponse = new CreateContainerResponse(); + this.createContainerResponse.setId("containerId"); + when(this.localDockerClient.createContainerCmd(anyString())).thenReturn(this.createContainerCmd); + when(this.createContainerCmd.withHostConfig(any())).thenReturn(this.createContainerCmd); + when(this.createContainerCmd.withHostConfig(any()).exec()).thenReturn(this.createContainerResponse); + when(this.createContainerCmd.withExposedPorts(anyList())).thenReturn(this.createContainerCmd); + when(this.createContainerCmd.withName(any())).thenReturn(this.createContainerCmd); + } + private void whenDockerClientMockContainerWithPorts() { List containerListmock = new LinkedList<>(); @@ -568,13 +629,37 @@ REGISTRY_USERNAME, new Password(REGISTRY_PASSWORD)))) } + private void whenMockForContainerInstancesDigestAdding() { + + // Build Respective CD's + ContainerInstanceDescriptor mcontCD1 = ContainerInstanceDescriptor.builder().setContainerID("1d3dewf34r5") + .setContainerName(CONTAINER_NAME_FRANK).setContainerImage(IMAGE_NAME_NGINX).build(); + + this.imageConfig = new ImageConfiguration.ImageConfigurationBuilder().setImageName(IMAGE_NAME_NGINX) + .setImageTag(IMAGE_TAG_LATEST).setImageDownloadTimeoutSeconds(0) + .setRegistryCredentials(Optional.of(new PasswordRegistryCredentials(Optional.of(REGISTRY_URL), + REGISTRY_USERNAME, new Password(REGISTRY_PASSWORD)))) + .build(); + + org.eclipse.kura.container.orchestration.ContainerPort containerPort = new org.eclipse.kura.container.orchestration.ContainerPort( + TCP_CONTAINER_PORT.getPrivatePort(), TCP_CONTAINER_PORT.getPublicPort()); + + this.containerConfig1 = ContainerConfiguration.builder().setContainerName(CONTAINER_NAME_FRANK) + .setImageConfiguration(imageConfig).setVolumes(Collections.singletonMap("test", "~/test/test")) + .setEnforcementDigest(Optional.of(CONTAINER_INSTANCE_DIGEST)).setLoggingType("NONE") + .setContainerPorts(Arrays.asList(containerPort)).build(); + + this.runningContainerDescriptor = new ContainerInstanceDescriptor[] { mcontCD1 }; + + } + private void whenRunContainer() throws KuraException, InterruptedException { // startContainer this.containerId = this.dockerService.startContainer(this.containerConfig1); } private void whenStopContainer() throws KuraException { - // startContainer + // stopContainer this.dockerService.stopContainer(this.containerId); } @@ -660,4 +745,18 @@ private void thenCheckIfImagesWereListed() { private void thenDigestsListEqualsExpectedOne(String[] digestsArray) { assertEquals(new HashSet<>(Arrays.asList(digestsArray)), this.digestsList); } + + private void thenContainerInstanceDigestIsAddedToAllowlist() { + assertFalse(this.dockerService.getContainerInstancesAllowlist().isEmpty()); + } + + private void thenContainerInstanceDigestIsNotInAllowlist() { + this.dockerService.getContainerInstancesAllowlist().stream().forEach(System.err::println); + assertTrue(this.dockerService.getContainerInstancesAllowlist().isEmpty()); + } + + private void thenContainerInstanceDigestIsExpectedOne(String expected) { + List actualDigests = new ArrayList<>(this.dockerService.getContainerInstancesAllowlist()); + assertEquals(actualDigests.get(0), expected); + } } diff --git a/kura/test/org.eclipse.kura.container.provider.test/src/test/java/org/eclipse/kura/container/provider/ContainerInstanceOptionsTest.java b/kura/test/org.eclipse.kura.container.provider.test/src/test/java/org/eclipse/kura/container/provider/ContainerInstanceOptionsTest.java index 00ce67e93c5..e1d64729b0d 100644 --- a/kura/test/org.eclipse.kura.container.provider.test/src/test/java/org/eclipse/kura/container/provider/ContainerInstanceOptionsTest.java +++ b/kura/test/org.eclipse.kura.container.provider.test/src/test/java/org/eclipse/kura/container/provider/ContainerInstanceOptionsTest.java @@ -56,6 +56,7 @@ public class ContainerInstanceOptionsTest { private static final String DEFAULT_CONTAINER_CPUS = ""; private static final String DEFAULT_CONTAINER_GPUS = ""; private static final String DEFAULT_CONTAINER_RUNTIME = ""; + private static final String DEFAULT_ENFORCEMENT_DIGEST = ""; private static final String CONTAINER_ENV = "container.env"; private static final String CONTAINER_PORTS_INTERNAL = "container.ports.internal"; @@ -81,6 +82,7 @@ public class ContainerInstanceOptionsTest { private static final String CONTAINER_CPUS = "container.cpus"; private static final String CONTAINER_GPUS = "container.gpus"; private static final String CONTAINER_RUNTIME = "container.runtime"; + private static final String ENFORCEMENT_DIGEST = "enforcement.digest"; private Map properties; @@ -750,6 +752,18 @@ public void testRuntimeOptionIsSet() { thenContainerRuntimeIs("coolRuntime"); } + @Test + public void testEnforcementDigest() { + givenDefaultProperties(); + givenEnforcementDigestProperty("sha256:test"); + givenConfigurableGenericDockerServiceOptions(); + + whenGetContainerDescriptor(); + + thenEnforcementDigestIsNotEmpty(); + thenEnforcementDigestIs("sha256:test"); + } + private void testMemoryOption(String stringValue, Long longValue) { givenDefaultProperties(); givenMemoryProperty(stringValue); @@ -793,6 +807,7 @@ private void givenDefaultProperties() { this.properties.put(CONTAINER_CPUS, DEFAULT_CONTAINER_CPUS); this.properties.put(CONTAINER_GPUS, DEFAULT_CONTAINER_GPUS); this.properties.put(CONTAINER_RUNTIME, DEFAULT_CONTAINER_RUNTIME); + this.properties.put(ENFORCEMENT_DIGEST, DEFAULT_ENFORCEMENT_DIGEST); } private void givenDifferentProperties() { @@ -818,6 +833,7 @@ private void givenDifferentProperties() { this.newProperties.put(CONTAINER_MEMORY, "100m"); this.newProperties.put(CONTAINER_CPUS, "1.5"); this.newProperties.put(CONTAINER_RUNTIME, "myRuntime"); + this.newProperties.put(ENFORCEMENT_DIGEST, ""); } private void givenMemoryProperty(String memory) { @@ -844,6 +860,12 @@ private void givenRuntimeProperty(String runtime) { } } + private void givenEnforcementDigestProperty(String digest) { + if (this.properties != null) { + this.properties.put(ENFORCEMENT_DIGEST, digest); + } + } + private void givenConfigurableGenericDockerServiceOptions() { this.cgdso = new ContainerInstanceOptions(this.properties); } @@ -1154,4 +1176,12 @@ private void thenContainerRuntimeIsNotEmpty() { private void thenContainerRuntimeIs(String value) { assertEquals(this.containerDescriptor.getRuntime().get(), value); } + + private void thenEnforcementDigestIsNotEmpty() { + assertTrue(this.containerDescriptor.getEnforcementDigest().isPresent()); + } + + private void thenEnforcementDigestIs(String value) { + assertEquals(this.containerDescriptor.getEnforcementDigest().get(), value); + } }