all: migrate omitempty to omitzero

These are statically discovered using #17670.

This migrates all usages of omitempty to instead use omitzero
where it is safe to do so. These should behave the same as before,
but allow the behavior to be identical between v1 and v2.

Updates tailscale/corp#791

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
dsnet/migrate-omitzero
Joe Tsai 4 weeks ago
parent 5b40f0bc54
commit fad54d7c20

@ -706,7 +706,7 @@ func (s *Server) serveAPI(w http.ResponseWriter, r *http.Request) {
type authResponse struct { type authResponse struct {
ServerMode ServerMode `json:"serverMode"` ServerMode ServerMode `json:"serverMode"`
Authorized bool `json:"authorized"` // has an authorized management session Authorized bool `json:"authorized"` // has an authorized management session
ViewerIdentity *viewerIdentity `json:"viewerIdentity,omitempty"` ViewerIdentity *viewerIdentity `json:"viewerIdentity,omitzero"`
NeedsSynoAuth bool `json:"needsSynoAuth,omitempty"` NeedsSynoAuth bool `json:"needsSynoAuth,omitempty"`
} }

@ -860,7 +860,7 @@ type ownerAnnotationValue struct {
type OwnerRef struct { type OwnerRef struct {
// OperatorID is the stable ID of the operator's Tailscale device. // OperatorID is the stable ID of the operator's Tailscale device.
OperatorID string `json:"operatorID,omitempty"` 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 { type Resource struct {

@ -194,7 +194,7 @@ type synoAPICaller interface {
type apiResponse struct { type apiResponse struct {
Success bool `json:"success"` Success bool `json:"success"`
Error *apiError `json:"error,omitempty"` Error *apiError `json:"error,omitzero"`
Data json.RawMessage `json:"data"` Data json.RawMessage `json:"data"`
} }

@ -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.AppConnector
OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.ConnectorSpec.Hostname OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.ConnectorSpec.Hostname
OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.ConnectorSpec.HostnamePrefix 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.RecorderPod.SecurityContext
OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.StatefulSet.Pod OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.StatefulSet.Pod
OmitEmptyShouldBeOmitZero tailscale.com/k8s-operator/apis/v1alpha1.Storage.S3 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.ObjectMeta.DeletionGracePeriodSeconds
OmitEmptyShouldBeOmitZero tailscale.com/kube/kubeapi.Status.Details 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/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 OmitEmptyShouldBeOmitZeroButHasIsZero tailscale.com/types/persist.Persist.AttestationKey
OmitEmptyUnsupportedInV1 tailscale.com/client/tailscale.KeyCapabilities.Devices OmitEmptyUnsupportedInV1 tailscale.com/client/tailscale.KeyCapabilities.Devices
OmitEmptyUnsupportedInV1 tailscale.com/client/tailscale/apitype.ExitNodeSuggestionResponse.Location OmitEmptyUnsupportedInV1 tailscale.com/client/tailscale/apitype.ExitNodeSuggestionResponse.Location

@ -41,7 +41,7 @@ type UnhealthyState struct {
Args Args `json:",omitempty"` Args Args `json:",omitempty"`
DependsOn []WarnableCode `json:",omitempty"` DependsOn []WarnableCode `json:",omitempty"`
ImpactsConnectivity bool `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 // ETag identifies a specific version of an UnhealthyState. If the contents
// of the other fields of two UnhealthyStates are the same, the ETags will // of the other fields of two UnhealthyStates are the same, the ETags will

@ -17,7 +17,7 @@ import (
// VIPService is a Tailscale VIPService with Tailscale API JSON representation. // VIPService is a Tailscale VIPService with Tailscale API JSON representation.
type VIPService struct { type VIPService struct {
// Name is a VIPService name in form svc:<leftmost-label-of-service-DNS-name>. // Name is a VIPService name in form svc:<leftmost-label-of-service-DNS-name>.
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: // Addrs are the IP addresses of the VIP Service. There are two addresses:
// the first is IPv4 and the second is IPv6. // the first is IPv4 and the second is IPv6.
// When creating a new VIP Service, the IP addresses are optional: if no // When creating a new VIP Service, the IP addresses are optional: if no

@ -30,7 +30,7 @@ type transaction struct {
Retries int `json:",omitempty"` Retries int `json:",omitempty"`
// Action is the action to be logged. It must correspond to a known action in the control plane. // 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 // Details is an opaque string specific to the action being logged. Empty strings may not
// be valid depending on the action being logged. // be valid depending on the action being logged.
Details string `json:",omitempty"` Details string `json:",omitempty"`

@ -234,7 +234,7 @@ type PartialFile struct {
// OutgoingFile represents an in-progress outgoing file transfer. // OutgoingFile represents an in-progress outgoing file transfer.
type OutgoingFile struct { type OutgoingFile struct {
ID string `json:",omitempty"` // unique identifier for this transfer (a type 4 UUID) 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" Name string `json:",omitempty"` // e.g. "foo.jpg"
Started time.Time // time transfer started Started time.Time // time transfer started
DeclaredSize int64 // or -1 if unknown DeclaredSize int64 // or -1 if unknown

@ -14,37 +14,37 @@ import (
// ConfigVAlpha is the config file format for the "alpha0" version. // ConfigVAlpha is the config file format for the "alpha0" version.
type ConfigVAlpha struct { type ConfigVAlpha struct {
Version string // "alpha0" for now 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 ServerURL *string `json:",omitzero"` // 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:") AuthKey *string `json:",omitzero"` // 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 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 OperatorUser *string `json:",omitzero"` // local user name who is allowed to operate tailscaled without being root or using sudo
Hostname *string `json:",omitempty"` Hostname *string `json:",omitzero"`
AcceptDNS opt.Bool `json:"acceptDNS,omitempty"` // --accept-dns AcceptDNS opt.Bool `json:"acceptDNS,omitzero"` // --accept-dns
AcceptRoutes opt.Bool `json:"acceptRoutes,omitempty"` // --accept-routes defaults to true AcceptRoutes opt.Bool `json:"acceptRoutes,omitzero"` // --accept-routes defaults to true
ExitNode *string `json:"exitNode,omitempty"` // IP, StableID, or MagicDNS base name ExitNode *string `json:"exitNode,omitzero"` // IP, StableID, or MagicDNS base name
AllowLANWhileUsingExitNode opt.Bool `json:"allowLANWhileUsingExitNode,omitempty"` AllowLANWhileUsingExitNode opt.Bool `json:"allowLANWhileUsingExitNode,omitzero"`
AdvertiseRoutes []netip.Prefix `json:",omitempty"` AdvertiseRoutes []netip.Prefix `json:",omitempty"`
DisableSNAT opt.Bool `json:",omitempty"` DisableSNAT opt.Bool `json:",omitzero"`
AdvertiseServices []string `json:",omitempty"` 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" NetfilterMode *string `json:",omitzero"` // "on", "off", "nodivert"
NoStatefulFiltering opt.Bool `json:",omitempty"` NoStatefulFiltering opt.Bool `json:",omitzero"`
PostureChecking opt.Bool `json:",omitempty"` PostureChecking opt.Bool `json:",omitzero"`
RunSSHServer opt.Bool `json:",omitempty"` // Tailscale SSH RunSSHServer opt.Bool `json:",omitzero"` // Tailscale SSH
RunWebClient opt.Bool `json:",omitempty"` RunWebClient opt.Bool `json:",omitzero"`
ShieldsUp opt.Bool `json:",omitempty"` ShieldsUp opt.Bool `json:",omitzero"`
AutoUpdate *AutoUpdatePrefs `json:",omitempty"` AutoUpdate *AutoUpdatePrefs `json:",omitzero"`
ServeConfigTemp *ServeConfig `json:",omitempty"` // TODO(bradfitz,maisem): make separate stable type for this ServeConfigTemp *ServeConfig `json:",omitzero"` // TODO(bradfitz,maisem): make separate stable type for this
// StaticEndpoints are additional, user-defined endpoints that this node // StaticEndpoints are additional, user-defined endpoints that this node
// should advertise amongst its wireguard endpoints. // should advertise amongst its wireguard endpoints.

@ -51,7 +51,7 @@ type Status struct {
// ExitNodeStatus describes the current exit node. // ExitNodeStatus describes the current exit node.
// If nil, an exit node is not in use. // If nil, an exit node is not in use.
ExitNodeStatus *ExitNodeStatus `json:"ExitNodeStatus,omitempty"` ExitNodeStatus *ExitNodeStatus `json:"ExitNodeStatus,omitzero"`
// Health contains health check problems. // Health contains health check problems.
// Empty means everything is good. (or at least that no known // 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 are the IP addresses assigned to the node.
TailscaleIPs []netip.Addr TailscaleIPs []netip.Addr
// AllowedIPs are IP addresses allowed to route to this node. // 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. // Tags are the list of ACL tags applied to this node.
// See tailscale.com/tailcfg#Node.Tags for more information. // 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 // PrimaryRoutes are the routes this node is currently the primary
// subnet router for, as determined by the control plane. It does // subnet router for, as determined by the control plane. It does
// not include the IPs in TailscaleIPs. // not include the IPs in TailscaleIPs.
PrimaryRoutes *views.Slice[netip.Prefix] `json:",omitempty"` PrimaryRoutes *views.Slice[netip.Prefix] `json:",omitzero"`
// Endpoints: // Endpoints:
Addrs []string Addrs []string
@ -327,7 +327,7 @@ type PeerStatus struct {
// will expire. // will expire.
KeyExpiry *time.Time `json:",omitempty"` KeyExpiry *time.Time `json:",omitempty"`
Location *tailcfg.Location `json:",omitempty"` Location *tailcfg.Location `json:",omitzero"`
} }
type TaildropTargetStatus int type TaildropTargetStatus int
@ -796,7 +796,7 @@ const (
) )
type UpdateProgress struct { type UpdateProgress struct {
Status SelfUpdateStatus `json:"status,omitempty"` Status SelfUpdateStatus `json:"status,omitzero"`
Message string `json:"message,omitempty"` Message string `json:"message,omitempty"`
Version string `json:"version,omitempty"` Version string `json:"version,omitempty"`
} }

@ -114,7 +114,7 @@ type Prefs struct {
// As of 2025-07-02, the only supported value is [AnyExitNode]. // As of 2025-07-02, the only supported value is [AnyExitNode].
// It's a string rather than a boolean to allow future extensibility // It's a string rather than a boolean to allow future extensibility
// (e.g., AutoExitNode = "mullvad" or AutoExitNode = "geo:us"). // (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 // 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 // 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. // removed since then, but the field remains an opt.Bool.
// //
// Linux-only. // Linux-only.
NoStatefulFiltering opt.Bool `json:",omitempty"` NoStatefulFiltering opt.Bool `json:",omitzero"`
// NetfilterMode specifies how much to manage netfilter rules for // NetfilterMode specifies how much to manage netfilter rules for
// Tailscale, if at all. // Tailscale, if at all.
@ -280,7 +280,7 @@ type Prefs struct {
// should be disabled. This field is currently experimental, and therefore // should be disabled. This field is currently experimental, and therefore
// no guarantees are made about its current naming and functionality when // no guarantees are made about its current naming and functionality when
// non-nil/enabled. // non-nil/enabled.
RelayServerPort *int `json:",omitempty"` RelayServerPort *int `json:",omitzero"`
// AllowSingleHosts was a legacy field that was always true // AllowSingleHosts was a legacy field that was always true
// for the past 4.5 years. It controlled whether Tailscale // for the past 4.5 years. It controlled whether Tailscale

@ -41,8 +41,8 @@ type Status struct {
// Config is an ingress service configuration. // Config is an ingress service configuration.
type Config struct { type Config struct {
IPv4Mapping *Mapping `json:"IPv4Mapping,omitempty"` IPv4Mapping *Mapping `json:"IPv4Mapping,omitzero"`
IPv6Mapping *Mapping `json:"IPv6Mapping,omitempty"` IPv6Mapping *Mapping `json:"IPv6Mapping,omitzero"`
} }
// Mapping describes a rule that forwards traffic from Tailscale Service IP to a // Mapping describes a rule that forwards traffic from Tailscale Service IP to a

@ -44,35 +44,35 @@ type VersionedConfig struct {
// Backwards compatibility version(s) of the config. Fields and sub-fields // Backwards compatibility version(s) of the config. Fields and sub-fields
// from here should only be added to, never changed in place. // 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. // V1Beta1 *ConfigV1Beta1 `json:",omitempty"` // Not yet used.
} }
type ConfigV1Alpha1 struct { type ConfigV1Alpha1 struct {
AuthKey *string `json:",omitempty"` // Tailscale auth key to use. AuthKey *string `json:",omitzero"` // Tailscale auth key to use.
State *string `json:",omitempty"` // Path to the Tailscale state. State *string `json:",omitzero"` // Path to the Tailscale state.
LogLevel *string `json:",omitempty"` // "debug", "info". Defaults to "info". LogLevel *string `json:",omitzero"` // "debug", "info". Defaults to "info".
App *string `json:",omitempty"` // e.g. kubetypes.AppProxyGroupKubeAPIServer App *string `json:",omitzero"` // e.g. kubetypes.AppProxyGroupKubeAPIServer
ServerURL *string `json:",omitempty"` // URL of the Tailscale coordination server. ServerURL *string `json:",omitzero"` // 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). LocalAddr *string `json:",omitzero"` // 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). LocalPort *uint16 `json:",omitzero"` // The port to use for serving HTTP health checks and metrics (defaults to 9002).
MetricsEnabled opt.Bool `json:",omitempty"` // Serve metrics on <LocalAddr>:<LocalPort>/metrics. MetricsEnabled opt.Bool `json:",omitzero"` // Serve metrics on <LocalAddr>:<LocalPort>/metrics.
HealthCheckEnabled opt.Bool `json:",omitempty"` // Serve health check on <LocalAddr>:<LocalPort>/metrics. HealthCheckEnabled opt.Bool `json:",omitzero"` // Serve health check on <LocalAddr>:<LocalPort>/metrics.
// TODO(tomhjp): The remaining fields should all be reloadable during // TODO(tomhjp): The remaining fields should all be reloadable during
// runtime, but currently missing most of the APIServerProxy fields. // runtime, but currently missing most of the APIServerProxy fields.
Hostname *string `json:",omitempty"` // Tailscale device hostname. Hostname *string `json:",omitzero"` // Tailscale device hostname.
AcceptRoutes opt.Bool `json:",omitempty"` // Accepts routes advertised by other Tailscale nodes. AcceptRoutes opt.Bool `json:",omitzero"` // Accepts routes advertised by other Tailscale nodes.
AdvertiseServices []string `json:",omitempty"` // Tailscale Services to advertise. 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. StaticEndpoints []netip.AddrPort `json:",omitempty"` // StaticEndpoints are additional, user-defined endpoints that this node should advertise amongst its wireguard endpoints.
} }
type APIServerProxyConfig struct { type APIServerProxyConfig struct {
Enabled opt.Bool `json:",omitempty"` // Whether to enable the API Server proxy. Enabled opt.Bool `json:",omitzero"` // Whether to enable the API Server proxy.
Mode *kubetypes.APIServerProxyMode `json:",omitempty"` // "auth" or "noauth" mode. Mode *kubetypes.APIServerProxyMode `json:",omitzero"` // "auth" or "noauth" mode.
ServiceName *tailcfg.ServiceName `json:",omitempty"` // Name of the Tailscale Service to advertise. ServiceName *tailcfg.ServiceName `json:",omitzero"` // Name of the Tailscale Service to advertise.
IssueCerts opt.Bool `json:",omitempty"` // Whether this replica should issue TLS certs for the Tailscale Service. 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. // Load reads and parses the config file at the provided path on disk.

@ -287,7 +287,7 @@ func (c *client) UpdateSecret(ctx context.Context, s *kubeapi.Secret) error {
type JSONPatch struct { type JSONPatch struct {
Op string `json:"op"` Op string `json:"op"`
Path string `json:"path"` 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. // JSONPatchResource updates a resource in the Kubernetes API using a JSON patch.

@ -14,7 +14,7 @@ import "net/netip"
type KubernetesCapRule struct { type KubernetesCapRule struct {
// Impersonate is a list of rules that specify how to impersonate the caller // Impersonate is a list of rules that specify how to impersonate the caller
// when proxying to the Kubernetes API. // 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 // 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 // of a 'kubectl exec' session, matching `src` of this grant, to an API
// server proxy, matching `dst` of this grant, should be sent to. // server proxy, matching `dst` of this grant, should be sent to.

@ -22,7 +22,7 @@ func TestUnmarshalAPIServerProxyMode(t *testing.T) {
for _, tc := range tests { for _, tc := range tests {
var s struct { var s struct {
Mode *APIServerProxyMode `json:",omitempty"` Mode *APIServerProxyMode `json:",omitzero"`
} }
err := json.Unmarshal([]byte(tc.data), &s) err := json.Unmarshal([]byte(tc.data), &s)
if tc.expected == "" { if tc.expected == "" {

@ -62,7 +62,7 @@ type CastHeader struct {
ConnectionID string `json:"connectionID"` ConnectionID string `json:"connectionID"`
// Fields that are only set for Kubernetes API server proxy session recordings: // 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 // Kubernetes contains 'kubectl exec/attach' session specific information for

@ -15,7 +15,7 @@ type DERPMap struct {
// HomeParams, if non-nil, is a change in home parameters. // HomeParams, if non-nil, is a change in home parameters.
// //
// The rest of the DEPRMap fields, if zero, means unchanged. // 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). // Regions is the set of geographic regions running DERP node(s).
// //

@ -412,7 +412,7 @@ type Node struct {
// Online is whether the node is currently connected to the // Online is whether the node is currently connected to the
// coordination server. A value of nil means unknown, or the // coordination server. A value of nil means unknown, or the
// current node doesn't have permission to know. // 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 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 // This only applies to traffic originating from the current node to the
// peer or any of its subnets. Traffic originating from subnet routes will // peer or any of its subnets. Traffic originating from subnet routes will
// not be masqueraded (e.g. in case of --snat-subnet-routes). // 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. // 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 // 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 // This only applies to traffic originating from the current node to the
// peer or any of its subnets. Traffic originating from subnet routes will // peer or any of its subnets. Traffic originating from subnet routes will
// not be masqueraded (e.g. in case of --snat-subnet-routes). // 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 // 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 // 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". // "5.10.0-17-amd64".
OSVersion string `json:",omitempty"` 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 Env string `json:",omitempty"` // a hostinfo.EnvType in string form
Distro string `json:",omitempty"` // "debian", "ubuntu", "nixos", ... Distro string `json:",omitempty"` // "debian", "ubuntu", "nixos", ...
DistroVersion string `json:",omitempty"` // "20.04", ... 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 is used to disambiguate Tailscale clients that run using tsnet.
App string `json:",omitempty"` // "k8s-operator", "golinks", ... 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) Package string `json:",omitempty"` // Tailscale package to disambiguate ("choco", "appstore", etc; "" for unknown)
DeviceModel string `json:",omitempty"` // mobile phone model ("Pixel 3a", "iPhone12,3") 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) 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 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) 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 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 SSH_HostKeys []string `json:"sshHostKeys,omitempty"` // if advertised
Cloud string `json:",omitempty"` Cloud string `json:",omitempty"`
Userspace opt.Bool `json:",omitempty"` // if the client is running in userspace (netstack) mode Userspace opt.Bool `json:",omitzero"` // 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 UserspaceRouter opt.Bool `json:",omitzero"` // 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 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 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 clients selected exit node, empty when unselected. ExitNodeID StableNodeID `json:",omitzero"` // the clients selected exit node, empty when unselected.
// Location represents geographical location data about a // Location represents geographical location data about a
// Tailscale host. Location is optional and only set if // Tailscale host. Location is optional and only set if
// explicitly declared by a node. // 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 // StateEncrypted reports whether the node state is stored encrypted on
// disk. The actual mechanism is platform-specific: // disk. The actual mechanism is platform-specific:
// * Apple nodes use the Keychain // * Apple nodes use the Keychain
// * Linux and Windows nodes use the TPM // * Linux and Windows nodes use the TPM
// * Android apps use EncryptedSharedPreferences // * Android apps use EncryptedSharedPreferences
StateEncrypted opt.Bool `json:",omitempty"` StateEncrypted opt.Bool `json:",omitzero"`
// NOTE: any new fields containing pointers in this type // NOTE: any new fields containing pointers in this type
// require changes to Hostinfo.Equal. // require changes to Hostinfo.Equal.
@ -1234,7 +1234,7 @@ type RegisterResponseAuth struct {
// At most one of Oauth2Token or AuthKey is set. // 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"` AuthKey string `json:",omitempty"`
} }
@ -1256,7 +1256,7 @@ type RegisterRequest struct {
NodeKey key.NodePublic NodeKey key.NodePublic
OldNodeKey key.NodePublic OldNodeKey key.NodePublic
NLKey key.NLPublic NLKey key.NLPublic
Auth *RegisterResponseAuth `json:",omitempty"` Auth *RegisterResponseAuth `json:",omitzero"`
// Expiry optionally specifies the requested key expiry. // Expiry optionally specifies the requested key expiry.
// The server policy may override. // The server policy may override.
// As a special case, if Expiry is in the past and NodeKey is // 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 { type NetPortRange struct {
_ structs.Incomparable _ structs.Incomparable
IP string // IP, CIDR, Range, or "*" (same formats as FilterRule.SrcIPs) 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 Ports PortRange
} }
@ -1978,7 +1978,7 @@ type MapResponse struct {
// provided URL. No auth headers are necessary. // provided URL. No auth headers are necessary.
// PingRequest may be sent on any MapResponse (ones with // PingRequest may be sent on any MapResponse (ones with
// KeepAlive true or false). // KeepAlive true or false).
PingRequest *PingRequest `json:",omitempty"` PingRequest *PingRequest `json:",omitzero"`
// PopBrowserURL, if non-empty, is a URL for the client to // PopBrowserURL, if non-empty, is a URL for the client to
// open to complete an action. The client should dup suppress // 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. // Node describes the node making the map request.
// Starting with MapRequest.Version 18, nil means unchanged. // Starting with MapRequest.Version 18, nil means unchanged.
Node *Node `json:",omitempty"` Node *Node `json:",omitzero"`
// DERPMap describe the set of DERP servers available. // DERPMap describe the set of DERP servers available.
// A nil value means unchanged. // A nil value means unchanged.
DERPMap *DERPMap `json:",omitempty"` DERPMap *DERPMap `json:",omitzero"`
// Peers, if non-empty, is the complete list of peers. // Peers, if non-empty, is the complete list of peers.
// It will be set in the first MapResponse for a long-polled request/response. // 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. // DNSConfig contains the DNS settings for the client to use.
// A nil value means no change from an earlier non-nil value. // 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 // Domain is the name of the network that this node is
// in. It's either of the form "example.com" (for user // 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. // requested that info about services be included in HostInfo.
// If unset, the most recent non-empty MapResponse value in // If unset, the most recent non-empty MapResponse value in
// the HTTP response stream is used. // the HTTP response stream is used.
CollectServices opt.Bool `json:",omitempty"` CollectServices opt.Bool `json:",omitzero"`
// PacketFilter are the firewall rules. // PacketFilter are the firewall rules.
// //
@ -2121,7 +2121,7 @@ type MapResponse struct {
// SSHPolicy, if non-nil, updates the SSH policy for how incoming // SSHPolicy, if non-nil, updates the SSH policy for how incoming
// SSH connections should be handled. // 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, if non-zero, is the current timestamp according to the control server.
ControlTime *time.Time `json:",omitempty"` ControlTime *time.Time `json:",omitempty"`
@ -2134,7 +2134,7 @@ type MapResponse struct {
// indicates the control plane believes TKA should be enabled. // indicates the control plane believes TKA should be enabled.
// A nil TKAInfo in a mapresponse stream (i.e. a 'delta' mapresponse) // A nil TKAInfo in a mapresponse stream (i.e. a 'delta' mapresponse)
// indicates no change from the value sent earlier. // 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 // DomainDataPlaneAuditLogID, if non-empty, is the per-tailnet log ID to be
// used when writing data plane audit logs. // used when writing data plane audit logs.
@ -2142,24 +2142,24 @@ type MapResponse struct {
// Debug is normally nil, except for when the control server // Debug is normally nil, except for when the control server
// is setting debug settings on a node. // 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 // ControlDialPlan tells the client how to connect to the control
// server. An initial nil is equivalent to new(ControlDialPlan). // server. An initial nil is equivalent to new(ControlDialPlan).
// A subsequent streamed nil means no change. // 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 // ClientVersion describes the latest client version that's available for
// download and whether the client is using it. A nil value means no change // download and whether the client is using it. A nil value means no change
// or nothing to report. // or nothing to report.
ClientVersion *ClientVersion `json:",omitempty"` ClientVersion *ClientVersion `json:",omitzero"`
// DefaultAutoUpdate is the default node auto-update setting for this // DefaultAutoUpdate is the default node auto-update setting for this
// tailnet. The node is free to opt-in or out locally regardless of 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 // value. This value is only used on first MapResponse from control, the
// auto-update setting doesn't change if the tailnet admin flips the // auto-update setting doesn't change if the tailnet admin flips the
// default after the node registered. // 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 // 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 // take when interacting with this message. For example, if the
// DisplayMessage is shown via a notification, the action label might be a // DisplayMessage is shown via a notification, the action label might be a
// button on that notification and clicking the button would open the URL. // 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 // 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 // Exit optionally specifies that the client should os.Exit
// with this code. This is a safety measure in case a client is crash // 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. // 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)) } 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. // Matching any one of the following four field causes a match.
// It must also match Certs, if non-empty. // It must also match Certs, if non-empty.
Node StableNodeID `json:"node,omitempty"` Node StableNodeID `json:"node,omitzero"`
NodeIP string `json:"nodeIP,omitempty"` NodeIP string `json:"nodeIP,omitempty"`
UserLogin string `json:"userLogin,omitempty"` // email-ish: foo@example.com, bar@github UserLogin string `json:"userLogin,omitempty"` // email-ish: foo@example.com, bar@github
Any bool `json:"any,omitempty"` // if true, match any connection 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. // OnRecorderFailure is the action to take if recording fails.
// If nil, the default action is to fail open. // 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. // SSHRecorderFailureAction is the action to take if recording fails.
@ -3196,7 +3196,7 @@ type PeerChange struct {
DiscoKey *key.DiscoPublic `json:",omitempty"` DiscoKey *key.DiscoPublic `json:",omitempty"`
// Online, if non-nil, means that the NodeID's online status changed. // 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, if non-nil, means that the NodeID's online status changed.
LastSeen *time.Time `json:",omitempty"` LastSeen *time.Time `json:",omitempty"`
@ -3284,7 +3284,7 @@ type AuditLogRequest struct {
// NodeKey is the client's current node key. // NodeKey is the client's current node key.
NodeKey key.NodePublic `json:",omitzero"` NodeKey key.NodePublic `json:",omitzero"`
// Action is the action to be logged. It must correspond to a known action in the control plane. // 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 // Details is an opaque string, specific to the action being logged. Empty strings may not
// be valid depending on the action being logged. // be valid depending on the action being logged.
Details string `json:",omitempty"` Details string `json:",omitempty"`

@ -50,7 +50,7 @@ type AccessLogRecord struct {
// client immediately after the error text, as well as logged here. This // client immediately after the error text, as well as logged here. This
// makes it easier to correlate support requests with server logs. If a // makes it easier to correlate support requests with server logs. If a
// RequestID generator is not configured, RequestID will be empty. // 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. // String returns m as a JSON string.

@ -44,7 +44,7 @@ func TestBool(t *testing.T) {
in: struct { in: struct {
True Bool True Bool
False Bool False Bool
Unset Bool `json:",omitempty"` Unset Bool `json:",omitzero"`
}{ }{
True: "true", True: "true",
False: "false", False: "false",

@ -39,8 +39,8 @@ type viewStruct struct {
Int int Int int
Addrs Slice[netip.Prefix] Addrs Slice[netip.Prefix]
Strings Slice[string] Strings Slice[string]
AddrsPtr *Slice[netip.Prefix] `json:",omitempty"` AddrsPtr *Slice[netip.Prefix] `json:",omitzero"`
StringsPtr *Slice[string] `json:",omitempty"` StringsPtr *Slice[string] `json:",omitzero"`
} }
type noPtrStruct struct { type noPtrStruct struct {

@ -428,8 +428,8 @@ type pongReply struct {
type EndpointChange struct { type EndpointChange struct {
When time.Time // when the change occurred When time.Time // when the change occurred
What string // what this change is What string // what this change is
From any `json:",omitempty"` // information about the previous state From any `json:",omitzero"` // information about the previous state
To any `json:",omitempty"` // information about the new state To any `json:",omitzero"` // information about the new state
} }
// shouldDeleteLocked reports whether we should delete this endpoint. // shouldDeleteLocked reports whether we should delete this endpoint.

Loading…
Cancel
Save