Skip to content

Commit

Permalink
add RefreshFrequency option for blocking strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
nickalie authored and acouvreur committed Nov 30, 2024
1 parent 82929a1 commit 30ea44a
Show file tree
Hide file tree
Showing 17 changed files with 97 additions and 54 deletions.
9 changes: 5 additions & 4 deletions app/http/routes/models/blocking_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package models
import "time"

type BlockingRequest struct {
Names []string `form:"names"`
Group string `form:"group"`
SessionDuration time.Duration `form:"session_duration"`
Timeout time.Duration `form:"timeout"`
Names []string `form:"names"`
Group string `form:"group"`
SessionDuration time.Duration `form:"session_duration"`
Timeout time.Duration `form:"timeout"`
RefreshFrequency time.Duration `form:"refresh_frequency"`
}
4 changes: 2 additions & 2 deletions app/http/routes/strategies.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ func (s *ServeStrategy) ServeBlocking(c *gin.Context) {
var sessionState *sessions.SessionState
var err error
if len(request.Names) > 0 {
sessionState, err = s.SessionsManager.RequestReadySession(c.Request.Context(), request.Names, request.SessionDuration, request.Timeout)
sessionState, err = s.SessionsManager.RequestReadySession(c.Request.Context(), request.Names, request.SessionDuration, request.Timeout, request.RefreshFrequency)
} else {
sessionState, err = s.SessionsManager.RequestReadySessionGroup(c.Request.Context(), request.Group, request.SessionDuration, request.Timeout)
sessionState, err = s.SessionsManager.RequestReadySessionGroup(c.Request.Context(), request.Group, request.SessionDuration, request.Timeout, request.RefreshFrequency)
}

if err != nil {
Expand Down
12 changes: 6 additions & 6 deletions app/sessions/sessions_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ const defaultRefreshFrequency = 2 * time.Second
type Manager interface {
RequestSession(names []string, duration time.Duration) *SessionState
RequestSessionGroup(group string, duration time.Duration) *SessionState
RequestReadySession(ctx context.Context, names []string, duration time.Duration, timeout time.Duration) (*SessionState, error)
RequestReadySessionGroup(ctx context.Context, group string, duration time.Duration, timeout time.Duration) (*SessionState, error)
RequestReadySession(ctx context.Context, names []string, duration time.Duration, timeout time.Duration, frequency time.Duration) (*SessionState, error)
RequestReadySessionGroup(ctx context.Context, group string, duration time.Duration, timeout time.Duration, frequency time.Duration) (*SessionState, error)

LoadSessions(io.ReadCloser) error
SaveSessions(io.WriteCloser) error
Expand Down Expand Up @@ -226,14 +226,14 @@ func (s *SessionsManager) requestSessionInstance(name string, duration time.Dura
return &requestState, nil
}

func (s *SessionsManager) RequestReadySession(ctx context.Context, names []string, duration time.Duration, timeout time.Duration) (*SessionState, error) {
func (s *SessionsManager) RequestReadySession(ctx context.Context, names []string, duration time.Duration, timeout time.Duration, frequency time.Duration) (*SessionState, error) {

session := s.RequestSession(names, duration)
if session.IsReady() {
return session, nil
}

ticker := time.NewTicker(5 * time.Second)
ticker := time.NewTicker(frequency)
readiness := make(chan *SessionState)
quit := make(chan struct{})

Expand Down Expand Up @@ -266,7 +266,7 @@ func (s *SessionsManager) RequestReadySession(ctx context.Context, names []strin
}
}

func (s *SessionsManager) RequestReadySessionGroup(ctx context.Context, group string, duration time.Duration, timeout time.Duration) (sessionState *SessionState, err error) {
func (s *SessionsManager) RequestReadySessionGroup(ctx context.Context, group string, duration time.Duration, timeout time.Duration, frequency time.Duration) (sessionState *SessionState, err error) {

if len(group) == 0 {
return nil, fmt.Errorf("group is mandatory")
Expand All @@ -278,7 +278,7 @@ func (s *SessionsManager) RequestReadySessionGroup(ctx context.Context, group st
return nil, fmt.Errorf("group has no member")
}

return s.RequestReadySession(ctx, names, duration, timeout)
return s.RequestReadySession(ctx, names, duration, timeout, frequency)
}

func (s *SessionsManager) ExpiresAfter(instance *instance.State, duration time.Duration) {
Expand Down
6 changes: 3 additions & 3 deletions app/sessions/sessions_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ func TestSessionsManager_RequestReadySessionCancelledByUser(t *testing.T) {

errchan := make(chan error)
go func() {
_, err := s.RequestReadySession(ctx, []string{"nginx", "whoami"}, time.Minute, time.Minute)
_, err := s.RequestReadySession(ctx, []string{"nginx", "whoami"}, time.Minute, time.Minute, time.Second)
errchan <- err
}()

Expand All @@ -167,7 +167,7 @@ func TestSessionsManager_RequestReadySessionCancelledByTimeout(t *testing.T) {

errchan := make(chan error)
go func() {
_, err := s.RequestReadySession(context.Background(), []string{"nginx", "whoami"}, time.Minute, time.Second)
_, err := s.RequestReadySession(context.Background(), []string{"nginx", "whoami"}, time.Minute, time.Second, time.Second)
errchan <- err
}()

Expand All @@ -190,7 +190,7 @@ func TestSessionsManager_RequestReadySession(t *testing.T) {

errchan := make(chan error)
go func() {
_, err := s.RequestReadySession(context.Background(), []string{"nginx", "whoami"}, time.Minute, time.Second)
_, err := s.RequestReadySession(context.Background(), []string{"nginx", "whoami"}, time.Minute, time.Second, time.Second)
errchan <- err
}()

Expand Down
2 changes: 2 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ It provides an integrations with multiple reverse proxies and different loading
viper.BindPFlag("strategy.dynamic.default-refresh-frequency", startCmd.Flags().Lookup("strategy.dynamic.default-refresh-frequency"))
startCmd.Flags().DurationVar(&conf.Strategy.Blocking.DefaultTimeout, "strategy.blocking.default-timeout", 1*time.Minute, "Default timeout used for blocking strategy")
viper.BindPFlag("strategy.blocking.default-timeout", startCmd.Flags().Lookup("strategy.blocking.default-timeout"))
startCmd.Flags().DurationVar(&conf.Strategy.Blocking.DefaultRefreshFrequency, "strategy.blocking.default-refresh-frequency", 5*time.Second, "Default refresh frequency for blocking strategy")
viper.BindPFlag("strategy.blocking.default-refresh-frequency", startCmd.Flags().Lookup("strategy.blocking.default-refresh-frequency"))

rootCmd.AddCommand(startCmd)
rootCmd.AddCommand(newVersionCommand())
Expand Down
1 change: 1 addition & 0 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ func TestPrecedence(t *testing.T) {
"--strategy.dynamic.default-theme", "cli",
"--strategy.dynamic.default-refresh-frequency", "3h",
"--strategy.blocking.default-timeout", "3h",
"--strategy.blocking.default-refresh-frequency", "3h",
})
cmd.Execute()

Expand Down
3 changes: 2 additions & 1 deletion cmd/testdata/config_cli_wanted.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"DefaultRefreshFrequency": 10800000000000
},
"Blocking": {
"DefaultTimeout": 10800000000000
"DefaultTimeout": 10800000000000,
"DefaultRefreshFrequency": 10800000000000
}
}
}
3 changes: 2 additions & 1 deletion cmd/testdata/config_default.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"DefaultRefreshFrequency": 5000000000
},
"Blocking": {
"DefaultTimeout": 60000000000
"DefaultTimeout": 60000000000,
"DefaultRefreshFrequency": 5000000000
}
}
}
3 changes: 2 additions & 1 deletion cmd/testdata/config_env_wanted.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"DefaultRefreshFrequency": 7200000000000
},
"Blocking": {
"DefaultTimeout": 7200000000000
"DefaultTimeout": 7200000000000,
"DefaultRefreshFrequency": 7200000000000
}
}
}
3 changes: 2 additions & 1 deletion cmd/testdata/config_yaml_wanted.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"DefaultRefreshFrequency": 3600000000000
},
"Blocking": {
"DefaultTimeout": 3600000000000
"DefaultTimeout": 3600000000000,
"DefaultRefreshFrequency": 3600000000000
}
}
}
3 changes: 2 additions & 1 deletion config/strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ type DynamicStrategy struct {
}

