tsnet: use proper log ID

refactor logpolicy config loading to make it easier to reuse from
outside the package.  Within tsnet, setup a basic logtail config.

Signed-off-by: Will Norris <will@tailscale.com>
pull/4813/head
Will Norris 3 years ago committed by GitHub
parent edc90ebc61
commit 09363064b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -95,6 +95,33 @@ type Policy struct {
PublicID logtail.PublicID PublicID logtail.PublicID
} }
// NewConfig creates a Config with collection and a newly generated PrivateID.
func NewConfig(collection string) *Config {
id, err := logtail.NewPrivateID()
if err != nil {
panic("logtail.NewPrivateID should never fail")
}
return &Config{
Collection: collection,
PrivateID: id,
PublicID: id.Public(),
}
}
// Validate verifies that the Config matches the collection,
// and that the PrivateID and PublicID pair are sensible.
func (c *Config) Validate(collection string) error {
switch {
case c.Collection != collection:
return fmt.Errorf("config collection %q does not match %q", c.Collection, collection)
case c.PrivateID.IsZero():
return errors.New("config has zero PrivateID")
case c.PrivateID.Public() != c.PublicID:
return errors.New("config PrivateID does not match PublicID")
}
return nil
}
// ToBytes returns the JSON representation of c. // ToBytes returns the JSON representation of c.
func (c *Config) ToBytes() []byte { func (c *Config) ToBytes() []byte {
data, err := json.MarshalIndent(c, "", "\t") data, err := json.MarshalIndent(c, "", "\t")
@ -105,7 +132,7 @@ func (c *Config) ToBytes() []byte {
} }
// Save writes the JSON representation of c to stateFile. // Save writes the JSON representation of c to stateFile.
func (c *Config) save(stateFile string) error { func (c *Config) Save(stateFile string) error {
c.PublicID = c.PrivateID.Public() c.PublicID = c.PrivateID.Public()
if err := os.MkdirAll(filepath.Dir(stateFile), 0750); err != nil { if err := os.MkdirAll(filepath.Dir(stateFile), 0750); err != nil {
return err return err
@ -117,7 +144,16 @@ func (c *Config) save(stateFile string) error {
return nil return nil
} }
// ConfigFromBytes parses a a Config from its JSON encoding. // ConfigFromFile reads a Config from a JSON file.
func ConfigFromFile(statefile string) (*Config, error) {
b, err := os.ReadFile(statefile)
if err != nil {
return nil, err
}
return ConfigFromBytes(b)
}
// ConfigFromBytes parses a Config from its JSON encoding.
func ConfigFromBytes(jsonEnc []byte) (*Config, error) { func ConfigFromBytes(jsonEnc []byte) (*Config, error) {
c := &Config{} c := &Config{}
if err := json.Unmarshal(jsonEnc, c); err != nil { if err := json.Unmarshal(jsonEnc, c); err != nil {
@ -470,38 +506,16 @@ func New(collection string) *Policy {
} }
} }
var oldc *Config newc, err := ConfigFromFile(cfgPath)
data, err := ioutil.ReadFile(cfgPath)
if err != nil {
earlyLogf("logpolicy.Read %v: %v", cfgPath, err)
oldc = &Config{}
oldc.Collection = collection
} else {
oldc, err = ConfigFromBytes(data)
if err != nil {
earlyLogf("logpolicy.Config unmarshal: %v", err)
oldc = &Config{}
}
}
newc := *oldc
if newc.Collection != collection {
log.Printf("logpolicy.Config: config collection %q does not match %q", newc.Collection, collection)
// We picked up an incompatible config file.
// Regenerate the private ID.
newc.PrivateID = logtail.PrivateID{}
newc.Collection = collection
}
if newc.PrivateID.IsZero() {
newc.PrivateID, err = logtail.NewPrivateID()
if err != nil { if err != nil {
log.Fatalf("logpolicy: NewPrivateID() should never fail") earlyLogf("logpolicy.ConfigFromFile %v: %v", cfgPath, err)
} newc = NewConfig(collection)
} }
newc.PublicID = newc.PrivateID.Public() if err := newc.Validate(collection); err != nil {
if newc != *oldc { earlyLogf("logpolicy.Config.Validate for %q: %v", cfgPath, err)
if err := newc.save(cfgPath); err != nil { newc := NewConfig(collection)
earlyLogf("logpolicy.Config.Save: %v", err) if err := newc.Save(cfgPath); err != nil {
earlyLogf("logpolicy.Config.Save for %v: %v", cfgPath, err)
} }
} }

@ -10,6 +10,7 @@ package tsnet
import ( import (
"context" "context"
"fmt" "fmt"
"io/ioutil"
"log" "log"
"net" "net"
"net/http" "net/http"
@ -29,6 +30,9 @@ import (
"tailscale.com/ipn/localapi" "tailscale.com/ipn/localapi"
"tailscale.com/ipn/store" "tailscale.com/ipn/store"
"tailscale.com/ipn/store/mem" "tailscale.com/ipn/store/mem"
"tailscale.com/logpolicy"
"tailscale.com/logtail"
"tailscale.com/logtail/filch"
"tailscale.com/net/nettest" "tailscale.com/net/nettest"
"tailscale.com/net/tsdial" "tailscale.com/net/tsdial"
"tailscale.com/smallzstd" "tailscale.com/smallzstd"
@ -76,6 +80,7 @@ type Server struct {
shutdownCtx context.Context shutdownCtx context.Context
shutdownCancel context.CancelFunc shutdownCancel context.CancelFunc
localClient *tailscale.LocalClient localClient *tailscale.LocalClient
logtail *logtail.Logger
mu sync.Mutex mu sync.Mutex
listeners map[listenKey]*listener listeners map[listenKey]*listener
@ -127,6 +132,11 @@ func (s *Server) Close() error {
} }
s.listeners = nil s.listeners = nil
// Perform a best-effort final flush.
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
s.logtail.Shutdown(ctx)
return nil return nil
} }
@ -178,8 +188,42 @@ func (s *Server) start() error {
return fmt.Errorf("%v is not a directory", s.rootPath) return fmt.Errorf("%v is not a directory", s.rootPath)
} }
// TODO(bradfitz): start logtail? don't use filch, perhaps? cfgPath := filepath.Join(s.rootPath, "tsnet.log.conf")
// only upload plumbed Logf?
lpc, err := logpolicy.ConfigFromFile(cfgPath)
switch {
case os.IsNotExist(err):
lpc = logpolicy.NewConfig(logtail.CollectionNode)
if err := lpc.Save(cfgPath); err != nil {
return fmt.Errorf("logpolicy.Config.Save for %v: %w", cfgPath, err)
}
case err != nil:
return fmt.Errorf("logpolicy.LoadConfig for %v: %w", cfgPath, err)
}
if err := lpc.Validate(logtail.CollectionNode); err != nil {
return fmt.Errorf("logpolicy.Config.Validate for %v: %w", cfgPath, err)
}
logid := lpc.PublicID.String()
f, err := filch.New(filepath.Join(s.rootPath, "tsnet"), filch.Options{ReplaceStderr: false})
if err != nil {
return fmt.Errorf("error creating filch: %w", err)
}
c := logtail.Config{
Collection: lpc.Collection,
PrivateID: lpc.PrivateID,
Stderr: ioutil.Discard, // log everything to Buffer
Buffer: f,
NewZstdEncoder: func() logtail.Encoder {
w, err := smallzstd.NewEncoder(nil)
if err != nil {
panic(err)
}
return w
},
HTTPC: &http.Client{Transport: logpolicy.NewLogtailTransport(logtail.DefaultHost)},
}
s.logtail = logtail.NewLogger(c, logf)
s.linkMon, err = monitor.New(logf) s.linkMon, err = monitor.New(logf)
if err != nil { if err != nil {
@ -226,7 +270,6 @@ func (s *Server) start() error {
return err return err
} }
} }
logid := "tsnet-TODO" // https://github.com/tailscale/tailscale/issues/3866
loginFlags := controlclient.LoginDefault loginFlags := controlclient.LoginDefault
if s.Ephemeral { if s.Ephemeral {
@ -283,6 +326,9 @@ func (s *Server) start() error {
} }
func (s *Server) logf(format string, a ...interface{}) { func (s *Server) logf(format string, a ...interface{}) {
if s.logtail != nil {
s.logtail.Logf(format, a...)
}
if s.Logf != nil { if s.Logf != nil {
s.Logf(format, a...) s.Logf(format, a...)
return return

Loading…
Cancel
Save