Merge changes from topic 'plugin-api'
* changes: ServerInfoIT: Use API to install plugin Add implementation of PluginApi PluginLoader: Ensure that $site/plugins folder exists Plugins#ListRequest: Remove parameter from all() method
This commit is contained in:
@@ -15,15 +15,89 @@
|
||||
package com.google.gerrit.acceptance.api.plugin;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||
import com.google.gerrit.acceptance.GerritConfig;
|
||||
import com.google.gerrit.acceptance.NoHttpd;
|
||||
import com.google.gerrit.common.RawInputUtil;
|
||||
import com.google.gerrit.extensions.api.plugins.PluginApi;
|
||||
import com.google.gerrit.extensions.api.plugins.Plugins.ListRequest;
|
||||
import com.google.gerrit.extensions.common.InstallPluginInput;
|
||||
import com.google.gerrit.extensions.common.PluginInfo;
|
||||
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import java.util.List;
|
||||
import org.junit.Test;
|
||||
|
||||
@NoHttpd
|
||||
public class PluginIT extends AbstractDaemonTest {
|
||||
private static final byte[] JS_PLUGIN_CONTENT =
|
||||
"Gerrit.install(function(self){});\n".getBytes(UTF_8);
|
||||
private static final List<String> PLUGINS =
|
||||
ImmutableList.of("plugin-a", "plugin-b", "plugin-c", "plugin-d");
|
||||
|
||||
@Test
|
||||
public void noPlugins() throws Exception {
|
||||
assertThat(gApi.plugins().list().get()).isEmpty();
|
||||
@GerritConfig(name = "plugins.allowRemoteAdmin", value = "true")
|
||||
public void pluginManagement() throws Exception {
|
||||
// No plugins are loaded
|
||||
assertThat(list().get()).isEmpty();
|
||||
assertThat(list().all().get()).isEmpty();
|
||||
|
||||
PluginApi test;
|
||||
PluginInfo info;
|
||||
|
||||
// Install all the plugins
|
||||
InstallPluginInput input = new InstallPluginInput();
|
||||
input.raw = RawInputUtil.create(JS_PLUGIN_CONTENT);
|
||||
for (String plugin : PLUGINS) {
|
||||
test = gApi.plugins().install(plugin + ".js", input);
|
||||
assertThat(test).isNotNull();
|
||||
info = test.get();
|
||||
assertThat(info.id).isEqualTo(plugin);
|
||||
assertThat(info.disabled).isNull();
|
||||
}
|
||||
assertPlugins(list().get(), PLUGINS);
|
||||
|
||||
// Disable
|
||||
test = gApi.plugins().name("plugin-a");
|
||||
test.disable();
|
||||
test = gApi.plugins().name("plugin-a");
|
||||
info = test.get();
|
||||
assertThat(info.disabled).isTrue();
|
||||
assertPlugins(list().get(), PLUGINS.subList(1, PLUGINS.size()));
|
||||
assertPlugins(list().all().get(), PLUGINS);
|
||||
|
||||
// Enable
|
||||
test.enable();
|
||||
test = gApi.plugins().name("plugin-a");
|
||||
info = test.get();
|
||||
assertThat(info.disabled).isNull();
|
||||
assertPlugins(list().get(), PLUGINS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void installNotAllowed() throws Exception {
|
||||
exception.expect(MethodNotAllowedException.class);
|
||||
exception.expectMessage("remote installation is disabled");
|
||||
gApi.plugins().install("test.js", new InstallPluginInput());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNonExistingThrowsNotFound() throws Exception {
|
||||
exception.expect(ResourceNotFoundException.class);
|
||||
gApi.plugins().name("does-not-exist");
|
||||
}
|
||||
|
||||
private ListRequest list() throws RestApiException {
|
||||
return gApi.plugins().list();
|
||||
}
|
||||
|
||||
private void assertPlugins(List<PluginInfo> actual, List<String> expected) {
|
||||
List<String> _actual = actual.stream().map(p -> p.id).collect(toList());
|
||||
assertThat(_actual).containsExactlyElementsIn(expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,22 +20,20 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
|
||||
import com.google.gerrit.acceptance.GerritConfig;
|
||||
import com.google.gerrit.acceptance.NoHttpd;
|
||||
import com.google.gerrit.acceptance.UseSsh;
|
||||
import com.google.gerrit.common.RawInputUtil;
|
||||
import com.google.gerrit.extensions.client.AccountFieldName;
|
||||
import com.google.gerrit.extensions.client.AuthType;
|
||||
import com.google.gerrit.extensions.common.InstallPluginInput;
|
||||
import com.google.gerrit.extensions.common.ServerInfo;
|
||||
import com.google.gerrit.server.config.AllProjectsNameProvider;
|
||||
import com.google.gerrit.server.config.AllUsersNameProvider;
|
||||
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.inject.Inject;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import org.junit.Test;
|
||||
|
||||
@NoHttpd
|
||||
public class ServerInfoIT extends AbstractDaemonTest {
|
||||
@Inject private SitePaths sitePaths;
|
||||
private static final byte[] JS_PLUGIN_CONTENT =
|
||||
"Gerrit.install(function(self){});\n".getBytes(UTF_8);
|
||||
|
||||
@Test
|
||||
// auth
|
||||
@@ -132,18 +130,16 @@ public class ServerInfoIT extends AbstractDaemonTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@UseSsh
|
||||
@GerritConfig(name = "plugins.allowRemoteAdmin", value = "true")
|
||||
public void serverConfigWithPlugin() throws Exception {
|
||||
Path plugins = sitePaths.plugins_dir;
|
||||
Files.createDirectory(plugins);
|
||||
Path jsplugin = plugins.resolve("js-plugin-1.js");
|
||||
Files.write(jsplugin, "Gerrit.install(function(self){});\n".getBytes(UTF_8));
|
||||
adminSshSession.exec("gerrit plugin reload");
|
||||
|
||||
ServerInfo i = gApi.config().server().getInfo();
|
||||
assertThat(i.plugin.jsResourcePaths).isEmpty();
|
||||
|
||||
// plugin
|
||||
InstallPluginInput input = new InstallPluginInput();
|
||||
input.raw = RawInputUtil.create(JS_PLUGIN_CONTENT);
|
||||
gApi.plugins().install("js-plugin-1.js", input);
|
||||
|
||||
i = gApi.config().server().getInfo();
|
||||
assertThat(i.plugin.jsResourcePaths).hasSize(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright (C) 2017 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.extensions.api.plugins;
|
||||
|
||||
import com.google.gerrit.extensions.common.PluginInfo;
|
||||
import com.google.gerrit.extensions.restapi.NotImplementedException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
|
||||
public interface PluginApi {
|
||||
PluginInfo get() throws RestApiException;
|
||||
|
||||
void enable() throws RestApiException;
|
||||
|
||||
void disable() throws RestApiException;
|
||||
|
||||
void reload() throws RestApiException;
|
||||
|
||||
/**
|
||||
* A default implementation which allows source compatibility when adding new methods to the
|
||||
* interface.
|
||||
*/
|
||||
class NotImplemented implements PluginApi {
|
||||
@Override
|
||||
public PluginInfo get() throws RestApiException {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() throws RestApiException {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() throws RestApiException {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() throws RestApiException {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package com.google.gerrit.extensions.api.plugins;
|
||||
|
||||
import com.google.gerrit.extensions.common.InstallPluginInput;
|
||||
import com.google.gerrit.extensions.common.PluginInfo;
|
||||
import com.google.gerrit.extensions.restapi.NotImplementedException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
@@ -24,7 +25,11 @@ import java.util.SortedMap;
|
||||
|
||||
public interface Plugins {
|
||||
|
||||
ListRequest list();
|
||||
ListRequest list() throws RestApiException;
|
||||
|
||||
PluginApi name(String name) throws RestApiException;
|
||||
|
||||
PluginApi install(String name, InstallPluginInput input) throws RestApiException;
|
||||
|
||||
abstract class ListRequest {
|
||||
private boolean all;
|
||||
@@ -40,8 +45,8 @@ public interface Plugins {
|
||||
|
||||
public abstract SortedMap<String, PluginInfo> getAsMap() throws RestApiException;
|
||||
|
||||
public ListRequest all(boolean all) {
|
||||
this.all = all;
|
||||
public ListRequest all() {
|
||||
this.all = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -59,5 +64,15 @@ public interface Plugins {
|
||||
public ListRequest list() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginApi name(String name) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginApi install(String name, InstallPluginInput input) throws RestApiException {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright (C) 2017 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.api.plugins;
|
||||
|
||||
import com.google.gerrit.extensions.api.plugins.PluginApi;
|
||||
import com.google.gerrit.extensions.common.PluginInfo;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.server.plugins.DisablePlugin;
|
||||
import com.google.gerrit.server.plugins.EnablePlugin;
|
||||
import com.google.gerrit.server.plugins.GetStatus;
|
||||
import com.google.gerrit.server.plugins.PluginResource;
|
||||
import com.google.gerrit.server.plugins.ReloadPlugin;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
||||
public class PluginApiImpl implements PluginApi {
|
||||
public interface Factory {
|
||||
PluginApiImpl create(PluginResource resource);
|
||||
}
|
||||
|
||||
private final GetStatus getStatus;
|
||||
private final EnablePlugin enable;
|
||||
private final DisablePlugin disable;
|
||||
private final ReloadPlugin reload;
|
||||
private final PluginResource resource;
|
||||
|
||||
@Inject
|
||||
PluginApiImpl(
|
||||
GetStatus getStatus,
|
||||
EnablePlugin enable,
|
||||
DisablePlugin disable,
|
||||
ReloadPlugin reload,
|
||||
@Assisted PluginResource resource) {
|
||||
this.getStatus = getStatus;
|
||||
this.enable = enable;
|
||||
this.disable = disable;
|
||||
this.reload = reload;
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginInfo get() throws RestApiException {
|
||||
return getStatus.apply(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() throws RestApiException {
|
||||
enable.apply(resource, new EnablePlugin.Input());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() throws RestApiException {
|
||||
disable.apply(resource, new DisablePlugin.Input());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() throws RestApiException {
|
||||
reload.apply(resource, new ReloadPlugin.Input());
|
||||
}
|
||||
}
|
||||
@@ -14,22 +14,44 @@
|
||||
|
||||
package com.google.gerrit.server.api.plugins;
|
||||
|
||||
import com.google.gerrit.extensions.api.plugins.PluginApi;
|
||||
import com.google.gerrit.extensions.api.plugins.Plugins;
|
||||
import com.google.gerrit.extensions.common.InstallPluginInput;
|
||||
import com.google.gerrit.extensions.common.PluginInfo;
|
||||
import com.google.gerrit.extensions.restapi.Response;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
||||
import com.google.gerrit.server.plugins.InstallPlugin;
|
||||
import com.google.gerrit.server.plugins.ListPlugins;
|
||||
import com.google.gerrit.server.plugins.PluginsCollection;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.util.SortedMap;
|
||||
|
||||
@Singleton
|
||||
public class PluginsImpl implements Plugins {
|
||||
private final PluginsCollection plugins;
|
||||
private final Provider<ListPlugins> listProvider;
|
||||
private final Provider<InstallPlugin> installProvider;
|
||||
private final PluginApiImpl.Factory pluginApi;
|
||||
|
||||
@Inject
|
||||
PluginsImpl(Provider<ListPlugins> listProvider) {
|
||||
PluginsImpl(
|
||||
PluginsCollection plugins,
|
||||
Provider<ListPlugins> listProvider,
|
||||
Provider<InstallPlugin> installProvider,
|
||||
PluginApiImpl.Factory pluginApi) {
|
||||
this.plugins = plugins;
|
||||
this.listProvider = listProvider;
|
||||
this.installProvider = installProvider;
|
||||
this.pluginApi = pluginApi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginApi name(String name) throws RestApiException {
|
||||
return pluginApi.create(plugins.parse(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -43,4 +65,15 @@ public class PluginsImpl implements Plugins {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginApi install(String name, InstallPluginInput input) throws RestApiException {
|
||||
try {
|
||||
Response<PluginInfo> created =
|
||||
installProvider.get().setName(name).apply(TopLevelResource.INSTANCE, input);
|
||||
return pluginApi.create(plugins.parse(created.value().id));
|
||||
} catch (IOException e) {
|
||||
throw new RestApiException("could not install plugin", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ import com.google.inject.Singleton;
|
||||
|
||||
@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
|
||||
@Singleton
|
||||
class DisablePlugin implements RestModifyView<PluginResource, Input> {
|
||||
static class Input {}
|
||||
public class DisablePlugin implements RestModifyView<PluginResource, Input> {
|
||||
public static class Input {}
|
||||
|
||||
private final PluginLoader loader;
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ import java.io.StringWriter;
|
||||
|
||||
@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
|
||||
@Singleton
|
||||
class EnablePlugin implements RestModifyView<PluginResource, Input> {
|
||||
static class Input {}
|
||||
public class EnablePlugin implements RestModifyView<PluginResource, Input> {
|
||||
public static class Input {}
|
||||
|
||||
private final PluginLoader loader;
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
class GetStatus implements RestReadView<PluginResource> {
|
||||
public class GetStatus implements RestReadView<PluginResource> {
|
||||
@Override
|
||||
public PluginInfo apply(PluginResource resource) {
|
||||
return ListPlugins.toPluginInfo(resource.getPlugin());
|
||||
|
||||
@@ -33,7 +33,7 @@ import java.net.URL;
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
|
||||
class InstallPlugin implements RestModifyView<TopLevelResource, InstallPluginInput> {
|
||||
public class InstallPlugin implements RestModifyView<TopLevelResource, InstallPluginInput> {
|
||||
private final PluginLoader loader;
|
||||
|
||||
private String name;
|
||||
|
||||
@@ -309,7 +309,16 @@ public class PluginLoader implements LifecycleListener {
|
||||
@Override
|
||||
public synchronized void start() {
|
||||
removeStalePluginFiles();
|
||||
log.info("Loading plugins from " + pluginsDir.toAbsolutePath());
|
||||
Path absolutePath = pluginsDir.toAbsolutePath();
|
||||
if (!Files.exists(absolutePath)) {
|
||||
log.info(absolutePath + " does not exist; creating");
|
||||
try {
|
||||
Files.createDirectories(absolutePath);
|
||||
} catch (IOException e) {
|
||||
log.error(String.format("Failed to create %s: %s", absolutePath, e.getMessage()));
|
||||
}
|
||||
}
|
||||
log.info("Loading plugins from " + absolutePath);
|
||||
srvInfoImpl.state = ServerInformation.State.STARTUP;
|
||||
rescan();
|
||||
srvInfoImpl.state = ServerInformation.State.RUNNING;
|
||||
|
||||
@@ -19,6 +19,7 @@ import static com.google.gerrit.server.plugins.PluginResource.PLUGIN_KIND;
|
||||
import com.google.gerrit.extensions.api.plugins.Plugins;
|
||||
import com.google.gerrit.extensions.registration.DynamicMap;
|
||||
import com.google.gerrit.extensions.restapi.RestApiModule;
|
||||
import com.google.gerrit.server.api.plugins.PluginApiImpl;
|
||||
import com.google.gerrit.server.api.plugins.PluginsImpl;
|
||||
|
||||
public class PluginRestApiModule extends RestApiModule {
|
||||
@@ -33,5 +34,6 @@ public class PluginRestApiModule extends RestApiModule {
|
||||
post(PLUGIN_KIND, "enable").to(EnablePlugin.class);
|
||||
post(PLUGIN_KIND, "reload").to(ReloadPlugin.class);
|
||||
bind(Plugins.class).to(PluginsImpl.class);
|
||||
factory(PluginApiImpl.Factory.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,11 @@ public class PluginsCollection
|
||||
@Override
|
||||
public PluginResource parse(TopLevelResource parent, IdString id)
|
||||
throws ResourceNotFoundException {
|
||||
Plugin p = loader.get(id.get());
|
||||
return parse(id.get());
|
||||
}
|
||||
|
||||
public PluginResource parse(String id) throws ResourceNotFoundException {
|
||||
Plugin p = loader.get(id);
|
||||
if (p == null) {
|
||||
throw new ResourceNotFoundException(id);
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ import java.io.StringWriter;
|
||||
|
||||
@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
|
||||
@Singleton
|
||||
class ReloadPlugin implements RestModifyView<PluginResource, Input> {
|
||||
static class Input {}
|
||||
public class ReloadPlugin implements RestModifyView<PluginResource, Input> {
|
||||
public static class Input {}
|
||||
|
||||
private final PluginLoader loader;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user