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

Revert "Revert "Implement systemd notification support after all templates ha…" #1841

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@
import (
"bytes"
"fmt"
"io/ioutil"

Check failure on line 9 in manager/runner_test.go

View workflow job for this annotation

GitHub Actions / Run linters

SA1019: "io/ioutil" has been deprecated since Go 1.19: As of Go 1.16, the same functionality is now provided by package [io] or package [os], and those implementations should be preferred in new code. See the specific function documentation for details. (staticcheck)
"os"
"os/exec"
"path/filepath"
Expand All @@ -20,6 +21,13 @@
"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 @@
}
})

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

out, err := ioutil.TempFile("", "")

Check failure on line 563 in manager/runner_test.go

View workflow job for this annotation

GitHub Actions / Run linters

use of `ioutil.TempFile` forbidden because "Use io and os packages instead of ioutil" (forbidigo)
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
}