Home / Class/ DockerConfigurationMetadata Class — spring-boot Architecture

DockerConfigurationMetadata Class — spring-boot Architecture

Architecture documentation for the DockerConfigurationMetadata class in DockerConfigurationMetadata.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/configuration/DockerConfigurationMetadata.java lines 49–298

final class DockerConfigurationMetadata {

	private static final String DOCKER_CONFIG = "DOCKER_CONFIG";

	private static final String DEFAULT_CONTEXT = "default";

	private static final String CONFIG_DIR = ".docker";

	private static final String CONTEXTS_DIR = "contexts";

	private static final String META_DIR = "meta";

	private static final String TLS_DIR = "tls";

	private static final String DOCKER_ENDPOINT = "docker";

	private static final String CONFIG_FILE_NAME = "config.json";

	private static final String CONTEXT_FILE_NAME = "meta.json";

	private static final Supplier<DockerConfigurationMetadata> systemEnvironmentConfigurationMetadata = SingletonSupplier
		.of(() -> DockerConfigurationMetadata.create(Environment.SYSTEM));

	private final String configLocation;

	private final DockerConfig config;

	private final DockerContext context;

	private DockerConfigurationMetadata(String configLocation, DockerConfig config, DockerContext context) {
		this.configLocation = configLocation;
		this.config = config;
		this.context = context;
	}

	DockerConfig getConfiguration() {
		return this.config;
	}

	DockerContext getContext() {
		return this.context;
	}

	DockerContext forContext(@Nullable String context) {
		return createDockerContext(this.configLocation, context);
	}

	static DockerConfigurationMetadata from(Environment environment) {
		if (environment == Environment.SYSTEM) {
			return systemEnvironmentConfigurationMetadata.get();
		}
		return create(environment);
	}

	private static DockerConfigurationMetadata create(Environment environment) {
		String configLocation = environment.get(DOCKER_CONFIG);
		configLocation = (configLocation != null) ? configLocation : getUserHomeConfigLocation();
		DockerConfig dockerConfig = createDockerConfig(configLocation);
		DockerContext dockerContext = createDockerContext(configLocation, dockerConfig.getCurrentContext());
		return new DockerConfigurationMetadata(configLocation, dockerConfig, dockerContext);
	}

	private static String getUserHomeConfigLocation() {
		return Path.of(System.getProperty("user.home"), CONFIG_DIR).toString();
	}

	private static DockerConfig createDockerConfig(String configLocation) {
		Path path = Path.of(configLocation, CONFIG_FILE_NAME);
		if (!path.toFile().exists()) {
			return DockerConfig.empty();
		}
		try {
			return DockerConfig.fromJson(readPathContent(path));
		}
		catch (JacksonException ex) {
			throw new IllegalStateException("Error parsing Docker configuration file '" + path + "'", ex);
		}
	}

	private static DockerContext createDockerContext(String configLocation, @Nullable String currentContext) {
		if (currentContext == null || DEFAULT_CONTEXT.equals(currentContext)) {
			return DockerContext.empty();
		}
		String hash = asHash(currentContext);
		Path metaPath = Path.of(configLocation, CONTEXTS_DIR, META_DIR, hash, CONTEXT_FILE_NAME);
		Path tlsPath = Path.of(configLocation, CONTEXTS_DIR, TLS_DIR, hash, DOCKER_ENDPOINT);
		if (!metaPath.toFile().exists()) {
			throw new IllegalArgumentException("Docker context '" + currentContext + "' does not exist");
		}
		try {
			DockerContext context = DockerContext.fromJson(readPathContent(metaPath));
			if (tlsPath.toFile().isDirectory()) {
				return context.withTlsPath(tlsPath.toString());
			}
			return context;
		}
		catch (JacksonException ex) {
			throw new IllegalStateException("Error parsing Docker context metadata file '" + metaPath + "'", ex);
		}
	}

