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 {
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"`
}

@ -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 {

@ -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"`
}

@ -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

@ -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

@ -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:<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:
// the first is IPv4 and the second is IPv6.
// When creating a new VIP Service, the IP addresses are optional: if no

@ -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"`

@ -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

@ -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.

@ -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"`
}

@ -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

@ -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

@ -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 <LocalAddr>:<LocalPort>/metrics.
HealthCheckEnabled opt.Bool `json:",omitempty"` // Serve health check on <LocalAddr>:<LocalPort>/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 <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
// 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.

@ -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.

@ -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.

@ -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 == "" {

@ -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

@ -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).
//

@ -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 clients 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"`

@ -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.

@ -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",

@ -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 {

@ -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.

Loading…
Cancel
Save