diff --git a/.bra.toml b/.bra.toml index 005306880a0..22f56050c52 100644 --- a/.bra.toml +++ b/.bra.toml @@ -2,9 +2,8 @@ init_cmds = [ ["make", "gen-go"], ["make", "gen-jsonnet"], - ["GO_BUILD_DEV=1", "make", "build-cli"], - ["GO_BUILD_DEV=1", "make", "build-server"], - ["./bin/grafana-server", "-packaging=dev", "cfg:app_mode=development"] + ["GO_BUILD_DEV=1", "make", "build-go"], + ["./bin/grafana", "server", "-packaging=dev", "cfg:app_mode=development"] ] watch_all = true follow_symlinks = true @@ -20,6 +19,6 @@ build_delay = 1500 cmds = [ ["make", "gen-go"], ["make", "gen-jsonnet"], - ["GO_BUILD_DEV=1", "make", "build-server"], - ["./bin/grafana-server", "-packaging=dev", "cfg:app_mode=development"] + ["GO_BUILD_DEV=1", "make", "build-go"], + ["./bin/grafana", "server", "-packaging=dev", "cfg:app_mode=development"] ] diff --git a/Dockerfile b/Dockerfile index 1032ba60ae7..966aebfa347 100644 --- a/Dockerfile +++ b/Dockerfile @@ -82,7 +82,7 @@ RUN export GF_GID_NAME=$(getent group $GF_GID | cut -d':' -f1) && \ chown -R "grafana:$GF_GID_NAME" "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING" && \ chmod -R 777 "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING" -COPY --from=go-builder /grafana/bin/*/grafana-server /grafana/bin/*/grafana-cli ./bin/ +COPY --from=go-builder /grafana/bin/*/grafana-server /grafana/bin/*/grafana-cli /grafana/bin/*/grafana ./bin/ COPY --from=js-builder /grafana/public ./public COPY --from=js-builder /grafana/tools ./tools diff --git a/Dockerfile.ubuntu b/Dockerfile.ubuntu index 077d97a0c99..a6b62ea1e25 100644 --- a/Dockerfile.ubuntu +++ b/Dockerfile.ubuntu @@ -77,7 +77,7 @@ RUN mkdir -p "$GF_PATHS_HOME/.aws" && \ chown -R grafana:grafana "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING" && \ chmod -R 777 "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING" -COPY --from=go-builder /src/grafana/bin/*/grafana-server /src/grafana/bin/*/grafana-cli bin/ +COPY --from=go-builder /src/grafana/bin/*/grafana-server /src/grafana/bin/*/grafana-cli /grafana/bin/*/grafana bin/ COPY --from=js-builder /usr/src/app/public public COPY --from=js-builder /usr/src/app/tools tools diff --git a/Makefile b/Makefile index 03a2c40b48b..18c873b8ad2 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ WIRE_TAGS = "oss" -include local/Makefile include .bingo/Variables.mk -.PHONY: all deps-go deps-js deps build-go build-server build-cli build-js build build-docker-full build-docker-full-ubuntu lint-go golangci-lint test-go test-js gen-ts test run run-frontend clean devenv devenv-down protobuf drone help gen-go gen-cue +.PHONY: all deps-go deps-js deps build-go build-backend build-server build-cli build-js build build-docker-full build-docker-full-ubuntu lint-go golangci-lint test-go test-js gen-ts test run run-frontend clean devenv devenv-down protobuf drone help gen-go gen-cue GO = go GO_FILES ?= ./pkg/... @@ -82,6 +82,10 @@ build-go: $(MERGED_SPEC_TARGET) gen-go ## Build all Go binaries. @echo "build go files" $(GO) run build.go $(GO_BUILD_FLAGS) build +build-backend: ## Build Grafana backend. + @echo "build backend" + $(GO) run build.go $(GO_BUILD_FLAGS) build-backend + build-server: ## Build Grafana server. @echo "build server" $(GO) run build.go $(GO_BUILD_FLAGS) build-server diff --git a/packaging/conf/nfpm.yaml b/packaging/conf/nfpm.yaml deleted file mode 100644 index 03d26fa5ff2..00000000000 --- a/packaging/conf/nfpm.yaml +++ /dev/null @@ -1,35 +0,0 @@ -name: "grafana" -arch: "${ARCH}" -platform: "linux" -version: "${VERSION}" -section: "default" -priority: "extra" -replaces: -- grafana -provides: -- grafana-server -- grafana-cli -depends: -- adduser -maintainer: "" -description: | - Grafana -vendor: "Grafana" -homepage: "https://grafana.com" -license: "Apache 2" -bindir: "/usr/sbin" -files: - "./bin/grafana-server": "/usr/sbin/grafana-server" - "./bin/grafana-cli": "/usr/sbin/grafana-cli" -config_files: - ./packaging/deb/init.d/grafana-server: "/etc/init.d/grafana-server" - ./packaging/deb/default/grafana-server: "/etc/default/grafana-server" - ./packaging/deb/systemd/grafana-server.service: "/usr/lib/systemd/system/grafana-server.service" -overrides: - rpm: - scripts: - preinstall: ./scripts/preinstall.sh - postremove: ./scripts/postremove.sh - deb: - scripts: - postinstall: ./packaging/deb/control/postinst diff --git a/packaging/deb/control/postinst b/packaging/deb/control/postinst index 952e71e2239..bb7a040a5fd 100755 --- a/packaging/deb/control/postinst +++ b/packaging/deb/control/postinst @@ -7,23 +7,24 @@ set -e IS_UPGRADE=false -case "$1" in - configure) - [ -z "$GRAFANA_USER" ] && GRAFANA_USER="grafana" - [ -z "$GRAFANA_GROUP" ] && GRAFANA_GROUP="grafana" - if ! getent group "$GRAFANA_GROUP" > /dev/null 2>&1 ; then - addgroup --system "$GRAFANA_GROUP" --quiet - fi - if ! id $GRAFANA_USER > /dev/null 2>&1 ; then - adduser --system --home /usr/share/grafana --no-create-home \ - --ingroup "$GRAFANA_GROUP" --disabled-password --shell /bin/false \ - "$GRAFANA_USER" - fi +# Initial installation: $1 == configure +# Upgrade: $1 == configure, and $2 not empty +if [ "$1" = configure ]; then + [ -z "$GRAFANA_USER" ] && GRAFANA_USER="grafana" + [ -z "$GRAFANA_GROUP" ] && GRAFANA_GROUP="grafana" + if ! getent group "$GRAFANA_GROUP" > /dev/null 2>&1 ; then + addgroup --system "$GRAFANA_GROUP" --quiet + fi + if ! id "$GRAFANA_USER" > /dev/null 2>&1 ; then + adduser --system --home /usr/share/grafana --no-create-home \ + --ingroup "$GRAFANA_GROUP" --disabled-password --shell /bin/false \ + "$GRAFANA_USER" + fi - # Set user permissions on /var/log/grafana, /var/lib/grafana - mkdir -p /var/log/grafana /var/lib/grafana - chown -R $GRAFANA_USER:$GRAFANA_GROUP /var/log/grafana /var/lib/grafana - chmod 755 /var/log/grafana /var/lib/grafana + # Set user permissions on /var/log/grafana, /var/lib/grafana + mkdir -p /var/log/grafana /var/lib/grafana + chown -R $GRAFANA_USER:$GRAFANA_GROUP /var/log/grafana /var/lib/grafana + chmod 755 /var/log/grafana /var/lib/grafana # copy user config files if [ ! -f $CONF_FILE ]; then @@ -97,6 +98,5 @@ case "$1" in fi echo " OK" - fi - ;; -esac + fi +fi diff --git a/packaging/deb/init.d/grafana-server b/packaging/deb/init.d/grafana-server index 5c1d9c8271a..840de5e9d62 100755 --- a/packaging/deb/init.d/grafana-server +++ b/packaging/deb/init.d/grafana-server @@ -36,7 +36,7 @@ CONF_FILE=$CONF_DIR/grafana.ini PROVISIONING_CFG_DIR=$CONF_DIR/provisioning MAX_OPEN_FILES=10000 PID_FILE=/var/run/$NAME.pid -DAEMON=/usr/sbin/$NAME +DAEMON=$GRAFANA_HOME/bin/grafana umask 0027 @@ -48,104 +48,102 @@ fi . /lib/lsb/init-functions if [ -r /etc/default/rcS ]; then - . /etc/default/rcS + . /etc/default/rcS fi # overwrite settings from default file if [ -f "$DEFAULT" ]; then - . "$DEFAULT" + . "$DEFAULT" fi -DAEMON_OPTS="--pidfile=${PID_FILE} --config=${CONF_FILE} --packaging=deb cfg:default.paths.provisioning=$PROVISIONING_CFG_DIR cfg:default.paths.data=${DATA_DIR} cfg:default.paths.logs=${LOG_DIR} cfg:default.paths.plugins=${PLUGINS_DIR}" +DAEMON_OPTS="server --pidfile=${PID_FILE} --config=${CONF_FILE} --packaging=deb cfg:default.paths.provisioning=$PROVISIONING_CFG_DIR cfg:default.paths.data=${DATA_DIR} cfg:default.paths.logs=${LOG_DIR} cfg:default.paths.plugins=${PLUGINS_DIR}" function checkUser() { if [ `id -u` -ne 0 ]; then - echo "You need root privileges to run this script" - exit 4 + echo "You need root privileges to run this script" + exit 4 fi } case "$1" in start) - checkUser - log_daemon_msg "Starting $DESC" + checkUser + log_daemon_msg "Starting $DESC" + pid=`pidofproc -p $PID_FILE grafana` + if [ -n "$pid" ] ; then + log_begin_msg "Already running." + log_end_msg 0 + exit 0 + fi - pid=`pidofproc -p $PID_FILE grafana` - if [ -n "$pid" ] ; then - log_begin_msg "Already running." - log_end_msg 0 - exit 0 - fi + # Prepare environment + mkdir -p "$LOG_DIR" "$DATA_DIR" && chown "$GRAFANA_USER":"$GRAFANA_GROUP" "$LOG_DIR" "$DATA_DIR" + touch "$PID_FILE" && chown "$GRAFANA_USER":"$GRAFANA_GROUP" "$PID_FILE" - # Prepare environment - mkdir -p "$LOG_DIR" "$DATA_DIR" && chown "$GRAFANA_USER":"$GRAFANA_GROUP" "$LOG_DIR" "$DATA_DIR" - touch "$PID_FILE" && chown "$GRAFANA_USER":"$GRAFANA_GROUP" "$PID_FILE" + if [ -n "$MAX_OPEN_FILES" ]; then + ulimit -n $MAX_OPEN_FILES + fi - if [ -n "$MAX_OPEN_FILES" ]; then - ulimit -n $MAX_OPEN_FILES - fi + # Start Daemon + start-stop-daemon --start -b --chdir "$WORK_DIR" --user "$GRAFANA_USER" -c "$GRAFANA_USER" --pidfile "$PID_FILE" --exec $DAEMON -- $DAEMON_OPTS + return=$? + if [ $return -eq 0 ] + then + sleep 1 + # check if pid file has been written to + if ! [[ -s $PID_FILE ]]; then + log_end_msg 1 + exit 1 + fi + i=0 + timeout=10 + # Wait for the process to be properly started before exiting + until { cat "$PID_FILE" | xargs kill -0; } >/dev/null 2>&1 + do + sleep 1 + i=$(($i + 1)) + if [ $i -gt $timeout ]; then + log_end_msg 1 + exit 1 + fi + done + fi - # Start Daemon - start-stop-daemon --start -b --chdir "$WORK_DIR" --user "$GRAFANA_USER" -c "$GRAFANA_USER" --pidfile "$PID_FILE" --exec $DAEMON -- $DAEMON_OPTS - return=$? - if [ $return -eq 0 ] - then - sleep 1 - - # check if pid file has been written to - if ! [[ -s $PID_FILE ]]; then - log_end_msg 1 - exit 1 - fi - - i=0 - timeout=10 - # Wait for the process to be properly started before exiting - until { cat "$PID_FILE" | xargs kill -0; } >/dev/null 2>&1 - do - sleep 1 - i=$(($i + 1)) - if [ $i -gt $timeout ]; then - log_end_msg 1 - exit 1 - fi - done - fi - log_end_msg $return - ;; + log_end_msg $return + ;; stop) - checkUser - log_daemon_msg "Stopping $DESC" + checkUser + log_daemon_msg "Stopping $DESC" - if [ -f "$PID_FILE" ]; then - start-stop-daemon --stop --pidfile "$PID_FILE" \ - --user "$GRAFANA_USER" \ - --retry=TERM/20/KILL/5 >/dev/null - if [ $? -eq 1 ]; then - log_progress_msg "$DESC is not running but pid file exists, cleaning up" - elif [ $? -eq 3 ]; then - PID="`cat $PID_FILE`" - log_failure_msg "Failed to stop $DESC (pid $PID)" - exit 1 - fi - rm -f "$PID_FILE" - else - log_progress_msg "(not running)" - fi - log_end_msg 0 - ;; + if [ -f "$PID_FILE" ]; then + start-stop-daemon --stop --pidfile "$PID_FILE" \ + --user "$GRAFANA_USER" \ + --retry=TERM/20/KILL/5 >/dev/null + if [ $? -eq 1 ]; then + log_progress_msg "$DESC is not running but pid file exists, cleaning up" + elif [ $? -eq 3 ]; then + PID="`cat $PID_FILE`" + log_failure_msg "Failed to stop $DESC (pid $PID)" + exit 1 + fi + rm -f "$PID_FILE" + else + log_progress_msg "(not running)" + fi + log_end_msg 0 + ;; status) - status_of_proc -p $PID_FILE grafana grafana && exit 0 || exit $? + status_of_proc -p $PID_FILE grafana grafana && exit 0 || exit $? ;; restart|force-reload) - if [ -f "$PID_FILE" ]; then - $0 stop - sleep 1 - fi - $0 start - ;; + if [ -f "$PID_FILE" ]; then + $0 stop + sleep 1 + fi + $0 start + ;; *) - log_success_msg "Usage: $0 {start|stop|restart|force-reload|status}" - exit 3 - ;; + log_success_msg "Usage: $0 {start|stop|restart|force-reload|status}" + exit 3 + ;; esac diff --git a/packaging/deb/systemd/grafana-server.service b/packaging/deb/systemd/grafana-server.service index 54be80c06bb..2d66ce889e3 100644 --- a/packaging/deb/systemd/grafana-server.service +++ b/packaging/deb/systemd/grafana-server.service @@ -14,15 +14,14 @@ Restart=on-failure WorkingDirectory=/usr/share/grafana RuntimeDirectory=grafana RuntimeDirectoryMode=0750 -ExecStart=/usr/sbin/grafana-server \ +ExecStart=/usr/share/grafana/bin/grafana server \ --config=${CONF_FILE} \ --pidfile=${PID_FILE_DIR}/grafana-server.pid \ --packaging=deb \ cfg:default.paths.logs=${LOG_DIR} \ cfg:default.paths.data=${DATA_DIR} \ cfg:default.paths.plugins=${PLUGINS_DIR} \ - cfg:default.paths.provisioning=${PROVISIONING_CFG_DIR} - + cfg:default.paths.provisioning=${PROVISIONING_CFG_DIR} LimitNOFILE=10000 TimeoutStopSec=20 diff --git a/packaging/docker/run.sh b/packaging/docker/run.sh index 30a70759616..ac1f9ea45a6 100755 --- a/packaging/docker/run.sh +++ b/packaging/docker/run.sh @@ -70,14 +70,14 @@ if [ ! -z "${GF_INSTALL_PLUGINS}" ]; then if [[ $plugin =~ .*\;.* ]]; then pluginUrl=$(echo "$plugin" | cut -d';' -f 1) pluginInstallFolder=$(echo "$plugin" | cut -d';' -f 2) - grafana-cli --pluginUrl ${pluginUrl} --pluginsDir "${GF_PATHS_PLUGINS}" plugins install "${pluginInstallFolder}" + grafana cli --pluginUrl ${pluginUrl} --pluginsDir "${GF_PATHS_PLUGINS}" plugins install "${pluginInstallFolder}" else - grafana-cli --pluginsDir "${GF_PATHS_PLUGINS}" plugins install ${plugin} + grafana cli --pluginsDir "${GF_PATHS_PLUGINS}" plugins install ${plugin} fi done fi -exec grafana-server \ +exec grafana server \ --homepath="$GF_PATHS_HOME" \ --config="$GF_PATHS_CONFIG" \ --packaging=docker \ diff --git a/packaging/mac/bin/grafana b/packaging/mac/bin/grafana index 74f4b00662b..a995c37d9cc 100755 --- a/packaging/mac/bin/grafana +++ b/packaging/mac/bin/grafana @@ -1,6 +1,6 @@ #!/usr/bin/env bash DAEMON=grafana-server -EXECUTABLE=/usr/local/bin/grafana-server +EXECUTABLE=/usr/share/grafana/bin/grafana CONFIG=/usr/local/etc/grafana/grafana.ini HOMEPATH=/usr/local/share/grafana LOGPATH=/usr/local/var/log/grafana @@ -11,7 +11,7 @@ DASHBOARDSCFGPATH=/usr/local/etc/grafana/dashboards case "$1" in start) - $EXECUTABLE --config=$CONFIG --homepath=$HOMEPATH cfg:default.paths.datasources=$DATASOURCECFGPATH cfg:default.paths.dashboards=$DASHBOARDSCFGPATH cfg:default.paths.logs=$LOGPATH cfg:default.paths.data=$DATAPATH cfg:default.paths.plugins=$PLUGINPATH 2> /dev/null & + $EXECUTABLE server --config=$CONFIG --homepath=$HOMEPATH cfg:default.paths.datasources=$DATASOURCECFGPATH cfg:default.paths.dashboards=$DASHBOARDSCFGPATH cfg:default.paths.logs=$LOGPATH cfg:default.paths.data=$DATAPATH cfg:default.paths.plugins=$PLUGINPATH 2> /dev/null & [ $? -eq 0 ] && echo "$DAEMON started" ;; stop) diff --git a/packaging/rpm/control/postinst b/packaging/rpm/control/postinst index bde2accb9bd..4ec126c3355 100755 --- a/packaging/rpm/control/postinst +++ b/packaging/rpm/control/postinst @@ -7,37 +7,42 @@ set -e startGrafana() { if [ -x /bin/systemctl ] ; then /bin/systemctl daemon-reload - /bin/systemctl start grafana-server.service - elif [ -x /etc/init.d/grafana-server ] ; then - /etc/init.d/grafana-server start - elif [ -x /etc/rc.d/init.d/grafana-server ] ; then - /etc/rc.d/init.d/grafana-server start - fi + /bin/systemctl start grafana-server.service + elif [ -x /etc/init.d/grafana-server ] ; then + /etc/init.d/grafana-server start + elif [ -x /etc/rc.d/init.d/grafana-server ] ; then + /etc/rc.d/init.d/grafana-server start + fi } stopGrafana() { - if [ -x /bin/systemctl ] ; then - /bin/systemctl stop grafana-server.service > /dev/null 2>&1 || : - elif [ -x /etc/init.d/grafana-service ] ; then - /etc/init.d/grafana-service stop - elif [ -x /etc/rc.d/init.d/grafana-service ] ; then - /etc/rc.d/init.d/grafana-service stop - fi + if [ -x /bin/systemctl ] ; then + /bin/systemctl stop grafana-server.service > /dev/null 2>&1 || : + elif [ -x /etc/init.d/grafana-service ] ; then + /etc/init.d/grafana-service stop + elif [ -x /etc/rc.d/init.d/grafana-service ] ; then + /etc/rc.d/init.d/grafana-service stop + fi } # Initial installation: $1 == 1 # Upgrade: $1 == 2, and configured to restart on upgrade if [ $1 -eq 1 ] ; then - [ -z "$GRAFANA_USER" ] && GRAFANA_USER="grafana" - [ -z "$GRAFANA_GROUP" ] && GRAFANA_GROUP="grafana" - if ! getent group "$GRAFANA_GROUP" > /dev/null 2>&1 ; then + [ -z "$GRAFANA_USER" ] && GRAFANA_USER="grafana" + [ -z "$GRAFANA_GROUP" ] && GRAFANA_GROUP="grafana" + if ! getent group "$GRAFANA_GROUP" > /dev/null 2>&1 ; then groupadd -r "$GRAFANA_GROUP" - fi - if ! getent passwd "$GRAFANA_USER" > /dev/null 2>&1 ; then - useradd -r -g grafana -d /usr/share/grafana -s /sbin/nologin \ - -c "grafana user" grafana - fi + fi + if ! getent passwd "$GRAFANA_USER" > /dev/null 2>&1 ; then + useradd -r -g "$GRAFANA_GROUP" -d /usr/share/grafana -s /sbin/nologin \ + -c "grafana user" "$GRAFANA_USER" + fi + + # Set user permissions on /var/log/grafana, /var/lib/grafana + mkdir -p /var/log/grafana /var/lib/grafana + chown -R $GRAFANA_USER:$GRAFANA_GROUP /var/log/grafana /var/lib/grafana + chmod 755 /var/log/grafana /var/lib/grafana # copy user config files if [ ! -f $CONF_FILE ]; then @@ -71,11 +76,6 @@ if [ $1 -eq 1 ] ; then cp /usr/share/grafana/conf/provisioning/alerting/sample.yaml $PROVISIONING_CFG_DIR/alerting/sample.yaml fi - # Set user permissions on /var/log/grafana, /var/lib/grafana - mkdir -p /var/log/grafana /var/lib/grafana - chown -R $GRAFANA_USER:$GRAFANA_GROUP /var/log/grafana /var/lib/grafana - chmod 755 /var/log/grafana /var/lib/grafana - # configuration files should not be modifiable by grafana user, as this can be a security issue chown -Rh root:$GRAFANA_GROUP /etc/grafana/* chmod 755 /etc/grafana diff --git a/packaging/rpm/init.d/grafana-server b/packaging/rpm/init.d/grafana-server index b7b41e58e8d..5803717eb30 100755 --- a/packaging/rpm/init.d/grafana-server +++ b/packaging/rpm/init.d/grafana-server @@ -22,6 +22,7 @@ PATH=/bin:/usr/bin:/sbin:/usr/sbin NAME=grafana-server DESC="Grafana Server" +DEFAULT=/etc/sysconfig/$NAME GRAFANA_USER=grafana GRAFANA_GROUP=grafana @@ -35,7 +36,7 @@ CONF_FILE=$CONF_DIR/grafana.ini PROVISIONING_CFG_DIR=$CONF_DIR/provisioning MAX_OPEN_FILES=10000 PID_FILE=/var/run/$NAME.pid -DAEMON=/usr/sbin/$NAME +DAEMON=$GRAFANA_HOME/bin/grafana if [ ! -x $DAEMON ]; then echo "Program not installed or not executable" @@ -58,9 +59,11 @@ if [ -f /etc/rc.d/init.d/functions ]; then fi # overwrite settings from default file -[ -e /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME +if [ -f "$DEFAULT" ]; then + . "$DEFAULT" +fi -DAEMON_OPTS="--pidfile=${PID_FILE} --config=${CONF_FILE} --packaging=rpm cfg:default.paths.provisioning=$PROVISIONING_CFG_DIR cfg:default.paths.data=${DATA_DIR} cfg:default.paths.logs=${LOG_DIR} cfg:default.paths.plugins=${PLUGINS_DIR}" +DAEMON_OPTS="server --pidfile=${PID_FILE} --config=${CONF_FILE} --packaging=rpm cfg:default.paths.provisioning=$PROVISIONING_CFG_DIR cfg:default.paths.data=${DATA_DIR} cfg:default.paths.logs=${LOG_DIR} cfg:default.paths.plugins=${PLUGINS_DIR}" function isRunning() { status -p $PID_FILE $NAME > /dev/null 2>&1 diff --git a/packaging/rpm/systemd/grafana-server.service b/packaging/rpm/systemd/grafana-server.service index e9af40136e4..9752271c6e4 100644 --- a/packaging/rpm/systemd/grafana-server.service +++ b/packaging/rpm/systemd/grafana-server.service @@ -14,14 +14,14 @@ Restart=on-failure WorkingDirectory=/usr/share/grafana RuntimeDirectory=grafana RuntimeDirectoryMode=0750 -ExecStart=/usr/sbin/grafana-server \ +ExecStart=/usr/share/grafana/bin/grafana server \ --config=${CONF_FILE} \ --pidfile=${PID_FILE_DIR}/grafana-server.pid \ --packaging=rpm \ cfg:default.paths.logs=${LOG_DIR} \ cfg:default.paths.data=${DATA_DIR} \ cfg:default.paths.plugins=${PLUGINS_DIR} \ - cfg:default.paths.provisioning=${PROVISIONING_CFG_DIR} + cfg:default.paths.provisioning=${PROVISIONING_CFG_DIR} LimitNOFILE=10000 TimeoutStopSec=20 diff --git a/packaging/wrappers/grafana b/packaging/wrappers/grafana new file mode 100755 index 00000000000..86e0fc9faa2 --- /dev/null +++ b/packaging/wrappers/grafana @@ -0,0 +1,49 @@ +#! /usr/bin/env bash + +# Wrapper for the grafana binary +# This file serves as a wrapper for the grafana binary. It ensures we set +# the system-wide Grafana configuration that was bundled with the package as we +# use the binary. + +DEFAULT=/etc/default/grafana + +GRAFANA_HOME="${GRAFANA_HOME:-/usr/share/grafana}" + +CONF_DIR=/etc/grafana +DATA_DIR=/var/lib/grafana +PLUGINS_DIR=/var/lib/grafana/plugins +LOG_DIR=/var/log/grafana + +CONF_FILE=$CONF_DIR/grafana.ini +PROVISIONING_CFG_DIR=$CONF_DIR/provisioning + +EXECUTABLE="$GRAFANA_HOME/bin/grafana" + +if [ ! -x $EXECUTABLE ]; then + echo "$EXECUTABLE not installed or not executable" + exit 5 +fi + +# overwrite settings from default file +if [ -f "$DEFAULT" ]; then + . "$DEFAULT" +fi + +OPTS="--homepath=${GRAFANA_HOME} \ + --config=${CONF_FILE} \ + --configOverrides='cfg:default.paths.provisioning=$PROVISIONING_CFG_DIR \ + cfg:default.paths.data=${DATA_DIR} \ + cfg:default.paths.logs=${LOG_DIR} \ + cfg:default.paths.plugins=${PLUGINS_DIR}'" + +CMD="${1:-}" +shift + +# special handling to pass --pluginsDir to cli +# can remove once it fully supports cfg:default.paths.plugins +if [ "$CMD" = cli ]; then + OPTS="$OPTS \ + --pluginsDir=${PLUGINS_DIR}" +fi + +eval $EXECUTABLE "$CMD" "$OPTS" "$@" diff --git a/packaging/wrappers/grafana-cli b/packaging/wrappers/grafana-cli index dafa075a2cf..7c6c46aef9e 100755 --- a/packaging/wrappers/grafana-cli +++ b/packaging/wrappers/grafana-cli @@ -1,13 +1,14 @@ #! /usr/bin/env bash -# Wrapper for the grafana-cli binary -# This file serves as a wrapper for the grafana-cli binary. It ensures we set +# Wrapper for the grafana binary +# This file serves as a wrapper for the grafana binary. It ensures we set # the system-wide Grafana configuration that was bundled with the package as we # use the binary. DEFAULT=/etc/default/grafana -GRAFANA_HOME=/usr/share/grafana +GRAFANA_HOME="${GRAFANA_HOME:-/usr/share/grafana}" + CONF_DIR=/etc/grafana DATA_DIR=/var/lib/grafana PLUGINS_DIR=/var/lib/grafana/plugins @@ -16,10 +17,10 @@ LOG_DIR=/var/log/grafana CONF_FILE=$CONF_DIR/grafana.ini PROVISIONING_CFG_DIR=$CONF_DIR/provisioning -EXECUTABLE=$GRAFANA_HOME/bin/grafana-cli +EXECUTABLE="$GRAFANA_HOME/bin/grafana" if [ ! -x $EXECUTABLE ]; then - echo "Program not installed or not executable" + echo "$EXECUTABLE not installed or not executable" exit 5 fi @@ -36,4 +37,6 @@ OPTS="--homepath=${GRAFANA_HOME} \ cfg:default.paths.logs=${LOG_DIR} \ cfg:default.paths.plugins=${PLUGINS_DIR}'" -eval $EXECUTABLE "$OPTS" '$@' +CMD=cli + +eval $EXECUTABLE "$CMD" "$OPTS" "$@" diff --git a/packaging/wrappers/grafana-server b/packaging/wrappers/grafana-server new file mode 100755 index 00000000000..d8a4fa930fc --- /dev/null +++ b/packaging/wrappers/grafana-server @@ -0,0 +1,41 @@ +#! /usr/bin/env bash + +# Wrapper for the grafana binary +# This file serves as a wrapper for the grafana binary. It ensures we set +# the system-wide Grafana configuration that was bundled with the package as we +# use the binary. + +DEFAULT=/etc/default/grafana + +GRAFANA_HOME="${GRAFANA_HOME:-/usr/share/grafana}" + +CONF_DIR=/etc/grafana +DATA_DIR=/var/lib/grafana +PLUGINS_DIR=/var/lib/grafana/plugins +LOG_DIR=/var/log/grafana + +CONF_FILE=$CONF_DIR/grafana.ini +PROVISIONING_CFG_DIR=$CONF_DIR/provisioning + +EXECUTABLE="$GRAFANA_HOME/bin/grafana" + +if [ ! -x $EXECUTABLE ]; then + echo "$EXECUTABLE not installed or not executable" + exit 5 +fi + +# overwrite settings from default file +if [ -f "$DEFAULT" ]; then + . "$DEFAULT" +fi + +OPTS="--homepath=${GRAFANA_HOME} \ + --config=${CONF_FILE} \ + --configOverrides='cfg:default.paths.provisioning=$PROVISIONING_CFG_DIR \ + cfg:default.paths.data=${DATA_DIR} \ + cfg:default.paths.logs=${LOG_DIR} \ + cfg:default.paths.plugins=${PLUGINS_DIR}'" + +CMD=server + +eval $EXECUTABLE "$CMD" "$OPTS" "$@" diff --git a/pkg/build/cmd.go b/pkg/build/cmd.go index bd0128d0cd4..2476caf2d42 100644 --- a/pkg/build/cmd.go +++ b/pkg/build/cmd.go @@ -17,11 +17,12 @@ const ( GoOSWindows = "windows" GoOSLinux = "linux" - ServerBinary = "grafana-server" - CLIBinary = "grafana-cli" + BackendBinary = "grafana" + ServerBinary = "grafana-server" + CLIBinary = "grafana-cli" ) -var binaries = []string{ServerBinary, CLIBinary} +var binaries = []string{BackendBinary, ServerBinary, CLIBinary} func logError(message string, err error) int { log.Println(message, err) @@ -64,6 +65,16 @@ func RunCmd() int { case "setup": setup(opts.goos) + case "build-backend": + if !opts.isDev { + clean(opts) + } + + if err := doBuild("grafana", "./pkg/cmd/grafana", opts); err != nil { + log.Println(err) + return 1 + } + case "build-srv", "build-server": if !opts.isDev { clean(opts) diff --git a/pkg/build/cmd/package.go b/pkg/build/cmd/package.go index 59beff98520..37b8d2e81fd 100644 --- a/pkg/build/cmd/package.go +++ b/pkg/build/cmd/package.go @@ -31,12 +31,9 @@ func Package(c *cli.Context) error { } cfg := config.Config{ - NumWorkers: c.Int("jobs"), + NumWorkers: c.Int("jobs"), + SignPackages: c.Bool("sign"), } - if err := gpg.LoadGPGKeys(&cfg); err != nil { - return cli.Exit(err, 1) - } - defer gpg.RemoveGPGFiles(cfg) ctx := context.Background() @@ -57,8 +54,14 @@ func Package(c *cli.Context) error { log.Printf("Packaging Grafana version %q, version mode %s, %s edition, variants %s", metadata.GrafanaVersion, releaseMode.Mode, edition, strings.Join(variantStrs, ",")) - if err := gpg.Import(cfg); err != nil { - return cli.Exit(err, 1) + if cfg.SignPackages { + if err := gpg.LoadGPGKeys(&cfg); err != nil { + return cli.Exit(err, 1) + } + defer gpg.RemoveGPGFiles(cfg) + if err := gpg.Import(cfg); err != nil { + return cli.Exit(err, 1) + } } p := syncutil.NewWorkerPool(cfg.NumWorkers) diff --git a/pkg/build/config/config.go b/pkg/build/config/config.go index 2f7dbe7358f..7c8b4b133bf 100644 --- a/pkg/build/config/config.go +++ b/pkg/build/config/config.go @@ -16,4 +16,5 @@ type Config struct { PullEnterprise bool NetworkConcurrency bool PackageVersion string + SignPackages bool } diff --git a/pkg/build/grafana/build.go b/pkg/build/grafana/build.go index b8fa022c396..4d661e8d851 100644 --- a/pkg/build/grafana/build.go +++ b/pkg/build/grafana/build.go @@ -12,7 +12,7 @@ import ( "github.com/grafana/grafana/pkg/build/golangutils" ) -var binaries = []string{"grafana-server", "grafana-cli"} +var binaries = []string{"grafana", "grafana-server", "grafana-cli"} const ( SuffixEnterprise2 = "-enterprise2" diff --git a/pkg/build/packaging/grafana.go b/pkg/build/packaging/grafana.go index 6d48dbb8b57..84e083c3844 100644 --- a/pkg/build/packaging/grafana.go +++ b/pkg/build/packaging/grafana.go @@ -75,9 +75,13 @@ func PackageGrafana( if err := packageGrafana(ctx, edition, version, grafanaDir, variants, shouldSign, p); err != nil { return err } - if err := signRPMPackages(edition, cfg, grafanaDir); err != nil { - return err + + if cfg.SignPackages { + if err := signRPMPackages(edition, cfg, grafanaDir); err != nil { + return err + } } + if err := checksumPackages(grafanaDir, edition); err != nil { return err } @@ -282,6 +286,7 @@ func shaFile(fpath string) error { // createPackage creates a Linux package. func createPackage(srcDir string, options linuxPackageOptions) error { + binary := "grafana" cliBinary := "grafana-cli" serverBinary := "grafana-server" @@ -310,10 +315,15 @@ func createPackage(srcDir string, options linuxPackageOptions) error { } } - if err := fs.CopyFile(options.cliBinaryWrapperSrc, filepath.Join(packageRoot, "usr", "sbin", cliBinary)); err != nil { + if err := fs.CopyFile(filepath.Join(options.wrapperFilePath, binary), + filepath.Join(packageRoot, "usr", "sbin", binary)); err != nil { return err } - if err := fs.CopyFile(filepath.Join(srcDir, "bin", serverBinary), + if err := fs.CopyFile(filepath.Join(options.wrapperFilePath, cliBinary), + filepath.Join(packageRoot, "usr", "sbin", cliBinary)); err != nil { + return err + } + if err := fs.CopyFile(filepath.Join(options.wrapperFilePath, serverBinary), filepath.Join(packageRoot, "usr", "sbin", serverBinary)); err != nil { return err } @@ -329,19 +339,17 @@ func createPackage(srcDir string, options linuxPackageOptions) error { if err := fs.CopyRecursive(srcDir, filepath.Join(packageRoot, options.homeDir)); err != nil { return err } - homeBinDir := filepath.Join(packageRoot, options.homeBinDir) - if err := os.RemoveAll(homeBinDir); err != nil { - return fmt.Errorf("failed to remove %q: %w", homeBinDir, err) - } - //nolint - if err := os.MkdirAll(homeBinDir, 0o755); err != nil { - return fmt.Errorf("failed to make directory %q: %w", homeBinDir, err) - } - // The grafana-cli binary is exposed through a wrapper to ensure a proper - // configuration is in place. To enable that, we need to store the original - // binary in a separate location to avoid conflicts. - if err := fs.CopyFile(filepath.Join(srcDir, "bin", cliBinary), filepath.Join(homeBinDir, cliBinary)); err != nil { - return err + + // remove unneeded binaries, these are exposed via wrappers that provide the needed configuration + for _, fileName := range []string{ + cliBinary, + cliBinary + ".md5", + serverBinary, + serverBinary + ".md5", + } { + if err := os.Remove(filepath.Join(packageRoot, options.homeBinDir, fileName)); err != nil { + return fmt.Errorf("failed to remove %q: %w", filepath.Join(options.homeBinDir, fileName), err) + } } if err := executeFPM(options, packageRoot, srcDir); err != nil { @@ -475,43 +483,6 @@ func copyBinaries(grafanaDir, tmpDir string, args grafana.BuildArgs, edition con return nil } -// copyScripts copies scripts from grafanaDir into tmpDir. -func copyScripts(grafanaDir, tmpDir string) error { - //nolint - if err := os.MkdirAll(filepath.Join(tmpDir, "scripts"), 0o755); err != nil { - return fmt.Errorf("failed to create dir %q: %w", filepath.Join(tmpDir, "scripts"), err) - } - scriptsDir := filepath.Join(grafanaDir, "scripts") - infos, err := os.ReadDir(scriptsDir) - if err != nil { - return fmt.Errorf("failed to list files in %q: %w", scriptsDir, err) - } - for _, file := range infos { - info, err := file.Info() - if err != nil { - return err - } - - if info.IsDir() { - continue - } - - if info.Mode()&os.ModeSymlink != 0 { - continue - } - - path := "" - - path = filepath.Join(scriptsDir, info.Name()) - - if err := fs.CopyFile(path, filepath.Join(tmpDir, "scripts", info.Name())); err != nil { - return fmt.Errorf("failed to copy %q to %q: %w", path, tmpDir, err) - } - } - - return nil -} - // copyConfFiles copies configuration files from grafanaDir into tmpDir. func copyConfFiles(grafanaDir, tmpDir string) error { //nolint:gosec @@ -717,9 +688,6 @@ func realPackageVariant(ctx context.Context, v config.Variant, edition config.Ed if err := copyBinaries(grafanaDir, tmpDir, args, edition); err != nil { return err } - if err := copyScripts(grafanaDir, tmpDir); err != nil { - return err - } if err := copyConfFiles(grafanaDir, tmpDir); err != nil { return err } @@ -773,7 +741,7 @@ func realPackageVariant(ctx context.Context, v config.Variant, edition config.Ed initdScriptSrc: filepath.Join(grafanaDir, "packaging", "deb", "init.d", "grafana-server"), defaultFileSrc: filepath.Join(grafanaDir, "packaging", "deb", "default", "grafana-server"), systemdFileSrc: filepath.Join(grafanaDir, "packaging", "deb", "systemd", "grafana-server.service"), - cliBinaryWrapperSrc: filepath.Join(grafanaDir, "packaging", "wrappers", "grafana-cli"), + wrapperFilePath: filepath.Join(grafanaDir, "packaging", "wrappers"), depends: []string{"adduser", "libfontconfig1"}, }); err != nil { return err @@ -807,7 +775,7 @@ func realPackageVariant(ctx context.Context, v config.Variant, edition config.Ed initdScriptSrc: filepath.Join(grafanaDir, "packaging", "rpm", "init.d", "grafana-server"), defaultFileSrc: filepath.Join(grafanaDir, "packaging", "rpm", "sysconfig", "grafana-server"), systemdFileSrc: filepath.Join(grafanaDir, "packaging", "rpm", "systemd", "grafana-server.service"), - cliBinaryWrapperSrc: filepath.Join(grafanaDir, "packaging", "wrappers", "grafana-cli"), + wrapperFilePath: filepath.Join(grafanaDir, "packaging", "wrappers"), // chkconfig is depended on since our systemd service wraps a SysV init script, and that requires chkconfig depends: []string{"/sbin/service", "chkconfig", "fontconfig", "freetype", "urw-fonts"}, }); err != nil { @@ -889,7 +857,7 @@ type linuxPackageOptions struct { initdScriptSrc string defaultFileSrc string systemdFileSrc string - cliBinaryWrapperSrc string + wrapperFilePath string depends []string } @@ -929,7 +897,7 @@ func createZip(srcDir, version, variantStr, sfx, grafanaDir string) error { return fmt.Errorf("failed to create %q: %w", fpath, err) } defer func() { - if err := tgt.Close(); err != nil { + if err := tgt.Close(); err != nil && !errors.Is(err, os.ErrClosed) { log.Println(err) } }() @@ -1045,7 +1013,7 @@ func createTarball(srcDir, version, variantStr, sfx, grafanaDir string) error { return fmt.Errorf("failed to create %q: %w", fpath, err) } defer func() { - if err := tgt.Close(); err != nil { + if err := tgt.Close(); err != nil && !errors.Is(err, os.ErrClosed) { log.Println(err) } }() diff --git a/pkg/cmd/grafana-cli/commands/cli.go b/pkg/cmd/grafana-cli/commands/cli.go index 17a2f1258d4..e9a77d97ce3 100644 --- a/pkg/cmd/grafana-cli/commands/cli.go +++ b/pkg/cmd/grafana-cli/commands/cli.go @@ -1,11 +1,9 @@ package commands import ( - "fmt" "os" "runtime" - "github.com/fatih/color" "github.com/grafana/grafana/pkg/cmd/grafana-cli/logger" "github.com/grafana/grafana/pkg/cmd/grafana-cli/services" "github.com/grafana/grafana/pkg/cmd/grafana-cli/utils" @@ -13,18 +11,10 @@ import ( ) // RunCLI is the entrypoint for the grafana-cli command. It returns the exit code for the grafana-cli program. -func RunCLI(version string) int { - setupLogging() - - app := &cli.App{ - Name: "Grafana CLI", - Authors: []*cli.Author{ - { - Name: "Grafana Project", - Email: "hello@grafana.com", - }, - }, - Version: version, +func CLICommand(version string) *cli.Command { + return &cli.Command{ + Name: "cli", + Usage: "run the grafana cli", Flags: []cli.Flag{ &cli.StringFlag{ Name: "pluginsDir", @@ -64,39 +54,19 @@ func RunCLI(version string) int { Name: "config", Usage: "Path to config file", }, + cli.VersionFlag, }, - Commands: Commands, - CommandNotFound: cmdNotFound, - } + Subcommands: Commands, + Before: func(c *cli.Context) error { + // backward-compatible handling for cli version flag + if c.Bool("version") { + cli.ShowVersion(c) + os.Exit(0) + } - app.Before = func(c *cli.Context) error { - services.Init(version, c.Bool("insecure"), c.Bool("debug")) - return nil - } - - if err := app.Run(os.Args); err != nil { - logger.Errorf("%s: %s %s\n", color.RedString("Error"), color.RedString("✗"), err) - return 1 - } - - return 0 -} - -func setupLogging() { - for _, f := range os.Args { - if f == "-d" || f == "--debug" || f == "-debug" { - logger.SetDebug(true) - } + logger.SetDebug(c.Bool("debug")) + services.Init(version, c.Bool("insecure"), c.Bool("debug")) + return nil + }, } } - -func cmdNotFound(c *cli.Context, command string) { - fmt.Printf( - "%s: '%s' is not a %s command. See '%s --help'.\n", - c.App.Name, - command, - c.App.Name, - os.Args[0], - ) - os.Exit(1) -} diff --git a/pkg/cmd/grafana-cli/commands/commands.go b/pkg/cmd/grafana-cli/commands/commands.go index 0b7ef9461d7..a4edda3aabe 100644 --- a/pkg/cmd/grafana-cli/commands/commands.go +++ b/pkg/cmd/grafana-cli/commands/commands.go @@ -78,7 +78,8 @@ func initCfg(cmd *utils.ContextCommandLine) (*setting.Cfg, error) { cfg, err := setting.NewCfgFromArgs(setting.CommandLineArgs{ Config: cmd.ConfigFile(), HomePath: cmd.HomePath(), - Args: append(configOptions, cmd.Args().Slice()...), // tailing arguments have precedence over the options string + // tailing arguments have precedence over the options string + Args: append(configOptions, cmd.Args().Slice()...), }) if err != nil { diff --git a/pkg/cmd/grafana-cli/main.go b/pkg/cmd/grafana-cli/main.go index 3f300aec687..af5636c210d 100644 --- a/pkg/cmd/grafana-cli/main.go +++ b/pkg/cmd/grafana-cli/main.go @@ -3,12 +3,9 @@ package main import ( "os" - "github.com/grafana/grafana/pkg/cmd/grafana-cli/commands" + "github.com/grafana/grafana/pkg/util/cmd" ) -// Version is overridden by build flags -var version = "main" - func main() { - os.Exit(commands.RunCLI(version)) + os.Exit(cmd.RunGrafanaCmd("cli")) } diff --git a/pkg/cmd/grafana-server/commands/cli.go b/pkg/cmd/grafana-server/commands/cli.go index ab1bf653e97..10ffd4668b2 100644 --- a/pkg/cmd/grafana-server/commands/cli.go +++ b/pkg/cmd/grafana-server/commands/cli.go @@ -13,6 +13,7 @@ import ( "runtime/debug" "runtime/trace" "strconv" + "strings" "syscall" "time" @@ -32,6 +33,7 @@ type ServerOptions struct { Commit string BuildBranch string BuildStamp string + Args []string } type exitWithCode struct { @@ -54,6 +56,8 @@ func RunServer(opt ServerOptions) int { pidFile = serverFs.String("pidfile", "", "path to pid file") packaging = serverFs.String("packaging", "unknown", "describes the way Grafana was installed") + configOverrides = serverFs.String("configOverrides", "", "Configuration options to override defaults as a string. e.g. cfg:default.paths.log=/dev/null") + v = serverFs.Bool("v", false, "prints current version and exits") vv = serverFs.Bool("vv", false, "prints current version, all dependencies and exits") profile = serverFs.Bool("profile", false, "Turn on pprof profiling") @@ -63,7 +67,7 @@ func RunServer(opt ServerOptions) int { tracingFile = serverFs.String("tracing-file", "trace.out", "Define tracing output file") ) - if err := serverFs.Parse(os.Args[1:]); err != nil { + if err := serverFs.Parse(opt.Args); err != nil { fmt.Fprintln(os.Stderr, err.Error()) return 1 } @@ -108,7 +112,7 @@ func RunServer(opt ServerOptions) int { }() } - if err := executeServer(*configFile, *homePath, *pidFile, *packaging, traceDiagnostics, opt); err != nil { + if err := executeServer(*configFile, *homePath, *pidFile, *packaging, *configOverrides, traceDiagnostics, opt); err != nil { code := 1 var ewc exitWithCode if errors.As(err, &ewc) { @@ -124,7 +128,7 @@ func RunServer(opt ServerOptions) int { return 0 } -func executeServer(configFile, homePath, pidFile, packaging string, traceDiagnostics *tracingDiagnostics, opt ServerOptions) error { +func executeServer(configFile, homePath, pidFile, packaging, configOverrides string, traceDiagnostics *tracingDiagnostics, opt ServerOptions) error { defer func() { if err := log.Close(); err != nil { fmt.Fprintf(os.Stderr, "Failed to close log: %s\n", err) @@ -185,11 +189,23 @@ func executeServer(configFile, homePath, pidFile, packaging string, traceDiagnos fmt.Println("Grafana server is running with elevated privileges. This is not recommended") } - s, err := server.Initialize(setting.CommandLineArgs{ - Config: configFile, HomePath: homePath, Args: serverFs.Args(), - }, server.Options{ - PidFile: pidFile, Version: opt.Version, Commit: opt.Commit, BuildBranch: opt.BuildBranch, - }, api.ServerOptions{}) + configOptions := strings.Split(configOverrides, " ") + + s, err := server.Initialize( + setting.CommandLineArgs{ + Config: configFile, + HomePath: homePath, + // tailing arguments have precedence over the options string + Args: append(configOptions, serverFs.Args()...), + }, + server.Options{ + PidFile: pidFile, + Version: opt.Version, + Commit: opt.Commit, + BuildBranch: opt.BuildBranch, + }, + api.ServerOptions{}, + ) if err != nil { fmt.Fprintf(os.Stderr, "Failed to start grafana. error: %s\n", err.Error()) return err diff --git a/pkg/cmd/grafana-server/main.go b/pkg/cmd/grafana-server/main.go index d615125d74d..8df5e6f7b26 100644 --- a/pkg/cmd/grafana-server/main.go +++ b/pkg/cmd/grafana-server/main.go @@ -3,20 +3,9 @@ package main import ( "os" - "github.com/grafana/grafana/pkg/cmd/grafana-server/commands" + "github.com/grafana/grafana/pkg/util/cmd" ) -// The following variables cannot be constants, since they can be overridden through the -X link flag -var version = "9.2.0" -var commit = "NA" -var buildBranch = "main" -var buildstamp string - func main() { - os.Exit(commands.RunServer(commands.ServerOptions{ - Version: version, - Commit: commit, - BuildBranch: buildBranch, - BuildStamp: buildstamp, - })) + os.Exit(cmd.RunGrafanaCmd("server")) } diff --git a/pkg/cmd/grafana/main.go b/pkg/cmd/grafana/main.go new file mode 100644 index 00000000000..291a0b1cc9f --- /dev/null +++ b/pkg/cmd/grafana/main.go @@ -0,0 +1,68 @@ +package main + +import ( + "fmt" + "os" + + "github.com/fatih/color" + gcli "github.com/grafana/grafana/pkg/cmd/grafana-cli/commands" + gsrv "github.com/grafana/grafana/pkg/cmd/grafana-server/commands" + "github.com/urfave/cli/v2" +) + +// The following variables cannot be constants, since they can be overridden through the -X link flag +var version = "9.2.0" +var commit = "NA" +var buildBranch = "main" +var buildstamp string + +func main() { + app := &cli.App{ + Name: "grafana", + Usage: "Grafana server and command line interface", + Authors: []*cli.Author{ + { + Name: "Grafana Project", + Email: "hello@grafana.com", + }, + }, + Version: version, + Commands: []*cli.Command{ + gcli.CLICommand(version), + { + Name: "server", + Usage: "server ", + Action: func(context *cli.Context) error { + os.Exit(gsrv.RunServer(gsrv.ServerOptions{ + Version: version, + Commit: commit, + BuildBranch: buildBranch, + BuildStamp: buildstamp, + Args: context.Args().Slice(), + })) + return nil + }, + SkipFlagParsing: true, + }, + }, + CommandNotFound: cmdNotFound, + } + + if err := app.Run(os.Args); err != nil { + fmt.Printf("%s: %s %s\n", color.RedString("Error"), color.RedString("✗"), err) + os.Exit(1) + } + + os.Exit(0) +} + +func cmdNotFound(c *cli.Context, command string) { + fmt.Printf( + "%s: '%s' is not a %s command. See '%s --help'.\n", + c.App.Name, + command, + c.App.Name, + os.Args[0], + ) + os.Exit(1) +} diff --git a/pkg/util/cmd/cmd.go b/pkg/util/cmd/cmd.go new file mode 100644 index 00000000000..b0ec8b07501 --- /dev/null +++ b/pkg/util/cmd/cmd.go @@ -0,0 +1,65 @@ +package cmd + +import ( + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "syscall" +) + +func RunGrafanaCmd(subCmd string) int { + curr, err := os.Executable() + if err != nil { + fmt.Println("Error locating executable:", err) + return 1 + } + + executable := "grafana" + if runtime.GOOS == "windows" { + executable += ".exe" + } + + binary := filepath.Join(filepath.Dir(filepath.Clean(curr)), executable) + if _, err := os.Stat(binary); err != nil { + binary, err = exec.LookPath(executable) + if err != nil { + fmt.Printf("Error locating %s: %s\n", executable, err) + return 1 + } + } + + // windows doesn't support syscall.Exec so we just run the main binary as a command + if runtime.GOOS == "windows" { + // bypassing gosec G204 because we need to build the command programmatically + // nolint:gosec + cmd := exec.Command(binary, append([]string{subCmd}, os.Args[1:]...)...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + cmd.Env = os.Environ() + err := cmd.Run() + if err == nil { + return 0 + } + var exitError *exec.ExitError + if errors.As(err, &exitError) { + return exitError.ExitCode() + } + return 1 + } + + args := append([]string{"grafana", subCmd}, os.Args[1:]...) + + // bypassing gosec G204 because we need to build the command programmatically + // nolint:gosec + execErr := syscall.Exec(binary, args, os.Environ()) + if execErr != nil { + fmt.Printf("Error running %s: %s\n", binary, execErr) + return 1 + } + + return 0 +}