Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flow Windows service: Support environment variables #5762

Merged
merged 15 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
11 changes: 11 additions & 0 deletions cmd/grafana-agent-service/config_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -42,9 +47,15 @@ func loadConfig() (*config, error) {
return nil, fmt.Errorf("failed to retrieve key Arguments: %w", err)
}

env, _, err := agentKey.GetStringsValue("Environment")
jkroepke marked this conversation as resolved.
Show resolved Hide resolved
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
}
7 changes: 4 additions & 3 deletions cmd/grafana-agent-service/main_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions cmd/grafana-agent-service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
11 changes: 9 additions & 2 deletions cmd/grafana-agent-service/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand All @@ -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) {
Expand Down
4 changes: 4 additions & 0 deletions cmd/grafana-agent-service/testdata/example_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net"
"net/http"
"os"
"strings"
)

func main() {
Expand Down Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions docs/sources/flow/setup/install/windows.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ To do a silent install of Grafana Agent on Windows, perform the following steps.
* `/CONFIG=<path>` Path to the configuration file. Default: `$INSTDIR\config.river`
* `/DISABLEREPORTING=<yes|no>` Disable [data collection][]. Default: `no`
* `/DISABLEPROFILING=<yes|no>` 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.
mattdurham marked this conversation as resolved.
Show resolved Hide resolved
* `Environment` (Type `REG_MULTI_SZ`) Each value represents a environment value `KEY=VALUE` for grafana-agent-flow binary.
Comment on lines +66 to +67
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think people might find it useful if we include a regedit printout of an example configuration. I don't insist on it, will leave it up to you to decide.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean the output of reg query ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess so? I've only used reg export and I suppose reg query does a similar job. As long as people can see the values in a format they can recognise, it's ok.


## Uninstall

Expand Down
7 changes: 7 additions & 0 deletions packaging/grafana-agent-flow/windows/install_script.nsis
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ OutFile "${OUT}"
Var PassedInParameters
Var Config
Var ConfigFlag
Var Environment
Var DisableReporting
Var DisableReportingFlag
Var DisableProfiling
Expand All @@ -51,6 +52,7 @@ Section "install"
${GetParameters} $PassedInParameters
${GetOptions} $PassedInParameters "/DISABLEPROFILING=" $DisableProfiling
${GetOptions} $PassedInParameters "/DISABLEREPORTING=" $DisableReporting
${GetOptions} $PassedInParameters "/ENVIRONMENT=" $Environment
jkroepke marked this conversation as resolved.
Show resolved Hide resolved
${GetOptions} $PassedInParameters "/CONFIG=" $Config

# Calls to functions like nsExec::ExecToLog below push the exit code to the
Expand Down Expand Up @@ -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

Expand Down