mirror of
https://github.com/tailscale/tailscale.git
synced 2025-07-29 15:23:45 +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> (cherry picked from commit 4df02bbb486d07b0ad23f59c4cb3675ab691e79b)
This commit is contained in:
parent
91d65e03e8
commit
4123469edf
@ -9,6 +9,7 @@ import (
|
||||
"maps"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
jsonv2 "github.com/go-json-experiment/json"
|
||||
"github.com/go-json-experiment/json/jsontext"
|
||||
@ -152,6 +153,24 @@ var (
|
||||
_ 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].
|
||||
func (s *Snapshot) MarshalJSONTo(out *jsontext.Encoder) error {
|
||||
data := &snapshotJSON{}
|
||||
@ -159,7 +178,7 @@ func (s *Snapshot) MarshalJSONTo(out *jsontext.Encoder) error {
|
||||
data.Summary = s.summary
|
||||
data.Settings = s.m
|
||||
}
|
||||
return jsonv2.MarshalEncode(out, data)
|
||||
return jsonv2.MarshalEncode(out, data, formatDurationAsUnits)
|
||||
}
|
||||
|
||||
// UnmarshalJSONFrom implements [jsonv2.UnmarshalerFrom].
|
||||
|
@ -491,6 +491,18 @@ func TestMarshalUnmarshalSnapshot(t *testing.T) {
|
||||
snapshot: NewSnapshot(map[Key]RawItem{"ListPolicy": RawItemOf([]string{"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",
|
||||
snapshot: NewSnapshot(
|
||||
|
Loading…
x
Reference in New Issue
Block a user