Skip to content
This repository was archived by the owner on Apr 27, 2026. It is now read-only.

Commit ba435b5

Browse files
honnixclaude
andcommitted
chore: migrate to flyte-sandbox-bundled for integration tests
The flyte-sandbox v1.9.1 Helm charts are broken upstream. Migrate to flyte-sandbox-bundled which uses kustomize manifests baked into the image. - Switch image to flyte-sandbox-bundled - Use containerd (ctr) instead of Docker to import jflyte image - Update ports: 30081->30080 (envoy proxy), 30084->30002 (minio) - Change wait strategy from log message to HTTP health check - Bump testcontainers 1.19.1->1.21.4 and jna 5.12.1->5.13.0 - Add docker-java.properties with api.version=1.44 for Docker 29+ - Add cgroupns=host and host.docker.internal extra host for Linux CI - Patch FLYTE_PLATFORM_INSECURE configmap value for correct injection - Fix SerializeJavaIT path for container working directory Signed-off-by: Hongxin Liang <honnix@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 6cc7dd3 commit ba435b5

7 files changed

Lines changed: 108 additions & 22 deletions

File tree

integration-tests/src/test/java/org/flyte/SerializeJavaIT.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,15 @@ class SerializeJavaIT extends Fixtures {
3636
@Test
3737
void testSerializeWorkflows() {
3838
try {
39-
File current = new File("target/protos");
40-
File tempDir = managed.resolve(current.getAbsolutePath()).toFile();
41-
boolean created = tempDir.mkdir();
42-
if (!created) {
39+
// Path must be relative to project root since jflyte runs in a container
40+
// with the project root as working directory
41+
String serializePath = "integration-tests/target/protos";
42+
File tempDir = new File("../" + serializePath);
43+
if (!tempDir.mkdirs() && !tempDir.exists()) {
4344
throw new IOException("Unable to create path");
4445
}
4546

46-
CLIENT.serializeWorkflows(CLASSPATH, tempDir.getPath());
47+
CLIENT.serializeWorkflows(CLASSPATH, serializePath);
4748

4849
boolean hasFibonacciWorkflow =
4950
Stream.of(tempDir.list())

integration-tests/src/test/java/org/flyte/utils/FlyteSandboxClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public static FlyteSandboxClient create() {
4747
String version = String.valueOf(System.currentTimeMillis());
4848

4949
String address = FlyteSandboxContainer.INSTANCE.getHost();
50-
int port = FlyteSandboxContainer.INSTANCE.getMappedPort(30081);
50+
int port = FlyteSandboxContainer.INSTANCE.getMappedPort(30080);
5151

5252
ManagedChannel channel =
5353
ManagedChannelBuilder.forTarget(address + ":" + port).usePlaintext().enableRetry().build();

integration-tests/src/test/java/org/flyte/utils/FlyteSandboxContainer.java

Lines changed: 90 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.nio.file.Files;
2626
import java.nio.file.Paths;
2727
import java.time.Duration;
28+
import java.util.Map;
2829
import org.apache.commons.compress.utils.IOUtils;
2930
import org.testcontainers.DockerClientFactory;
3031
import org.testcontainers.containers.BindMode;
@@ -33,7 +34,8 @@
3334

3435
public class FlyteSandboxContainer extends GenericContainer<FlyteSandboxContainer> {
3536

36-
public static final String IMAGE_NAME = "ghcr.io/flyteorg/flyte-sandbox:v1.9.1";
37+
public static final String IMAGE_NAME =
38+
"ghcr.io/flyteorg/flyte-sandbox-bundled:sha-f3ab1b7480bad4072f7ecb695660fdf47032a6c4";
3739

3840
public static final FlyteSandboxContainer INSTANCE =
3941
new FlyteSandboxContainer()
@@ -48,13 +50,14 @@ public class FlyteSandboxContainer extends GenericContainer<FlyteSandboxContaine
4850
private static void startContainer() {
4951
INSTANCE.start();
5052

51-
// Flyte sandbox uses Docker in Docker, we have to copy jflyte container into inner Docker
53+
// Flyte sandbox-bundled uses k3s with containerd (not Docker-in-Docker),
54+
// so we use ctr to import the jflyte image into the inner containerd
5255
// otherwise, flytepropeller can't use the right version for pod execution
5356

5457
DockerClient client = DockerClientFactory.instance().client();
5558
try (InputStream imageInputStream = client.saveImageCmd(JFlyteContainer.IMAGE_NAME).exec()) {
5659

57-
try (OutputStream outputStream = Files.newOutputStream(Paths.get("target/jflyte.tar.gz"))) {
60+
try (OutputStream outputStream = Files.newOutputStream(Paths.get("target/jflyte.tar"))) {
5861
IOUtils.copy(imageInputStream, outputStream);
5962
}
6063

@@ -64,7 +67,14 @@ private static void startContainer() {
6467

6568
ExecResult execResult =
6669
INSTANCE.execInContainer(
67-
"docker", "load", "-i", "integration-tests/target/jflyte.tar.gz");
70+
"ctr",
71+
"--address",
72+
"/run/k3s/containerd/containerd.sock",
73+
"--namespace",
74+
"k8s.io",
75+
"images",
76+
"import",
77+
"integration-tests/target/jflyte.tar");
6878

6979
if (execResult.getExitCode() != 0) {
7080
throw new RuntimeException(execResult.getStderr() + " " + execResult.getStdout());
@@ -75,6 +85,68 @@ private static void startContainer() {
7585
Thread.currentThread().interrupt();
7686
throw new RuntimeException("failed to load jflyte image", e);
7787
}
88+
89+
// The sandbox manifest has FLYTE_PLATFORM_INSECURE as a YAML boolean (true)
90+
// which may not be injected correctly as a string env var. Patch the configmap
91+
// to use a quoted string value and restart the flyte-sandbox pod.
92+
patchInsecureEnvVar();
93+
}
94+
95+
private static void patchInsecureEnvVar() {
96+
try {
97+
ExecResult patchResult =
98+
INSTANCE.execInContainer(
99+
"kubectl",
100+
"get",
101+
"configmap",
102+
"-n",
103+
"flyte",
104+
"flyte-sandbox-config",
105+
"-o",
106+
"jsonpath={.data.100-inline-config\\.yaml}");
107+
108+
if (patchResult.getExitCode() != 0) {
109+
throw new RuntimeException("Failed to read configmap: " + patchResult.getStderr());
110+
}
111+
112+
String config = patchResult.getStdout();
113+
if (config.contains("FLYTE_PLATFORM_INSECURE: true")) {
114+
String patched =
115+
config.replace("FLYTE_PLATFORM_INSECURE: true", "FLYTE_PLATFORM_INSECURE: 'true'");
116+
117+
ExecResult applyResult =
118+
INSTANCE.execInContainer(
119+
"sh",
120+
"-c",
121+
"kubectl create configmap flyte-sandbox-config -n flyte"
122+
+ " --from-literal='100-inline-config.yaml="
123+
+ patched.replace("'", "'\"'\"'")
124+
+ "' --dry-run=client -o yaml | kubectl apply -f -");
125+
126+
if (applyResult.getExitCode() != 0) {
127+
throw new RuntimeException("Failed to patch configmap: " + applyResult.getStderr());
128+
}
129+
130+
// Restart flyte-sandbox pod to pick up the new config
131+
INSTANCE.execInContainer(
132+
"kubectl", "rollout", "restart", "deployment/flyte-sandbox", "-n", "flyte");
133+
134+
// Wait for the rollout to complete
135+
INSTANCE.execInContainer(
136+
"kubectl",
137+
"rollout",
138+
"status",
139+
"deployment/flyte-sandbox",
140+
"-n",
141+
"flyte",
142+
"--timeout=120s");
143+
}
144+
} catch (IOException e) {
145+
throw new UncheckedIOException("failed to patch insecure env var", e);
146+
} catch (InterruptedException e) {
147+
Thread.currentThread().interrupt();
148+
throw new RuntimeException("failed to patch insecure env var", e);
149+
}
78150
}
79151

80152
FlyteSandboxContainer() {
@@ -84,23 +156,31 @@ private static void startContainer() {
84156

85157
withPrivilegedMode(true);
86158

159+
// k3s requires tmpfs mounts and cgroupns=host on Linux (CI)
160+
withTmpFs(Map.of("/run", "", "/var/run", ""));
161+
withCreateContainerCmdModifier(
162+
cmd ->
163+
cmd.getHostConfig()
164+
.withCgroupnsMode("host")
165+
// host.docker.internal is needed by the bootstrap to template manifests;
166+
// Docker Desktop adds it automatically, but Linux Docker does not
167+
.withExtraHosts("host.docker.internal:host-gateway"));
168+
87169
withNetworkAliases("flyte");
88170

89171
withWorkingDirectory(workingDir);
90172
withFileSystemBind(workingDir, workingDir, BindMode.READ_ONLY);
91173

92174
withExposedPorts(
93-
30081, // flyteadmin
94-
30082, // k8s dashboard
95-
30084, // minio
96-
30086 // k8s api
175+
30080, // envoy proxy (flyteadmin http + grpc)
176+
30002 // minio
97177
);
98178

99179
withReuse(true);
100180

101181
withNetwork(FlyteSandboxNetwork.INSTANCE);
102182

103-
waitingFor(Wait.forLogMessage(".*Flyte is ready!.*", 1));
183+
waitingFor(Wait.forHttp("/healthcheck").forPort(30080));
104184
withStartupTimeout(Duration.ofMinutes(5));
105185
}
106186

@@ -110,10 +190,8 @@ public void start() {
110190

111191
logger().info("Flyte is ready!");
112192

113-
String consoleUri = String.format("http://%s:%d/console", getHost(), getMappedPort(30081));
114-
String k8sUri = String.format("http://%s:%d", getHost(), getMappedPort(30082));
193+
String consoleUri = String.format("http://%s:%d/console", getHost(), getMappedPort(30080));
115194

116195
logger().info("Flyte UI is available at " + consoleUri);
117-
logger().info("K8s dashboard is available at " + k8sUri);
118196
}
119197
}

integration-tests/src/test/java/org/flyte/utils/JFlyteContainer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ class JFlyteContainer extends GenericContainer<JFlyteContainer> {
3030
static final String IMAGE_NAME;
3131
static final Map<String, String> envVars =
3232
ImmutableMap.<String, String>builder()
33-
.put("FLYTE_PLATFORM_URL", "flyte:30081")
33+
.put("FLYTE_PLATFORM_URL", "flyte:30080")
3434
.put("FLYTE_PLATFORM_INSECURE", "True")
35-
.put("FLYTE_AWS_ENDPOINT", "http://flyte:30084")
35+
.put("FLYTE_AWS_ENDPOINT", "http://flyte:30002")
3636
.put("FLYTE_AWS_ACCESS_KEY_ID", "minio")
3737
.put("FLYTE_AWS_SECRET_ACCESS_KEY", "miniostorage")
3838
.put("FLYTE_STAGING_LOCATION", "s3://my-s3-bucket")
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
api.version=1.44

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@
379379
<dependency>
380380
<groupId>net.java.dev.jna</groupId>
381381
<artifactId>jna</artifactId>
382-
<version>5.12.1</version>
382+
<version>5.13.0</version>
383383
</dependency>
384384
<dependency>
385385
<groupId>com.fasterxml.jackson</groupId>
@@ -405,7 +405,7 @@
405405
<dependency>
406406
<groupId>org.testcontainers</groupId>
407407
<artifactId>testcontainers-bom</artifactId>
408-
<version>1.19.1</version>
408+
<version>1.21.4</version>
409409
<type>pom</type>
410410
<scope>import</scope>
411411
</dependency>

spotbugs-exclude.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,11 @@
6464
<Bug pattern="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE"/>
6565
</Match>
6666

67+
<!-- HostConfig is always set by Testcontainers before the modifier runs -->
68+
<Match>
69+
<Class name="org.flyte.utils.FlyteSandboxContainer"/>
70+
<Bug pattern="NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"/>
71+
</Match>
72+
6773
</FindBugsFilter>
6874

0 commit comments

Comments
 (0)