diff --git a/CHANGELOG.md b/CHANGELOG.md index 37b204161473..67478fd28990 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,10 @@ Main (unreleased) - Agent Management: Introduce support for templated configuration. (@jcreixell) +### Enhancements + +- Flow Windows service: Support environment variables. (@jkroepke) + ### Bugfixes - Permit `X-Faro-Session-ID` header in CORS requests for the `faro.receiver` diff --git a/cmd/grafana-agent-service/config_windows.go b/cmd/grafana-agent-service/config_windows.go index 961e95ad5e51..e4d712585986 100644 --- a/cmd/grafana-agent-service/config_windows.go +++ b/cmd/grafana-agent-service/config_windows.go @@ -16,6 +16,11 @@ type config struct { // not included. Args []string + // Environment holds environment variables for the Grafana Agent service. + // Each item represents an environment variable in form "key=value". + // All environments variables from the current process with be merged into Environment + Environment []string + // WorkingDirectory points to the working directory to run the Grafana Agent // binary from. WorkingDirectory string @@ -42,9 +47,15 @@ func loadConfig() (*config, error) { return nil, fmt.Errorf("failed to retrieve key Arguments: %w", err) } + env, _, err := agentKey.GetStringsValue("Environment") + if err != nil { + return nil, fmt.Errorf("failed to retrieve key Environment: %w", err) + } + return &config{ ServicePath: servicePath, Args: args, + Environment: env, WorkingDirectory: filepath.Dir(servicePath), }, nil } diff --git a/cmd/grafana-agent-service/main_windows.go b/cmd/grafana-agent-service/main_windows.go index f3660f73e7cc..a94a71228516 100644 --- a/cmd/grafana-agent-service/main_windows.go +++ b/cmd/grafana-agent-service/main_windows.go @@ -29,9 +29,10 @@ func main() { } cfg := serviceManagerConfig{ - Path: managerConfig.ServicePath, - Args: managerConfig.Args, - Dir: managerConfig.WorkingDirectory, + Path: managerConfig.ServicePath, + Args: managerConfig.Args, + Environment: managerConfig.Environment, + Dir: managerConfig.WorkingDirectory, // Send logs directly to the event logger. Stdout: logger, diff --git a/cmd/grafana-agent-service/service.go b/cmd/grafana-agent-service/service.go index 10060e19cc28..c302a98a1a36 100644 --- a/cmd/grafana-agent-service/service.go +++ b/cmd/grafana-agent-service/service.go @@ -24,6 +24,9 @@ type serviceManagerConfig struct { // Args of the binary to run, not including the command itself. Args []string + // Environment of the binary to run, including the command environment itself. + Environment []string + // Dir specifies the working directory to run the binary from. If Dir is // empty, the working directory of the current process is used. Dir string @@ -84,5 +87,7 @@ func (svc *serviceManager) buildCommand(ctx context.Context) *exec.Cmd { cmd.Dir = svc.cfg.Dir cmd.Stdout = svc.cfg.Stdout cmd.Stderr = svc.cfg.Stderr + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, svc.cfg.Environment...) return cmd } diff --git a/cmd/grafana-agent-service/service_test.go b/cmd/grafana-agent-service/service_test.go index d6389132071b..a1d3124b034c 100644 --- a/cmd/grafana-agent-service/service_test.go +++ b/cmd/grafana-agent-service/service_test.go @@ -30,8 +30,9 @@ func Test_serviceManager(t *testing.T) { listenHost := getListenHost(t) mgr := newServiceManager(l, serviceManagerConfig{ - Path: serviceBinary, - Args: []string{"-listen-addr", listenHost}, + Path: serviceBinary, + Args: []string{"-listen-addr", listenHost}, + Environment: []string{"LISTEN=" + listenHost}, }) go mgr.Run(componenttest.TestContext(t)) @@ -40,6 +41,12 @@ func Test_serviceManager(t *testing.T) { require.NoError(t, err) require.Equal(t, []byte("Hello, world!"), resp) }) + + util.Eventually(t, func(t require.TestingT) { + resp, err := makeServiceRequest(listenHost, "/echo/env", nil) + require.NoError(t, err) + require.Contains(t, string(resp), "LISTEN="+listenHost) + }) }) t.Run("terminates service binary", func(t *testing.T) { diff --git a/cmd/grafana-agent-service/testdata/example_service.go b/cmd/grafana-agent-service/testdata/example_service.go index ff11e61dfd85..64808b1b8608 100644 --- a/cmd/grafana-agent-service/testdata/example_service.go +++ b/cmd/grafana-agent-service/testdata/example_service.go @@ -7,6 +7,7 @@ import ( "net" "net/http" "os" + "strings" ) func main() { @@ -46,6 +47,9 @@ func run() error { mux.HandleFunc("/echo/response", func(w http.ResponseWriter, r *http.Request) { _, _ = io.Copy(w, r.Body) }) + mux.HandleFunc("/echo/env", func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write([]byte(strings.Join(os.Environ(), "\n"))) + }) srv := &http.Server{Handler: mux} _ = srv.Serve(lis) diff --git a/docs/sources/flow/setup/install/windows.md b/docs/sources/flow/setup/install/windows.md index 84b0607e139b..f9106e5f936f 100644 --- a/docs/sources/flow/setup/install/windows.md +++ b/docs/sources/flow/setup/install/windows.md @@ -57,6 +57,14 @@ To do a silent install of Grafana Agent on Windows, perform the following steps. * `/CONFIG=` Path to the configuration file. Default: `$INSTDIR\config.river` * `/DISABLEREPORTING=` Disable [data collection][]. Default: `no` * `/DISABLEPROFILING=` Disable profiling endpoint. Default: `no` +* `/ENVIRONMENT="KEY=VALUE\0KEY2=VALUE2"` Define environment variables for Windows Service. Default: `` + +## Service Configuration + +Grafana Agent uses the Windows Registry `HKLM\Software\Grafana\Grafana Agent Flow` for service configuration. + +* `Arguments` (Type `REG_MULTI_SZ`) Each value represents a binary argument for grafana-agent-flow binary. +* `Environment` (Type `REG_MULTI_SZ`) Each value represents a environment value `KEY=VALUE` for grafana-agent-flow binary. ## Uninstall diff --git a/packaging/grafana-agent-flow/windows/install_script.nsis b/packaging/grafana-agent-flow/windows/install_script.nsis index 19f3fc6cd668..011e6b59c121 100644 --- a/packaging/grafana-agent-flow/windows/install_script.nsis +++ b/packaging/grafana-agent-flow/windows/install_script.nsis @@ -31,6 +31,7 @@ OutFile "${OUT}" Var PassedInParameters Var Config Var ConfigFlag +Var Environment Var DisableReporting Var DisableReportingFlag Var DisableProfiling @@ -51,6 +52,7 @@ Section "install" ${GetParameters} $PassedInParameters ${GetOptions} $PassedInParameters "/DISABLEPROFILING=" $DisableProfiling ${GetOptions} $PassedInParameters "/DISABLEREPORTING=" $DisableReporting + ${GetOptions} $PassedInParameters "/ENVIRONMENT=" $Environment ${GetOptions} $PassedInParameters "/CONFIG=" $Config # Calls to functions like nsExec::ExecToLog below push the exit code to the @@ -146,6 +148,11 @@ Function InitializeRegistry Pop $0 # Ignore return result ${EndIf} + # Define the environment key, which holds environment variables to pass to the + # service. + nsExec::ExecToLog 'Reg.exe add "${REGKEY}" /reg:64 /v Environment /t REG_MULTI_SZ /d "$Environment"' + Pop $0 # Ignore return result + Return FunctionEnd