Skip to content

Commit

Permalink
Implement systemd notification support after all templates have been …
Browse files Browse the repository at this point in the history
…rendered (#1794)

* Reopening PR, request to add feature systemd notify on runner

* Change the directory structure
  • Loading branch information
dyudin0821 authored Nov 9, 2023
1 parent 2d2654f commit 6b9060e
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 0 deletions.
21 changes: 21 additions & 0 deletions manager/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/hashicorp/consul-template/config"
dep "github.com/hashicorp/consul-template/dependency"
"github.com/hashicorp/consul-template/renderer"
"github.com/hashicorp/consul-template/systemd"
"github.com/hashicorp/consul-template/template"
"github.com/hashicorp/consul-template/watch"

Expand All @@ -32,6 +33,10 @@ const (
viewLimit = 128
)

type notifier interface {
Notify(string) error
}

// Runner responsible rendering Templates and invoking Commands.
type Runner struct {
// ErrCh and DoneCh are channels where errors and finish notifications occur.
Expand Down Expand Up @@ -108,6 +113,12 @@ type Runner struct {
// NOTE this is only used when CT is being used as a library.
Env map[string]string

// notifier is called after all templates have been successfully rendered
notifier notifier

// ready indicates that the runner has rendered each template at least once
ready bool

// stopLock is the lock around checking if the runner can be stopped
stopLock sync.Mutex

Expand Down Expand Up @@ -377,6 +388,13 @@ func (r *Runner) Start() {
r.Stop()
return
}

if r.notifier != nil && !r.ready {
if notifErr := r.notifier.Notify(systemd.Ready); notifErr != nil {
log.Printf("[DEBUG] (runner) systemd notify failed: %v", notifErr)
}
}
r.ready = true
}

OUTER:
Expand Down Expand Up @@ -1000,6 +1018,9 @@ func (r *Runner) init(clients *dep.ClientSet) error {
}
}

r.notifier = &systemd.Notifier{}
r.ready = false

return nil
}

Expand Down
53 changes: 53 additions & 0 deletions manager/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package manager
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
Expand All @@ -20,6 +21,13 @@ import (
"github.com/hashicorp/consul-template/template"
)

type mockNotifier struct{ s string }

func (n *mockNotifier) Notify(state string) error {
n.s = state
return nil
}

func TestRunner_initTemplates(t *testing.T) {
c := config.TestConfig(
&config.Config{
Expand Down Expand Up @@ -549,6 +557,51 @@ func TestRunner_Start(t *testing.T) {
}
})

t.Run("notify", func(t *testing.T) {
t.Parallel()

out, err := ioutil.TempFile("", "")
if err != nil {
t.Fatal(err)
}
defer os.Remove(out.Name())

c := config.DefaultConfig().Merge(&config.Config{
Templates: &config.TemplateConfigs{
&config.TemplateConfig{
Contents: config.String(`test`),
Destination: config.String(out.Name()),
},
},
})
c.Finalize()

r, err := NewRunner(c, false)
if err != nil {
t.Fatal(err)
}

notify := &mockNotifier{}
r.notifier = notify

go r.Start()
defer r.Stop()

select {
case err := <-r.ErrCh:
t.Fatal(err)
case <-r.renderedCh:
if !r.ready {
t.Errorf("\nexp: %#v\nact: %#v", true, r.ready)
}
if exp, act := "READY=1", notify.s; exp != act {
t.Errorf("\nexp: %#v\nact: %#v", exp, act)
}
case <-time.After(2 * time.Second):
t.Fatal("timeout")
}
})

t.Run("run_no_deps", func(t *testing.T) {
out, err := os.CreateTemp("", "")
if err != nil {
Expand Down
35 changes: 35 additions & 0 deletions systemd/notifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package systemd

import (
"errors"
"net"
"os"
)

var errNoSocket = errors.New("no socket")

const Ready = "READY=1"

// Notifier provides a method to send a message to systemd.
type Notifier struct{}

// Notify sends a message to the init daemon. It is common to ignore the error.
func (n *Notifier) Notify(state string) error {
addr := &net.UnixAddr{
Name: os.Getenv("NOTIFY_SOCKET"),
Net: "unixgram",
}

if addr.Name == "" {
return errNoSocket
}

conn, err := net.DialUnix(addr.Net, nil, addr)
if err != nil {
return err
}
defer conn.Close()

_, err = conn.Write([]byte(state))
return err
}

0 comments on commit 6b9060e

Please sign in to comment.