Skip to content

Commit 860d707

Browse files
committed
exec session PipedOutputStream passed to Dockerutil for handling exit properly, updatecontainer logic fixed for minimize downtime ,agent container image layers optimized
1 parent f9ac4ff commit 860d707

7 files changed

Lines changed: 123 additions & 80 deletions

File tree

Dockerfile

Lines changed: 61 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -106,73 +106,76 @@ RUN true && \
106106
usermod -aG root,wheel iofog-agent && \
107107
true
108108

109+
# Intermediate stage to collect all ubi-dep files
110+
FROM registry.access.redhat.com/ubi9/ubi-minimal:latest AS ubi-dep-staging
111+
COPY --from=ubi-dep /usr/share/zoneinfo /staging/usr/share/zoneinfo
112+
COPY --from=ubi-dep /usr/bin/curl /staging/usr/bin/curl
113+
COPY --from=ubi-dep /usr/bin/grep /staging/usr/bin/grep
114+
COPY --from=ubi-dep /usr/bin/gzip /staging/usr/bin/gzip
115+
COPY --from=ubi-dep /usr/bin/pgrep /staging/usr/bin/pgrep
116+
COPY --from=ubi-dep /usr/bin/awk /staging/usr/bin/awk
117+
COPY --from=ubi-dep /etc/ssl/certs/ca-bundle.crt /staging/etc/ssl/certs/ca-bundle.crt
118+
COPY --from=ubi-dep /etc/pki/tls/certs/ca-bundle.crt /staging/etc/pki/tls/certs/ca-bundle.crt
119+
COPY --from=ubi-dep /usr/lib64/libc.so.6 /staging/usr/lib64/libc.so.6
120+
COPY --from=ubi-dep /usr/lib64/libcom_err.so.2 /staging/usr/lib64/libcom_err.so.2
121+
COPY --from=ubi-dep /usr/lib64/libcrypto.so.3 /staging/usr/lib64/libcrypto.so.3
122+
COPY --from=ubi-dep /usr/lib64/libcurl.so.4 /staging/usr/lib64/libcurl.so.4
123+
COPY --from=ubi-dep /usr/lib64/libffi.so.8 /staging/usr/lib64/libffi.so.8
124+
COPY --from=ubi-dep /usr/lib64/libgmp.so.10 /staging/usr/lib64/libgmp.so.10
125+
COPY --from=ubi-dep /usr/lib64/libgnutls.so.30 /staging/usr/lib64/libgnutls.so.30
126+
COPY --from=ubi-dep /usr/lib64/libgssapi_krb5.so.2 /staging/usr/lib64/libgssapi_krb5.so.2
127+
COPY --from=ubi-dep /usr/lib64/libpcre.so.1 /staging/usr/lib64/libpcre.so.1
128+
COPY --from=ubi-dep /usr/lib64/libhogweed.so.6 /staging/usr/lib64/libhogweed.so.6
129+
COPY --from=ubi-dep /usr/lib64/libidn2.so.0 /staging/usr/lib64/libidn2.so.0
130+
COPY --from=ubi-dep /usr/lib64/libk5crypto.so.3 /staging/usr/lib64/libk5crypto.so.3
131+
COPY --from=ubi-dep /usr/lib64/libkeyutils.so.1 /staging/usr/lib64/libkeyutils.so.1
132+
COPY --from=ubi-dep /usr/lib64/libkrb5.so.3 /staging/usr/lib64/libkrb5.so.3
133+
COPY --from=ubi-dep /usr/lib64/libkrb5support.so.0 /staging/usr/lib64/libkrb5support.so.0
134+
COPY --from=ubi-dep /usr/lib64/libnettle.so.8 /staging/usr/lib64/libnettle.so.8
135+
COPY --from=ubi-dep /usr/lib64/libnghttp2.so.14 /staging/usr/lib64/libnghttp2.so.14
136+
COPY --from=ubi-dep /usr/lib64/libp11-kit.so.0 /staging/usr/lib64/libp11-kit.so.0
137+
COPY --from=ubi-dep /usr/lib64/libresolv.so.2 /staging/usr/lib64/libresolv.so.2
138+
COPY --from=ubi-dep /usr/lib64/libssl.so.3 /staging/usr/lib64/libssl.so.3
139+
COPY --from=ubi-dep /usr/lib64/libtasn1.so.6 /staging/usr/lib64/libtasn1.so.6
140+
COPY --from=ubi-dep /usr/lib64/libunistring.so.2 /staging/usr/lib64/libunistring.so.2
141+
COPY --from=ubi-dep /usr/lib64/libz.so.1 /staging/usr/lib64/libz.so.1
142+
COPY --from=ubi-dep /usr/lib64/libzstd.so.1 /staging/usr/lib64/libzstd.so.1
143+
COPY --from=ubi-dep /usr/lib64/libm.so.6 /staging/usr/lib64/libm.so.6
144+
COPY --from=ubi-dep /usr/lib64/libmpfr.so.6 /staging/usr/lib64/libmpfr.so.6
145+
COPY --from=ubi-dep /usr/lib64/libreadline.so.8 /staging/usr/lib64/libreadline.so.8
146+
COPY --from=ubi-dep /usr/lib64/libsigsegv.so.2 /staging/usr/lib64/libsigsegv.so.2
147+
COPY --from=ubi-dep /usr/lib64/libtinfo.so.6 /staging/usr/lib64/libtinfo.so.6
148+
COPY --from=ubi-dep /usr/lib64/libprocps.so.8 /staging/usr/lib64/libprocps.so.8
149+
COPY --from=ubi-dep /usr/lib64/libsystemd.so.0 /staging/usr/lib64/libsystemd.so.0
150+
COPY --from=ubi-dep /usr/lib64/liblz4.so.1 /staging/usr/lib64/liblz4.so.1
151+
COPY --from=ubi-dep /usr/lib64/libcap.so.2 /staging/usr/lib64/libcap.so.2
152+
COPY --from=ubi-dep /usr/lib64/libgcrypt.so.20 /staging/usr/lib64/libgcrypt.so.20
153+
COPY --from=ubi-dep /usr/lib64/libgpg-error.so.0 /staging/usr/lib64/libgpg-error.so.0
154+
COPY --from=ubi-dep /usr/lib64/liblzma.so.5 /staging/usr/lib64/liblzma.so.5
155+
COPY --from=ubi-dep /etc/passwd /staging/etc/passwd
156+
COPY --from=ubi-dep /etc/group /staging/etc/group
157+
COPY --from=ubi-dep /etc/shadow /staging/etc/shadow
158+
159+
# Intermediate stage to collect all builder files
160+
FROM registry.access.redhat.com/ubi9/ubi-minimal:latest AS builder-staging
161+
COPY --from=builder packaging/iofog-agent/usr /staging/usr
162+
COPY --from=builder packaging/iofog-agent/etc/systemd/system/iofog-agent.service /staging/etc/systemd/system/iofog-agent.service
163+
COPY --from=builder packaging/iofog-agent/etc/bash_completion.d /staging/etc/bash_completion.d
164+
COPY --from=builder packaging/iofog-agent/etc/iofog-agent /staging/etc/iofog-agent
165+
109166
# Final stage using UBI Micro
110167
FROM registry.access.redhat.com/ubi9/ubi-micro:latest
111168

