diff --git a/client/web/web.go b/client/web/web.go index dbd3d5df0..39d85ce1f 100644 --- a/client/web/web.go +++ b/client/web/web.go @@ -706,7 +706,7 @@ func (s *Server) serveAPI(w http.ResponseWriter, r *http.Request) { type authResponse struct { ServerMode ServerMode `json:"serverMode"` Authorized bool `json:"authorized"` // has an authorized management session - ViewerIdentity *viewerIdentity `json:"viewerIdentity,omitempty"` + ViewerIdentity *viewerIdentity `json:"viewerIdentity,omitzero"` NeedsSynoAuth bool `json:"needsSynoAuth,omitempty"` } diff --git a/cmd/k8s-operator/ingress-for-pg.go b/cmd/k8s-operator/ingress-for-pg.go index 3afeb528f..f1414a8bd 100644 --- a/cmd/k8s-operator/ingress-for-pg.go +++ b/cmd/k8s-operator/ingress-for-pg.go @@ -860,7 +860,7 @@ type ownerAnnotationValue struct { type OwnerRef struct { // OperatorID is the stable ID of the operator's Tailscale device. OperatorID string `json:"operatorID,omitempty"` - Resource *Resource `json:"resource,omitempty"` // optional, used to identify the ProxyGroup that owns this Tailscale Service. + Resource *Resource `json:"resource,omitzero"` // optional, used to identify the ProxyGroup that owns this Tailscale Service. } type Resource struct { diff --git a/cmd/tailscale/cli/configure-synology-cert.go b/cmd/tailscale/cli/configure-synology-cert.go index b5168ef92..e219e75f3 100644 --- a/cmd/tailscale/cli/configure-synology-cert.go +++ b/cmd/tailscale/cli/configure-synology-cert.go @@ -194,7 +194,7 @@ type synoAPICaller interface { type apiResponse struct { Success bool `json:"success"` - Error *apiError `json:"error,omitempty"` + Error *apiError `json:"error,omitzero"` Data json.RawMessage `json:"data"` } diff --git a/cmd/vet/jsontags_allowlist b/cmd/vet/jsontags_allowlist index 060a81b05..68f05f3ef 100644 --- a/cmd/vet/jsontags_allowlist +++ b/cmd/vet/jsontags_allowlist @@ -1,39 +1,3 @@ -OmitEmptyShouldBeOmitZero tailscale.com/client/web.authResponse.ViewerIdentity -OmitEmptyShouldBeOmitZero tailscale.com/cmd/k8s-operator.OwnerRef.Resource -OmitEmptyShouldBeOmitZero tailscale.com/cmd/tailscale/cli.apiResponse.Error -OmitEmptyShouldBeOmitZero tailscale.com/health.UnhealthyState.PrimaryAction -OmitEmptyShouldBeOmitZero tailscale.com/internal/client/tailscale.VIPService.Name -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.AcceptDNS -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.AcceptRoutes -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.AllowLANWhileUsingExitNode -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.AppConnector -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.AuthKey -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.AutoUpdate -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.DisableSNAT -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.Enabled -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.ExitNode -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.Hostname -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.Locked -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.NetfilterMode -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.NoStatefulFiltering -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.OperatorUser -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.PostureChecking -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.RunSSHServer -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.RunWebClient -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.ServeConfigTemp -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.ServerURL -OmitEmptyShouldBeOmitZero tailscale.com/ipn.ConfigVAlpha.ShieldsUp -OmitEmptyShouldBeOmitZero tailscale.com/ipn.OutgoingFile.PeerID -OmitEmptyShouldBeOmitZero tailscale.com/ipn.Prefs.AutoExitNode -OmitEmptyShouldBeOmitZero tailscale.com/ipn.Prefs.NoStatefulFiltering -OmitEmptyShouldBeOmitZero tailscale.com/ipn.Prefs.RelayServerPort -OmitEmptyShouldBeOmitZero tailscale.com/ipn/auditlog.transaction.Action -OmitEmptyShouldBeOmitZero tailscale.com/ipn/ipnstate.PeerStatus.AllowedIPs -OmitEmptyShouldBeOmitZero tailscale.com/ipn/ipnstate.PeerStatus.Location -OmitEmptyShouldBeOmitZero tailscale.com/ipn/ipnstate.PeerStatus.PrimaryRoutes -OmitEmptyShouldBeOmitZero tailscale.com/ipn/ipnstate.PeerStatus.Tags -OmitEmptyShouldBeOmitZero tailscale.com/ipn/ipnstate.Status.ExitNodeStatus -OmitEmptyShouldBeOmitZero tailscale.com/ipn/ipnstate.UpdateProgress.Status OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.ConnectorSpec.AppConnector OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.ConnectorSpec.Hostname OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.ConnectorSpec.HostnamePrefix @@ -65,71 +29,9 @@ OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.RecorderPod.A OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.RecorderPod.SecurityContext OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.StatefulSet.Pod OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.Storage.S3 -OmitEmptyShouldBeOmitZero tailscale.com/kube/ingressservices.Config.IPv4Mapping -OmitEmptyShouldBeOmitZero tailscale.com/kube/ingressservices.Config.IPv6Mapping -OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.APIServerProxyConfig.Enabled -OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.APIServerProxyConfig.IssueCerts -OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.APIServerProxyConfig.Mode -OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.APIServerProxyConfig.ServiceName -OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.AcceptRoutes -OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.APIServerProxy -OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.App -OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.AuthKey -OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.HealthCheckEnabled -OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.Hostname -OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.LocalAddr -OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.LocalPort -OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.LogLevel -OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.MetricsEnabled -OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.ServerURL -OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.ConfigV1Alpha1.State -OmitEmptyShouldBeOmitZero tailscale.com/kube/k8s-proxy/conf.VersionedConfig.V1Alpha1 OmitEmptyShouldBeOmitZero tailscale.com/kube/kubeapi.ObjectMeta.DeletionGracePeriodSeconds OmitEmptyShouldBeOmitZero tailscale.com/kube/kubeapi.Status.Details -OmitEmptyShouldBeOmitZero tailscale.com/kube/kubeclient.JSONPatch.Value -OmitEmptyShouldBeOmitZero tailscale.com/kube/kubetypes.*.Mode -OmitEmptyShouldBeOmitZero tailscale.com/kube/kubetypes.KubernetesCapRule.Impersonate -OmitEmptyShouldBeOmitZero tailscale.com/sessionrecording.CastHeader.Kubernetes -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.AuditLogRequest.Action -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Debug.Exit -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.DERPMap.HomeParams -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.DisplayMessage.PrimaryAction -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Hostinfo.AppConnector -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Hostinfo.Container -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Hostinfo.Desktop -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Hostinfo.Location -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Hostinfo.NetInfo -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Hostinfo.StateEncrypted -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Hostinfo.TPM -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Hostinfo.Userspace -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Hostinfo.UserspaceRouter -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.ClientVersion -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.CollectServices -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.ControlDialPlan -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.Debug -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.DefaultAutoUpdate -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.DERPMap -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.DNSConfig -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.Node -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.PingRequest -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.SSHPolicy -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.MapResponse.TKAInfo -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.NetPortRange.Bits -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Node.Online -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Node.SelfNodeV4MasqAddrForThisPeer -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.Node.SelfNodeV6MasqAddrForThisPeer -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.PeerChange.Online -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.RegisterRequest.Auth -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.RegisterResponseAuth.Oauth2Token -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.SSHAction.OnRecordingFailure -OmitEmptyShouldBeOmitZero tailscale.com/tailcfg.SSHPrincipal.Node OmitEmptyShouldBeOmitZero tailscale.com/tempfork/acme.*.ExternalAccountBinding -OmitEmptyShouldBeOmitZero tailscale.com/tsweb.AccessLogRecord.RequestID -OmitEmptyShouldBeOmitZero tailscale.com/types/opt.*.Unset -OmitEmptyShouldBeOmitZero tailscale.com/types/views.viewStruct.AddrsPtr -OmitEmptyShouldBeOmitZero tailscale.com/types/views.viewStruct.StringsPtr -OmitEmptyShouldBeOmitZero tailscale.com/wgengine/magicsock.EndpointChange.From -OmitEmptyShouldBeOmitZero tailscale.com/wgengine/magicsock.EndpointChange.To OmitEmptyShouldBeOmitZeroButHasIsZero tailscale.com/types/persist.Persist.AttestationKey OmitEmptyUnsupportedInV1 tailscale.com/client/tailscale.KeyCapabilities.Devices OmitEmptyUnsupportedInV1 tailscale.com/client/tailscale/apitype.ExitNodeSuggestionResponse.Location diff --git a/health/state.go b/health/state.go index e6d937b6a..3ce78c653 100644 --- a/health/state.go +++ b/health/state.go @@ -41,7 +41,7 @@ type UnhealthyState struct { Args Args `json:",omitempty"` DependsOn []WarnableCode `json:",omitempty"` ImpactsConnectivity bool `json:",omitempty"` - PrimaryAction *UnhealthyStateAction `json:",omitempty"` + PrimaryAction *UnhealthyStateAction `json:",omitzero"` // ETag identifies a specific version of an UnhealthyState. If the contents // of the other fields of two UnhealthyStates are the same, the ETags will diff --git a/internal/client/tailscale/vip_service.go b/internal/client/tailscale/vip_service.go index 48c59ce45..e47c60654 100644 --- a/internal/client/tailscale/vip_service.go +++ b/internal/client/tailscale/vip_service.go @@ -17,7 +17,7 @@ import ( // VIPService is a Tailscale VIPService with Tailscale API JSON representation. type VIPService struct { // Name is a VIPService name in form svc:. - Name tailcfg.ServiceName `json:"name,omitempty"` + Name tailcfg.ServiceName `json:"name,omitzero"` // Addrs are the IP addresses of the VIP Service. There are two addresses: // the first is IPv4 and the second is IPv6. // When creating a new VIP Service, the IP addresses are optional: if no diff --git a/ipn/auditlog/auditlog.go b/ipn/auditlog/auditlog.go index 0460bc4e2..6fb3e4242 100644 --- a/ipn/auditlog/auditlog.go +++ b/ipn/auditlog/auditlog.go @@ -30,7 +30,7 @@ type transaction struct { Retries int `json:",omitempty"` // Action is the action to be logged. It must correspond to a known action in the control plane. - Action tailcfg.ClientAuditAction `json:",omitempty"` + Action tailcfg.ClientAuditAction `json:",omitzero"` // Details is an opaque string specific to the action being logged. Empty strings may not // be valid depending on the action being logged. Details string `json:",omitempty"` diff --git a/ipn/backend.go b/ipn/backend.go index 91cf81ca5..2b1ee6c78 100644 --- a/ipn/backend.go +++ b/ipn/backend.go @@ -234,7 +234,7 @@ type PartialFile struct { // OutgoingFile represents an in-progress outgoing file transfer. type OutgoingFile struct { ID string `json:",omitempty"` // unique identifier for this transfer (a type 4 UUID) - PeerID tailcfg.StableNodeID `json:",omitempty"` // identifier for the peer to which this is being transferred + PeerID tailcfg.StableNodeID `json:",omitzero"` // identifier for the peer to which this is being transferred Name string `json:",omitempty"` // e.g. "foo.jpg" Started time.Time // time transfer started DeclaredSize int64 // or -1 if unknown diff --git a/ipn/conf.go b/ipn/conf.go index 2c9fb2fd1..f75c8bdfe 100644 --- a/ipn/conf.go +++ b/ipn/conf.go @@ -14,37 +14,37 @@ import ( // ConfigVAlpha is the config file format for the "alpha0" version. type ConfigVAlpha struct { Version string // "alpha0" for now - Locked opt.Bool `json:",omitempty"` // whether the config is locked from being changed by 'tailscale set'; it defaults to true + Locked opt.Bool `json:",omitzero"` // whether the config is locked from being changed by 'tailscale set'; it defaults to true - ServerURL *string `json:",omitempty"` // defaults to https://controlplane.tailscale.com - AuthKey *string `json:",omitempty"` // as needed if NeedsLogin. either key or path to a file (if prefixed with "file:") - Enabled opt.Bool `json:",omitempty"` // wantRunning; empty string defaults to true + ServerURL *string `json:",omitzero"` // defaults to https://controlplane.tailscale.com + AuthKey *string `json:",omitzero"` // as needed if NeedsLogin. either key or path to a file (if prefixed with "file:") + Enabled opt.Bool `json:",omitzero"` // wantRunning; empty string defaults to true - OperatorUser *string `json:",omitempty"` // local user name who is allowed to operate tailscaled without being root or using sudo - Hostname *string `json:",omitempty"` + OperatorUser *string `json:",omitzero"` // local user name who is allowed to operate tailscaled without being root or using sudo + Hostname *string `json:",omitzero"` - AcceptDNS opt.Bool `json:"acceptDNS,omitempty"` // --accept-dns - AcceptRoutes opt.Bool `json:"acceptRoutes,omitempty"` // --accept-routes defaults to true + AcceptDNS opt.Bool `json:"acceptDNS,omitzero"` // --accept-dns + AcceptRoutes opt.Bool `json:"acceptRoutes,omitzero"` // --accept-routes defaults to true - ExitNode *string `json:"exitNode,omitempty"` // IP, StableID, or MagicDNS base name - AllowLANWhileUsingExitNode opt.Bool `json:"allowLANWhileUsingExitNode,omitempty"` + ExitNode *string `json:"exitNode,omitzero"` // IP, StableID, or MagicDNS base name + AllowLANWhileUsingExitNode opt.Bool `json:"allowLANWhileUsingExitNode,omitzero"` AdvertiseRoutes []netip.Prefix `json:",omitempty"` - DisableSNAT opt.Bool `json:",omitempty"` + DisableSNAT opt.Bool `json:",omitzero"` AdvertiseServices []string `json:",omitempty"` - AppConnector *AppConnectorPrefs `json:",omitempty"` // advertise app connector; defaults to false (if nil or explicitly set to false) + AppConnector *AppConnectorPrefs `json:",omitzero"` // advertise app connector; defaults to false (if nil or explicitly set to false) - NetfilterMode *string `json:",omitempty"` // "on", "off", "nodivert" - NoStatefulFiltering opt.Bool `json:",omitempty"` + NetfilterMode *string `json:",omitzero"` // "on", "off", "nodivert" + NoStatefulFiltering opt.Bool `json:",omitzero"` - PostureChecking opt.Bool `json:",omitempty"` - RunSSHServer opt.Bool `json:",omitempty"` // Tailscale SSH - RunWebClient opt.Bool `json:",omitempty"` - ShieldsUp opt.Bool `json:",omitempty"` - AutoUpdate *AutoUpdatePrefs `json:",omitempty"` - ServeConfigTemp *ServeConfig `json:",omitempty"` // TODO(bradfitz,maisem): make separate stable type for this + PostureChecking opt.Bool `json:",omitzero"` + RunSSHServer opt.Bool `json:",omitzero"` // Tailscale SSH + RunWebClient opt.Bool `json:",omitzero"` + ShieldsUp opt.Bool `json:",omitzero"` + AutoUpdate *AutoUpdatePrefs `json:",omitzero"` + ServeConfigTemp *ServeConfig `json:",omitzero"` // TODO(bradfitz,maisem): make separate stable type for this // StaticEndpoints are additional, user-defined endpoints that this node // should advertise amongst its wireguard endpoints. diff --git a/ipn/ipnstate/ipnstate.go b/ipn/ipnstate/ipnstate.go index e7ae2d62b..ec9ac6356 100644 --- a/ipn/ipnstate/ipnstate.go +++ b/ipn/ipnstate/ipnstate.go @@ -51,7 +51,7 @@ type Status struct { // ExitNodeStatus describes the current exit node. // If nil, an exit node is not in use. - ExitNodeStatus *ExitNodeStatus `json:"ExitNodeStatus,omitempty"` + ExitNodeStatus *ExitNodeStatus `json:"ExitNodeStatus,omitzero"` // Health contains health check problems. // Empty means everything is good. (or at least that no known @@ -239,16 +239,16 @@ type PeerStatus struct { // TailscaleIPs are the IP addresses assigned to the node. TailscaleIPs []netip.Addr // AllowedIPs are IP addresses allowed to route to this node. - AllowedIPs *views.Slice[netip.Prefix] `json:",omitempty"` + AllowedIPs *views.Slice[netip.Prefix] `json:",omitzero"` // Tags are the list of ACL tags applied to this node. // See tailscale.com/tailcfg#Node.Tags for more information. - Tags *views.Slice[string] `json:",omitempty"` + Tags *views.Slice[string] `json:",omitzero"` // PrimaryRoutes are the routes this node is currently the primary // subnet router for, as determined by the control plane. It does // not include the IPs in TailscaleIPs. - PrimaryRoutes *views.Slice[netip.Prefix] `json:",omitempty"` + PrimaryRoutes *views.Slice[netip.Prefix] `json:",omitzero"` // Endpoints: Addrs []string @@ -327,7 +327,7 @@ type PeerStatus struct { // will expire. KeyExpiry *time.Time `json:",omitempty"` - Location *tailcfg.Location `json:",omitempty"` + Location *tailcfg.Location `json:",omitzero"` } type TaildropTargetStatus int @@ -796,7 +796,7 @@ const ( ) type UpdateProgress struct { - Status SelfUpdateStatus `json:"status,omitempty"` + Status SelfUpdateStatus `json:"status,omitzero"` Message string `json:"message,omitempty"` Version string `json:"version,omitempty"` } diff --git a/ipn/prefs.go b/ipn/prefs.go index 81dd1c1c3..b7e622d6c 100644 --- a/ipn/prefs.go +++ b/ipn/prefs.go @@ -114,7 +114,7 @@ type Prefs struct { // As of 2025-07-02, the only supported value is [AnyExitNode]. // It's a string rather than a boolean to allow future extensibility // (e.g., AutoExitNode = "mullvad" or AutoExitNode = "geo:us"). - AutoExitNode ExitNodeExpression `json:",omitempty"` + AutoExitNode ExitNodeExpression `json:",omitzero"` // InternalExitNodePrior is the most recently used ExitNodeID in string form. It is set by // the backend on transition from exit node on to off and used by the @@ -231,7 +231,7 @@ type Prefs struct { // removed since then, but the field remains an opt.Bool. // // Linux-only. - NoStatefulFiltering opt.Bool `json:",omitempty"` + NoStatefulFiltering opt.Bool `json:",omitzero"` // NetfilterMode specifies how much to manage netfilter rules for // Tailscale, if at all. @@ -280,7 +280,7 @@ type Prefs struct { // should be disabled. This field is currently experimental, and therefore // no guarantees are made about its current naming and functionality when // non-nil/enabled. - RelayServerPort *int `json:",omitempty"` + RelayServerPort *int `json:",omitzero"` // AllowSingleHosts was a legacy field that was always true // for the past 4.5 years. It controlled whether Tailscale diff --git a/kube/ingressservices/ingressservices.go b/kube/ingressservices/ingressservices.go index f79410761..7aeccc7ed 100644 --- a/kube/ingressservices/ingressservices.go +++ b/kube/ingressservices/ingressservices.go @@ -41,8 +41,8 @@ type Status struct { // Config is an ingress service configuration. type Config struct { - IPv4Mapping *Mapping `json:"IPv4Mapping,omitempty"` - IPv6Mapping *Mapping `json:"IPv6Mapping,omitempty"` + IPv4Mapping *Mapping `json:"IPv4Mapping,omitzero"` + IPv6Mapping *Mapping `json:"IPv6Mapping,omitzero"` } // Mapping describes a rule that forwards traffic from Tailscale Service IP to a diff --git a/kube/k8s-proxy/conf/conf.go b/kube/k8s-proxy/conf/conf.go index 529495243..a607449e8 100644 --- a/kube/k8s-proxy/conf/conf.go +++ b/kube/k8s-proxy/conf/conf.go @@ -44,35 +44,35 @@ type VersionedConfig struct { // Backwards compatibility version(s) of the config. Fields and sub-fields // from here should only be added to, never changed in place. - V1Alpha1 *ConfigV1Alpha1 `json:",omitempty"` + V1Alpha1 *ConfigV1Alpha1 `json:",omitzero"` // V1Beta1 *ConfigV1Beta1 `json:",omitempty"` // Not yet used. } type ConfigV1Alpha1 struct { - AuthKey *string `json:",omitempty"` // Tailscale auth key to use. - State *string `json:",omitempty"` // Path to the Tailscale state. - LogLevel *string `json:",omitempty"` // "debug", "info". Defaults to "info". - App *string `json:",omitempty"` // e.g. kubetypes.AppProxyGroupKubeAPIServer - ServerURL *string `json:",omitempty"` // URL of the Tailscale coordination server. - LocalAddr *string `json:",omitempty"` // The address to use for serving HTTP health checks and metrics (defaults to all interfaces). - LocalPort *uint16 `json:",omitempty"` // The port to use for serving HTTP health checks and metrics (defaults to 9002). - MetricsEnabled opt.Bool `json:",omitempty"` // Serve metrics on :/metrics. - HealthCheckEnabled opt.Bool `json:",omitempty"` // Serve health check on :/metrics. + AuthKey *string `json:",omitzero"` // Tailscale auth key to use. + State *string `json:",omitzero"` // Path to the Tailscale state. + LogLevel *string `json:",omitzero"` // "debug", "info". Defaults to "info". + App *string `json:",omitzero"` // e.g. kubetypes.AppProxyGroupKubeAPIServer + ServerURL *string `json:",omitzero"` // URL of the Tailscale coordination server. + LocalAddr *string `json:",omitzero"` // The address to use for serving HTTP health checks and metrics (defaults to all interfaces). + LocalPort *uint16 `json:",omitzero"` // The port to use for serving HTTP health checks and metrics (defaults to 9002). + MetricsEnabled opt.Bool `json:",omitzero"` // Serve metrics on :/metrics. + HealthCheckEnabled opt.Bool `json:",omitzero"` // Serve health check on :/metrics. // TODO(tomhjp): The remaining fields should all be reloadable during // runtime, but currently missing most of the APIServerProxy fields. - Hostname *string `json:",omitempty"` // Tailscale device hostname. - AcceptRoutes opt.Bool `json:",omitempty"` // Accepts routes advertised by other Tailscale nodes. + Hostname *string `json:",omitzero"` // Tailscale device hostname. + AcceptRoutes opt.Bool `json:",omitzero"` // Accepts routes advertised by other Tailscale nodes. AdvertiseServices []string `json:",omitempty"` // Tailscale Services to advertise. - APIServerProxy *APIServerProxyConfig `json:",omitempty"` // Config specific to the API Server proxy. + APIServerProxy *APIServerProxyConfig `json:",omitzero"` // Config specific to the API Server proxy. StaticEndpoints []netip.AddrPort `json:",omitempty"` // StaticEndpoints are additional, user-defined endpoints that this node should advertise amongst its wireguard endpoints. } type APIServerProxyConfig struct { - Enabled opt.Bool `json:",omitempty"` // Whether to enable the API Server proxy. - Mode *kubetypes.APIServerProxyMode `json:",omitempty"` // "auth" or "noauth" mode. - ServiceName *tailcfg.ServiceName `json:",omitempty"` // Name of the Tailscale Service to advertise. - IssueCerts opt.Bool `json:",omitempty"` // Whether this replica should issue TLS certs for the Tailscale Service. + Enabled opt.Bool `json:",omitzero"` // Whether to enable the API Server proxy. + Mode *kubetypes.APIServerProxyMode `json:",omitzero"` // "auth" or "noauth" mode. + ServiceName *tailcfg.ServiceName `json:",omitzero"` // Name of the Tailscale Service to advertise. + IssueCerts opt.Bool `json:",omitzero"` // Whether this replica should issue TLS certs for the Tailscale Service. } // Load reads and parses the config file at the provided path on disk. diff --git a/kube/kubeclient/client.go b/kube/kubeclient/client.go index 0ed960f4d..89b14da5d 100644 --- a/kube/kubeclient/client.go +++ b/kube/kubeclient/client.go @@ -287,7 +287,7 @@ func (c *client) UpdateSecret(ctx context.Context, s *kubeapi.Secret) error { type JSONPatch struct { Op string `json:"op"` Path string `json:"path"` - Value any `json:"value,omitempty"` + Value any `json:"value,omitzero"` } // JSONPatchResource updates a resource in the Kubernetes API using a JSON patch. diff --git a/kube/kubetypes/grants.go b/kube/kubetypes/grants.go index 4dc278ff1..18642f2dc 100644 --- a/kube/kubetypes/grants.go +++ b/kube/kubetypes/grants.go @@ -14,7 +14,7 @@ import "net/netip" type KubernetesCapRule struct { // Impersonate is a list of rules that specify how to impersonate the caller // when proxying to the Kubernetes API. - Impersonate *ImpersonateRule `json:"impersonate,omitempty"` + Impersonate *ImpersonateRule `json:"impersonate,omitzero"` // Recorders defines a tag of a tsrecorder instance(s) that a recording // of a 'kubectl exec' session, matching `src` of this grant, to an API // server proxy, matching `dst` of this grant, should be sent to. diff --git a/kube/kubetypes/types_test.go b/kube/kubetypes/types_test.go index ea1846b32..9d328e0b2 100644 --- a/kube/kubetypes/types_test.go +++ b/kube/kubetypes/types_test.go @@ -22,7 +22,7 @@ func TestUnmarshalAPIServerProxyMode(t *testing.T) { for _, tc := range tests { var s struct { - Mode *APIServerProxyMode `json:",omitempty"` + Mode *APIServerProxyMode `json:",omitzero"` } err := json.Unmarshal([]byte(tc.data), &s) if tc.expected == "" { diff --git a/sessionrecording/header.go b/sessionrecording/header.go index 220852216..816f54f76 100644 --- a/sessionrecording/header.go +++ b/sessionrecording/header.go @@ -62,7 +62,7 @@ type CastHeader struct { ConnectionID string `json:"connectionID"` // Fields that are only set for Kubernetes API server proxy session recordings: - Kubernetes *Kubernetes `json:"kubernetes,omitempty"` + Kubernetes *Kubernetes `json:"kubernetes,omitzero"` } // Kubernetes contains 'kubectl exec/attach' session specific information for diff --git a/tailcfg/derpmap.go b/tailcfg/derpmap.go index e05559f3e..97c10640b 100644 --- a/tailcfg/derpmap.go +++ b/tailcfg/derpmap.go @@ -15,7 +15,7 @@ type DERPMap struct { // HomeParams, if non-nil, is a change in home parameters. // // The rest of the DEPRMap fields, if zero, means unchanged. - HomeParams *DERPHomeParams `json:",omitempty"` + HomeParams *DERPHomeParams `json:",omitzero"` // Regions is the set of geographic regions running DERP node(s). // diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index a95d0559c..e45f7b8c2 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -412,7 +412,7 @@ type Node struct { // Online is whether the node is currently connected to the // coordination server. A value of nil means unknown, or the // current node doesn't have permission to know. - Online *bool `json:",omitempty"` + Online *bool `json:",omitzero"` MachineAuthorized bool `json:",omitempty"` // TODO(crawshaw): replace with MachineStatus @@ -486,7 +486,7 @@ type Node struct { // This only applies to traffic originating from the current node to the // peer or any of its subnets. Traffic originating from subnet routes will // not be masqueraded (e.g. in case of --snat-subnet-routes). - SelfNodeV4MasqAddrForThisPeer *netip.Addr `json:",omitempty"` + SelfNodeV4MasqAddrForThisPeer *netip.Addr `json:",omitzero"` // SelfNodeV6MasqAddrForThisPeer is the IPv6 that this peer knows the current node as. // It may be empty if the peer knows the current node by its native @@ -501,7 +501,7 @@ type Node struct { // This only applies to traffic originating from the current node to the // peer or any of its subnets. Traffic originating from subnet routes will // not be masqueraded (e.g. in case of --snat-subnet-routes). - SelfNodeV6MasqAddrForThisPeer *netip.Addr `json:",omitempty"` + SelfNodeV6MasqAddrForThisPeer *netip.Addr `json:",omitzero"` // IsWireGuardOnly indicates that this is a non-Tailscale WireGuard peer, it // is not expected to speak Disco or DERP, and it must have Endpoints in @@ -844,7 +844,7 @@ type Hostinfo struct { // "5.10.0-17-amd64". OSVersion string `json:",omitempty"` - Container opt.Bool `json:",omitempty"` // best-effort whether the client is running in a container + Container opt.Bool `json:",omitzero"` // best-effort whether the client is running in a container Env string `json:",omitempty"` // a hostinfo.EnvType in string form Distro string `json:",omitempty"` // "debian", "ubuntu", "nixos", ... DistroVersion string `json:",omitempty"` // "20.04", ... @@ -853,7 +853,7 @@ type Hostinfo struct { // App is used to disambiguate Tailscale clients that run using tsnet. App string `json:",omitempty"` // "k8s-operator", "golinks", ... - Desktop opt.Bool `json:",omitempty"` // if a desktop was detected on Linux + Desktop opt.Bool `json:",omitzero"` // if a desktop was detected on Linux Package string `json:",omitempty"` // Tailscale package to disambiguate ("choco", "appstore", etc; "" for unknown) DeviceModel string `json:",omitempty"` // mobile phone model ("Pixel 3a", "iPhone12,3") PushDeviceToken string `json:",omitempty"` // macOS/iOS APNs device token for notifications (and Android in the future) @@ -879,27 +879,27 @@ type Hostinfo struct { RequestTags []string `json:",omitempty"` // set of ACL tags this node wants to claim WoLMACs []string `json:",omitempty"` // MAC address(es) to send Wake-on-LAN packets to wake this node (lowercase hex w/ colons) Services []Service `json:",omitempty"` // services advertised by this machine - NetInfo *NetInfo `json:",omitempty"` + NetInfo *NetInfo `json:",omitzero"` SSH_HostKeys []string `json:"sshHostKeys,omitempty"` // if advertised Cloud string `json:",omitempty"` - Userspace opt.Bool `json:",omitempty"` // if the client is running in userspace (netstack) mode - UserspaceRouter opt.Bool `json:",omitempty"` // if the client's subnet router is running in userspace (netstack) mode - AppConnector opt.Bool `json:",omitempty"` // if the client is running the app-connector service + Userspace opt.Bool `json:",omitzero"` // if the client is running in userspace (netstack) mode + UserspaceRouter opt.Bool `json:",omitzero"` // if the client's subnet router is running in userspace (netstack) mode + AppConnector opt.Bool `json:",omitzero"` // if the client is running the app-connector service ServicesHash string `json:",omitempty"` // opaque hash of the most recent list of tailnet services, change in hash indicates config should be fetched via c2n ExitNodeID StableNodeID `json:",omitzero"` // the client’s selected exit node, empty when unselected. // Location represents geographical location data about a // Tailscale host. Location is optional and only set if // explicitly declared by a node. - Location *Location `json:",omitempty"` + Location *Location `json:",omitzero"` - TPM *TPMInfo `json:",omitempty"` // TPM device metadata, if available + TPM *TPMInfo `json:",omitzero"` // TPM device metadata, if available // StateEncrypted reports whether the node state is stored encrypted on // disk. The actual mechanism is platform-specific: // * Apple nodes use the Keychain // * Linux and Windows nodes use the TPM // * Android apps use EncryptedSharedPreferences - StateEncrypted opt.Bool `json:",omitempty"` + StateEncrypted opt.Bool `json:",omitzero"` // NOTE: any new fields containing pointers in this type // require changes to Hostinfo.Equal. @@ -1234,7 +1234,7 @@ type RegisterResponseAuth struct { // At most one of Oauth2Token or AuthKey is set. - Oauth2Token *Oauth2Token `json:",omitempty"` // used by pre-1.66 Android only + Oauth2Token *Oauth2Token `json:",omitzero"` // used by pre-1.66 Android only AuthKey string `json:",omitempty"` } @@ -1256,7 +1256,7 @@ type RegisterRequest struct { NodeKey key.NodePublic OldNodeKey key.NodePublic NLKey key.NLPublic - Auth *RegisterResponseAuth `json:",omitempty"` + Auth *RegisterResponseAuth `json:",omitzero"` // Expiry optionally specifies the requested key expiry. // The server policy may override. // As a special case, if Expiry is in the past and NodeKey is @@ -1501,7 +1501,7 @@ func (pr PortRange) String() string { type NetPortRange struct { _ structs.Incomparable IP string // IP, CIDR, Range, or "*" (same formats as FilterRule.SrcIPs) - Bits *int `json:",omitempty"` // deprecated; the 2020 way to turn IP into a CIDR. See FilterRule.SrcBits. + Bits *int `json:",omitzero"` // deprecated; the 2020 way to turn IP into a CIDR. See FilterRule.SrcBits. Ports PortRange } @@ -1978,7 +1978,7 @@ type MapResponse struct { // provided URL. No auth headers are necessary. // PingRequest may be sent on any MapResponse (ones with // KeepAlive true or false). - PingRequest *PingRequest `json:",omitempty"` + PingRequest *PingRequest `json:",omitzero"` // PopBrowserURL, if non-empty, is a URL for the client to // open to complete an action. The client should dup suppress @@ -1989,11 +1989,11 @@ type MapResponse struct { // Node describes the node making the map request. // Starting with MapRequest.Version 18, nil means unchanged. - Node *Node `json:",omitempty"` + Node *Node `json:",omitzero"` // DERPMap describe the set of DERP servers available. // A nil value means unchanged. - DERPMap *DERPMap `json:",omitempty"` + DERPMap *DERPMap `json:",omitzero"` // Peers, if non-empty, is the complete list of peers. // It will be set in the first MapResponse for a long-polled request/response. @@ -2030,7 +2030,7 @@ type MapResponse struct { // DNSConfig contains the DNS settings for the client to use. // A nil value means no change from an earlier non-nil value. - DNSConfig *DNSConfig `json:",omitempty"` + DNSConfig *DNSConfig `json:",omitzero"` // Domain is the name of the network that this node is // in. It's either of the form "example.com" (for user @@ -2045,7 +2045,7 @@ type MapResponse struct { // requested that info about services be included in HostInfo. // If unset, the most recent non-empty MapResponse value in // the HTTP response stream is used. - CollectServices opt.Bool `json:",omitempty"` + CollectServices opt.Bool `json:",omitzero"` // PacketFilter are the firewall rules. // @@ -2121,7 +2121,7 @@ type MapResponse struct { // SSHPolicy, if non-nil, updates the SSH policy for how incoming // SSH connections should be handled. - SSHPolicy *SSHPolicy `json:",omitempty"` + SSHPolicy *SSHPolicy `json:",omitzero"` // ControlTime, if non-zero, is the current timestamp according to the control server. ControlTime *time.Time `json:",omitempty"` @@ -2134,7 +2134,7 @@ type MapResponse struct { // indicates the control plane believes TKA should be enabled. // A nil TKAInfo in a mapresponse stream (i.e. a 'delta' mapresponse) // indicates no change from the value sent earlier. - TKAInfo *TKAInfo `json:",omitempty"` + TKAInfo *TKAInfo `json:",omitzero"` // DomainDataPlaneAuditLogID, if non-empty, is the per-tailnet log ID to be // used when writing data plane audit logs. @@ -2142,24 +2142,24 @@ type MapResponse struct { // Debug is normally nil, except for when the control server // is setting debug settings on a node. - Debug *Debug `json:",omitempty"` + Debug *Debug `json:",omitzero"` // ControlDialPlan tells the client how to connect to the control // server. An initial nil is equivalent to new(ControlDialPlan). // A subsequent streamed nil means no change. - ControlDialPlan *ControlDialPlan `json:",omitempty"` + ControlDialPlan *ControlDialPlan `json:",omitzero"` // ClientVersion describes the latest client version that's available for // download and whether the client is using it. A nil value means no change // or nothing to report. - ClientVersion *ClientVersion `json:",omitempty"` + ClientVersion *ClientVersion `json:",omitzero"` // DefaultAutoUpdate is the default node auto-update setting for this // tailnet. The node is free to opt-in or out locally regardless of this // value. This value is only used on first MapResponse from control, the // auto-update setting doesn't change if the tailnet admin flips the // default after the node registered. - DefaultAutoUpdate opt.Bool `json:",omitempty"` + DefaultAutoUpdate opt.Bool `json:",omitzero"` } // DisplayMessage represents a health state of the node from the control plane's @@ -2197,7 +2197,7 @@ type DisplayMessage struct { // take when interacting with this message. For example, if the // DisplayMessage is shown via a notification, the action label might be a // button on that notification and clicking the button would open the URL. - PrimaryAction *DisplayMessageAction `json:",omitempty"` + PrimaryAction *DisplayMessageAction `json:",omitzero"` } // DisplayMessageAction represents an action (URL and link) to be presented to @@ -2334,7 +2334,7 @@ type Debug struct { // Exit optionally specifies that the client should os.Exit // with this code. This is a safety measure in case a client is crash // looping or in an unsafe state and we need to remotely shut it down. - Exit *int `json:",omitempty"` + Exit *int `json:",omitzero"` } func (id ID) String() string { return fmt.Sprintf("id:%d", int64(id)) } @@ -2878,7 +2878,7 @@ type SSHPrincipal struct { // Matching any one of the following four field causes a match. // It must also match Certs, if non-empty. - Node StableNodeID `json:"node,omitempty"` + Node StableNodeID `json:"node,omitzero"` NodeIP string `json:"nodeIP,omitempty"` UserLogin string `json:"userLogin,omitempty"` // email-ish: foo@example.com, bar@github Any bool `json:"any,omitempty"` // if true, match any connection @@ -2951,7 +2951,7 @@ type SSHAction struct { // OnRecorderFailure is the action to take if recording fails. // If nil, the default action is to fail open. - OnRecordingFailure *SSHRecorderFailureAction `json:"onRecordingFailure,omitempty"` + OnRecordingFailure *SSHRecorderFailureAction `json:"onRecordingFailure,omitzero"` } // SSHRecorderFailureAction is the action to take if recording fails. @@ -3196,7 +3196,7 @@ type PeerChange struct { DiscoKey *key.DiscoPublic `json:",omitempty"` // Online, if non-nil, means that the NodeID's online status changed. - Online *bool `json:",omitempty"` + Online *bool `json:",omitzero"` // LastSeen, if non-nil, means that the NodeID's online status changed. LastSeen *time.Time `json:",omitempty"` @@ -3284,7 +3284,7 @@ type AuditLogRequest struct { // NodeKey is the client's current node key. NodeKey key.NodePublic `json:",omitzero"` // Action is the action to be logged. It must correspond to a known action in the control plane. - Action ClientAuditAction `json:",omitempty"` + Action ClientAuditAction `json:",omitzero"` // Details is an opaque string, specific to the action being logged. Empty strings may not // be valid depending on the action being logged. Details string `json:",omitempty"` diff --git a/tsweb/log.go b/tsweb/log.go index 51f95e95f..59dc29224 100644 --- a/tsweb/log.go +++ b/tsweb/log.go @@ -50,7 +50,7 @@ type AccessLogRecord struct { // client immediately after the error text, as well as logged here. This // makes it easier to correlate support requests with server logs. If a // RequestID generator is not configured, RequestID will be empty. - RequestID RequestID `json:"request_id,omitempty"` + RequestID RequestID `json:"request_id,omitzero"` } // String returns m as a JSON string. diff --git a/types/opt/bool_test.go b/types/opt/bool_test.go index dddbcfc19..dc2935683 100644 --- a/types/opt/bool_test.go +++ b/types/opt/bool_test.go @@ -44,7 +44,7 @@ func TestBool(t *testing.T) { in: struct { True Bool False Bool - Unset Bool `json:",omitempty"` + Unset Bool `json:",omitzero"` }{ True: "true", False: "false", diff --git a/types/views/views_test.go b/types/views/views_test.go index 5a30c11a1..7e49ea4a1 100644 --- a/types/views/views_test.go +++ b/types/views/views_test.go @@ -39,8 +39,8 @@ type viewStruct struct { Int int Addrs Slice[netip.Prefix] Strings Slice[string] - AddrsPtr *Slice[netip.Prefix] `json:",omitempty"` - StringsPtr *Slice[string] `json:",omitempty"` + AddrsPtr *Slice[netip.Prefix] `json:",omitzero"` + StringsPtr *Slice[string] `json:",omitzero"` } type noPtrStruct struct { diff --git a/wgengine/magicsock/endpoint.go b/wgengine/magicsock/endpoint.go index 2010775a1..48982aae7 100644 --- a/wgengine/magicsock/endpoint.go +++ b/wgengine/magicsock/endpoint.go @@ -428,8 +428,8 @@ type pongReply struct { type EndpointChange struct { When time.Time // when the change occurred What string // what this change is - From any `json:",omitempty"` // information about the previous state - To any `json:",omitempty"` // information about the new state + From any `json:",omitzero"` // information about the previous state + To any `json:",omitzero"` // information about the new state } // shouldDeleteLocked reports whether we should delete this endpoint.