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

Propagate base image's environment variable config to built image #716

Merged
merged 16 commits into from
Jul 26, 2018
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class BuildImageStep
private static final String DESCRIPTION = "Building container configuration";

private final BuildConfiguration buildConfiguration;
private final PullBaseImageStep pullBaseImageStep;
private final PullAndCacheBaseImageLayersStep pullAndCacheBaseImageLayersStep;
private final ImmutableList<BuildAndCacheApplicationLayerStep> buildAndCacheApplicationLayerSteps;

Expand All @@ -48,15 +49,18 @@ class BuildImageStep
BuildImageStep(
ListeningExecutorService listeningExecutorService,
BuildConfiguration buildConfiguration,
PullBaseImageStep pullBaseImageStep,
PullAndCacheBaseImageLayersStep pullAndCacheBaseImageLayersStep,
ImmutableList<BuildAndCacheApplicationLayerStep> buildAndCacheApplicationLayerSteps) {
this.listeningExecutorService = listeningExecutorService;
this.buildConfiguration = buildConfiguration;
this.pullBaseImageStep = pullBaseImageStep;
this.pullAndCacheBaseImageLayersStep = pullAndCacheBaseImageLayersStep;
this.buildAndCacheApplicationLayerSteps = buildAndCacheApplicationLayerSteps;

listenableFuture =
Futures.whenAllSucceed(pullAndCacheBaseImageLayersStep.getFuture())
Futures.whenAllSucceed(
pullBaseImageStep.getFuture(), pullAndCacheBaseImageLayersStep.getFuture())
.call(this, listeningExecutorService);
}

Expand Down Expand Up @@ -96,8 +100,15 @@ private Image<CachedLayer> afterCachedLayersSteps()
buildAndCacheApplicationLayerSteps) {
imageBuilder.addLayer(NonBlockingSteps.get(buildAndCacheApplicationLayerStep));
}

