From 2706618588f49975a0d024a88a647bcd78ea4fa5 Mon Sep 17 00:00:00 2001 From: James Nord Date: Wed, 10 Jun 2015 17:41:57 +0100 Subject: [PATCH 01/14] this is not a tag --- pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.xml b/pom.xml index f03f01f..28460a8 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,6 @@ scm:git:ssh://github.com/jenkinsci/unique-id-plugin.git scm:git:ssh://git@github.com/jenkinsci/unique-id-plugin.git https://github.com/jenkinsci/unique-id-plugin - unique-id-1.2 From 18da63602c63640687f08e88a3c083ca5ac89c14 Mon Sep 17 00:00:00 2001 From: James Nord Date: Wed, 10 Jun 2015 17:42:40 +0100 Subject: [PATCH 02/14] update to maven-release-plugin 2.5.1. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 28460a8..e025512 100644 --- a/pom.xml +++ b/pom.xml @@ -47,7 +47,7 @@ maven-release-plugin - 2.4 + 2.5.1 From 19e747428bf515d0fbb64ff2044e3c60356fb7c0 Mon Sep 17 00:00:00 2001 From: James Nord Date: Thu, 11 Jun 2015 00:48:01 +0100 Subject: [PATCH 03/14] add .gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8801278 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target/ +/.classpath +/.project +/.settings \ No newline at end of file From b5b5f08cd05556814b9651a135d06b14539eb6a6 Mon Sep 17 00:00:00 2001 From: James Nord Date: Thu, 11 Jun 2015 04:32:26 +0100 Subject: [PATCH 04/14] [JENKINS-28843] Initial implementation of ID migrator. --- pom.xml | 31 +++++ .../jenkinsci/plugins/uniqueid/IdStore.java | 43 ++++++- .../plugins/uniqueid/impl/FolderIdStore.java | 34 ++++-- .../jenkinsci/plugins/uniqueid/impl/Id.java | 13 ++ .../uniqueid/impl/IdStoreMigratorV1ToV2.java | 113 ++++++++++++++++++ .../plugins/uniqueid/impl/JobIdStore.java | 23 ++-- .../plugins/uniqueid/impl/LegacyIdStore.java | 101 ++++++++++++++++ .../plugins/uniqueid/impl/RunIdStore.java | 34 +++--- .../implv2/PersistenceRootIdStore.java | 71 +++++++++++ .../plugins/uniqueid/implv2/RunIdStore.java | 45 +++++++ .../impl/IdStoreMigratorV1ToV2Test.java | 87 ++++++++++++++ .../__testMigration.zip | Bin 0 -> 27739 bytes .../testMigration.zip | Bin 0 -> 14274 bytes 13 files changed, 552 insertions(+), 43 deletions(-) create mode 100644 src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java create mode 100644 src/main/java/org/jenkinsci/plugins/uniqueid/impl/LegacyIdStore.java create mode 100644 src/main/java/org/jenkinsci/plugins/uniqueid/implv2/PersistenceRootIdStore.java create mode 100644 src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java create mode 100644 src/test/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test.java create mode 100644 src/test/resources/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test/__testMigration.zip create mode 100644 src/test/resources/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test/testMigration.zip diff --git a/pom.xml b/pom.xml index e025512..2b24690 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,11 @@ https://github.com/jenkinsci/unique-id-plugin + + 1.6 + 1.6 + + org.jenkins-ci.plugins @@ -26,6 +31,24 @@ 4.0 true + + org.jenkins-ci + test-annotations + 1.2 + test + + + junit + junit + 4.12 + test + + + org.hamcrest + hamcrest-core + 1.3 + test + @@ -49,6 +72,14 @@ maven-release-plugin 2.5.1 + + org.jenkins-ci.tools + maven-hpi-plugin + true + + 1.3 + + diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/IdStore.java b/src/main/java/org/jenkinsci/plugins/uniqueid/IdStore.java index ea19209..c203a26 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/IdStore.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/IdStore.java @@ -1,16 +1,27 @@ package org.jenkinsci.plugins.uniqueid; import hudson.ExtensionPoint; + import jenkins.model.Jenkins; +import java.io.UnsupportedEncodingException; +import java.util.UUID; + import javax.annotation.Nullable; +import org.apache.commons.codec.binary.Base64; + /** * An abstraction to persistently store and retrieve unique id's * for various Jenkins model objects. * * These keys are guaranteed to be unique with a Jenkins * and immutable across the lifetime of the given object. + * + * Implementations should not store the ID inside any specific item configuration as it is + * common for users top copy items either through the UI or manually and this will cause the + * IDs to become non-unique. + * * * @param */ @@ -26,17 +37,19 @@ public IdStore (Class forType) { * Creates an unique id for the given object. * Subsequent calls are idempotent. * - * @param object + * @param object the object to make the id for. + * @throws Exception if we could not store the unique ID for some reason. */ - public abstract void make(T object); + public abstract void make(T object) throws Exception; /** * Get the id for this given object. * @param object - * @return the id or null if none assigned. + * @return the id or {@code null} if none assigned. + * @throws Exception if we could not retrieve the unique ID for some reason. */ @Nullable - public abstract String get(T object); + public abstract String get(T object) throws Exception; public boolean supports(Class clazz) { return type.isAssignableFrom(clazz); @@ -62,8 +75,9 @@ public static IdStore forClass(Class clazz) { * Convenience method which makes the id for the given object. * * @throws java.lang.IllegalArgumentException if the type is not supported. + * @throws Exception if we could not store the unique ID for some reason. */ - public static void makeId(Object object) { + public static void makeId(Object object) throws IllegalArgumentException, Exception { IdStore store = forClass(object.getClass()); if (store == null) { throw new IllegalArgumentException("Unsupported type: " + object.getClass().getName()); @@ -76,8 +90,9 @@ public static void makeId(Object object) { * Convenience method which retrieves the id for the given object. * * @throws java.lang.IllegalArgumentException if the type is not supported. + * @throws Exception if we could not store the unique ID for some reason. */ - public static String getId(Object object) { + public static String getId(Object object) throws IllegalArgumentException, Exception { IdStore store = forClass(object.getClass()); if (store == null) { throw new IllegalArgumentException("Unsupported type: " + object.getClass().getName()); @@ -86,4 +101,20 @@ public static String getId(Object object) { } } + /** + * Generates a new unique ID. + * Subclasses do not need to use this to create unique IDs and are free to create IDs by other methods. + * @return a string that should be unique against all jenkins instances. + */ + protected static String generateUniqueID() { + try { + return Base64.encodeBase64String(UUID.randomUUID().toString().getBytes("UTF-8")).substring(0, 30); + } catch (UnsupportedEncodingException e) { + // impossible condition + Error err = new InternalError("The JLS mandates UTF-8 yet it is not available on this JVM. Your JVM is broken."); + err.initCause(e); + throw err; + } + } + } diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/FolderIdStore.java b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/FolderIdStore.java index 3bb1115..814d166 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/FolderIdStore.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/FolderIdStore.java @@ -1,37 +1,47 @@ package org.jenkinsci.plugins.uniqueid.impl; -import com.cloudbees.hudson.plugins.folder.Folder; -import com.cloudbees.hudson.plugins.folder.FolderProperty; -import com.cloudbees.hudson.plugins.folder.FolderPropertyDescriptor; import hudson.Extension; import hudson.model.Action; import hudson.model.Actionable; -import org.jenkinsci.plugins.uniqueid.IdStore; +import hudson.util.DescribableList; import java.io.IOException; import java.util.Collection; import java.util.Collections; -import java.util.logging.Level; +import java.util.Iterator; import java.util.logging.Logger; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +import com.cloudbees.hudson.plugins.folder.FolderProperty; +import com.cloudbees.hudson.plugins.folder.FolderPropertyDescriptor; +import com.cloudbees.hudson.plugins.folder.Folder; + /** * Stores ids for folders as a {@link FolderIdProperty} + * @deprecated {@see PersistenceRootIdStore} */ @Extension(optional = true) -public class FolderIdStore extends IdStore { +@Deprecated +@Restricted(NoExternalUse.class) +public class FolderIdStore extends LegacyIdStore { public FolderIdStore() { super(Folder.class); } @Override - public void make(Folder folder) { - if (folder.getProperties().get(FolderIdProperty.class) == null) { - try { - folder.addProperty(new FolderIdProperty()); - } catch (IOException e) { - LOGGER.log(Level.SEVERE, "Failed to add property",e); + public void remove(Folder folder) throws IOException { + DescribableList,FolderPropertyDescriptor> properties = folder.getProperties(); + + for (Iterator> itr = properties.iterator(); itr.hasNext(); ) { + FolderProperty prop = itr.next(); + + if (prop instanceof FolderIdProperty) { + itr.remove(); } } + folder.save(); } @Override diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/Id.java b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/Id.java index 6abc0f7..10b7017 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/Id.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/Id.java @@ -2,15 +2,22 @@ import hudson.model.Action; import hudson.model.Actionable; + import org.apache.commons.codec.binary.Base64; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; import javax.annotation.Nullable; + import java.util.UUID; import java.util.logging.Logger; /** * An action which stores an id. + * @deprecated users should not use this as it ties the ID explicitly to the item and as will not work when copying items (for example). */ +@Deprecated +@Restricted(NoExternalUse.class) class Id implements Action { @@ -36,7 +43,13 @@ public String getId() { return id; } + + /** + * @deprecated Sub classes should not use this as it stores the ID in the actionable item. + * @return + */ @Nullable + @Deprecated protected static String getId(Actionable actionable) { Id id = actionable.getAction(Id.class); if (id != null) { diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java new file mode 100644 index 0000000..dde0f4d --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java @@ -0,0 +1,113 @@ +package org.jenkinsci.plugins.uniqueid.impl; + +import hudson.Extension; +import hudson.init.InitMilestone; +import hudson.init.Initializer; +import hudson.model.Item; +import hudson.model.PersistenceRoot; +import hudson.model.Job; +import hudson.model.Run; +import hudson.util.RunList; + +import jenkins.model.Jenkins; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.annotation.Nonnull; + +import org.jenkinsci.plugins.uniqueid.implv2.PersistenceRootIdStore; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +/** + * Converts legacy UniqueIDs that are stored inside a Folder/Job/Run configuration to UniqueIDs that are stored alongside the Folder/Job/Run. + * + */ +@Restricted(NoExternalUse.class) +@Extension +public class IdStoreMigratorV1ToV2 { + + private static Logger LOGGER = Logger.getLogger(IdStoreMigratorV1ToV2.class.getName()); + + /** + * Migrates any IDs stored in Folder/Job/Run configuration + * @throws IOException + */ + + @Initializer(after=InitMilestone.JOB_LOADED, before=InitMilestone.COMPLETED, fatal=true) + public static void migrateIdStore() throws IOException { + Jenkins jenkins = Jenkins.getInstance(); + if (jenkins == null) { + throw new IllegalStateException("Jenkins is null, so it is impossible to migrate the IDs"); + } + File marker = new File(jenkins.getRootDir(), "IdStoreMigration.txt"); + if (marker.exists()) { + LOGGER.log(Level.INFO, "Migration of IDStore already perfomed, so skipping migration."); + return; + } + LOGGER.log(Level.INFO, "Starting migration of IDs"); + + performMigration(jenkins); + + LOGGER.log(Level.INFO, "Finished migration of IDs"); + if (!marker.createNewFile()) { + throw new IOException("Failed to record the completion of the IDStore Migration. " + + "This will cause performance issues on subsequent startup. " + + "Please create an empty file at '" + marker.getCanonicalPath() + "'"); + } + } + + @SuppressWarnings("unchecked") + static void performMigration(@Nonnull Jenkins jenkins) { + List allItems = jenkins.getAllItems(); + + for (Item item : allItems) { + // can only be Folder or Job here (not a run) - and these both implement PersistenceRoot + if (item instanceof PersistenceRoot) { + migrate((PersistenceRoot) item); + } + else { + LOGGER.log(Level.WARNING, "Expected item of type Folder or Job which implement PersistenceRoot, but got a {0} so can not migrate the IdStore for this item", + item.getClass().getName()); + } + + if (item instanceof Job) { + // need to migrate the RunIDs if they exist. + Job job = (Job) item; + RunList builds = job.getBuilds(); + for (Run build : builds) { + migrate(build); + } + } + } + } + + private static void migrate(PersistenceRoot pr) { + LOGGER.log(Level.FINE, "migrating {0}" , pr.toString()); + try { + String id = LegacyIdStore.getId(pr); + if (id != null) { + PersistenceRootIdStore.create(pr, id); + LegacyIdStore.removeId(pr); + } + } catch (IOException ex) { + // need to rethrow (but add some context first) otherwise the migration will continue to run + // and it will not have migrated everything :-( + throw new IDStoreMigrationException("Failure whilst migrating " + pr.toString(), ex); + } + } + + /** + * Exception to indicate a failure to migrate the IDStore. + */ + private static class IDStoreMigrationException extends RuntimeException { + + public IDStoreMigrationException(String message, Throwable cause) { + super(message,cause); + } + } +} diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/JobIdStore.java b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/JobIdStore.java index 5e5b2af..e7299cb 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/JobIdStore.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/JobIdStore.java @@ -6,7 +6,6 @@ import hudson.model.Job; import hudson.model.JobProperty; import hudson.model.JobPropertyDescriptor; -import org.jenkinsci.plugins.uniqueid.IdStore; import java.io.IOException; import java.util.Collection; @@ -14,26 +13,31 @@ import java.util.logging.Level; import java.util.logging.Logger; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + /** * Stores ids for jobs in {@link JobIdProperty} + * @deprecated {@see PersistenceRootIdStore} */ @Extension -public class JobIdStore extends IdStore { +@Deprecated +@Restricted(NoExternalUse.class) +public class JobIdStore extends LegacyIdStore { public JobIdStore() { super(Job.class); } @Override - public void make(Job job) { - if (job.getProperty(JobIdProperty.class) == null) { - try { - job.addProperty(new JobIdProperty()); - } catch (IOException e) { - LOGGER.log(Level.SEVERE, "Failed to add property",e); - } + public void remove(Job job) { + try { + while (job.removeProperty(JobIdProperty.class) != null) {} + } catch (IOException ex) { + LOGGER.log(Level.WARNING, "Failed to remove property from " + job.getFullName(), ex); } } + @Override public String get(Job thing) { return Id.getId((Actionable) thing); @@ -43,6 +47,7 @@ public String get(Job thing) { /** * A unique Id for Jobs. */ + @Deprecated public static class JobIdProperty extends JobProperty> { private Id id = new Id(); diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/LegacyIdStore.java b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/LegacyIdStore.java new file mode 100644 index 0000000..11c38b6 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/LegacyIdStore.java @@ -0,0 +1,101 @@ +package org.jenkinsci.plugins.uniqueid.impl; + +import hudson.ExtensionPoint; + +import jenkins.model.Jenkins; + +import java.io.IOException; + +import javax.annotation.Nullable; + +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +/** + * An abstraction to persistently store and retrieve unique id's + * for various Jenkins model objects. + * + * These keys are guaranteed to be unique with a Jenkins + * and immutable across the lifetime of the given object. + * + * Implementations should not store the ID inside any specific item configuration as it is + * common for users top copy items either through the UI or manually and this will cause the + * IDs to become non-unique. + * + * + * @param + */ +@Restricted(NoExternalUse.class) +@Deprecated +public abstract class LegacyIdStore implements ExtensionPoint { + + private final Class type; + + public LegacyIdStore (Class forType) { + this.type = forType; + } + + /** + * Remove the unique id associated with the given object. + * @param object + */ + public abstract void remove(T object) throws IOException; + + /** + * Get the id for this given object. + * @param object + * @return the id or null if none assigned. + */ + @Nullable + public abstract String get(T object); + + public boolean supports(Class clazz) { + return type.isAssignableFrom(clazz); + } + + /** + * Retrieve an {@link LegacyIdStore} for the given type + * @param clazz + * @param + * @return the store which supports the type, or null if none + */ + @Nullable + public static LegacyIdStore forClass(Class clazz) { + for (LegacyIdStore store : Jenkins.getInstance().getExtensionList(LegacyIdStore.class)) { + if (store.supports(clazz)) { + return store; + } + } + return null; + } + + /** + * Convenience method which makes the id for the given object. + * + * @throws java.lang.IllegalArgumentException if the type is not supported. + * @throws IOException if we could not remove the ID from the Object. + */ + public static void removeId(Object object) throws IOException{ + LegacyIdStore store = forClass(object.getClass()); + if (store == null) { + throw new IllegalArgumentException("Unsupported type: " + object.getClass().getName()); + } else { + store.remove(object); + } + } + + /** + * Convenience method which retrieves the id for the given object. + * + * @throws java.lang.IllegalArgumentException if the type is not supported. + */ + public static String getId(Object object) { + LegacyIdStore store = forClass(object.getClass()); + if (store == null) { + throw new IllegalArgumentException("Unsupported type: " + object.getClass().getName()); + } else { + return store.get(object); + } + } + +} diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/RunIdStore.java b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/RunIdStore.java index 1f8937c..11a0ac0 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/RunIdStore.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/RunIdStore.java @@ -1,39 +1,41 @@ package org.jenkinsci.plugins.uniqueid.impl; import hudson.Extension; +import hudson.model.Action; import hudson.model.Actionable; import hudson.model.Run; -import org.jenkinsci.plugins.uniqueid.IdStore; import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; +import java.util.Iterator; +import java.util.List; + +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; /** - * Stores id's for runs as an action on the Run. + * Controls id's for runs. */ @Extension -public class RunIdStore extends IdStore { +@Deprecated +@Restricted(NoExternalUse.class) +public class RunIdStore extends LegacyIdStore { + public RunIdStore() { super(Run.class); } @Override - public void make(Run run) { - if (run.getAction(Id.class) == null) { - run.addAction(new Id()); - try { - run.save(); - } catch (IOException e) { - LOGGER.log(Level.SEVERE,"Failed to save id",e); - } + public void remove(Run run) throws IOException { + List allActions = run.getActions(); + List ids = run.getActions(Id.class); + if (!ids.isEmpty()) { + allActions.removeAll(ids); + run.save(); } } + @Override public String get(Run thing) { return Id.getId((Actionable) thing); } - - private final static Logger LOGGER = Logger.getLogger(RunIdStore.class.getName()); - } diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/PersistenceRootIdStore.java b/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/PersistenceRootIdStore.java new file mode 100644 index 0000000..6dee36e --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/PersistenceRootIdStore.java @@ -0,0 +1,71 @@ +package org.jenkinsci.plugins.uniqueid.implv2; + +import hudson.Extension; +import hudson.model.PersistenceRoot; + +import java.io.File; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.commons.io.FileUtils; +import org.jenkinsci.plugins.uniqueid.IdStore; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +/** + * The {@link PersistenceRootIdStore} allows the storing of a Unique ID for any PersistenceRoot item. This replaces the + * need for {@link FolderIdStore}, {@link JobIdStore} and {@link RunIdStore} + */ +@Extension +public class PersistenceRootIdStore extends IdStore { + + /** Our Logger. */ + private final static Logger LOGGER = Logger.getLogger(PersistenceRootIdStore.class.getName()); + + /** The name of the file in which we store the unique ID. */ + private final static String ID_FILE = "uniqueID.txt"; + + public PersistenceRootIdStore() { + super(PersistenceRoot.class); + } + + @Override + public void make(PersistenceRoot object) { + File f = new File(object.getRootDir(), ID_FILE); + if (!f.exists()) { + try { + FileUtils.writeStringToFile(f, IdStore.generateUniqueID(), "UTF-8"); + } catch (IOException ex) { + LOGGER.log(Level.WARNING, "Failed to store unique ID for " + object.toString(), ex); + } + } + } + + @Override + public String get(PersistenceRoot object) { + File f = new File(object.getRootDir(), ID_FILE); + if (f.exists() && f.canRead()) { + try { + return FileUtils.readFileToString(f, "UTF-8"); + } catch (IOException ex) { + LOGGER.log(Level.WARNING, "Failed to retreive unique ID for " + object.toString(), ex); + } + } + return null; + } + + @Restricted(NoExternalUse.class) + public static void create(PersistenceRoot object, String uniqueId) throws IOException { + File f = new File(object.getRootDir(), ID_FILE); + if (!f.exists()) { + LOGGER.log(Level.FINE, "Creating file ({1}) to store ID for ({0}) whose RootDir is ({2}).", new Object[] {object.toString(), f, object.getRootDir()}); + // no need to migrate if its there to begin with! + FileUtils.writeStringToFile(f, uniqueId, "UTF-8"); + } + else { + LOGGER.log(Level.FINE, "**NOT** creating file ({1}) to store ID for ({0}) whose RootDir is ({2}).", new Object[] {object.toString(), f, object.getRootDir()}); + } + } + +} diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java b/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java new file mode 100644 index 0000000..86f71b9 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java @@ -0,0 +1,45 @@ +package org.jenkinsci.plugins.uniqueid.implv2; + +import hudson.Extension; +import hudson.model.PersistenceRoot; +import hudson.model.Job; +import hudson.model.Run; + +import org.jenkinsci.plugins.uniqueid.IdStore; + +/** + * Stores id's for runs as an action on the Run. + */ +@Extension(ordinal=1) // needs to take priority over the PersistenceRootIdStore +public class RunIdStore extends IdStore { + public RunIdStore() { + super(Run.class); + } + + @Override + public void make(Run run) throws IllegalArgumentException, Exception { + // we calculate these on the fly, or serve up migrated IDs if they exist. + // in order to calculate on the fly we require the parent to have an id. + IdStore.makeId(run.getParent()); + } + + @Override + public String get(Run run) throws IllegalArgumentException, Exception { + IdStore persistanceStore = IdStore.forClass(PersistenceRoot.class); + + String id = persistanceStore.get(run); + if (id != null) { + // migrated legacy id + return id; + } + + // calculate the ID. + Job parent = run.getParent(); + String parentID = IdStore.getId(parent); + if (parentID != null) { + return parentID + '_' + run.getNumber(); + } + return null; + } + +} diff --git a/src/test/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test.java b/src/test/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test.java new file mode 100644 index 0000000..714bc38 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test.java @@ -0,0 +1,87 @@ +package org.jenkinsci.plugins.uniqueid.impl; + +import hudson.model.ItemGroup; +import hudson.model.PersistenceRoot; +import hudson.model.Job; +import hudson.model.Run; + +import jenkins.model.Jenkins; + +import java.io.File; + +import org.apache.commons.io.FileUtils; +import org.jenkinsci.plugins.uniqueid.IdStore; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.recipes.LocalData; + +import com.cloudbees.hudson.plugins.folder.Folder; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; + +public class IdStoreMigratorV1ToV2Test { + + @Rule + public JenkinsRule jenkinsRule = new JenkinsRule(); + + @Test + @Issue("JENKINS-28843") + @LocalData + public void testMigration() throws Exception { + Jenkins jenkins = jenkinsRule.jenkins; + assertThat("All jobs loaded correctly", jenkins.getAllItems(), hasSize(4)); + + Folder folderNoID = jenkins.getItem("folderNoID", jenkins, Folder.class); + Folder folderWithID = jenkins.getItem("folderWithID", jenkins, Folder.class); + + Job jobNoID = jenkins.getItem("jobNoID", (ItemGroup)folderWithID, Job.class); + Job jobWithID = jenkins.getItem("jobWithID", (ItemGroup)folderWithID, Job.class); + + assertThat(folderNoID, notNullValue()); + assertThat(folderWithID, notNullValue()); + assertThat(jobNoID, notNullValue()); + assertThat(jobWithID, notNullValue()); + + checkID(folderNoID, null); + checkID(folderWithID, "YzUxN2JiZTYtNGVhZS00NDQxLTg5NT"); + + checkID(jobNoID, null); + checkID(jobWithID, "ZGQxMDNhYzUtMTJlOC00YTc4LTgzOT"); + + + checkID(jobNoID.getBuildByNumber(1), null); + checkID(jobNoID.getBuildByNumber(2), null); + + checkID(jobWithID.getBuildByNumber(1), "ZGQxMDNhYzUtMTJlOC00YTc4LTgzOT_1"); // build 1 had no id so its generated on the fly from the parent + checkID(jobWithID.getBuildByNumber(2), "NGQ0ODM2NjktZGM0OS00MjdkLWE3NT"); + } + + private static void checkID(PersistenceRoot obj, String expectedID) throws Exception { + assertThat("Checking " + obj.toString(), IdStore.getId(obj), is(expectedID)); + if (expectedID != null) { + File f = new File(obj.getRootDir(), "config.xml"); + + String string = FileUtils.readFileToString(f, "UTF-8"); + // main config should not contain a reference to the unique ID any more. + assertThat("config.xml for " + obj.toString() + " still contains the ID", string, not(containsString(expectedID))); + } + } + + private static void checkID(Run obj, String expectedID) throws Exception { + assertThat("Checking " + obj.toString(), IdStore.getId(obj), is(expectedID)); + if (expectedID != null) { + File f = new File(obj.getRootDir(), "build.xml"); + + String string = FileUtils.readFileToString(f, "UTF-8"); + // main config should not contain a reference to the unique ID any more. + assertThat("build.xml for " + obj.toString() + " still contains the ID", string, not(containsString(expectedID))); + } + } +} diff --git a/src/test/resources/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test/__testMigration.zip b/src/test/resources/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test/__testMigration.zip new file mode 100644 index 0000000000000000000000000000000000000000..60b0539c8aa6d5aeb5272a95ac09934b7e88fe63 GIT binary patch literal 27739 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 CnlTXo literal 0 HcmV?d00001 diff --git a/src/test/resources/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test/testMigration.zip b/src/test/resources/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test/testMigration.zip new file mode 100644 index 0000000000000000000000000000000000000000..ba51565dec1183de24e597bf1a4a32de6740a4d5 GIT binary patch literal 14274 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 z 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 Date: Thu, 11 Jun 2015 11:39:54 +0100 Subject: [PATCH 05/14] Remove file that should not have been checked in --- .../__testMigration.zip | Bin 27739 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/test/resources/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test/__testMigration.zip diff --git a/src/test/resources/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test/__testMigration.zip b/src/test/resources/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test/__testMigration.zip deleted file mode 100644 index 60b0539c8aa6d5aeb5272a95ac09934b7e88fe63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27739 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 CnlTXo From 1de8efd7732a55528270322ecd78eae2966c62db Mon Sep 17 00:00:00 2001 From: James Nord Date: Thu, 11 Jun 2015 11:40:52 +0100 Subject: [PATCH 06/14] make filenames more appropriate. Change the filenames so that they are more easily identified with this plugin. Fix some typos. --- .../plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java | 4 ++-- .../plugins/uniqueid/implv2/PersistenceRootIdStore.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java index dde0f4d..f85a184 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java @@ -32,7 +32,7 @@ public class IdStoreMigratorV1ToV2 { private static Logger LOGGER = Logger.getLogger(IdStoreMigratorV1ToV2.class.getName()); - + /** * Migrates any IDs stored in Folder/Job/Run configuration * @throws IOException @@ -44,7 +44,7 @@ public static void migrateIdStore() throws IOException { if (jenkins == null) { throw new IllegalStateException("Jenkins is null, so it is impossible to migrate the IDs"); } - File marker = new File(jenkins.getRootDir(), "IdStoreMigration.txt"); + File marker = new File(jenkins.getRootDir(), "unique-id-migration.txt"); if (marker.exists()) { LOGGER.log(Level.INFO, "Migration of IDStore already perfomed, so skipping migration."); return; diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/PersistenceRootIdStore.java b/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/PersistenceRootIdStore.java index 6dee36e..f55ccef 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/PersistenceRootIdStore.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/PersistenceRootIdStore.java @@ -24,7 +24,7 @@ public class PersistenceRootIdStore extends IdStore { private final static Logger LOGGER = Logger.getLogger(PersistenceRootIdStore.class.getName()); /** The name of the file in which we store the unique ID. */ - private final static String ID_FILE = "uniqueID.txt"; + private final static String ID_FILE = "unique-id.txt"; public PersistenceRootIdStore() { super(PersistenceRoot.class); @@ -49,7 +49,7 @@ public String get(PersistenceRoot object) { try { return FileUtils.readFileToString(f, "UTF-8"); } catch (IOException ex) { - LOGGER.log(Level.WARNING, "Failed to retreive unique ID for " + object.toString(), ex); + LOGGER.log(Level.WARNING, "Failed to retrieve unique ID for " + object.toString(), ex); } } return null; From 76f95d15a77a5c6fe9b15a208b6a0460bf4948aa Mon Sep 17 00:00:00 2001 From: James Nord Date: Thu, 11 Jun 2015 13:57:51 +0100 Subject: [PATCH 07/14] Fixed typos in implv2/RunIdStore.java. --- .../org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java b/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java index 86f71b9..8738e93 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java @@ -8,7 +8,7 @@ import org.jenkinsci.plugins.uniqueid.IdStore; /** - * Stores id's for runs as an action on the Run. + * Manages Unique IDs for Runs. */ @Extension(ordinal=1) // needs to take priority over the PersistenceRootIdStore public class RunIdStore extends IdStore { @@ -25,9 +25,9 @@ public void make(Run run) throws IllegalArgumentException, Exception { @Override public String get(Run run) throws IllegalArgumentException, Exception { - IdStore persistanceStore = IdStore.forClass(PersistenceRoot.class); + IdStore persistenceStore = IdStore.forClass(PersistenceRoot.class); - String id = persistanceStore.get(run); + String id = persistenceStore.get(run); if (id != null) { // migrated legacy id return id; From 944138b4eb7e9bb7e0a097b80bb09d585e63770a Mon Sep 17 00:00:00 2001 From: James Nord Date: Thu, 11 Jun 2015 15:07:17 +0100 Subject: [PATCH 08/14] UniqueIDs from Runs are now migrated in the background. Unique IDs are migrated in the background, however if someone attempts to retrieve the unique id before it haen migrated then it will be migrated on demand by the loading of the Run. The code now requires JDK7 to run in order to handle creation of IDs in a thread safe manner. --- .gitignore | 1 + pom.xml | 8 +- .../jenkinsci/plugins/uniqueid/IdStore.java | 12 +-- .../jenkinsci/plugins/uniqueid/impl/Id.java | 20 ++++- .../uniqueid/impl/IdStoreMigratorV1ToV2.java | 89 ++++++++++++++----- .../implv2/PersistenceRootIdStore.java | 16 +++- .../plugins/uniqueid/implv2/RunIdStore.java | 4 +- .../jenkinsci/plugins/uniqueid/IdTest.java | 10 ++- 8 files changed, 118 insertions(+), 42 deletions(-) diff --git a/.gitignore b/.gitignore index 8801278..aaeeb2d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target/ +/work/ /.classpath /.project /.settings \ No newline at end of file diff --git a/pom.xml b/pom.xml index b549394..45bf28c 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ org.jenkins-ci.plugins unique-id - 1.3-SNAPSHOT + 2.0-SNAPSHOT hpi Unique ID Library Plugin http://wiki.jenkins-ci.org/display/JENKINS/Unique+Id+Plugin @@ -20,8 +20,8 @@ - 1.6 - 1.6 + 1.7 + 1.7 @@ -83,7 +83,7 @@ maven-hpi-plugin true - 1.3 + 2.0 diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/IdStore.java b/src/main/java/org/jenkinsci/plugins/uniqueid/IdStore.java index c203a26..2f35907 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/IdStore.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/IdStore.java @@ -38,18 +38,16 @@ public IdStore (Class forType) { * Subsequent calls are idempotent. * * @param object the object to make the id for. - * @throws Exception if we could not store the unique ID for some reason. */ - public abstract void make(T object) throws Exception; + public abstract void make(T object); /** * Get the id for this given object. * @param object * @return the id or {@code null} if none assigned. - * @throws Exception if we could not retrieve the unique ID for some reason. */ @Nullable - public abstract String get(T object) throws Exception; + public abstract String get(T object); public boolean supports(Class clazz) { return type.isAssignableFrom(clazz); @@ -75,9 +73,8 @@ public static IdStore forClass(Class clazz) { * Convenience method which makes the id for the given object. * * @throws java.lang.IllegalArgumentException if the type is not supported. - * @throws Exception if we could not store the unique ID for some reason. */ - public static void makeId(Object object) throws IllegalArgumentException, Exception { + public static void makeId(Object object) throws IllegalArgumentException { IdStore store = forClass(object.getClass()); if (store == null) { throw new IllegalArgumentException("Unsupported type: " + object.getClass().getName()); @@ -90,9 +87,8 @@ public static void makeId(Object object) throws IllegalArgumentException, Except * Convenience method which retrieves the id for the given object. * * @throws java.lang.IllegalArgumentException if the type is not supported. - * @throws Exception if we could not store the unique ID for some reason. */ - public static String getId(Object object) throws IllegalArgumentException, Exception { + public static String getId(Object object) throws IllegalArgumentException { IdStore store = forClass(object.getClass()); if (store == null) { throw new IllegalArgumentException("Unsupported type: " + object.getClass().getName()); diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/Id.java b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/Id.java index 10b7017..03cedef 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/Id.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/Id.java @@ -1,7 +1,10 @@ package org.jenkinsci.plugins.uniqueid.impl; +import jenkins.model.RunAction2; + import hudson.model.Action; import hudson.model.Actionable; +import hudson.model.Run; import org.apache.commons.codec.binary.Base64; import org.kohsuke.accmod.Restricted; @@ -13,13 +16,14 @@ import java.util.logging.Logger; /** - * An action which stores an id. + * DO NOT USE * @deprecated users should not use this as it ties the ID explicitly to the item and as will not work when copying items (for example). */ @Deprecated @Restricted(NoExternalUse.class) -class Id implements Action { +class Id implements Action , RunAction2 { + private final static Logger LOGGER = Logger.getLogger(Id.class.getName()); private final String id; @@ -60,5 +64,15 @@ protected static String getId(Actionable actionable) { } - private final static Logger LOGGER = Logger.getLogger(Id.class.getName()); + + public void onAttached(Run r) { + // NO-OP + } + + /** + * Migrates the run away from using this Action. + */ + public void onLoad(Run r) { + IdStoreMigratorV1ToV2.migrate(r); + } } diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java index f85a184..34edf31 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java @@ -6,14 +6,14 @@ import hudson.model.Item; import hudson.model.PersistenceRoot; import hudson.model.Job; -import hudson.model.Run; -import hudson.util.RunList; import jenkins.model.Jenkins; import java.io.File; import java.io.IOException; +import java.util.Iterator; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; @@ -32,6 +32,8 @@ public class IdStoreMigratorV1ToV2 { private static Logger LOGGER = Logger.getLogger(IdStoreMigratorV1ToV2.class.getName()); + + private static final String MARKER_FILE_NAME = "unique-id-migration.txt"; /** * Migrates any IDs stored in Folder/Job/Run configuration @@ -44,21 +46,15 @@ public static void migrateIdStore() throws IOException { if (jenkins == null) { throw new IllegalStateException("Jenkins is null, so it is impossible to migrate the IDs"); } - File marker = new File(jenkins.getRootDir(), "unique-id-migration.txt"); + File marker = new File(jenkins.getRootDir(), MARKER_FILE_NAME); if (marker.exists()) { - LOGGER.log(Level.INFO, "Migration of IDStore already perfomed, so skipping migration."); + LOGGER.log(Level.INFO, "Migration of IDStore already performed, so skipping migration."); return; } LOGGER.log(Level.INFO, "Starting migration of IDs"); performMigration(jenkins); - LOGGER.log(Level.INFO, "Finished migration of IDs"); - if (!marker.createNewFile()) { - throw new IOException("Failed to record the completion of the IDStore Migration. " + - "This will cause performance issues on subsequent startup. " + - "Please create an empty file at '" + marker.getCanonicalPath() + "'"); - } } @SuppressWarnings("unchecked") @@ -74,19 +70,15 @@ static void performMigration(@Nonnull Jenkins jenkins) { LOGGER.log(Level.WARNING, "Expected item of type Folder or Job which implement PersistenceRoot, but got a {0} so can not migrate the IdStore for this item", item.getClass().getName()); } - - if (item instanceof Job) { - // need to migrate the RunIDs if they exist. - Job job = (Job) item; - RunList builds = job.getBuilds(); - for (Run build : builds) { - migrate(build); - } - } } + LOGGER.log(Level.INFO, "migration of unique IDs for Jobs and Folders complete - will continue to process Runs in the background."); + + Thread t = new Thread(new RunIDMigrationThread(), "unique-id background migration thread"); + t.setDaemon(true); + t.start(); } - private static void migrate(PersistenceRoot pr) { + static void migrate(PersistenceRoot pr) { LOGGER.log(Level.FINE, "migrating {0}" , pr.toString()); try { String id = LegacyIdStore.getId(pr); @@ -105,9 +97,64 @@ private static void migrate(PersistenceRoot pr) { * Exception to indicate a failure to migrate the IDStore. */ private static class IDStoreMigrationException extends RuntimeException { - + public IDStoreMigrationException(String message, Throwable cause) { super(message,cause); } } + + private static class RunIDMigrationThread implements Runnable { + + public void run() { + Jenkins jenkins = Jenkins.getInstance(); + if (jenkins == null) { + throw new IllegalStateException("Jenkins is null, so it is impossible to migrate the IDs"); + } + // if new jobs are added that is ok - as their runs will not need to be migrated. + // if jobs are deleted from Jenkins we need to handle that fact! + List allJobs = jenkins.getAllItems(Job.class); + int totalJobs = allJobs.size(); + int migratedJobs = 0; + int migratedBuilds = 0; + final long startTime = System.currentTimeMillis(); + long lastLog = System.currentTimeMillis(); + for (Job job : allJobs) { + // Force the loading of the builds. + migratedJobs++; + if (job.getConfigFile().getFile().exists()) { + // we have not been deleted! + for (Iterator iterator = job.getBuilds().iterator(); iterator.hasNext(); iterator.next()) { + // the build is migrated by the action in Id.onLoad(Run) + migratedBuilds++; + } + } + if ((System.currentTimeMillis() - lastLog) > (60 * 1000L) ) { + lastLog = System.currentTimeMillis(); + LOGGER.log(Level.INFO, "Processed {0} builds, and have inspected all runs from {1} out of {2} jobs.", + new Object[] {migratedBuilds, migratedJobs, totalJobs}); + } + } + // all done... + final long duration = System.currentTimeMillis() - startTime; + final long minutes = TimeUnit.MILLISECONDS.toMinutes(System.currentTimeMillis() - startTime); + final long seconds = TimeUnit.MILLISECONDS.toMinutes(System.currentTimeMillis() - startTime - TimeUnit.HOURS.toMillis(minutes)); + + LOGGER.log(Level.INFO, "Finished unique-id migration of builds in {0} minutes {1} seconds. Processed {2} runs from {3} jobs.", + new Object[] {minutes, seconds, migratedBuilds, migratedJobs}); + File marker = new File(jenkins.getRootDir(), MARKER_FILE_NAME); + try { + if (!marker.createNewFile()) { + LOGGER.log(Level.WARNING, "Failed to record the completion of the IDStore Migration. " + + "This will cause performance issues on subsequent startup. " + + "Please create an empty file at '" + marker.getCanonicalPath() + "'"); + } + } + catch (IOException ex) { + LOGGER.log(Level.WARNING, "Failed to record the completion of the IDStore Migration. " + + "This will cause performance issues on subsequent startup. " + + "Please create an empty file in the Jenkins home directory called '" + MARKER_FILE_NAME + "'.", ex); + } + } + } + } diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/PersistenceRootIdStore.java b/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/PersistenceRootIdStore.java index f55ccef..a8834a0 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/PersistenceRootIdStore.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/PersistenceRootIdStore.java @@ -5,6 +5,9 @@ import java.io.File; import java.io.IOException; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.util.logging.Level; import java.util.logging.Logger; @@ -13,6 +16,7 @@ import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; + /** * The {@link PersistenceRootIdStore} allows the storing of a Unique ID for any PersistenceRoot item. This replaces the * need for {@link FolderIdStore}, {@link JobIdStore} and {@link RunIdStore} @@ -34,9 +38,19 @@ public PersistenceRootIdStore() { public void make(PersistenceRoot object) { File f = new File(object.getRootDir(), ID_FILE); if (!f.exists()) { + File tmp = null; try { + tmp = File.createTempFile(".unique-id_", ".tmp", object.getRootDir()); FileUtils.writeStringToFile(f, IdStore.generateUniqueID(), "UTF-8"); - } catch (IOException ex) { + try { + Files.move(tmp.toPath(), f.toPath(), StandardCopyOption.ATOMIC_MOVE); + } + catch (FileAlreadyExistsException ignored) { + FileUtils.deleteQuietly(tmp); + return; // we already have an id. + } + } + catch (IOException ex) { LOGGER.log(Level.WARNING, "Failed to store unique ID for " + object.toString(), ex); } } diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java b/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java index 8738e93..a5ed14a 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java @@ -17,14 +17,14 @@ public RunIdStore() { } @Override - public void make(Run run) throws IllegalArgumentException, Exception { + public void make(Run run) { // we calculate these on the fly, or serve up migrated IDs if they exist. // in order to calculate on the fly we require the parent to have an id. IdStore.makeId(run.getParent()); } @Override - public String get(Run run) throws IllegalArgumentException, Exception { + public String get(Run run) { IdStore persistenceStore = IdStore.forClass(PersistenceRoot.class); String id = persistenceStore.get(run); diff --git a/src/test/java/org/jenkinsci/plugins/uniqueid/IdTest.java b/src/test/java/org/jenkinsci/plugins/uniqueid/IdTest.java index a7fa1fe..19cc00f 100644 --- a/src/test/java/org/jenkinsci/plugins/uniqueid/IdTest.java +++ b/src/test/java/org/jenkinsci/plugins/uniqueid/IdTest.java @@ -3,7 +3,6 @@ import com.cloudbees.hudson.plugins.folder.Folder; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; -import hudson.model.Job; import hudson.model.Project; import org.junit.Rule; import org.junit.Test; @@ -24,9 +23,14 @@ public void project() throws Exception { String id = IdStore.getId(p); AbstractBuild build = jenkinsRule.buildAndAssertSuccess(p); - assertNull(IdStore.getId(build)); - IdStore.makeId(build); + // a build will get an id computed from its parent. String buildId = IdStore.getId(build); + assertEquals(buildId, id+"_1"); + + // should be a no-op + IdStore.makeId(build); + assertEquals(IdStore.getId(build), buildId); + jenkinsRule.jenkins.reload(); AbstractProject resurrectedProject = jenkinsRule.jenkins.getItemByFullName(p.getFullName(), AbstractProject.class); From 07f5de0eaefa7f081f404ffe0f6e05fc9addf3da Mon Sep 17 00:00:00 2001 From: James Nord Date: Thu, 11 Jun 2015 15:54:11 +0100 Subject: [PATCH 09/14] Cleanup code with Java7 APIs. Use standardCharset to avoid UTF-8 related issues. --- .../java/org/jenkinsci/plugins/uniqueid/IdStore.java | 10 ++-------- .../uniqueid/implv2/PersistenceRootIdStore.java | 7 ++++--- .../uniqueid/impl/IdStoreMigratorV1ToV2Test.java | 5 +++-- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/IdStore.java b/src/main/java/org/jenkinsci/plugins/uniqueid/IdStore.java index 2f35907..55c85f5 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/IdStore.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/IdStore.java @@ -5,6 +5,7 @@ import jenkins.model.Jenkins; import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.util.UUID; import javax.annotation.Nullable; @@ -103,14 +104,7 @@ public static String getId(Object object) throws IllegalArgumentException { * @return a string that should be unique against all jenkins instances. */ protected static String generateUniqueID() { - try { - return Base64.encodeBase64String(UUID.randomUUID().toString().getBytes("UTF-8")).substring(0, 30); - } catch (UnsupportedEncodingException e) { - // impossible condition - Error err = new InternalError("The JLS mandates UTF-8 yet it is not available on this JVM. Your JVM is broken."); - err.initCause(e); - throw err; - } + return Base64.encodeBase64String(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)).substring(0, 30); } } diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/PersistenceRootIdStore.java b/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/PersistenceRootIdStore.java index a8834a0..43068ee 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/PersistenceRootIdStore.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/PersistenceRootIdStore.java @@ -5,6 +5,7 @@ import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; import java.nio.file.StandardCopyOption; @@ -41,7 +42,7 @@ public void make(PersistenceRoot object) { File tmp = null; try { tmp = File.createTempFile(".unique-id_", ".tmp", object.getRootDir()); - FileUtils.writeStringToFile(f, IdStore.generateUniqueID(), "UTF-8"); + FileUtils.writeStringToFile(f, IdStore.generateUniqueID(), StandardCharsets.UTF_8); try { Files.move(tmp.toPath(), f.toPath(), StandardCopyOption.ATOMIC_MOVE); } @@ -61,7 +62,7 @@ public String get(PersistenceRoot object) { File f = new File(object.getRootDir(), ID_FILE); if (f.exists() && f.canRead()) { try { - return FileUtils.readFileToString(f, "UTF-8"); + return FileUtils.readFileToString(f, StandardCharsets.UTF_8); } catch (IOException ex) { LOGGER.log(Level.WARNING, "Failed to retrieve unique ID for " + object.toString(), ex); } @@ -75,7 +76,7 @@ public static void create(PersistenceRoot object, String uniqueId) throws IOExce if (!f.exists()) { LOGGER.log(Level.FINE, "Creating file ({1}) to store ID for ({0}) whose RootDir is ({2}).", new Object[] {object.toString(), f, object.getRootDir()}); // no need to migrate if its there to begin with! - FileUtils.writeStringToFile(f, uniqueId, "UTF-8"); + FileUtils.writeStringToFile(f, uniqueId, StandardCharsets.UTF_8); } else { LOGGER.log(Level.FINE, "**NOT** creating file ({1}) to store ID for ({0}) whose RootDir is ({2}).", new Object[] {object.toString(), f, object.getRootDir()}); diff --git a/src/test/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test.java b/src/test/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test.java index 714bc38..a3a6adf 100644 --- a/src/test/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test.java +++ b/src/test/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test.java @@ -8,6 +8,7 @@ import jenkins.model.Jenkins; import java.io.File; +import java.nio.charset.StandardCharsets; import org.apache.commons.io.FileUtils; import org.jenkinsci.plugins.uniqueid.IdStore; @@ -68,7 +69,7 @@ private static void checkID(PersistenceRoot obj, String expectedID) throws Excep if (expectedID != null) { File f = new File(obj.getRootDir(), "config.xml"); - String string = FileUtils.readFileToString(f, "UTF-8"); + String string = FileUtils.readFileToString(f, StandardCharsets.UTF_8); // main config should not contain a reference to the unique ID any more. assertThat("config.xml for " + obj.toString() + " still contains the ID", string, not(containsString(expectedID))); } @@ -79,7 +80,7 @@ private static void checkID(Run obj, String expectedID) throws Exception { if (expectedID != null) { File f = new File(obj.getRootDir(), "build.xml"); - String string = FileUtils.readFileToString(f, "UTF-8"); + String string = FileUtils.readFileToString(f, StandardCharsets.UTF_8); // main config should not contain a reference to the unique ID any more. assertThat("build.xml for " + obj.toString() + " still contains the ID", string, not(containsString(expectedID))); } From cbee5b5f0a3425b92bf41ab32d5725725a075832 Mon Sep 17 00:00:00 2001 From: James Nord Date: Thu, 11 Jun 2015 15:58:59 +0100 Subject: [PATCH 10/14] update javadoc for RunIdStore --- .../org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java b/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java index a5ed14a..fef7f29 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java @@ -8,7 +8,10 @@ import org.jenkinsci.plugins.uniqueid.IdStore; /** - * Manages Unique IDs for Runs. + * Manages Unique IDs for Runs. + * Whilst we could use the {@link PersistenceRootIdStore} that will create extra files for + * every single build. A build already has a unique identifier (build number / build id) and a parent job can have a + * unique ID, so we build one from the parent and our build number to save creating a file. */ @Extension(ordinal=1) // needs to take priority over the PersistenceRootIdStore public class RunIdStore extends IdStore { From c38cbb0d9cf44d485432db94104302e000a9e58a Mon Sep 17 00:00:00 2001 From: James Nord Date: Thu, 11 Jun 2015 16:29:42 +0100 Subject: [PATCH 11/14] Access something from the build to ensure loading happens. --- .../plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java index 34edf31..cb37616 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java @@ -6,6 +6,7 @@ import hudson.model.Item; import hudson.model.PersistenceRoot; import hudson.model.Job; +import hudson.model.Run; import jenkins.model.Jenkins; @@ -123,8 +124,13 @@ public void run() { migratedJobs++; if (job.getConfigFile().getFile().exists()) { // we have not been deleted! - for (Iterator iterator = job.getBuilds().iterator(); iterator.hasNext(); iterator.next()) { + for (Iterator iterator = job.getBuilds().iterator(); iterator.hasNext();) { // the build is migrated by the action in Id.onLoad(Run) + // touch something in the build just to force loading incase it gets more lazy in the future. + Object r = iterator.next(); + if (r != null && r instanceof Run) { + ((Run)r).getResult(); + } migratedBuilds++; } } From 4258879d175dae0618f664d3b658d28ee6a5b858 Mon Sep 17 00:00:00 2001 From: James Nord Date: Thu, 11 Jun 2015 17:18:18 +0100 Subject: [PATCH 12/14] Fix incorrect time conversion. --- .../jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java index cb37616..1e0e7c8 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2.java @@ -143,7 +143,7 @@ public void run() { // all done... final long duration = System.currentTimeMillis() - startTime; final long minutes = TimeUnit.MILLISECONDS.toMinutes(System.currentTimeMillis() - startTime); - final long seconds = TimeUnit.MILLISECONDS.toMinutes(System.currentTimeMillis() - startTime - TimeUnit.HOURS.toMillis(minutes)); + final long seconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - startTime - TimeUnit.MINUTES.toMillis(minutes)); LOGGER.log(Level.INFO, "Finished unique-id migration of builds in {0} minutes {1} seconds. Processed {2} runs from {3} jobs.", new Object[] {minutes, seconds, migratedBuilds, migratedJobs}); From 9cb4df5c280b5fd5aa15cdb2073da8d94d8da94f Mon Sep 17 00:00:00 2001 From: James Nord Date: Thu, 11 Jun 2015 17:20:25 +0100 Subject: [PATCH 13/14] rename variable for oleg --- .../java/org/jenkinsci/plugins/uniqueid/impl/RunIdStore.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/RunIdStore.java b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/RunIdStore.java index 11a0ac0..b1f4eb0 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/impl/RunIdStore.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/impl/RunIdStore.java @@ -26,10 +26,10 @@ public RunIdStore() { @Override public void remove(Run run) throws IOException { - List allActions = run.getActions(); + List actionList = run.getActions(); List ids = run.getActions(Id.class); if (!ids.isEmpty()) { - allActions.removeAll(ids); + actionList.removeAll(ids); run.save(); } } From f4700e8dbad978518a7385d13d1d4bdc2a837fb4 Mon Sep 17 00:00:00 2001 From: James Nord Date: Thu, 11 Jun 2015 18:53:47 +0100 Subject: [PATCH 14/14] switch to use Id for the build --- .../java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java | 2 +- src/test/java/org/jenkinsci/plugins/uniqueid/IdTest.java | 2 +- .../plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test.java | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java b/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java index fef7f29..6d91cd8 100644 --- a/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java +++ b/src/main/java/org/jenkinsci/plugins/uniqueid/implv2/RunIdStore.java @@ -40,7 +40,7 @@ public String get(Run run) { Job parent = run.getParent(); String parentID = IdStore.getId(parent); if (parentID != null) { - return parentID + '_' + run.getNumber(); + return parentID + '_' + run.getId(); } return null; } diff --git a/src/test/java/org/jenkinsci/plugins/uniqueid/IdTest.java b/src/test/java/org/jenkinsci/plugins/uniqueid/IdTest.java index 19cc00f..3885c65 100644 --- a/src/test/java/org/jenkinsci/plugins/uniqueid/IdTest.java +++ b/src/test/java/org/jenkinsci/plugins/uniqueid/IdTest.java @@ -25,7 +25,7 @@ public void project() throws Exception { // a build will get an id computed from its parent. String buildId = IdStore.getId(build); - assertEquals(buildId, id+"_1"); + assertEquals(buildId, id+'_'+build.getId()); // should be a no-op IdStore.makeId(build); diff --git a/src/test/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test.java b/src/test/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test.java index a3a6adf..34f11b1 100644 --- a/src/test/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test.java +++ b/src/test/java/org/jenkinsci/plugins/uniqueid/impl/IdStoreMigratorV1ToV2Test.java @@ -60,7 +60,8 @@ public void testMigration() throws Exception { checkID(jobNoID.getBuildByNumber(1), null); checkID(jobNoID.getBuildByNumber(2), null); - checkID(jobWithID.getBuildByNumber(1), "ZGQxMDNhYzUtMTJlOC00YTc4LTgzOT_1"); // build 1 had no id so its generated on the fly from the parent + // build 1 had no id so its generated on the fly from the parent + checkID(jobWithID.getBuildByNumber(1), "ZGQxMDNhYzUtMTJlOC00YTc4LTgzOT_" + jobWithID.getBuildByNumber(1).getId()); checkID(jobWithID.getBuildByNumber(2), "NGQ0ODM2NjktZGM0OS00MjdkLWE3NT"); }