github.com/caos/orbos@v1.5.14-0.20221103111702-e6cd0cea7ad4/cmd/nodeagent/main.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"crypto/sha256"
     6  	"flag"
     7  	"fmt"
     8  	"math/rand"
     9  	"net/http"
    10  	"os"
    11  	"os/signal"
    12  	"runtime"
    13  	"strings"
    14  	"syscall"
    15  	"time"
    16  
    17  	"github.com/caos/orbos/internal/operator/nodeagent/networking"
    18  
    19  	"github.com/caos/orbos/mntr"
    20  	"github.com/caos/orbos/pkg/git"
    21  
    22  	_ "net/http/pprof"
    23  
    24  	"github.com/caos/orbos/internal/operator/nodeagent"
    25  	"github.com/caos/orbos/internal/operator/nodeagent/dep"
    26  	"github.com/caos/orbos/internal/operator/nodeagent/dep/conv"
    27  	"github.com/caos/orbos/internal/operator/nodeagent/firewall"
    28  )
    29  
    30  var (
    31  	gitCommit string
    32  	version   string
    33  )
    34  
    35  func main() {
    36  
    37  	ctx, cancelCtx := context.WithCancel(context.Background())
    38  
    39  	monitor := mntr.Monitor{
    40  		OnInfo:   mntr.LogMessage,
    41  		OnChange: mntr.LogMessage,
    42  		OnError:  mntr.LogError,
    43  	}
    44  
    45  	defer func() { monitor.RecoverPanic(recover()) }()
    46  
    47  	verbose := flag.Bool("verbose", false, "Print logs for debugging")
    48  	printVersion := flag.Bool("version", false, "Print build information")
    49  	ignorePorts := flag.String("ignore-ports", "", "Comma separated list of firewall ports that are ignored")
    50  	nodeAgentID := flag.String("id", "", "The managed machines ID")
    51  	pprof := flag.Bool("pprof", false, "start pprof as port 6060")
    52  	sentryEnvironment := flag.String("environment", "", "Sentry environment")
    53  
    54  	flag.Parse()
    55  
    56  	if *printVersion {
    57  		fmt.Printf("%s %s\n", version, gitCommit)
    58  		os.Exit(0)
    59  	}
    60  
    61  	if *sentryEnvironment != "" {
    62  		if err := mntr.Ingest(monitor, "orbos", version, *sentryEnvironment, "node-agent"); err != nil {
    63  			panic(err)
    64  		}
    65  	}
    66  
    67  	monitor.WithField("id", nodeAgentID).CaptureMessage("nodeagent invoked")
    68  
    69  	if *verbose {
    70  		monitor = monitor.Verbose()
    71  	}
    72  
    73  	if *nodeAgentID == "" {
    74  		panic("flag --id is required")
    75  	}
    76  
    77  	monitor.WithFields(map[string]interface{}{
    78  		"version":           version,
    79  		"commit":            gitCommit,
    80  		"verbose":           *verbose,
    81  		"nodeAgentID":       *nodeAgentID,
    82  		"sentryEnvironment": *sentryEnvironment,
    83  	}).Info("Node Agent is starting")
    84  
    85  	mutexActionChannel := make(chan interface{})
    86  
    87  	signalChannel := make(chan os.Signal)
    88  	signal.Notify(signalChannel,
    89  		syscall.SIGTERM,
    90  		syscall.SIGINT,
    91  		syscall.SIGQUIT,
    92  	)
    93  
    94  	go func() {
    95  		for sig := range signalChannel {
    96  			monitor.WithField("signal", sig.String()).Info("Received signal")
    97  			cancelCtx()
    98  			mutexActionChannel <- sig
    99  		}
   100  	}()
   101  
   102  	if *pprof {
   103  		go func() {
   104  			monitor.Info(http.ListenAndServe("localhost:6060", nil).Error())
   105  		}()
   106  	}
   107  
   108  	runningOnOS, err := dep.GetOperatingSystem()
   109  	if err != nil {
   110  		panic(err)
   111  	}
   112  
   113  	repoKey, err := nodeagent.RepoKey()
   114  	if err != nil {
   115  		panic(err)
   116  	}
   117  
   118  	pruned := strings.Split(string(repoKey), "-----")[2]
   119  	hashed := sha256.Sum256([]byte(pruned))
   120  	conv := conv.New(ctx, monitor, runningOnOS, fmt.Sprintf("%x", hashed[:]))
   121  
   122  	gitClient := git.New(ctx, monitor, fmt.Sprintf("Node Agent %s", *nodeAgentID), "node-agent@caos.ch")
   123  
   124  	var portsSlice []string
   125  	if len(*ignorePorts) > 0 {
   126  		portsSlice = strings.Split(*ignorePorts, ",")
   127  	}
   128  
   129  	itFunc := nodeagent.Iterator(
   130  		monitor,
   131  		gitClient,
   132  		gitCommit,
   133  		*nodeAgentID,
   134  		firewall.Ensurer(monitor, runningOnOS.OperatingSystem, portsSlice),
   135  		networking.Ensurer(monitor, runningOnOS.OperatingSystem),
   136  		conv,
   137  		conv.Init())
   138  
   139  	type updateType struct{}
   140  	go func() {
   141  		for range time.Tick(24 * time.Hour) {
   142  			timer := time.NewTimer(time.Duration(rand.Intn(120)) * time.Minute)
   143  			<-timer.C
   144  			mutexActionChannel <- updateType{}
   145  			timer.Stop()
   146  		}
   147  	}()
   148  
   149  	type iterateType struct{}
   150  	//trigger first iteration
   151  	go func() { mutexActionChannel <- iterateType{} }()
   152  
   153  	go func() {
   154  		for range time.Tick(5 * time.Minute) {
   155  			if PrintMemUsage(monitor) > 250 {
   156  				monitor.Info("Shutting down as memory usage exceeded 250 MiB")
   157  				mutexActionChannel <- syscall.Signal(0)
   158  			}
   159  		}
   160  	}()
   161  
   162  	for action := range mutexActionChannel {
   163  		switch sig := action.(type) {
   164  		case os.Signal:
   165  			monitor.WithField("signal", sig.String()).Info("Shutting down")
   166  			os.Exit(0)
   167  		case iterateType:
   168  			monitor.Info("Starting iteration")
   169  			itFunc()
   170  			monitor.Info("Iteration done")
   171  			go func() {
   172  				//trigger next iteration
   173  				time.Sleep(10 * time.Second)
   174  				mutexActionChannel <- iterateType{}
   175  			}()
   176  		case updateType:
   177  			monitor.Info("Starting update")
   178  			if err := conv.Update(); err != nil {
   179  				monitor.Error(fmt.Errorf("updating packages failed: %w", err))
   180  			} else {
   181  				monitor.Info("Update done")
   182  			}
   183  		}
   184  	}
   185  }
   186  
   187  func PrintMemUsage(monitor mntr.Monitor) uint64 {
   188  	var m runtime.MemStats
   189  	runtime.ReadMemStats(&m)
   190  	mB := m.Sys / 1024 / 1024
   191  	monitor.WithFields(map[string]interface{}{
   192  		"MiB": mB,
   193  	}).Info("Read current memory usage")
   194  	return mB
   195  }