From 2fb45eeec898e0c8fa4ab088a6dc843420af8e2d Mon Sep 17 00:00:00 2001 From: gotjosh Date: Mon, 24 Jun 2019 20:20:21 +0100 Subject: [PATCH] Grafana-CLI: Wrapper for `grafana-cli` within RPM/DEB packages and config/homepath are now global flags (#17695) * Feature: Introduce a grafana-cli wrapper When our users install the *nix packed version of grafana, tendency is to use the services and scripts installed as part of the package for grafana-server. These leverage the default configuration options by specifying the several default paths. This introduces a similar approach for the grafana-cli binary. We exposed it through a wrapper to ensure a proper configuration is in place. To enable that, we add the .real suffix to the original binary (grafana-cli.real) and then use a bash script named grafana-cli as the wrapper. * Make the config and homepath flags global * Introduce `configOverrides` as a global flag This flag allows us to pass configuration overrides as a string. The string follows the convention of configuration arguments separated by a space e.g. "cfg:default.paths.data=/dev/nullX cfg:default.paths.logs=/dev/nullX" Also, it is backwards compatible with similar the previous configuration method through tailing arguments. Tailing arguments take presedence over the configuration options string. * Only log configuration information in debug mode * Move the grafana-cli binary to $GRAFANA_HOME/bin As part of the package install process, we copy all the release files and directories into the grafana home directory. This includes the /bin folder from where we copied the binaries into their respective destinations. After that, the /bin folder gets deleted as we don't want to keep duplicates of the binaries around. As part of this commit, we moved the re-creation of /bin within grafana-home and the copy of the original binary (again) after the folder gets deleted. --- build.go | 53 +++++++++++++++-------- docs/sources/administration/cli.md | 2 +- packaging/wrappers/grafana-cli | 39 +++++++++++++++++ pkg/cmd/grafana-cli/commands/commands.go | 26 ++++------- pkg/cmd/grafana-cli/main.go | 12 +++++ pkg/cmd/grafana-cli/utils/command_line.go | 8 ++++ 6 files changed, 104 insertions(+), 36 deletions(-) create mode 100755 packaging/wrappers/grafana-cli diff --git a/build.go b/build.go index 41441e9d892..6cfa79ea750 100644 --- a/build.go +++ b/build.go @@ -43,7 +43,9 @@ var ( workingDir string includeBuildId bool = true buildId string = "0" - binaries []string = []string{"grafana-server", "grafana-cli"} + serverBinary string = "grafana-server" + cliBinary string = "grafana-cli" + binaries []string = []string{serverBinary, cliBinary} isDev bool = false enterprise bool = false skipRpmGen bool = false @@ -230,6 +232,7 @@ type linuxPackageOptions struct { packageType string packageArch string homeDir string + homeBinDir string binPath string serverBinPath string cliBinPath string @@ -240,10 +243,11 @@ type linuxPackageOptions struct { initdScriptFilePath string systemdServiceFilePath string - postinstSrc string - initdScriptSrc string - defaultFileSrc string - systemdFileSrc string + postinstSrc string + initdScriptSrc string + defaultFileSrc string + systemdFileSrc string + cliBinaryWrapperSrc string depends []string } @@ -258,6 +262,7 @@ func createDebPackages() { packageType: "deb", packageArch: debPkgArch, homeDir: "/usr/share/grafana", + homeBinDir: "/usr/share/grafana/bin", binPath: "/usr/sbin", configDir: "/etc/grafana", etcDefaultPath: "/etc/default", @@ -265,10 +270,11 @@ func createDebPackages() { initdScriptFilePath: "/etc/init.d/grafana-server", systemdServiceFilePath: "/usr/lib/systemd/system/grafana-server.service", - postinstSrc: "packaging/deb/control/postinst", - initdScriptSrc: "packaging/deb/init.d/grafana-server", - defaultFileSrc: "packaging/deb/default/grafana-server", - systemdFileSrc: "packaging/deb/systemd/grafana-server.service", + postinstSrc: "packaging/deb/control/postinst", + initdScriptSrc: "packaging/deb/init.d/grafana-server", + defaultFileSrc: "packaging/deb/default/grafana-server", + systemdFileSrc: "packaging/deb/systemd/grafana-server.service", + cliBinaryWrapperSrc: "packaging/wrappers/grafana-cli", depends: []string{"adduser", "libfontconfig1"}, }) @@ -286,6 +292,7 @@ func createRpmPackages() { packageType: "rpm", packageArch: rpmPkgArch, homeDir: "/usr/share/grafana", + homeBinDir: "/usr/share/grafana/bin", binPath: "/usr/sbin", configDir: "/etc/grafana", etcDefaultPath: "/etc/sysconfig", @@ -293,10 +300,11 @@ func createRpmPackages() { initdScriptFilePath: "/etc/init.d/grafana-server", systemdServiceFilePath: "/usr/lib/systemd/system/grafana-server.service", - postinstSrc: "packaging/rpm/control/postinst", - initdScriptSrc: "packaging/rpm/init.d/grafana-server", - defaultFileSrc: "packaging/rpm/sysconfig/grafana-server", - systemdFileSrc: "packaging/rpm/systemd/grafana-server.service", + postinstSrc: "packaging/rpm/control/postinst", + initdScriptSrc: "packaging/rpm/init.d/grafana-server", + defaultFileSrc: "packaging/rpm/sysconfig/grafana-server", + systemdFileSrc: "packaging/rpm/systemd/grafana-server.service", + cliBinaryWrapperSrc: "packaging/wrappers/grafana-cli", depends: []string{"/sbin/service", "fontconfig", "freetype", "urw-fonts"}, }) @@ -323,10 +331,12 @@ func createPackage(options linuxPackageOptions) { runPrint("mkdir", "-p", filepath.Join(packageRoot, "/usr/lib/systemd/system")) runPrint("mkdir", "-p", filepath.Join(packageRoot, "/usr/sbin")) - // copy binary - for _, binary := range binaries { - runPrint("cp", "-p", filepath.Join(workingDir, "tmp/bin/"+binary), filepath.Join(packageRoot, "/usr/sbin/"+binary)) - } + // copy grafana-cli wrapper + runPrint("cp", "-p", options.cliBinaryWrapperSrc, filepath.Join(packageRoot, "/usr/sbin/"+cliBinary)) + + // copy grafana-server binary + runPrint("cp", "-p", filepath.Join(workingDir, "tmp/bin/"+serverBinary), filepath.Join(packageRoot, "/usr/sbin/"+serverBinary)) + // copy init.d script runPrint("cp", "-p", options.initdScriptSrc, filepath.Join(packageRoot, options.initdScriptFilePath)) // copy environment var file @@ -338,6 +348,13 @@ func createPackage(options linuxPackageOptions) { // remove bin path runPrint("rm", "-rf", filepath.Join(packageRoot, options.homeDir, "bin")) + // create /bin within home + runPrint("mkdir", "-p", filepath.Join(packageRoot, options.homeBinDir)) + // 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. + runPrint("cp", "-p", filepath.Join(workingDir, "tmp/bin/"+cliBinary), filepath.Join(packageRoot, options.homeBinDir, cliBinary)) + args := []string{ "-s", "dir", "--description", "Grafana", @@ -391,7 +408,7 @@ func createPackage(options linuxPackageOptions) { args = append(args, "--iteration", linuxPackageIteration) } - // add dependenciesj + // add dependencies for _, dep := range options.depends { args = append(args, "--depends", dep) } diff --git a/docs/sources/administration/cli.md b/docs/sources/administration/cli.md index 998627ca25f..74943c9dc22 100644 --- a/docs/sources/administration/cli.md +++ b/docs/sources/administration/cli.md @@ -37,7 +37,7 @@ If running the command returns this error: then there are two flags that can be used to set homepath and the config file path. -`grafana-cli admin reset-admin-password --homepath "/usr/share/grafana" newpass` +`grafana-cli --homepath "/usr/share/grafana" admin reset-admin-password newpass` If you have not lost the admin password then it is better to set in the Grafana UI. If you need to set the password in a script then the [Grafana API](http://docs.grafana.org/http_api/user/#change-password) can be used. Here is an example using curl with basic auth: diff --git a/packaging/wrappers/grafana-cli b/packaging/wrappers/grafana-cli new file mode 100755 index 00000000000..9cad151c0d7 --- /dev/null +++ b/packaging/wrappers/grafana-cli @@ -0,0 +1,39 @@ +#! /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 +# 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 +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-cli + +if [ ! -x $EXECUTABLE ]; then + echo "Program 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} \ + --pluginsDir=${PLUGINS_DIR} \ + --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}'" + +eval $EXECUTABLE "$OPTS" "$@" diff --git a/pkg/cmd/grafana-cli/commands/commands.go b/pkg/cmd/grafana-cli/commands/commands.go index b5a47ecb765..4c20cd4f0c3 100644 --- a/pkg/cmd/grafana-cli/commands/commands.go +++ b/pkg/cmd/grafana-cli/commands/commands.go @@ -2,6 +2,7 @@ package commands import ( "os" + "strings" "github.com/codegangsta/cli" "github.com/fatih/color" @@ -16,16 +17,20 @@ import ( func runDbCommand(command func(commandLine utils.CommandLine, sqlStore *sqlstore.SqlStore) error) func(context *cli.Context) { return func(context *cli.Context) { cmd := &utils.ContextCommandLine{Context: context} + debug := cmd.GlobalBool("debug") cfg := setting.NewCfg() + configOptions := strings.Split(cmd.GlobalString("configOverrides"), " ") cfg.Load(&setting.CommandLineArgs{ - Config: cmd.String("config"), - HomePath: cmd.String("homepath"), - Args: context.Args(), + Config: cmd.ConfigFile(), + HomePath: cmd.HomePath(), + Args: append(configOptions, cmd.Args()...), // tailing arguments have precedence over the options string }) - cfg.LogConfigSources() + if debug { + cfg.LogConfigSources() + } engine := &sqlstore.SqlStore{} engine.Cfg = cfg @@ -95,23 +100,11 @@ var pluginCommands = []cli.Command{ }, } -var dbCommandFlags = []cli.Flag{ - cli.StringFlag{ - Name: "homepath", - Usage: "path to grafana install/home path, defaults to working directory", - }, - cli.StringFlag{ - Name: "config", - Usage: "path to config file", - }, -} - var adminCommands = []cli.Command{ { Name: "reset-admin-password", Usage: "reset-admin-password ", Action: runDbCommand(resetPasswordCommand), - Flags: dbCommandFlags, }, { Name: "data-migration", @@ -121,7 +114,6 @@ var adminCommands = []cli.Command{ Name: "encrypt-datasource-passwords", Usage: "Migrates passwords from unsecured fields to secure_json_data field. Return ok unless there is an error. Safe to execute multiple times.", Action: runDbCommand(datamigrations.EncryptDatasourcePaswords), - Flags: dbCommandFlags, }, }, }, diff --git a/pkg/cmd/grafana-cli/main.go b/pkg/cmd/grafana-cli/main.go index 016acde802a..4c4039c071f 100644 --- a/pkg/cmd/grafana-cli/main.go +++ b/pkg/cmd/grafana-cli/main.go @@ -51,6 +51,18 @@ func main() { Name: "debug, d", Usage: "enable debug logging", }, + cli.StringFlag{ + Name: "configOverrides", + Usage: "configuration options to override defaults as a string. e.g. cfg:default.paths.log=/dev/null", + }, + cli.StringFlag{ + Name: "homepath", + Usage: "path to grafana install/home path, defaults to working directory", + }, + cli.StringFlag{ + Name: "config", + Usage: "path to config file", + }, } app.Before = func(c *cli.Context) error { diff --git a/pkg/cmd/grafana-cli/utils/command_line.go b/pkg/cmd/grafana-cli/utils/command_line.go index d3142d0f195..15546f2f392 100644 --- a/pkg/cmd/grafana-cli/utils/command_line.go +++ b/pkg/cmd/grafana-cli/utils/command_line.go @@ -38,6 +38,10 @@ func (c *ContextCommandLine) Application() *cli.App { return c.App } +func (c *ContextCommandLine) HomePath() string { return c.GlobalString("homepath") } + +func (c *ContextCommandLine) ConfigFile() string { return c.GlobalString("config") } + func (c *ContextCommandLine) PluginDirectory() string { return c.GlobalString("pluginsDir") } @@ -49,3 +53,7 @@ func (c *ContextCommandLine) RepoDirectory() string { func (c *ContextCommandLine) PluginURL() string { return c.GlobalString("pluginUrl") } + +func (c *ContextCommandLine) OptionsString() string { + return c.GlobalString("configOverrides") +}