mirror of https://github.com/caddyserver/caddy.git
core: Add support for `d` duration unit (#3323)
* caddy: Add support for `d` duration unit * Improvements to ParseDuration; add unit tests Co-authored-by: Matthew Holt <mholt@users.noreply.github.com>
This commit is contained in:
parent
35e1d92d58
commit
ef6e53bb5f
32
caddy.go
32
caddy.go
|
@ -486,7 +486,7 @@ func Validate(cfg *Config) error {
|
||||||
// Duration can be an integer or a string. An integer is
|
// Duration can be an integer or a string. An integer is
|
||||||
// interpreted as nanoseconds. If a string, it is a Go
|
// interpreted as nanoseconds. If a string, it is a Go
|
||||||
// time.Duration value such as `300ms`, `1.5h`, or `2h45m`;
|
// time.Duration value such as `300ms`, `1.5h`, or `2h45m`;
|
||||||
// valid units are `ns`, `us`/`µs`, `ms`, `s`, `m`, and `h`.
|
// valid units are `ns`, `us`/`µs`, `ms`, `s`, `m`, `h`, and `d`.
|
||||||
type Duration time.Duration
|
type Duration time.Duration
|
||||||
|
|
||||||
// UnmarshalJSON satisfies json.Unmarshaler.
|
// UnmarshalJSON satisfies json.Unmarshaler.
|
||||||
|
@ -497,7 +497,7 @@ func (d *Duration) UnmarshalJSON(b []byte) error {
|
||||||
var dur time.Duration
|
var dur time.Duration
|
||||||
var err error
|
var err error
|
||||||
if b[0] == byte('"') && b[len(b)-1] == byte('"') {
|
if b[0] == byte('"') && b[len(b)-1] == byte('"') {
|
||||||
dur, err = time.ParseDuration(strings.Trim(string(b), `"`))
|
dur, err = ParseDuration(strings.Trim(string(b), `"`))
|
||||||
} else {
|
} else {
|
||||||
err = json.Unmarshal(b, &dur)
|
err = json.Unmarshal(b, &dur)
|
||||||
}
|
}
|
||||||
|
@ -505,6 +505,34 @@ func (d *Duration) UnmarshalJSON(b []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseDuration parses a duration string, adding
|
||||||
|
// support for the "d" unit meaning number of days,
|
||||||
|
// where a day is assumed to be 24h.
|
||||||
|
func ParseDuration(s string) (time.Duration, error) {
|
||||||
|
var inNumber bool
|
||||||
|
var numStart int
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
ch := s[i]
|
||||||
|
if ch == 'd' {
|
||||||
|
daysStr := s[numStart:i]
|
||||||
|
days, err := strconv.ParseFloat(daysStr, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
hours := days * 24.0
|
||||||
|
hoursStr := strconv.FormatFloat(hours, 'f', -1, 64)
|
||||||
|
s = s[:numStart] + hoursStr + "h" + s[i+1:]
|
||||||
|
i--
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !inNumber {
|
||||||
|
numStart = i
|
||||||
|
}
|
||||||
|
inNumber = (ch >= '0' && ch <= '9') || ch == '.' || ch == '-' || ch == '+'
|
||||||
|
}
|
||||||
|
return time.ParseDuration(s)
|
||||||
|
}
|
||||||
|
|
||||||
// GoModule returns the build info of this Caddy
|
// GoModule returns the build info of this Caddy
|
||||||
// build from debug.BuildInfo (requires Go modules).
|
// build from debug.BuildInfo (requires Go modules).
|
||||||
// If no version information is available, a non-nil
|
// If no version information is available, a non-nil
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright 2015 Matthew Holt and The Caddy Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package caddy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseDuration(t *testing.T) {
|
||||||
|
const day = 24 * time.Hour
|
||||||
|
for i, tc := range []struct {
|
||||||
|
input string
|
||||||
|
expect time.Duration
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
input: "3h",
|
||||||
|
expect: 3 * time.Hour,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "1d",
|
||||||
|
expect: day,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "1d30m",
|
||||||
|
expect: day + 30*time.Minute,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "1m2d",
|
||||||
|
expect: time.Minute + day*2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "1m2d30s",
|
||||||
|
expect: time.Minute + day*2 + 30*time.Second,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "1d2d",
|
||||||
|
expect: 3 * day,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "1.5d",
|
||||||
|
expect: time.Duration(1.5 * float64(day)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "4m1.25d",
|
||||||
|
expect: 4*time.Minute + time.Duration(1.25*float64(day)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "-1.25d12h",
|
||||||
|
expect: time.Duration(-1.25*float64(day)) - 12*time.Hour,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
actual, err := ParseDuration(tc.input)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Test %d ('%s'): Got error: %v", i, tc.input, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if actual != tc.expect {
|
||||||
|
t.Errorf("Test %d ('%s'): Expected=%s Actual=%s", i, tc.input, tc.expect, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,7 +16,6 @@ package httpcaddyfile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
"github.com/caddyserver/caddy/v2"
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||||
|
@ -227,7 +226,7 @@ func parseOptOnDemand(d *caddyfile.Dispenser) (interface{}, error) {
|
||||||
if !d.NextArg() {
|
if !d.NextArg() {
|
||||||
return nil, d.ArgErr()
|
return nil, d.ArgErr()
|
||||||
}
|
}
|
||||||
dur, err := time.ParseDuration(d.Val())
|
dur, err := caddy.ParseDuration(d.Val())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -489,3 +489,53 @@ func TestGlobalOptions(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`)
|
}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLogRollDays(t *testing.T) {
|
||||||
|
caddytest.AssertAdapt(t, `
|
||||||
|
:80
|
||||||
|
|
||||||
|
log {
|
||||||
|
output file /var/log/access.log {
|
||||||
|
roll_size 1gb
|
||||||
|
roll_keep 5
|
||||||
|
roll_keep_for 90d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, "caddyfile", `{
|
||||||
|
"logging": {
|
||||||
|
"logs": {
|
||||||
|
"default": {
|
||||||
|
"exclude": [
|
||||||
|
"http.log.access.log0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"log0": {
|
||||||
|
"writer": {
|
||||||
|
"filename": "/var/log/access.log",
|
||||||
|
"output": "file",
|
||||||
|
"roll_keep": 5,
|
||||||
|
"roll_keep_days": 90,
|
||||||
|
"roll_size_mb": 954
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"http.log.access.log0"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"apps": {
|
||||||
|
"http": {
|
||||||
|
"servers": {
|
||||||
|
"srv0": {
|
||||||
|
"listen": [
|
||||||
|
":80"
|
||||||
|
],
|
||||||
|
"logs": {
|
||||||
|
"default_logger_name": "log0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
}
|
||||||
|
|
|
@ -311,7 +311,7 @@ func (f Flags) Float64(name string) float64 {
|
||||||
// is not a duration type. It panics if the flag is
|
// is not a duration type. It panics if the flag is
|
||||||
// not in the flag set.
|
// not in the flag set.
|
||||||
func (f Flags) Duration(name string) time.Duration {
|
func (f Flags) Duration(name string) time.Duration {
|
||||||
val, _ := time.ParseDuration(f.String(name))
|
val, _ := caddy.ParseDuration(f.String(name))
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
"github.com/caddyserver/caddy/v2"
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
"github.com/caddyserver/caddy/v2/caddyconfig"
|
||||||
|
@ -250,7 +249,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
if h.LoadBalancing == nil {
|
if h.LoadBalancing == nil {
|
||||||
h.LoadBalancing = new(LoadBalancing)
|
h.LoadBalancing = new(LoadBalancing)
|
||||||
}
|
}
|
||||||
dur, err := time.ParseDuration(d.Val())
|
dur, err := caddy.ParseDuration(d.Val())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return d.Errf("bad duration value %s: %v", d.Val(), err)
|
return d.Errf("bad duration value %s: %v", d.Val(), err)
|
||||||
}
|
}
|
||||||
|
@ -263,7 +262,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
if h.LoadBalancing == nil {
|
if h.LoadBalancing == nil {
|
||||||
h.LoadBalancing = new(LoadBalancing)
|
h.LoadBalancing = new(LoadBalancing)
|
||||||
}
|
}
|
||||||
dur, err := time.ParseDuration(d.Val())
|
dur, err := caddy.ParseDuration(d.Val())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return d.Errf("bad interval value '%s': %v", d.Val(), err)
|
return d.Errf("bad interval value '%s': %v", d.Val(), err)
|
||||||
}
|
}
|
||||||
|
@ -307,7 +306,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
if h.HealthChecks.Active == nil {
|
if h.HealthChecks.Active == nil {
|
||||||
h.HealthChecks.Active = new(ActiveHealthChecks)
|
h.HealthChecks.Active = new(ActiveHealthChecks)
|
||||||
}
|
}
|
||||||
dur, err := time.ParseDuration(d.Val())
|
dur, err := caddy.ParseDuration(d.Val())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return d.Errf("bad interval value %s: %v", d.Val(), err)
|
return d.Errf("bad interval value %s: %v", d.Val(), err)
|
||||||
}
|
}
|
||||||
|
@ -323,7 +322,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
if h.HealthChecks.Active == nil {
|
if h.HealthChecks.Active == nil {
|
||||||
h.HealthChecks.Active = new(ActiveHealthChecks)
|
h.HealthChecks.Active = new(ActiveHealthChecks)
|
||||||
}
|
}
|
||||||
dur, err := time.ParseDuration(d.Val())
|
dur, err := caddy.ParseDuration(d.Val())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return d.Errf("bad timeout value %s: %v", d.Val(), err)
|
return d.Errf("bad timeout value %s: %v", d.Val(), err)
|
||||||
}
|
}
|
||||||
|
@ -387,7 +386,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
if h.HealthChecks.Passive == nil {
|
if h.HealthChecks.Passive == nil {
|
||||||
h.HealthChecks.Passive = new(PassiveHealthChecks)
|
h.HealthChecks.Passive = new(PassiveHealthChecks)
|
||||||
}
|
}
|
||||||
dur, err := time.ParseDuration(d.Val())
|
dur, err := caddy.ParseDuration(d.Val())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return d.Errf("bad duration value '%s': %v", d.Val(), err)
|
return d.Errf("bad duration value '%s': %v", d.Val(), err)
|
||||||
}
|
}
|
||||||
|
@ -441,7 +440,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
if h.HealthChecks.Passive == nil {
|
if h.HealthChecks.Passive == nil {
|
||||||
h.HealthChecks.Passive = new(PassiveHealthChecks)
|
h.HealthChecks.Passive = new(PassiveHealthChecks)
|
||||||
}
|
}
|
||||||
dur, err := time.ParseDuration(d.Val())
|
dur, err := caddy.ParseDuration(d.Val())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return d.Errf("bad duration value '%s': %v", d.Val(), err)
|
return d.Errf("bad duration value '%s': %v", d.Val(), err)
|
||||||
}
|
}
|
||||||
|
@ -454,7 +453,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
if fi, err := strconv.Atoi(d.Val()); err == nil {
|
if fi, err := strconv.Atoi(d.Val()); err == nil {
|
||||||
h.FlushInterval = caddy.Duration(fi)
|
h.FlushInterval = caddy.Duration(fi)
|
||||||
} else {
|
} else {
|
||||||
dur, err := time.ParseDuration(d.Val())
|
dur, err := caddy.ParseDuration(d.Val())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return d.Errf("bad duration value '%s': %v", d.Val(), err)
|
return d.Errf("bad duration value '%s': %v", d.Val(), err)
|
||||||
}
|
}
|
||||||
|
@ -606,7 +605,7 @@ func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
if !d.NextArg() {
|
if !d.NextArg() {
|
||||||
return d.ArgErr()
|
return d.ArgErr()
|
||||||
}
|
}
|
||||||
dur, err := time.ParseDuration(d.Val())
|
dur, err := caddy.ParseDuration(d.Val())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return d.Errf("bad timeout value '%s': %v", d.Val(), err)
|
return d.Errf("bad timeout value '%s': %v", d.Val(), err)
|
||||||
}
|
}
|
||||||
|
@ -641,7 +640,7 @@ func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
if !d.NextArg() {
|
if !d.NextArg() {
|
||||||
return d.ArgErr()
|
return d.ArgErr()
|
||||||
}
|
}
|
||||||
dur, err := time.ParseDuration(d.Val())
|
dur, err := caddy.ParseDuration(d.Val())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return d.Errf("bad timeout value '%s': %v", d.Val(), err)
|
return d.Errf("bad timeout value '%s': %v", d.Val(), err)
|
||||||
}
|
}
|
||||||
|
@ -683,7 +682,7 @@ func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
h.KeepAlive.Enabled = &disable
|
h.KeepAlive.Enabled = &disable
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
dur, err := time.ParseDuration(d.Val())
|
dur, err := caddy.ParseDuration(d.Val())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return d.Errf("bad duration value '%s': %v", d.Val(), err)
|
return d.Errf("bad duration value '%s': %v", d.Val(), err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
"github.com/caddyserver/caddy/v2"
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||||
|
@ -194,7 +193,7 @@ func (fw *FileWriter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
if !d.AllArgs(&keepForStr) {
|
if !d.AllArgs(&keepForStr) {
|
||||||
return d.ArgErr()
|
return d.ArgErr()
|
||||||
}
|
}
|
||||||
keepFor, err := time.ParseDuration(keepForStr)
|
keepFor, err := caddy.ParseDuration(keepForStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return d.Errf("parsing roll_keep_for duration: %v", err)
|
return d.Errf("parsing roll_keep_for duration: %v", err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue