net.oneandone.stool.setup.UpgradeBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of main Show documentation
Show all versions of main Show documentation
Stool's main component. Java Library, cli, setup code.
/**
* Copyright 1&1 Internet AG, https://github.com/1and1/
*
* 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 net.oneandone.stool.setup;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import net.oneandone.inline.Console;
import net.oneandone.stool.cli.Import;
import net.oneandone.stool.cli.Main;
import net.oneandone.stool.configuration.Bedroom;
import net.oneandone.stool.configuration.StageConfiguration;
import net.oneandone.stool.configuration.StoolConfiguration;
import net.oneandone.stool.util.Logging;
import net.oneandone.stool.util.Session;
import net.oneandone.sushi.fs.file.FileNode;
import net.oneandone.sushi.util.Diff;
import net.oneandone.sushi.util.Separator;
import net.oneandone.sushi.util.Strings;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class UpgradeBuilder {
private final Console console;
private final Home home;
private final FileNode from;
// assigned during upgrade:
private StoolConfiguration stoolRaw;
private Session session = null;
private String currentStage = null;
/** maps names to ids */
private Map stages = new HashMap<>();
public UpgradeBuilder(Console console, Home home, FileNode from) {
this.console = console;
this.home = home;
this.from = from;
}
public String version() throws IOException {
return from.join("version").readString().trim();
}
public void run(boolean withConfig) throws IOException {
String oldVersion;
String newVersion;
Upgrade stage33_34;
newVersion = Main.versionString(from.getWorld());
oldVersion = version();
if (!oldVersion.startsWith("3.3.")) {
throw new IOException("don't know how to upgrade " + oldVersion + " -> " + newVersion);
}
console.info.println("upgrade " + oldVersion + " -> " + newVersion);
stage33_34 = stage33_34();
all(withConfig, stool33_34(stage33_34), stage33_34);
}
private void all(boolean withConfig, Upgrade stoolMapper, Upgrade stageMapper) throws IOException {
String id;
FileNode stage;
Logging logging;
Import i;
String originalOwner;
if (withConfig) {
stool(from, stoolMapper);
} else {
stoolRaw = StoolConfiguration.load(home.gson(), home.dir);
}
logging = Logging.forHome(home.dir);
session = Session.load(false, home.dir, logging, "upgrade", console, home.dir.getWorld(), null, null);
if (!session.listWithoutSystem().isEmpty()) {
console.info.println("warning: ports of existing and not imported stages get lost.");
}
for (FileNode oldBackstage : from.join("backstages").list()) {
id = getId(oldBackstage.join("config.json"));
currentStage = oldBackstage.getName();
stages.put(currentStage, id); // always add, because ports file need everything
if (home.dir.join("backstages", id).isDirectory()) {
console.info.println("stage already imported, ignored: " + currentStage + " (" + id + ")");
continue;
}
if ("dashboard".equals(currentStage)) {
console.info.println("not imported: " + currentStage);
continue;
}
console.info.println("import " + oldBackstage);
stage = oldBackstage.join("anchor").resolveLink();
originalOwner = chown(stage, session.user);
i = new Import(session);
i.setUpgradeId(id);
i.dirs(stage.getAbsolute());
i.doRun();
transform(oldBackstage.join("config.json"), stage.join(".backstage/config.json"), stageMapper);
chown(stage, originalOwner);
}
ports();
bedroom();
}
private String chown(FileNode directory, String newOwner) throws IOException {
String was;
was = directory.getOwner().toString();
if (!was.equals(newOwner)) {
session.chown(newOwner, directory);
}
return was;
}
private void ports() throws IOException {
List entries;
List out;
String name;
String id;
out = new ArrayList<>();
for (String line : from.join("run/ports").readLines()) {
entries = Separator.SPACE.split(line);
name = entries.get(2);
id = stages.get(name);
if (id == null) {
console.info.println("note: garbage-collected from ports: " + name);
} else {
entries.add(3, id);
out.add(Separator.SPACE.join(entries));
}
}
home.dir.join("run/ports").writeLines(out);
}
private void bedroom() throws IOException {
Gson gson;
Bedroom old;
Bedroom next;
String id;
gson = home.gson();
old = Bedroom.loadOrCreate(gson, from);
next = Bedroom.loadOrCreate(gson, home.dir);
for (String name : old) {
id = stages.get(name);
if (id == null) {
throw new IllegalStateException(name);
}
next.add(gson, id);
}
}
private static String getId(FileNode config) throws IOException {
JsonObject json;
try (Reader src = config.newReader()) {
json = new JsonParser().parse(src).getAsJsonObject();
return json.get("id").getAsString();
}
}
private void stool(FileNode from, Upgrade stoolMapper) throws IOException {
String in;
String without;
String preliminary; // without the defaults
in = from.join("config.json").readString();
without = Transform.transform(in, new Upgrade() {
void defaultsRemove() {}
});
preliminary = Transform.transform(without, stoolMapper);
stoolRaw = home.gson().fromJson(preliminary, StoolConfiguration.class);
transform(in, home.dir.join("config.json"), stoolMapper);
}
private void transform(FileNode src, FileNode dest, Upgrade mapper) throws IOException {
transform(src.readString(), dest, mapper);
}
private void transform(String in, FileNode dest, Upgrade mapper) throws IOException {
String out;
console.verbose.println("upgrade " + dest.getAbsolute());
out = Transform.transform(in, mapper);
if (!in.equals(out)) {
console.info.println("M " + dest.getAbsolute());
console.info.println(Strings.indent(Diff.diff(in, out), " "));
dest.writeString(out);
}
}
//--
public Upgrade stool33_34(Upgrade stage33_34) {
return new Upgrade() {
void promptRemove() {
}
JsonElement defaultsTransform(JsonElement element) {
JsonObject result;
JsonObject obj;
String url;
JsonObject defaults;
result = new JsonObject();
obj = element.getAsJsonObject();
for (Map.Entry entry : obj.entrySet()) {
url = entry.getKey();
defaults = entry.getValue().getAsJsonObject();
result.add(url.isEmpty() ? "" : "svn:" + url, migrate(defaults));
}
return result;
}
String contactAdminRename() {
return "admin";
}
//--
private JsonObject migrate(JsonObject e) {
String str;
str = Transform.transform(Transform.toString(e), stage33_34);
return new JsonParser().parse(str).getAsJsonObject();
}
};
}
public Upgrade stage33_34() {
return new Upgrade() {
JsonElement extensionsTransform(JsonElement e) {
JsonObject extensions;
JsonObject logstash;
if (session == null) {
// in defaults -- handled in globals below
throw new IllegalStateException();
}
// in stage
extensions = e.getAsJsonObject();
extensions.remove("-pustefix.editor");
extensions.remove("+pustefix.editor");
logstash = new JsonObject();
logstash.add("output", new JsonPrimitive(""));
logstash.add("link", new JsonPrimitive(""));
extensions.add("-logstash", logstash);
return e;
}
void idRemove() {
}
void sslUrlRemove() {
}
void tomcatPermRemove() {
}
String suffixesRename() {
return "url";
}
JsonElement suffixesTransform(JsonElement e) {
String hostpath;
JsonArray array;
hostpath = "%h:%p";
if (session == null) {
// upgrade defaults - contains a string, not an array
array = new JsonArray();
for (String str : Separator.COMMA.split(e.getAsString())) {
array.add(str);
}
} else {
array = e.getAsJsonArray();
}
if (stoolRaw != null && stoolRaw.vhosts) {
hostpath = "%a.%s." + hostpath;
}
hostpath = hostpath + allSuffixes(array);
return new JsonPrimitive("(http|https)://" + hostpath );
}
JsonElement untilTransform(JsonElement e) {
String str;
str = e.getAsString();
if ("reserved".equals(str)) {
return new JsonPrimitive("never");
} else {
return e;
}
}
String untilRename() {
return "expire";
}
void global(JsonObject src, JsonObject dest) {
JsonArray array;
if (session == null) {
// this call is to upgrade defaults
dest.remove("pustefix.editor.version");
dest.remove("pustefix.editor.userdata");
} else {
array = new JsonArray();
array.add(new JsonPrimitive(StageConfiguration.NOTIFY_OWNER));
array.add(new JsonPrimitive(StageConfiguration.NOTIFY_CREATOR));
dest.add("notify", array);
dest.add("quota", new JsonPrimitive(10000));
dest.add("name", new JsonPrimitive(currentStage));
}
}
};
}
public static String allSuffixes(JsonArray array) {
int count;
String str;
StringBuilder builder;
builder = new StringBuilder();
count = 0;
for (JsonElement e : array) {
str = e.getAsString();
if (str.contains("[]")) {
throw new UnsupportedOperationException("don't know how to upgrade suffixes '" + str + "'");
}
if (count > 0) {
builder.append('|');
}
builder.append(str);
count++;
}
return count < 2 ? builder.toString() : "(" + builder.toString() + ")";
}
}