Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor configuration build/push/tar build steps to reduce duplicate code #269

Merged
merged 7 commits into from
May 14, 2018

Conversation

TadCordle
Copy link
Contributor

Part of #262 (refactors BuildContainerConfigurationStep, BuildTarballLoadDockerStep, and PushImageStep).

Fixes #263 (adds ability to add blobs to TarStreamBuilder without temp files).

TadCordle added 2 commits May 11, 2018 14:56
…lLoadDockerSteps, and PushImageStep; allow adding blobs to TarStreamBuilder
@@ -32,7 +30,7 @@
import java.util.concurrent.Future;

/** Builds the container configuration. */
class BuildContainerConfigurationStep implements Callable<ListenableFuture<Blob>> {
class BuildContainerConfigurationStep implements Callable<ListenableFuture<Image>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BuildImageStep now?

pushBaseImageLayerFuturesFuture, pushContainerConfigurationFutureFuture)
pushBaseImageLayerFuturesFuture,
pushContainerConfigurationFutureFuture,
buildContainerConfigurationFutureFuture)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dependency on buildContainerConfigurationFutureFuture shouldn't be necessary since pushContainerConfigurationFutureFuture already depends on it.

@@ -61,6 +68,17 @@ public void addEntry(TarArchiveEntry entry) {
entries.add(entry);
}

/** Adds a blob to the archive. */
public void addEntry(Blob blob, String name) throws IOException {
// TODO: Efficiency? We're writing blob contents twice (once here to get entry size, once when
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think you could do it by creating a class that encompasses either a file or a blob and a name, and it has a method to write it to a TarArchiveOutputStream with the right TarArchiveEntry and contents.

@@ -29,8 +29,8 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/** Builds the container configuration. */
class BuildContainerConfigurationStep implements Callable<ListenableFuture<Image>> {
/** Builds the container image. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually the {@link Image} class, which is a model for the container image.

@@ -87,18 +87,19 @@ private Void afterPushBaseImageLayerFuturesFuture()
DockerLoadManifestTemplate manifestTemplate = new DockerLoadManifestTemplate();
for (Layer layer : image.getLayers()) {
String layerName = layer.getBlobDescriptor().getDigest() + ".tar.gz";
tarStreamBuilder.addEntry(layer.getBlob(), layerName);
tarStreamBuilder.addEntry(Blobs.writeToByteArray(layer.getBlob()), layerName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use the cached content file so that we're not storing the files in memory.

// TODO: Efficiency? We're writing blob contents twice (once here to get entry size, once when
// writing to TarArchiveOutputStream).
byte[] bytes = Blobs.writeToByteArray(blob);
public void addEntry(byte[] contents, String name) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we generally want to avoid using a raw byte[], so String would prob be better here.

@@ -145,10 +144,10 @@ public void run()

timer2.lap("Setting up build container configuration");
// Builds the container configuration.
ListenableFuture<ListenableFuture<Blob>> buildContainerConfigurationFutureFuture =
ListenableFuture<ListenableFuture<Image>> buildContainerConfigurationFutureFuture =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/buildContainerConfigurationFutureFuture/buildImageFutureFuture

private final BuildConfiguration buildConfiguration;
private final ListeningExecutorService listeningExecutorService;
private final ListenableFuture<List<ListenableFuture<CachedLayer>>>
pullBaseImageLayerFuturesFuture;
private final List<ListenableFuture<CachedLayer>> buildApplicationLayerFutures;
private final ListenableFuture<ListenableFuture<Blob>> buildConfigurationFutureFuture;
private final ListenableFuture<ListenableFuture<Image>> buildConfigurationFutureFuture;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buildImageFutureFuture


BuildTarballAndLoadDockerStep(
BuildConfiguration buildConfiguration,
ListeningExecutorService listeningExecutorService,
ListenableFuture<List<ListenableFuture<CachedLayer>>> pullBaseImageLayerFuturesFuture,
List<ListenableFuture<CachedLayer>> buildApplicationLayerFutures,
ListenableFuture<ListenableFuture<Blob>> buildConfigurationFutureFuture) {
ListenableFuture<ListenableFuture<Image>> buildConfigurationFutureFuture) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buildImageFutureFuture

@@ -124,10 +123,10 @@ public void run()

timer2.lap("Setting up build container configuration");
// Builds the container configuration.
ListenableFuture<ListenableFuture<Blob>> buildContainerConfigurationFutureFuture =
ListenableFuture<ListenableFuture<Image>> buildContainerConfigurationFutureFuture =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buildImageFutureFuture

DockerLoadManifestTemplate manifestTemplate = new DockerLoadManifestTemplate();

for (Layer layer : image.getLayers()) {
Path cachedFile = ((CachedLayer) layer).getContentFile();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we should actually change Image to only hold and return CachedLayers so that we don't have to do the cast here, but that change could prob go in a TODO/separate issue/PR.

/** Writes each entry in the filesystem to the tarball archive stream. */
private static void writeEntriesAsTarArchive(
List<TarArchiveEntry> entries, OutputStream tarByteStream) throws IOException {
/** Map of TarArchiveEntries and their corresponding content as output stream consumers. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe phrase like:

Maps from {@link TarArchiveEntry} to function that outputs the entry onto a {@link TarArchiveOutputStream}. The order of the entries is the order they belong in the tarball.

private static void writeEntriesAsTarArchive(
List<TarArchiveEntry> entries, OutputStream tarByteStream) throws IOException {
/** Map of TarArchiveEntries and their corresponding content as output stream consumers. */
private final LinkedHashMap<TarArchiveEntry, TarArchiveOutputStreamConsumer> blobContents =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

archiveMap?

tarArchiveOutputStream -> {
if (entry.isFile()) {
try (InputStream contentStream =
new BufferedInputStream(new FileInputStream(entry.getFile()))) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Files.newInputStream

}

/** Adds a blob to the archive. */
public void addEntry(String contents, String name) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should add a test for this case.

tarStreamBuilder.addEntry(
new TarArchiveEntry(layerFile.toFile(), layerFile.getFileName().toString()));
}
DockerLoadManifestTemplate manifestTemplate = new DockerLoadManifestTemplate();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prob make a TODO/file an issue to refactor this tar building to a separate class specifically for building a Docker image tar so that it is unit-testable. Generally, these async steps should be kept as minimal as possible.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like #261 is this.

public void testToBlob_tarArchiveEntries() throws IOException {
setUpWithTarEntries();

Blob blob = testTarStreamBuilder.toBlob();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like all these verification methods are the same for each test - can put right into verifyTarArchive.

}

/** Creates a TarStreamBuilder using Strings. */
private void setUpWithStrings() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more with both files and strings would be good.

Copy link
Contributor

@coollog coollog left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to go! 👍

}

/** Creates a compressed blob from the TarStreamBuilder and verifies it. */
private void verifyBlobWithCompression() throws IOException {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine since it's just a test helper, but since theres only two line difference between these functions, it could just be one function with a parameter "compress". Then, like line 140 could be blob.writeTo(compress?new GZIPOutputStream(tarByteOutputStream):tarByteOutputStream).

@TadCordle TadCordle merged commit 50ad775 into master May 14, 2018
@TadCordle TadCordle deleted the refactor_config_steps branch May 14, 2018 19:59
@TadCordle TadCordle mentioned this pull request May 23, 2018
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants