mirror of
https://github.com/tailscale/tailscale.git
synced 2025-07-30 07:43:42 +00:00
util/syspolicy/setting: use a custom marshaler for time.Duration
jsonv2 now returns an error when you marshal or unmarshal a time.Duration without an explicit format flag. This is an intentional, temporary choice until the default [time.Duration] representation is decided (see golang/go#71631). setting.Snapshot can hold time.Duration values inside a map[string]any, so the jsonv2 update breaks marshaling. In this PR, we start using a custom marshaler until that decision is made or golang/go#71664 lets us specify the format explicitly. This fixes `tailscale syspolicy list` failing when KeyExpirationNotice or any other time.Duration policy setting is configured. Fixes #16683 Signed-off-by: Nick Khyl <nickk@tailscale.com>
This commit is contained in:
parent
a9f3fd1c67
commit
4df02bbb48
@ -9,6 +9,7 @@ import (
|
|||||||
"maps"
|
"maps"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
jsonv2 "github.com/go-json-experiment/json"
|
jsonv2 "github.com/go-json-experiment/json"
|
||||||
"github.com/go-json-experiment/json/jsontext"
|
"github.com/go-json-experiment/json/jsontext"
|
||||||
@ -152,6 +153,24 @@ var (
|
|||||||
_ jsonv2.UnmarshalerFrom = (*Snapshot)(nil)
|
_ jsonv2.UnmarshalerFrom = (*Snapshot)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// As of 2025-07-28, jsonv2 no longer has a default representation for [time.Duration],
|
||||||
|
// so we need to provide a custom marshaler.
|
||||||
|
//
|
||||||
|
// This is temporary until the decision on the default representation is made
|
||||||
|
// (see https://github.com/golang/go/issues/71631#issuecomment-2981670799).
|
||||||
|
//
|
||||||
|
// In the future, we might either use the default representation (if compatible with
|
||||||
|
// [time.Duration.String]) or specify something like json.WithFormat[time.Duration]("units")
|
||||||
|
// when golang/go#71664 is implemented.
|
||||||
|
//
|
||||||
|
// TODO(nickkhyl): revisit this when the decision on the default [time.Duration]
|
||||||
|
// representation is made in golang/go#71631 and/or golang/go#71664 is implemented.
|
||||||
|
var formatDurationAsUnits = jsonv2.JoinOptions(
|
||||||
|
jsonv2.WithMarshalers(jsonv2.MarshalToFunc(func(e *jsontext.Encoder, t time.Duration) error {
|
||||||
|
return e.WriteToken(jsontext.String(t.String()))
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
|
||||||
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
// MarshalJSONTo implements [jsonv2.MarshalerTo].
|
||||||
func (s *Snapshot) MarshalJSONTo(out *jsontext.Encoder) error {
|
func (s *Snapshot) MarshalJSONTo(out *jsontext.Encoder) error {
|
||||||
data := &snapshotJSON{}
|
data := &snapshotJSON{}
|
||||||
@ -159,7 +178,7 @@ func (s *Snapshot) MarshalJSONTo(out *jsontext.Encoder) error {
|
|||||||
data.Summary = s.summary
|
data.Summary = s.summary
|
||||||
data.Settings = s.m
|
data.Settings = s.m
|
||||||
}
|
}
|
||||||
return jsonv2.MarshalEncode(out, data)
|
return jsonv2.MarshalEncode(out, data, formatDurationAsUnits)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
||||||
|
@ -491,6 +491,18 @@ func TestMarshalUnmarshalSnapshot(t *testing.T) {
|
|||||||
snapshot: NewSnapshot(map[Key]RawItem{"ListPolicy": RawItemOf([]string{"Value1", "Value2"})}),
|
snapshot: NewSnapshot(map[Key]RawItem{"ListPolicy": RawItemOf([]string{"Value1", "Value2"})}),
|
||||||
wantJSON: `{"Settings": {"ListPolicy": {"Value": ["Value1", "Value2"]}}}`,
|
wantJSON: `{"Settings": {"ListPolicy": {"Value": ["Value1", "Value2"]}}}`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Duration/Zero",
|
||||||
|
snapshot: NewSnapshot(map[Key]RawItem{"DurationPolicy": RawItemOf(time.Duration(0))}),
|
||||||
|
wantJSON: `{"Settings": {"DurationPolicy": {"Value": "0s"}}}`,
|
||||||
|
wantBack: NewSnapshot(map[Key]RawItem{"DurationPolicy": RawItemOf("0s")}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Duration/NonZero",
|
||||||
|
snapshot: NewSnapshot(map[Key]RawItem{"DurationPolicy": RawItemOf(2 * time.Hour)}),
|
||||||
|
wantJSON: `{"Settings": {"DurationPolicy": {"Value": "2h0m0s"}}}`,
|
||||||
|
wantBack: NewSnapshot(map[Key]RawItem{"DurationPolicy": RawItemOf("2h0m0s")}),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Empty/With-Summary",
|
name: "Empty/With-Summary",
|
||||||
snapshot: NewSnapshot(
|
snapshot: NewSnapshot(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user