diff --git a/pom.xml b/pom.xml
index ab5ad132..6102f7c7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.jenkins-ci.plugins
plugin
- 3.20
+ 3.24
io.jenkins.plugins
@@ -16,10 +16,9 @@
1.2
-SNAPSHOT
- 2.1.1
+ 2.1.1
2.121.3
8
- 2.29
4.0.0-rc2883.3399537b3d44
2.20
true
@@ -44,6 +43,11 @@
+
+ io.jenkins.plugins
+ artifact-manager-jclouds
+ 1.0-SNAPSHOT
+
io.jenkins.plugins
aws-global-configuration
@@ -59,11 +63,6 @@
aws-credentials
1.23
-
- org.jenkins-ci.plugins
- apache-httpcomponents-client-4-api
- 4.5.5-3.0
-
org.mockito
mockito-core
@@ -84,12 +83,7 @@
org.jenkins-ci.plugins.workflow
workflow-api
- ${workflow-api-plugin.version}
-
-
- org.jenkins-ci.plugins.workflow
- workflow-api
- ${workflow-api-plugin.version}
+ 2.29
tests
test
@@ -141,12 +135,6 @@
2.9
test
-
- org.kohsuke.metainf-services
- metainf-services
- 1.7
- test
-
io.findify
s3mock_2.12
@@ -161,6 +149,10 @@
com.amazonaws
aws-java-sdk-s3
+
+ com.google.guava
+ guava
+
@@ -232,13 +224,13 @@
jenkins-core
${jenkins-core.version}
-
+
com.google.guava
guava
-
+
com.google.inject.extensions
guice-assistedinject
4.0
@@ -290,11 +282,13 @@
org.jenkins-ci.tools
maven-hpi-plugin
-
+
com.google.common.
-
diff --git a/src/main/java/io/jenkins/plugins/artifact_manager_jclouds/BlobStoreProvider.java b/src/main/java/io/jenkins/plugins/artifact_manager_jclouds/BlobStoreProvider.java
deleted file mode 100644
index ba154d56..00000000
--- a/src/main/java/io/jenkins/plugins/artifact_manager_jclouds/BlobStoreProvider.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * The MIT License
- *
- * Copyright 2018 CloudBees, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package io.jenkins.plugins.artifact_manager_jclouds;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.net.URI;
-import java.net.URL;
-
-import org.jclouds.blobstore.BlobStoreContext;
-import org.jclouds.blobstore.domain.Blob;
-import org.kohsuke.accmod.Restricted;
-import org.kohsuke.accmod.restrictions.Beta;
-
-import edu.umd.cs.findbugs.annotations.NonNull;
-import hudson.ExtensionPoint;
-import hudson.model.AbstractDescribableImpl;
-
-/**
- * Provider for jclouds-based blob stores usable for artifact storage.
- * An instance will be copied into a build record together with any fields it defines.
- */
-@Restricted(Beta.class)
-public abstract class BlobStoreProvider extends AbstractDescribableImpl implements ExtensionPoint, Serializable {
-
- private static final long serialVersionUID = -861350249543443493L;
-
- public enum HttpMethod {
- GET, PUT;
- }
-
- /** A constant for the blob path prefix to use. */
- @NonNull
- public abstract String getPrefix();
-
- /** A constant for the blob container name to use. */
- @NonNull
- public abstract String getContainer();
-
- /** A constant to define whether we should delete artifacts or leave them to be managed on the blob service side. */
- public abstract boolean isDeleteArtifacts();
-
- /** A constant to define whether we should delete stashes or leave them to be managed on the blob service side. */
- public abstract boolean isDeleteStashes();
-
- /** Creates the jclouds handle for working with blob. */
- @NonNull
- public abstract BlobStoreContext getContext() throws IOException;
-
- /**
- * Get a provider-specific URI.
- *
- * @param container
- * container where this exists.
- * @param key
- * fully qualified name relative to the container.
- * @return the URI
- */
- @NonNull
- public abstract URI toURI(@NonNull String container, @NonNull String key);
-
- /**
- * Generate a URL valid for downloading OR uploading the blob for a limited period of time
- *
- * @param blob
- * blob to generate the URL for
- * @param httpMethod
- * HTTP method to create a URL for (downloads or uploads)
- * @return the URL
- * @throws IOException
- */
- @NonNull
- public abstract URL toExternalURL(@NonNull Blob blob, @NonNull HttpMethod httpMethod) throws IOException;
-
- @Override
- public BlobStoreProviderDescriptor getDescriptor() {
- return (BlobStoreProviderDescriptor) super.getDescriptor();
- }
-
-}
diff --git a/src/main/java/io/jenkins/plugins/artifact_manager_jclouds/BlobStoreProviderDescriptor.java b/src/main/java/io/jenkins/plugins/artifact_manager_jclouds/BlobStoreProviderDescriptor.java
deleted file mode 100644
index 96fbb017..00000000
--- a/src/main/java/io/jenkins/plugins/artifact_manager_jclouds/BlobStoreProviderDescriptor.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * The MIT License
- *
- * Copyright 2018 CloudBees, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package io.jenkins.plugins.artifact_manager_jclouds;
-
-import hudson.model.Descriptor;
-import org.kohsuke.accmod.Restricted;
-import org.kohsuke.accmod.restrictions.Beta;
-
-/**
- * Descriptor type for {@link BlobStoreProvider}.
- */
-@Restricted(Beta.class)
-public abstract class BlobStoreProviderDescriptor extends Descriptor {}
diff --git a/src/main/java/io/jenkins/plugins/artifact_manager_jclouds/JCloudsArtifactManager.java b/src/main/java/io/jenkins/plugins/artifact_manager_jclouds/JCloudsArtifactManager.java
deleted file mode 100644
index e06da2a7..00000000
--- a/src/main/java/io/jenkins/plugins/artifact_manager_jclouds/JCloudsArtifactManager.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * The MIT License
- *
- * Copyright 2018 CloudBees, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package io.jenkins.plugins.artifact_manager_jclouds;
-
-import hudson.AbortException;
-import hudson.EnvVars;
-import hudson.FilePath;
-import hudson.Launcher;
-import hudson.Util;
-import hudson.model.BuildListener;
-import hudson.model.Run;
-import hudson.model.TaskListener;
-import hudson.remoting.VirtualChannel;
-import hudson.slaves.WorkspaceList;
-import hudson.util.DirScanner;
-import hudson.util.io.ArchiverFactory;
-import edu.umd.cs.findbugs.annotations.NonNull;
-import io.jenkins.plugins.artifact_manager_jclouds.BlobStoreProvider.HttpMethod;
-import io.jenkins.plugins.httpclient.RobustHTTPClient;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.URI;
-import java.net.URL;
-import java.nio.file.Files;
-import java.nio.file.InvalidPathException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import jenkins.MasterToSlaveFileCallable;
-import jenkins.model.ArtifactManager;
-import jenkins.util.VirtualFile;
-import org.apache.http.client.methods.HttpGet;
-import org.jclouds.blobstore.BlobStore;
-import org.jclouds.blobstore.BlobStoreContext;
-import org.jclouds.blobstore.BlobStores;
-import org.jclouds.blobstore.domain.Blob;
-import org.jclouds.blobstore.domain.StorageMetadata;
-import org.jclouds.blobstore.options.CopyOptions;
-import org.jclouds.blobstore.options.ListContainerOptions;
-import org.jenkinsci.plugins.workflow.flow.StashManager;
-import org.kohsuke.accmod.Restricted;
-import org.kohsuke.accmod.restrictions.NoExternalUse;
-
-/**
- * Jenkins artifact/stash implementation using any blob store supported by Apache jclouds.
- * To offer a new backend, implement {@link BlobStoreProvider}.
- */
-@Restricted(NoExternalUse.class)
-public final class JCloudsArtifactManager extends ArtifactManager implements StashManager.StashAwareArtifactManager {
-
- private static final Logger LOGGER = Logger.getLogger(JCloudsArtifactManager.class.getName());
-
- static RobustHTTPClient client = new RobustHTTPClient();
-
- private final BlobStoreProvider provider;
-
- private transient String key; // e.g. myorg/myrepo/master/123
-
- JCloudsArtifactManager(@NonNull Run, ?> build, BlobStoreProvider provider) {
- this.provider = provider;
- onLoad(build);
- }
-
- private Object readResolve() {
- if (provider == null) {
- throw new IllegalStateException("Missing provider field");
- }
- return this;
- }
-
- @Override
- public void onLoad(@NonNull Run, ?> build) {
- this.key = String.format("%s/%s", build.getParent().getFullName(), build.getNumber());
- }
-
- private String getBlobPath(String path) {
- return getBlobPath(key, path);
- }
-
- private String getBlobPath(String key, String path) {
- return String.format("%s%s/%s", provider.getPrefix(), key, path);
- }
-
- /*
- * This could be called multiple times
- */
- @Override
- public void archive(FilePath workspace, Launcher launcher, BuildListener listener, Map artifacts)
- throws IOException, InterruptedException {
- LOGGER.log(Level.FINE, "Archiving from {0}: {1}", new Object[] { workspace, artifacts });
- Map artifactUrls = new HashMap<>();
- BlobStore blobStore = getContext().getBlobStore();
-
- // Map artifacts to urls for upload
- for (Map.Entry entry : artifacts.entrySet()) {
- String path = "artifacts/" + entry.getKey();
- String blobPath = getBlobPath(path);
- Blob blob = blobStore.blobBuilder(blobPath).build();
- blob.getMetadata().setContainer(provider.getContainer());
- artifactUrls.put(entry.getValue(), provider.toExternalURL(blob, HttpMethod.PUT));
- }
-
- workspace.act(new UploadToBlobStorage(artifactUrls, listener));
- listener.getLogger().printf("Uploaded %s artifact(s) to %s%n", artifactUrls.size(), provider.toURI(provider.getContainer(), getBlobPath("artifacts/")));
- }
-
- private static class UploadToBlobStorage extends MasterToSlaveFileCallable {
- private static final long serialVersionUID = 1L;
-
- private final Map artifactUrls; // e.g. "target/x.war", "http://..."
- private final TaskListener listener;
- // Bind when constructed on the master side; on the agent side, deserialize the same configuration.
- private final RobustHTTPClient client = JCloudsArtifactManager.client;
-
- UploadToBlobStorage(Map artifactUrls, TaskListener listener) {
- this.artifactUrls = artifactUrls;
- this.listener = listener;
- }
-
- @Override
- public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
- for (Map.Entry entry : artifactUrls.entrySet()) {
- client.uploadFile(new File(f, entry.getKey()), entry.getValue(), listener);
- }
- return null;
- }
- }
-
- @Override
- public boolean delete() throws IOException, InterruptedException {
- String blobPath = getBlobPath("");
- if (!provider.isDeleteArtifacts()) {
- LOGGER.log(Level.FINE, "Ignoring blob deletion: {0}", blobPath);
- return false;
- }
- return JCloudsVirtualFile.delete(provider, getContext().getBlobStore(), blobPath);
- }
-
- @Override
- public VirtualFile root() {
- return new JCloudsVirtualFile(provider, provider.getContainer(), getBlobPath("artifacts"));
- }
-
- @Override
- public void stash(String name, FilePath workspace, Launcher launcher, EnvVars env, TaskListener listener, String includes, String excludes, boolean useDefaultExcludes, boolean allowEmpty) throws IOException, InterruptedException {
- BlobStore blobStore = getContext().getBlobStore();
-
- // Map stash to url for upload
- String path = getBlobPath("stashes/" + name + ".tgz");
- Blob blob = blobStore.blobBuilder(path).build();
- blob.getMetadata().setContainer(provider.getContainer());
- URL url = provider.toExternalURL(blob, HttpMethod.PUT);
- workspace.act(new Stash(url, provider.toURI(provider.getContainer(), path), includes, excludes, useDefaultExcludes, allowEmpty, WorkspaceList.tempDir(workspace).getRemote(), listener));
- }
-
- private static final class Stash extends MasterToSlaveFileCallable {
- private static final long serialVersionUID = 1L;
- private final URL url;
- private final URI uri;
- private final String includes, excludes;
- private final boolean useDefaultExcludes;
- private final boolean allowEmpty;
- private final String tempDir;
- private final TaskListener listener;
- private final RobustHTTPClient client = JCloudsArtifactManager.client;
-
- Stash(URL url, URI uri, String includes, String excludes, boolean useDefaultExcludes, boolean allowEmpty, String tempDir, TaskListener listener) throws IOException {
- /** Actual destination as a presigned URL. */
- this.url = url;
- /** Logical location for display purposes only. */
- this.uri = uri;
- this.includes = includes;
- this.excludes = excludes;
- this.useDefaultExcludes = useDefaultExcludes;
- this.allowEmpty = allowEmpty;
- this.tempDir = tempDir;
- this.listener = listener;
- }
-
- @Override
- public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
- // TODO use streaming upload rather than a temp file; is it necessary to set the content length in advance?
- // (we prefer not to upload individual files for stashes, so as to preserve symlinks & file permissions, as StashManager’s default does)
- Path tempDirP = Paths.get(tempDir);
- Files.createDirectories(tempDirP);
- Path tmp = Files.createTempFile(tempDirP, "stash", ".tgz");
- try {
- int count;
- try (OutputStream os = Files.newOutputStream(tmp)) {
- count = new FilePath(f).archive(ArchiverFactory.TARGZ, os, new DirScanner.Glob(Util.fixEmpty(includes) == null ? "**" : includes, excludes, useDefaultExcludes));
- } catch (InvalidPathException e) {
- throw new IOException(e);
- }
- if (count == 0 && !allowEmpty) {
- throw new AbortException("No files included in stash");
- }
- client.uploadFile(tmp.toFile(), url, listener);
- listener.getLogger().printf("Stashed %d file(s) to %s%n", count, uri);
- return null;
- } finally {
- Files.delete(tmp);
- }
- }
- }
-
- @Override
- public void unstash(String name, FilePath workspace, Launcher launcher, EnvVars env, TaskListener listener) throws IOException, InterruptedException {
- BlobStore blobStore = getContext().getBlobStore();
-
- // Map stash to url for download
- String blobPath = getBlobPath("stashes/" + name + ".tgz");
- Blob blob = blobStore.getBlob(provider.getContainer(), blobPath);
- if (blob == null) {
- throw new AbortException(
- String.format("No such saved stash ‘%s’ found at %s/%s", name, provider.getContainer(), blobPath));
- }
- URL url = provider.toExternalURL(blob, HttpMethod.GET);
- workspace.act(new Unstash(url, listener));
- listener.getLogger().printf("Unstashed file(s) from %s%n", provider.toURI(provider.getContainer(), blobPath));
- }
-
- private static final class Unstash extends MasterToSlaveFileCallable {
- private static final long serialVersionUID = 1L;
- private final URL url;
- private final TaskListener listener;
- private final RobustHTTPClient client = JCloudsArtifactManager.client;
-
- Unstash(URL url, TaskListener listener) throws IOException {
- this.url = url;
- this.listener = listener;
- }
-
- @Override
- public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
- client.connect("download", "download " + RobustHTTPClient.sanitize(url) + " into " + f, c -> c.execute(new HttpGet(url.toString())), response -> {
- try (InputStream is = response.getEntity().getContent()) {
- new FilePath(f).untarFrom(is, FilePath.TarCompression.GZIP);
- // Note that this API currently offers no count of files in the tarball we could report.
- }
- }, listener);
- return null;
- }
- }
-
- @Override
- public void clearAllStashes(TaskListener listener) throws IOException, InterruptedException {
- String stashPrefix = getBlobPath("stashes/");
-
- if (!provider.isDeleteStashes()) {
- LOGGER.log(Level.FINE, "Ignoring stash deletion: {0}", stashPrefix);
- return;
- }
-
- BlobStore blobStore = getContext().getBlobStore();
- int count = 0;
- try {
- for (StorageMetadata sm : BlobStores.listAll(blobStore, provider.getContainer(), ListContainerOptions.Builder.prefix(stashPrefix).recursive())) {
- String path = sm.getName();
- assert path.startsWith(stashPrefix);
- LOGGER.fine("deleting " + path);
- blobStore.removeBlob(provider.getContainer(), path);
- count++;
- }
- } catch (RuntimeException x) {
- throw new IOException(x);
- }
- listener.getLogger().printf("Deleted %d stash(es) from %s%n", count, provider.toURI(provider.getContainer(), stashPrefix));
- }
-
- @Override
- public void copyAllArtifactsAndStashes(Run, ?> to, TaskListener listener) throws IOException, InterruptedException {
- ArtifactManager am = to.pickArtifactManager();
- if (!(am instanceof JCloudsArtifactManager)) {
- throw new AbortException("Cannot copy artifacts and stashes to " + to + " using " + am.getClass().getName());
- }
- JCloudsArtifactManager dest = (JCloudsArtifactManager) am;
- String allPrefix = getBlobPath("");
- BlobStore blobStore = getContext().getBlobStore();
- int count = 0;
- try {
- for (StorageMetadata sm : BlobStores.listAll(blobStore, provider.getContainer(), ListContainerOptions.Builder.prefix(allPrefix).recursive())) {
- String path = sm.getName();
- assert path.startsWith(allPrefix);
- String destPath = getBlobPath(dest.key, path.substring(allPrefix.length()));
- LOGGER.fine("copying " + path + " to " + destPath);
- blobStore.copyBlob(provider.getContainer(), path, provider.getContainer(), destPath, CopyOptions.NONE);
- count++;
- }
- } catch (RuntimeException x) {
- throw new IOException(x);
- }
- listener.getLogger().printf("Copied %d artifact(s)/stash(es) from %s to %s%n", count, provider.toURI(provider.getContainer(), allPrefix), provider.toURI(provider.getContainer(), dest.getBlobPath("")));
- }
-
- private BlobStoreContext getContext() throws IOException {
- return provider.getContext();
- }
-
-}
diff --git a/src/main/java/io/jenkins/plugins/artifact_manager_jclouds/JCloudsArtifactManagerFactory.java b/src/main/java/io/jenkins/plugins/artifact_manager_jclouds/JCloudsArtifactManagerFactory.java
deleted file mode 100644
index 7dfed389..00000000
--- a/src/main/java/io/jenkins/plugins/artifact_manager_jclouds/JCloudsArtifactManagerFactory.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * The MIT License
- *
- * Copyright 2018 CloudBees, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package io.jenkins.plugins.artifact_manager_jclouds;
-
-import hudson.Extension;
-import hudson.model.Run;
-import jenkins.model.ArtifactManager;
-import jenkins.model.ArtifactManagerFactory;
-import jenkins.model.ArtifactManagerFactoryDescriptor;
-import org.jenkinsci.Symbol;
-import org.kohsuke.accmod.Restricted;
-import org.kohsuke.accmod.restrictions.NoExternalUse;
-import org.kohsuke.stapler.DataBoundConstructor;
-
-/**
- * Factory for {@link ArtifactManager}
- */
-@Restricted(NoExternalUse.class)
-public class JCloudsArtifactManagerFactory extends ArtifactManagerFactory {
-
- private final BlobStoreProvider provider;
-
- @DataBoundConstructor
- public JCloudsArtifactManagerFactory(BlobStoreProvider provider) {
- if (provider == null) {
- throw new IllegalArgumentException();
- }
- this.provider = provider;
- }
-
- private Object readResolve() {
- if (provider == null) {
- throw new IllegalStateException("Missing provider field");
- }
- return this;
- }
-
- public BlobStoreProvider getProvider() {
- return provider;
- }
-
- @Override
- public ArtifactManager managerFor(Run, ?> build) {
- return new JCloudsArtifactManager(build, provider);
- }
-
- @Symbol("jclouds")
- @Extension
- public static final class DescriptorImpl extends ArtifactManagerFactoryDescriptor {
-
- @Override
- public String getDisplayName() {
- return "Cloud Artifact Storage";
- }
-
- }
-
-}
diff --git a/src/main/java/io/jenkins/plugins/artifact_manager_jclouds/JCloudsVirtualFile.java b/src/main/java/io/jenkins/plugins/artifact_manager_jclouds/JCloudsVirtualFile.java
deleted file mode 100644
index 00dce6e2..00000000
--- a/src/main/java/io/jenkins/plugins/artifact_manager_jclouds/JCloudsVirtualFile.java
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * The MIT License
- *
- * Copyright 2018 CloudBees, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package io.jenkins.plugins.artifact_manager_jclouds;
-
-import static org.jclouds.blobstore.options.ListContainerOptions.Builder.*;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URL;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.stream.StreamSupport;
-
-import org.jclouds.blobstore.BlobStore;
-import org.jclouds.blobstore.BlobStoreContext;
-import org.jclouds.blobstore.BlobStores;
-import org.jclouds.blobstore.domain.Blob;
-import org.jclouds.blobstore.domain.MutableBlobMetadata;
-import org.jclouds.blobstore.domain.StorageMetadata;
-import org.jclouds.blobstore.options.ListContainerOptions;
-import org.jclouds.rest.AuthorizationException;
-import org.kohsuke.accmod.Restricted;
-import org.kohsuke.accmod.restrictions.NoExternalUse;
-
-import edu.umd.cs.findbugs.annotations.CheckForNull;
-import edu.umd.cs.findbugs.annotations.NonNull;
-import hudson.AbortException;
-import hudson.remoting.Callable;
-import io.jenkins.plugins.artifact_manager_jclouds.BlobStoreProvider.HttpMethod;
-import jenkins.util.VirtualFile;
-
-/**
- * JClouds BlobStore Guide
- */
-@Restricted(NoExternalUse.class)
-public class JCloudsVirtualFile extends VirtualFile {
-
- private static final long serialVersionUID = -5126878907895121335L;
-
- private static final Logger LOGGER = Logger.getLogger(JCloudsVirtualFile.class.getName());
-
- @NonNull
- private BlobStoreProvider provider;
- @NonNull
- private final String container;
- @NonNull
- private final String key;
- @CheckForNull
- private transient Blob blob;
- @CheckForNull
- private transient BlobStoreContext context;
-
- public JCloudsVirtualFile(@NonNull BlobStoreProvider provider, @NonNull String container, @NonNull String key) {
- this.provider = provider;
- this.container = container;
- this.key = key;
- assert !key.isEmpty();
- assert !key.startsWith("/");
- assert !key.endsWith("/");
- }
-
- private JCloudsVirtualFile(@NonNull JCloudsVirtualFile related, @NonNull String key) {
- this(related.provider, related.container, key);
- context = related.context;
- }
-
- /**
- * Build jclouds blob context that is the base for all operations
- */
- @Restricted(NoExternalUse.class) // testing only
- BlobStoreContext getContext() throws IOException {
- if (context == null) {
- context = provider.getContext();
- }
- return context;
- }
-
- private String getContainer() {
- return container;
- }
-
- /**
- * Returns the full name, directories included
- */
- private String getKey() {
- return key;
- }
-
- /**
- * Returns the base name
- */
- @Override
- public String getName() {
- return key.replaceFirst(".+/", "");
- }
-
- private Blob getBlob() throws IOException {
- if (blob == null) {
- LOGGER.log(Level.FINE, "checking for existence of blob {0} / {1}", new Object[] {container, key});
- blob = getContext().getBlobStore().getBlob(getContainer(), getKey());
- if (blob == null) {
- blob = getContext().getBlobStore().blobBuilder(getKey()).build();
- blob.getMetadata().setContainer(getContainer());
- }
- }
- return blob;
- }
-
- @Override
- public URI toURI() {
- return provider.toURI(container, key);
- }
-
- @Override
- public URL toExternalURL() throws IOException {
- return provider.toExternalURL(getBlob(), HttpMethod.GET);
- }
-
- @Override
- public VirtualFile getParent() {
- // undefined to go outside …/artifacts
- return new JCloudsVirtualFile(this, key.replaceFirst("/[^/]+$", ""));
- }
-
- @Override
- public boolean isDirectory() throws IOException {
- String keyS = key + "/";
- CacheFrame frame = findCacheFrame(keyS);
- if (frame != null) {
- LOGGER.log(Level.FINER, "cache hit on directory status of {0} / {1}", new Object[] {container, key});
- String relSlash = keyS.substring(frame.root.length()); // "" or "sub/dir/"
- return frame.children.keySet().stream().anyMatch(f -> f.startsWith(relSlash));
- }
- LOGGER.log(Level.FINE, "checking directory status {0} / {1}", new Object[] {container, key});
- return !getContext().getBlobStore().list(getContainer(), prefix(key + "/")).isEmpty();
- }
-
- @Override
- public boolean isFile() throws IOException {
- CacheFrame frame = findCacheFrame(key);
- if (frame != null) {
- String rel = key.substring(frame.root.length());
- CachedMetadata metadata = frame.children.get(rel);
- LOGGER.log(Level.FINER, "cache hit on file status of {0} / {1}", new Object[] {container, key});
- return metadata != null;
- }
- LOGGER.log(Level.FINE, "checking file status {0} / {1}", new Object[] {container, key});
- return getBlob().getMetadata().getSize() != null;
- }
-
- @Override
- public boolean exists() throws IOException {
- return isDirectory() || isFile();
- }
-
- /**
- * List all the blobs under this one
- *
- * @return some blobs
- * @throws RuntimeException either now or when the stream is processed; wrap in {@link IOException} if desired
- */
- private Iterable listStorageMetadata(boolean recursive) throws IOException {
- ListContainerOptions options = prefix(key + "/");
- if (recursive) {
- options.recursive();
- }
- return BlobStores.listAll(getContext().getBlobStore(), getContainer(), options);
- }
-
- @Override
- public VirtualFile[] list() throws IOException {
- String keyS = key + "/";
- CacheFrame frame = findCacheFrame(keyS);
- if (frame != null) {
- LOGGER.log(Level.FINER, "cache hit on listing of {0} / {1}", new Object[] {container, key});
- String relSlash = keyS.substring(frame.root.length()); // "" or "sub/dir/"
- return frame.children.keySet().stream(). // filenames relative to frame root
- filter(f -> f.startsWith(relSlash)). // those inside this dir
- map(f -> f.substring(relSlash.length()).replaceFirst("/.+", "")). // just the file simple name, or direct subdir name
- distinct(). // ignore duplicates if have multiple files under one direct subdir
- map(simple -> new JCloudsVirtualFile(this, keyS + simple)). // direct children
- toArray(VirtualFile[]::new);
- }
- VirtualFile[] list;
- try {
- list = StreamSupport.stream(listStorageMetadata(false).spliterator(), false)
- .map(meta -> new JCloudsVirtualFile(this, meta.getName().replaceFirst("/$", "")))
- .toArray(VirtualFile[]::new);
- } catch (RuntimeException x) {
- throw new IOException(x);
- }
- LOGGER.log(Level.FINEST, "Listing files from {0} {1}: {2}",
- new String[] { getContainer(), getKey(), Arrays.toString(list) });
- return list;
- }
-
- @Override
- public VirtualFile child(String name) {
- return new JCloudsVirtualFile(this, key + "/" + name);
- }
-
- @Override
- public long length() throws IOException {
- CacheFrame frame = findCacheFrame(key);
- if (frame != null) {
- String rel = key.substring(frame.root.length());
- CachedMetadata metadata = frame.children.get(rel);
- LOGGER.log(Level.FINER, "cache hit on length of {0} / {1}", new Object[] {container, key});
- return metadata != null ? metadata.length : 0;
- }
- LOGGER.log(Level.FINE, "checking length {0} / {1}", new Object[] {container, key});
- MutableBlobMetadata metadata = getBlob().getMetadata();
- Long size = metadata == null ? Long.valueOf(0) : metadata.getSize();
- return size == null ? 0 : size;
- }
-
- @Override
- public long lastModified() throws IOException {
- CacheFrame frame = findCacheFrame(key);
- if (frame != null) {
- String rel = key.substring(frame.root.length());
- CachedMetadata metadata = frame.children.get(rel);
- LOGGER.log(Level.FINER, "cache hit on lastModified of {0} / {1}", new Object[] {container, key});
- return metadata != null ? metadata.lastModified : 0;
- }
- LOGGER.log(Level.FINE, "checking modification time {0} / {1}", new Object[] {container, key});
- MutableBlobMetadata metadata = getBlob().getMetadata();
- return metadata == null || metadata.getLastModified() == null ? 0 : metadata.getLastModified().getTime();
- }
-
- @Override
- public boolean canRead() throws IOException {
- return true;
- }
-
- @Override
- public InputStream open() throws IOException {
- LOGGER.log(Level.FINE, "reading {0} / {1}", new Object[] {container, key});
- if (isDirectory()) {
- // That is what java.io.FileInputStream.open throws
- throw new FileNotFoundException(String.format("%s/%s (Is a directory)", getContainer(), getKey()));
- }
- if (!isFile()) {
- throw new FileNotFoundException(
- String.format("%s/%s (No such file or directory)", getContainer(), getKey()));
- }
- return getBlob().getPayload().openStream();
- }
-
- /**
- * Cache of metadata collected during {@link #run}.
- * Keys are {@link #container}.
- * Values are a stack of cache frames, one per nested {@link #run} call.
- */
- private static final ThreadLocal