diff --git a/.github/workflows/cloud-upgrade-test.yml b/.github/workflows/cloud-upgrade-test.yml new file mode 100644 index 00000000..7b572b59 --- /dev/null +++ b/.github/workflows/cloud-upgrade-test.yml @@ -0,0 +1,191 @@ +name: Corfu Cluster Test + +on: push + +jobs: + upgrade-test: + runs-on: ubuntu-latest + timeout-minutes: 120 + + env: + PKG_USERNAME: ${{ secrets.pkg_username }} + PUBLISH_TOKEN: ${{ secrets.publish_token }} + + steps: + - uses: actions/checkout@v2 + with: + repository: "CorfuDB/CorfuDB" + + - name: Setup BuildX + uses: docker/setup-buildx-action@v2 + + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@0.6.0 + with: + access_token: ${{ github.token }} + + - name: Cache local Maven repository + uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Checkout Corfu Master + uses: actions/checkout@v2 + with: + repository: "CorfuDB/CorfuDB" + path: enable-lr + + - name: Build V2 Image + run: | + .ci/infrastructure-docker-build.sh docker openjdk:8-jdk-bullseye + + - name: Checkout Corfu 3.2.3 + uses: actions/checkout@v2 + with: + repository: "CorfuDB/CorfuDB" + ref: corfu-cloud-0.3.2.3 + + - name: Build V1 Image + run: | + .ci/infrastructure-docker-build.sh docker openjdk:8-jdk-bullseye + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Build test client docker image + working-directory: cloud/corfu/corfu-cloud-test + run: | + ./docker-build.sh + + - name: Set up cluster + uses: AbsaOSS/k3d-action@v2 + with: + cluster-name: "corfu" + args: >- + --volume /tmp/k3dvol:/tmp/k3dvol + -p "8082:30080@agent:0" + --agents 3 + + - name: Import images + run: | + k3d image import corfudb/corfu-server:0.3.2-SNAPSHOT \ + corfudb/corfu-server:0.4.0-SNAPSHOT \ + corfudb/corfu-cloud-test:latest \ + -c corfu + + - name: Set up Helm + working-directory: ./cloud/corfu + run: | + curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" + curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 + chmod 700 get_helm.sh + ./get_helm.sh + helm repo add jetstack https://charts.jetstack.io + helm repo update + + - name: Initialize v1 cluster + working-directory: ./cloud/corfu + run: | + helm install corfu corfu --set tls.enabled=false --set tls.certificate.enabled=false --set global.replicas=3 --set image.repository=corfudb/corfu-server --set image.tag=0.3.2-SNAPSHOT + helm install corfu2 corfu --set tls.enabled=false --set tls.certificate.enabled=false --set global.replicas=3 --set image.repository=corfudb/corfu-server --set image.tag=0.3.2-SNAPSHOT --set lr.name="log-replication2" --set nameOverride="corfu2" --set serviceAccount.name="corfu2" --set nameOverride="corfu2" --set fullnameOverride="corfu2" --set cluster.type="sink" + sleep 30 + + - name: Cluster Verify V1 + working-directory: ./cloud.corfu + run: | + local lr_version=V1 + + # Wait for Corfu to be ready + while ! kubectl logs corfu-0 -c corfu | grep -q "DATA"; do + echo "Corfu is not ready yet..." + sleep 15 + done + echo "Corfu is Ready!!!!" + + # Get the leader of the log replication + lr_leader="" + while true; do + if kubectl logs log-replication-0 | grep -q "acquired"; then + lr_leader="log-replication-0" + break + fi + if kubectl logs log-replication-1 | grep -q "acquired"; then + lr_leader="log-replication-1" + break + fi + if kubectl logs log-replication-2 | grep -q "acquired"; then + lr_leader="log-replication-2" + break + fi + done + + echo "LR Leader is: $lr_leader" + + lr_ready_str="" + if [ $lr_version = "V2" ]; then + lr_ready_str="Received leadership response from node" + else + lr_ready_str="Negotiation complete" + fi + + # Wait for the log replication leader to be ready + while ! kubectl logs $lr_leader | grep -q $lr_ready_str; do + echo "LR is not ready yet..." + sleep 10 + done + + echo "Ready to Replicate!!!!" + + - name: Upgrade cluster + working-directory: ./cloud/corfu + run: | + helm upgrade corfu corfu --set tls.enabled=false --set tls.certificate.enabled=false --set global.replicas=3 --set image.repository=corfudb/corfu-server --set image.tag=0.4.0-SNAPSHOT --set version.new=true + helm upgrade corfu2 corfu --set tls.enabled=false --set tls.certificate.enabled=false --set global.replicas=3 --set image.repository=corfudb/corfu-server --set image.tag=0.4.0-SNAPSHOT --set lr.name="log-replication2" --set nameOverride="corfu2" --set serviceAccount.name="corfu2" --set nameOverride="corfu2" --set fullnameOverride="corfu2" --set cluster.type="sink" --set version.new=true + + while kubectl describe pods --all-namespaces | grep -q "0.3.2-SNAPSHOT"; do + echo "Waiting for pods to be re-imaged..." + sleep 10 + done + + echo "Cluster upgrade complete!!!" + + - name: Test cluster + working-directory: ./cloud/corfu + run: | + echo "Writing Data To Source..." + helm install corfu-cloud-test corfu-cloud-test-helm --set tls.enabled=false --set jobs.job=1 + + while ! kubectl get pods -o wide | grep corfu-cloud-test | grep -q Completed; do + echo "Waiting for test to finish..." + sleep 5 + done + + helm uninstall corfu-cloud-test + while kubectl get pods -o wide | grep -q corfu-cloud-test; do + echo "Removing test agent..." + sleep 5 + done + + echo "Test Complete!!!" + + - name: Validate test + working-directory: ./cloud/corfu + run: | + echo "Starting test validation!!!" + helm install corfu-cloud-test corfu-cloud-test-helm --set tls.enabled=false --set jobs.job=2 + + while ! kubectl get pods -o wide | grep corfu-cloud-test | grep -q Completed; do + echo "Waiting for validation to complete..." + sleep 5 + done + + helm uninstall corfu-cloud-test + while kubectl get pods -o wide | grep -q corfu-cloud-test; do + echo "Removing test agent..." + sleep 5 + done + + echo "Validation Complete!!!" diff --git a/cloud/corfu/cluster_deploy.sh b/cloud/corfu/cluster_deploy.sh index 13286e63..a405e2b0 100755 --- a/cloud/corfu/cluster_deploy.sh +++ b/cloud/corfu/cluster_deploy.sh @@ -13,13 +13,12 @@ cluster_setup() { image_imports() { k3d image import corfudb/corfu-server:0.3.2-SNAPSHOT -c corfu k3d image import corfudb/corfu-server:0.4.0-SNAPSHOT -c corfu - k3d image import corfudb/corfu-client-example:latest -c corfu + k3d image import corfudb/corfu-cloud-test:latest -c corfu } helm_setup() { helm repo add jetstack https://charts.jetstack.io helm repo update - helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --version v1.8.0 --set installCRDs=true } init_v1_cluster() { @@ -94,16 +93,16 @@ cluster_upgrade() { cluster_test() { echo "Writing Data To Source..." - helm install corfu-client corfu-client-example-helm --set tls.enabled=false --set jobs.job=1 + helm install corfu-cloud-test corfu-cloud-test-helm --set tls.enabled=false --set jobs.job=1 - while ! kubectl get pods -o wide | grep corfu-client | grep -q Completed; do + while ! kubectl get pods -o wide | grep corfu-cloud-test | grep -q Completed; do echo "Waiting for test to finish..." sleep 5 done - helm uninstall corfu-client - while kubectl get pods -o wide | grep -q corfu-client; do + helm uninstall corfu-cloud-test + while kubectl get pods -o wide | grep -q corfu-cloud-test; do echo "Removing test agent..." sleep 5 done @@ -113,15 +112,15 @@ cluster_test() { cluster_test_validate() { echo "Starting test validation!!!" - helm install corfu-client corfu-client-example-helm --set tls.enabled=false --set jobs.job=2 + helm install corfu-cloud-test corfu-cloud-test-helm --set tls.enabled=false --set jobs.job=2 - while ! kubectl get pods -o wide | grep corfu-client | grep -q Completed; do + while ! kubectl get pods -o wide | grep corfu-cloud-test | grep -q Completed; do echo "Waiting for validation to complete..." sleep 5 done - helm uninstall corfu-client - while kubectl get pods -o wide | grep -q corfu-client; do + helm uninstall corfu-cloud-test + while kubectl get pods -o wide | grep -q corfu-cloud-test; do echo "Removing test agent..." sleep 5 done @@ -140,4 +139,4 @@ cluster_test cluster_upgrade cluster_verify V2 -cluster_test_validate \ No newline at end of file +cluster_test_validate diff --git a/cloud/corfu/corfu-client-example-helm/templates/job.yaml b/cloud/corfu/corfu-client-example-helm/templates/job.yaml index 75b1ef03..a5715db7 100644 --- a/cloud/corfu/corfu-client-example-helm/templates/job.yaml +++ b/cloud/corfu/corfu-client-example-helm/templates/job.yaml @@ -9,12 +9,12 @@ spec: containers: - name: corfu-client image: corfudb/corfu-client-example:latest - imagePullPolicy: Never + imagePullPolicy: Always command: - "sh" - "-c" - | - java -cp *.jar org.corfudb.cloud.runtime.example.Main {{ .Values.corfuEndpoint }} {{ .Values.jobs.job }} + java -cp *.jar org.corfudb.cloud.runtime.example.Main {{ .Values.corfuEndpoint }} {{- if .Values.tls.enabled }} \ /certs/keystore.jks /password/password /certs/truststore.jks /password/password {{- end }} diff --git a/cloud/corfu/corfu-client-example-helm/values.yaml b/cloud/corfu/corfu-client-example-helm/values.yaml index b119857c..c08fdd0c 100644 --- a/cloud/corfu/corfu-client-example-helm/values.yaml +++ b/cloud/corfu/corfu-client-example-helm/values.yaml @@ -2,12 +2,9 @@ image: registry: "docker.io" repository: "corfudb/corfu-client-example" tag: "latest" - pullPolicy: Never + pullPolicy: Always corfuEndpoint: "corfu-0.corfu-headless.default.svc.cluster.local" tls: enabled: true certificateName: corfu-certificate-tls passwordName: corfu-password -jobs: - # 1: test, 2: validate, 3: test & validate - job: 3 diff --git a/cloud/corfu/corfu-client-example/build.gradle.kts b/cloud/corfu/corfu-client-example/build.gradle.kts index de65d7db..ab609368 100644 --- a/cloud/corfu/corfu-client-example/build.gradle.kts +++ b/cloud/corfu/corfu-client-example/build.gradle.kts @@ -29,8 +29,6 @@ dependencies { implementation("org.corfudb:runtime:${corfuVersion}") { exclude(group = "io.netty", module = "netty-tcnative") } - implementation("org.corfudb:infrastructure:${corfuVersion}") - implementation("com.github.luben:zstd-jni:1.4.8-1") testImplementation("org.junit.jupiter:junit-jupiter-engine:${junitVersion}") } diff --git a/cloud/corfu/corfu-client-example/src/main/java/org.corfudb.cloud.runtime.example/Main.java b/cloud/corfu/corfu-client-example/src/main/java/org.corfudb.cloud.runtime.example/Main.java index 90a29c5d..da23444f 100644 --- a/cloud/corfu/corfu-client-example/src/main/java/org.corfudb.cloud.runtime.example/Main.java +++ b/cloud/corfu/corfu-client-example/src/main/java/org.corfudb.cloud.runtime.example/Main.java @@ -1,28 +1,14 @@ package org.corfudb.cloud.runtime.example; import com.google.common.reflect.TypeToken; -import java.util.Map; -import org.corfudb.infrastructure.logreplication.proto.Sample; -import org.corfudb.infrastructure.logreplication.proto.LogReplicationMetadata.LogReplicationMetadataKey; -import org.corfudb.infrastructure.logreplication.proto.LogReplicationMetadata.LogReplicationMetadataVal; -import org.corfudb.runtime.CorfuOptions; import org.corfudb.runtime.CorfuRuntime; -import org.corfudb.runtime.collections.CorfuTable; -import org.corfudb.runtime.collections.CorfuStore; -import org.corfudb.runtime.collections.CorfuRecord; -import org.corfudb.runtime.collections.Table; -import org.corfudb.runtime.collections.TableOptions; -import org.corfudb.runtime.collections.TxnContext; -import org.corfudb.runtime.CorfuStoreMetadata.TableName; -import org.corfudb.runtime.CorfuStoreMetadata.TableDescriptors; -import org.corfudb.runtime.CorfuStoreMetadata.TableMetadata; -import org.corfudb.runtime.view.ObjectsView; +import org.corfudb.runtime.collections.PersistentCorfuTable; import org.corfudb.util.NodeLocator; -import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.time.Duration; import java.util.Collections; -import java.util.concurrent.TimeUnit; +import java.util.Map; + /** * This tutorial demonstrates a simple Corfu application. @@ -46,7 +32,7 @@ private static CorfuRuntime getRuntimeAndConnect(String host, boolean tlsEnabled CorfuRuntime.CorfuRuntimeParameters.CorfuRuntimeParametersBuilder builder = CorfuRuntime.CorfuRuntimeParameters .builder() - .connectionTimeout(Duration.ofSeconds(20)) + .connectionTimeout(Duration.ofSeconds(2)) .layoutServers(Collections.singletonList(loc)); if (tlsEnabled) { builder.tlsEnabled(tlsEnabled) @@ -61,7 +47,7 @@ private static CorfuRuntime getRuntimeAndConnect(String host, boolean tlsEnabled } // Sample code - public static void main(String[] args) throws Exception { + public static void main(String[] args) { System.out.println("Start application. Got args: " + Arrays.toString(args)); // Parse the options given, using docopt. /* @@ -76,7 +62,6 @@ public static void main(String[] args) throws Exception { * which is a Java object that contains all of the Corfu utilities exposed to applications. */ String ip = "localhost"; - int job = 3; boolean tlsEnabled = false; String keyStore = ""; String keyStorePassword = ""; @@ -87,144 +72,63 @@ public static void main(String[] args) throws Exception { ip = args[0]; } if (args.length >= 2) { - job = Integer.parseInt(args[1]); + keyStore = args[1]; } if (args.length >= 3) { - keyStore = args[2]; + keyStorePassword = args[2]; } if (args.length >= 4) { - keyStorePassword = args[3]; + trustStore = args[3]; } if (args.length >= 5) { - trustStore = args[4]; - } - if (args.length >= 6) { - trustStorePassword = args[5]; + trustStorePassword = args[4]; } - if (args.length == 6) { + if (args.length == 5) { tlsEnabled = true; } - if (job >= 1) { - if (job != 2) { - test(ip, tlsEnabled, keyStore, keyStorePassword, trustStore, trustStorePassword); - } - validate(ip, tlsEnabled, keyStore, keyStorePassword, trustStore, trustStorePassword); - } - } - - public static void test(String ip, boolean tlsEnabled, String keyStore, String keyStorePassword, String trustStore, String trustStorePassword) throws Exception { - CorfuRuntime runtimeSource = getRuntimeAndConnect(ip, tlsEnabled, keyStore, keyStorePassword, trustStore, trustStorePassword); - CorfuRuntime runtimeSink = getRuntimeAndConnect("corfu2-0.corfu2-headless.default.svc.cluster.local", - tlsEnabled, keyStore, keyStorePassword, trustStore, trustStorePassword); - CorfuStore corfuStoreSource = new CorfuStore(runtimeSource); - CorfuStore corfuStoreSink = new CorfuStore(runtimeSink); - String NAMESPACE = "LR-Test"; - String streamA = "MyTestTable"; + CorfuRuntime runtime = getRuntimeAndConnect(ip, tlsEnabled, keyStore, keyStorePassword, trustStore, trustStorePassword); - Table mapA = corfuStoreSource.openTable( - NAMESPACE, - streamA, - Sample.StringKey.class, - Sample.IntValue.class, - Sample.Metadata.class, - TableOptions.builder().schemaOptions( - CorfuOptions.SchemaOptions.newBuilder() - .setIsFederated(true) - .build()) - .build() - ); - - Table mapASink = corfuStoreSink.openTable( - NAMESPACE, - streamA, - Sample.StringKey.class, - Sample.IntValue.class, - Sample.Metadata.class, - TableOptions.builder().schemaOptions( - CorfuOptions.SchemaOptions.newBuilder() - .setIsFederated(true) - .build()) - .build() - ); - - int totalEntries = 200; - int startIndex = 0; + /** + * Obviously, this application is not doing much yet, + * but you can already invoke getRuntimeAndConnect to test if you can connect to a deployed Corfu service. + * + * Above, you will need to point it to a host and port which is running the service. + * See {@link https://github.com/CorfuDB/CorfuDB} for instructions on how to deploy Corfu. + */ - int maxIndex = totalEntries + startIndex; - for (int i = startIndex; i < maxIndex; i++) { - try (TxnContext txn = corfuStoreSource.txn(NAMESPACE)) { - txn.putRecord(mapA, Sample.StringKey.newBuilder().setKey(String.valueOf(i)).build(), - Sample.IntValue.newBuilder().setValue(i).build(), null); - txn.commit(); - } - } + /** + * Next, we will illustrate how to declare a Java object backed by a Corfu Stream. + * A Corfu Stream is a log dedicated specifically to the history of updates of one object. + * We will instantiate a stream by giving it a name "A", + * and then instantiate an object by specifying its class + */ + Map map = runtime.getObjectsView() + .build() + .setStreamName("A") // stream name + .setTypeToken(new TypeToken>() {}) + .open(); // instantiate the object! - try (TxnContext txn = corfuStoreSource.txn(NAMESPACE)) { - int tableSize = txn.getTable(streamA).count(); - System.out.println("Size of source table after adding entries is: " + tableSize); - txn.commit(); + /** + * The magic has already happened! map is an in-memory view of a shared map, backed by the Corfu log. + * The application can perform put and get on this map from different application instances, + * crash and restart applications, and so on. + * The map will persist and be consistent across all applications. + * + * For example, try the following code repeatedly in a sequence, in between run/exit, + * from multiple instances, and see the different interleaving of values that result. + */ + Integer previous = map.get("a"); + if (previous == null) { + System.out.println("This is the first time we were run!"); + map.put("a", 1); } - } - - public static void validate(String ip, boolean tlsEnabled, String keyStore, String keyStorePassword, String trustStore, String trustStorePassword) throws Exception { - CorfuRuntime runtimeSource = getRuntimeAndConnect(ip, tlsEnabled, keyStore, keyStorePassword, trustStore, trustStorePassword); - CorfuRuntime runtimeSink = getRuntimeAndConnect("corfu2-0.corfu2-headless.default.svc.cluster.local", - tlsEnabled, keyStore, keyStorePassword, trustStore, trustStorePassword); - - CorfuStore corfuStoreSource = new CorfuStore(runtimeSource); - CorfuStore corfuStoreSink = new CorfuStore(runtimeSink); - - String NAMESPACE = "LR-Test"; - String streamA = "MyTestTable"; - - Table mapA = corfuStoreSource.openTable( - NAMESPACE, - streamA, - Sample.StringKey.class, - Sample.IntValue.class, - Sample.Metadata.class, - TableOptions.builder().schemaOptions( - CorfuOptions.SchemaOptions.newBuilder() - .setIsFederated(true) - .build()) - .build() - ); - - Table mapASink = corfuStoreSink.openTable( - NAMESPACE, - streamA, - Sample.StringKey.class, - Sample.IntValue.class, - Sample.Metadata.class, - TableOptions.builder().schemaOptions( - CorfuOptions.SchemaOptions.newBuilder() - .setIsFederated(true) - .build()) - .build() - ); - - while (true) { - try (TxnContext txn = corfuStoreSource.txn(NAMESPACE)) { - int tableSize = txn.getTable(streamA).count(); - System.out.println("Size of source table is: " + tableSize); - txn.commit(); - } - - try (TxnContext txn = corfuStoreSink.txn(NAMESPACE)) { - int tableSize = txn.getTable(streamA).count(); - - System.out.println("Size of sink table is: " + tableSize); - txn.commit(); - - if (tableSize == 200) { - break; - } - } - TimeUnit.SECONDS.sleep(5); + else { + map.put("a", ++previous); + System.out.println("This is the " + previous + " time we were run!"); } } } \ No newline at end of file diff --git a/cloud/corfu/corfu-cloud-test-helm/Chart.yaml b/cloud/corfu/corfu-cloud-test-helm/Chart.yaml new file mode 100644 index 00000000..50fa16e7 --- /dev/null +++ b/cloud/corfu/corfu-cloud-test-helm/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: corfu-cloud-test +version: 0.1.0 diff --git a/cloud/corfu/corfu-cloud-test-helm/templates/job.yaml b/cloud/corfu/corfu-cloud-test-helm/templates/job.yaml new file mode 100644 index 00000000..b24171f0 --- /dev/null +++ b/cloud/corfu/corfu-cloud-test-helm/templates/job.yaml @@ -0,0 +1,36 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: corfu-cloud-test +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: corfu-client + image: corfudb/corfu-cloud-test:latest + imagePullPolicy: Never + command: + - "sh" + - "-c" + - | + java -cp *.jar org.corfudb.cloud.runtime.test.Main {{ .Values.corfuEndpoint }} {{ .Values.jobs.job }} + {{- if .Values.tls.enabled }} \ + /certs/keystore.jks /password/password /certs/truststore.jks /password/password + {{- end }} + volumeMounts: + {{- if .Values.tls.enabled }} + - name: certificate + mountPath: /certs + - name: password + mountPath: /password + {{- end }} + volumes: + {{- if .Values.tls.enabled }} + - name: certificate + secret: + secretName: {{ .Values.tls.certificateName }} + - name: password + secret: + secretName: {{ .Values.tls.passwordName }} + {{- end }} diff --git a/cloud/corfu/corfu-cloud-test-helm/values.yaml b/cloud/corfu/corfu-cloud-test-helm/values.yaml new file mode 100644 index 00000000..4d4750f8 --- /dev/null +++ b/cloud/corfu/corfu-cloud-test-helm/values.yaml @@ -0,0 +1,13 @@ +image: + registry: "docker.io" + repository: "corfudb/corfu-cloud-test" + tag: "latest" + pullPolicy: Never +corfuEndpoint: "corfu-0.corfu-headless.default.svc.cluster.local" +tls: + enabled: true + certificateName: corfu-certificate-tls + passwordName: corfu-password +jobs: + # 1: test, 2: validate, 3: test & validate + job: 3 diff --git a/cloud/corfu/corfu-cloud-test/Dockerfile b/cloud/corfu/corfu-cloud-test/Dockerfile new file mode 100644 index 00000000..b3f0a7e1 --- /dev/null +++ b/cloud/corfu/corfu-cloud-test/Dockerfile @@ -0,0 +1,7 @@ +FROM openjdk:8-jdk-alpine3.8 + +ADD ./build/libs/corfu-cloud-test.jar /app/ + +WORKDIR /app + +CMD java -cp *.jar org.corfudb.cloud.runtime.test.Main "$@" diff --git a/cloud/corfu/corfu-cloud-test/build.gradle.kts b/cloud/corfu/corfu-cloud-test/build.gradle.kts new file mode 100644 index 00000000..de65d7db --- /dev/null +++ b/cloud/corfu/corfu-cloud-test/build.gradle.kts @@ -0,0 +1,60 @@ +import java.nio.charset.StandardCharsets + +plugins { + java +} + +repositories { + mavenLocal() + mavenCentral() + + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/corfudb/corfudb") + // For accessing GitHub Secrets in CorfuDB repo + credentials { + username = System.getenv("PKG_USERNAME") + password = System.getenv("PUBLISH_TOKEN") + } + } +} + +val corfuVersion = "0.3.2-SNAPSHOT" +val logbackVersion = "1.2.11" +val junitVersion = "5.8.2" + +dependencies { + implementation("ch.qos.logback:logback-classic:${logbackVersion}") + implementation("org.latencyutils:LatencyUtils:2.0.3") + implementation("org.corfudb:runtime:${corfuVersion}") { + exclude(group = "io.netty", module = "netty-tcnative") + } + implementation("org.corfudb:infrastructure:${corfuVersion}") + implementation("com.github.luben:zstd-jni:1.4.8-1") + + testImplementation("org.junit.jupiter:junit-jupiter-engine:${junitVersion}") +} + +version = project.file("version") + .readText(StandardCharsets.UTF_8) + .trim() + .substring("version=".length) + +//Fat jar +tasks.withType { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + + archiveFileName.set("${project.name}.${archiveExtension.get()}") + + manifest { + attributes["Main-Class"] = "org.corfudb.cloud.runtime.example.Main" + } + + from(configurations.compileClasspath.get().map { + if (it.isDirectory) it else zipTree(it) + }) +} + +tasks.create("version").doLast { + println(version) +} diff --git a/cloud/corfu/corfu-cloud-test/docker-build.sh b/cloud/corfu/corfu-cloud-test/docker-build.sh new file mode 100755 index 00000000..49d4846e --- /dev/null +++ b/cloud/corfu/corfu-cloud-test/docker-build.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +./gradlew clean jar --stacktrace + +docker build -t corfudb/corfu-cloud-test:latest . \ No newline at end of file diff --git a/cloud/corfu/corfu-cloud-test/gradle/wrapper/gradle-wrapper.jar b/cloud/corfu/corfu-cloud-test/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..cc4fdc29 Binary files /dev/null and b/cloud/corfu/corfu-cloud-test/gradle/wrapper/gradle-wrapper.jar differ diff --git a/cloud/corfu/corfu-cloud-test/gradle/wrapper/gradle-wrapper.properties b/cloud/corfu/corfu-cloud-test/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..36d6137a --- /dev/null +++ b/cloud/corfu/corfu-cloud-test/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Jan 23 21:33:25 PST 2020 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/cloud/corfu/corfu-cloud-test/gradlew b/cloud/corfu/corfu-cloud-test/gradlew new file mode 100755 index 00000000..2fe81a7d --- /dev/null +++ b/cloud/corfu/corfu-cloud-test/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# 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 +# +# https://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. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/cloud/corfu/corfu-cloud-test/gradlew.bat b/cloud/corfu/corfu-cloud-test/gradlew.bat new file mode 100644 index 00000000..9618d8d9 --- /dev/null +++ b/cloud/corfu/corfu-cloud-test/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/cloud/corfu/corfu-cloud-test/settings.gradle.kts b/cloud/corfu/corfu-cloud-test/settings.gradle.kts new file mode 100644 index 00000000..39e1730d --- /dev/null +++ b/cloud/corfu/corfu-cloud-test/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "corfu-cloud-test" diff --git a/cloud/corfu/corfu-cloud-test/src/main/java/org.corfudb.cloud.runtime.test/Main.java b/cloud/corfu/corfu-cloud-test/src/main/java/org.corfudb.cloud.runtime.test/Main.java new file mode 100644 index 00000000..97eb0e16 --- /dev/null +++ b/cloud/corfu/corfu-cloud-test/src/main/java/org.corfudb.cloud.runtime.test/Main.java @@ -0,0 +1,230 @@ +package org.corfudb.cloud.runtime.test; + +import com.google.common.reflect.TypeToken; +import java.util.Map; +import org.corfudb.infrastructure.logreplication.proto.Sample; +import org.corfudb.infrastructure.logreplication.proto.LogReplicationMetadata.LogReplicationMetadataKey; +import org.corfudb.infrastructure.logreplication.proto.LogReplicationMetadata.LogReplicationMetadataVal; +import org.corfudb.runtime.CorfuOptions; +import org.corfudb.runtime.CorfuRuntime; +import org.corfudb.runtime.collections.CorfuTable; +import org.corfudb.runtime.collections.CorfuStore; +import org.corfudb.runtime.collections.CorfuRecord; +import org.corfudb.runtime.collections.Table; +import org.corfudb.runtime.collections.TableOptions; +import org.corfudb.runtime.collections.TxnContext; +import org.corfudb.runtime.CorfuStoreMetadata.TableName; +import org.corfudb.runtime.CorfuStoreMetadata.TableDescriptors; +import org.corfudb.runtime.CorfuStoreMetadata.TableMetadata; +import org.corfudb.runtime.view.ObjectsView; +import org.corfudb.util.NodeLocator; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.time.Duration; +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +/** + * This tutorial demonstrates a simple Corfu application. + * + */ +public class Main { + private static final String USAGE = "Usage: HelloCorfu [-c ]\n" + + "Options:\n" + + " -c Set the configuration host and port [default: localhost:9000]\n"; + + /** + * Internally, the corfuRuntime interacts with the CorfuDB service over TCP/IP sockets. + * + * @param host specifies the IP:port of the CorfuService + * The configuration string has format "hostname:port", for example, "localhost:9090". + * @return a CorfuRuntime object, with which Corfu applications perform all Corfu operations + */ + private static CorfuRuntime getRuntimeAndConnect(String host, boolean tlsEnabled, String keyStore, String keyStorePassword, String trustStore, String trustStorePassword) { + + NodeLocator loc = NodeLocator.builder().host(host).port(9000).build(); + + CorfuRuntime.CorfuRuntimeParameters.CorfuRuntimeParametersBuilder builder = CorfuRuntime.CorfuRuntimeParameters + .builder() + .connectionTimeout(Duration.ofSeconds(20)) + .layoutServers(Collections.singletonList(loc)); + if (tlsEnabled) { + builder.tlsEnabled(tlsEnabled) + .keyStore(keyStore) + .ksPasswordFile(keyStorePassword) + .trustStore(trustStore) + .tsPasswordFile(trustStorePassword); + } + CorfuRuntime runtime = CorfuRuntime.fromParameters(builder.build()); + runtime.connect(); + return runtime; + } + + // Sample code + public static void main(String[] args) throws Exception { + System.out.println("Start application. Got args: " + Arrays.toString(args)); + // Parse the options given, using docopt. + /* + Map opts = + new Docopt(USAGE) + .withVersion(GitRepositoryState.getRepositoryState().describe) + .parse(args); + String corfuConfigurationString = (String) opts.get("-c"); + */ + /** + * First, the application needs to instantiate a CorfuRuntime, + * which is a Java object that contains all of the Corfu utilities exposed to applications. + */ + String ip = "localhost"; + int job = 3; + boolean tlsEnabled = false; + String keyStore = ""; + String keyStorePassword = ""; + String trustStore = ""; + String trustStorePassword = ""; + + if(args.length>=1) { + ip = args[0]; + } + if (args.length >= 2) { + job = Integer.parseInt(args[1]); + } + if (args.length >= 3) { + keyStore = args[2]; + } + if (args.length >= 4) { + keyStorePassword = args[3]; + } + if (args.length >= 5) { + trustStore = args[4]; + } + if (args.length >= 6) { + trustStorePassword = args[5]; + } + + if (args.length == 6) { + tlsEnabled = true; + } + + if (job >= 1) { + if (job != 2) { + test(ip, tlsEnabled, keyStore, keyStorePassword, trustStore, trustStorePassword); + } + validate(ip, tlsEnabled, keyStore, keyStorePassword, trustStore, trustStorePassword); + } + } + + public static void test(String ip, boolean tlsEnabled, String keyStore, String keyStorePassword, String trustStore, String trustStorePassword) throws Exception { + CorfuRuntime runtimeSource = getRuntimeAndConnect(ip, tlsEnabled, keyStore, keyStorePassword, trustStore, trustStorePassword); + CorfuRuntime runtimeSink = getRuntimeAndConnect("corfu2-0.corfu2-headless.default.svc.cluster.local", + tlsEnabled, keyStore, keyStorePassword, trustStore, trustStorePassword); + + CorfuStore corfuStoreSource = new CorfuStore(runtimeSource); + CorfuStore corfuStoreSink = new CorfuStore(runtimeSink); + + String NAMESPACE = "LR-Test"; + String streamA = "MyTestTable"; + + Table mapA = corfuStoreSource.openTable( + NAMESPACE, + streamA, + Sample.StringKey.class, + Sample.IntValue.class, + Sample.Metadata.class, + TableOptions.builder().schemaOptions( + CorfuOptions.SchemaOptions.newBuilder() + .setIsFederated(true) + .build()) + .build() + ); + + Table mapASink = corfuStoreSink.openTable( + NAMESPACE, + streamA, + Sample.StringKey.class, + Sample.IntValue.class, + Sample.Metadata.class, + TableOptions.builder().schemaOptions( + CorfuOptions.SchemaOptions.newBuilder() + .setIsFederated(true) + .build()) + .build() + ); + + int totalEntries = 200; + int startIndex = 0; + + int maxIndex = totalEntries + startIndex; + for (int i = startIndex; i < maxIndex; i++) { + try (TxnContext txn = corfuStoreSource.txn(NAMESPACE)) { + txn.putRecord(mapA, Sample.StringKey.newBuilder().setKey(String.valueOf(i)).build(), + Sample.IntValue.newBuilder().setValue(i).build(), null); + txn.commit(); + } + } + + try (TxnContext txn = corfuStoreSource.txn(NAMESPACE)) { + int tableSize = txn.getTable(streamA).count(); + System.out.println("Size of source table after adding entries is: " + tableSize); + txn.commit(); + } + } + + public static void validate(String ip, boolean tlsEnabled, String keyStore, String keyStorePassword, String trustStore, String trustStorePassword) throws Exception { + CorfuRuntime runtimeSource = getRuntimeAndConnect(ip, tlsEnabled, keyStore, keyStorePassword, trustStore, trustStorePassword); + CorfuRuntime runtimeSink = getRuntimeAndConnect("corfu2-0.corfu2-headless.default.svc.cluster.local", + tlsEnabled, keyStore, keyStorePassword, trustStore, trustStorePassword); + + CorfuStore corfuStoreSource = new CorfuStore(runtimeSource); + CorfuStore corfuStoreSink = new CorfuStore(runtimeSink); + + String NAMESPACE = "LR-Test"; + String streamA = "MyTestTable"; + + Table mapA = corfuStoreSource.openTable( + NAMESPACE, + streamA, + Sample.StringKey.class, + Sample.IntValue.class, + Sample.Metadata.class, + TableOptions.builder().schemaOptions( + CorfuOptions.SchemaOptions.newBuilder() + .setIsFederated(true) + .build()) + .build() + ); + + Table mapASink = corfuStoreSink.openTable( + NAMESPACE, + streamA, + Sample.StringKey.class, + Sample.IntValue.class, + Sample.Metadata.class, + TableOptions.builder().schemaOptions( + CorfuOptions.SchemaOptions.newBuilder() + .setIsFederated(true) + .build()) + .build() + ); + + while (true) { + try (TxnContext txn = corfuStoreSource.txn(NAMESPACE)) { + int tableSize = txn.getTable(streamA).count(); + System.out.println("Size of source table is: " + tableSize); + txn.commit(); + } + + try (TxnContext txn = corfuStoreSink.txn(NAMESPACE)) { + int tableSize = txn.getTable(streamA).count(); + + System.out.println("Size of sink table is: " + tableSize); + txn.commit(); + + if (tableSize == 200) { + break; + } + } + TimeUnit.SECONDS.sleep(5); + } + } +} \ No newline at end of file diff --git a/cloud/corfu/corfu-cloud-test/src/main/resources/logback.xml b/cloud/corfu/corfu-cloud-test/src/main/resources/logback.xml new file mode 100644 index 00000000..6620026a --- /dev/null +++ b/cloud/corfu/corfu-cloud-test/src/main/resources/logback.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + %date{yyyy-MM-dd'T'HH:mm:ss.SSSXXX, UTC} | %-5level | %15.15thread{15} | %50.50(%logger.%M:%L) | %msg%n%xException + + + + + + ${LOG_DIRECTORY}/integration-tools.log + + ${LOG_DIRECTORY}/integration-tools.%i.log.gz + 1 + 10 + + + 100MB + + + + %date{yyyy-MM-dd'T'HH:mm:ss.SSSXXX, UTC} | %-5level | %30.30thread | %30.30logger{30} | %msg%n%xException + + + + + + + + + + + + \ No newline at end of file diff --git a/cloud/corfu/corfu-cloud-test/version b/cloud/corfu/corfu-cloud-test/version new file mode 100644 index 00000000..aef125e0 --- /dev/null +++ b/cloud/corfu/corfu-cloud-test/version @@ -0,0 +1 @@ +version=1.0.0 diff --git a/cloud/corfu/corfu/templates/Deployment.yaml b/cloud/corfu/corfu/templates/Deployment.yaml index 650f0702..e8e2a503 100644 --- a/cloud/corfu/corfu/templates/Deployment.yaml +++ b/cloud/corfu/corfu/templates/Deployment.yaml @@ -40,13 +40,6 @@ spec: valueFrom: fieldRef: fieldPath: metadata.uid - VolumeMounts: - - name: log-dir - mountPath: /var/log/corfu-log-replication - - name: config-dir - mountPath: /config/corfu-log-replication - - name: lr - mountPath: /common/configs/ command: - "sh" - "-c"