Read config from environment variables 51/64351/4
authorPiotr Jaszczyk <piotr.jaszczyk@nokia.com>
Tue, 4 Sep 2018 07:59:20 +0000 (09:59 +0200)
committerPiotr Jaszczyk <piotr.jaszczyk@nokia.com>
Tue, 4 Sep 2018 11:18:50 +0000 (13:18 +0200)
The application configuration should be read from command line options
as well as from environment variables. The priority: cmd-line over env
over defaults.

Change-Id: I785fd1fbaf66f3eab84a162f037153f1688ed791
Issue-ID: DCAEGEN2-748
Signed-off-by: Piotr Jaszczyk <piotr.jaszczyk@nokia.com>
hv-collector-utils/src/main/kotlin/org/onap/dcae/collectors/veshv/utils/arrow/core.kt
hv-collector-utils/src/main/kotlin/org/onap/dcae/collectors/veshv/utils/commandline/ArgBasedConfiguration.kt
hv-collector-utils/src/main/kotlin/org/onap/dcae/collectors/veshv/utils/commandline/CommandLineOption.kt
hv-collector-utils/src/test/kotlin/org/onap/dcae/collectors/veshv/utils/arrow/CoreKtTest.kt
hv-collector-utils/src/test/kotlin/org/onap/dcae/collectors/veshv/utils/commandline/CommandLineOptionTest.kt [new file with mode: 0644]

index 844d18d..2d538b7 100644 (file)
 package org.onap.dcae.collectors.veshv.utils.arrow
 
 import arrow.core.Either
+import arrow.core.None
 import arrow.core.Option
+import arrow.core.Some
 import arrow.core.identity
+import arrow.syntax.collections.firstOption
+import java.util.*
 import java.util.concurrent.atomic.AtomicReference
 
 /**
@@ -32,3 +36,11 @@ import java.util.concurrent.atomic.AtomicReference
 fun <A> Either<A, A>.flatten() = fold(::identity, ::identity)
 
 fun <A> AtomicReference<A>.getOption() = Option.fromNullable(get())
+
+fun <A> Option.Companion.fromNullablesChain(firstValue: A?, vararg nextValues: () -> A?): Option<A> =
+        if (firstValue != null)
+            Option.just(firstValue)
+        else nextValues.asSequence()
+                .map { it() }
+                .filter { it != null }
+                .firstOption()
index 1663488..1ebe4e4 100644 (file)
@@ -27,6 +27,7 @@ import arrow.core.getOrElse
 import org.apache.commons.cli.CommandLine
 import org.apache.commons.cli.CommandLineParser
 import org.apache.commons.cli.Options
+import org.onap.dcae.collectors.veshv.utils.arrow.fromNullablesChain
 import java.io.File
 import java.nio.file.Path
 import java.nio.file.Paths
@@ -77,6 +78,7 @@ abstract class ArgBasedConfiguration<T>(private val parser: CommandLineParser) {
 
     protected fun stringPathToPath(path: String): Path = Paths.get(File(path).toURI())
 
-    private fun CommandLine.optionValue(cmdLineOpt: CommandLineOption): Option<String> =
-            Option.fromNullable(getOptionValue(cmdLineOpt.option.opt))
+    private fun CommandLine.optionValue(cmdLineOpt: CommandLineOption) = Option.fromNullablesChain(
+            getOptionValue(cmdLineOpt.option.opt),
+            { System.getenv(cmdLineOpt.environmentVariableName()) })
 }
index 836a05d..3a154db 100644 (file)
@@ -117,5 +117,14 @@ enum class CommandLineOption(val option: Option) {
             .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"
+    }
 }
index 585851b..2935943 100644 (file)
@@ -20,6 +20,7 @@
 package org.onap.dcae.collectors.veshv.utils.arrow
 
 import arrow.core.None
+import arrow.core.Option
 import arrow.core.Some
 import org.assertj.core.api.Assertions.assertThat
 import org.jetbrains.spek.api.Spek
@@ -27,6 +28,7 @@ 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.jetbrains.spek.api.dsl.xdescribe
 import java.util.concurrent.atomic.AtomicReference
 
 
@@ -34,7 +36,7 @@ import java.util.concurrent.atomic.AtomicReference
  * @author Piotr Jaszczyk <piotr.jaszczyk></piotr.jaszczyk>@nokia.com>
  * @since August 2018
  */