type BlockingStrategy struct {
DefaultTimeout time.Duration `mapstructure:"DEFAULT_TIMEOUT" yaml:"defaultTimeout" default:"1m"`
DefaultTimeout time.Duration `mapstructure:"DEFAULT_TIMEOUT" yaml:"defaultTimeout" default:"1m"`
DefaultRefreshFrequency time.Duration `mapstructure:"DEFAULT_REFRESH_FREQUENCY" yaml:"defaultRefreshFrequency" default:"5s"`
}

type Strategy struct {
Expand Down
1 change: 1 addition & 0 deletions docs/plugins/caddy.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ You can have the following configuration:
}
blocking {
[timeout 1m]
[refresh_frequency 2s]
}
}
reverse_proxy myservice:port
Expand Down
36 changes: 21 additions & 15 deletions plugins/caddy/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ type DynamicConfiguration struct {
}

type BlockingConfiguration struct {
Timeout *time.Duration
Timeout *time.Duration
RefreshFrequency *time.Duration
}

type Config struct {
Expand All @@ -49,21 +50,20 @@ func CreateConfig() *Config {

// UnmarshalCaddyfile implements caddyfile.Unmarshaler. Syntax:
//
// sablier [<sablierURL>] {
// [names container1,container2,...]
// [group mygroup]
// [session_duration 30m]
// dynamic {
// [display_name This is my display name]
// [show_details yes|true|on]
// [theme hacker-terminal]
// [refresh_frequency 2s]
// }
// blocking {
// [timeout 1m]
// }
// sablier [<sablierURL>] {
// [names container1,container2,...]
// [group mygroup]
// [session_duration 30m]
// dynamic {
// [display_name This is my display name]
// [show_details yes|true|on]
// [theme hacker-terminal]
// [refresh_frequency 2s]
// }
//
// blocking {
// [timeout 1m]
// }
// }
func (c *Config) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
if d.NextArg() {
Expand Down Expand Up @@ -172,6 +172,12 @@ func parseBlocking(d *caddyfile.Dispenser) (*BlockingConfiguration, error) {
return nil, err
}
conf.Timeout = &duration
case "refresh_frequency":
duration, err := time.ParseDuration(args)
if err != nil {
return nil, err
}
conf.RefreshFrequency = &duration
}
}
return conf, nil
Expand Down
14 changes: 14 additions & 0 deletions plugins/caddy/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,19 @@ func TestConfig_BuildRequest(t *testing.T) {
want: createRequest("GET", "http://sablier:10000/api/strategies/blocking?names=nginx&names=apache&session_duration=1m&timeout=5m", nil),
wantErr: false,
},
{
name: "blocking session with refresh frequency",
fields: caddy.Config{
SablierURL: "http://sablier:10000",
Names: []string{"nginx", "apache"},
SessionDuration: &oneMinute,
Dynamic: &caddy.DynamicConfiguration{
RefreshFrequency: &oneMinute,
},
},
want: createRequest("GET", "http://sablier:10000/api/strategies/blocking?names=nginx&names=apache&refresh_frequency=1m&session_duration=1m", nil),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -296,6 +309,7 @@ func TestConfig_UnmarshalCaddyfile(t *testing.T) {
session_duration 1m
blocking {
timeout 1m
refresh_frequency 1m
}
}`,
want: caddy.Config{
Expand Down
35 changes: 18 additions & 17 deletions plugins/nginx/njs/sablier.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ function call(r) {
* @property {string} theme
* @property {string} refreshFrequency
* @property {string} timeout
*
*
*/

/**
*
* @param {*} headers
*
* @param {*} headers
* @returns {SablierConfig}
*/
function createConfigurationFromVariables(r) {
Expand All @@ -56,9 +56,9 @@ function createConfigurationFromVariables(r) {
}

/**
*
* @param {SablierConfig} c
* @returns
*
* @param {SablierConfig} c
* @returns
*/
function buildRequest(c) {
if (c.timeout == undefined || c.timeout == "") {
Expand All @@ -69,9 +69,9 @@ function buildRequest(c) {
}

/**
*
* @param {SablierConfig} config
* @returns
*
* @param {SablierConfig} config
* @returns
*/
function createDynamicUrl(config) {
const url = `${config.sablierUrl}/api/strategies/dynamic`
Expand All @@ -88,21 +88,22 @@ function buildRequest(c) {
} else if(config.group) {
query.group = config.group
} else {
throw new Error('you must specify names or group');
throw new Error('you must specify names or group');
}

return {url, query: querystring.stringify(query)}
}

/**
*
* @param {SablierConfig} config
* @returns
*
* @param {SablierConfig} config
* @returns
*/
function createBlockingUrl(config) {
const url = `${config.sablierUrl}/api/strategies/blocking`
const query = {
const query = {
session_duration: config.sessionDuration,
refresh_frequency: config.refreshFrequency,
timeout:config.timeout,
};

Expand All @@ -111,10 +112,10 @@ function buildRequest(c) {
} else if(config.group) {
query.group = config.group
} else {
throw new Error('you must specify names or group');
throw new Error('you must specify names or group');
}

return {url, query: querystring.stringify(query)}
return {url, query: querystring.stringify(query)}
}

export default { call };
export default { call };
13 changes: 12 additions & 1 deletion plugins/traefik/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ type DynamicConfiguration struct {
}

type BlockingConfiguration struct {
Timeout string `yaml:"timeout"`
Timeout string `yaml:"timeout"`
RefreshFrequency string `yaml:"refreshFrequency"`
}

type Config struct {
Expand Down Expand Up @@ -154,6 +155,16 @@ func (c *Config) buildBlockingRequest() (*http.Request, error) {
q.Add("session_duration", c.SessionDuration)
}

if c.Blocking.RefreshFrequency != "" {
_, err := time.ParseDuration(c.Dynamic.RefreshFrequency)

if err != nil {
return nil, fmt.Errorf("error parsing dynamic.refreshFrequency: %v", err)
}

q.Add("refresh_frequency", c.Dynamic.RefreshFrequency)
}

for _, name := range c.splittedNames {
q.Add("names", name)
}
Expand Down
3 changes: 2 additions & 1 deletion sablier.sample.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ strategy:
default-theme: hacker-terminal
default-refresh-frequency: 5s
blocking:
default-timeout: 1m
default-timeout: 1m
default-refresh-frequency: 5s

0 comments on commit 30ea44a

Please sign in to comment.