	private static String asHash(String currentContext) {
		try {
			MessageDigest digest = MessageDigest.getInstance("SHA-256");
			byte[] hash = digest.digest(currentContext.getBytes(StandardCharsets.UTF_8));
			return HexFormat.of().formatHex(hash);
		}
		catch (NoSuchAlgorithmException ex) {
			throw new IllegalStateException("SHA-256 is not available", ex);
		}
	}

	private static String readPathContent(Path path) {
		try {
			return Files.readString(path);
		}
		catch (IOException ex) {
			throw new IllegalStateException("Error reading Docker configuration file '" + path + "'", ex);
		}
	}

	static final class DockerConfig extends MappedObject {

		private final @Nullable String currentContext;

		private final @Nullable String credsStore;

		private final Map<String, String> credHelpers;

		private final Map<String, Auth> auths;

		private DockerConfig(JsonNode node) {
			super(node, MethodHandles.lookup());
			this.currentContext = valueAt("/currentContext", String.class);
			this.credsStore = valueAt("/credsStore", String.class);
			this.credHelpers = mapAt("/credHelpers", JsonNode::stringValue);
			this.auths = mapAt("/auths", Auth::new);
		}

		@Nullable String getCurrentContext() {
			return this.currentContext;
		}

		@Nullable String getCredsStore() {
			return this.credsStore;
		}

		Map<String, String> getCredHelpers() {
			return this.credHelpers;
		}

		Map<String, Auth> getAuths() {
			return this.auths;
		}

		static DockerConfig fromJson(String json) {
			return new DockerConfig(SharedJsonMapper.get().readTree(json));
		}

		static DockerConfig empty() {
			return new DockerConfig(NullNode.instance);
		}

	}

	static final class Auth extends MappedObject {

		private final @Nullable String username;

		private final @Nullable String password;

		private final @Nullable String email;

		Auth(JsonNode node) {
			super(node, MethodHandles.lookup());
			String auth = valueAt("/auth", String.class);
			if (StringUtils.hasLength(auth)) {
				String[] parts = new String(Base64.getDecoder().decode(auth)).split(":", 2);
				Assert.state(parts.length == 2, "Malformed auth in docker configuration metadata");
				this.username = parts[0];
				this.password = trim(parts[1], Character.MIN_VALUE);
			}
			else {
				this.username = valueAt("/username", String.class);
				this.password = valueAt("/password", String.class);
			}
			this.email = valueAt("/email", String.class);
		}

		@Nullable String getUsername() {
			return this.username;
		}

		@Nullable String getPassword() {
			return this.password;
		}

		@Nullable String getEmail() {
			return this.email;
		}

		private static String trim(String source, char character) {
			source = StringUtils.trimLeadingCharacter(source, character);
			return StringUtils.trimTrailingCharacter(source, character);
		}

	}

	static final class DockerContext extends MappedObject {

		private final @Nullable String dockerHost;

		private final @Nullable Boolean skipTlsVerify;

		private final @Nullable String tlsPath;

		private DockerContext(JsonNode node, @Nullable String tlsPath) {
			super(node, MethodHandles.lookup());
			this.dockerHost = valueAt("/Endpoints/" + DOCKER_ENDPOINT + "/Host", String.class);
			this.skipTlsVerify = valueAt("/Endpoints/" + DOCKER_ENDPOINT + "/SkipTLSVerify", Boolean.class);
			this.tlsPath = tlsPath;
		}

		@Nullable String getDockerHost() {
			return this.dockerHost;
		}

		Boolean isTlsVerify() {
			return this.skipTlsVerify != null && !this.skipTlsVerify;
		}

		@Nullable String getTlsPath() {
			return this.tlsPath;
		}

		DockerContext withTlsPath(String tlsPath) {
			return new DockerContext(this.getNode(), tlsPath);
		}

		static DockerContext fromJson(String json) {
			return new DockerContext(SharedJsonMapper.get().readTree(json), null);
		}

		static DockerContext empty() {
			return new DockerContext(NullNode.instance, null);
		}

	}

}

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free