Skip to content

Commit 52df504

Browse files
authored
Adds Skaffold to run skaffold. (#7)
1 parent c48e1e4 commit 52df504

13 files changed

Lines changed: 664 additions & 17 deletions

File tree

skaffold-gradle-plugin/build.gradle

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ configurations {
5757
dependencies {
5858
// These are copied over from skaffold-plugins-core and are necessary for the skaffold-plugins-core sourcesets.
5959
compile 'com.google.guava:guava:26.0-jre'
60+
compile 'org.slf4j:slf4j-api:1.7.25'
6061

6162
testCompile 'junit:junit:4.12'
6263
testCompile 'org.mockito:mockito-core:2.21.0'
@@ -93,6 +94,12 @@ tasks.withType(JavaCompile) {
9394
}
9495
}
9596

97+
// Fail build on javadoc warnings
98+
tasks.withType(Javadoc) {
99+
options.addBooleanOption('Xwerror', true)
100+
}
101+
assemble.dependsOn javadoc
102+
96103
tasks.withType(Test) {
97104
reports.html.setDestination file("${reporting.baseDir}/${name}")
98105
}

skaffold-maven-plugin/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@
5858
<version>26.0-jre</version>
5959
<scope>compile</scope>
6060
</dependency>
61+
<dependency>
62+
<groupId>org.slf4j</groupId>
63+
<artifactId>slf4j-api</artifactId>
64+
<version>1.7.25</version>
65+
</dependency>
6166
<!-- End dependencies from skaffold-plugins-core -->
6267

6368
<dependency>

skaffold-plugins-core/build.gradle

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ configurations {
3636
dependencies {
3737
// Make sure these are consistent with skaffold-maven-plugin and skaffold-gradle-plugin.
3838
compile 'com.google.guava:guava:26.0-jre'
39+
compile 'org.slf4j:slf4j-api:1.7.25'
3940

4041
testCompile 'junit:junit:4.12'
4142
testCompile 'org.mockito:mockito-core:2.21.0'
@@ -70,6 +71,12 @@ tasks.withType(JavaCompile) {
7071
}
7172
}
7273

74+
// Fail build on javadoc warnings
75+
tasks.withType(Javadoc) {
76+
options.addBooleanOption('Xwerror', true)
77+
}
78+
assemble.dependsOn javadoc
79+
7380
tasks.withType(Test) {
7481
reports.html.setDestination file("${reporting.baseDir}/${name}")
7582
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright 2018 Google LLC. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
17+
package com.google.cloud.tools.skaffold.downloader;
18+
19+
import java.io.IOException;
20+
import java.nio.file.Files;
21+
import java.nio.file.Path;
22+
import java.nio.file.StandardCopyOption;
23+
import org.junit.Assert;
24+
import org.junit.Before;
25+
import org.junit.Rule;
26+
import org.junit.Test;
27+
import org.junit.rules.TemporaryFolder;
28+
29+
/** Integration tests for {@link CachedSkaffoldManager}. */
30+
public class CachedSkaffoldManagerIntegrationTest {
31+
32+
@Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
33+
34+
private Path fakeSkaffoldLocation;
35+
private Path fakeDigestLocation;
36+
private Path latestDigestLocation;
37+
38+
@Before
39+
public void setUp() throws IOException {
40+
Path temporaryDirectory = temporaryFolder.newFolder().toPath();
41+
fakeSkaffoldLocation = temporaryDirectory.resolve("skaffold");
42+
fakeDigestLocation = temporaryDirectory.resolve("digest");
43+
latestDigestLocation = temporaryDirectory.resolve("latestdigest");
44+
}
45+
46+
@Test
47+
public void testCheckIsLatest() throws IOException {
48+
Assert.assertFalse(
49+
CachedSkaffoldManager.checkIsLatest(fakeSkaffoldLocation, fakeDigestLocation));
50+
51+
// Creating the skaffold executable file is not enough.
52+
Files.createFile(fakeSkaffoldLocation);
53+
Assert.assertFalse(
54+
CachedSkaffoldManager.checkIsLatest(fakeSkaffoldLocation, fakeDigestLocation));
55+
56+
// Creating the digest file is not enough.
57+
Files.createFile(fakeDigestLocation);
58+
Assert.assertFalse(
59+
CachedSkaffoldManager.checkIsLatest(fakeSkaffoldLocation, fakeDigestLocation));
60+
61+
// Populating the digest file with an arbitrary byte is not enough.
62+
byte[] arbitraryByteArray = new byte[] {0x10};
63+
Files.write(fakeDigestLocation, arbitraryByteArray);
64+
Assert.assertFalse(
65+
CachedSkaffoldManager.checkIsLatest(fakeSkaffoldLocation, fakeDigestLocation));
66+
67+
// Downloading the latest digest is enough.
68+
SkaffoldDownloader.downloadLatestDigest(latestDigestLocation);
69+
Files.move(latestDigestLocation, fakeDigestLocation, StandardCopyOption.REPLACE_EXISTING);
70+
Assert.assertTrue(
71+
CachedSkaffoldManager.checkIsLatest(fakeSkaffoldLocation, fakeDigestLocation));
72+
}
73+
74+
@Test
75+
public void testUpdateToLatest() throws IOException, InterruptedException {
76+
Assert.assertFalse(
77+
CachedSkaffoldManager.checkIsLatest(fakeSkaffoldLocation, fakeDigestLocation));
78+
CachedSkaffoldManager.updateToLatest(fakeSkaffoldLocation, fakeDigestLocation);
79+
Assert.assertTrue(
80+
CachedSkaffoldManager.checkIsLatest(fakeSkaffoldLocation, fakeDigestLocation));
81+
Assert.assertEquals(0, new ProcessBuilder(fakeSkaffoldLocation.toString()).start().waitFor());
82+
}
83+
}
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
/*
2+
* Copyright 2018 Google LLC. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
17+
package com.google.cloud.tools.skaffold.command;
18+
19+
import com.google.common.annotations.VisibleForTesting;
20+
import com.google.common.io.ByteStreams;
21+
import com.google.common.util.concurrent.ListenableFuture;
22+
import com.google.common.util.concurrent.ListeningExecutorService;
23+
import com.google.common.util.concurrent.MoreExecutors;
24+
import java.io.IOException;
25+
import java.io.InputStream;
26+
import java.io.OutputStream;
27+
import java.nio.file.Path;
28+
import java.util.ArrayList;
29+
import java.util.Arrays;
30+
import java.util.List;
31+
import java.util.concurrent.Callable;
32+
import java.util.concurrent.ExecutionException;
33+
import java.util.concurrent.ExecutorService;
34+
import java.util.concurrent.Executors;
35+
import java.util.function.Function;
36+
import java.util.function.Supplier;
37+
import javax.annotation.Nullable;
38+
39+
/** Runs {@code skaffold} commands. */
40+
public class Skaffold {
41+
42+
@VisibleForTesting
43+
static Supplier<ExecutorService> executorServiceSupplier = Executors::newCachedThreadPool;
44+
45+
/**
46+
* Initializes {@link Skaffold} with a custom path to the {@code skaffold} executable.
47+
*
48+
* @param executablePath the path to {@code skaffold}
49+
* @return a new {@link Skaffold}
50+
*/
51+
public static Skaffold atPath(Path executablePath) {
52+
return new Skaffold(getListeningExecutorService(), executablePath.toString());
53+
}
54+
55+
/**
56+
* Sets the {@link ExecutorService} to handle the {@code skaffold} process. Uses {@link
57+
* Executors#newCachedThreadPool} by default.
58+
*
59+
* @param executorService the executor
60+
*/
61+
public static void setExecutorService(ExecutorService executorService) {
62+
Skaffold.executorServiceSupplier = () -> executorService;
63+
}
64+
65+
@VisibleForTesting
66+
static ListeningExecutorService getListeningExecutorService() {
67+
return MoreExecutors.listeningDecorator(Skaffold.executorServiceSupplier.get());
68+
}
69+
70+
private static Callable<Void> redirect(InputStream inputStream, OutputStream outputStream) {
71+
return () -> {
72+
ByteStreams.copy(inputStream, outputStream);
73+
return null;
74+
};
75+
}
76+
77+
private final ListeningExecutorService listeningExecutorService;
78+
private final List<String> initialTokens;
79+
80+
private Function<List<String>, ProcessBuilder> processBuilderFactory = ProcessBuilder::new;
81+
@Nullable private Path skaffoldYaml;
82+
@Nullable private String profile;
83+
@Nullable private InputStream stdinInputStream;
84+
@Nullable private OutputStream stdoutOutputStream;
85+
@Nullable private OutputStream stderrOutputStream;
86+
87+
@VisibleForTesting
88+
Skaffold(ListeningExecutorService listeningExecutorService, String... initialTokens) {
89+
this.listeningExecutorService = listeningExecutorService;
90+
this.initialTokens = Arrays.asList(initialTokens);
91+
}
92+
93+
public Skaffold setSkaffoldYaml(Path skaffoldYaml) {
94+
this.skaffoldYaml = skaffoldYaml;
95+
return this;
96+
}
97+
98+
public Skaffold setProfile(String profile) {
99+
this.profile = profile;
100+
return this;
101+
}
102+
103+
/**
104+
* Sets the {@link InputStream} to provide to {@code skaffold} as the stdin.
105+
*
106+
* @param stdinInputStream provides the stdin
107+
* @return this
108+
*/
109+
public Skaffold redirectToStdin(InputStream stdinInputStream) {
110+
this.stdinInputStream = stdinInputStream;
111+
return this;
112+
}
113+
114+
/**
115+
* Sets the {@link OutputStream} to receive the stdout.
116+
*
117+
* @param stdoutOutputStream receives the stdout
118+
* @return this
119+
*/
120+
public Skaffold redirectStdoutTo(OutputStream stdoutOutputStream) {
121+
this.stdoutOutputStream = stdoutOutputStream;
122+
return this;
123+
}
124+
125+
/**
126+
* Sets the {@link OutputStream} to receive the stderr.
127+
*
128+
* @param stderrOutputStream receives the stderr
129+
* @return this
130+
*/
131+
public Skaffold redirectStderrTo(OutputStream stderrOutputStream) {
132+
this.stderrOutputStream = stderrOutputStream;
133+
return this;
134+
}
135+
136+
/**
137+
* Calls {@code skaffold deploy}.
138+
*
139+
* @return the process exit code
140+
* @throws ExecutionException if an error occurred while handling the process I/O
141+
* @throws InterruptedException if the process was interrupted during execution
142+
* @throws IOException if an I/O exception occurred
143+
*/
144+
public int deploy() throws InterruptedException, IOException, ExecutionException {
145+
List<String> command = new ArrayList<>();
146+
command.addAll(initialTokens);
147+
command.addAll(getFlags());
148+
command.add("deploy");
149+
150+
Process skaffoldProcess = processBuilderFactory.apply(command).start();
151+
152+
if (stdinInputStream != null) {
153+
try (OutputStream stdin = skaffoldProcess.getOutputStream()) {
154+
ByteStreams.copy(stdinInputStream, stdin);
155+
}
156+
}
157+
158+
List<ListenableFuture<Void>> listenableFutures = new ArrayList<>();
159+
160+
try (InputStream stdout = skaffoldProcess.getInputStream();
161+
InputStream stderr = skaffoldProcess.getErrorStream()) {
162+
if (stdoutOutputStream != null) {
163+
listenableFutures.add(
164+
listeningExecutorService.submit(redirect(stdout, stdoutOutputStream)));
165+
}
166+
if (stderrOutputStream != null) {
167+
listenableFutures.add(
168+
listeningExecutorService.submit(redirect(stderr, stderrOutputStream)));
169+
}
170+
171+
for (ListenableFuture<Void> listenableFuture : listenableFutures) {
172+
listenableFuture.get();
173+
}
174+
}
175+
176+
return skaffoldProcess.waitFor();
177+
}
178+
179+
@VisibleForTesting
180+
Skaffold setProcessBuilderFactory(Function<List<String>, ProcessBuilder> processBuilderFactory) {
181+
this.processBuilderFactory = processBuilderFactory;
182+
return this;
183+
}
184+
185+
private List<String> getFlags() {
186+
List<String> flags = new ArrayList<>();
187+
if (skaffoldYaml != null) {
188+
flags.add("--filename");
189+
flags.add(skaffoldYaml.toString());
190+
}
191+
if (profile != null) {
192+
flags.add("--profile");
193+
flags.add(profile);
194+
}
195+
return flags;
196+
}
197+
}

0 commit comments

Comments
 (0)