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
|
||||
// interpreted as nanoseconds. If a string, it is a Go
|
||||
// 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
|
||||
|
||||
// UnmarshalJSON satisfies json.Unmarshaler.
|
||||
|
@ -497,7 +497,7 @@ func (d *Duration) UnmarshalJSON(b []byte) error {
|
|||
var dur time.Duration
|
||||
var err error
|
||||
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 {
|
||||
err = json.Unmarshal(b, &dur)
|
||||
}
|
||||
|
@ -505,6 +505,34 @@ func (d *Duration) UnmarshalJSON(b []byte) error {
|
|||
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
|
||||
// build from debug.BuildInfo (requires Go modules).
|
||||
// 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 (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
|
@ -227,7 +226,7 @@ func parseOptOnDemand(d *caddyfile.Dispenser) (interface{}, error) {
|
|||
if !d.NextArg() {
|
||||
return nil, d.ArgErr()
|
||||
}
|
||||
dur, err := time.ParseDuration(d.Val())
|
||||
dur, err := caddy.ParseDuration(d.Val())
|
||||
if err != nil {
|
||||
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
|
||||
// not in the flag set.
|
||||
func (f Flags) Duration(name string) time.Duration {
|
||||
val, _ := time.ParseDuration(f.String(name))
|
||||
val, _ := caddy.ParseDuration(f.String(name))
|
||||
return val
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ import (
|
|||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
||||
|
@ -250,7 +249,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
|||
if h.LoadBalancing == nil {
|
||||
h.LoadBalancing = new(LoadBalancing)
|
||||
}
|
||||
dur, err := time.ParseDuration(d.Val())
|
||||
dur, err := caddy.ParseDuration(d.Val())
|
||||
if err != nil {
|
||||
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 {
|
||||
h.LoadBalancing = new(LoadBalancing)
|
||||
}
|
||||
dur, err := time.ParseDuration(d.Val())
|
||||
dur, err := caddy.ParseDuration(d.Val())
|
||||
if err != nil {
|
||||
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 {
|
||||
h.HealthChecks.Active = new(ActiveHealthChecks)
|
||||
}
|
||||
dur, err := time.ParseDuration(d.Val())
|
||||
dur, err := caddy.ParseDuration(d.Val())
|
||||
if err != nil {
|
||||
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 {
|
||||
h.HealthChecks.Active = new(ActiveHealthChecks)
|
||||
}
|
||||
dur, err := time.ParseDuration(d.Val())
|
||||
dur, err := caddy.ParseDuration(d.Val())
|
||||
if err != nil {
|
||||
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 {
|
||||
h.HealthChecks.Passive = new(PassiveHealthChecks)
|
||||
}
|
||||
dur, err := time.ParseDuration(d.Val())
|
||||
dur, err := caddy.ParseDuration(d.Val())
|
||||
if err != nil {
|
||||
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 {
|
||||
h.HealthChecks.Passive = new(PassiveHealthChecks)
|
||||
}
|
||||
dur, err := time.ParseDuration(d.Val())
|
||||
dur, err := caddy.ParseDuration(d.Val())
|
||||
if err != nil {
|
||||
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 {
|
||||
h.FlushInterval = caddy.Duration(fi)
|
||||
} else {
|
||||
dur, err := time.ParseDuration(d.Val())
|
||||
dur, err := caddy.ParseDuration(d.Val())
|
||||
if err != nil {
|
||||
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() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
dur, err := time.ParseDuration(d.Val())
|
||||
dur, err := caddy.ParseDuration(d.Val())
|
||||
if err != nil {
|
||||
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() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
dur, err := time.ParseDuration(d.Val())
|
||||
dur, err := caddy.ParseDuration(d.Val())
|
||||
if err != nil {
|
||||
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
|
||||
break
|
||||
}
|
||||
dur, err := time.ParseDuration(d.Val())
|
||||
dur, err := caddy.ParseDuration(d.Val())
|
||||
if err != nil {
|
||||
return d.Errf("bad duration value '%s': %v", d.Val(), err)
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
|
@ -194,7 +193,7 @@ func (fw *FileWriter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
|||
if !d.AllArgs(&keepForStr) {
|
||||
return d.ArgErr()
|
||||
}
|
||||
keepFor, err := time.ParseDuration(keepForStr)
|
||||
keepFor, err := caddy.ParseDuration(keepForStr)
|
||||
if err != nil {
|
||||
return d.Errf("parsing roll_keep_for duration: %v", err)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue