From 78be6c808f0a77c6dc39bd5b0bc9af8f4042febd Mon Sep 17 00:00:00 2001 From: Jakub Dudycz Date: Wed, 27 Feb 2019 12:23:00 +0100 Subject: Extract module for ArgBasedConfiguration Change-Id: Ib11700650583378eb347451950a24c5f238382e4 Signed-off-by: Jakub Dudycz Issue-ID: DCAEGEN2-1292 --- sources/hv-collector-commandline/pom.xml | 66 ++++++++ .../veshv/commandline/ArgBasedConfiguration.kt | 57 +++++++ .../veshv/commandline/CommandLineOption.kt | 168 +++++++++++++++++++++ .../veshv/commandline/WrongArgumentError.kt | 69 +++++++++ .../collectors/veshv/commandline/extensions.kt | 66 ++++++++ .../veshv/commandline/CommandLineOptionTest.kt | 64 ++++++++ .../veshv/commandline/WrongArgumentErrorTest.kt | 62 ++++++++ 7 files changed, 552 insertions(+) create mode 100644 sources/hv-collector-commandline/pom.xml create mode 100644 sources/hv-collector-commandline/src/main/kotlin/org/onap/dcae/collectors/veshv/commandline/ArgBasedConfiguration.kt create mode 100644 sources/hv-collector-commandline/src/main/kotlin/org/onap/dcae/collectors/veshv/commandline/CommandLineOption.kt create mode 100644 sources/hv-collector-commandline/src/main/kotlin/org/onap/dcae/collectors/veshv/commandline/WrongArgumentError.kt create mode 100644 sources/hv-collector-commandline/src/main/kotlin/org/onap/dcae/collectors/veshv/commandline/extensions.kt create mode 100644 sources/hv-collector-commandline/src/test/kotlin/org/onap/dcae/collectors/veshv/commandline/CommandLineOptionTest.kt create mode 100644 sources/hv-collector-commandline/src/test/kotlin/org/onap/dcae/collectors/veshv/commandline/WrongArgumentErrorTest.kt (limited to 'sources/hv-collector-commandline') diff --git a/sources/hv-collector-commandline/pom.xml b/sources/hv-collector-commandline/pom.xml new file mode 100644 index 00000000..8c3482f1 --- /dev/null +++ b/sources/hv-collector-commandline/pom.xml @@ -0,0 +1,66 @@ + + + + hv-collector-sources + org.onap.dcaegen2.collectors.hv-ves + 1.1.0-SNAPSHOT + + 4.0.0 + + hv-collector-commandline + + + + + kotlin-maven-plugin + org.jetbrains.kotlin + + + maven-surefire-plugin + org.apache.maven.plugins + + + + + + + ${project.parent.groupId} + hv-collector-utils + ${project.parent.version} + + + commons-cli + commons-cli + + + org.jetbrains.kotlin + kotlin-reflect + + + io.arrow-kt + arrow-effects + + + org.assertj + assertj-core + test + + + org.jetbrains.kotlin + kotlin-test + test + + + org.jetbrains.spek + spek-api + test + + + org.jetbrains.spek + spek-junit-platform-engine + test + + + \ No newline at end of file diff --git a/sources/hv-collector-commandline/src/main/kotlin/org/onap/dcae/collectors/veshv/commandline/ArgBasedConfiguration.kt b/sources/hv-collector-commandline/src/main/kotlin/org/onap/dcae/collectors/veshv/commandline/ArgBasedConfiguration.kt new file mode 100644 index 00000000..1e45c923 --- /dev/null +++ b/sources/hv-collector-commandline/src/main/kotlin/org/onap/dcae/collectors/veshv/commandline/ArgBasedConfiguration.kt @@ -0,0 +1,57 @@ +/* + * ============LICENSE_START======================================================= + * dcaegen2-collectors-veshv + * ================================================================================ + * Copyright (C) 2018-2019 NOKIA + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.dcae.collectors.veshv.commandline + +import arrow.core.Either +import arrow.core.Option +import arrow.core.Try +import arrow.core.flatMap +import org.apache.commons.cli.CommandLine +import org.apache.commons.cli.CommandLineParser +import org.apache.commons.cli.Options +import java.io.File +import java.nio.file.Path +import java.nio.file.Paths + +abstract class ArgBasedConfiguration(private val parser: CommandLineParser) { + abstract val cmdLineOptionsList: List + + fun parse(args: Array): Either { + val parseResult = Try { + val commandLineOptions = cmdLineOptionsList.map { it.option }.fold(Options(), Options::addOption) + parser.parse(commandLineOptions, args) + } + return parseResult + .toEither() + .mapLeft { ex -> WrongArgumentError(ex, cmdLineOptionsList) } + .map(this::getConfiguration) + .flatMap { + it.toEither { + WrongArgumentError( + message = "Unexpected error when parsing command line arguments", + cmdLineOptionsList = cmdLineOptionsList) + } + } + } + + protected abstract fun getConfiguration(cmdLine: CommandLine): Option + + protected fun stringPathToPath(path: String): Path = Paths.get(File(path).toURI()) +} diff --git a/sources/hv-collector-commandline/src/main/kotlin/org/onap/dcae/collectors/veshv/commandline/CommandLineOption.kt b/sources/hv-collector-commandline/src/main/kotlin/org/onap/dcae/collectors/veshv/commandline/CommandLineOption.kt new file mode 100644 index 00000000..0c3f60bb --- /dev/null +++ b/sources/hv-collector-commandline/src/main/kotlin/org/onap/dcae/collectors/veshv/commandline/CommandLineOption.kt @@ -0,0 +1,168 @@ +/* + * ============LICENSE_START======================================================= + * dcaegen2-collectors-veshv + * ================================================================================ + * Copyright (C) 2018-2019 NOKIA + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.dcae.collectors.veshv.commandline + +import org.apache.commons.cli.Option + + +enum class CommandLineOption(val option: Option, val required: Boolean = false) { + HEALTH_CHECK_API_PORT( + Option.builder("H") + .longOpt("health-check-api-port") + .hasArg() + .desc("Health check rest api listen port") + .build() + ), + LISTEN_PORT( + Option.builder("p") + .longOpt("listen-port") + .hasArg() + .desc("Listen port") + .build(), + required = true + ), + CONSUL_CONFIG_URL( + Option.builder("c") + .longOpt("config-url") + .hasArg() + .desc("URL of ves configuration on consul") + .build(), + required = true + ), + CONSUL_FIRST_REQUEST_DELAY( + Option.builder("d") + .longOpt("first-request-delay") + .hasArg() + .desc("Delay of first request to consul in seconds") + .build() + ), + CONSUL_REQUEST_INTERVAL( + Option.builder("I") + .longOpt("request-interval") + .hasArg() + .desc("Interval of consul configuration requests in seconds") + .build() + ), + VES_HV_PORT( + Option.builder("v") + .longOpt("ves-port") + .hasArg() + .desc("VesHvCollector port") + .build(), + required = true + ), + VES_HV_HOST( + Option.builder("h") + .longOpt("ves-host") + .hasArg() + .desc("VesHvCollector host") + .build(), + required = true + ), + KAFKA_SERVERS( + Option.builder("s") + .longOpt("kafka-bootstrap-servers") + .hasArg() + .desc("Comma-separated Kafka bootstrap servers in : format") + .build(), + required = true + ), + KAFKA_TOPICS( + Option.builder("f") + .longOpt("kafka-topics") + .hasArg() + .desc("Comma-separated Kafka topics") + .build(), + required = true + ), + SSL_DISABLE( + Option.builder("l") + .longOpt("ssl-disable") + .desc("Disable SSL encryption") + .build() + ), + KEY_STORE_FILE( + Option.builder("k") + .longOpt("key-store") + .hasArg() + .desc("Key store in PKCS12 format") + .build() + ), + KEY_STORE_PASSWORD( + Option.builder("kp") + .longOpt("key-store-password") + .hasArg() + .desc("Key store password") + .build() + ), + TRUST_STORE_FILE( + Option.builder("t") + .longOpt("trust-store") + .hasArg() + .desc("File with trusted certificate bundle in PKCS12 format") + .build() + ), + TRUST_STORE_PASSWORD( + Option.builder("tp") + .longOpt("trust-store-password") + .hasArg() + .desc("Trust store password") + .build() + ), + IDLE_TIMEOUT_SEC( + Option.builder("i") + .longOpt("idle-timeout-sec") + .hasArg() + .desc( + """Idle timeout for remote hosts. After given time without any data exchange the + |connection might be closed.""".trimMargin() + ) + .build() + ), + MAXIMUM_PAYLOAD_SIZE_BYTES( + Option.builder("m") + .longOpt("max-payload-size") + .hasArg() + .desc("Maximum supported payload size in bytes") + .build() + ), + LOG_LEVEL( + Option.builder("ll") + .longOpt("log-level") + .hasArg() + .desc("Log level") + .build() + ), + DUMMY_MODE( + Option.builder("u") + .longOpt("dummy") + .desc("If present will start in dummy mode (dummy external services)") + .build() + ); + + fun environmentVariableName(prefix: String = DEFAULT_ENV_PREFIX): String = + option.longOpt.toUpperCase().replace('-', '_').let { mainPart -> + "${prefix}_${mainPart}" + } + + companion object { + private const val DEFAULT_ENV_PREFIX = "VESHV" + } +} diff --git a/sources/hv-collector-commandline/src/main/kotlin/org/onap/dcae/collectors/veshv/commandline/WrongArgumentError.kt b/sources/hv-collector-commandline/src/main/kotlin/org/onap/dcae/collectors/veshv/commandline/WrongArgumentError.kt new file mode 100644 index 00000000..f3749b35 --- /dev/null +++ b/sources/hv-collector-commandline/src/main/kotlin/org/onap/dcae/collectors/veshv/commandline/WrongArgumentError.kt @@ -0,0 +1,69 @@ +/* + * ============LICENSE_START======================================================= + * dcaegen2-collectors-veshv + * ================================================================================ + * Copyright (C) 2018-2019 NOKIA + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.dcae.collectors.veshv.commandline + +import arrow.core.Option +import org.apache.commons.cli.HelpFormatter +import org.apache.commons.cli.Options + + +data class WrongArgumentError( + val message: String, + val cause: Throwable? = null, + val cmdLineOptionsList: List) { + + constructor(par: Throwable, cmdLineOptionsList: List) : + this(par.message ?: "", + par, + cmdLineOptionsList) + + fun printMessage() { + println(message) + } + + fun printHelp(programName: String) { + val formatter = HelpFormatter() + val footer = "All parameters can be specified as environment variables using upper-snake-case full " + + "name with prefix `VESHV_`." + + formatter.printHelp( + programName, + generateRequiredParametersNote(cmdLineOptionsList), + getOptions(), + footer) + } + + private fun getOptions() = cmdLineOptionsList.map { it.option }.fold(Options(), Options::addOption) + + companion object { + fun generateRequiredParametersNote(cmdLineOptionsList: List): String { + val requiredParams = Option.fromNullable(cmdLineOptionsList.filter { it.required } + .takeUnless { it.isEmpty() }) + return requiredParams.fold( + { "" }, + { + it.map { commandLineOption -> commandLineOption.option.opt } + .joinToString(prefix = "Required parameters: ", separator = ", ") + } + ) + } + } + +} diff --git a/sources/hv-collector-commandline/src/main/kotlin/org/onap/dcae/collectors/veshv/commandline/extensions.kt b/sources/hv-collector-commandline/src/main/kotlin/org/onap/dcae/collectors/veshv/commandline/extensions.kt new file mode 100644 index 00000000..c0fbcde6 --- /dev/null +++ b/sources/hv-collector-commandline/src/main/kotlin/org/onap/dcae/collectors/veshv/commandline/extensions.kt @@ -0,0 +1,66 @@ +/* + * ============LICENSE_START======================================================= + * dcaegen2-collectors-veshv + * ================================================================================ + * Copyright (C) 2018-2019 NOKIA + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.dcae.collectors.veshv.commandline + +import arrow.core.Option +import arrow.core.getOrElse +import arrow.effects.IO +import arrow.syntax.function.curried +import org.apache.commons.cli.CommandLine +import org.onap.dcae.collectors.veshv.utils.arrow.ExitFailure +import org.onap.dcae.collectors.veshv.utils.arrow.fromNullablesChain + +/** + * @author Piotr Jaszczyk + * @since June 2018 + */ + +fun handleWrongArgumentError(programName: String, err: WrongArgumentError): IO = IO { + err.printMessage() + err.printHelp(programName) +}.flatMap { ExitFailure(2).io() } + +val handleWrongArgumentErrorCurried = ::handleWrongArgumentError.curried() + +fun CommandLine.longValue(cmdLineOpt: CommandLineOption, default: Long): Long = + longValue(cmdLineOpt).getOrElse { default } + +fun CommandLine.stringValue(cmdLineOpt: CommandLineOption, default: String): String = + optionValue(cmdLineOpt).getOrElse { default } + +fun CommandLine.intValue(cmdLineOpt: CommandLineOption, default: Int): Int = + intValue(cmdLineOpt).getOrElse { default } + +fun CommandLine.intValue(cmdLineOpt: CommandLineOption): Option = + optionValue(cmdLineOpt).map(String::toInt) + +fun CommandLine.longValue(cmdLineOpt: CommandLineOption): Option = + optionValue(cmdLineOpt).map(String::toLong) + +fun CommandLine.stringValue(cmdLineOpt: CommandLineOption): Option = + optionValue(cmdLineOpt) + +fun CommandLine.hasOption(cmdLineOpt: CommandLineOption): Boolean = + this.hasOption(cmdLineOpt.option.opt) || + System.getenv(cmdLineOpt.environmentVariableName()) != null + +private fun CommandLine.optionValue(cmdLineOpt: CommandLineOption) = Option.fromNullablesChain( + getOptionValue(cmdLineOpt.option.opt), + { System.getenv(cmdLineOpt.environmentVariableName()) }) diff --git a/sources/hv-collector-commandline/src/test/kotlin/org/onap/dcae/collectors/veshv/commandline/CommandLineOptionTest.kt b/sources/hv-collector-commandline/src/test/kotlin/org/onap/dcae/collectors/veshv/commandline/CommandLineOptionTest.kt new file mode 100644 index 00000000..736710ff --- /dev/null +++ b/sources/hv-collector-commandline/src/test/kotlin/org/onap/dcae/collectors/veshv/commandline/CommandLineOptionTest.kt @@ -0,0 +1,64 @@ +/* + * ============LICENSE_START======================================================= + * dcaegen2-collectors-veshv + * ================================================================================ + * Copyright (C) 2018-2019 NOKIA + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.dcae.collectors.veshv.commandline + +import org.assertj.core.api.Assertions.assertThat +import org.jetbrains.spek.api.Spek +import org.jetbrains.spek.api.dsl.describe +import org.jetbrains.spek.api.dsl.given +import org.jetbrains.spek.api.dsl.it +import org.jetbrains.spek.api.dsl.on +import org.onap.dcae.collectors.veshv.commandline.CommandLineOption.DUMMY_MODE +import org.onap.dcae.collectors.veshv.commandline.CommandLineOption.KAFKA_SERVERS + +/** + * @author Piotr Jaszczyk + * @since September 2018 + */ +class CommandLineOptionTest : Spek({ + describe("command line options enum") { + describe("environment variables") { + given("sample option and prefix") { + val opt = KAFKA_SERVERS + val prefix = "CONFIG" + + on("calling environmentVariableName") { + val result = opt.environmentVariableName(prefix) + + it("should return prefixed upper snake cased long option name") { + assertThat(result).isEqualTo("CONFIG_KAFKA_BOOTSTRAP_SERVERS") + } + } + } + + given("sample option without prefix") { + val opt = DUMMY_MODE + + on("calling environmentVariableName") { + val result = opt.environmentVariableName() + + it("should return prefixed upper snake cased long option name") { + assertThat(result).isEqualTo("VESHV_DUMMY") + } + } + } + } + } +}) diff --git a/sources/hv-collector-commandline/src/test/kotlin/org/onap/dcae/collectors/veshv/commandline/WrongArgumentErrorTest.kt b/sources/hv-collector-commandline/src/test/kotlin/org/onap/dcae/collectors/veshv/commandline/WrongArgumentErrorTest.kt new file mode 100644 index 00000000..e177294f --- /dev/null +++ b/sources/hv-collector-commandline/src/test/kotlin/org/onap/dcae/collectors/veshv/commandline/WrongArgumentErrorTest.kt @@ -0,0 +1,62 @@ +/* + * ============LICENSE_START======================================================= + * dcaegen2-collectors-veshv + * ================================================================================ + * Copyright (C) 2018-2019 NOKIA + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.dcae.collectors.veshv.commandline + +import org.assertj.core.api.Assertions.assertThat +import org.jetbrains.spek.api.Spek +import org.jetbrains.spek.api.dsl.describe +import org.jetbrains.spek.api.dsl.given +import org.jetbrains.spek.api.dsl.it +import org.jetbrains.spek.api.dsl.on +import org.onap.dcae.collectors.veshv.commandline.CommandLineOption.HEALTH_CHECK_API_PORT +import org.onap.dcae.collectors.veshv.commandline.CommandLineOption.LISTEN_PORT + + +class WrongArgumentErrorTest : Spek({ + + describe("help message logic test") { + + given("at least one required option") { + val filledOptionList = listOf( + HEALTH_CHECK_API_PORT, + LISTEN_PORT) + on("help message preparation") { + val requiredParameters = WrongArgumentError.generateRequiredParametersNote(filledOptionList) + + it("should print out required fields") { + assertThat(requiredParameters).isEqualTo("Required parameters: p") + } + } + } + + given("no required option") { + val filledOptionList = listOf(HEALTH_CHECK_API_PORT) + + on("help message preparation") { + val requiredParameters = WrongArgumentError.generateRequiredParametersNote(filledOptionList) + + it("should not print required fields") { + assertThat(requiredParameters).isEqualTo("") + } + } + } + } + +}) -- cgit 1.2.3-korg