org.metaeffekt.artifact.resolver.alpine.AlpineDownloaderAdapter Maven / Gradle / Ivy
/*
* Copyright 2021-2024 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
*
* http://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.metaeffekt.artifact.resolver.alpine;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.metaeffekt.artifact.resolver.ArtifactResolverConfig;
import org.metaeffekt.artifact.resolver.MarkerFileStatus;
import org.metaeffekt.artifact.resolver.ResolverResult;
import org.metaeffekt.artifact.resolver.download.WebAccess;
import org.metaeffekt.artifact.resolver.generic.AbstractDownloadingAdapter;
import org.metaeffekt.artifact.resolver.generic.FileLocation;
import org.metaeffekt.artifact.resolver.manager.DownloadEnvironmentManager;
import org.metaeffekt.artifact.resolver.manager.execenv.StableExecutionEnvironment;
import org.metaeffekt.artifact.resolver.manager.execenv.exception.EnvironmentInitializationFailure;
import org.metaeffekt.artifact.resolver.manager.execenv.exception.EnvironmentOperationFailure;
import org.metaeffekt.artifact.resolver.model.ArtifactPartResolver;
import org.metaeffekt.artifact.resolver.model.DownloadLocation;
import org.metaeffekt.artifact.resolver.model.ResolvedArtifactPart;
import org.metaeffekt.core.inventory.processor.model.Artifact;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import static org.metaeffekt.artifact.resolver.model.ArtifactPartType.SOURCE_ARCHIVE;
@Slf4j
public class AlpineDownloaderAdapter extends AbstractDownloadingAdapter {
private final DownloadEnvironmentManager environmentManager;
private final ArtifactResolverConfig resolverConfig;
public AlpineDownloaderAdapter(@NonNull DownloadLocation downloadLocation,
@NonNull WebAccess webAccess,
@NonNull DownloadEnvironmentManager environmentManager,
@NonNull ArtifactResolverConfig resolverConfig) {
super(downloadLocation, webAccess);
this.environmentManager = environmentManager;
this.resolverConfig = resolverConfig;
}
public ArtifactPartResolver downloadSourceArchive(Artifact artifact, @NonNull AlpinePackageReference reference) {
// pre-compute params since they are properly final
final AlpineEnvironmentParameters params = new AlpineEnvironmentParameters(getWebAccess().getProxyConfig(),
"v" + reference.getAlpineVersion());
return new ArtifactPartResolver(artifact, SOURCE_ARCHIVE,
// supplier
() -> resolve(params, reference),
// enricher
rap -> enrich(rap)
);
}
public ResolverResult resolve(AlpineEnvironmentParameters environmentParameters, AlpinePackageReference reference) {
final FileLocation fileLocation = reference.deriveFileLocation();
// where to put the downloaded file
final File outputFile = deriveDownloadFile(fileLocation);
final MarkerFileStatus markerFileStatus = handleMarkerFile(fileLocation, reference);
// skip attempt if marker status indicates 'skip'
if (markerFileStatus.isIndicateSkip()) return null;
// if the marker is valid and the output file exists; return with the existing file
if (!markerFileStatus.isInvalidMarker() && outputFile.exists()) {
return resolve(outputFile, null);
}
// if we got here. we reattempt download.
log.trace("Attempting download for reference [{}].", reference);
// delete existing marker file
final File markerFile = markerFileStatus.getMarkerFile();
if (markerFile.exists() && !markerFile.delete()) {
log.warn("Could not delete existing marker [{}]. Ignoring.", markerFile);
}
// recreate marker file: marks download attempt and signifies failure if the marker remains empty
final File markerParentFile = markerFile.getParentFile();
if (!markerParentFile.exists() && !markerParentFile.mkdirs()) {
log.warn("Could neither find nor create marker directory [{}]. Ignoring.", markerParentFile.getAbsolutePath());
}
// FIXME: we should provide this a method in FileUtils; incredibly verbose for a simple operation
try {
if (!markerFile.createNewFile()) {
log.warn("Could not create new marker [{}] for reference [{}]. Ignoring.", markerFile, reference);
}
} catch (IOException e) {
log.warn("Could not create marker [{}] for reference [{}]. Ignoring.", markerFile, reference);
}
// attempt download/resolve
String errorMessage = null;
try {
final StableExecutionEnvironment execEnv;
try {
execEnv = environmentManager.getEnvironment(environmentParameters);
} catch (EnvironmentInitializationFailure e) {
throw new IllegalStateException("Could not get working environment.", e);
}
// sanity check returned environment
if (!(execEnv instanceof AlpineEnvironment)) {
log.error("Environment lookup for alpine returned object [{}] instead of an alpine env.", execEnv.getClass().getName());
return null;
}
((AlpineEnvironment) execEnv).getSourceArchive(reference, outputFile.toPath(), resolverConfig);
// the download appears to have succeeded if we got here, fill the marker file
writeMarkerFile(reference, markerFile, outputFile);
} catch (EnvironmentOperationFailure e) {
errorMessage = "Could not load package source archive.";
log.error("Operation on package [{}] failed.", reference, e);
log.error(ExceptionUtils.getStackTrace(e));
}
return resolve(outputFile, errorMessage);
}
private static void writeMarkerFile(AlpinePackageReference reference, File markerFile, File outputFile) {
try {
Files.write(markerFile.toPath(), outputFile.getAbsolutePath().getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
log.warn("Could not write to marker [{}] for ref [{}].", markerFile, reference);
}
}
private Artifact enrich(ResolvedArtifactPart resolvedArtifactPart) {
// the resolved part needs this info set to be of any use
final Artifact enrichedArtifact = new Artifact(resolvedArtifactPart.getOriginalArtifact());
final File resolvedFile = resolvedArtifactPart.getResolvedFile();
if (resolvedFile != null) {
enrichedArtifact.set(SOURCE_ARCHIVE.modulatePathAttribute(), resolvedFile.getAbsolutePath());
}
return enrichedArtifact;
}
private MarkerFileStatus handleMarkerFile(FileLocation fileLocation, AlpinePackageReference reference) {
final File markerFile = deriveMarkerFile(fileLocation,
reference.getPkgname() + "-" + reference.getPkgver() + "-" + reference.getPkgrel());
if (markerFile.exists()) {
log.trace("Marker file for [{}] exists.", reference);
try {
long markerSize = Files.size(markerFile.toPath());
if (markerSize <= 0) {
return new MarkerFileStatus(markerFile, null, false, true);
}
} catch (IOException e) {
log.warn("Error getting size of marker file [{}].", markerFile);
return new MarkerFileStatus(markerFile, null, false, true);
}
} else {
return new MarkerFileStatus(markerFile, null, false, false);
}
// a marker file with content exists; there was a previous attempt
// check whether a downloaded file has been included in the marker file
try (InputStream inputStream = Files.newInputStream(markerFile.toPath())) {
final String pathFromMarker = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
final File referencedFile = new File(pathFromMarker);
// sanity check previous output
if (referencedFile.exists()) {
if (!Files.isRegularFile(referencedFile.toPath()) || Files.size(referencedFile.toPath()) <= 0) {
// the previous output is likely invalid: when might an empty archive appear?
log.warn("Previous referenced file [{}] by marker [{}] for package [{}] failed the sanity checks.",
referencedFile, markerFile, reference);
return new MarkerFileStatus(markerFile, null, false, true);
} else {
// all appears fine with the marker; deliver the reference file if it exists
if (referencedFile.exists()) {
return new MarkerFileStatus(markerFile, referencedFile, false, false);
}
}
}
} catch (IOException e) {
log.error("Unable to read output path from marker file [{}].", markerFile);
return new MarkerFileStatus(markerFile, null, false, true);
}
// check whether a previous attempts failed recently.
if (hasRecentFailedDownloadAttempt(markerFile)) {
// download failed recently: we lazily skip it for now, considering it likely to fail again.
log.info("Skipping failed download [{}]. Recent attempt failed for package: [{}].", reference, reference);
return new MarkerFileStatus(markerFile, null, true, false);
}
// in any other cases return a status indicating to attempt another download
return new MarkerFileStatus(markerFile, null, false, false);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy