All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hubspot.singularity.executor.task.SingularityExecutorArtifactVerifier Maven / Gradle / Ivy

The newest version!
package com.hubspot.singularity.executor.task;

import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
import com.hubspot.deploy.S3Artifact;
import com.hubspot.deploy.S3ArtifactSignature;
import com.hubspot.singularity.executor.config.SingularityExecutorConfiguration;
import com.hubspot.singularity.s3.base.config.SingularityS3Configuration;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.slf4j.Logger;

public class SingularityExecutorArtifactVerifier {
  private final Logger log;
  private final SingularityExecutorConfiguration executorConfiguration;
  private final SingularityExecutorTaskDefinition taskDefinition;
  private final SingularityS3Configuration s3Configuration;

  public SingularityExecutorArtifactVerifier(
    SingularityExecutorTaskDefinition taskDefinition,
    Logger log,
    SingularityExecutorConfiguration executorConfiguration,
    SingularityS3Configuration s3Configuration
  ) {
    this.log = log;
    this.executorConfiguration = executorConfiguration;
    this.taskDefinition = taskDefinition;
    this.s3Configuration = s3Configuration;
  }

  public void checkSignatures(
    List s3Artifacts,
    List s3ArtifactsWithSignatures
  ) {
    Map artifactsByFilename = s3Artifacts
      .stream()
      .collect(
        Collectors.toMap(
          S3Artifact::getFilename,
          Function.identity(),
          (lhsDuplicate, rhsDuplicate) -> {
            log.warn(
              "Duplicate artifact filenames found ({}; {})",
              lhsDuplicate,
              rhsDuplicate
            );
            return lhsDuplicate;
          }
        )
      );
    Map signaturesByFilename = s3ArtifactsWithSignatures
      .stream()
      .collect(
        Collectors.toMap(
          S3ArtifactSignature::getArtifactFilename,
          Function.identity(),
          (lhsDuplicate, rhsDuplicate) -> {
            log.warn(
              "Duplicate signature filenames found ({}; {})",
              lhsDuplicate,
              rhsDuplicate
            );
            return lhsDuplicate;
          }
        )
      );

    SetView signaturesMissingArtifacts = Sets.difference(
      signaturesByFilename.keySet(),
      artifactsByFilename.keySet()
    );

    if (signaturesMissingArtifacts.size() > 0) {
      if (executorConfiguration.isFailOnSignatureWithNoMatchingArtifact()) {
        throw new ArtifactVerificationException(
          String.format(
            "No matching artifact(s) found for signature(s) %s",
            signaturesMissingArtifacts
          )
        );
      } else {
        log.warn(
          "No matching artifact(s) found for signature(s) {}",
          signaturesMissingArtifacts
        );
      }
    }

    SetView artifactsMissingSignatures = Sets.difference(
      artifactsByFilename.keySet(),
      signaturesByFilename.keySet()
    );
    if (artifactsMissingSignatures.size() > 0) {
      if (executorConfiguration.isFailOnArtifactWithNoMatchingSignature()) {
        throw new ArtifactVerificationException(
          String.format(
            "No signature(s) found for artifact(s) %s",
            artifactsMissingSignatures
          )
        );
      } else {
        log.warn("No signature(s) found for artifact(s) {}", artifactsMissingSignatures);
      }
    }

    if (s3ArtifactsWithSignatures.isEmpty()) {
      log.info(
        "No files containing artifact signatures specified, skipping verification."
      );
      return;
    }

    for (S3ArtifactSignature s3ArtifactSignature : s3ArtifactsWithSignatures) {
      S3Artifact maybeMatchingForSignature = artifactsByFilename.get(
        s3ArtifactSignature.getArtifactFilename()
      );
      if (maybeMatchingForSignature != null) {
        checkArtifactSignature(maybeMatchingForSignature, s3ArtifactSignature);
      }
    }
  }

  private void checkArtifactSignature(
    S3Artifact s3Artifact,
    S3ArtifactSignature s3ArtifactSignature
  ) {
    final Path artifactPath = Paths.get(
      s3Configuration.getArtifactCacheDirectory(),
      s3Artifact.getFilenameForCache()
    );
    final Path artifactSignaturePath = Paths.get(
      s3Configuration.getArtifactCacheDirectory(),
      s3ArtifactSignature.getFilenameForCache()
    );

    if (!Files.exists(artifactPath)) {
      log.warn(
        "Artifact {} not found for signature {}",
        artifactPath,
        s3ArtifactSignature
      );
      return;
    }

    final List verifyCommand = new ArrayList<>(
      executorConfiguration.getArtifactSignatureVerificationCommand().size()
    );

    for (String arg : executorConfiguration.getArtifactSignatureVerificationCommand()) {
      verifyCommand.add(
        arg
          .replace("{artifactPath}", artifactPath.toString())
          .replace("{artifactSignaturePath}", artifactSignaturePath.toString())
      );
    }

    try {
      final ProcessBuilder processBuilder = new ProcessBuilder(verifyCommand);

      processBuilder.directory(taskDefinition.getTaskDirectoryPath().toFile());

      processBuilder.redirectError(taskDefinition.getSignatureVerifyOutPath().toFile());
      processBuilder.redirectOutput(taskDefinition.getSignatureVerifyOutPath().toFile());

      final Process p = processBuilder.start();

      p.waitFor(); // TODO: add some sort of timeout?

      if (p.exitValue() != 0) {
        log.error(
          "Failed to validate signature in file {} for artifact file {}",
          s3ArtifactSignature.getFilename(),
          s3ArtifactSignature.getArtifactFilename()
        );

        if (executorConfiguration.isFailTaskOnInvalidArtifactSignature()) {
          throw new ArtifactVerificationException(
            String.format("Failed to validate signature for artifact %s", artifactPath)
          );
        }
      } else {
        log.info(
          "Signature in {} for artifact {} is valid!",
          s3ArtifactSignature.getFilename(),
          s3ArtifactSignature.getArtifactFilename()
        );
      }
    } catch (InterruptedException | IOException e) {
      throw new RuntimeException(e);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy