Skip to content

Commit

Permalink
Merge pull request #49 from Greybird/master
Browse files Browse the repository at this point in the history
[JENKINS-40803] allow plugins to specify the config file resolution context
  • Loading branch information
imod committed Mar 5, 2018
2 parents b5b4d0d + 48087c5 commit e76cff0
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 0 deletions.
@@ -0,0 +1,36 @@
package org.jenkinsci.plugins.configfiles;

import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.model.ItemGroup;
import jenkins.model.Jenkins;

/**
* ConfigContextResolver provides a way for a plugin to specify the context ({@link ItemGroup}) used to retrieve
* configuration files from for classes deriving from {@link ItemGroup} it defines.
*/
public abstract class ConfigContextResolver implements ExtensionPoint {

public static ExtensionList<ConfigContextResolver> all() {
return Jenkins.getInstance().getExtensionList(ConfigContextResolver.class);
}
/**
* Optionally provides the {@link ItemGroup} from which configuration files should be retrieved for the provided
* one.
*
* <p> <b>Examples of expected usage:</b>
* <p> In promoted-builds-plugin, the itemGroup used as context for a promotion run is a
* {@code hudson.plugins.promoted_builds.JobPropertyImpl} which is not supported by the configuration file retrieval
* logic in {@link ConfigFiles#getByIdOrNull(ItemGroup, String)}.
* <p> However, as the {@code hudson.plugins.promoted_builds.JobPropertyImpl} has an owner property containing the
* promoted build, it is possible for the promoted-builds-plugin to implement this extension by returning the
* owner's parent when the itemGroup provided to the {@link #getConfigContext(ItemGroup)} method is an instance of
* {@code hudson.plugins.promoted_builds.JobPropertyImpl}, and null if it is of any other type.
* <p> This will allow for the configuration file retrieval code to revert to the standard logic from the owner's
* parent starting point.
*
* @param itemGroup the source {@link ItemGroup}
* @return an {@link ItemGroup} to use to retrieve configuration, or null.
*/
public abstract ItemGroup getConfigContext(ItemGroup itemGroup);
}
13 changes: 13 additions & 0 deletions src/main/java/org/jenkinsci/plugins/configfiles/ConfigFiles.java
Expand Up @@ -14,6 +14,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand Down Expand Up @@ -49,6 +50,7 @@ public static List<Config> getConfigsInContext(@Nullable ItemGroup itemGroup, Cl
List<Config> configs = new ArrayList<Config>();

while (itemGroup != null) {
itemGroup = resolveItemGroup(itemGroup);
if (folderPluginInstalled() && itemGroup instanceof AbstractFolder) {

final AbstractFolder<?> folder = AbstractFolder.class.cast(itemGroup);
Expand Down Expand Up @@ -91,6 +93,7 @@ public static List<Config> getConfigsInContext(@Nullable ItemGroup itemGroup, Cl
public static <T extends Config> T getByIdOrNull(@Nullable ItemGroup itemGroup, @NonNull String configId) {

while (itemGroup != null) {
itemGroup = resolveItemGroup(itemGroup);
if (folderPluginInstalled() && itemGroup instanceof AbstractFolder) {
final AbstractFolder<?> folder = AbstractFolder.class.cast(itemGroup);
ConfigFileStore store = folder.getProperties().get(FolderConfigFileProperty.class);
Expand Down Expand Up @@ -166,4 +169,14 @@ public static <T extends Config> T getByIdOrNull(@NonNull Run<?, ?> build, @NonN

return (T) configFile;
}

private static ItemGroup resolveItemGroup(ItemGroup itemGroup) {
for (ConfigContextResolver resolver : ConfigContextResolver.all()) {
ItemGroup resolvedItemGroup = resolver.getConfigContext(itemGroup);
if (resolvedItemGroup != null) {
return resolvedItemGroup;
}
}
return itemGroup;
}
}
124 changes: 124 additions & 0 deletions src/test/java/org/jenkinsci/plugins/configfiles/ConfigFilesTest.java
@@ -0,0 +1,124 @@
package org.jenkinsci.plugins.configfiles;

import hudson.model.Item;
import hudson.model.ItemGroup;
import jenkins.model.Jenkins;
import org.acegisecurity.AccessDeniedException;
import org.jenkinsci.lib.configprovider.model.Config;
import org.jenkinsci.plugins.configfiles.custom.CustomConfig;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.TestExtension;

import javax.annotation.CheckForNull;
import java.io.File;
import java.io.IOException;
import java.util.Collection;

public class ConfigFilesTest {

private static final String CONFIG_ID = "ConfigFilesTestId";

@Rule
public JenkinsRule j = new JenkinsRule();

@Test
public void testConfigContextResolver() {
GlobalConfigFiles store = j.getInstance().getExtensionList(GlobalConfigFiles.class).get(GlobalConfigFiles.class);
Assert.assertTrue(store.getConfigs().isEmpty());

CustomConfig config = new CustomConfig(CONFIG_ID, "name", "comment", "content");
store.save(config);

try {
ConfigFiles.getByIdOrNull(new TestItemGroup(), CONFIG_ID);
Assert.fail("should have thrown");
} catch(IllegalArgumentException e) {
}

j.getInstance().getExtensionList(ConfigContextResolver.class).get(TestResolver.class).isActive = true;

Config retrievedConfig = ConfigFiles.getByIdOrNull(new TestItemGroup(), CONFIG_ID);
Assert.assertNotNull(retrievedConfig);
}

@TestExtension
public static class TestResolver extends ConfigContextResolver
{
private boolean isActive = false;

@Override
public ItemGroup getConfigContext(ItemGroup itemGroup) {
if (isActive) {
return Jenkins.getInstance();
}
return null;
}
}

private class TestItemGroup implements ItemGroup<Item>
{
@Override
public String getFullName() {
return null;
}

@Override
public String getFullDisplayName() {
return null;
}

@Override
public Collection<Item> getItems() {
return null;
}

@Override
public String getUrl() {
return null;
}

@Override
public String getUrlChildPrefix() {
return null;
}

@CheckForNull
@Override
public Item getItem(String name) throws AccessDeniedException {
return null;
}

@Override
public File getRootDirFor(Item child) {
return null;
}

@Override
public void onRenamed(Item item, String oldName, String newName) throws IOException {

}

@Override
public void onDeleted(Item item) throws IOException {

}

@Override
public String getDisplayName() {
return null;
}

@Override
public File getRootDir() {
return null;
}

@Override
public void save() throws IOException {

}
}
}

0 comments on commit e76cff0

Please sign in to comment.