diff --git a/LICENSE-BINARY-DIST b/LICENSE-BINARY-DIST index 9fb503538..a50dabe28 100644 --- a/LICENSE-BINARY-DIST +++ b/LICENSE-BINARY-DIST @@ -280,6 +280,7 @@ commons-io:commons-io commons-logging:commons-logging commons-net:commons-net dev.failsafe:failsafe +info.picocli:picocli io.airlift:aircompressor io.grpc:grpc-alts io.grpc:grpc-api @@ -399,6 +400,7 @@ io.quarkus:quarkus-micrometer-registry-prometheus io.quarkus:quarkus-mutiny io.quarkus:quarkus-netty io.quarkus:quarkus-opentelemetry +io.quarkus:quarkus-picocli io.quarkus:quarkus-reactive-routes io.quarkus:quarkus-rest io.quarkus:quarkus-rest-common diff --git a/gradle/projects.main.properties b/gradle/projects.main.properties index bee868322..515eaf5cc 100644 --- a/gradle/projects.main.properties +++ b/gradle/projects.main.properties @@ -24,6 +24,7 @@ polaris-api-management-model=api/management-model polaris-api-management-service=api/management-service polaris-service-common=service/common polaris-quarkus-service=quarkus/service +polaris-quarkus-admin=quarkus/admin polaris-eclipselink=extension/persistence/eclipselink polaris-jpa-model=extension/persistence/jpa-model aggregated-license-report=aggregated-license-report diff --git a/quarkus/admin/build.gradle.kts b/quarkus/admin/build.gradle.kts new file mode 100644 index 000000000..4f8131de3 --- /dev/null +++ b/quarkus/admin/build.gradle.kts @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import io.quarkus.gradle.tasks.QuarkusBuild + +plugins { + alias(libs.plugins.quarkus) + alias(libs.plugins.openapi.generator) + id("polaris-server") + id("polaris-license-report") + id("application") +} + +dependencies { + implementation(project(":polaris-core")) + implementation(project(":polaris-api-management-service")) + implementation(project(":polaris-api-iceberg-service")) + implementation(project(":polaris-service-common")) + implementation(project(":polaris-quarkus-service")) + + implementation(platform(libs.quarkus.bom)) + implementation("io.quarkus:quarkus-picocli") + + implementation("org.jboss.slf4j:slf4j-jboss-logmanager") + + // override dnsjava version in dependencies due to https://github.com/dnsjava/dnsjava/issues/329 + implementation(platform(libs.dnsjava)) + + testImplementation(enforcedPlatform(libs.quarkus.bom)) + testImplementation("io.quarkus:quarkus-junit5") + + testImplementation(platform(libs.junit.bom)) + testImplementation(libs.bundles.junit.testing) +} + +tasks.withType().configureEach { + from("src/main/resources") { + expand("polarisVersion" to version) + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } +} + +quarkus { + quarkusBuildProperties.put("quarkus.package.type", "uber-jar") + // Pull manifest attributes from the "main" `jar` task to get the + // release-information into the jars generated by Quarkus. + quarkusBuildProperties.putAll( + provider { + tasks + .named("jar", Jar::class.java) + .get() + .manifest + .attributes + .map { e -> "quarkus.package.jar.manifest.attributes.\"${e.key}\"" to e.value.toString() } + .toMap() + } + ) +} + +publishing { + publications { + named("maven") { + val quarkusBuild = tasks.getByName("quarkusBuild") + artifact(quarkusBuild.runnerJar) { + classifier = "runner" + builtBy(quarkusBuild) + } + } + } +} diff --git a/quarkus/admin/src/main/docker/Dockerfile.jvm b/quarkus/admin/src/main/docker/Dockerfile.jvm new file mode 100644 index 000000000..fbb3bf087 --- /dev/null +++ b/quarkus/admin/src/main/docker/Dockerfile.jvm @@ -0,0 +1,46 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +FROM registry.access.redhat.com/ubi9/openjdk-21:1.20-2.1726695192 + +LABEL org.opencontainers.image.source=https://github.com/apache/polaris +LABEL org.opencontainers.image.description="Apache Polaris (incubating) Admin Tool" +LABEL org.opencontainers.image.licenses=Apache-2.0 + +ENV LANGUAGE='en_US:en' + +USER root +RUN groupadd --gid 10001 polaris \ + && useradd --uid 10000 --gid polaris polaris \ + && chown -R polaris:polaris /opt/jboss/container \ + && chown -R polaris:polaris /deployments + +USER polaris +WORKDIR /home/polaris +ENV USER=polaris +ENV UID=10000 +ENV HOME=/home/polaris + +# We make four distinct layers so if there are application changes the library layers can be re-used +COPY --chown=polaris:polaris build/quarkus-app/lib/ /deployments/lib/ +COPY --chown=polaris:polaris build/quarkus-app/*.jar /deployments/ +COPY --chown=polaris:polaris build/quarkus-app/app/ /deployments/app/ +COPY --chown=polaris:polaris build/quarkus-app/quarkus/ /deployments/quarkus/ + +ENV AB_JOLOKIA_OFF="" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" diff --git a/quarkus/admin/src/main/java/org/apache/polaris/service/quarkus/admin/BaseCommand.java b/quarkus/admin/src/main/java/org/apache/polaris/service/quarkus/admin/BaseCommand.java new file mode 100644 index 000000000..ce9957242 --- /dev/null +++ b/quarkus/admin/src/main/java/org/apache/polaris/service/quarkus/admin/BaseCommand.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.polaris.service.quarkus.admin; + +import jakarta.inject.Inject; +import java.util.concurrent.Callable; +import org.apache.polaris.core.persistence.MetaStoreManagerFactory; +import org.apache.polaris.service.quarkus.persistence.QuarkusPersistenceConfiguration; +import picocli.CommandLine.Model.CommandSpec; +import picocli.CommandLine.Spec; + +public abstract class BaseCommand implements Callable { + + public static final Integer EXIT_CODE_GENERIC_ERROR = 1; + public static final Integer EXIT_CODE_BOOTSTRAP_ERROR = 2; + public static final Integer EXIT_CODE_PURGE_ERROR = 3; + + @Inject QuarkusPersistenceConfiguration persistenceConfiguration; + + @Inject MetaStoreManagerFactory metaStoreManagerFactory; + + @Spec CommandSpec spec; + + protected void warnOnInMemory() { + if (persistenceConfiguration.type().equalsIgnoreCase("in-memory")) { + spec.commandLine() + .getErr() + .println( + spec.commandLine() + .getColorScheme() + .errorText( + """ + ********************************************************************************************* + ** Running the Admin Tool on a Polaris instance with in-memory persistence is meaningless! ** + ********************************************************************************************* + """)); + } + } +} diff --git a/quarkus/admin/src/main/java/org/apache/polaris/service/quarkus/admin/BootstrapCommand.java b/quarkus/admin/src/main/java/org/apache/polaris/service/quarkus/admin/BootstrapCommand.java new file mode 100644 index 000000000..68fcdb31d --- /dev/null +++ b/quarkus/admin/src/main/java/org/apache/polaris/service/quarkus/admin/BootstrapCommand.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.polaris.service.quarkus.admin; + +import java.util.List; +import java.util.Map; +import org.apache.polaris.core.auth.PolarisSecretsManager.PrincipalSecretsResult; +import picocli.CommandLine; + +@CommandLine.Command( + name = "bootstrap", + mixinStandardHelpOptions = true, + description = "Bootstraps principal credentials for all realms and prints them to log.") +public class BootstrapCommand extends BaseCommand { + + @CommandLine.Option( + names = {"-r", "--realm"}, + required = true, + description = "The name of the realm to bootstrap.") + List realms; + + @Override + public Integer call() { + warnOnInMemory(); + + // Execute the bootstrap + Map results = metaStoreManagerFactory.bootstrapRealms(realms); + + // Log any errors: + boolean success = true; + for (Map.Entry result : results.entrySet()) { + if (!result.getValue().isSuccess()) { + spec.commandLine() + .getErr() + .printf( + "Bootstrapping `%s` failed: %s%n", + result.getKey(), result.getValue().getReturnStatus().toString()); + success = false; + } + } + + if (success) { + spec.commandLine().getOut().println("Bootstrap completed successfully."); + return 0; + } else { + spec.commandLine().getErr().println("Bootstrap encountered errors during operation."); + return EXIT_CODE_BOOTSTRAP_ERROR; + } + } +} diff --git a/quarkus/admin/src/main/java/org/apache/polaris/service/quarkus/admin/PolarisAdminTool.java b/quarkus/admin/src/main/java/org/apache/polaris/service/quarkus/admin/PolarisAdminTool.java new file mode 100644 index 000000000..c81d9273b --- /dev/null +++ b/quarkus/admin/src/main/java/org/apache/polaris/service/quarkus/admin/PolarisAdminTool.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.polaris.service.quarkus.admin; + +import io.quarkus.picocli.runtime.annotations.TopCommand; +import java.io.PrintWriter; +import picocli.CommandLine.Command; +import picocli.CommandLine.HelpCommand; + +@TopCommand +@Command( + name = "polaris-admin-tool-runner.jar", + mixinStandardHelpOptions = true, + versionProvider = PolarisVersionProvider.class, + description = "Nessie Server Admin Tool", + subcommands = { + HelpCommand.class, + BootstrapCommand.class, + PurgeCommand.class, + }) +public class PolarisAdminTool extends BaseCommand { + + @Override + public Integer call() { + return info(); + } + + private int info() { + warnOnInMemory(); + + PrintWriter out = spec.commandLine().getOut(); + + out.println("Polaris server information & maintenance tool."); + out.println("Use the 'help' command."); + out.println(); + return 0; + } +} diff --git a/quarkus/admin/src/main/java/org/apache/polaris/service/quarkus/admin/PolarisVersionProvider.java b/quarkus/admin/src/main/java/org/apache/polaris/service/quarkus/admin/PolarisVersionProvider.java new file mode 100644 index 000000000..090948b18 --- /dev/null +++ b/quarkus/admin/src/main/java/org/apache/polaris/service/quarkus/admin/PolarisVersionProvider.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.polaris.service.quarkus.admin; + +import java.io.InputStream; +import java.net.URL; +import java.util.Objects; +import java.util.Properties; +import picocli.CommandLine.IVersionProvider; + +public class PolarisVersionProvider implements IVersionProvider { + + @Override + public String[] getVersion() throws Exception { + URL resource = + Objects.requireNonNull(PolarisVersionProvider.class.getResource("version.properties")); + try (InputStream input = resource.openConnection().getInputStream()) { + Properties props = new Properties(); + props.load(input); + return new String[] {props.getProperty("polaris.version")}; + } + } +} diff --git a/quarkus/admin/src/main/java/org/apache/polaris/service/quarkus/admin/PurgeCommand.java b/quarkus/admin/src/main/java/org/apache/polaris/service/quarkus/admin/PurgeCommand.java new file mode 100644 index 000000000..292ffcab8 --- /dev/null +++ b/quarkus/admin/src/main/java/org/apache/polaris/service/quarkus/admin/PurgeCommand.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.polaris.service.quarkus.admin; + +import java.util.List; +import picocli.CommandLine; + +@CommandLine.Command( + name = "purge", + mixinStandardHelpOptions = true, + description = "Purge principal credentials for all realms and prints them to log.") +public class PurgeCommand extends BaseCommand { + + @CommandLine.Option( + names = {"-r", "--realm"}, + required = true, + description = "The name of the realm to purge.") + List realms; + + @Override + public Integer call() { + warnOnInMemory(); + + try { + metaStoreManagerFactory.purgeRealms(realms); + spec.commandLine().getOut().println("Purge completed successfully."); + return 0; + } catch (Exception e) { + spec.commandLine().getErr().println("Purge encountered errors during operation."); + return EXIT_CODE_PURGE_ERROR; + } + } +} diff --git a/quarkus/admin/src/main/resources/application.properties b/quarkus/admin/src/main/resources/application.properties new file mode 100644 index 000000000..0b1bfc789 --- /dev/null +++ b/quarkus/admin/src/main/resources/application.properties @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +quarkus.application.name=Apache Polaris (incubating) Admin Tool diff --git a/quarkus/admin/src/main/resources/org/apache/polaris/service/quarkus/admin/version.properties b/quarkus/admin/src/main/resources/org/apache/polaris/service/quarkus/admin/version.properties new file mode 100644 index 000000000..b36811eef --- /dev/null +++ b/quarkus/admin/src/main/resources/org/apache/polaris/service/quarkus/admin/version.properties @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +polaris.version=${polarisVersion} diff --git a/quarkus/admin/src/test/java/org/apache/polaris/service/quarkus/admin/BootstrapCommandTest.java b/quarkus/admin/src/test/java/org/apache/polaris/service/quarkus/admin/BootstrapCommandTest.java new file mode 100644 index 000000000..a21fe6dea --- /dev/null +++ b/quarkus/admin/src/test/java/org/apache/polaris/service/quarkus/admin/BootstrapCommandTest.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.polaris.service.quarkus.admin; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.quarkus.test.junit.main.Launch; +import io.quarkus.test.junit.main.LaunchResult; +import io.quarkus.test.junit.main.QuarkusMainTest; +import org.junit.jupiter.api.Test; + +@QuarkusMainTest +class BootstrapCommandTest { + + @Test + @Launch(value = {"bootstrap", "-r", "realm1", "-r", "realm2"}) + public void testBootstrap(LaunchResult result) { + assertThat(result.getOutput()).contains("Bootstrap completed successfully."); + } +} diff --git a/quarkus/admin/src/test/java/org/apache/polaris/service/quarkus/admin/PurgeCommandTest.java b/quarkus/admin/src/test/java/org/apache/polaris/service/quarkus/admin/PurgeCommandTest.java new file mode 100644 index 000000000..902cf8a10 --- /dev/null +++ b/quarkus/admin/src/test/java/org/apache/polaris/service/quarkus/admin/PurgeCommandTest.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.polaris.service.quarkus.admin; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.quarkus.test.junit.main.Launch; +import io.quarkus.test.junit.main.LaunchResult; +import io.quarkus.test.junit.main.QuarkusMainTest; +import org.junit.jupiter.api.Test; + +@QuarkusMainTest +class PurgeCommandTest { + + @Test + @Launch(value = {"purge", "-r", "realm1", "-r", "realm2"}) + public void testPurge(LaunchResult result) { + assertThat(result.getOutput()).contains("Purge completed successfully."); + } +} diff --git a/quarkus/service/build.gradle.kts b/quarkus/service/build.gradle.kts index c4103b120..621f1cb38 100644 --- a/quarkus/service/build.gradle.kts +++ b/quarkus/service/build.gradle.kts @@ -20,6 +20,7 @@ plugins { alias(libs.plugins.quarkus) alias(libs.plugins.openapi.generator) + alias(libs.plugins.jandex) id("polaris-server") id("polaris-license-report") id("application") @@ -177,6 +178,10 @@ tasks.named("compileJava") { dependsOn("compileQuarkusGeneratedSourcesJava") } tasks.named("sourcesJar") { dependsOn("compileQuarkusGeneratedSourcesJava") } +tasks.named("javadoc") { dependsOn("jandex") } + +tasks.named("quarkusDependenciesBuild") { dependsOn("jandex") } + distributions { main { contents {