update prom config function in view model

This commit is contained in:
Martin Ptáček
2023-05-30 13:44:01 +02:00
parent a0041d63b9
commit fe2057c445
5 changed files with 143 additions and 154 deletions

View File

@@ -47,6 +47,7 @@ android {
} }
dependencies { dependencies {
implementation 'androidx.work:work-multiprocess:2.8.1'
def core_version = "1.10.1" def core_version = "1.10.1"
// custom - prometheus client java library // custom - prometheus client java library

View File

@@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
@@ -119,21 +118,6 @@ private fun TabPage(
} }
} }
private fun onCheckedChangeServer(
value : Boolean,
promViewModel: PromViewModel,
showDialog : MutableState<String>
){
if (value) {
val result : String? = promViewModel.turnServerOn()
if(result != null){
showDialog.value = result
}
} else {
promViewModel.turnServerOff()
}
}
@Composable @Composable
private fun PrometheusServerPage( private fun PrometheusServerPage(
promViewModel: PromViewModel, promViewModel: PromViewModel,
@@ -149,12 +133,14 @@ private fun PrometheusServerPage(
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally, 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( Switch(
checked = uiState.serverTurnedOn, checked = uiState.promConfig.prometheusServerEnabled,
onCheckedChange = {value : Boolean? -> onCheckedChange = {value : Boolean? ->
if(value != null){ 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) 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( TextField(
value = uiState.fqdn, value = uiState.promConfig.pushproxFqdn,
singleLine = true, singleLine = true,
enabled = !uiState.pushProxTurnedOn,
onValueChange = { onValueChange = {
promViewModel.updatePushProxFQDN(it) promViewModel.updatePromConfig(UpdatePromConfig.pushproxFqdn, it)
}, },
label = { label = {
Text(text = "Fully Qualified Domain Name") Text(text = "Fully Qualified Domain Name")
@@ -221,11 +196,10 @@ private fun PushProxPage(
) )
TextField( TextField(
value = uiState.pushProxURL, value = uiState.promConfig.pushproxProxyUrl,
singleLine = true, singleLine = true,
enabled = !uiState.pushProxTurnedOn,
onValueChange = { onValueChange = {
promViewModel.updatePushProxURL(it) promViewModel.updatePromConfig(UpdatePromConfig.pushproxProxyUrl, it)
}, },
label = { label = {
Text(text = "PushProx proxy URL") Text(text = "PushProx proxy URL")

View File

@@ -11,37 +11,41 @@ import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.OutOfQuotaPolicy import androidx.work.OutOfQuotaPolicy
import androidx.work.WorkManager 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 com.birdthedeveloper.prometheus.android.prometheus.android.exporter.worker.PushProxWorker
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.lang.Exception
enum class ConfigFileState { enum class ConfigFileState {
LOADING, LOADING,
ERROR, // file not found or was not parsed ERROR, // file was not parsed succesfully
MISSING, MISSING,
SUCCESS SUCCESS
} }
enum class UpdatePromConfig {
prometheusServerEnabled,
prometheusServerPort,
pushproxEnabled,
pushproxFqdn,
pushproxProxyUrl,
remoteWriteEnabled,
remoteWriteScrapeInterval,
remoteWriteEndpoint,
}
data class PromUiState( data class PromUiState(
val tabIndex : Int = 0, val tabIndex : Int = 0,
val serverTurnedOn : Boolean = false, val promConfig: PromConfiguration = PromConfiguration(),
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 configFileState : ConfigFileState = ConfigFileState.LOADING, val configFileState : ConfigFileState = ConfigFileState.LOADING,
) )
private val TAG : String = "PROMVIEWMODEL" private val TAG : String = "PROMVIEWMODEL"
class PromViewModel(): ViewModel() { 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" private val PROM_UNIQUE_WORK : String = "prom_unique_job"
@@ -50,23 +54,44 @@ class PromViewModel(): ViewModel() {
private lateinit var getContext: () -> Context private lateinit var getContext: () -> Context
init { init {
loadConfigurationFile()
}
private fun loadConfigurationFile(){
Log.v(TAG, "Checking for configuration file") Log.v(TAG, "Checking for configuration file")
viewModelScope.launch { viewModelScope.launch {
//TODO check for configuration file
delay(1000) 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 -> _uiState.update { current ->
current.copy(configFileState = ConfigFileState.MISSING) current.copy(configFileState = ConfigFileState.MISSING)
} }
} }
} }
fun getDefaultPort() : Int {
return DEFAULT_SERVER_PORT
} }
fun initializeWithApplicationContext(getContext : () -> Context){ fun initializeWithApplicationContext(getContext : () -> Context){
this.getContext = getContext this.getContext = getContext
loadConfigurationFile()
} }
fun updateTabIndex(index : Int){ fun updateTabIndex(index : Int){
@@ -77,59 +102,11 @@ class PromViewModel(): ViewModel() {
} }
} }
private fun getPromServerPort() : Int{ fun startWorker(){
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(){
val workManagerInstance = WorkManager.getInstance(getContext()) val workManagerInstance = WorkManager.getInstance(getContext())
// worker configuration // worker configuration
val inputData : Data = PushProxConfig( val inputData : Data = _uiState.value.promConfig.toWorkData()
pushProxFqdn = _uiState.value.fqdn,
pushProxUrl = _uiState.value.pushProxURL,
).toData()
// constraints // constraints
val constraints = Constraints.Builder() val constraints = Constraints.Builder()
@@ -151,47 +128,54 @@ class PromViewModel(): ViewModel() {
).enqueue() ).enqueue()
} }
// if result is not null, it contains an error message fun stopWorker(){
fun turnPushProxOn() : String?{ //TODO implement this thingy
val error : String? = validatePushProxSettings()
if(error != null){ return error }
// idempotent call
launchPushProxUsingWorkManager()
_uiState.update { current ->
current.copy(
pushProxTurnedOn = true
)
}
return null
}
fun turnPushProxOff(){
val workerManagerInstance = WorkManager.getInstance(getContext()) val workerManagerInstance = WorkManager.getInstance(getContext())
workerManagerInstance.cancelUniqueWork(PROM_UNIQUE_WORK) workerManagerInstance.cancelUniqueWork(PROM_UNIQUE_WORK)
_uiState.update {current ->
current.copy(
pushProxTurnedOn = false
)
}
} }
fun updatePushProxURL(url : String){ fun updatePromConfig(part : UpdatePromConfig, value : Any){
_uiState.update { current -> when(part){
current.copy( UpdatePromConfig.prometheusServerEnabled -> _uiState.update { current ->
pushProxURL = url 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
)
} }
} }
} }

View File

@@ -1,5 +1,6 @@
package com.birdthedeveloper.prometheus.android.prometheus.android.exporter.compose package com.birdthedeveloper.prometheus.android.prometheus.android.exporter.compose
import android.content.Context
import androidx.work.Data import androidx.work.Data
import androidx.work.workDataOf import androidx.work.workDataOf
@@ -11,16 +12,17 @@ data class PromConfiguration(
val prometheusServerEnabled : Boolean = true, val prometheusServerEnabled : Boolean = true,
val prometheusServerPort : Int = defaultPrometheusServerPort, val prometheusServerPort : Int = defaultPrometheusServerPort,
val pushproxEnabled : Boolean = false, val pushproxEnabled : Boolean = false,
val pushproxFqdn : String? = null, val pushproxFqdn : String = "",
val pushproxProxyUrl : String? = null, val pushproxProxyUrl : String = "",
val remoteWriteEnabled : Boolean = false, val remoteWriteEnabled : Boolean = false,
val remoteWriteScrapeInterval : Int = defaultRemoteWriteScrapeInterval, 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 { companion object {
suspend fun configFileExists(): Boolean { suspend fun configFileExists(context : Context): Boolean {
//TODO implement this asap //TODO implement this asap
return false return false
} }
@@ -30,11 +32,11 @@ data class PromConfiguration(
prometheusServerEnabled = data.getBoolean("0", true), prometheusServerEnabled = data.getBoolean("0", true),
prometheusServerPort = data.getInt("1", defaultPrometheusServerPort), prometheusServerPort = data.getInt("1", defaultPrometheusServerPort),
pushproxEnabled = data.getBoolean("2", false), pushproxEnabled = data.getBoolean("2", false),
pushproxFqdn = data.getString("3"), pushproxFqdn = data.getString("3") ?: "",
pushproxProxyUrl = data.getString("4"), pushproxProxyUrl = data.getString("4") ?: "",
remoteWriteEnabled = data.getBoolean("5", false), remoteWriteEnabled = data.getBoolean("5", false),
remoteWriteScrapeInterval = data.getInt("6", defaultRemoteWriteScrapeInterval), remoteWriteScrapeInterval = data.getInt("6", defaultRemoteWriteScrapeInterval),
remoteWriteEndpoint = data.getString("7"), remoteWriteEndpoint = data.getString("7") ?: "",
) )
} }

View File

@@ -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()
}
}