112-
# Copy dependencies from the ubi-dep stage
113-
COPY --from=ubi-dep /usr/share/zoneinfo /usr/share/zoneinfo
114-
COPY --from=ubi-dep /usr/bin/curl /usr/bin/
115-
COPY --from=ubi-dep /usr/bin/grep /usr/bin/
116-
COPY --from=ubi-dep /usr/bin/gzip /usr/bin/
117-
COPY --from=ubi-dep /usr/bin/pgrep /usr/bin/
118-
COPY --from=ubi-dep /usr/bin/awk /usr/bin/
119-
COPY --from=ubi-dep /etc/ssl/certs/ca-bundle.crt /etc/ssl/certs/
120-
COPY --from=ubi-dep /etc/pki/tls/certs/ca-bundle.crt /etc/pki/tls/certs/
121-
122-
# Copy required shared libraries for curl grep awk
123-
COPY --from=ubi-dep /usr/lib64/libc.so.6 /usr/lib64/
124-
COPY --from=ubi-dep /usr/lib64/libcom_err.so.2 /usr/lib64/
125-
COPY --from=ubi-dep /usr/lib64/libcrypto.so.3 /usr/lib64/
126-
COPY --from=ubi-dep /usr/lib64/libcurl.so.4 /usr/lib64/
127-
COPY --from=ubi-dep /usr/lib64/libffi.so.8 /usr/lib64/
128-
COPY --from=ubi-dep /usr/lib64/libgmp.so.10 /usr/lib64/
129-
COPY --from=ubi-dep /usr/lib64/libgnutls.so.30 /usr/lib64/
130-
COPY --from=ubi-dep /usr/lib64/libgssapi_krb5.so.2 /usr/lib64/
131-
COPY --from=ubi-dep /usr/lib64/libpcre.so.1 /usr/lib64/
132-
COPY --from=ubi-dep /usr/lib64/libhogweed.so.6 /usr/lib64/
133-
COPY --from=ubi-dep /usr/lib64/libidn2.so.0 /usr/lib64/
134-
COPY --from=ubi-dep /usr/lib64/libk5crypto.so.3 /usr/lib64/
135-
COPY --from=ubi-dep /usr/lib64/libkeyutils.so.1 /usr/lib64/
136-
COPY --from=ubi-dep /usr/lib64/libkrb5.so.3 /usr/lib64/
137-
COPY --from=ubi-dep /usr/lib64/libkrb5support.so.0 /usr/lib64/
138-
COPY --from=ubi-dep /usr/lib64/libnettle.so.8 /usr/lib64/
139-
COPY --from=ubi-dep /usr/lib64/libnghttp2.so.14 /usr/lib64/
140-
COPY --from=ubi-dep /usr/lib64/libp11-kit.so.0 /usr/lib64/
141-
COPY --from=ubi-dep /usr/lib64/libresolv.so.2 /usr/lib64/
142-
COPY --from=ubi-dep /usr/lib64/libssl.so.3 /usr/lib64/
143-
COPY --from=ubi-dep /usr/lib64/libtasn1.so.6 /usr/lib64/
144-
COPY --from=ubi-dep /usr/lib64/libunistring.so.2 /usr/lib64/
145-
COPY --from=ubi-dep /usr/lib64/libz.so.1 /usr/lib64/
146-
COPY --from=ubi-dep /usr/lib64/libzstd.so.1 /usr/lib64/
147-
COPY --from=ubi-dep /usr/lib64/libm.so.6 /usr/lib64/
148-
COPY --from=ubi-dep /usr/lib64/libmpfr.so.6 /usr/lib64/
149-
COPY --from=ubi-dep /usr/lib64/libreadline.so.8 /usr/lib64/
150-
COPY --from=ubi-dep /usr/lib64/libsigsegv.so.2 /usr/lib64/
151-
COPY --from=ubi-dep /usr/lib64/libtinfo.so.6 /usr/lib64/
152-
COPY --from=ubi-dep /usr/lib64/libprocps.so.8 /usr/lib64/
153-
COPY --from=ubi-dep /usr/lib64/libsystemd.so.0 /usr/lib64/
154-
COPY --from=ubi-dep /usr/lib64/liblz4.so.1 /usr/lib64/
155-
COPY --from=ubi-dep /usr/lib64/libcap.so.2 /usr/lib64/
156-
COPY --from=ubi-dep /usr/lib64/libgcrypt.so.20 /usr/lib64/
157-
COPY --from=ubi-dep /usr/lib64/libgpg-error.so.0 /usr/lib64/
158-
COPY --from=ubi-dep /usr/lib64/liblzma.so.5 /usr/lib64/
159-
# COPY --from=ubi-dep /usr/lib64/libblkid.so.1 /usr/lib64/
160-
161-
162-
# Copy the iofog-agent user and related configurations
163-
COPY --from=ubi-dep /etc/passwd /etc/passwd
164-
COPY --from=ubi-dep /etc/group /etc/group
165-
COPY --from=ubi-dep /etc/shadow /etc/shadow
169+
# Copy all dependencies from the staging stage in a single layer
170+
COPY --from=ubi-dep-staging /staging/ /
166171