-internal class CoreKtTest: Spek({
+internal class CoreKtTest : Spek({
     describe("AtomicReference.getOption") {
         given("empty atomic reference") {
             val atomicReference = AtomicReference<String>()
@@ -60,4 +62,81 @@ internal class CoreKtTest: Spek({
             }
         }
     }
-})
\ No newline at end of file
+
+    describe("Option.fromNullablesChain") {
+        given("one non-null element") {
+            val just = "some text"
+            on("calling factory") {
+                val result = Option.fromNullablesChain(just)
+
+                it("should return Some($just)") {
+                    assertThat(result).isEqualTo(Some(just))
+                }
+            }
+        }
+
+        given("one null element") {
+            val just: String? = null
+            on("calling factory") {
+                val result = Option.fromNullablesChain(just)
+
+                it("should return None") {
+                    assertThat(result).isEqualTo(None)
+                }
+            }
+        }
+
+        given("first non-null element") {
+            val first = "some text"
+            val second: String? = null
+            var secondAskedForValue = false
+            on("calling factory") {
+                val result = Option.fromNullablesChain(first, { secondAskedForValue = true; second })
+
+                it("should return Some($first)") {
+                    assertThat(result).isEqualTo(Some(first))
+                }
+
+                it("should have not called second provider (should be lazy)") {
+                    assertThat(secondAskedForValue).isFalse()
+                }
+            }
+        }
+
+        given("two non-null elements") {
+            val first = "some text"
+            val second = "another text"
+            on("calling factory") {
+                val result = Option.fromNullablesChain(first, { second })
+
+                it("should return Some($first)") {
+                    assertThat(result).isEqualTo(Some(first))
+                }
+            }
+        }
+
+        given("two null elements") {
+            val first: String? = null
+            val second: String? = null
+            on("calling factory") {
+                val result = Option.fromNullablesChain(first, { second })
+
+                it("should return None") {
+                    assertThat(result).isEqualTo(None)
+                }
+            }
+        }
+
+        given("second non-null element") {
+            val first: String? = null
+            val second = "another text"
+            on("calling factory") {
+                val result = Option.fromNullablesChain(first, { second })
+
+                it("should return Some($second)") {
+                    assertThat(result).isEqualTo(Some(second))
+                }
+            }
+        }
+    }
+})
diff --git a/hv-collector-utils/src/test/kotlin/org/onap/dcae/collectors/veshv/utils/commandline/CommandLineOptionTest.kt b/hv-collector-utils/src/test/kotlin/org/onap/dcae/collectors/veshv/utils/commandline/CommandLineOptionTest.kt
new file mode 100644 (file)
index 0000000..f36df04
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * ============LICENSE_START=======================================================
+ * dcaegen2-collectors-veshv
+ * ================================================================================
+ * Copyright (C) 2018 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.utils.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
+
+/**
+ * @author Piotr Jaszczyk <piotr.jaszczyk@nokia.com>
+ * @since September 2018
+ */
+class CommandLineOptionTest : Spek({
+    describe("command line options enum") {
+        describe("environment variables") {
+            given("sample option and prefix") {
+                val opt = CommandLineOption.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 = CommandLineOption.DUMMY_MODE
+
+                on("calling environmentVariableName") {
+                    val result = opt.environmentVariableName()
+
+                    it("should return prefixed upper snake cased long option name") {
+                        assertThat(result).isEqualTo("VESHV_DUMMY")
+                    }
+                }
+            }
+        }
+    }
+})