From 0687d7c71bcd75de684fc9500c8c0a2a54341522 Mon Sep 17 00:00:00 2001 From: Steve Hawkins Date: Tue, 5 Jul 2022 12:52:52 -0400 Subject: [PATCH] fix fabric8io#4234: fixing the skip method for base64 inputstream --- CHANGELOG.md | 1 + .../client/utils/internal/Base64.java | 48 +++++------------ .../client/utils/internal/Base64Test.java | 37 +++++++++++++ .../java/io/fabric8/kubernetes/PodIT.java | 53 +++++++++++++++++++ 4 files changed, 103 insertions(+), 36 deletions(-) create mode 100644 kubernetes-client/src/test/java/io/fabric8/kubernetes/client/utils/internal/Base64Test.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 38f811776a2..487160dc602 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Fix #2811: Approve/Reject CSR not supported in v1beta1 CertificateSigningRequest API * Fix #4216: Update metadata when `replaceStatus()` is called * Fix #4217: patchStatus doesn't increment metadata.generation field in Kubernetes Mock Server (CRUD) +* Fix #4234: corrected the skip method for base64 inputstream #### Improvements * Fix #3227 : Move `config.openshift.io` apiGroup resources out of `openshift-model/` diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/internal/Base64.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/internal/Base64.java index c39543e2169..a218e50911d 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/internal/Base64.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/internal/Base64.java @@ -17,6 +17,9 @@ import io.fabric8.kubernetes.client.KubernetesClientException; +import java.io.IOException; + +//@formatter:off /** *

Encodes and decodes to and from Base64 notation.

*

Homepage: http://iharder.net/base64.

