Skip to content

Commit

Permalink
BuildX attestation configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
chonton authored and rohanKanojia committed Apr 19, 2023
1 parent e1a9e88 commit 043a569
Show file tree
Hide file tree
Showing 8 changed files with 265 additions and 84 deletions.
4 changes: 4 additions & 0 deletions src/main/asciidoc/inc/build/_buildx.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ is relative to the user's home directory.
linux/arm64, darwin/amd64). Each `<platform>` element may have a comma separated list of platforms. Empty `<platform>`
elements are ignored. If no platform architecture is specified, buildx is *not* used. You can use

| *attestations*
| The configuration of attestation modes. The `<provenance>` element may be set to `min`,
`max`, or `false`. The `<sbom>` element may be set to `true` or `false`. The `<provenance>`
element defaults to `min` and the `<sbom>` element defaults to `false`.
|===

.Examples
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package io.fabric8.maven.docker.config;

import io.fabric8.maven.docker.util.EnvUtil;
import java.io.Serializable;
import java.util.List;
import org.apache.maven.plugins.annotations.Parameter;

/**
* Attestation Configuration
*/
public class AttestationConfiguration implements Serializable {

/**
* Provenance attestation mode; one of true, false, min, max
*/
@Parameter
private String provenance;

/**
* Enable Software Bill of Materials attestation
*/
@Parameter
private Boolean sbom;

public String getProvenance() {
return provenance;
}

public Boolean getSbom() {
return sbom;
}

public static class Builder {

private final AttestationConfiguration config = new AttestationConfiguration();
private boolean isEmpty = true;

public AttestationConfiguration build() {
return isEmpty ? null : config;
}

public AttestationConfiguration.Builder provenance(String provenance) {
config.provenance = provenance;
if (provenance != null) {
isEmpty = false;
}
return this;
}

public AttestationConfiguration.Builder sbom(Boolean sbom) {
config.sbom = sbom;
if (sbom != null) {
isEmpty = false;
}
return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class BuildXConfiguration implements Serializable {
*/
@Parameter
private String builderName;

/**
* Configuration file to create builder
*/
Expand All @@ -32,10 +33,11 @@ public class BuildXConfiguration implements Serializable {
@Parameter
private List<String> platforms;

@Nonnull
public List<String> getPlatforms() {
return EnvUtil.splitAtCommasAndTrim(platforms);
}
/**
* Attestation configuration
*/
@Parameter
private AttestationConfiguration attestations;

public String getBuilderName() {
return builderName;
Expand All @@ -53,6 +55,14 @@ public boolean isBuildX() {
return !getPlatforms().isEmpty();
}

@Nonnull
public List<String> getPlatforms() {
return EnvUtil.splitAtCommasAndTrim(platforms);
}

public AttestationConfiguration getAttestations() {
return attestations;
}

public static class Builder {

Expand Down Expand Up @@ -94,5 +104,13 @@ public Builder platforms(List<String> platforms) {
}
return this;
}

public Builder attestations(AttestationConfiguration attestations) {
config.attestations = attestations;
if (attestations != null) {
isEmpty = false;
}
return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ public enum ConfigKey {
BUILDX_BUILDERNAME("buildx.builderName"),
BUILDX_CONFIGFILE("buildx.configFile"),
BUILDX_DOCKERSTATEDIR("buildx.dockerStateDir"),
BUILDX_ATTESTATION_PROVENANCE("buildx.attestations.provenance"),
BUILDX_ATTESTATION_SBOM("buildx.attestations.sbom"),
CAP_ADD,
CAP_DROP,
SYSCTLS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* limitations under the License.
*/

import io.fabric8.maven.docker.config.AttestationConfiguration;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
Expand Down Expand Up @@ -335,6 +336,18 @@ private BuildXConfiguration extractBuildx(BuildXConfiguration config, ValueProvi
.configFile(valueProvider.getString(BUILDX_CONFIGFILE, config.getConfigFile()))
.dockerStateDir(valueProvider.getString(BUILDX_DOCKERSTATEDIR, config.getDockerStateDir()))
.platforms(valueProvider.getList(BUILDX_PLATFORMS, config.getPlatforms()))
.attestations(extractAttestations(config.getAttestations(), valueProvider))
.build();
}

private AttestationConfiguration extractAttestations(AttestationConfiguration config, ValueProvider valueProvider) {
if (config == null) {
config = new AttestationConfiguration();
}

return new AttestationConfiguration.Builder()
.provenance(valueProvider.getString(BUILDX_ATTESTATION_PROVENANCE, config.getProvenance()))
.sbom(valueProvider.getBoolean(BUILDX_ATTESTATION_SBOM, config.getSbom()))
.build();
}

Expand Down
91 changes: 54 additions & 37 deletions src/main/java/io/fabric8/maven/docker/service/BuildXService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.fabric8.maven.docker.access.DockerAccess;
import io.fabric8.maven.docker.assembly.BuildDirs;
import io.fabric8.maven.docker.assembly.DockerAssemblyManager;
import io.fabric8.maven.docker.config.AttestationConfiguration;
import io.fabric8.maven.docker.config.BuildImageConfiguration;
import io.fabric8.maven.docker.config.BuildXConfiguration;
import io.fabric8.maven.docker.config.ImageConfiguration;
Expand Down Expand Up @@ -116,19 +117,13 @@ protected void buildX(List<String> buildX, String builderName, BuildDirs buildDi
BuildImageConfiguration buildConfiguration = imageConfig.getBuildConfiguration();

List<String> cmdLine = new ArrayList<>(buildX);
cmdLine.add("build");
cmdLine.add("--progress=plain");
cmdLine.add("--builder");
cmdLine.add(builderName);
cmdLine.add("--platform");
cmdLine.add(String.join(",", platforms));
append(cmdLine, "build", "--progress=plain", "--builder", builderName, "--platform",
String.join(",", platforms), "--tag",
new ImageName(imageConfig.getName()).getFullName(configuredRegistry));
buildConfiguration.getTags().forEach(t -> {
cmdLine.add("--tag");
cmdLine.add(new ImageName(imageConfig.getName(), t).getFullName(configuredRegistry));
}
);
cmdLine.add("--tag");
cmdLine.add(new ImageName(imageConfig.getName()).getFullName(configuredRegistry));
cmdLine.add("--tag");
cmdLine.add(new ImageName(imageConfig.getName(), t).getFullName(configuredRegistry));
});

Map<String, String> args = buildConfiguration.getArgs();
if (args != null) {
Expand All @@ -137,24 +132,46 @@ protected void buildX(List<String> buildX, String builderName, BuildDirs buildDi
cmdLine.add(key + '=' + value);
});
}

AttestationConfiguration attestations = buildConfiguration.getBuildX().getAttestations();
if (attestations != null) {
if (Boolean.TRUE.equals(attestations.getSbom())) {
cmdLine.add("--sbom=true");
}
String provenance = attestations.getProvenance();
if (provenance != null) {
switch (provenance) {
case "min":
case "max":
cmdLine.add("--provenance=mode=" + provenance);
break;
case "false":
case "true":
cmdLine.add("--provenance=" + provenance);
break;
default:
logger.error("Unsupported provenance mode %s", provenance);
}
}
}

if (buildConfiguration.squash()) {
cmdLine.add("--squash");
}
if (extraParam != null) {
cmdLine.add(extraParam);
}

String[] ctxCmds;
File contextDir = buildConfiguration.getContextDir();
if (contextDir != null) {
Path destinationPath = getContextPath(buildArchive);
Path dockerFileRelativePath = contextDir.toPath().relativize(buildConfiguration.getDockerFile().toPath());
ctxCmds = new String[] { "--file=" + destinationPath.resolve(dockerFileRelativePath), destinationPath.toString() };
append(cmdLine, "--file=" + destinationPath.resolve(dockerFileRelativePath), destinationPath.toString());
} else {
ctxCmds = new String[] { buildDirs.getOutputDirectory().getAbsolutePath() };
cmdLine.add(buildDirs.getOutputDirectory().getAbsolutePath());
}
if (extraParam != null) {
cmdLine.add(extraParam);
}

int rc = exec.process(cmdLine, ctxCmds);
int rc = exec.process(cmdLine);
if (rc != 0) {
throw new MojoExecutionException("Error status (" + rc + ") when building");
}
Expand Down Expand Up @@ -197,25 +214,32 @@ protected String createBuilder(Path configPath, List<String> buildX, ImageConfig
}
Path builderPath = configPath.resolve(Paths.get("buildx", "instances", builderName));
if(Files.notExists(builderPath)) {
List<String> cmds = new ArrayList<>(buildX);
append(cmds, "create", "--driver", "docker-container", "--name", builderName);
String buildConfig = buildXConfiguration.getConfigFile();
int rc = exec.process(buildX, buildConfig == null
? new String[] { "create", "--driver", "docker-container", "--name", builderName }
: new String[] { "create", "--driver", "docker-container", "--name", builderName, "--config",
buildDirs.getProjectPath(EnvUtil.resolveHomeReference(buildConfig)).toString() }
);
if(buildConfig != null) {
append(cmds, "--config",
buildDirs.getProjectPath(EnvUtil.resolveHomeReference(buildConfig)).toString());
}
int rc = exec.process(cmds);
if (rc != 0) {
throw new MojoExecutionException("Error status (" + rc + ") while creating builder " + builderName);
}
}
return builderName;
}

public static <T> List<T> append(List<T> collection, T... members) {
collection.addAll(Arrays.asList(members));
return collection;
}

interface Builder<C> {
void useBuilder(List<String> buildX, String builderName, BuildDirs buildDirs, ImageConfiguration imageConfig, String configuredRegistry, C context) throws MojoExecutionException;
}

public interface Exec {
int process(List<String> buildX, String... cmd) throws MojoExecutionException;
int process(List<String> cmdArgs) throws MojoExecutionException;
}

public static class DefaultExec implements Exec {
Expand All @@ -225,26 +249,19 @@ public DefaultExec(Logger logger) {
this.logger = logger;
}

@Override public int process(List<String> buildX, String... cmd) throws MojoExecutionException {
List<String> cmdLine;
if (cmd.length > 0) {
cmdLine = new ArrayList<>(buildX);
cmdLine.addAll(Arrays.asList(cmd));
} else {
cmdLine = buildX;
}
@Override public int process(List<String> cmdArgs) throws MojoExecutionException {
try {
logger.info(String.join(" ", cmdLine));
ProcessBuilder builder = new ProcessBuilder(cmdLine);
logger.info(String.join(" ", cmdArgs));
ProcessBuilder builder = new ProcessBuilder(cmdArgs);
Process process = builder.start();
pumpStream(process.getInputStream());
pumpStream(process.getErrorStream());
return process.waitFor();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw new MojoExecutionException("Interrupted while executing " + cmdLine, ex);
throw new MojoExecutionException("Interrupted while executing " + cmdArgs, ex);
} catch (IOException ex) {
throw new MojoExecutionException("unable to execute " + cmdLine, ex);
throw new MojoExecutionException("unable to execute " + cmdArgs, ex);
}
}

Expand Down
Loading

0 comments on commit 043a569

Please sign in to comment.