// Use environment from base image if not configured
if (buildConfiguration.getEnvironment() == null) {
Copy link
Member

Choose a reason for hiding this comment

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

Later we will have the ability to add (not overwrite) env variables, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Possibly, I'm not sure what the plan is for that.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yea, I think environment variables should be overwritten only at the specific variable level, and not at the entire environment level.

Copy link
Contributor

Choose a reason for hiding this comment

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

So it could be like initialize imageBuilder with the base image, then apply overwrites from buildConfiguration.

imageBuilder.setEnvironment(
NonBlockingSteps.get(pullBaseImageStep).getBaseImage().getEnvironment());
} else {
imageBuilder.setEnvironment(buildConfiguration.getEnvironment());
}
imageBuilder.setCreated(buildConfiguration.getCreationTime());
imageBuilder.setEnvironment(buildConfiguration.getEnvironment());
imageBuilder.setEntrypoint(buildConfiguration.getEntrypoint());
imageBuilder.setJavaArguments(buildConfiguration.getJavaArguments());
imageBuilder.setExposedPorts(buildConfiguration.getExposedPorts());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.google.cloud.tools.jib.registry.RegistryClient;
import com.google.cloud.tools.jib.registry.RegistryException;
import com.google.cloud.tools.jib.registry.RegistryUnauthorizedException;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
Expand All @@ -62,7 +63,8 @@ static class BaseImageWithAuthorization {
private final Image<Layer> baseImage;
private final @Nullable Authorization baseImageAuthorization;

private BaseImageWithAuthorization(
@VisibleForTesting
BaseImageWithAuthorization(
Image<Layer> baseImage, @Nullable Authorization baseImageAuthorization) {
this.baseImage = baseImage;
this.baseImageAuthorization = baseImageAuthorization;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ public StepsRunner runBuildImageStep() {
new BuildImageStep(
listeningExecutorService,
buildConfiguration,
Preconditions.checkNotNull(pullBaseImageStep),
Preconditions.checkNotNull(pullAndCacheBaseImageLayersStep),
Preconditions.checkNotNull(buildAndCacheApplicationLayerSteps));
return this;
Expand Down
28 changes: 8 additions & 20 deletions jib-core/src/main/java/com/google/cloud/tools/jib/image/Image.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.google.cloud.tools.jib.configuration.Port;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.time.Instant;
import java.util.List;
import java.util.Map;
Expand All @@ -30,7 +31,7 @@ public class Image<T extends Layer> {
public static class Builder<T extends Layer> {

private final ImageLayers.Builder<T> imageLayersBuilder = ImageLayers.builder();
private ImmutableList.Builder<String> environmentBuilder = ImmutableList.builder();
private ImmutableMap.Builder<String, String> environmentBuilder = ImmutableMap.builder();

@Nullable private Instant created;
@Nullable private ImmutableList<String> entrypoint;
Expand All @@ -56,11 +57,9 @@ public Builder<T> setCreated(Instant created) {
*/
public Builder<T> setEnvironment(@Nullable Map<String, String> environment) {
if (environment == null) {
this.environmentBuilder = ImmutableList.builder();
this.environmentBuilder = ImmutableMap.builder();
} else {
for (Map.Entry<String, String> environmentVariable : environment.entrySet()) {
setEnvironmentVariable(environmentVariable.getKey(), environmentVariable.getValue());
}
this.environmentBuilder.putAll(ImmutableMap.copyOf(environment));
Copy link
Member

@chanseokoh chanseokoh Jul 26, 2018

Choose a reason for hiding this comment

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

I'm thinking this is no different from and just could be environmentBuilder.putAll(environment), am I right? You are just putting references to objects (Strings in this case) into the builder, not like you are doing a deep copy of String instances. (And Strings are immutable, so there is no point of deep copying, actually.) Am I right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds right to me.

}
return this;
}
Expand All @@ -73,18 +72,7 @@ public Builder<T> setEnvironment(@Nullable Map<String, String> environment) {
* @return this
*/
public Builder<T> setEnvironmentVariable(String name, String value) {
environmentBuilder.add(name + "=" + value);
return this;
}

/**
* Adds an environment variable definition in the format {@code NAME=VALUE}.
*
* @param environmentVariableDefinition the definition to add
* @return this
*/
public Builder<T> addEnvironmentVariableDefinition(String environmentVariableDefinition) {
environmentBuilder.add(environmentVariableDefinition);
environmentBuilder.put(name, value);
return this;
}

Expand Down Expand Up @@ -167,7 +155,7 @@ public static <T extends Layer> Builder<T> builder() {
private final ImageLayers<T> layers;

/** Environment variable definitions for running the image, in the format {@code NAME=VALUE}. */
@Nullable private final ImmutableList<String> environment;
@Nullable private final ImmutableMap<String, String> environment;

/** Initial command to run when running the image. */
@Nullable private final ImmutableList<String> entrypoint;
Expand All @@ -181,7 +169,7 @@ public static <T extends Layer> Builder<T> builder() {
private Image(
@Nullable Instant created,
ImageLayers<T> layers,
@Nullable ImmutableList<String> environment,
@Nullable ImmutableMap<String, String> environment,
@Nullable ImmutableList<String> entrypoint,
@Nullable ImmutableList<String> javaArguments,
@Nullable ImmutableList<Port> exposedPorts) {
Expand All @@ -199,7 +187,7 @@ public Instant getCreated() {
}

@Nullable
public ImmutableList<String> getEnvironment() {
public ImmutableMap<String, String> getEnvironment() {
return environment;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.google.cloud.tools.jib.image.Image;
import com.google.cloud.tools.jib.json.JsonTemplateMapper;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedMap;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
Expand Down Expand Up @@ -67,6 +68,24 @@ public class ImageToJsonTranslator {
return result.build();
}

/**
* Converts the map of environment variables to a list with items in the format "NAME=VALUE".
*
* @return the list
*/
@VisibleForTesting
@Nullable
static ImmutableList<String> environmentMapToList(@Nullable Map<String, String> environment) {
if (environment == null) {
return null;
}
ImmutableList.Builder<String> builder = ImmutableList.builder();
for (Map.Entry<String, String> environmentVariable : environment.entrySet()) {
builder.add(environmentVariable.getKey() + "=" + environmentVariable.getValue());
Copy link
Member

Choose a reason for hiding this comment

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

I see this PR does not change the current behavior, but I'm just wondering if any kind of escaping should also be considered. Do we have to convert this to a list? Maybe changing some API, we could pass the map and don't worry about escaping at all, although I admit it'd be very rare to have = in a variable name.

Copy link
Contributor Author

@TadCordle TadCordle Jul 25, 2018

Choose a reason for hiding this comment

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

Yeah, I definitely think this can be more bullet proof. Actually I think this breaks if there's an = in the value, not just the name.

Edit: Not this, the opposite conversion in JsonToImageTranslator

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm I think we might want to guard this at the environment-setting level - so maybe we might need to have a separate Environment class to be this abstraction (like mentioned in #15)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can handle this when we handle #15 then?

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we can just throw an IllegalArgumentException for now?

}
return builder.build();
Copy link
Member

Choose a reason for hiding this comment

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

If you like streams, you could simplify this a bit:

    Preconditions.checkArgument(
        Iterables.any(environment.keySet(), s -> s.contains("=")),
        "Environment variable name cannot contain '='");
    return environment
        .entrySet()
        .stream()
        .map(entry -> entry.getKey() + "=" + entry.getValue())
        .collect(ImmutableList.toImmutableList());

}

private final Image<CachedLayer> image;

/**
Expand Down Expand Up @@ -96,7 +115,7 @@ public Blob getContainerConfigurationBlob() {
template.setCreated(image.getCreated() == null ? null : image.getCreated().toString());

// Adds the environment variables.
template.setContainerEnvironment(image.getEnvironment());
template.setContainerEnvironment(environmentMapToList(image.getEnvironment()));

// Sets the entrypoint.
template.setContainerEntrypoint(image.getEntrypoint());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ public class JsonToImageTranslator {
*/
private static final Pattern portPattern = Pattern.compile("(\\d+)(?:/(tcp|udp))?");

/**
* Pattern used for parsing environment variables in the format {@code NAME=VALUE}. {@code NAME}
* should start with a letter and contain only letters (uppercase and lower) and numbers. {@code
* VALUE} can contain anything.
*
* <p>Example matches: NAME=VALUE, A12345=$$$$$
*/
@VisibleForTesting
static final Pattern environmentPattern = Pattern.compile("([A-Za-z][A-Za-z0-9_]+)=(.*)");
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe environment variable names can actually contain any character (http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html) - These strings have the form name=value; names shall not contain the character '='., so we should probably have this pattern be like (.*?)=(.*).

Copy link
Contributor Author

@TadCordle TadCordle Jul 25, 2018

Choose a reason for hiding this comment

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

Here it says:

env: (array of strings, OPTIONAL) with the same semantics as IEEE Std 1003.1-2008's environ.

And in the link you shared:

Environment variable names used by the utilities in the Shell and Utilities volume of IEEE Std 1003.1-2001 consist solely of uppercase letters, digits, and the '_' (underscore) from the characters defined in Portable Character Set and do not begin with a digit

Copy link
Contributor

Choose a reason for hiding this comment

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

Yep, for environment variables used in shell utilities (like bash), the names shouldn't consist of anything besides the alphanumerics, but for process environment variables, the definition is more lenient to allow any character besides =.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

reading is hard

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Shouldn't we make sure the name has at least one character though? I think what you gave will match something like "=ABC". Is that valid?

Copy link
Contributor

Choose a reason for hiding this comment

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

That’s a good point - let’s change the star to a plus.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That matches "====", I think ([^=].*?)=(.*) should work

Copy link
Contributor

Choose a reason for hiding this comment

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

Yea, I think having = in the value is okay


/**
* Translates {@link V21ManifestTemplate} to {@link Image}.
*
Expand Down Expand Up @@ -139,7 +149,14 @@ public static Image<Layer> toImage(

if (containerConfigurationTemplate.getContainerEnvironment() != null) {
for (String environmentVariable : containerConfigurationTemplate.getContainerEnvironment()) {
imageBuilder.addEnvironmentVariableDefinition(environmentVariable);
Matcher matcher = environmentPattern.matcher(environmentVariable);
if (!matcher.matches()) {
throw new BadContainerConfigurationFormatException(
"Invalid environment variable definition: " + environmentVariable);
}
String name = matcher.group(1);
String value = matcher.group(2);
Copy link
Member

Choose a reason for hiding this comment

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

When patterns are far away from their use, it can help to use a named group (e.g., matcher.group("name") and matcher.group("value")).

Copy link
Contributor

Choose a reason for hiding this comment

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

+1 We should probably do this for some of the other regex patterns too.

imageBuilder.setEnvironmentVariable(name, value);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.google.cloud.tools.jib.configuration.BuildConfiguration;
import com.google.cloud.tools.jib.image.DescriptorDigest;
import com.google.cloud.tools.jib.image.Image;
import com.google.cloud.tools.jib.image.Layer;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.Futures;
Expand All @@ -46,6 +47,8 @@ public class BuildImageStepTest {

@Mock private BuildConfiguration mockBuildConfiguration;
@Mock private BuildLogger mockBuildLogger;
@Mock private PullBaseImageStep mockPullBaseImageStep;
@Mock private Image<Layer> mockBaseImage;
Copy link
Contributor

Choose a reason for hiding this comment

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

We could prob just have a real Image since there's nothing in Image that needs to be mocked.

@Mock private PullAndCacheBaseImageLayersStep mockPullAndCacheBaseImageLayersStep;
@Mock private PullAndCacheBaseImageLayerStep mockPullAndCacheBaseImageLayerStep;
@Mock private BuildAndCacheApplicationLayerStep mockBuildAndCacheApplicationLayerStep;
Expand All @@ -70,15 +73,19 @@ public void setUp() throws DigestException {
Mockito.when(mockBuildConfiguration.getExposedPorts()).thenReturn(ImmutableList.of());
Mockito.when(mockBuildConfiguration.getEntrypoint()).thenReturn(ImmutableList.of());

Mockito.when(mockPullAndCacheBaseImageLayerStep.getFuture())
.thenReturn(Futures.immediateFuture(testCachedLayer));
Mockito.when(mockPullAndCacheBaseImageLayersStep.getFuture())
.thenReturn(
Futures.immediateFuture(
ImmutableList.of(
mockPullAndCacheBaseImageLayerStep,
mockPullAndCacheBaseImageLayerStep,
mockPullAndCacheBaseImageLayerStep)));
Mockito.when(mockPullAndCacheBaseImageLayerStep.getFuture())
.thenReturn(Futures.immediateFuture(testCachedLayer));
Mockito.when(mockPullBaseImageStep.getFuture())
.thenReturn(
Futures.immediateFuture(
new PullBaseImageStep.BaseImageWithAuthorization(mockBaseImage, null)));
Mockito.when(mockBuildAndCacheApplicationLayerStep.getFuture())
.thenReturn(Futures.immediateFuture(testCachedLayer));
}
Expand All @@ -89,6 +96,7 @@ public void test_validateAsyncDependencies() throws ExecutionException, Interrup
new BuildImageStep(
MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()),
mockBuildConfiguration,
mockPullBaseImageStep,
mockPullAndCacheBaseImageLayersStep,
ImmutableList.of(
mockBuildAndCacheApplicationLayerStep,
Expand All @@ -98,4 +106,24 @@ public void test_validateAsyncDependencies() throws ExecutionException, Interrup
Assert.assertEquals(
testDescriptorDigest, image.getLayers().asList().get(0).getBlobDescriptor().getDigest());
}

@Test
public void test_propagateBaseImageConfiguration()
throws ExecutionException, InterruptedException {
Mockito.when(mockBuildConfiguration.getEnvironment()).thenReturn(null);
Mockito.when(mockBaseImage.getEnvironment()).thenReturn(ImmutableMap.of("NAME", "VALUE"));

BuildImageStep buildImageStep =
new BuildImageStep(
MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()),
mockBuildConfiguration,
mockPullBaseImageStep,
mockPullAndCacheBaseImageLayersStep,
ImmutableList.of(
mockBuildAndCacheApplicationLayerStep,
mockBuildAndCacheApplicationLayerStep,
mockBuildAndCacheApplicationLayerStep));
Image<CachedLayer> image = buildImageStep.getFuture().get().getFuture().get();
Assert.assertEquals(ImmutableMap.of("NAME", "VALUE"), image.getEnvironment());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.google.cloud.tools.jib.configuration.Port;
import com.google.cloud.tools.jib.configuration.Port.Protocol;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.time.Instant;
import java.util.Arrays;
import org.junit.Assert;
Expand All @@ -45,9 +46,6 @@ public void setUp() throws LayerPropertyNotFoundException {

@Test
public void test_smokeTest() throws LayerPropertyNotFoundException {
ImmutableList<String> expectedEnvironment =
ImmutableList.of("crepecake=is great", "VARIABLE=VALUE");

Image<Layer> image =
Image.builder()
.setCreated(Instant.ofEpochSecond(10000))
Expand All @@ -63,7 +61,8 @@ public void test_smokeTest() throws LayerPropertyNotFoundException {
Assert.assertEquals(
mockDescriptorDigest, image.getLayers().get(0).getBlobDescriptor().getDigest());
Assert.assertEquals(Instant.ofEpochSecond(10000), image.getCreated());
Assert.assertEquals(expectedEnvironment, image.getEnvironment());
Assert.assertEquals(
ImmutableMap.of("crepecake", "is great", "VARIABLE", "VALUE"), image.getEnvironment());
Assert.assertEquals(Arrays.asList("some", "command"), image.getEntrypoint());
Assert.assertEquals(Arrays.asList("arg1", "arg2"), image.getJavaArguments());
Assert.assertEquals(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ public void testPortListToMap() {
Assert.assertEquals(expected, ImageToJsonTranslator.portListToMap(input));
}

@Test
public void testEnvironmentMapToList() {
ImmutableMap<String, String> input = ImmutableMap.of("NAME1", "VALUE1", "NAME2", "VALUE2");
ImmutableList<String> expected = ImmutableList.of("NAME1=VALUE1", "NAME2=VALUE2");
Assert.assertEquals(expected, ImageToJsonTranslator.environmentMapToList(input));
}

/** Tests translation of image to {@link BuildableManifestTemplate}. */
private <T extends BuildableManifestTemplate> void testGetManifest(
Class<T> manifestTemplateClass, String translatedJsonFilename)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import org.junit.Assert;
import org.junit.Test;

Expand Down Expand Up @@ -110,6 +111,32 @@ public void testPortMapToList() throws BadContainerConfigurationFormatException
}
}

@Test
public void testJsonToImageTranslatorRegex() {
assertGoodPattern("NAME=VALUE", "NAME", "VALUE");
Copy link
Contributor

Choose a reason for hiding this comment

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

Might want to call this like assertGoodEnvironmentPattern

assertGoodPattern("A1203921=www=ww", "A1203921", "www=ww");
assertGoodPattern("abcABC123=", "abcABC123", "");
assertGoodPattern("m_a_8943=100", "m_a_8943", "100");
assertGoodPattern("A_B_C_D=*****", "A_B_C_D", "*****");

assertBadPattern("123=ABC");
assertBadPattern("=================");
assertBadPattern("A_B_C");
assertBadPattern("$$$$$=fjdkslajfkdsl");
}

private void assertGoodPattern(String input, String expectedName, String expectedValue) {
Matcher matcher = JsonToImageTranslator.environmentPattern.matcher(input);
Assert.assertTrue(matcher.matches());
Assert.assertEquals(expectedName, matcher.group(1));
Assert.assertEquals(expectedValue, matcher.group(2));
}

private void assertBadPattern(String input) {
Matcher matcher = JsonToImageTranslator.environmentPattern.matcher(input);
Assert.assertFalse(matcher.matches());
}

private <T extends BuildableManifestTemplate> void testToImage_buildable(
String jsonFilename, Class<T> manifestTemplateClass)
throws IOException, LayerPropertyNotFoundException, LayerCountMismatchException,
Expand Down Expand Up @@ -144,7 +171,7 @@ private <T extends BuildableManifestTemplate> void testToImage_buildable(
layers.get(0).getDiffId());
Assert.assertEquals(Instant.ofEpochSecond(20), image.getCreated());
Assert.assertEquals(Arrays.asList("some", "entrypoint", "command"), image.getEntrypoint());
Assert.assertEquals(Arrays.asList("VAR1=VAL1", "VAR2=VAL2"), image.getEnvironment());
Assert.assertEquals(ImmutableMap.of("VAR1", "VAL1", "VAR2", "VAL2"), image.getEnvironment());
Assert.assertEquals(
ImmutableList.of(
new Port(1000, Protocol.TCP),
Expand Down
1 change: 1 addition & 0 deletions jib-gradle-plugin/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.

- Only builds non-empty layers ([#516](https://github.com/GoogleContainerTools/jib/pull/516/files))
- Fixed slow image reference parsing ([#680](https://github.com/GoogleContainerTools/jib/pull/680))
- Environment variable configuration is propagated from the base image ([#716](https://github.com/GoogleContainerTools/jib/pull/716))
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Propagates environment variables from base image...


## 0.9.7

Expand Down
Loading