Feat: Env variables to set health timeouts

- HEALTH_OPENVPN_DURATION_INITIAL
- HEALTH_OPENVPN_DURATION_ADDITION
This commit is contained in:
Quentin McGaw (desktop)
2021-07-22 20:13:20 +00:00
parent 8beff34cca
commit 6acb7caf5b
11 changed files with 307 additions and 20 deletions

View File

@@ -0,0 +1,41 @@
package configuration
import (
"strings"
"github.com/qdm12/golibs/params"
)
// Health contains settings for the healthcheck and health server.
type Health struct {
OpenVPN HealthyWait
}
func (settings *Health) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *Health) lines() (lines []string) {
lines = append(lines, lastIndent+"Health:")
lines = append(lines, indent+lastIndent+"OpenVPN:")
for _, line := range settings.OpenVPN.lines() {
lines = append(lines, indent+indent+line)
}
return lines
}
func (settings *Health) read(r reader) (err error) {
settings.OpenVPN.Initial, err = r.env.Duration("HEALTH_OPENVPN_DURATION_INITIAL", params.Default("6s"))
if err != nil {
return err
}
settings.OpenVPN.Addition, err = r.env.Duration("HEALTH_OPENVPN_DURATION_ADDITION", params.Default("5s"))
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,150 @@
package configuration
import (
"errors"
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/qdm12/golibs/params/mock_params"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_Health_String(t *testing.T) {
t.Parallel()
var health Health
const expected = "|--Health:\n |--OpenVPN:\n |--Initial duration: 0s"
s := health.String()
assert.Equal(t, expected, s)
}
func Test_Health_lines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings Health
lines []string
}{
"empty": {
lines: []string{
"|--Health:",
" |--OpenVPN:",
" |--Initial duration: 0s",
},
},
"filled settings": {
settings: Health{
OpenVPN: HealthyWait{
Initial: time.Second,
Addition: time.Minute,
},
},
lines: []string{
"|--Health:",
" |--OpenVPN:",
" |--Initial duration: 1s",
" |--Addition duration: 1m0s",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.lines()
assert.Equal(t, testCase.lines, lines)
})
}
}
func Test_Health_read(t *testing.T) {
t.Parallel()
errDummy := errors.New("dummy")
testCases := map[string]struct {
openvpnInitialDuration time.Duration
openvpnInitialErr error
openvpnAdditionDuration time.Duration
openvpnAdditionErr error
expected Health
err error
}{
"success": {
openvpnInitialDuration: time.Second,
openvpnAdditionDuration: time.Minute,
expected: Health{
OpenVPN: HealthyWait{
Initial: time.Second,
Addition: time.Minute,
},
},
},
"initial error": {
openvpnInitialDuration: time.Second,
openvpnInitialErr: errDummy,
openvpnAdditionDuration: time.Minute,
expected: Health{
OpenVPN: HealthyWait{
Initial: time.Second,
},
},
err: errDummy,
},
"addition error": {
openvpnInitialDuration: time.Second,
openvpnAdditionDuration: time.Minute,
openvpnAdditionErr: errDummy,
expected: Health{
OpenVPN: HealthyWait{
Initial: time.Second,
Addition: time.Minute,
},
},
err: errDummy,
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
env := mock_params.NewMockEnv(ctrl)
env.EXPECT().
Duration("HEALTH_OPENVPN_DURATION_INITIAL", gomock.Any()).
Return(testCase.openvpnInitialDuration, testCase.openvpnInitialErr)
if testCase.openvpnInitialErr == nil {
env.EXPECT().
Duration("HEALTH_OPENVPN_DURATION_ADDITION", gomock.Any()).
Return(testCase.openvpnAdditionDuration, testCase.openvpnAdditionErr)
}
r := reader{
env: env,
}
var health Health
err := health.read(r)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.expected, health)
})
}
}

View File

@@ -0,0 +1,55 @@
package configuration
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func Test_HealthyWait_String(t *testing.T) {
t.Parallel()
var healthyWait HealthyWait
const expected = "|--Initial duration: 0s"
s := healthyWait.String()
assert.Equal(t, expected, s)
}
func Test_HealthyWait_lines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings HealthyWait
lines []string
}{
"empty": {
lines: []string{
"|--Initial duration: 0s",
},
},
"filled settings": {
settings: HealthyWait{
Initial: time.Second,
Addition: time.Minute,
},
lines: []string{
"|--Initial duration: 1s",
"|--Addition duration: 1m0s",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.lines()
assert.Equal(t, testCase.lines, lines)
})
}
}

View File

@@ -0,0 +1,30 @@
package configuration
import (
"strings"
"time"
)
type HealthyWait struct {
// Initial is the initial duration to wait for the program
// to be healthy before taking action.
Initial time.Duration
// Addition is the duration to add to the Initial duration
// after Initial has expired to wait longer for the program
// to be healthy.
Addition time.Duration
}
func (settings *HealthyWait) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *HealthyWait) lines() (lines []string) {
lines = append(lines, lastIndent+"Initial duration: "+settings.Initial.String())
if settings.Addition > 0 {
lines = append(lines, lastIndent+"Addition duration: "+settings.Addition.String())
}
return lines
}

View File

@@ -22,6 +22,7 @@ type Settings struct {
PublicIP PublicIP
VersionInformation bool
ControlServer ControlServer
Health Health
}
func (settings *Settings) String() string {
@@ -36,6 +37,7 @@ func (settings *Settings) lines() (lines []string) {
lines = append(lines, settings.System.lines()...)
lines = append(lines, settings.HTTPProxy.lines()...)
lines = append(lines, settings.ShadowSocks.lines()...)
lines = append(lines, settings.Health.lines()...)
lines = append(lines, settings.ControlServer.lines()...)
lines = append(lines, settings.Updater.lines()...)
lines = append(lines, settings.PublicIP.lines()...)
@@ -55,6 +57,7 @@ var (
ErrControlServer = errors.New("cannot read control server settings")
ErrUpdater = errors.New("cannot read Updater settings")
ErrPublicIP = errors.New("cannot read Public IP getter settings")
ErrHealth = errors.New("cannot read health settings")
)
// Read obtains all configuration options for the program and returns an error as soon
@@ -107,5 +110,9 @@ func (settings *Settings) Read(env params.Env, os os.OS, logger logging.Logger)
return fmt.Errorf("%w: %s", ErrPublicIP, err)
}
if err := settings.Health.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrHealth, err)
}
return nil
}

View File

@@ -37,6 +37,9 @@ func Test_Settings_lines(t *testing.T) {
" |--Process user ID: 0",
" |--Process group ID: 0",
" |--Timezone: NOT SET ⚠️ - it can cause time related issues",
"|--Health:",
" |--OpenVPN:",
" |--Initial duration: 0s",
"|--HTTP control server:",
" |--Listening port: 0",
"|--Public IP getter: disabled",