package agent import ( "context" "crypto/tls" "os" "path/filepath" "sync" "github.com/k3s-io/k3s/pkg/agent" "github.com/k3s-io/k3s/pkg/agent/https" "github.com/k3s-io/k3s/pkg/cli/cmds" "github.com/k3s-io/k3s/pkg/daemons/config" "github.com/k3s-io/k3s/pkg/datadir" k3smetrics "github.com/k3s-io/k3s/pkg/metrics" "github.com/k3s-io/k3s/pkg/proctitle" "github.com/k3s-io/k3s/pkg/profile" "github.com/k3s-io/k3s/pkg/signals" "github.com/k3s-io/k3s/pkg/spegel" "github.com/k3s-io/k3s/pkg/util" "github.com/k3s-io/k3s/pkg/util/errors" "github.com/k3s-io/k3s/pkg/util/logger" "github.com/k3s-io/k3s/pkg/util/mux" "github.com/k3s-io/k3s/pkg/util/permissions" "github.com/k3s-io/k3s/pkg/version" "github.com/k3s-io/k3s/pkg/vpn" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" "k8s.io/klog/v2" ) func Run(clx *cli.Context) (rerr error) { // Validate build env cmds.MustValidateGolang() // hide process arguments from ps output, since they may contain // database credentials or other secrets. proctitle.SetProcTitle(os.Args[0] + " agent") // Evacuate cgroup v2 before doing anything else that may fork. if err := cmds.EvacuateCgroup2(); err != nil { return err } // Initialize logging, and subprocess reaping if necessary. // Log output redirection and subprocess reaping both require forking. if err := cmds.InitLogging(); err != nil { return err } klog.EnableContextualLogging(true) ctx := logger.NewContext(signals.SetupSignalContext(), version.Program) wg := &sync.WaitGroup{} // If exiting due to an error, ensure that contexts are cancelled so that the // WaitGroup exits. Otherwise, wait for something else to initiate shutdown. defer func() { if rerr != nil { // do not need to pass the error in here, it will be reported by the CLI error handler signals.RequestShutdown(nil) } else { <-ctx.Done() rerr = ctx.Err() } wg.Wait() }() if !cmds.AgentConfig.Rootless { if err := permissions.IsPrivileged(); err != nil { return errors.WithMessage(err, "agent requires additional privilege if not run with --rootless") } } if cmds.AgentConfig.TokenFile != "" { token, err := util.ReadFile(cmds.AgentConfig.TokenFile) if err != nil { return err } cmds.AgentConfig.Token = token } clientKubeletCert := filepath.Join(cmds.AgentConfig.DataDir, "agent", "client-kubelet.crt") clientKubeletKey := filepath.Join(cmds.AgentConfig.DataDir, "agent", "client-kubelet.key") _, err := tls.LoadX509KeyPair(clientKubeletCert, clientKubeletKey) if err != nil && cmds.AgentConfig.Token == "" { return errors.New("--token is required") } if cmds.AgentConfig.ServerURL == "" { return errors.New("--server is required") } if cmds.AgentConfig.FlannelIface != "" && len(cmds.AgentConfig.NodeIP.Value()) == 0 { ip, err := util.GetIPFromInterface(cmds.AgentConfig.FlannelIface) if err != nil { return err } cmds.AgentConfig.NodeIP.Set(ip) } logrus.Info("Starting " + version.Program + " agent " + clx.App.Version) dataDir, err := datadir.LocalHome(cmds.AgentConfig.DataDir, cmds.AgentConfig.Rootless) if err != nil { return err } cfg := cmds.AgentConfig cfg.Debug = clx.Bool("debug") cfg.DataDir = dataDir go cmds.WriteCoverage(ctx) if cfg.VPNAuthFile != "" { cfg.VPNAuth, err = util.ReadFile(cfg.VPNAuthFile) if err != nil { return err } } // Starts the VPN in the agent if config was set up if cfg.VPNAuth != "" { err := vpn.StartVPN(cfg.VPNAuth) if err != nil { return err } } // Until the agent is run and retrieves config from the server, we won't know // if the embedded registry is enabled. If it is not enabled, these are not // used as the registry is never started. registry := spegel.DefaultRegistry registry.Bootstrapper = spegel.NewAgentBootstrapper(cfg.ServerURL, cfg.Token, cfg.DataDir) registry.Router = func(ctx context.Context, nodeConfig *config.Node) (*mux.Router, error) { return https.Start(ctx, nodeConfig, nil) } // same deal for metrics - these are not used if the extra metrics listener is not enabled. metrics := k3smetrics.DefaultMetrics metrics.Router = func(ctx context.Context, nodeConfig *config.Node) (*mux.Router, error) { return https.Start(ctx, nodeConfig, nil) } // and for pprof as well pprof := profile.DefaultProfiler pprof.Router = func(ctx context.Context, nodeConfig *config.Node) (*mux.Router, error) { return https.Start(ctx, nodeConfig, nil) } return agent.Run(ctx, wg, cfg) }