@@ -1329,7 +1332,7 @@ public static byte[] decode( String s, int options ) throws java.io.IOException * @see Base64 * @since 1.3 */ - public static class InputStream extends java.io.FilterInputStream { + public static class InputStream extends java.io.InputStream { private boolean encode; // Encoding or decoding private int position; // Current position in the buffer @@ -1340,6 +1343,7 @@ public static class InputStream extends java.io.FilterInputStream { private boolean breakLines; // Break lines at less than 80 characters private int options; // Record options used to create the stream. private byte[] decodabet; // Local copies to avoid extra method calls + private java.io.InputStream in; /** @@ -1375,7 +1379,7 @@ public InputStream( java.io.InputStream in ) { */ public InputStream( java.io.InputStream in, int options ) { - super( in ); + this.in = in; this.options = options; // Record for later this.breakLines = (options & DO_BREAK_LINES) > 0; this.encode = (options & ENCODE) > 0; @@ -1385,6 +1389,11 @@ public InputStream( java.io.InputStream in, int options ) { this.lineLength = 0; this.decodabet = getDecodabet(options); } // end constructor + + @Override + public void close() throws IOException { + in.close(); + } /** * Reads enough of the input stream to convert @@ -1489,40 +1498,6 @@ else if( i == 0 ){ } // end else } // end read - - /** - * Calls {@link #read()} repeatedly until the end of stream - * is reached or len bytes are read. - * Returns number of bytes read into array or -1 if - * end of stream is encountered. - * - * @param dest array to hold values - * @param off offset for array - * @param len max number of bytes to read into array - * @return bytes read into array or -1 if end of stream is encountered. - * @since 1.3 - */ - @Override - public int read( byte[] dest, int off, int len ) - throws java.io.IOException { - int i; - int b; - for( i = 0; i < len; i++ ) { - b = read(); - - if( b >= 0 ) { - dest[off + i] = (byte) b; - } - else if( i == 0 ) { - return -1; - } - else { - break; // Out of 'for' loop - } // Out of 'for' loop - } // end for: each byte read - return i; - } // end read - } // end inner class InputStream @@ -1756,3 +1731,4 @@ public void resumeEncoding() { } // end class Base64 +//@formatter:on diff --git a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/utils/internal/Base64Test.java b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/utils/internal/Base64Test.java new file mode 100644 index 00000000000..f396667355c --- /dev/null +++ b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/utils/internal/Base64Test.java @@ -0,0 +1,37 @@ +/** + * Copyright (C) 2015 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.fabric8.kubernetes.client.utils.internal; + +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class Base64Test { + + @Test + void testSkip() throws IOException { + Base64.InputStream is = new Base64.InputStream( + new ByteArrayInputStream(Base64.encodeBytesToBytes("hello".getBytes(StandardCharsets.UTF_8)))); + assertEquals(104, is.read()); + assertEquals(3, is.skip(3)); + assertEquals(111, is.read()); + assertEquals(-1, is.read()); + } +} diff --git a/kubernetes-itests/src/test/java/io/fabric8/kubernetes/PodIT.java b/kubernetes-itests/src/test/java/io/fabric8/kubernetes/PodIT.java index db29edf993e..79dc612f99b 100644 --- a/kubernetes-itests/src/test/java/io/fabric8/kubernetes/PodIT.java +++ b/kubernetes-itests/src/test/java/io/fabric8/kubernetes/PodIT.java @@ -32,6 +32,8 @@ import io.fabric8.kubernetes.client.dsl.ExecWatch; import io.fabric8.kubernetes.client.dsl.PodResource; import io.fabric8.kubernetes.client.readiness.Readiness; +import io.fabric8.kubernetes.client.utils.InputStreamPumper; +import org.awaitility.Awaitility; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,6 +51,7 @@ import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; @@ -215,6 +218,43 @@ public void onExit(int code, Status status) { assertNotNull(out.toString()); } + @Test + void execExitCode() throws Exception { + client.pods().withName("pod-standard").waitUntilReady(POD_READY_WAIT_IN_SECONDS, TimeUnit.SECONDS); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ExecWatch watch = client.pods().withName("pod-standard") + .writingOutput(out) + .exec("sh", "-c", "echo 'hello world!'"); + assertEquals(0, watch.exitCode().join()); + assertNotNull("hello world!", out.toString()); + } + + @Test + void execInteractiveShell() throws Exception { + client.pods().withName("pod-standard").waitUntilReady(POD_READY_WAIT_IN_SECONDS, TimeUnit.SECONDS); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ExecWatch watch = client.pods().withName("pod-standard") + .redirectingInput() + .redirectingOutput() + .redirectingError() + .withTTY() + .exec("sh", "-i"); + + InputStreamPumper.pump(watch.getOutput(), baos::write, Executors.newSingleThreadExecutor()); + + watch.getInput().write("whoami\n".getBytes(StandardCharsets.UTF_8)); + watch.getInput().flush(); + + Awaitility.await().atMost(30, TimeUnit.SECONDS).until(() -> { + return new String(baos.toByteArray(), StandardCharsets.UTF_8).contains("root"); + }); + + watch.close(); + + // no error is expected + assertEquals(-1, watch.getError().read()); + } + @Test void readFile() throws IOException { client.pods().withName("pod-standard").waitUntilReady(POD_READY_WAIT_IN_SECONDS, TimeUnit.SECONDS); @@ -285,6 +325,19 @@ void uploadDir() throws IOException { } } + @Test + void copyDir() throws IOException { + client.pods().withName("pod-standard").waitUntilReady(POD_READY_WAIT_IN_SECONDS, TimeUnit.SECONDS); + + final Path tmpDir = Files.createTempDirectory("copyFile"); + + PodResource podResource = client.pods().withName("pod-standard"); + podResource.dir("/etc").copy(tmpDir); + + Path msg = tmpDir.resolve("/etc/hosts"); + assertTrue(Files.exists(msg)); + } + @Test void copyFile() throws IOException { client.pods().withName("pod-standard").waitUntilReady(POD_READY_WAIT_IN_SECONDS, TimeUnit.SECONDS);