ImageArchive Class — spring-boot Architecture
Architecture documentation for the ImageArchive class in ImageArchive.java from the spring-boot codebase.
Entity Profile
Relationship Graph
Source Code
buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/type/ImageArchive.java lines 57–318
public class ImageArchive implements TarArchive {
private static final Instant WINDOWS_EPOCH_PLUS_SECOND = OffsetDateTime.of(1980, 1, 1, 0, 0, 1, 0, ZoneOffset.UTC)
.toInstant();
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ISO_ZONED_DATE_TIME
.withZone(ZoneOffset.UTC);
private static final String EMPTY_LAYER_NAME_PREFIX = "blank_";
private static final IOConsumer<Update> NO_UPDATES = (update) -> {
};
private final JsonMapper jsonMapper;
private final ImageConfig imageConfig;
private final Instant createDate;
private final @Nullable ImageReference tag;
private final String os;
private final @Nullable String architecture;
private final @Nullable String variant;
private final List<LayerId> existingLayers;
private final List<Layer> newLayers;
ImageArchive(JsonMapper jsonMapper, ImageConfig imageConfig, Instant createDate, @Nullable ImageReference tag,
String os, @Nullable String architecture, @Nullable String variant, List<LayerId> existingLayers,
List<Layer> newLayers) {
this.jsonMapper = jsonMapper;
this.imageConfig = imageConfig;
this.createDate = createDate;
this.tag = tag;
this.os = os;
this.architecture = architecture;
this.variant = variant;
this.existingLayers = existingLayers;
this.newLayers = newLayers;
}
/**
* Return the image config for the archive.
* @return the image config
*/
public ImageConfig getImageConfig() {
return this.imageConfig;
}
/**
* Return the create date of the archive.
* @return the create date
*/
public Instant getCreateDate() {
return this.createDate;
}
/**
* Return the tag of the archive.
* @return the tag
*/
public @Nullable ImageReference getTag() {
return this.tag;
}
@Override
public void writeTo(OutputStream outputStream) throws IOException {
TarArchive.of(this::write).writeTo(outputStream);
}
private void write(Layout writer) throws IOException {
List<LayerId> writtenLayers = writeLayers(writer);
String config = writeConfig(writer, writtenLayers);
writeManifest(writer, config, writtenLayers);
}
private List<LayerId> writeLayers(Layout writer) throws IOException {
for (int i = 0; i < this.existingLayers.size(); i++) {
writeEmptyLayer(writer, EMPTY_LAYER_NAME_PREFIX + i);
}
List<LayerId> writtenLayers = new ArrayList<>();
for (Layer layer : this.newLayers) {
writtenLayers.add(writeLayer(writer, layer));
}
return Collections.unmodifiableList(writtenLayers);
}
private void writeEmptyLayer(Layout writer, String name) throws IOException {
writer.file(name, Owner.ROOT, Content.of(""));
}
private LayerId writeLayer(Layout writer, Layer layer) throws IOException {
LayerId id = layer.getId();
writer.file(id.getHash() + ".tar", Owner.ROOT, layer);
return id;
}
private String writeConfig(Layout writer, List<LayerId> writtenLayers) throws IOException {
try {
ObjectNode config = createConfig(writtenLayers);
String json = this.jsonMapper.writeValueAsString(config).replace("\r\n", "\n");
MessageDigest digest = MessageDigest.getInstance("SHA-256");
InspectedContent content = InspectedContent.of(Content.of(json), digest::update);
String name = LayerId.ofSha256Digest(digest.digest()).getHash() + ".json";
writer.file(name, Owner.ROOT, content);
return name;
}
catch (NoSuchAlgorithmException ex) {
throw new IllegalStateException(ex);
}
}
private ObjectNode createConfig(List<LayerId> writtenLayers) {
ObjectNode config = this.jsonMapper.createObjectNode();
config.set("Config", this.imageConfig.getNodeCopy());
config.set("Created", config.stringNode(getCreatedDate()));
config.set("History", createHistory(writtenLayers));
config.set("Os", config.stringNode(this.os));
config.set("Architecture", config.stringNode(this.architecture));
config.set("Variant", config.stringNode(this.variant));
config.set("RootFS", createRootFs(writtenLayers));
return config;
}
private String getCreatedDate() {
return DATE_FORMATTER.format(this.createDate);
}
private JsonNode createHistory(List<LayerId> writtenLayers) {
ArrayNode history = this.jsonMapper.createArrayNode();
int size = this.existingLayers.size() + writtenLayers.size();
for (int i = 0; i < size; i++) {
history.addObject();
}
return history;
}
private JsonNode createRootFs(List<LayerId> writtenLayers) {
ObjectNode rootFs = this.jsonMapper.createObjectNode();
ArrayNode diffIds = rootFs.putArray("diff_ids");
this.existingLayers.stream().map(Object::toString).forEach(diffIds::add);
writtenLayers.stream().map(Object::toString).forEach(diffIds::add);
return rootFs;
}
private void writeManifest(Layout writer, String config, List<LayerId> writtenLayers) throws IOException {
ArrayNode manifest = createManifest(config, writtenLayers);
String manifestJson = this.jsonMapper.writeValueAsString(manifest);
writer.file("manifest.json", Owner.ROOT, Content.of(manifestJson));
}
private ArrayNode createManifest(String config, List<LayerId> writtenLayers) {
ArrayNode manifest = this.jsonMapper.createArrayNode();
ObjectNode entry = manifest.addObject();
entry.set("Config", entry.stringNode(config));
entry.set("Layers", getManifestLayers(writtenLayers));
if (this.tag != null) {
entry.set("RepoTags", entry.arrayNode().add(this.tag.toString()));
}
return manifest;
}
private ArrayNode getManifestLayers(List<LayerId> writtenLayers) {
ArrayNode layers = this.jsonMapper.createArrayNode();
for (int i = 0; i < this.existingLayers.size(); i++) {
layers.add(EMPTY_LAYER_NAME_PREFIX + i);
}
writtenLayers.stream().map((id) -> id.getHash() + ".tar").forEach(layers::add);
return layers;
}
/**
* Create a new {@link ImageArchive} based on an existing {@link Image}.
* @param image the image that this archive is based on
* @return the new image archive.
* @throws IOException on IO error
*/
public static ImageArchive from(Image image) throws IOException {
return from(image, NO_UPDATES);
}
/**
* Create a new {@link ImageArchive} based on an existing {@link Image}.
* @param image the image that this archive is based on
* @param update consumer to apply updates
* @return the new image archive.
* @throws IOException on IO error
*/
public static ImageArchive from(Image image, IOConsumer<Update> update) throws IOException {
return new Update(image).applyTo(update);
}
/**
* Update class used to change data when creating an image archive.
*/
public static final class Update {
private final Image image;
private ImageConfig config;
private @Nullable Instant createDate;
private @Nullable ImageReference tag;
private final List<Layer> newLayers = new ArrayList<>();
private Update(Image image) {
this.image = image;
this.config = image.getConfig();
}
private ImageArchive applyTo(IOConsumer<Update> update) throws IOException {
update.accept(this);
Instant createDate = (this.createDate != null) ? this.createDate : WINDOWS_EPOCH_PLUS_SECOND;
return new ImageArchive(SharedJsonMapper.get(), this.config, createDate, this.tag, this.image.getOs(),
this.image.getArchitecture(), this.image.getVariant(), this.image.getLayers(),
Collections.unmodifiableList(this.newLayers));
}
/**
* Apply updates to the {@link ImageConfig}.
* @param update consumer to apply updates
*/
public void withUpdatedConfig(Consumer<ImageConfig.Update> update) {
this.config = this.config.copy(update);
}
/**
* Add a new layer to the image archive.
* @param layer the layer to add
*/
public void withNewLayer(Layer layer) {
Assert.notNull(layer, "'layer' must not be null");
this.newLayers.add(layer);
}
/**
* Set the create date for the image archive.
* @param createDate the create date
*/
public void withCreateDate(Instant createDate) {
Assert.notNull(createDate, "'createDate' must not be null");
this.createDate = createDate;
}
/**
* Set the tag for the image archive.
* @param tag the tag
*/
public void withTag(ImageReference tag) {
Assert.notNull(tag, "'tag' must not be null");
this.tag = tag.inTaggedForm();
}
}
}
Domain
Source
Analyze Your Own Codebase
Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.
Try Supermodel Free