blob: 445c10aad664f85afc0480b6cd8f3e7692953f2f [file] [log] [blame]
// Copyright (C) 2009 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.pgm;
import static com.google.gerrit.server.schema.DataSourceProvider.Context.SINGLE_USER;
import static com.google.inject.Stage.PRODUCTION;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.pgm.init.Browser;
import com.google.gerrit.pgm.init.InitFlags;
import com.google.gerrit.pgm.init.InitModule;
import com.google.gerrit.pgm.init.ReloadSiteLibrary;
import com.google.gerrit.pgm.init.SitePathInitializer;
import com.google.gerrit.pgm.util.ConsoleUI;
import com.google.gerrit.pgm.util.Die;
import com.google.gerrit.pgm.util.ErrorLogFile;
import com.google.gerrit.pgm.util.IoUtil;
import com.google.gerrit.pgm.util.SiteProgram;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.GitProjectImporter;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.LocalDiskRepositoryManager;
import com.google.gerrit.server.schema.SchemaUpdater;
import com.google.gerrit.server.schema.UpdateUI;
import com.google.gerrit.server.util.HostPlatform;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.SchemaFactory;
import com.google.gwtorm.client.StatementExecutor;
import com.google.gwtorm.jdbc.JdbcExecutor;
import com.google.gwtorm.jdbc.JdbcSchema;
import com.google.inject.AbstractModule;
import com.google.inject.CreationException;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.spi.Message;
import org.kohsuke.args4j.Option;
import java.io.Console;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/** Initialize a new Gerrit installation. */
public class Init extends SiteProgram {
@Option(name = "--batch", usage = "Batch mode; skip interactive prompting")
private boolean batchMode;
@Option(name = "--import-projects", usage = "Import git repositories as projects")
private boolean importProjects;
@Option(name = "--no-auto-start", usage = "Don't automatically start daemon after init")
private boolean noAutoStart;
@Override
public int run() throws Exception {
ErrorLogFile.errorOnlyConsole();
final SiteInit init = createSiteInit();
init.flags.importProjects = importProjects;
init.flags.autoStart = !noAutoStart && init.site.isNew;
final SiteRun run;
try {
init.initializer.run();
init.flags.deleteOnFailure = false;
run = createSiteRun(init);
run.upgradeSchema();
run.importGit();
} catch (Exception failure) {
if (init.flags.deleteOnFailure) {
recursiveDelete(getSitePath());
}
throw failure;
} catch (Error failure) {
if (init.flags.deleteOnFailure) {
recursiveDelete(getSitePath());
}
throw failure;
}
System.err.println("Initialized " + getSitePath().getCanonicalPath());
run.start();
return 0;
}
static class SiteInit {
final SitePaths site;
final InitFlags flags;
final ConsoleUI ui;
final SitePathInitializer initializer;
@Inject
SiteInit(final SitePaths site, final InitFlags flags, final ConsoleUI ui,
final SitePathInitializer initializer) {
this.site = site;
this.flags = flags;
this.ui = ui;
this.initializer = initializer;
}
}
private SiteInit createSiteInit() {
final ConsoleUI ui = ConsoleUI.getInstance(batchMode);
final File sitePath = getSitePath();
final List<Module> m = new ArrayList<Module>();
m.add(new InitModule());
m.add(new AbstractModule() {
@Override
protected void configure() {
bind(ConsoleUI.class).toInstance(ui);
bind(File.class).annotatedWith(SitePath.class).toInstance(sitePath);
bind(ReloadSiteLibrary.class).toInstance(new ReloadSiteLibrary() {
@Override
public void reload() {
Init.super.loadSiteLib();
}
});
}
});
try {
return Guice.createInjector(PRODUCTION, m).getInstance(SiteInit.class);
} catch (CreationException ce) {
final Message first = ce.getErrorMessages().iterator().next();
Throwable why = first.getCause();
if (why instanceof Die) {
throw (Die) why;
}
final StringBuilder buf = new StringBuilder();
buf.append(why.getMessage());
why = why.getCause();
while (why != null) {
buf.append("\n caused by ");
buf.append(why.toString());
why = why.getCause();
}
throw die(buf.toString(), new RuntimeException("InitInjector failed", ce));
}
}
static class SiteRun {
final ConsoleUI ui;
final SitePaths site;
final InitFlags flags;
final SchemaUpdater schemaUpdater;
final SchemaFactory<ReviewDb> schema;
final GitRepositoryManager repositoryManager;
final GitProjectImporter gitProjectImporter;
final Browser browser;
@Inject
SiteRun(final ConsoleUI ui, final SitePaths site, final InitFlags flags,
final SchemaUpdater schemaUpdater,
final SchemaFactory<ReviewDb> schema,
final GitRepositoryManager repositoryManager,
final GitProjectImporter gitProjectImporter, final Browser browser) {
this.ui = ui;
this.site = site;
this.flags = flags;
this.schemaUpdater = schemaUpdater;
this.schema = schema;
this.repositoryManager = repositoryManager;
this.gitProjectImporter = gitProjectImporter;
this.browser = browser;
}
void upgradeSchema() throws OrmException {
final List<String> pruneList = new ArrayList<String>();
schemaUpdater.update(new UpdateUI() {
@Override
public void message(String msg) {
System.err.println(msg);
System.err.flush();
}
@Override
public boolean yesno(boolean def, String msg) {
return ui.yesno(def, msg);
}
@Override
public void pruneSchema(StatementExecutor e, List<String> prune) {
for (String p : prune) {
if (!pruneList.contains(p)) {
pruneList.add(p);
}
}
}
});
if (!pruneList.isEmpty()) {
StringBuilder msg = new StringBuilder();
msg.append("Execute the following SQL to drop unused objects:\n");
msg.append("\n");
for (String sql : pruneList) {
msg.append(" ");
msg.append(sql);
msg.append(";\n");
}
if (ui.isBatch()) {
System.err.print(msg);
System.err.flush();
} else if (ui.yesno(true, "%s\nExecute now", msg)) {
final JdbcSchema db = (JdbcSchema) schema.open();
try {
final JdbcExecutor e = new JdbcExecutor(db);
try {
for (String sql : pruneList) {
e.execute(sql);
}
} finally {
e.close();
}
} finally {
db.close();
}
}
}
}
void importGit() throws OrmException, IOException {
if (flags.importProjects) {
gitProjectImporter.run(new GitProjectImporter.Messages() {
@Override
public void info(String msg) {
System.err.println(msg);
System.err.flush();
}
@Override
public void warning(String msg) {
info(msg);
}
});
}
}
void start() throws IOException {
if (flags.autoStart) {
if (HostPlatform.isWin32()) {
System.err.println("Automatic startup not supported on Win32.");
} else {
startDaemon();
if (!ui.isBatch()) {
browser.open(PageLinks.ADMIN_PROJECTS);
}
}
}
}
void startDaemon() {
final String[] argv = {site.gerrit_sh.getAbsolutePath(), "start"};
final Process proc;
try {
System.err.println("Executing " + argv[0] + " " + argv[1]);
proc = Runtime.getRuntime().exec(argv);
} catch (IOException e) {
System.err.println("error: cannot start Gerrit: " + e.getMessage());
return;
}
try {
proc.getOutputStream().close();
} catch (IOException e) {
}
IoUtil.copyWithThread(proc.getInputStream(), System.err);
IoUtil.copyWithThread(proc.getErrorStream(), System.err);
for (;;) {
try {
final int rc = proc.waitFor();
if (rc != 0) {
System.err.println("error: cannot start Gerrit: exit status " + rc);
}
break;
} catch (InterruptedException e) {
// retry
}
}
}
}
private SiteRun createSiteRun(final SiteInit init) {
return createSysInjector(init).getInstance(SiteRun.class);
}
private Injector createSysInjector(final SiteInit init) {
final List<Module> modules = new ArrayList<Module>();
modules.add(new AbstractModule() {
@Override
protected void configure() {
bind(ConsoleUI.class).toInstance(init.ui);
bind(InitFlags.class).toInstance(init.flags);
bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class);
bind(GitProjectImporter.class);
}
});
return createDbInjector(SINGLE_USER).createChildInjector(modules);
}
private static void recursiveDelete(File path) {
File[] entries = path.listFiles();
if (entries != null) {
for (File e : entries) {
recursiveDelete(e);
}
}
if (!path.delete() && path.exists()) {
System.err.println("warn: Cannot remove " + path);
}
}
}