167172

168173
ENV JAVA_HOME=/opt/java/openjdk
169174
ENV PATH="${JAVA_HOME}/bin:${PATH}"
170175
COPY --from=jre-build /javaruntime $JAVA_HOME
171176

172-
COPY --from=builder packaging/iofog-agent/usr ./usr
173-
COPY --from=builder packaging/iofog-agent/etc/systemd/system/iofog-agent.service /etc/systemd/system/iofog-agent.service
174-
COPY --from=builder packaging/iofog-agent/etc/bash_completion.d /etc/bash_completion.d/
175-
COPY --from=builder packaging/iofog-agent/etc/iofog-agent /etc/iofog-agent/
177+
# Copy all files from builder staging stage in a single layer
178+
COPY --from=builder-staging /staging/ /
176179

177180
RUN true && \
178181
mv /etc/iofog-agent/config_new.xml /etc/iofog-agent/config.xml && \

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ plugins {
55

66
allprojects {
77
group = 'org.eclipse'
8-
version = '3.5.5'
8+
version = '3.5.6'
99
}
1010

1111
subprojects {

iofog-agent-daemon/src/main/java/org/eclipse/iofog/process_manager/ContainerManager.java

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.eclipse.iofog.process_manager.ExecSessionCallback;
3030
import java.util.concurrent.CompletableFuture;
3131
import java.io.PipedInputStream;
32+
import java.io.PipedOutputStream;
3233

3334
/**
3435
* provides methods to manage Docker containers
@@ -71,15 +72,51 @@ private Registry getRegistry(Microservice microservice) throws AgentSystemExcept
7172

7273
/**
7374
* removes an existing {@link Container} and creates a new one
75+
* Improved flow: Pull image first while old container is still running (minimizes downtime),
76+
* then stop old container (releases ports), then create and start new container.
7477
*
7578
* @param withCleanUp if true then removes old image and volumes
7679
* @throws Exception exception
7780
*/
7881
private void updateContainer(Microservice microservice, boolean withCleanUp) throws Exception {
7982
LoggingService.logInfo(MODULE_NAME, "Start update container for microservice : " + microservice.getImageName());
8083
microservice.setUpdating(true);
84+
docker = DockerUtil.getInstance();
85+
86+
// Step 1: Pull new image while old container is still running
87+
// This keeps the service available during the slow image pull operation
88+
setMicroserviceStatus(microservice.getMicroserviceUuid(), MicroserviceState.PULLING);
89+
Registry registry = getRegistry(microservice);
90+
if (!registry.getUrl().equals("from_cache")) {
91+
try {
92+
docker.pullImage(microservice.getImageName(), microservice.getMicroserviceUuid(),
93+
microservice.getPlatform(), registry);
94+
StatusReporter.setProcessManagerStatus().setMicroservicesStatePercentage(
95+
microservice.getMicroserviceUuid(), Constants.PERCENTAGE_COMPLETION);
96+
LoggingService.logInfo(MODULE_NAME, "Successfully pulled image \"" + microservice.getImageName() + "\" while old container was running");
97+
} catch (Exception e) {
98+
LoggingService.logError(MODULE_NAME,
99+
"unable to pull \"" + microservice.getImageName() + "\" from registry. trying local cache",
100+
new AgentSystemException(e.getMessage(), e));
101+
// Continue with local cache if pull fails
102+
}
103+
}
104+
105+
// Verify image exists (either pulled or in cache)
106+
if (!docker.findLocalImage(microservice.getImageName())) {
107+
microservice.setUpdating(false);
108+
throw new NotFoundException("Image not found: " + microservice.getImageName() +
109+
". Pull failed and image not in local cache.");
110+
}
111+
112+
// Step 2: Now stop and remove old container (releases ports)
113+
// Downtime starts here, but it's brief compared to pull time
81114
removeContainerByMicroserviceUuid(microservice.getMicroserviceUuid(), withCleanUp);
82-
createContainer(microservice);
115+
116+
// Step 3: Create and start new container (can use same ports now)
117+
// Pass false to createContainer to skip pulling since we already pulled
118+
createContainer(microservice, false);
119+
83120
microservice.setUpdating(false);
84121
LoggingService.logDebug(MODULE_NAME, "Finished update container for microservice : " + microservice.getImageName());
85122
}
@@ -260,17 +297,19 @@ public void execute(ContainerTask task) throws Exception {
260297
case CREATE_EXEC:
261298
if (microserviceOptional.isPresent()) {
262299
ExecSessionCallback pmCallback = task.getCallback();
263-
// Get the stdin pipe from ProcessManager.ExecSessionCallback
300+
// Get both pipes from ProcessManager.ExecSessionCallback
264301
PipedInputStream stdinPipe = pmCallback.getStdinPipe();
265-
if (stdinPipe == null) {
266-
throw new AgentSystemException("Failed to get stdin pipe from callback", null);
302+
PipedOutputStream stdinOutputStream = pmCallback.getStdin();
303+
if (stdinPipe == null || stdinOutputStream == null) {
304+
throw new AgentSystemException("Failed to get stdin pipes from callback", null);
267305
}
268306

269307
// Create a new DockerUtil.ExecSessionCallback that forwards to the ProcessManager callback
270308
DockerUtil.ExecSessionCallback dockerCallback = docker.new ExecSessionCallback(
271309
"iofog_" + task.getMicroserviceUuid(), // Use a unique ID for the exec session
272310
30, // 30 minutes timeout
273-
stdinPipe
311+
stdinPipe,
312+
stdinOutputStream // Pass the output stream
274313
) {
275314
@Override
276315
public void onNext(Frame frame) {

iofog-agent-daemon/src/main/java/org/eclipse/iofog/process_manager/DockerUtil.java

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1390,24 +1390,20 @@ public class ExecSessionCallback extends ResultCallback.Adapter<Frame> {
13901390
private PipedInputStream ptyStdinPipe;
13911391
private long lastActivityTime;
13921392

1393-
public ExecSessionCallback(String execId, long inactivityTimeoutMinutes, PipedInputStream stdinPipe) {
1393+
public ExecSessionCallback(String execId, long inactivityTimeoutMinutes,
1394+
PipedInputStream stdinPipe, PipedOutputStream stdinOutputStream) {
13941395
this.execId = execId;
13951396
this.startTime = System.currentTimeMillis();
13961397
this.inactivityTimeoutMinutes = inactivityTimeoutMinutes;
13971398
this.lastActivityTime = startTime;
13981399

1399-
// Use the provided pipe instead of creating a new one
1400+
// Use the provided pipes instead of creating new ones
14001401
this.ptyStdinPipe = stdinPipe;
1402+
this.ptyStdin = stdinOutputStream; // Use the provided output stream
14011403

1402-
// Create the output stream connected to the input pipe
1403-
try {
1404-
this.ptyStdin = new PipedOutputStream(ptyStdinPipe);
1405-
LoggingService.logDebug(MODULE_NAME, "Created output stream for exec session: " + execId +
1406-
", ptyStdin=" + (ptyStdin != null) +
1407-
", ptyStdinPipe=" + (ptyStdinPipe != null));
1408-
} catch (IOException e) {
1409-
LoggingService.logError(MODULE_NAME, "Failed to create output stream for exec session: " + execId, e);
1410-
}
1404+
LoggingService.logDebug(MODULE_NAME, "Created exec session callback: " + execId +
1405+
", ptyStdin=" + (ptyStdin != null) +
1406+
", ptyStdinPipe=" + (ptyStdinPipe != null));
14111407
}
14121408

14131409
private void resetInactivityTimer() {
@@ -1450,8 +1446,8 @@ public void onComplete() {
14501446

14511447
@Override
14521448
public void close() throws IOException {
1453-
if (ptyStdinPipe != null) ptyStdinPipe.close();
1454-
if (ptyStdin != null) ptyStdin.close();
1449+
// Don't close pipes we don't own - ProcessManager owns them
1450+
// Only call super.close() to clean up the callback itself
14551451
super.close();
14561452
}
14571453

iofog-agent-daemon/src/main/java/org/eclipse/iofog/process_manager/ExecSessionCallback.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,4 +334,9 @@ public PipedInputStream getStdinPipe() {
334334
LoggingService.logDebug(MODULE_NAME, "Getting stdin pipe: " + (ptyStdinPipe != null));
335335
return ptyStdinPipe;
336336
}
337+
338+
public PipedOutputStream getStdin() {
339+
LoggingService.logDebug(MODULE_NAME, "Getting stdin output stream: " + (stdin != null));
340+
return (PipedOutputStream) stdin;
341+
}
337342
}

iofog-agent-daemon/src/test/java/org/eclipse/iofog/command_line/CommandLineActionTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public void setup() {
9393
.thenReturn(new HashMap<>())
9494
.thenThrow(new Exception("item not found or defined more than once"));
9595

96-
Mockito.when(CmdProperties.getVersion()).thenReturn("3.5.5");
96+
Mockito.when(CmdProperties.getVersion()).thenReturn("3.5.6");
9797
Mockito.when(CmdProperties.getVersionMessage()).thenReturn(version);
9898
Mockito.when(CmdProperties.getDeprovisionMessage()).thenReturn("Deprovisioning from controller ... %s");
9999
Mockito.when(CmdProperties.getProvisionMessage()).thenReturn("Provisioning with key \"%s\" ... Result: %s");
@@ -364,7 +364,7 @@ private static boolean isEqual(List list1, List list2) {
364364
"0.00 MB\\nSystem Available Memory : " +
365365
"0.00 MB\\nSystem Total CPU : 0.00 %";
366366

367-
private String version = "ioFog Agent 3.5.5 \n" +
367+
private String version = "ioFog Agent 3.5.6 \n" +
368368
"Copyright (c) 2023 Datasance Teknoloji A.S. \n" +
369369
"Eclipse ioFog is provided under the Eclipse Public License 2.0 (EPL-2.0) \n" +
370370
"https://www.eclipse.org/legal/epl-v20.html";

iofog-agent-daemon/src/test/java/org/eclipse/iofog/utils/CmdPropertiesTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public void tearDown() throws Exception {
4646

4747
//@Test
4848
//public void getVersionMessage() {
49-
// assertEquals("ioFog Agent 3.5.5 \nCopyright (c) 2023 Datasance Teknoloji A.S. \nEclipse ioFog is provided under the Eclipse Public License 2.0 (EPL-2.0) \nhttps://www.eclipse.org/legal/epl-v20.html",
49+
// assertEquals("ioFog Agent 3.5.6 \nCopyright (c) 2023 Datasance Teknoloji A.S. \nEclipse ioFog is provided under the Eclipse Public License 2.0 (EPL-2.0) \nhttps://www.eclipse.org/legal/epl-v20.html",
5050
// CmdProperties.getVersionMessage());
5151
//}
5252

0 commit comments

Comments
 (0)