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:
David Pursehouse
2017-07-28 07:36:10 +00:00
committed by Gerrit Code Review
14 changed files with 290 additions and 30 deletions

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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());

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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;