diff --git a/client/app/build.gradle b/client/app/build.gradle index a9787c9..f596919 100644 --- a/client/app/build.gradle +++ b/client/app/build.gradle @@ -47,6 +47,7 @@ android { } dependencies { + implementation 'androidx.work:work-multiprocess:2.8.1' def core_version = "1.10.1" // custom - prometheus client java library diff --git a/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/compose/HomeActivity.kt b/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/compose/HomeActivity.kt index b8a746f..65639db 100644 --- a/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/compose/HomeActivity.kt +++ b/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/compose/HomeActivity.kt @@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -99,19 +98,19 @@ private fun TabPage( navController: NavHostController, modifier: Modifier, -){ +) { val tabs = mapOf(0 to "Prom Server", 1 to "PushProx", 2 to "Remote write") - val uiState : PromUiState by promViewModel.uiState.collectAsState() + val uiState: PromUiState by promViewModel.uiState.collectAsState() Column(modifier = modifier) { TabRow(selectedTabIndex = uiState.tabIndex) { - tabs.forEach{ (index, text) -> - Tab(text = {Text(text)}, + tabs.forEach { (index, text) -> + Tab(text = { Text(text) }, selected = index == uiState.tabIndex, onClick = { promViewModel.updateTabIndex(index) }) } } - when(uiState.tabIndex){ + when (uiState.tabIndex) { 0 -> PrometheusServerPage(promViewModel, Modifier) 1 -> PushProxPage(promViewModel) 2 -> RemoteWritePage(promViewModel) @@ -119,21 +118,6 @@ private fun TabPage( } } -private fun onCheckedChangeServer( - value : Boolean, - promViewModel: PromViewModel, - showDialog : MutableState -){ - if (value) { - val result : String? = promViewModel.turnServerOn() - if(result != null){ - showDialog.value = result - } - } else { - promViewModel.turnServerOff() - } -} - @Composable private fun PrometheusServerPage( promViewModel: PromViewModel, @@ -149,12 +133,14 @@ private fun PrometheusServerPage( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { - Text(text = "Turn on Android Exporter on default port ${promViewModel.getDefaultPort()}") + Text( + text = "Turn on Android Exporter on port ${uiState.promConfig.prometheusServerPort}" + ) Switch( - checked = uiState.serverTurnedOn, + checked = uiState.promConfig.prometheusServerEnabled, onCheckedChange = {value : Boolean? -> if(value != null){ - onCheckedChangeServer(value, promViewModel, showDialogText) + promViewModel.updatePromConfig(UpdatePromConfig.prometheusServerEnabled, value) } } ) @@ -197,22 +183,11 @@ private fun PushProxPage( modifier = Modifier.padding(bottom = 30.dp) ) - if(uiState.pushProxTurnedOn){ - Text( - text = """ - To edit PushProx proxy URL or FQDN, turn it off first. - """.trimIndent(), - textAlign = TextAlign.Center, - modifier = Modifier.padding(bottom = 12.dp), - ) - } - TextField( - value = uiState.fqdn, + value = uiState.promConfig.pushproxFqdn, singleLine = true, - enabled = !uiState.pushProxTurnedOn, onValueChange = { - promViewModel.updatePushProxFQDN(it) + promViewModel.updatePromConfig(UpdatePromConfig.pushproxFqdn, it) }, label = { Text(text = "Fully Qualified Domain Name") @@ -221,11 +196,10 @@ private fun PushProxPage( ) TextField( - value = uiState.pushProxURL, + value = uiState.promConfig.pushproxProxyUrl, singleLine = true, - enabled = !uiState.pushProxTurnedOn, onValueChange = { - promViewModel.updatePushProxURL(it) + promViewModel.updatePromConfig(UpdatePromConfig.pushproxProxyUrl, it) }, label = { Text(text = "PushProx proxy URL") diff --git a/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/compose/PromViewModel.kt b/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/compose/PromViewModel.kt index adfd441..f0a0e16 100644 --- a/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/compose/PromViewModel.kt +++ b/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/compose/PromViewModel.kt @@ -11,37 +11,41 @@ import androidx.work.NetworkType import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OutOfQuotaPolicy import androidx.work.WorkManager -import com.birdthedeveloper.prometheus.android.prometheus.android.exporter.worker.PushProxConfig import com.birdthedeveloper.prometheus.android.prometheus.android.exporter.worker.PushProxWorker -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import java.lang.Exception enum class ConfigFileState { LOADING, - ERROR, // file not found or was not parsed + ERROR, // file was not parsed succesfully MISSING, SUCCESS } +enum class UpdatePromConfig { + prometheusServerEnabled, + prometheusServerPort, + pushproxEnabled, + pushproxFqdn, + pushproxProxyUrl, + remoteWriteEnabled, + remoteWriteScrapeInterval, + remoteWriteEndpoint, +} + data class PromUiState( val tabIndex : Int = 0, - val serverTurnedOn : Boolean = false, - val pushProxTurnedOn : Boolean = false, - val serverPort : Int? = null, // if null, use default port - val fqdn : String = "test.example.com", - val pushProxURL : String = "143.42.59.63:8080", + val promConfig: PromConfiguration = PromConfiguration(), val configFileState : ConfigFileState = ConfigFileState.LOADING, ) private val TAG : String = "PROMVIEWMODEL" class PromViewModel(): ViewModel() { - // constants - private val DEFAULT_SERVER_PORT : Int = 10101 //TODO register with prometheus community private val PROM_UNIQUE_WORK : String = "prom_unique_job" @@ -50,23 +54,44 @@ class PromViewModel(): ViewModel() { private lateinit var getContext: () -> Context + init { + loadConfigurationFile() + } + + private fun loadConfigurationFile(){ Log.v(TAG, "Checking for configuration file") viewModelScope.launch { - //TODO check for configuration file - delay(1000) - _uiState.update { current -> - current.copy(configFileState = ConfigFileState.MISSING) + + val fileExists = PromConfiguration.configFileExists(context = getContext()) + if (fileExists) { + val tempPromConfiguration : PromConfiguration + try { + tempPromConfiguration = PromConfiguration.loadFromConfigFile() + + _uiState.update { current -> + current.copy( + promConfig = tempPromConfiguration, + configFileState = ConfigFileState.SUCCESS, + ) + } + + }catch (e : Exception){ + _uiState.update { current -> + current.copy(configFileState = ConfigFileState.ERROR) + } + } + }else{ + _uiState.update { current -> + current.copy(configFileState = ConfigFileState.MISSING) + } } } } - fun getDefaultPort() : Int { - return DEFAULT_SERVER_PORT - } - fun initializeWithApplicationContext(getContext : () -> Context){ this.getContext = getContext + loadConfigurationFile() } fun updateTabIndex(index : Int){ @@ -77,59 +102,11 @@ class PromViewModel(): ViewModel() { } } - private fun getPromServerPort() : Int{ - return if(_uiState.value.serverPort != null){ - _uiState.value.serverPort!! - }else{ - DEFAULT_SERVER_PORT - } - } - - // if result is not null, it contains an error message - fun turnServerOn() : String?{ - try{ - //TODO rewrite asap -// prometheusServer.startInBackground( -// PrometheusServerConfig( -// getPromServerPort(), -// ::performScrape -// ) -// ) - }catch(e : Exception){ - Log.v(TAG, e.toString()) - return "Prometheus server failed!" - } - - _uiState.update { current -> - current.copy( - serverTurnedOn = true - ) - } - return null; - } - - fun turnServerOff(){ - //TODO implement - } - - private fun validatePushProxSettings() : String? { - val fqdn = _uiState.value.fqdn.trim().trim('\n') - val url = _uiState.value.pushProxURL.trim().trim('\n') - - if( fqdn.isEmpty() ) return "Fully Qualified Domain Name cannot be empty!" - if( url.isEmpty() ) return "PushProx URL cannot be empty!" - - return null - } - - private fun launchPushProxUsingWorkManager(){ + fun startWorker(){ val workManagerInstance = WorkManager.getInstance(getContext()) // worker configuration - val inputData : Data = PushProxConfig( - pushProxFqdn = _uiState.value.fqdn, - pushProxUrl = _uiState.value.pushProxURL, - ).toData() + val inputData : Data = _uiState.value.promConfig.toWorkData() // constraints val constraints = Constraints.Builder() @@ -151,47 +128,54 @@ class PromViewModel(): ViewModel() { ).enqueue() } - // if result is not null, it contains an error message - fun turnPushProxOn() : String?{ - val error : String? = validatePushProxSettings() - if(error != null){ return error } - - // idempotent call - launchPushProxUsingWorkManager() - - _uiState.update { current -> - current.copy( - pushProxTurnedOn = true - ) - } - - return null - } - - fun turnPushProxOff(){ + fun stopWorker(){ + //TODO implement this thingy val workerManagerInstance = WorkManager.getInstance(getContext()) workerManagerInstance.cancelUniqueWork(PROM_UNIQUE_WORK) - - _uiState.update {current -> - current.copy( - pushProxTurnedOn = false - ) - } } - fun updatePushProxURL(url : String){ - _uiState.update { current -> - current.copy( - pushProxURL = url - ) + fun updatePromConfig(part : UpdatePromConfig, value : Any){ + when(part){ + UpdatePromConfig.prometheusServerEnabled -> _uiState.update { current -> + current.copy(promConfig = current.promConfig.copy( + prometheusServerEnabled = value as Boolean + )) + } + UpdatePromConfig.prometheusServerPort -> _uiState.update { current -> + current.copy(promConfig = current.promConfig.copy( + prometheusServerPort = value as Int, + )) + } + UpdatePromConfig.pushproxEnabled -> _uiState.update { current -> + current.copy(promConfig = current.promConfig.copy( + pushproxEnabled = value as Boolean, + )) + } + UpdatePromConfig.pushproxFqdn -> _uiState.update { current -> + current.copy(promConfig = current.promConfig.copy( + pushproxFqdn = value as String, + )) + } + UpdatePromConfig.pushproxProxyUrl -> _uiState.update { current -> + current.copy(promConfig = current.promConfig.copy( + pushproxProxyUrl = value as String, + )) + } + UpdatePromConfig.remoteWriteEnabled -> _uiState.update { current -> + current.copy(promConfig = current.promConfig.copy( + remoteWriteEnabled = value as Boolean, + )) + } + UpdatePromConfig.remoteWriteScrapeInterval -> _uiState.update { current -> + current.copy(promConfig = current.promConfig.copy( + remoteWriteScrapeInterval = value as Int, + )) + } + UpdatePromConfig.remoteWriteEndpoint -> _uiState.update { current -> + current.copy(promConfig = current.promConfig.copy( + remoteWriteEndpoint = value as String, + )) + } } } - - fun updatePushProxFQDN(fqdn : String){ - _uiState.update { current -> - current.copy( - fqdn = fqdn - ) - } - } -} \ No newline at end of file +} diff --git a/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/compose/configuration.kt b/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/compose/configuration.kt index afd8963..41bd06c 100644 --- a/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/compose/configuration.kt +++ b/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/compose/configuration.kt @@ -1,5 +1,6 @@ package com.birdthedeveloper.prometheus.android.prometheus.android.exporter.compose +import android.content.Context import androidx.work.Data import androidx.work.workDataOf @@ -11,16 +12,17 @@ data class PromConfiguration( val prometheusServerEnabled : Boolean = true, val prometheusServerPort : Int = defaultPrometheusServerPort, val pushproxEnabled : Boolean = false, - val pushproxFqdn : String? = null, - val pushproxProxyUrl : String? = null, + val pushproxFqdn : String = "", + val pushproxProxyUrl : String = "", val remoteWriteEnabled : Boolean = false, val remoteWriteScrapeInterval : Int = defaultRemoteWriteScrapeInterval, - val remoteWriteEndpoint : String? = null, + val remoteWriteEndpoint : String = "", ) { - private val filepath : String = "" + private val filepath : String = "config.yaml" + private val alternativeFilepath : String = "config.yml" companion object { - suspend fun configFileExists(): Boolean { + suspend fun configFileExists(context : Context): Boolean { //TODO implement this asap return false } @@ -30,11 +32,11 @@ data class PromConfiguration( prometheusServerEnabled = data.getBoolean("0", true), prometheusServerPort = data.getInt("1", defaultPrometheusServerPort), pushproxEnabled = data.getBoolean("2", false), - pushproxFqdn = data.getString("3"), - pushproxProxyUrl = data.getString("4"), + pushproxFqdn = data.getString("3") ?: "", + pushproxProxyUrl = data.getString("4") ?: "", remoteWriteEnabled = data.getBoolean("5", false), remoteWriteScrapeInterval = data.getInt("6", defaultRemoteWriteScrapeInterval), - remoteWriteEndpoint = data.getString("7"), + remoteWriteEndpoint = data.getString("7") ?: "", ) } diff --git a/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/worker/PromWorker.kt b/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/worker/PromWorker.kt new file mode 100644 index 0000000..ca26c37 --- /dev/null +++ b/client/app/src/main/java/com/birdthedeveloper/prometheus/android/prometheus/android/exporter/worker/PromWorker.kt @@ -0,0 +1,28 @@ +package com.birdthedeveloper.prometheus.android.prometheus.android.exporter.worker + +import android.content.Context +import android.util.Log +import androidx.work.WorkerParameters +import androidx.work.multiprocess.RemoteCoroutineWorker +import com.birdthedeveloper.prometheus.android.prometheus.android.exporter.compose.PromConfiguration + +private val TAG = "Worker" + +class PromWorker( + val context : Context, + val parameters : WorkerParameters, +) : RemoteCoroutineWorker(context = context, parameters = parameters) { + + override suspend fun doRemoteWork(): Result { + val inputConfiguration : PromConfiguration = PromConfiguration.fromWorkData(inputData) + + while(true){ + Log.v(TAG, "Worker is working") + } + + //TODO implement this asap + + return Result.success() + } + +} \ No newline at end of file