diff options
Diffstat (limited to 'modules')
48 files changed, 1637 insertions, 0 deletions
diff --git a/modules/ri-base/classpath.txt b/modules/ri-base/classpath.txt new file mode 100644 index 0000000..d5370b2 --- /dev/null +++ b/modules/ri-base/classpath.txt @@ -0,0 +1,27 @@ +io.trygvis.rules-sandbox.module:ri-base:1.0-SNAPSHOT:kjar +com.fasterxml.jackson.core:jackson-annotations:2.12.0:jar +com.fasterxml.jackson.core:jackson-core:2.12.0:jar +com.fasterxml.jackson.core:jackson-databind:2.12.0:jar +com.github.javaparser:javaparser-core:3.13.10:jar +com.thoughtworks.xstream:xstream:1.4.14:jar +commons-codec:commons-codec:1.14:jar +commons-io:commons-io:2.8.0:jar +io.trygvis.rules-sandbox.module:ri-module-api:1.0-SNAPSHOT:jar +org.antlr:antlr-runtime:3.5.2:jar +org.drools:drools-canonical-model:7.48.0.Final:jar +org.drools:drools-compiler:7.48.0.Final:jar +org.drools:drools-core:7.48.0.Final:jar +org.drools:drools-core-dynamic:7.48.0.Final:jar +org.drools:drools-core-reflective:7.48.0.Final:jar +org.drools:drools-ecj:7.48.0.Final:jar +org.drools:drools-model-compiler:7.48.0.Final:jar +org.drools:drools-mvel-compiler:7.48.0.Final:jar +org.drools:drools-mvel-parser:7.48.0.Final:jar +org.kie:kie-api:7.48.0.Final:jar +org.kie:kie-internal:7.48.0.Final:jar +org.kie:kie-memory-compiler:7.48.0.Final:jar +org.kie.soup:kie-soup-maven-support:7.48.0.Final:jar +org.kie.soup:kie-soup-xstream:7.48.0.Final:jar +org.slf4j:slf4j-api:1.7.30:jar +xmlpull:xmlpull:1.1.3.1:jar +xpp3:xpp3_min:1.1.4c:jar diff --git a/modules/ri-base/pom.xml b/modules/ri-base/pom.xml new file mode 100644 index 0000000..119368c --- /dev/null +++ b/modules/ri-base/pom.xml @@ -0,0 +1,32 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>io.trygvis.rules-sandbox.module</groupId> + <artifactId>ri-module-parent</artifactId> + <version>1.0-SNAPSHOT</version> + <relativePath>../ri-module-parent/pom.xml</relativePath> + </parent> + + <artifactId>ri-base</artifactId> + <packaging>kjar</packaging> + + <dependencies> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-core</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>ri-module-api</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + +</project> diff --git a/modules/ri-base/src/main/java/io/trygvis/rules/core/Problem.java b/modules/ri-base/src/main/java/io/trygvis/rules/core/Problem.java new file mode 100644 index 0000000..04d1af3 --- /dev/null +++ b/modules/ri-base/src/main/java/io/trygvis/rules/core/Problem.java @@ -0,0 +1,11 @@ +package io.trygvis.rules.core; + +public class Problem { + public final String message; + public final Object object; + + public Problem(String message, Object object) { + this.message = message; + this.object = object; + } +} diff --git a/modules/ri-base/src/main/java/io/trygvis/rules/dba/Cluster.java b/modules/ri-base/src/main/java/io/trygvis/rules/dba/Cluster.java new file mode 100644 index 0000000..6b23cdd --- /dev/null +++ b/modules/ri-base/src/main/java/io/trygvis/rules/dba/Cluster.java @@ -0,0 +1,15 @@ +package io.trygvis.rules.dba; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; + +@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "name") +public class Cluster { + public String name; + + public Cluster(String name) { + this.name = name; + } + + protected Cluster() {} +} diff --git a/modules/ri-base/src/main/java/io/trygvis/rules/dba/Container.java b/modules/ri-base/src/main/java/io/trygvis/rules/dba/Container.java new file mode 100644 index 0000000..a420671 --- /dev/null +++ b/modules/ri-base/src/main/java/io/trygvis/rules/dba/Container.java @@ -0,0 +1,59 @@ +package io.trygvis.rules.dba; + +import io.trygvis.rules.machine.Machine; +import io.trygvis.rules.machine.MachineSpecification; + +//@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") +public class Container { + public String id; + public Cluster cluster; + public String name; + public String machineRole; + public String image; + public String tag; + + private Machine machine; + public MachineSpecification machineSpecification; + + public Container(Cluster cluster, String name, String machineRole, String image, String tag, + MachineSpecification machineSpecification) { + this.id = cluster.name + "-" + name; + this.cluster = cluster; + this.name = name; + this.machineRole = machineRole; + this.image = image; + this.tag = tag; + this.machineSpecification = machineSpecification; + } + + protected Container() { + } + + public Cluster getCluster() { + return cluster; + } + + public String getName() { + return name; + } + + public String getMachineRole() { + return machineRole; + } + + public String getImage() { + return image; + } + + public String getTag() { + return tag; + } + + public Machine getMachine() { + return machine; + } + + public void setMachine(Machine machine) { + this.machine = machine; + } +} diff --git a/modules/ri-base/src/main/java/io/trygvis/rules/dns/DnsEntry.java b/modules/ri-base/src/main/java/io/trygvis/rules/dns/DnsEntry.java new file mode 100644 index 0000000..105ef79 --- /dev/null +++ b/modules/ri-base/src/main/java/io/trygvis/rules/dns/DnsEntry.java @@ -0,0 +1,27 @@ +package io.trygvis.rules.dns; + +public class DnsEntry { + public String fqdn; + public String type; + + public DnsEntry(String fqdn, String type) { + this.fqdn = fqdn; + this.type = type; + } + + public static DnsEntry a(String fqdn) { + return new DnsEntry(fqdn, "A"); + } + + public static DnsEntry aaaa(String fqdn) { + return new DnsEntry(fqdn, "AAAA"); + } + + public String getFqdn() { + return fqdn; + } + + public String getType() { + return type; + } +} diff --git a/modules/ri-base/src/main/java/io/trygvis/rules/dns/DnsEntryTerraformExpression.java b/modules/ri-base/src/main/java/io/trygvis/rules/dns/DnsEntryTerraformExpression.java new file mode 100644 index 0000000..79bf934 --- /dev/null +++ b/modules/ri-base/src/main/java/io/trygvis/rules/dns/DnsEntryTerraformExpression.java @@ -0,0 +1,21 @@ +package io.trygvis.rules.dns; + +public class DnsEntryTerraformExpression { + public DnsEntry entry; + public String key; + public String expression; + + public DnsEntryTerraformExpression(DnsEntry entry, String key, String expression) { + this.entry = entry; + this.key = key; + this.expression = expression; + } + + public String getKey() { + return key; + } + + public String getExpression() { + return expression; + } +} diff --git a/modules/ri-base/src/main/java/io/trygvis/rules/dns/DnsZone.java b/modules/ri-base/src/main/java/io/trygvis/rules/dns/DnsZone.java new file mode 100644 index 0000000..1af5c8f --- /dev/null +++ b/modules/ri-base/src/main/java/io/trygvis/rules/dns/DnsZone.java @@ -0,0 +1,9 @@ +package io.trygvis.rules.dns; + +public class DnsZone { + public final String name; + + public DnsZone(String name) { + this.name = name; + } +} diff --git a/modules/ri-base/src/main/java/io/trygvis/rules/engine/KeyValue.java b/modules/ri-base/src/main/java/io/trygvis/rules/engine/KeyValue.java new file mode 100644 index 0000000..5046169 --- /dev/null +++ b/modules/ri-base/src/main/java/io/trygvis/rules/engine/KeyValue.java @@ -0,0 +1,14 @@ +package io.trygvis.rules.engine; + +public class KeyValue { + public String key; + public String value; + + public KeyValue() { + } + + public KeyValue(String key, String value) { + this.key = key; + this.value = value; + } +} diff --git a/modules/ri-base/src/main/java/io/trygvis/rules/machine/Machine.java b/modules/ri-base/src/main/java/io/trygvis/rules/machine/Machine.java new file mode 100644 index 0000000..8f162c6 --- /dev/null +++ b/modules/ri-base/src/main/java/io/trygvis/rules/machine/Machine.java @@ -0,0 +1,29 @@ +package io.trygvis.rules.machine; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; + +@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "name") +public class Machine { + public String name; + private String fqdn; + + public Machine() { + } + + public Machine(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public String getFqdn() { + return fqdn; + } + + public void setFqdn(String fqdn) { + this.fqdn = fqdn; + } +} diff --git a/modules/ri-base/src/main/java/io/trygvis/rules/machine/MachineSpecification.java b/modules/ri-base/src/main/java/io/trygvis/rules/machine/MachineSpecification.java new file mode 100644 index 0000000..9d38b2d --- /dev/null +++ b/modules/ri-base/src/main/java/io/trygvis/rules/machine/MachineSpecification.java @@ -0,0 +1,21 @@ +package io.trygvis.rules.machine; + +public class MachineSpecification { + public int cpu; + public int memory; + + protected MachineSpecification() {} + + public MachineSpecification(int cpu, int memory) { + this.cpu = cpu; + this.memory = memory; + } + + public int getCpu() { + return cpu; + } + + public int getMemory() { + return memory; + } +} diff --git a/modules/ri-base/src/main/java/io/trygvis/rules/network/IpCalc.java b/modules/ri-base/src/main/java/io/trygvis/rules/network/IpCalc.java new file mode 100644 index 0000000..7ec344c --- /dev/null +++ b/modules/ri-base/src/main/java/io/trygvis/rules/network/IpCalc.java @@ -0,0 +1,6 @@ +package io.trygvis.rules.network; + +import java.util.regex.Pattern; + +public class IpCalc { +} diff --git a/modules/ri-base/src/main/java/io/trygvis/rules/network/Ipv4Address.java b/modules/ri-base/src/main/java/io/trygvis/rules/network/Ipv4Address.java new file mode 100644 index 0000000..9021198 --- /dev/null +++ b/modules/ri-base/src/main/java/io/trygvis/rules/network/Ipv4Address.java @@ -0,0 +1,58 @@ +package io.trygvis.rules.network; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import java.io.IOException; +import java.util.Objects; + +@JsonSerialize(using = Ipv4Address.Serializer.class) +public class Ipv4Address implements Comparable<Ipv4Address> { + public final int address; + + public Ipv4Address(int address) { + this.address = address; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o instanceof Ipv4Address) { + Ipv4Address other = (Ipv4Address) o; + return address == other.address; + } + + return false; + } + + @Override + public int hashCode() { + return Objects.hash(address); + } + + @Override + public int compareTo(Ipv4Address o) { + return address - o.address; + } + + @Override + public String toString() { + return "%d.%d.%d.%d".formatted( + address >> 24 & 0xff, + address >> 16 & 0xff, + address >> 8 & 0xff, + address & 0xff); + } + + public static class Serializer extends JsonSerializer<Ipv4Address> { + @Override + public void serialize(Ipv4Address value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeStartObject(); + gen.writeObjectField("value", value.toString()); + gen.writeEndObject(); + } + } +} diff --git a/modules/ri-base/src/main/java/io/trygvis/rules/network/Ipv4Cidr.java b/modules/ri-base/src/main/java/io/trygvis/rules/network/Ipv4Cidr.java new file mode 100644 index 0000000..851af95 --- /dev/null +++ b/modules/ri-base/src/main/java/io/trygvis/rules/network/Ipv4Cidr.java @@ -0,0 +1,150 @@ +package io.trygvis.rules.network; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.regex.Pattern; + +@JsonSerialize(using = Ipv4Cidr.Serializer.class) +public class Ipv4Cidr implements Comparable<Ipv4Cidr> { + public final int network; + public final int bits; + + public Ipv4Cidr(int network, int bits) { + if (bits < 0 || bits > 32) { + throw new IllegalArgumentException("bits must be [0, 32]"); + } + + int hostBits = 32 - bits; + int netmask = (-1 >> hostBits) << hostBits; + + int x = network & ~netmask; + + if (x != 0) { + throw new IllegalArgumentException("The host part of the address must be 0."); + } + + this.network = network; + this.bits = bits; + } + + @Override + public String toString() { + return "%d.%d.%d.%d/%d".formatted( + network >> 24 & 0xff, + network >> 16 & 0xff, + network >> 8 & 0xff, + network & 0xff, + bits); + } + + private String formatIpv4(int address) { + return "%d.%d.%d.%d".formatted(address >> 24 & 0xff, address >> 16 & 0xff, address >> 8 & 0xff, address & 0xff); + } + + public List<Ipv4Cidr> partition(int bits) { + if (bits <= 0 || bits <= this.bits || bits > 32) { + throw new IllegalArgumentException("Invalid new network size"); + } + + var list = new ArrayList<Ipv4Cidr>(); + + int count = 1 << (bits - this.bits); + for (int i = 0; i < count; i++) { + var network = this.network | (i << (32 - bits)); + + list.add(new Ipv4Cidr(network, bits)); + } + + return list; + } + + public List<Ipv4Address> addresses() { + int size = 1 << 32 - bits; + var end = network + size; + var addresses = new ArrayList<Ipv4Address>(size); + for (int address = network; address < end; address++) { + addresses.add(new Ipv4Address(address)); + } + + return addresses; + } + + @Override + public int compareTo(Ipv4Cidr o) { + if (this == o) { + return 0; + } + + var ret = network - o.network; + if (ret != 0) { + return ret; + } + + return bits - o.bits; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Ipv4Cidr ipv4Cidr = (Ipv4Cidr) o; + return network == ipv4Cidr.network && bits == ipv4Cidr.bits; + } + + @Override + public int hashCode() { + return Objects.hash(network, bits); + } + + private static final Pattern pattern = Pattern.compile("([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})/([0-9]{1,3})"); + + public static Ipv4Cidr parseCidr(String cidr) { + var matcher = pattern.matcher(cidr); + if (!matcher.matches()) { + throw new IllegalArgumentException("Not a CIDR: " + cidr); + } + + var b1 = matcher.group(1); + var b2 = matcher.group(2); + var b3 = matcher.group(3); + var b4 = matcher.group(4); + + int network = parse(b1) << 24 | + parse(b2) << 16 | + parse(b3) << 8 | + parse(b4); + +// System.out.printf("network = %x%n", network); + + var l = matcher.group(5); + var bits = Integer.parseInt(l); +// System.out.printf("netmask = %08x%n", netmask); + + return new Ipv4Cidr(network, bits); + } + + private static int parse(String s) { + var i = Integer.parseInt(s); + if (i > 255) { + throw new IllegalArgumentException("Not a CIDR"); + } + + return i; + } + + public static class Serializer extends JsonSerializer<Ipv4Cidr> { + @Override + public void serialize(Ipv4Cidr value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeStartObject(); + gen.writeObjectField("value", value.toString()); + gen.writeEndObject(); + } + } +} diff --git a/modules/ri-base/src/main/java/io/trygvis/rules/terraform/TerraformInputVariable.java b/modules/ri-base/src/main/java/io/trygvis/rules/terraform/TerraformInputVariable.java new file mode 100644 index 0000000..1415767 --- /dev/null +++ b/modules/ri-base/src/main/java/io/trygvis/rules/terraform/TerraformInputVariable.java @@ -0,0 +1,41 @@ +package io.trygvis.rules.terraform; + +public class TerraformInputVariable { + private String module; + + private String name; + private String type; + private String default_; + + public TerraformInputVariable(String module) { + this.module = module; + } + + public String getModule() { + return module; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getDefault() { + return default_; + } + + public void setDefault(String default_) { + this.default_ = default_; + } +} diff --git a/modules/ri-base/src/main/java/io/trygvis/rules/terraform/TerraformMain.java b/modules/ri-base/src/main/java/io/trygvis/rules/terraform/TerraformMain.java new file mode 100644 index 0000000..82626d2 --- /dev/null +++ b/modules/ri-base/src/main/java/io/trygvis/rules/terraform/TerraformMain.java @@ -0,0 +1,21 @@ +package io.trygvis.rules.terraform; + +import java.util.Map; +import java.util.TreeMap; + +public class TerraformMain { + private String module; + private Map<String, TerraformProvider> providers = new TreeMap<String, TerraformProvider>(); + + public TerraformMain(String module) { + this.module = module; + } + + public String getModule() { + return module; + } + + public Map<String, TerraformProvider> getProviders() { + return providers; + } +} diff --git a/modules/ri-base/src/main/java/io/trygvis/rules/terraform/TerraformProvider.java b/modules/ri-base/src/main/java/io/trygvis/rules/terraform/TerraformProvider.java new file mode 100644 index 0000000..613d953 --- /dev/null +++ b/modules/ri-base/src/main/java/io/trygvis/rules/terraform/TerraformProvider.java @@ -0,0 +1,22 @@ +package io.trygvis.rules.terraform; + +public class TerraformProvider { + private String source; + private String version; + + protected TerraformProvider() { + } + + public TerraformProvider(String source, String version) { + this.source = source; + this.version = version; + } + + public String getSource() { + return source; + } + + public String getVersion() { + return version; + } +} diff --git a/modules/ri-base/src/main/java/io/trygvis/rules/terraform/TerraformResource.java b/modules/ri-base/src/main/java/io/trygvis/rules/terraform/TerraformResource.java new file mode 100644 index 0000000..351a3e1 --- /dev/null +++ b/modules/ri-base/src/main/java/io/trygvis/rules/terraform/TerraformResource.java @@ -0,0 +1,213 @@ +package io.trygvis.rules.terraform; + +import com.fasterxml.jackson.annotation.JsonValue; + +import java.util.*; + +@SuppressWarnings("unused") +public class TerraformResource { + private static final String EOL = System.getProperty("line.separator"); + private final String output; + private final String kind; + private final String instance; + private final Map<String, TerraformValue> values = new LinkedHashMap<>(); + + public TerraformResource(String output, String kind, String instance) { + this.output = output; + this.kind = kind; + this.instance = instance; + } + + public String getOutput() { + return output; + } + + public String getName() { + return kind + "." + instance; + } + + public String getKind() { + return kind; + } + + public String getInstance() { + return instance; + } + + public Map<String, TerraformValue> getValues() { + return values; + } + + public TerraformResource set(String key, String value) { + values.put(key, new StringTerraformValue(value)); + return this; + } + + public TerraformResource set(String key, boolean value) { + values.put(key, new BooleanTerraformValue(value)); + return this; + } + + public TerraformResource set(String key, int value) { + values.put(key, new IntegerTerraformValue(value)); + return this; + } + + public TerraformResource setExpression(String key, String value) { + values.put(key, new ExpressionTerraformValue(value)); + return this; + } + + public ArrayTerraformValue array(String key) { + var array = new ArrayTerraformValue(); + values.put(key, array); + return array; + } + + public String asString() { + var buf = new StringBuilder(); + + buf.append("resource "); + buf.append(quote(kind)); + buf.append(" "); + buf.append(quote(instance)); + buf.append(" {"); + buf.append(EOL); + + for (var entry : values.entrySet()) { + buf.append(" "); + buf.append(entry.getKey()); + buf.append(" = "); + buf.append(entry.getValue().asString()); + buf.append(EOL); + } + + buf.append("}"); + + return buf.toString(); + } + + public interface TerraformValue { + String asString(); + } + + public static class StringTerraformValue implements TerraformValue { + private final String value; + + public StringTerraformValue(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String asString() { + return quote(value); + } + } + + public static class BooleanTerraformValue implements TerraformValue { + private final boolean value; + + public BooleanTerraformValue(boolean value) { + this.value = value; + } + + @JsonValue + public boolean getValue() { + return value; + } + + @Override + public String asString() { + return String.valueOf(value); + } + } + + public static class IntegerTerraformValue implements TerraformValue { + private final int value; + + public IntegerTerraformValue(int value) { + this.value = value; + } + + @JsonValue + public int getValue() { + return value; + } + + @Override + public String asString() { + return String.valueOf(value); + } + } + + public static class ExpressionTerraformValue implements TerraformValue { + private final String value; + + public ExpressionTerraformValue(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String asString() { + return value; + } + } + + public static class ArrayTerraformValue implements TerraformValue { + private final List<TerraformValue> values = new ArrayList<>(); + + @JsonValue + public List<TerraformValue> getValues() { + return values; + } + + public ArrayTerraformValue add(String value) { + values.add(new StringTerraformValue(value)); + return this; + } + + public ArrayTerraformValue add(int value) { + values.add(new IntegerTerraformValue(value)); + return this; + } + + public ArrayTerraformValue add(boolean value) { + values.add(new BooleanTerraformValue(value)); + return this; + } + + public ArrayTerraformValue addExpression(String value) { + values.add(new ExpressionTerraformValue(value)); + return this; + } + + @Override + public String asString() { + if (values.isEmpty()) { + return "[]"; + } + + if (values.size() == 1) { + return "[" + values.get(0).asString() + "]"; + } + + var j = new StringJoiner(",\n ", "[\n", "\n ]"); + values.forEach(value -> j.add(value.asString())); + return j.toString(); + } + } + + private static String quote(String value) { + return "\"%s\"".formatted(value); + } +} diff --git a/modules/ri-base/src/main/resources/META-INF/kmodule.xml b/modules/ri-base/src/main/resources/META-INF/kmodule.xml new file mode 100644 index 0000000..4650977 --- /dev/null +++ b/modules/ri-base/src/main/resources/META-INF/kmodule.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://www.drools.org/xsd/kmodule" + xsi:schemaLocation="http://www.drools.org/xsd/kmodule https://www.drools.org/xsd/kmodule_7_1.xsd"> + + <kbase name="all" packages="io.trygvis.rules.*"/> + <kbase name="engine" packages="io.trygvis.rules.engine"/> + <kbase name="dba" packages="io.trygvis.rules.dba"/> + <kbase name="machine" packages="io.trygvis.rules.machine"/> + <kbase name="terraform" packages="io.trygvis.rules.terraform"/> + <kbase name="scaleway" packages="io.trygvis.rules.scaleway"/> +</kmodule> diff --git a/modules/ri-base/src/main/resources/io/trygvis/rules/dba/dba.drl b/modules/ri-base/src/main/resources/io/trygvis/rules/dba/dba.drl new file mode 100644 index 0000000..c31bc57 --- /dev/null +++ b/modules/ri-base/src/main/resources/io/trygvis/rules/dba/dba.drl @@ -0,0 +1,69 @@ +package io.trygvis.rules.dba + +import io.trygvis.rules.core.Problem +import io.trygvis.rules.machine.Machine +import java.util.ArrayList +import java.util.Map +import java.util.HashMap +import java.util.List +import java.util.stream.Collectors +import java.util.Collections + +global io.trygvis.rules.engine.TemplateEngine te; + +dialect "mvel" + +declare DbaMachineRole + machine : String + roles : String[] +end + +rule "Assign containers to machine" +when + $machine : Machine() + $machineRole : DbaMachineRole(machine == $machine.name) + $container : Container(machine == null, $machineRole.roles contains machineRole) +then + System.out.println("Assigning container to machine: " + $machine.name); + modify ($container) { + machine = $machine + } +end + +rule "Containers without hosts" + agenda-group "generate" +when + $container : Container(machine == null) +then + insert(new Problem("No machine for container", $container)) +end + +rule "Generate docker-compose.yaml" + agenda-group "generate" +when + $cluster : Cluster() + $containers : ArrayList(size > 0) from collect(Container(cluster == $cluster)) +then + System.out.println("Docker compose for cluster: " + $cluster.name + " with " + $containers.size() + " containers"); + + Map containersByMachine = new HashMap(); + for (Object o : $containers) { + Container c = (Container) o; + + List list = (List) containersByMachine.get(c.getMachine()); + if (list == null) { + list = new ArrayList(); + containersByMachine.put(c.getMachine(), list); + } + list.add(c); + } + + System.out.println("containersByMachine = " + containersByMachine); + + String path = "ansible/dba/" + $cluster.name + ".yml"; + te.template("dba/cluster", path, Map.of( + "cluster", $cluster, + "containers", $containers, + "containersByMachine", containersByMachine + )); +end diff --git a/modules/ri-base/src/main/resources/io/trygvis/rules/engine/init.drl b/modules/ri-base/src/main/resources/io/trygvis/rules/engine/init.drl new file mode 100644 index 0000000..267cc4a --- /dev/null +++ b/modules/ri-base/src/main/resources/io/trygvis/rules/engine/init.drl @@ -0,0 +1,17 @@ +package io.trygvis.rules.engine; + +import java.util.Map +import org.apache.commons.io.FileSystem +import org.apache.commons.io.FileUtils +import java.io.File + +global io.trygvis.rules.engine.TemplateEngine te; + +rule "Clean directories" + agenda-group "init" +when + not(KeyValue(key == "rm-gen")); +then + te.clean(); + insert(new KeyValue("rm-gen", null)); +end diff --git a/modules/ri-base/src/main/resources/io/trygvis/rules/machine/machine.drl b/modules/ri-base/src/main/resources/io/trygvis/rules/machine/machine.drl new file mode 100644 index 0000000..0250cc6 --- /dev/null +++ b/modules/ri-base/src/main/resources/io/trygvis/rules/machine/machine.drl @@ -0,0 +1 @@ +package io.trygvis.rules.machine; diff --git a/modules/ri-base/src/main/resources/io/trygvis/rules/scaleway/terraform.drl b/modules/ri-base/src/main/resources/io/trygvis/rules/scaleway/terraform.drl new file mode 100644 index 0000000..7e0ff03 --- /dev/null +++ b/modules/ri-base/src/main/resources/io/trygvis/rules/scaleway/terraform.drl @@ -0,0 +1,81 @@ +package io.trygvis.rules.scaleway + +import io.trygvis.rules.dba.Cluster +import io.trygvis.rules.dba.Container +import io.trygvis.rules.machine.Machine +import io.trygvis.rules.dns.DnsEntry +import io.trygvis.rules.dns.DnsEntryTerraformExpression +import java.util.ArrayList; +import java.util.Map; + +global io.trygvis.rules.engine.TemplateEngine te; + +dialect "mvel" + +declare ScalewayMachine + machine : Machine + key : String +end + +declare GoogleManagedZoneTerraformExpression + name : String +end + +rule "Terraform for Machine" +when + $machine: Machine() +then + ScalewayMachine scw = new ScalewayMachine(); + scw.setKey($machine.name); + scw.setMachine($machine); + + insert(scw); +end + +rule "Create DNS entry for Terraform Machine" +when + $machine : Machine(fqdn != null) + not(DnsEntry(fqdn == $machine.fqdn)) +then + DnsEntry a = DnsEntry.a($machine.fqdn); + insert(a); + + String ipv4 = "scaleway_instance_ip.%s.address".formatted($machine.name); + insert(new DnsEntryTerraformExpression(a, $machine.name, ipv4)); +end + +rule "main-scaleway-machine.tf" + agenda-group "generate" +when + $managedZones : ArrayList() from collect(GoogleManagedZoneTerraformExpression()) +then + String path = "terraform/main-scaleway-machine.tf"; + te.template("terraform/main-scaleway-machine", path, Map.of( + "managedZones", $managedZones + )); +end + +rule "TF for TerraformMachine" + agenda-group "generate" +when + $m: Machine() + $scw: ScalewayMachine(machine == $m) +then + String path = "terraform/scaleway-machine-%s.tf".formatted($scw.getKey()); + te.template("terraform/machine", path, Map.of("m", $m, "scw", $scw)); +end + +rule "Terraform for DNS" + agenda-group "generate" +when + $entry: DnsEntry() + $tf : DnsEntryTerraformExpression(entry == $entry) + $managedZone : GoogleManagedZoneTerraformExpression() +then + String path = "terraform/dns-%s.tf".formatted($tf.key); + te.template("terraform/record-set", path, Map.of( + "entry", $entry, + "managedZone", $managedZone, + "tf", $tf) + ); +end diff --git a/modules/ri-base/src/main/resources/io/trygvis/rules/terraform/terraform-resources.drl b/modules/ri-base/src/main/resources/io/trygvis/rules/terraform/terraform-resources.drl new file mode 100644 index 0000000..40a81f0 --- /dev/null +++ b/modules/ri-base/src/main/resources/io/trygvis/rules/terraform/terraform-resources.drl @@ -0,0 +1,28 @@ +package io.trygvis.rules.terraform; + +import java.util.ArrayList +import java.util.HashSet +import java.util.Map +import java.util.Set + +global io.trygvis.rules.engine.TemplateEngine te; + +dialect "mvel" + +rule "terraform-resources" + agenda-group "generate" +when + $r : TerraformResource() +then + te.template("terraform/resource", $r.output, Map.of("resource", $r)); +end + +rule "main.tf" + agenda-group "generate" +when + $main : TerraformMain() + $variables : ArrayList() from collect(TerraformInputVariable(module == $main.module)) +then + te.template("terraform/main", $main.module + "/main.tf", Map.of("main", $main)); + te.template("terraform/variables", $main.module + "/vars.tf", Map.of("variables", $variables)); +end diff --git a/modules/ri-base/src/main/resources/templates/dba/cluster.j2 b/modules/ri-base/src/main/resources/templates/dba/cluster.j2 new file mode 100644 index 0000000..045fbcd --- /dev/null +++ b/modules/ri-base/src/main/resources/templates/dba/cluster.j2 @@ -0,0 +1,19 @@ +# Generated + +# cluster: {{ cluster.name }} +{%- for m, containers in containersByMachine.entrySet() %} +--- +- host: + - {{ m.name }} + tasks: + import_role: + name: docker-service + vars: + template: | + version: "3" + services: +{%- for c in containers %} + {{ c.name }}: + image: {{ c.image }}:{{ c.tag }} +{%- endfor %} +{% endfor %} diff --git a/modules/ri-base/src/main/resources/templates/platform-ansible.j2 b/modules/ri-base/src/main/resources/templates/platform-ansible.j2 new file mode 100644 index 0000000..72e3247 --- /dev/null +++ b/modules/ri-base/src/main/resources/templates/platform-ansible.j2 @@ -0,0 +1,6 @@ +# Ansible +- hosts: + - {{ m.key }} + tasks: + - import_role: + name: acme-platform diff --git a/modules/ri-base/src/main/resources/templates/terraform/machine-outputs.j2 b/modules/ri-base/src/main/resources/templates/terraform/machine-outputs.j2 new file mode 100644 index 0000000..438fac6 --- /dev/null +++ b/modules/ri-base/src/main/resources/templates/terraform/machine-outputs.j2 @@ -0,0 +1,7 @@ +output "addresses" { + value = { +{%- for m in machines %} + {{ m.key }}: scaleway_instance_ip.{{ m.key }}.address, +{%- endfor %} + } +} diff --git a/modules/ri-base/src/main/resources/templates/terraform/machine.j2 b/modules/ri-base/src/main/resources/templates/terraform/machine.j2 new file mode 100644 index 0000000..0ac8b90 --- /dev/null +++ b/modules/ri-base/src/main/resources/templates/terraform/machine.j2 @@ -0,0 +1,19 @@ +resource "scaleway_instance_server" "{{ scw.key }}" { + name = "acme-1" + type = "DEV1-S" + image = "b3042271-d2b1-4f87-b407-aedd3bbd1663" + ip_id = scaleway_instance_ip.{{ scw.key }}.id + enable_dynamic_ip = false + enable_ipv6 = true +} + +resource "scaleway_instance_ip" "{{ scw.key }}" {} + +resource "scaleway_instance_ip_reverse_dns" "{{ scw.key }}" { + ip_id = scaleway_instance_ip.{{ scw.key }}.id + reverse = "{{ m.fqdn }}." +} + +output "{{scw.key}}_public_ip" { + value = scaleway_instance_server.{{ scw.key }}.public_ip +} diff --git a/modules/ri-base/src/main/resources/templates/terraform/main-scaleway-machine.j2 b/modules/ri-base/src/main/resources/templates/terraform/main-scaleway-machine.j2 new file mode 100644 index 0000000..eab9fac --- /dev/null +++ b/modules/ri-base/src/main/resources/templates/terraform/main-scaleway-machine.j2 @@ -0,0 +1,19 @@ +# Generated + +terraform { + required_providers { + scaleway = { + source = "scaleway/scaleway" + version = "1.17.2" + } + } +} + +provider "scaleway" { +} + +{% -for z in managedZones %} +variable "{{z.name}}" { + type = string +} +{% endfor -%} diff --git a/modules/ri-base/src/main/resources/templates/terraform/main.j2 b/modules/ri-base/src/main/resources/templates/terraform/main.j2 new file mode 100644 index 0000000..009f4ed --- /dev/null +++ b/modules/ri-base/src/main/resources/templates/terraform/main.j2 @@ -0,0 +1,10 @@ +terraform { + required_providers { +{% for alias, provider in main.providers.entrySet() %} + {{ alias }} = { + version = "{{ provider.version }}" + source = "{{ provider.source }}" + } +{%- endfor %} + } +} diff --git a/modules/ri-base/src/main/resources/templates/terraform/record-set.j2 b/modules/ri-base/src/main/resources/templates/terraform/record-set.j2 new file mode 100644 index 0000000..b89ee00 --- /dev/null +++ b/modules/ri-base/src/main/resources/templates/terraform/record-set.j2 @@ -0,0 +1,8 @@ +resource "google_dns_record_set" "{{ tf.key }}" { + name = "{{ entry.fqdn }}" + managed_zone = var.{{ managedZone.name }} + type = "{{ entry.type }}" + ttl = 300 + + rrdatas = [{{ tf.expression }}] +} diff --git a/modules/ri-base/src/main/resources/templates/terraform/resource.j2 b/modules/ri-base/src/main/resources/templates/terraform/resource.j2 new file mode 100644 index 0000000..91af481 --- /dev/null +++ b/modules/ri-base/src/main/resources/templates/terraform/resource.j2 @@ -0,0 +1 @@ +{{ resource.asString() -}} diff --git a/modules/ri-base/src/main/resources/templates/terraform/variables.j2 b/modules/ri-base/src/main/resources/templates/terraform/variables.j2 new file mode 100644 index 0000000..6c37d99 --- /dev/null +++ b/modules/ri-base/src/main/resources/templates/terraform/variables.j2 @@ -0,0 +1,10 @@ +{%- for var in variables %} +variable "{{ var.name }}" { +{%- if var.type %} + type = {{ var.type }} +{% endif %} +{%- if var.default %} + default = {{ var.default }} +{% endif %} +} +{% endfor %} diff --git a/modules/ri-base/src/test/java/io/trygvis/rules/network/Ipv4CidrTest.txt b/modules/ri-base/src/test/java/io/trygvis/rules/network/Ipv4CidrTest.txt new file mode 100644 index 0000000..826f586 --- /dev/null +++ b/modules/ri-base/src/test/java/io/trygvis/rules/network/Ipv4CidrTest.txt @@ -0,0 +1,37 @@ +package io.trygvis.rules.network; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static io.trygvis.rules.network.Ipv4Cidr.parseCidr; +import static org.junit.jupiter.api.Assertions.*; + +class Ipv4CidrTest { + + @Test + public void basic() { + Assertions.assertThrows(IllegalArgumentException.class, () -> parseCidr("192.168.1.1/24").addresses()); + assertEquals(256, parseCidr("192.168.1.0/24").addresses().size()); + assertEquals(128, parseCidr("192.168.1.128/25").addresses().size()); + } + + @ParameterizedTest + @ValueSource(strings = { + "192.168.1.0/24", + "192.168.1.128/25", + }) + public void testParsing(String s) { + assertEquals(s, parseCidr(s).toString()); + } + + @Test + public void partition() { + var children = parseCidr("192.168.1.0/24").partition(26); + assertEquals(1 << 2, children.size()); + for (var cidr : children) { + assertEquals(26, cidr.bits); + } + } +} diff --git a/modules/ri-docker/pom.xml b/modules/ri-docker/pom.xml new file mode 100644 index 0000000..49729da --- /dev/null +++ b/modules/ri-docker/pom.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>io.trygvis.rules-sandbox.module</groupId> + <artifactId>ri-module-parent</artifactId> + <version>1.0-SNAPSHOT</version> + <relativePath>../ri-module-parent/pom.xml</relativePath> + </parent> + + <artifactId>ri-docker</artifactId> + <packaging>kjar</packaging> + + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>ri-base</artifactId> + <version>1.0-SNAPSHOT</version> + </dependency> + </dependencies> + +</project> diff --git a/modules/ri-module-api/classpath.txt b/modules/ri-module-api/classpath.txt new file mode 100644 index 0000000..1f0f0a6 --- /dev/null +++ b/modules/ri-module-api/classpath.txt @@ -0,0 +1,23 @@ +io.trygvis.rules-sandbox.module:ri-module-api:1.0-SNAPSHOT:jar +com.github.javaparser:javaparser-core:3.13.10:jar +com.thoughtworks.xstream:xstream:1.4.14:jar +commons-codec:commons-codec:1.14:jar +commons-io:commons-io:2.8.0:jar +org.antlr:antlr-runtime:3.5.2:jar +org.drools:drools-canonical-model:7.48.0.Final:jar +org.drools:drools-compiler:7.48.0.Final:jar +org.drools:drools-core:7.48.0.Final:jar +org.drools:drools-core-dynamic:7.48.0.Final:jar +org.drools:drools-core-reflective:7.48.0.Final:jar +org.drools:drools-ecj:7.48.0.Final:jar +org.drools:drools-model-compiler:7.48.0.Final:jar +org.drools:drools-mvel-compiler:7.48.0.Final:jar +org.drools:drools-mvel-parser:7.48.0.Final:jar +org.kie:kie-api:7.48.0.Final:jar +org.kie:kie-internal:7.48.0.Final:jar +org.kie:kie-memory-compiler:7.48.0.Final:jar +org.kie.soup:kie-soup-maven-support:7.48.0.Final:jar +org.kie.soup:kie-soup-xstream:7.48.0.Final:jar +org.slf4j:slf4j-api:1.7.30:jar +xmlpull:xmlpull:1.1.3.1:jar +xpp3:xpp3_min:1.1.4c:jar diff --git a/modules/ri-module-api/pom.xml b/modules/ri-module-api/pom.xml new file mode 100644 index 0000000..118b5ba --- /dev/null +++ b/modules/ri-module-api/pom.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>io.trygvis.rules-sandbox.module</groupId> + <artifactId>ri-module-parent</artifactId> + <version>1.0-SNAPSHOT</version> + <relativePath>../ri-module-parent/pom.xml</relativePath> + </parent> + + <artifactId>ri-module-api</artifactId> + +</project> diff --git a/modules/ri-module-api/src/main/java/io/trygvis/rules/engine/TemplateEngine.java b/modules/ri-module-api/src/main/java/io/trygvis/rules/engine/TemplateEngine.java new file mode 100644 index 0000000..eafa6e4 --- /dev/null +++ b/modules/ri-module-api/src/main/java/io/trygvis/rules/engine/TemplateEngine.java @@ -0,0 +1,10 @@ +package io.trygvis.rules.engine; + +import java.io.IOException; +import java.util.Map; + +public interface TemplateEngine { + void clean(); + + void template(String name, String output, Map<String, Object> params) throws IOException; +} diff --git a/modules/ri-module-parent/classpath.txt b/modules/ri-module-parent/classpath.txt new file mode 100644 index 0000000..8624088 --- /dev/null +++ b/modules/ri-module-parent/classpath.txt @@ -0,0 +1,23 @@ +io.trygvis.rules-sandbox.module:ri-module-parent:1.0-SNAPSHOT:pom +com.github.javaparser:javaparser-core:3.13.10:jar +com.thoughtworks.xstream:xstream:1.4.14:jar +commons-codec:commons-codec:1.14:jar +commons-io:commons-io:2.8.0:jar +org.antlr:antlr-runtime:3.5.2:jar +org.drools:drools-canonical-model:7.48.0.Final:jar +org.drools:drools-compiler:7.48.0.Final:jar +org.drools:drools-core:7.48.0.Final:jar +org.drools:drools-core-dynamic:7.48.0.Final:jar +org.drools:drools-core-reflective:7.48.0.Final:jar +org.drools:drools-ecj:7.48.0.Final:jar +org.drools:drools-model-compiler:7.48.0.Final:jar +org.drools:drools-mvel-compiler:7.48.0.Final:jar +org.drools:drools-mvel-parser:7.48.0.Final:jar +org.kie:kie-api:7.48.0.Final:jar +org.kie:kie-internal:7.48.0.Final:jar +org.kie:kie-memory-compiler:7.48.0.Final:jar +org.kie.soup:kie-soup-maven-support:7.48.0.Final:jar +org.kie.soup:kie-soup-xstream:7.48.0.Final:jar +org.slf4j:slf4j-api:1.7.30:jar +xmlpull:xmlpull:1.1.3.1:jar +xpp3:xpp3_min:1.1.4c:jar diff --git a/modules/ri-module-parent/pom.xml b/modules/ri-module-parent/pom.xml new file mode 100644 index 0000000..d593ca8 --- /dev/null +++ b/modules/ri-module-parent/pom.xml @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <artifactId>ri-module-parent</artifactId> + <groupId>io.trygvis.rules-sandbox.module</groupId> + <version>1.0-SNAPSHOT</version> + <packaging>pom</packaging> + + <properties> + <version.drools>7.48.0.Final</version.drools> + <java.version>13</java.version> + </properties> + + <dependencies> + <dependency> + <groupId>org.drools</groupId> + <artifactId>drools-model-compiler</artifactId> + </dependency> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + </dependency> + </dependencies> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>io.trygvis.rules-sandbox</groupId> + <artifactId>ri-engine</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <build> + <finalName>${project.artifactId}</finalName> + <plugins> + <plugin> + <groupId>org.kie</groupId> + <artifactId>kie-maven-plugin</artifactId> + <extensions>true</extensions> + </plugin> + <plugin> + <groupId>eu.nets.oss.maven</groupId> + <artifactId>classpath-maven-plugin</artifactId> + <executions> + <execution> + <id>default</id> + <goals> + <goal>export-classpath</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + <pluginManagement> + <plugins> + <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.--> + <plugin> + <groupId>org.eclipse.m2e</groupId> + <artifactId>lifecycle-mapping</artifactId> + <version>1.0.0</version> + <configuration> + <lifecycleMappingMetadata> + <pluginExecutions> + <pluginExecution> + <pluginExecutionFilter> + <groupId>org.kie</groupId> + <artifactId>kie-maven-plugin</artifactId> + <versionRange>[7.48.0.Final,)</versionRange> + <goals> + <goal>generatePMMLModel</goal> + <goal>generateDMNModel</goal> + <goal>build</goal> + <goal>generateANC</goal> + <goal>generateModel</goal> + <goal>validateDMN</goal> + <goal>injectreactive</goal> + </goals> + </pluginExecutionFilter> + <action> + <ignore></ignore> + </action> + </pluginExecution> + </pluginExecutions> + </lifecycleMappingMetadata> + </configuration> + </plugin> + <plugin> + <groupId>org.kie</groupId> + <artifactId>kie-maven-plugin</artifactId> + <version>${version.drools}</version> + <extensions>true</extensions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.8.1</version> + <configuration> + <encoding>UTF-8</encoding> + <release>${java.version}</release> + <source>${java.version}</source> + <target>${java.version}</target> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-resources-plugin</artifactId> + <version>3.2.0</version> + <configuration> + <encoding>UTF-8</encoding> + </configuration> + </plugin> + <plugin> + <groupId>eu.nets.oss.maven</groupId> + <artifactId>classpath-maven-plugin</artifactId> + <version>1.0</version> + </plugin> + </plugins> + </pluginManagement> + </build> + +</project> diff --git a/modules/ri-wireguard/classpath.txt b/modules/ri-wireguard/classpath.txt new file mode 100644 index 0000000..ef1b057 --- /dev/null +++ b/modules/ri-wireguard/classpath.txt @@ -0,0 +1,28 @@ +io.trygvis.rules-sandbox.module:ri-wireguard:1.0-SNAPSHOT:kjar +com.fasterxml.jackson.core:jackson-annotations:2.12.0:jar +com.fasterxml.jackson.core:jackson-core:2.12.0:jar +com.fasterxml.jackson.core:jackson-databind:2.12.0:jar +com.github.javaparser:javaparser-core:3.13.10:jar +com.thoughtworks.xstream:xstream:1.4.14:jar +commons-codec:commons-codec:1.14:jar +commons-io:commons-io:2.8.0:jar +io.trygvis.rules-sandbox.module:ri-base:1.0-SNAPSHOT:jar +io.trygvis.rules-sandbox.module:ri-module-api:1.0-SNAPSHOT:jar +org.antlr:antlr-runtime:3.5.2:jar +org.drools:drools-canonical-model:7.48.0.Final:jar +org.drools:drools-compiler:7.48.0.Final:jar +org.drools:drools-core:7.48.0.Final:jar +org.drools:drools-core-dynamic:7.48.0.Final:jar +org.drools:drools-core-reflective:7.48.0.Final:jar +org.drools:drools-ecj:7.48.0.Final:jar +org.drools:drools-model-compiler:7.48.0.Final:jar +org.drools:drools-mvel-compiler:7.48.0.Final:jar +org.drools:drools-mvel-parser:7.48.0.Final:jar +org.kie:kie-api:7.48.0.Final:jar +org.kie:kie-internal:7.48.0.Final:jar +org.kie:kie-memory-compiler:7.48.0.Final:jar +org.kie.soup:kie-soup-maven-support:7.48.0.Final:jar +org.kie.soup:kie-soup-xstream:7.48.0.Final:jar +org.slf4j:slf4j-api:1.7.30:jar +xmlpull:xmlpull:1.1.3.1:jar +xpp3:xpp3_min:1.1.4c:jar diff --git a/modules/ri-wireguard/pom.xml b/modules/ri-wireguard/pom.xml new file mode 100644 index 0000000..cc569d7 --- /dev/null +++ b/modules/ri-wireguard/pom.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>io.trygvis.rules-sandbox.module</groupId> + <artifactId>ri-module-parent</artifactId> + <version>1.0-SNAPSHOT</version> + <relativePath>../ri-module-parent/pom.xml</relativePath> + </parent> + + <artifactId>ri-wireguard</artifactId> + <packaging>kjar</packaging> + + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>ri-base</artifactId> + <version>1.0-SNAPSHOT</version> + </dependency> + </dependencies> + +</project> diff --git a/modules/ri-wireguard/src/main/resources/META-INF/kmodule.xml b/modules/ri-wireguard/src/main/resources/META-INF/kmodule.xml new file mode 100644 index 0000000..de617f7 --- /dev/null +++ b/modules/ri-wireguard/src/main/resources/META-INF/kmodule.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://www.drools.org/xsd/kmodule" + xsi:schemaLocation="http://www.drools.org/xsd/kmodule https://www.drools.org/xsd/kmodule_7_1.xsd"> + + <kbase name="wireguard" packages="io.trygvis.rules.wireguard"> +<!-- <ksession name="wireguard"/>--> + </kbase> +</kmodule> diff --git a/modules/ri-wireguard/src/main/resources/io/trygvis/rules/wireguard/wireguard.drl b/modules/ri-wireguard/src/main/resources/io/trygvis/rules/wireguard/wireguard.drl new file mode 100644 index 0000000..5630ab6 --- /dev/null +++ b/modules/ri-wireguard/src/main/resources/io/trygvis/rules/wireguard/wireguard.drl @@ -0,0 +1,185 @@ +package io.trygvis.rules.wireguard; + +import io.trygvis.rules.dns.DnsEntry; +import io.trygvis.rules.machine.Machine; +import io.trygvis.rules.network.IpCalc +import io.trygvis.rules.network.Ipv4Address +import io.trygvis.rules.network.Ipv4Cidr +import java.util.ArrayList +import java.util.List +import java.util.Map + +global io.trygvis.rules.engine.TemplateEngine te; + +dialect "mvel" + +declare WgNet + name : String + domain : String + port : int + linkCidr : String + networkCidr : String + networkBits : int +end + +declare WgIpPool + net : WgNet + role : String + cidr : Ipv4Cidr +end + +declare WgHost + machine : Machine + net : WgNet + publicName : String + publicPort : int + ip : String // This host's IP + networkCidr : String +end + +declare WgConnection + name : String + host : WgHost + to : WgHost +end + +declare WgIpAllocation + host : WgHost + role : String + ip : Ipv4Address +end + +rule "Create IP pools" when + $net : WgNet() +// not(Ipv4Cidr(network == Ipv4Cidr.parseCidr($net.linkCidr).network)) +then + System.out.println("Creating main IP pools"); + insert(new WgIpPool($net, "link", Ipv4Cidr.parseCidr($net.getLinkCidr()))) + insert(new WgIpPool($net, "networks", Ipv4Cidr.parseCidr($net.getNetworkCidr()))) +end + +rule "WgHost VPN machines" +when + $machine : Machine() + $wgNet : WgNet(name == "vpn0") + not(WgHost(machine == $machine)) +then + WgHost wgHost = new WgHost(); + wgHost.machine = $machine; + wgHost.net = $wgNet; + wgHost.publicName = $machine.fqdn; + wgHost.publicPort = $wgNet.port; + insert(wgHost) +end + +rule "Set public name of WgHost" +when + $host : WgHost(publicName == null) + $m : Machine(this == $host.machine, fqdn != null) +then + modify($host) { + publicName = $m.fqdn + } +end + +rule "Make DNS entries for all VPN hosts" +when + $h : WgHost() + not(DnsEntry(fqdn == "%s.%s".formatted($h.machine.name, $h.net.domain), type == "A")) +then + String fqdn = "%s.%s".formatted($h.machine.name, $h.net.domain); + insert(DnsEntry.a(fqdn)) +end + +rule "Connect VPN nodes" + salience -1 +when + $h : WgHost() + $other : WgHost(publicName != null, this != $h) +then + System.out.printf("VPN connection from %s to %s%n", $h.machine.name, $other.machine.name); + WgConnection c = new WgConnection(); + c.host = $h; + c.to = $other; + insert(c) +end + +rule "Name connections" +when + $c : WgConnection(name == null, host != null, to != null) +then + String n = $c.host.machine.name + "_x_" + $c.to.machine.name; + modify($c) { + name = n + } +end + +// This and the next rule needs to use .toString(), the specific objects might be generated multiple times, +// but Drools use identityHashCode() to find equal objects, not equals(). +rule "Assign IP" +when + $pool : WgIpPool(role == "link") + $ip : Ipv4Address() from $pool.cidr.addresses() + not(WgHost(net == $pool.net, ip == $ip.toString())) + $host : WgHost(net == $pool.net, ip == null) +then + System.out.printf("IP: net=%s, pool.role=%s, host=%s, ip=%s%n", $pool.net.name, $pool.role, $host.machine.name, $ip); + modify($host) { + ip = $ip.toString() + } +end + +rule "Assign network CIDR" +when + $net : WgNet() + $network : Ipv4Cidr() from Ipv4Cidr.parseCidr($net.networkCidr).partition($net.networkBits) + $host : WgHost(net == $net, networkCidr == null) + not(WgHost(net == $net, networkCidr == $network.toString())) +then + System.out.printf("Network CIDR: net=%s, host=%s, network=%s%n", $net.name, $host.machine.name, $network); + modify($host) { + networkCidr = $network.toString() + } +end + +rule "Generate per-net files" + agenda-group "generate" + salience 10 +when + $net : WgNet() + $hosts : ArrayList() from collect(WgHost(net == $net)) +then + te.template("wireguard/ansible", "wireguard-" + $net.name + ".yml", Map.of( + "net", $net + )); + + List machines = new ArrayList(); + for (Object o : $hosts) { + WgHost m = (WgHost) o; + machines.add(m.getMachine()); + } + + te.template("wireguard/inventory", "inventory.yml", Map.of( + "net", $net, + "hosts", machines + )); +end + +rule "Generate per-net, per-host files" + agenda-group "generate" + salience 10 +when + $net : WgNet() + $host : WgHost(net == $net) + $peers : ArrayList() from accumulate(WgConnection(host == $host, $to: to), collectList($to)) +then + System.out.printf("Generating per-host files: net=%s, host=%s%n", $net.name, $host.machine.name); + + String output = "host_vars/%s/wireguard.yml".formatted($host.machine.name); + + te.template("wireguard/ansible-host", output, Map.of( + "net", $net, + "host", $host, + "peers", $peers + )); +end diff --git a/modules/ri-wireguard/src/main/resources/logback.xml b/modules/ri-wireguard/src/main/resources/logback.xml new file mode 100644 index 0000000..66ae905 --- /dev/null +++ b/modules/ri-wireguard/src/main/resources/logback.xml @@ -0,0 +1,13 @@ +<configuration> + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> + <encoder> + <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> + </encoder> + </appender> + + <logger name="org.apache.http" level="WARN"/> + <root level="DEBUG"> + <appender-ref ref="STDOUT" /> + </root> + +</configuration> diff --git a/modules/ri-wireguard/src/main/resources/templates/wireguard/ansible-host.j2 b/modules/ri-wireguard/src/main/resources/templates/wireguard/ansible-host.j2 new file mode 100644 index 0000000..6cb3a05 --- /dev/null +++ b/modules/ri-wireguard/src/main/resources/templates/wireguard/ansible-host.j2 @@ -0,0 +1,12 @@ +# Generated +wireguard_port: {{ host.publicPort }} +link_address: {{ host.ip }} +network_cidr: {{ host.networkCidr }} +wireguard_peers: +{%- for peer in peers %} + {{ peer.machine.name }}: + public_address: {{ peer.publicName }} + public_port: {{ peer.publicPort }} + gateway: {{ peer.ip }} + network: {{ peer.networkCidr }} +{%- endfor %} diff --git a/modules/ri-wireguard/src/main/resources/templates/wireguard/ansible.j2 b/modules/ri-wireguard/src/main/resources/templates/wireguard/ansible.j2 new file mode 100644 index 0000000..ad4d034 --- /dev/null +++ b/modules/ri-wireguard/src/main/resources/templates/wireguard/ansible.j2 @@ -0,0 +1,7 @@ +- hosts: wireguard_{{ net.name }} + vars: + wireguard_if: {{ net.name }} + tasks: + - name: wireguard + import_role: + name: wireguard diff --git a/modules/ri-wireguard/src/main/resources/templates/wireguard/inventory.j2 b/modules/ri-wireguard/src/main/resources/templates/wireguard/inventory.j2 new file mode 100644 index 0000000..64f3b5b --- /dev/null +++ b/modules/ri-wireguard/src/main/resources/templates/wireguard/inventory.j2 @@ -0,0 +1,13 @@ +# Generated +all: + hosts: + {%- for host in hosts %} + {{ host.getName() }}: + ansible_host: {{ host.getFqdn() }} + {%- endfor %} + children: + wireguard_{{ net.name }}: + hosts: +{%- for host in hosts %} + {{ host.getName() }}: +{%- endfor %} |