2023-02-10 02:36:01 +00:00
import assert from "node:assert" ;
2023-02-19 15:05:00 +00:00
import { join } from "node:path" ;
2023-02-10 02:36:01 +00:00
import { Alpine } from "../../alpine.js" ;
import { Runit } from "../../runit/index.js" ;
import { loadGrafanaSecretsFile } from "./secrets.js" ;
2023-02-19 15:05:00 +00:00
const provisioningDir = "/etc/grafana/provisioning/" ;
2023-02-10 02:36:01 +00:00
// TODO: grafana-image-renderer?
// /etc/conf.d/grafana
// # To enable image rendering run
// # $ apk add grafana-image-renderer
// # $ /etc/init.d/grafana-image-renderer start
// # and configure /etc/grafana.ini to use it
// #[rendering]
// #server_url = http://127.0.0.1:3001/render
// #callback_url = http://127.0.0.1:3000/
export async function setupGrafana (
alpine : Alpine ,
runit : Runit
) : Promise < void > {
await alpine . addPackages ( [ "grafana" ] ) ;
const passwd = await alpine . readPasswd ( ) ;
const user = passwd . find ( ( e ) = > e . name === "grafana" ) ;
assert ( ! ! user , "no existe el usuario grafana" ) ;
// TODO: data
await alpine . fstab . addTmpfs ( "/var/lib/grafana" , {
uid : user.uid ,
gid : user.gid ,
mode : "700" ,
} ) ;
await alpine . writeFile ( "/etc/grafana.ini" , await genConfig ( ) , user ) ;
2023-02-19 15:05:00 +00:00
await alpine . writeFile (
join ( provisioningDir , "datasources/loki.yaml" ) ,
`
apiVersion : 1
datasources :
- name : Loki
type : loki
access : proxy
url : http : //localhost:3100
` ,
user
) ;
2023-02-10 02:36:01 +00:00
await alpine . sudoWriteExecutable (
"/usr/local/sbin/nulo-grafana-cli" ,
` #!/bin/sh
cd /
exec chpst - u grafana :grafana grafana - cli -- homepath / usr / share / grafana -- config / etc / grafana . ini "$@" `
) ;
await runit . addService (
"grafana" ,
` #!/bin/sh
export GRAFANA_HOME = / v a r / l i b / g r a f a n a
cd "$GRAFANA_HOME"
2023-02-19 15:05:00 +00:00
exec chpst - u grafana :grafana grafana - server - config / etc / grafana . ini - homepath / usr / share / grafana
2023-02-10 02:36:01 +00:00
`
) ;
}
async function genConfig ( ) : Promise < string > {
const secrets = await loadGrafanaSecretsFile ( ) ;
return `
; app_mode = production
; instance_name = \ $ { HOSTNAME }
# force migration will run migrations that might cause dataloss
; force_migration = false
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Paths # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[ paths ]
data = / v a r / l i b / g r a f a n a
# Temporary files in \ ` data \` directory older than given duration will be removed
; temp_data_lifetime = 24 h
# Directory where grafana can store logs
; logs = / v a r / l o g / g r a f a n a
plugins = / v a r / l i b / g r a f a n a / p l u g i n s
# folder that contains provisioning config files that grafana will apply on startup and while running .
2023-02-19 15:05:00 +00:00
provisioning = $ { provisioningDir }
2023-02-10 02:36:01 +00:00
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Server # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[ server ]
# Protocol ( http , https , h2 , socket )
; protocol = http
http_addr = 127.0 . 0.1
http_port = 3050
# The public facing domain name used to access grafana from a browser
; domain = localhost
# Redirect to correct domain if host header does not match domain
# Prevents DNS rebinding attacks
; enforce_domain = false
# The full public facing url you use in browser , used for redirects and emails
# If you use reverse proxy and sub path specify full url ( with sub path )
; root_url = % ( protocol ) s : //%(domain)s:%(http_port)s/
# the path relative working path
; static_root_path = public
; socket =
# Sets the maximum time using a duration format ( 5 s / 5 m / 5 ms ) before timing out read of an incoming request and closing idle connections .
# 0 means there is no timeout for reading the request .
; read_timeout = 0
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Database # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[ database ]
; type = sqlite3
; name = grafana
; path = grafana . db
# For "sqlite3" only . cache mode setting used for connecting to the database . ( private , shared )
; cache_mode = private
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Data proxy # # # # # # # # # # # # # # # # # # # # # # # # # # #
[ dataproxy ]
# This enables data proxy logging , default is false
; logging = false
# How long the data proxy waits to read the headers of the response before timing out , default is 30 seconds .
# This setting also applies to core backend HTTP data sources where query requests use an HTTP client with timeout set .
; timeout = 30
# How long the data proxy waits to establish a TCP connection before timing out , default is 10 seconds .
; dialTimeout = 10
# How many seconds the data proxy waits before sending a keepalive probe request .
; keep_alive_seconds = 30
# How many seconds the data proxy waits for a successful TLS Handshake before timing out .
; tls_handshake_timeout_seconds = 10
# How many seconds the data proxy will wait for a server ' s first response headers after
# fully writing the request headers if the request has an "Expect: 100-continue"
# header . A value of 0 will result in the body being sent immediately , without
# waiting for the server to approve .
; expect_continue_timeout_seconds = 1
# Optionally limits the total number of connections per host , including connections in the dialing ,
# active , and idle states . On limit violation , dials will block .
# A value of zero ( 0 ) means no limit .
; max_conns_per_host = 0
# The maximum number of idle connections that Grafana will keep alive .
; max_idle_connections = 100
# How many seconds the data proxy keeps an idle connection open before timing out .
; idle_conn_timeout_seconds = 90
# If enabled and user is not anonymous , data proxy will add X - Grafana - User header with username into the request , default is false .
; send_user_header = false
# Limit the amount of bytes that will be read / accepted from responses of outgoing HTTP requests .
; response_limit = 0
# Limits the number of rows that Grafana will process from SQL data sources .
; row_limit = 1000000
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Analytics # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[ analytics ]
; reporting_enabled = true
; reporting_distributor = grafana - labs
; check_for_updates = true
; check_for_plugin_updates = true
# Controls if the UI contains any links to user feedback forms
; feedback_links_enabled = true
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Security # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[ security ]
disable_initial_admin_creation = false
admin_user = admin
admin_password = $ { secrets . defaultAdminPassword }
disable_gravatar = true
# set to true if you host Grafana behind HTTPS . default is false .
; cookie_secure = false
# Enable adding the Content - Security - Policy header to your requests .
# CSP allows to control resources the user agent is allowed to load and helps prevent XSS attacks .
content_security_policy = true
# Set Content Security Policy template used when adding the Content - Security - Policy header to your requests .
# $NONCE in the template includes a random nonce .
# $ROOT_PATH is server . root_url without the protocol .
; content_security_policy_template = "" "script-src 'self' 'unsafe-eval' 'unsafe-inline' 'strict-dynamic' $NONCE;object-src 'none';font-src 'self';style-src 'self' 'unsafe-inline' blob:;img-src * data:;base-uri 'self';connect-src 'self' grafana.com ws://$ROOT_PATH wss://$ROOT_PATH;manifest-src 'self';media-src 'none';form-action 'self';" ""
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Snapshots # # # # # # # # # # # # # # # # # # # # # # # # # # #
[ snapshots ]
# snapshot sharing options
; external_enabled = true
; external_snapshot_url = https : //snapshots.raintank.io
; external_snapshot_name = Publish to snapshots . raintank . io
# Set to true to enable this Grafana instance act as an external snapshot server and allow unauthenticated requests for
# creating and deleting snapshots .
; public_mode = false
# remove expired snapshot
; snapshot_remove_expired = true
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Users # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[ users ]
allow_sign_up = false
default_locale = es - AR
# Path to a custom home page . Users are only redirected to this if the default home dashboard is used . It should match a frontend route and contain a leading slash .
; home_page =
# External user management , these options affect the organization users view
; external_manage_link_url =
; external_manage_link_name =
; external_manage_info =
# Viewers can edit / inspect dashboard settings in the browser . But not save the dashboard .
; viewers_can_edit = false
# Editors can administrate dashboard , folders and teams they create
; editors_can_admin = false
# The duration in time a user invitation remains valid before expiring . This setting should be expressed as a duration . Examples : 6h ( hours ) , 2 d ( days ) , 1 w ( week ) . Default is 24 h ( 24 hours ) . The minimum supported duration is 15 m ( 15 minutes ) .
; user_invite_max_lifetime_duration = 24 h
# Enter a comma - separated list of users login to hide them in the Grafana UI . These users are shown to Grafana admins and themselves .
; hidden_users =
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Basic Auth # # # # # # # # # # # # # # # # # # # # # # # # # #
[ auth . basic ]
; enabled = true
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # SMTP / Emailing # # # # # # # # # # # # # # # # # # # # # # # # # #
# TODO : smtp
[ smtp ]
; enabled = false
; host = localhost :25
; user =
# If the password contains # or ; you have to wrap it with triple quotes . Ex "" "#password;" ""
; password =
; cert_file =
; key_file =
; skip_verify = false
; from_address = admin @grafana . localhost
; from_name = Grafana
# EHLO identity in SMTP dialog ( defaults to instance_name )
; ehlo_identity = dashboard . example . com
# SMTP startTLS policy ( defaults to 'OpportunisticStartTLS' )
; startTLS_policy = NoStartTLS
[ emails ]
; welcome_email_on_sign_up = false
; templates_pattern = emails / * . html , emails / * . txt
; content_types = text / html
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Logging # # # # # # # # # # # # # # # # # # # # # # # # # #
[ log ]
# TODO : syslog
# Either "console" , "file" , "syslog" . Default is console and file
# Use space to separate multiple modes , e . g . "console file"
mode = console file
# Either "debug" , "info" , "warn" , "error" , "critical" , default is "info"
; level = info
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Unified Alerting # # # # # # # # # # # # # # # # # # # #
[ unified_alerting ]
# Enable the Unified Alerting sub - system and interface . When enabled we ' ll migrate all of your alert rules and notification channels to the new system . New alert rules will be created and your notification channels will be converted into an Alertmanager configuration . Previous data is preserved to enable backwards compatibility but new data is removed .
; enabled = true
# Specify the frequency of polling for admin config changes .
# The interval string is a possibly signed sequence of decimal numbers , followed by a unit suffix ( ms , s , m , h , d ) , e . g . 30 s or 1 m .
; admin_config_poll_interval = 60 s
# Specify the frequency of polling for Alertmanager config changes .
# The interval string is a possibly signed sequence of decimal numbers , followed by a unit suffix ( ms , s , m , h , d ) , e . g . 30 s or 1 m .
; alertmanager_config_poll_interval = 60 s
# Time to wait for an instance to send a notification via the Alertmanager . In HA , each Grafana instance will
# be assigned a position ( e . g . 0 , 1 ) . We then multiply this position with the timeout to indicate how long should
# each instance wait before sending the notification to take into account replication lag .
# The interval string is a possibly signed sequence of decimal numbers , followed by a unit suffix ( ms , s , m , h , d ) , e . g . 30 s or 1 m .
; ha_peer_timeout = "15s"
# The interval between sending gossip messages . By lowering this value ( more frequent ) gossip messages are propagated
# across cluster more quickly at the expense of increased bandwidth usage .
# The interval string is a possibly signed sequence of decimal numbers , followed by a unit suffix ( ms , s , m , h , d ) , e . g . 30 s or 1 m .
; ha_gossip_interval = "200ms"
# The interval between gossip full state syncs . Setting this interval lower ( more frequent ) will increase convergence speeds
# across larger clusters at the expense of increased bandwidth usage .
# The interval string is a possibly signed sequence of decimal numbers , followed by a unit suffix ( ms , s , m , h , d ) , e . g . 30 s or 1 m .
; ha_push_pull_interval = "60s"
# Enable or disable alerting rule execution . The alerting UI remains visible . This option has a legacy version in the [ alerting ] section that takes precedence .
; execute_alerts = true
# Alert evaluation timeout when fetching data from the datasource . This option has a legacy version in the [ alerting ] section that takes precedence .
# The timeout string is a possibly signed sequence of decimal numbers , followed by a unit suffix ( ms , s , m , h , d ) , e . g . 30 s or 1 m .
; evaluation_timeout = 30 s
# Number of times we ' ll attempt to evaluate an alert rule before giving up on that evaluation . This option has a legacy version in the [ alerting ] section that takes precedence .
; max_attempts = 3
# Minimum interval to enforce between rule evaluations . Rules will be adjusted if they are less than this value or if they are not multiple of the scheduler interval ( 10 s ) . Higher values can help with resource management as we ' ll schedule fewer evaluations over time . This option has a legacy version in the [ alerting ] section that takes precedence .
# The interval string is a possibly signed sequence of decimal numbers , followed by a unit suffix ( ms , s , m , h , d ) , e . g . 30 s or 1 m .
; min_interval = 10 s
[ unified_alerting . reserved_labels ]
# Comma - separated list of reserved labels added by the Grafana Alerting engine that should be disabled .
# For example : disabled_labels = grafana_folder
; disabled_labels =
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Internal Grafana Metrics # # # # # # # # # # # # # # # # # # # # # # # # # #
# Metrics available at HTTP URL / metrics and / metrics / plugins / : pluginId
[ metrics ]
# Disable / Enable internal metrics
; enabled = true
# Graphite Publish interval
; interval_seconds = 10
# Disable total stats ( stat_totals_ * ) metrics to be generated
; disable_total_stats = false
# If both are set , basic auth will be required for the metrics endpoints .
; basic_auth_username =
; basic_auth_password =
# Metrics environment info adds dimensions to the grafana_environment_info metric , which
# can expose more information about the Grafana instance .
[ metrics . environment_info ]
# exampleLabel1 = exampleValue1
# exampleLabel2 = exampleValue2
# Send internal metrics to Graphite
[ metrics . graphite ]
# Enable by setting the address setting ( ex localhost :2003 )
; address =
; prefix = prod . grafana . % ( instance_name ) s .
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Distributed tracing # # # # # # # # # # # #
# Opentracing is deprecated use opentelemetry instead
[ tracing . jaeger ]
# Enable by setting the address sending traces to jaeger ( ex localhost :6831 )
; address = localhost :6831
# Tag that will always be included in when creating new spans . ex ( tag1 :value1 , tag2 :value2 )
; always_included_tag = tag1 :value1
# Type specifies the type of the sampler : const , probabilistic , rateLimiting , or remote
; sampler_type = const
# jaeger samplerconfig param
# for "const" sampler , 0 or 1 for always false / true respectively
# for "probabilistic" sampler , a probability between 0 and 1
# for "rateLimiting" sampler , the number of spans per second
# for "remote" sampler , param is the same as for "probabilistic"
# and indicates the initial sampling rate before the actual one
# is received from the mothership
; sampler_param = 1
# sampling_server_url is the URL of a sampling manager providing a sampling strategy .
; sampling_server_url =
# Whether or not to use Zipkin propagation ( x - b3 - HTTP headers ) .
; zipkin_propagation = false
# Setting this to true disables shared RPC spans .
# Not disabling is the most common setting when using Zipkin elsewhere in your infrastructure .
; disable_shared_zipkin_spans = false
[ tracing . opentelemetry ]
# attributes that will always be included in when creating new spans . ex ( key1 :value1 , key2 :value2 )
; custom_attributes = key1 :value1 , key2 :value2
[ tracing . opentelemetry . jaeger ]
# jaeger destination ( ex http : //localhost:14268/api/traces)
; address = http : //localhost:14268/api/traces
# Propagation specifies the text map propagation format : w3c , jaeger
; propagation = jaeger
# This is a configuration for OTLP exporter with GRPC protocol
[ tracing . opentelemetry . otlp ]
# otlp destination ( ex localhost :4317 )
; address = localhost :4317
# Propagation specifies the text map propagation format : w3c , jaeger
; propagation = w3c
[ rendering ]
# Options to configure a remote HTTP image rendering service , e . g . using https : //github.com/grafana/grafana-image-renderer.
# URL to a remote HTTP image renderer service , e . g . http : //localhost:8081/render, will enable Grafana to render panels and dashboards to PNG-images using HTTP requests to an external service.
; server_url =
# If the remote HTTP image renderer service runs on a different server than the Grafana server you may have to configure this to a URL where Grafana is reachable , e . g . http : //grafana.domain/.
; callback_url =
# An auth token that will be sent to and verified by the renderer . The renderer will deny any request without an auth token matching the one configured on the renderer side .
; renderer_token = -
# Concurrent render request limit affects when the / render HTTP endpoint is used . Rendering many images at the same time can overload the server ,
# which this setting can help protect against by only allowing a certain amount of concurrent requests .
; concurrent_render_request_limit = 30
[ panels ]
# If set to true Grafana will allow script tags in text panels . Not recommended as it enable XSS vulnerabilities .
; disable_sanitize_html = false
[ plugins ]
; enable_alpha = false
; app_tls_skip_verify_insecure = false
# Enter a comma - separated list of plugin identifiers to identify plugins to load even if they are unsigned . Plugins with modified signatures are never loaded .
; allow_loading_unsigned_plugins =
# Enable or disable installing / uninstalling / updating plugins directly from within Grafana .
; plugin_admin_enabled = false
; plugin_admin_external_manage_enabled = false
; plugin_catalog_url = https : //grafana.com/grafana/plugins/
# Enter a comma - separated list of plugin identifiers to hide in the plugin catalog .
; plugin_catalog_hidden_plugins =
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Grafana Live # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
[ live ]
# max_connections to Grafana Live WebSocket endpoint per Grafana server instance . See Grafana Live docs
# if you are planning to make it higher than default 100 since this can require some OS and infrastructure
# tuning . 0 disables Live , - 1 means unlimited connections .
; max_connections = 100
# allowed_origins is a comma - separated list of origins that can establish connection with Grafana Live .
# If not set then origin will be matched over root_url . Supports wildcard symbol "*" .
; allowed_origins =
# engine defines an HA ( high availability ) engine to use for Grafana Live . By default no engine used - in
# this case Live features work only on a single Grafana server . Available options : "redis" .
# Setting ha_engine is an EXPERIMENTAL feature .
; ha_engine =
# ha_engine_address sets a connection address for Live HA engine . Depending on engine type address format can differ .
# For now we only support Redis connection address in "host:port" format .
# This option is EXPERIMENTAL .
; ha_engine_address = "127.0.0.1:6379"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Grafana Image Renderer Plugin # # # # # # # # # # # # # # # # # # # # # # # # # #
[ plugin . grafana - image - renderer ]
# Instruct headless browser instance to use a default timezone when not provided by Grafana , e . g . when rendering panel image of alert .
# See ICU ’ s metaZones . txt ( https : //cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt) for a list of supported
# timezone IDs . Fallbacks to TZ environment variable if not set .
; rendering_timezone =
# Instruct headless browser instance to use a default language when not provided by Grafana , e . g . when rendering panel image of alert .
# Please refer to the HTTP header Accept - Language to understand how to format this value , e . g . 'fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5' .
; rendering_language =
# Instruct headless browser instance to use a default device scale factor when not provided by Grafana , e . g . when rendering panel image of alert .
# Default is 1 . Using a higher value will produce more detailed images ( higher DPI ) , but will require more disk space to store an image .
; rendering_viewport_device_scale_factor =
# Instruct headless browser instance whether to ignore HTTPS errors during navigation . Per default HTTPS errors are not ignored . Due to
# the security risk it ' s not recommended to ignore HTTPS errors .
; rendering_ignore_https_errors =
# Instruct headless browser instance whether to capture and log verbose information when rendering an image . Default is false and will
# only capture and log error messages . When enabled , debug messages are captured and logged as well .
# For the verbose information to be included in the Grafana server log you have to adjust the rendering log level to debug , configure
# [ log ] . filter = rendering :debug.
; rendering_verbose_logging =
# Instruct headless browser instance whether to output its debug and error messages into running process of remote rendering service .
# Default is false . This can be useful to enable ( true ) when troubleshooting .
; rendering_dumpio =
# Additional arguments to pass to the headless browser instance . Default is -- no - sandbox . The list of Chromium flags can be found
# here ( https : //peter.sh/experiments/chromium-command-line-switches/). Multiple arguments is separated with comma-character.
; rendering_args =
# You can configure the plugin to use a different browser binary instead of the pre - packaged version of Chromium .
# Please note that this is not recommended , since you may encounter problems if the installed version of Chrome / Chromium is not
# compatible with the plugin .
; rendering_chrome_bin =
# Instruct how headless browser instances are created . Default is 'default' and will create a new browser instance on each request .
# Mode 'clustered' will make sure that only a maximum of browsers / incognito pages can execute concurrently .
# Mode 'reusable' will have one browser instance and will create a new incognito page on each request .
; rendering_mode =
# When rendering_mode = clustered , you can instruct how many browsers or incognito pages can execute concurrently . Default is 'browser'
# and will cluster using browser instances .
# Mode 'context' will cluster using incognito pages .
; rendering_clustering_mode =
# When rendering_mode = clustered , you can define the maximum number of browser instances / incognito pages that can execute concurrently . Default is '5' .
; rendering_clustering_max_concurrency =
# When rendering_mode = clustered , you can specify the duration a rendering request can take before it will time out . Default is 30 seconds .
; rendering_clustering_timeout =
# Limit the maximum viewport width , height and device scale factor that can be requested .
; rendering_viewport_max_width =
; rendering_viewport_max_height =
; rendering_viewport_max_device_scale_factor =
# Change the listening host and port of the gRPC server . Default host is 127.0 . 0.1 and default port is 0 and will automatically assign
# a port not in use .
; grpc_host =
; grpc_port =
[ enterprise ]
# Path to a valid Grafana Enterprise license . jwt file
; license_path =
[ feature_toggles ]
# there are currently two ways to enable feature toggles in the grafana . ini .
# you can either pass an array of feature you want to enable to the enable field or
# configure each toggle by setting the name of the toggle to true / false . Toggles set to true / false
# will take presidence over toggles in the enable list .
; enable = feature1 , feature2
; feature1 = true
; feature2 = false
` ;
}