Skip to content

Commit

Permalink
feat: Add support for systemd notify (#1954)
Browse files Browse the repository at this point in the history
- Integrated systemd notification support using github.com/coreos/go-systemd/v22/daemon.
- Updated manager/runner.go to signal readiness to systemd.
- Modified .golangci.yml to include github.com/coreos/go-systemd in linters-settings.
- Updated dependencies in go.mod and go.sum for github.com/coreos/go-systemd/v22 v22.5.0.
  • Loading branch information
dyudin0821 authored Jun 27, 2024
1 parent 9e7e00e commit f8fbb6e
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 0 deletions.
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ linters-settings:
- github.com/pkg/errors
- github.com/stretchr/testify/assert
- github.com/stretchr/testify/require
- github.com/coreos/go-systemd

run:
timeout: 10m
Expand Down
31 changes: 31 additions & 0 deletions cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package main

import (
"context"
"flag"
"fmt"
"io"
Expand All @@ -13,6 +14,7 @@ import (
"sync"
"time"

"github.com/coreos/go-systemd/v22/daemon"
"github.com/hashicorp/consul-template/config"
"github.com/hashicorp/consul-template/logging"
"github.com/hashicorp/consul-template/manager"
Expand Down Expand Up @@ -105,11 +107,38 @@ func (cli *CLI) Run(args []string) int {
return ExitCodeOK
}

// Create a context with cancellation
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// Create a channel for signaling readiness to the systemd init system.
readyCh := make(chan struct{})

// Start a goroutine that listens for signals on the readyCh channel.
// When a signal (an empty struct) is received, it notifies systemd that
// the application is ready by calling daemon.SdNotify with SdNotifyReady.
// If an error occurs during the notification, it logs a warning message.
go func() {
for {
select {
case <-readyCh:
_, err := daemon.SdNotify(false, daemon.SdNotifyReady)
if err != nil {
log.Printf("[WARN] failed to signal readiness to systemd: %v", err)
}
case <-ctx.Done():
return
}
}
}()

// Initial runner
runner, err := manager.NewRunner(config, dry)
if err != nil {
return logError(err, ExitCodeRunnerError)
}

runner.SetReadyChannel(readyCh)
go runner.Start()

// Listen for monitored signals
Expand Down Expand Up @@ -137,6 +166,7 @@ func (cli *CLI) Run(args []string) int {
case <-service_os.Shutdown_Channel():
fmt.Fprintf(cli.errStream, "Cleaning up...\n")
runner.StopImmediately()
cancel() // Cancel the context to stop the readyCh goroutine
return ExitCodeInterrupt
case s := <-cli.signalCh:
log.Printf("[DEBUG] (cli) receiving signal %q", s)
Expand All @@ -163,6 +193,7 @@ func (cli *CLI) Run(args []string) int {
if err != nil {
return logError(err, ExitCodeRunnerError)
}
runner.SetReadyChannel(readyCh)
go runner.Start()
case *config.KillSignal:
fmt.Fprintf(cli.errStream, "Cleaning up...\n")
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ require (
github.com/Masterminds/semver/v3 v3.2.0 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
github.com/coreos/go-systemd/v22 v22.5.0
github.com/fatih/color v1.17.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
github.com/google/uuid v1.3.0 // indirect
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4r
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
Expand All @@ -52,6 +54,7 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
Expand Down
21 changes: 21 additions & 0 deletions manager/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@ type Runner struct {
// Runner config. This prevents risk of data races when reading config for
// other elements started by the Runner, like template functions.
finalConfigCopy config.Config

// readyCh is a channel used to signal readiness to the systemd init system.
// When a struct{} is sent on this channel, it triggers a notification to systemd
// that the application is ready. This helps in managing application state and
// integration with systemd's readiness protocol.
readyCh chan struct{}
}

// RenderEvent captures the time and events that occurred for a template
Expand Down Expand Up @@ -309,6 +315,14 @@ func (r *Runner) Start() {

if r.allTemplatesRendered() {
log.Printf("[DEBUG] (runner) all templates rendered")

// If the readyCh channel is not nil, send an empty struct to signal readiness.
// This will notify the systemd init system that the application is ready to
// handle requests. This mechanism integrates with systemd's readiness protocol.
if r.readyCh != nil {
r.readyCh <- struct{}{}
}

// Enable quiescence for all templates if we have specified wait
// intervals.
NEXT_Q:
Expand Down Expand Up @@ -722,6 +736,13 @@ func (r *Runner) Run() error {
return nil
}

// SetReadyChannel sets the readyCh channel which is used to signal readiness to the systemd init system.
// The channel should be a struct{} channel, and when an empty struct is sent on this channel,
// it will trigger a notification to systemd that the application is ready.
func (r *Runner) SetReadyChannel(ch chan struct{}) {
r.readyCh = ch
}

type templateRunCtx struct {
// commands is the set of commands that will be executed after all templates
// have run. When adding to the commands, care should be taken not to
Expand Down

0 comments on commit f8fbb6e

Please sign in to comment.