Documentation
¶
Index ¶
- Constants
- Variables
- func CtxAsBool(ctx context.Context, key contextKey) bool
- func CtxAsInt(ctx context.Context, key contextKey) int
- func CtxAsString(ctx context.Context, key contextKey) string
- func ExecuteClusterRemoteCmd(ctx context.Context, w io.Writer, hosts []RemoteCmdHost, remoteCmd string)
- func NewClient(ctx context.Context) *tailscale.Client
- func NewOauthClient(ctx context.Context) *tailscale.Client
- func ParseColumns(s string) (mapset.Set[string], mapset.Set[string])
- func ParseFilter(filter string) (filtercomp.AST, error)
- func RenderASCIITableView(ctx context.Context, tableView *GeneralTableView, w io.Writer) error
- func RenderIPs(ctx context.Context, tableView *GeneralTableView, w io.Writer) error
- func RenderJson(ctx context.Context, tableView *GeneralTableView, w io.Writer) error
- func RenderLogLine(ctx context.Context, w io.Writer, idx int, isStdErr bool, ...)
- func RenderRemoteSummary(ctx context.Context, w io.Writer, success, errors uint32, ...) error
- func RenderTableView(ctx context.Context, tableView *GeneralTableView, w io.Writer) error
- type CachedRepository
- type ConfigCtx
- type ContextView
- type DBQuery
- type Db
- func (d *Db[T]) Close() error
- func (d *Db[T]) Erase() error
- func (d *Db[T]) Exists(ctx context.Context) (bool, error)
- func (d *Db[T]) File() string
- func (d *Db[T]) IndexOpaqueItems(ctx context.Context, bucketName string, items []T) error
- func (d *Db[T]) LookupOpaqueItem(ctx context.Context, bucketName, primaryKey string) (*T, error)
- func (d *Db[T]) Open() error
- func (d *Db[T]) SearchOpaqueItems(ctx context.Context, bucketName string, query DBQuery) ([]T, error)
- func (d *Db[T]) TailnetScope() string
- type DbStats
- type DevicesTable
- type DevicesView
- type GeneralTableView
- type Header
- type HeaderMatchName
- type Indexer
- type InnerRepo
- type MockedDeviceRepo
- type RemoteCmdHost
- type RemoteDeviceRepo
- type SelfView
- type SortDirection
- type SortSpec
- type TailnetView
- type TailscaleAPICfgCtx
- type TailscaleCLICfgCtx
- type WrappedDevice
Constants ¶
const ( AppLongName = "Tailscale IPs" AppShortName = "tips" )
const ( // These two buckets contain FULL data. DevicesBucket = "bucket:devices.full" StatsBucket = "bucket:stats" StatsKey = "key:stats" )
Variables ¶
var ( AppVersion = "0.0.1" UserAgent = fmt.Sprintf("%s/%s", AppShortName, AppVersion) )
var ( // CtxKeyConfig holds all config settings that were resolved from the environment/config file/cli flags CtxKeyConfig = contextKey("configuration") CtxKeyUserQuery = contextKey("user-query") )
var ( HdrAddress = Header{Title: "Address", MatchName: MatchNameAddress} HdrAuthorized = Header{Title: "Authorized", MatchName: MatchNameAuthorized} HdrExitStatus = Header{Title: "Exit Status", MatchName: MatchNameExitStatus} HdrIpv4 = Header{Title: "Ipv4", MatchName: MatchNameIpv4} HdrIpv6 = Header{Title: "Ipv6", MatchName: MatchNameIpv6} HdrLastSeenAgo = Header{Title: "Last Seen", MatchName: MatchNameLastSeenAgo, ReqEnriched: true} HdrMachine = Header{Title: "Machine", MatchName: MatchNameMachine} HdrNo = Header{Title: "No", MatchName: MatchNameNo} HdrTags = Header{Title: "Tags", MatchName: MatchNameTags} HdrUser = Header{Title: "User", MatchName: MatchNameUser} HdrVersion = Header{Title: "Version", MatchName: MatchNameVersion} // AllHeadersList must contain the complete list of headers. AllHeadersList = []Header{ HdrAddress, HdrAuthorized, HdrExitStatus, HdrIpv4, HdrIpv6, HdrLastSeenAgo, HdrMachine, HdrNo, HdrTags, HdrUser, HdrVersion, } // AllHeadersMap initializes a map of HeaderMatchName to Header. AllHeadersMap = func() map[HeaderMatchName]Header { a := make(map[HeaderMatchName]Header) for _, hdr := range AllHeadersList { a[hdr.MatchName] = hdr } return a }() // DefaultColumnSet is the column set that ships out of the box. // Order matters which is why it's created as a slice. DefaultColumnSet = []Header{ HdrNo, HdrMachine, HdrIpv4, HdrTags, HdrUser, HdrVersion, HdrExitStatus, HdrLastSeenAgo, } )
Functions ¶
func CtxAsString ¶
func ExecuteClusterRemoteCmd ¶
func ParseFilter ¶
func ParseFilter(filter string) (filtercomp.AST, error)
func RenderASCIITableView ¶
func RenderJson ¶
func RenderLogLine ¶
func RenderRemoteSummary ¶
func RenderTableView ¶
Types ¶
type CachedRepository ¶
type CachedRepository struct {
// contains filtered or unexported fields
}
func NewCachedRepo ¶
func NewCachedRepo(innerRepo InnerRepo) *CachedRepository
func (*CachedRepository) DevicesResource ¶
func (c *CachedRepository) DevicesResource(ctx context.Context) ([]*WrappedDevice, error)
type ConfigCtx ¶
type ConfigCtx struct {
Basic bool
CacheTimeout time.Duration
Columns mapset.Set[string]
ColumnsExclude mapset.Set[string]
Concurrency int
Filters filtercomp.AST
IPsOutput bool
IPsDelimiter string
JsonOutput bool
NoCache bool
NoColor bool
PrefixFilter *prefixcomp.PrimaryFilterAST
RemoteCmd string
Slice *slicecomp.Slice
SortOrder []SortSpec
Tailnet string
CachedElapsed time.Duration
TailscaleAPI TailscaleAPICfgCtx
TailscaleCLI TailscaleCLICfgCtx
Page int
TestMode bool
}
func CtxAsConfig ¶
func NewConfigCtx ¶
func NewConfigCtx() *ConfigCtx
func (*ConfigCtx) IsRemoteCommand ¶
type ContextView ¶
type DBQuery ¶ added in v0.0.4
type DBQuery struct {
PrefixFilters *prefixcomp.PrimaryFilterAST
PrimaryKeys []string
}
type Db ¶ added in v0.0.6
type Db[T Indexer] struct { // contains filtered or unexported fields }
func (*Db[T]) IndexOpaqueItems ¶ added in v0.0.6
func (*Db[T]) LookupOpaqueItem ¶ added in v0.0.6
func (*Db[T]) SearchOpaqueItems ¶ added in v0.0.6
func (d *Db[T]) SearchOpaqueItems(ctx context.Context, bucketName string, query DBQuery) ([]T, error)
SearchOpaqueItems can generically search with 3 different ways. 1. Using one or more primary keys, in which case this is a direct lookup (not technically a search) 2. Using the * (all/everything) construct, this is just a full table scan really. 3. Using a prefix scan, this is a seek to a segment of the index and should be fast assuming good selectivity.
func (*Db[T]) TailnetScope ¶ added in v0.0.6
type DevicesTable ¶
type DevicesTable struct {
TailnetView
Devices *DevicesView
}
type GeneralTableView ¶
type GeneralTableView struct {
ContextView
TailnetView
Self *SelfView
Headers []Header
Rows [][]string
}
func ProcessDevicesTable ¶
func ProcessDevicesTable(ctx context.Context, devList []*WrappedDevice) (*GeneralTableView, error)
ProcessDevicesTable will apply sorting (if required), slicing (if required) and the massage/transformation of data to produce a final `*DevicesTable` that has everything required to render.
func (*GeneralTableView) HeaderTitles ¶ added in v0.0.7
func (g *GeneralTableView) HeaderTitles() []string
type Header ¶ added in v0.0.7
type Header struct {
ReqEnriched bool
MatchName HeaderMatchName
Title string
}
type HeaderMatchName ¶ added in v0.0.7
type HeaderMatchName string
const ( MatchNameAddress HeaderMatchName = "address" MatchNameAuthorized HeaderMatchName = "authorized" MatchNameBlocksIncomingConnections HeaderMatchName = "blocksincomingconnections" MatchNameClientVersion HeaderMatchName = "clientversion" MatchNameExitStatus HeaderMatchName = "exitstatus" MatchNameFullname HeaderMatchName = "fullname" MatchNameIpv4 HeaderMatchName = "ipv4" MatchNameIpv6 HeaderMatchName = "ipv6" MatchNameHostname HeaderMatchName = "hostname" MatchNameLastSeen HeaderMatchName = "lastseen" MatchNameLastSeenAgo HeaderMatchName = "lastseen.ago" MatchNameMachine HeaderMatchName = "machine" MatchNameName HeaderMatchName = "name" MatchNameNo HeaderMatchName = "no" MatchNameOS HeaderMatchName = "os" MatchNameTags HeaderMatchName = "tags" MatchNameUser HeaderMatchName = "user" MatchNameVersion HeaderMatchName = "version" )
type InnerRepo ¶
type InnerRepo interface {
DevicesResource(ctx context.Context) ([]*WrappedDevice, error)
}
type MockedDeviceRepo ¶
type MockedDeviceRepo struct {
// contains filtered or unexported fields
}
func NewMockedDeviceRepo ¶
func NewMockedDeviceRepo() *MockedDeviceRepo
func NewMockedDeviceRepoWithPath ¶ added in v0.0.6
func NewMockedDeviceRepoWithPath(filePath string) *MockedDeviceRepo
func (*MockedDeviceRepo) DevicesResource ¶
func (r *MockedDeviceRepo) DevicesResource(ctx context.Context) ([]*WrappedDevice, error)
type RemoteCmdHost ¶
type RemoteDeviceRepo ¶
type RemoteDeviceRepo struct {
// contains filtered or unexported fields
}
func NewRemoteDeviceRepo ¶
func NewRemoteDeviceRepo(client *tailscale.Client) *RemoteDeviceRepo
func (*RemoteDeviceRepo) DevicesResource ¶
func (r *RemoteDeviceRepo) DevicesResource(ctx context.Context) ([]*WrappedDevice, error)
type SortSpec ¶
type SortSpec struct {
Field string
Direction SortDirection
}
func ParseSortString ¶
Parse the sort string and return a slice of SortSpec
type TailnetView ¶
TailnetView has everything known about a Tailnet
type TailscaleAPICfgCtx ¶
type TailscaleAPICfgCtx struct {
Timeout time.Duration
// ApiKey for regular authentication
ApiKey string
// OAuthClientID for OAuth based login.
OAuthClientID string
// OAuthClientSecret for Oauth based login.
OAuthClientSecret string
// ElapsedTime records the time this API call took. It's meant to be mutated during the API call and populated then.
ElapsedTime time.Duration
}
type TailscaleCLICfgCtx ¶
type TailscaleCLICfgCtx struct {
}
type WrappedDevice ¶
type WrappedDevice struct {
tailscale.Device
EnrichedInfo *tailscale_cli.DeviceInfo `json:"enrichedInfo"`
}
WrappedDevice is a type that wraps the core `tailscale.Device` type. It also holds the joined `tailscale_cli.DeviceInfo` that may or may not be present when fetched from within the tailnet. It also implements the `Indexer` interface, so it may be stored in the DB.
func (*WrappedDevice) EvalColumnField ¶ added in v0.0.7
func (w *WrappedDevice) EvalColumnField(ctx context.Context, idx int, headerMatchName HeaderMatchName) string
EvalColumnField is invoked for each "column" requested per device field. This code was built purposely to be dynamic and if it gets more complex it may be worthwhile to break the code up further into discreet functions per field. One additional thing I've been considering is the memoize of any redundant "heavy" work but so far there is none here.
func (*WrappedDevice) Key ¶
func (w *WrappedDevice) Key() string
Key returns the device field of how this device gets indexed into the cached db. Currently, it just uses the name such: "blade.tail372c.ts.net" which implies devices are stored in alphabetical order as ascending via their `name` field.