pkg

package
v0.0.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 15, 2024 License: MIT Imports: 36 Imported by: 0

Documentation

Index

Constants

View Source
const (
	AppLongName  = "Tailscale IPs"
	AppShortName = "tips"
)
View Source
const (
	// These two buckets contain FULL data.
	DevicesBucket = "bucket:devices.full"
	StatsBucket   = "bucket:stats"

	StatsKey = "key:stats"
)

Variables

View Source
var (
	AppVersion = "0.0.1"
	UserAgent  = fmt.Sprintf("%s/%s", AppShortName, AppVersion)
)
View Source
var (
	// CtxKeyConfig holds all config settings that were resolved from the environment/config file/cli flags
	CtxKeyConfig    = contextKey("configuration")
	CtxKeyUserQuery = contextKey("user-query")
)
View Source
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 CtxAsBool

func CtxAsBool(ctx context.Context, key contextKey) bool

func CtxAsInt

func CtxAsInt(ctx context.Context, key contextKey) int

func CtxAsString

func CtxAsString(ctx context.Context, key contextKey) string

func ExecuteClusterRemoteCmd

func ExecuteClusterRemoteCmd(ctx context.Context, w io.Writer, hosts []RemoteCmdHost, remoteCmd string)

func NewClient

func NewClient(ctx context.Context) *tailscale.Client

func NewOauthClient

func NewOauthClient(ctx context.Context) *tailscale.Client

func ParseColumns

func ParseColumns(s string) (mapset.Set[string], mapset.Set[string])

func ParseFilter

func ParseFilter(filter string) (filtercomp.AST, error)

func RenderASCIITableView

func RenderASCIITableView(ctx context.Context, tableView *GeneralTableView, w io.Writer) error

func RenderIPs

func RenderIPs(ctx context.Context, tableView *GeneralTableView, w io.Writer) error

func RenderJson

func RenderJson(ctx context.Context, tableView *GeneralTableView, w io.Writer) error

func RenderLogLine

func RenderLogLine(ctx context.Context, w io.Writer, idx int, isStdErr bool, hostname, alias, line string)

func RenderRemoteSummary

func RenderRemoteSummary(ctx context.Context, w io.Writer, success, errors uint32, elapsed time.Duration) error

func RenderTableView

func RenderTableView(ctx context.Context, tableView *GeneralTableView, w io.Writer) error

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 CtxAsConfig(ctx context.Context, key contextKey) *ConfigCtx

func NewConfigCtx

func NewConfigCtx() *ConfigCtx

func (*ConfigCtx) IsRemoteCommand

func (c *ConfigCtx) IsRemoteCommand() bool

type ContextView

type ContextView struct {
	Query      string
	APIElapsed time.Duration
	CLIElapsed time.Duration
}

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 NewDB2

func NewDB2[T Indexer](tailnetScope string) *Db[T]

func (*Db[T]) Close added in v0.0.6

func (d *Db[T]) Close() error

func (*Db[T]) Erase added in v0.0.6

func (d *Db[T]) Erase() error

func (*Db[T]) Exists added in v0.0.6

func (d *Db[T]) Exists(ctx context.Context) (bool, error)

func (*Db[T]) File added in v0.0.6

func (d *Db[T]) File() string

func (*Db[T]) IndexOpaqueItems added in v0.0.6

func (d *Db[T]) IndexOpaqueItems(ctx context.Context, bucketName string, items []T) error

func (*Db[T]) LookupOpaqueItem added in v0.0.6

func (d *Db[T]) LookupOpaqueItem(ctx context.Context, bucketName, primaryKey string) (*T, error)

func (*Db[T]) Open added in v0.0.6

func (d *Db[T]) Open() error

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

func (d *Db[T]) TailnetScope() string

type DbStats added in v0.0.6

type DbStats struct {
	DevicesCount  int `json:"devices_count"`
	EnrichedCount int `json:"enriched_count"`
}

type DevicesTable

type DevicesTable struct {
	TailnetView
	Devices *DevicesView
}

type DevicesView

type DevicesView struct {
}

DevicesView has everything needed to be rendered.

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

type Indexer interface {
	Key() string
}

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 RemoteCmdHost struct {
	Original string
	Alias    string
}

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 SelfView

type SelfView struct {
	Index   int
	DNSName string
}

type SortDirection

type SortDirection int
const (
	Ascending SortDirection = iota
	Descending
)

type SortSpec

type SortSpec struct {
	Field     string
	Direction SortDirection
}

func ParseSortString

func ParseSortString(sortString string) []SortSpec

Parse the sort string and return a slice of SortSpec

type TailnetView

type TailnetView struct {
	Tailnet       string
	TotalMachines int
}

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.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL