Polish "Add option to customize cache volume names when building an image"
See gh-28292
This commit is contained in:
@@ -75,7 +75,9 @@ public class BuildRequest {
|
||||
|
||||
private final List<ImageReference> tags;
|
||||
|
||||
private final Map<String, String> cacheVolumeNames;
|
||||
private final Cache buildCache;
|
||||
|
||||
private final Cache launchCache;
|
||||
|
||||
BuildRequest(ImageReference name, Function<Owner, TarArchive> applicationContent) {
|
||||
Assert.notNull(name, "Name must not be null");
|
||||
@@ -94,13 +96,14 @@ public class BuildRequest {
|
||||
this.bindings = Collections.emptyList();
|
||||
this.network = null;
|
||||
this.tags = Collections.emptyList();
|
||||
this.cacheVolumeNames = Collections.emptyMap();
|
||||
this.buildCache = null;
|
||||
this.launchCache = null;
|
||||
}
|
||||
|
||||
BuildRequest(ImageReference name, Function<Owner, TarArchive> applicationContent, ImageReference builder,
|
||||
ImageReference runImage, Creator creator, Map<String, String> env, boolean cleanCache,
|
||||
boolean verboseLogging, PullPolicy pullPolicy, boolean publish, List<BuildpackReference> buildpacks,
|
||||
List<Binding> bindings, String network, List<ImageReference> tags, Map<String, String> cacheVolumeNames) {
|
||||
List<Binding> bindings, String network, List<ImageReference> tags, Cache buildCache, Cache launchCache) {
|
||||
this.name = name;
|
||||
this.applicationContent = applicationContent;
|
||||
this.builder = builder;
|
||||
@@ -115,7 +118,8 @@ public class BuildRequest {
|
||||
this.bindings = bindings;
|
||||
this.network = network;
|
||||
this.tags = tags;
|
||||
this.cacheVolumeNames = cacheVolumeNames;
|
||||
this.buildCache = buildCache;
|
||||
this.launchCache = launchCache;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,7 +131,7 @@ public class BuildRequest {
|
||||
Assert.notNull(builder, "Builder must not be null");
|
||||
return new BuildRequest(this.name, this.applicationContent, builder.inTaggedOrDigestForm(), this.runImage,
|
||||
this.creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish,
|
||||
this.buildpacks, this.bindings, this.network, this.tags, this.cacheVolumeNames);
|
||||
this.buildpacks, this.bindings, this.network, this.tags, this.buildCache, this.launchCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,7 +142,7 @@ public class BuildRequest {
|
||||
public BuildRequest withRunImage(ImageReference runImageName) {
|
||||
return new BuildRequest(this.name, this.applicationContent, this.builder, runImageName.inTaggedOrDigestForm(),
|
||||
this.creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish,
|
||||
this.buildpacks, this.bindings, this.network, this.tags, this.cacheVolumeNames);
|
||||
this.buildpacks, this.bindings, this.network, this.tags, this.buildCache, this.launchCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,7 +154,7 @@ public class BuildRequest {
|
||||
Assert.notNull(creator, "Creator must not be null");
|
||||
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, creator, this.env,
|
||||
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
|
||||
this.network, this.tags, this.cacheVolumeNames);
|
||||
this.network, this.tags, this.buildCache, this.launchCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,7 +170,7 @@ public class BuildRequest {
|
||||
env.put(name, value);
|
||||
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator,
|
||||
Collections.unmodifiableMap(env), this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish,
|
||||
this.buildpacks, this.bindings, this.network, this.tags, this.cacheVolumeNames);
|
||||
this.buildpacks, this.bindings, this.network, this.tags, this.buildCache, this.launchCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,7 +184,8 @@ public class BuildRequest {
|
||||
updatedEnv.putAll(env);
|
||||
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator,
|
||||
Collections.unmodifiableMap(updatedEnv), this.cleanCache, this.verboseLogging, this.pullPolicy,
|
||||
this.publish, this.buildpacks, this.bindings, this.network, this.tags, this.cacheVolumeNames);
|
||||
this.publish, this.buildpacks, this.bindings, this.network, this.tags, this.buildCache,
|
||||
this.launchCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,7 +196,7 @@ public class BuildRequest {
|
||||
public BuildRequest withCleanCache(boolean cleanCache) {
|
||||
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
|
||||
cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
|
||||
this.network, this.tags, this.cacheVolumeNames);
|
||||
this.network, this.tags, this.buildCache, this.launchCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,7 +207,7 @@ public class BuildRequest {
|
||||
public BuildRequest withVerboseLogging(boolean verboseLogging) {
|
||||
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
|
||||
this.cleanCache, verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
|
||||
this.network, this.tags, this.cacheVolumeNames);
|
||||
this.network, this.tags, this.buildCache, this.launchCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,7 +218,7 @@ public class BuildRequest {
|
||||
public BuildRequest withPullPolicy(PullPolicy pullPolicy) {
|
||||
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
|
||||
this.cleanCache, this.verboseLogging, pullPolicy, this.publish, this.buildpacks, this.bindings,
|
||||
this.network, this.tags, this.cacheVolumeNames);
|
||||
this.network, this.tags, this.buildCache, this.launchCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,7 +229,7 @@ public class BuildRequest {
|
||||
public BuildRequest withPublish(boolean publish) {
|
||||
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
|
||||
this.cleanCache, this.verboseLogging, this.pullPolicy, publish, this.buildpacks, this.bindings,
|
||||
this.network, this.tags, this.cacheVolumeNames);
|
||||
this.network, this.tags, this.buildCache, this.launchCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -248,7 +253,7 @@ public class BuildRequest {
|
||||
Assert.notNull(buildpacks, "Buildpacks must not be null");
|
||||
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
|
||||
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, buildpacks, this.bindings,
|
||||
this.network, this.tags, this.cacheVolumeNames);
|
||||
this.network, this.tags, this.buildCache, this.launchCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -272,7 +277,7 @@ public class BuildRequest {
|
||||
Assert.notNull(bindings, "Bindings must not be null");
|
||||
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
|
||||
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, bindings,
|
||||
this.network, this.tags, this.cacheVolumeNames);
|
||||
this.network, this.tags, this.buildCache, this.launchCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,7 +289,7 @@ public class BuildRequest {
|
||||
public BuildRequest withNetwork(String network) {
|
||||
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
|
||||
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
|
||||
network, this.tags, this.cacheVolumeNames);
|
||||
network, this.tags, this.buildCache, this.launchCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -306,39 +311,31 @@ public class BuildRequest {
|
||||
Assert.notNull(tags, "Tags must not be null");
|
||||
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
|
||||
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
|
||||
this.network, tags, this.cacheVolumeNames);
|
||||
this.network, tags, this.buildCache, this.launchCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link BuildRequest} with an additional cache volume name.
|
||||
* @param type the cache volume type
|
||||
* @param name the cache volume name
|
||||
* Return a new {@link BuildRequest} with an updated build cache.
|
||||
* @param buildCache the build cache
|
||||
* @return an updated build request
|
||||
*/
|
||||
public BuildRequest withCacheVolumeName(String type, String name) {
|
||||
Assert.hasText(type, "Type must not be empty");
|
||||
Assert.state((type.equals("build") || type.equals("launch")), "Type must be either 'build' or 'launch'");
|
||||
Assert.hasText(name, "Name must not be empty");
|
||||
Map<String, String> cacheVolumeNames = new LinkedHashMap<>(this.cacheVolumeNames);
|
||||
cacheVolumeNames.put(type, name);
|
||||
public BuildRequest withBuildCache(Cache buildCache) {
|
||||
Assert.notNull(buildCache, "BuildCache must not be null");
|
||||
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
|
||||
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
|
||||
this.network, this.tags, Collections.unmodifiableMap(cacheVolumeNames));
|
||||
this.network, this.tags, buildCache, this.launchCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link BuildRequest} with additional cache volume names.
|
||||
* @param entries the additional cache volume names
|
||||
* Return a new {@link BuildRequest} with an updated launch cache.
|
||||
* @param launchCache the cache
|
||||
* @return an updated build request
|
||||
*/
|
||||
public BuildRequest withCacheVolumeNames(Map<String, String> entries) {
|
||||
Assert.notNull(entries, "Entries must not be null");
|
||||
Assert.state(!entries.isEmpty(), "Entries must not be empty");
|
||||
BuildRequest request = null;
|
||||
for (Map.Entry<String, String> entry : entries.entrySet()) {
|
||||
request = withCacheVolumeName(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return request;
|
||||
public BuildRequest withLaunchCache(Cache launchCache) {
|
||||
Assert.notNull(launchCache, "LaunchCache must not be null");
|
||||
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
|
||||
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
|
||||
this.network, this.tags, this.buildCache, launchCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -459,11 +456,19 @@ public class BuildRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the custom cache volume names that should be used by the lifecycle.
|
||||
* @return the cache volume names
|
||||
* Return the custom build cache that should be used by the lifecycle.
|
||||
* @return the build cache
|
||||
*/
|
||||
public Map<String, String> getCacheVolumeNames() {
|
||||
return this.cacheVolumeNames;
|
||||
public Cache getBuildCache() {
|
||||
return this.buildCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the custom launch cache that should be used by the lifecycle.
|
||||
* @return the launch cache
|
||||
*/
|
||||
public Cache getLaunchCache() {
|
||||
return this.launchCache;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 org.springframework.boot.buildpack.platform.build;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Details of a cache for use by the CNB builder.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
* @since 2.6.0
|
||||
*/
|
||||
public class Cache {
|
||||
|
||||
/**
|
||||
* The format of the cache.
|
||||
*/
|
||||
public enum Format {
|
||||
|
||||
/**
|
||||
* A cache stored as a volume in the Docker daemon.
|
||||
*/
|
||||
VOLUME;
|
||||
|
||||
}
|
||||
|
||||
protected final Format format;
|
||||
|
||||
Cache(Format format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the details of the cache if it is a volume cache.
|
||||
* @return the cache, or {@code null} if it is not a volume cache
|
||||
*/
|
||||
public Volume getVolume() {
|
||||
return (this.format.equals(Format.VOLUME)) ? (Volume) this : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code Cache} that uses a volume with the provided name.
|
||||
* @param name the cache volume name
|
||||
* @return a new cache instance
|
||||
*/
|
||||
public static Cache volume(String name) {
|
||||
Assert.notNull(name, "Name must not be null");
|
||||
return new Volume(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Cache other = (Cache) obj;
|
||||
return Objects.equals(this.format, other.format);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return ObjectUtils.nullSafeHashCode(this.format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Details of a cache stored in a Docker volume.
|
||||
*/
|
||||
public static class Volume extends Cache {
|
||||
|
||||
private final String name;
|
||||
|
||||
Volume(String name) {
|
||||
super(Format.VOLUME);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
Volume other = (Volume) obj;
|
||||
return Objects.equals(this.name, other.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + ObjectUtils.nullSafeHashCode(this.name);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -87,18 +87,36 @@ class Lifecycle implements Closeable {
|
||||
this.platformVersion = getPlatformVersion(builder.getBuilderMetadata().getLifecycle());
|
||||
this.layersVolume = createRandomVolumeName("pack-layers-");
|
||||
this.applicationVolume = createRandomVolumeName("pack-app-");
|
||||
this.buildCacheVolume = createCacheVolumeName(request, "build");
|
||||
this.launchCacheVolume = createCacheVolumeName(request, "launch");
|
||||
this.buildCacheVolume = getBuildCacheVolumeName(request);
|
||||
this.launchCacheVolume = getLaunchCacheVolumeName(request);
|
||||
}
|
||||
|
||||
protected VolumeName createRandomVolumeName(String prefix) {
|
||||
return VolumeName.random(prefix);
|
||||
}
|
||||
|
||||
private VolumeName createCacheVolumeName(BuildRequest request, String suffix) {
|
||||
if (!request.getCacheVolumeNames().isEmpty() && request.getCacheVolumeNames().containsKey(suffix)) {
|
||||
return VolumeName.of(request.getCacheVolumeNames().get(suffix));
|
||||
private VolumeName getBuildCacheVolumeName(BuildRequest request) {
|
||||
if (request.getBuildCache() != null) {
|
||||
return getVolumeName(request.getBuildCache());
|
||||
}
|
||||
return createCacheVolumeName(request, "build");
|
||||
}
|
||||
|
||||
private VolumeName getLaunchCacheVolumeName(BuildRequest request) {
|
||||
if (request.getLaunchCache() != null) {
|
||||
return getVolumeName(request.getLaunchCache());
|
||||
}
|
||||
return createCacheVolumeName(request, "launch");
|
||||
}
|
||||
|
||||
private VolumeName getVolumeName(Cache cache) {
|
||||
if (cache.getVolume() != null) {
|
||||
return VolumeName.of(cache.getVolume().getName());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private VolumeName createCacheVolumeName(BuildRequest request, String suffix) {
|
||||
return VolumeName.basedOn(request.getName(), ImageReference::toLegacyString, "pack-cache-", "." + suffix, 6);
|
||||
}
|
||||
|
||||
|
||||
@@ -210,11 +210,11 @@ class BuildRequestTests {
|
||||
@Test
|
||||
void withTagsAddsTags() throws IOException {
|
||||
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
|
||||
BuildRequest witTags = request.withTags(ImageReference.of("docker.io/library/my-app:latest"),
|
||||
BuildRequest withTags = request.withTags(ImageReference.of("docker.io/library/my-app:latest"),
|
||||
ImageReference.of("example.com/custom/my-app:0.0.1"),
|
||||
ImageReference.of("example.com/custom/my-app:latest"));
|
||||
assertThat(request.getTags()).isEmpty();
|
||||
assertThat(witTags.getTags()).containsExactly(ImageReference.of("docker.io/library/my-app:latest"),
|
||||
assertThat(withTags.getTags()).containsExactly(ImageReference.of("docker.io/library/my-app:latest"),
|
||||
ImageReference.of("example.com/custom/my-app:0.0.1"),
|
||||
ImageReference.of("example.com/custom/my-app:latest"));
|
||||
}
|
||||
@@ -226,6 +226,36 @@ class BuildRequestTests {
|
||||
.withMessage("Tags must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void withBuildVolumeCacheAddsCache() throws IOException {
|
||||
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
|
||||
BuildRequest withCache = request.withBuildCache(Cache.volume("build-volume"));
|
||||
assertThat(request.getBuildCache()).isNull();
|
||||
assertThat(withCache.getBuildCache()).isEqualTo(Cache.volume("build-volume"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void withBuildVolumeCacheWhenCacheIsNullThrowsException() throws IOException {
|
||||
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> request.withBuildCache(null))
|
||||
.withMessage("BuildCache must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void withLaunchVolumeCacheAddsCache() throws IOException {
|
||||
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
|
||||
BuildRequest withCache = request.withLaunchCache(Cache.volume("launch-volume"));
|
||||
assertThat(request.getLaunchCache()).isNull();
|
||||
assertThat(withCache.getLaunchCache()).isEqualTo(Cache.volume("launch-volume"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void withLaunchVolumeCacheWhenCacheIsNullThrowsException() throws IOException {
|
||||
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> request.withLaunchCache(null))
|
||||
.withMessage("LaunchCache must not be null");
|
||||
}
|
||||
|
||||
private void hasExpectedJarContent(TarArchive archive) {
|
||||
try {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
|
||||
@@ -200,6 +200,18 @@ class LifecycleTests {
|
||||
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void executeWithCacheVolumeNamesExecutesPhases() throws Exception {
|
||||
given(this.docker.container().create(any())).willAnswer(answerWithGeneratedContainerId());
|
||||
given(this.docker.container().create(any(), any())).willAnswer(answerWithGeneratedContainerId());
|
||||
given(this.docker.container().wait(any())).willReturn(ContainerStatus.of(0, null));
|
||||
BuildRequest request = getTestRequest().withBuildCache(Cache.volume("build-volume"))
|
||||
.withLaunchCache(Cache.volume("launch-volume"));
|
||||
createLifecycle(request).execute();
|
||||
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-cache-volumes.json"));
|
||||
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
|
||||
}
|
||||
|
||||
private DockerApi mockDockerApi() {
|
||||
DockerApi docker = mock(DockerApi.class);
|
||||
ImageApi imageApi = mock(ImageApi.class);
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"User": "root",
|
||||
"Image": "pack.local/ephemeral-builder",
|
||||
"Cmd": [
|
||||
"/cnb/lifecycle/creator",
|
||||
"-app",
|
||||
"/workspace",
|
||||
"-platform",
|
||||
"/platform",
|
||||
"-run-image",
|
||||
"docker.io/cloudfoundry/run:latest",
|
||||
"-layers",
|
||||
"/layers",
|
||||
"-cache-dir",
|
||||
"/cache",
|
||||
"-launch-cache",
|
||||
"/launch-cache",
|
||||
"-daemon",
|
||||
"-process-type=web",
|
||||
"docker.io/library/my-application:latest"
|
||||
],
|
||||
"Env": [
|
||||
"CNB_PLATFORM_API=0.4"
|
||||
],
|
||||
"Labels": {
|
||||
"author": "spring-boot"
|
||||
},
|
||||
"HostConfig": {
|
||||
"Binds": [
|
||||
"/var/run/docker.sock:/var/run/docker.sock",
|
||||
"pack-layers-aaaaaaaaaa:/layers",
|
||||
"pack-app-aaaaaaaaaa:/workspace",
|
||||
"build-volume:/cache",
|
||||
"launch-volume:/launch-cache"
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user