2022-03-02 18:47:27 -05:00
//go:build !no_embedded_executor
2020-04-27 13:09:58 -04:00
2025-12-05 19:13:56 -05:00
package embed
2020-04-27 13:09:58 -04:00
import (
"context"
2025-03-04 15:03:15 -05:00
"errors"
2023-04-21 08:41:48 -04:00
"flag"
2025-12-05 17:51:05 -05:00
"fmt"
"net"
2020-04-27 13:09:58 -04:00
"net/http"
2025-12-05 17:51:05 -05:00
"os/exec"
"path/filepath"
2022-07-29 16:38:10 -04:00
"runtime/debug"
2023-04-21 08:41:48 -04:00
"strconv"
2025-03-27 17:04:48 -04:00
"sync"
2023-04-21 08:41:48 -04:00
"time"
2020-04-27 13:09:58 -04:00
2024-01-08 17:38:36 -05:00
"github.com/k3s-io/k3s/pkg/agent/containerd"
2025-03-06 03:39:32 -05:00
"github.com/k3s-io/k3s/pkg/agent/cri"
2024-01-08 17:38:36 -05:00
"github.com/k3s-io/k3s/pkg/agent/cridockerd"
2025-11-25 14:26:54 -05:00
"github.com/k3s-io/k3s/pkg/agent/flannel"
"github.com/k3s-io/k3s/pkg/agent/netpol"
2022-03-02 18:47:27 -05:00
"github.com/k3s-io/k3s/pkg/cli/cmds"
daemonconfig "github.com/k3s-io/k3s/pkg/daemons/config"
2025-12-05 19:13:56 -05:00
"github.com/k3s-io/k3s/pkg/daemons/executor"
"github.com/k3s-io/k3s/pkg/executor/embed/etcd"
2025-09-12 17:10:21 -04:00
"github.com/k3s-io/k3s/pkg/signals"
2022-04-27 15:03:47 -04:00
"github.com/k3s-io/k3s/pkg/util"
2022-03-02 18:47:27 -05:00
"github.com/k3s-io/k3s/pkg/version"
2025-12-05 17:51:05 -05:00
"github.com/k3s-io/k3s/pkg/vpn"
2025-09-12 17:10:21 -04:00
pkgerrors "github.com/pkg/errors"
2020-04-27 13:09:58 -04:00
"github.com/sirupsen/logrus"
2021-06-29 10:28:38 -04:00
"k8s.io/apiserver/pkg/authentication/authenticator"
cloudprovider "k8s.io/cloud-provider"
ccmapp "k8s.io/cloud-provider/app"
cloudcontrollerconfig "k8s.io/cloud-provider/app/config"
2023-08-22 17:09:31 -04:00
"k8s.io/cloud-provider/names"
2021-06-29 10:28:38 -04:00
ccmopt "k8s.io/cloud-provider/options"
cliflag "k8s.io/component-base/cli/flag"
2023-04-21 08:41:48 -04:00
"k8s.io/klog/v2"
2023-08-22 17:09:31 -04:00
apiapp "k8s.io/kubernetes/cmd/kube-apiserver/app"
2020-04-27 13:09:58 -04:00
cmapp "k8s.io/kubernetes/cmd/kube-controller-manager/app"
2021-06-29 10:28:38 -04:00
proxy "k8s.io/kubernetes/cmd/kube-proxy/app"
2020-04-27 13:09:58 -04:00
sapp "k8s.io/kubernetes/cmd/kube-scheduler/app"
2021-06-29 10:28:38 -04:00
kubelet "k8s.io/kubernetes/cmd/kubelet/app"
2025-12-05 17:51:05 -05:00
utilsnet "k8s.io/utils/net"
2021-06-29 10:28:38 -04:00
// registering k3s cloud provider
2022-03-02 18:47:27 -05:00
_ "github.com/k3s-io/k3s/pkg/cloudprovider"
2020-04-27 13:09:58 -04:00
)
2025-03-27 17:04:48 -04:00
var once sync . Once
2020-04-27 13:09:58 -04:00
func init ( ) {
2025-12-05 19:13:56 -05:00
executor . Set ( & Embedded { } )
}
// explicit type check
var _ executor . Executor = & Embedded { }
type Embedded struct {
apiServerReady <- chan struct { }
etcdReady chan struct { }
criReady chan struct { }
nodeConfig * daemonconfig . Node
2020-04-27 13:09:58 -04:00
}
2021-10-28 20:38:31 -04:00
func ( e * Embedded ) Bootstrap ( ctx context . Context , nodeConfig * daemonconfig . Node , cfg cmds . Agent ) error {
2025-03-05 03:35:58 -05:00
e . apiServerReady = util . APIServerReadyChan ( ctx , nodeConfig . AgentConfig . KubeConfigK3sController , util . DefaultAPIServerReadyTimeout )
2025-03-14 18:24:49 -04:00
e . etcdReady = make ( chan struct { } )
2025-03-06 03:39:32 -05:00
e . criReady = make ( chan struct { } )
2021-10-28 20:38:31 -04:00
e . nodeConfig = nodeConfig
2023-04-21 08:41:48 -04:00
2025-03-27 17:04:48 -04:00
go once . Do ( func ( ) {
2023-04-21 08:41:48 -04:00
// Ensure that the log verbosity remains set to the configured level by resetting it at 1-second intervals
// for the first 2 minutes that K3s is starting up. This is necessary because each of the Kubernetes
// components will initialize klog and reset the verbosity flag when they are starting.
logCtx , cancel := context . WithTimeout ( ctx , time . Second * 120 )
defer cancel ( )
klog . InitFlags ( nil )
for {
flag . Set ( "v" , strconv . Itoa ( cmds . LogConfig . VLevel ) )
select {
case <- time . After ( time . Second ) :
case <- logCtx . Done ( ) :
return
}
}
2025-03-27 17:04:48 -04:00
} )
2023-04-21 08:41:48 -04:00
2025-12-05 18:48:02 -05:00
if nodeConfig . Flannel . Backend != flannel . BackendNone {
2025-12-05 17:51:05 -05:00
var err error
if len ( cfg . FlannelIface ) > 0 {
2025-12-05 18:48:02 -05:00
nodeConfig . Flannel . Iface , err = net . InterfaceByName ( cfg . FlannelIface )
2025-12-05 17:51:05 -05:00
if err != nil {
return pkgerrors . WithMessagef ( err , "unable to find interface %s" , cfg . FlannelIface )
}
}
// If there is a VPN, we must overwrite NodeIP and flannel interface
var vpnInfo vpn . VPNInfo
if cfg . VPNAuth != "" {
vpnInfo , err = vpn . GetVPNInfo ( cfg . VPNAuth )
if err != nil {
return err
}
// Pass ipv4, ipv6 or both depending on nodeIPs mode
nodeIPs := nodeConfig . AgentConfig . NodeIPs
var vpnIPs [ ] net . IP
if utilsnet . IsIPv4 ( nodeIPs [ 0 ] ) && vpnInfo . IPv4Address != nil {
vpnIPs = append ( vpnIPs , vpnInfo . IPv4Address )
if vpnInfo . IPv6Address != nil {
vpnIPs = append ( vpnIPs , vpnInfo . IPv6Address )
}
} else if utilsnet . IsIPv6 ( nodeIPs [ 0 ] ) && vpnInfo . IPv6Address != nil {
vpnIPs = append ( vpnIPs , vpnInfo . IPv6Address )
if vpnInfo . IPv4Address != nil {
vpnIPs = append ( vpnIPs , vpnInfo . IPv4Address )
}
} else {
return fmt . Errorf ( "address family mismatch when assigning VPN addresses to node: node=%v, VPN ipv4=%v ipv6=%v" , nodeIPs , vpnInfo . IPv4Address , vpnInfo . IPv6Address )
}
// Overwrite nodeip and flannel interface and throw a warning if user explicitly set those parameters
if len ( vpnIPs ) != 0 {
logrus . Infof ( "Node-ip changed to %v due to VPN" , vpnIPs )
if len ( cfg . NodeIP . Value ( ) ) != 0 {
logrus . Warn ( "VPN provider overrides configured node-ip parameter" )
}
if len ( cfg . NodeExternalIP . Value ( ) ) != 0 {
logrus . Warn ( "VPN provider overrides node-external-ip parameter" )
}
nodeIPs = vpnIPs
2025-12-05 18:48:02 -05:00
nodeConfig . Flannel . Iface , err = net . InterfaceByName ( vpnInfo . VPNInterface )
2025-12-05 17:51:05 -05:00
if err != nil {
return pkgerrors . WithMessagef ( err , "unable to find vpn interface: %s" , vpnInfo . VPNInterface )
}
}
}
// set paths for embedded flannel if enabled
hostLocal , err := exec . LookPath ( "host-local" )
if err != nil {
return pkgerrors . WithMessagef ( err , "failed to find host-local" )
}
if cfg . FlannelConf == "" {
2025-12-05 18:48:02 -05:00
nodeConfig . Flannel . ConfFile = filepath . Join ( cfg . DataDir , "agent" , "etc" , "flannel" , "net-conf.json" )
2025-12-05 17:51:05 -05:00
} else {
2025-12-05 18:48:02 -05:00
nodeConfig . Flannel . ConfFile = cfg . FlannelConf
nodeConfig . Flannel . ConfOverride = true
2025-12-05 17:51:05 -05:00
}
nodeConfig . AgentConfig . CNIBinDir = filepath . Dir ( hostLocal )
nodeConfig . AgentConfig . CNIConfDir = filepath . Join ( cfg . DataDir , "agent" , "etc" , "cni" , "net.d" )
// It does not make sense to use VPN without its flannel backend
if cfg . VPNAuth != "" {
2025-12-05 18:48:02 -05:00
nodeConfig . Flannel . Backend = vpnInfo . ProviderName
2025-12-05 17:51:05 -05:00
}
}
2021-05-11 15:50:08 -04:00
return nil
}
2022-04-27 15:03:47 -04:00
func ( e * Embedded ) Kubelet ( ctx context . Context , args [ ] string ) error {
2020-08-05 12:26:36 -04:00
command := kubelet . NewKubeletCommand ( context . Background ( ) )
2020-04-27 13:09:58 -04:00
command . SetArgs ( args )
go func ( ) {
2025-03-05 03:35:58 -05:00
<- e . APIServerReadyChan ( )
2021-09-08 13:56:18 -04:00
defer func ( ) {
if err := recover ( ) ; err != nil {
2023-04-21 15:44:08 -04:00
logrus . WithField ( "stack" , string ( debug . Stack ( ) ) ) . Fatalf ( "kubelet panic: %v" , err )
2021-09-08 13:56:18 -04:00
}
} ( )
2024-01-24 19:44:12 -05:00
err := command . ExecuteContext ( ctx )
if err != nil && ! errors . Is ( err , context . Canceled ) {
2025-09-12 17:10:21 -04:00
signals . RequestShutdown ( pkgerrors . WithMessage ( err , "kubelet exited" ) )
2024-01-24 19:44:12 -05:00
}
2025-09-12 17:10:21 -04:00
signals . RequestShutdown ( nil )
2020-04-27 13:09:58 -04:00
} ( )
return nil
}
2023-10-16 14:53:09 -04:00
func ( e * Embedded ) KubeProxy ( ctx context . Context , args [ ] string ) error {
2020-04-27 13:09:58 -04:00
command := proxy . NewProxyCommand ( )
2025-03-26 15:40:28 -04:00
command . SetArgs ( util . GetArgs ( platformKubeProxyArgs ( e . nodeConfig ) , args ) )
2020-04-27 13:09:58 -04:00
go func ( ) {
2025-03-05 03:35:58 -05:00
<- e . APIServerReadyChan ( )
2021-09-08 13:56:18 -04:00
defer func ( ) {
if err := recover ( ) ; err != nil {
2023-04-21 15:44:08 -04:00
logrus . WithField ( "stack" , string ( debug . Stack ( ) ) ) . Fatalf ( "kube-proxy panic: %v" , err )
2021-09-08 13:56:18 -04:00
}
} ( )
2024-01-24 19:44:12 -05:00
err := command . ExecuteContext ( ctx )
if err != nil && ! errors . Is ( err , context . Canceled ) {
2025-09-12 17:10:21 -04:00
signals . RequestShutdown ( pkgerrors . WithMessage ( err , "kube-proxy exited" ) )
2024-01-24 19:44:12 -05:00
}
2025-09-12 17:10:21 -04:00
signals . RequestShutdown ( nil )
2020-04-27 13:09:58 -04:00
} ( )
return nil
}
2021-10-28 20:38:31 -04:00
func ( * Embedded ) APIServerHandlers ( ctx context . Context ) ( authenticator . Request , http . Handler , error ) {
2023-08-22 17:09:31 -04:00
startupConfig := <- apiapp . StartupConfig
2021-10-12 02:13:10 -04:00
return startupConfig . Authenticator , startupConfig . Handler , nil
}
2025-03-14 18:24:49 -04:00
func ( e * Embedded ) APIServer ( ctx context . Context , args [ ] string ) error {
2023-08-22 17:09:31 -04:00
command := apiapp . NewAPIServerCommand ( ctx . Done ( ) )
2020-04-27 13:09:58 -04:00
command . SetArgs ( args )
go func ( ) {
2025-03-14 18:24:49 -04:00
<- e . ETCDReadyChan ( )
2021-09-08 13:56:18 -04:00
defer func ( ) {
if err := recover ( ) ; err != nil {
2023-04-21 15:44:08 -04:00
logrus . WithField ( "stack" , string ( debug . Stack ( ) ) ) . Fatalf ( "apiserver panic: %v" , err )
2021-09-08 13:56:18 -04:00
}
} ( )
2024-01-24 19:44:12 -05:00
err := command . ExecuteContext ( ctx )
if err != nil && ! errors . Is ( err , context . Canceled ) {
2025-09-12 17:10:21 -04:00
signals . RequestShutdown ( pkgerrors . WithMessage ( err , "apiserver exited" ) )
2024-01-24 19:44:12 -05:00
}
2025-09-12 17:10:21 -04:00
signals . RequestShutdown ( nil )
2020-04-27 13:09:58 -04:00
} ( )
2021-10-12 02:13:10 -04:00
return nil
2020-04-27 13:09:58 -04:00
}
2025-03-05 03:35:58 -05:00
func ( e * Embedded ) Scheduler ( ctx context . Context , nodeReady <- chan struct { } , args [ ] string ) error {
2024-12-20 16:17:14 -05:00
command := sapp . NewSchedulerCommand ( ctx . Done ( ) )
2020-04-27 13:09:58 -04:00
command . SetArgs ( args )
go func ( ) {
2025-03-05 03:35:58 -05:00
<- e . APIServerReadyChan ( )
<- nodeReady
2021-09-08 13:56:18 -04:00
defer func ( ) {
if err := recover ( ) ; err != nil {
2023-04-21 15:44:08 -04:00
logrus . WithField ( "stack" , string ( debug . Stack ( ) ) ) . Fatalf ( "scheduler panic: %v" , err )
2021-09-08 13:56:18 -04:00
}
} ( )
2024-01-24 19:44:12 -05:00
err := command . ExecuteContext ( ctx )
if err != nil && ! errors . Is ( err , context . Canceled ) {
2025-09-12 17:10:21 -04:00
signals . RequestShutdown ( pkgerrors . WithMessage ( err , "scheduler exited" ) )
2024-01-24 19:44:12 -05:00
}
2025-09-12 17:10:21 -04:00
signals . RequestShutdown ( nil )
2020-04-27 13:09:58 -04:00
} ( )
return nil
}
2025-03-05 03:35:58 -05:00
func ( e * Embedded ) ControllerManager ( ctx context . Context , args [ ] string ) error {
2020-04-27 13:09:58 -04:00
command := cmapp . NewControllerManagerCommand ( )
command . SetArgs ( args )
go func ( ) {
2025-03-05 03:35:58 -05:00
<- e . APIServerReadyChan ( )
2021-09-08 13:56:18 -04:00
defer func ( ) {
if err := recover ( ) ; err != nil {
2023-04-21 15:44:08 -04:00
logrus . WithField ( "stack" , string ( debug . Stack ( ) ) ) . Fatalf ( "controller-manager panic: %v" , err )
2021-09-08 13:56:18 -04:00
}
} ( )
2024-01-24 19:44:12 -05:00
err := command . ExecuteContext ( ctx )
if err != nil && ! errors . Is ( err , context . Canceled ) {
2025-09-12 17:10:21 -04:00
signals . RequestShutdown ( pkgerrors . WithMessage ( err , "controller-manager exited" ) )
2024-01-24 19:44:12 -05:00
}
2025-09-12 17:10:21 -04:00
signals . RequestShutdown ( nil )
2020-04-27 13:09:58 -04:00
} ( )
return nil
}
2021-06-29 10:28:38 -04:00
2021-10-28 20:38:31 -04:00
func ( * Embedded ) CloudControllerManager ( ctx context . Context , ccmRBACReady <- chan struct { } , args [ ] string ) error {
2021-06-29 10:28:38 -04:00
ccmOptions , err := ccmopt . NewCloudControllerManagerOptions ( )
if err != nil {
logrus . Fatalf ( "unable to initialize command options: %v" , err )
}
cloudInitializer := func ( config * cloudcontrollerconfig . CompletedConfig ) cloudprovider . Interface {
2023-06-20 03:08:08 -04:00
cloud , err := cloudprovider . InitCloudProvider ( version . Program , config . ComponentConfig . KubeCloudShared . CloudProvider . CloudConfigFile )
2021-06-29 10:28:38 -04:00
if err != nil {
logrus . Fatalf ( "Cloud provider could not be initialized: %v" , err )
}
if cloud == nil {
logrus . Fatalf ( "Cloud provider is nil" )
}
return cloud
}
2023-08-22 17:09:31 -04:00
controllerAliases := names . CCMControllerAliases ( )
command := ccmapp . NewCloudControllerManagerCommand (
ccmOptions ,
cloudInitializer ,
ccmapp . DefaultInitFuncConstructors ,
controllerAliases ,
cliflag . NamedFlagSets { } ,
ctx . Done ( ) )
2021-06-29 10:28:38 -04:00
command . SetArgs ( args )
go func ( ) {
<- ccmRBACReady
2021-09-08 13:56:18 -04:00
defer func ( ) {
if err := recover ( ) ; err != nil {
2023-04-21 15:44:08 -04:00
logrus . WithField ( "stack" , string ( debug . Stack ( ) ) ) . Fatalf ( "cloud-controller-manager panic: %v" , err )
2021-09-08 13:56:18 -04:00
}
} ( )
2024-01-24 19:44:12 -05:00
err := command . ExecuteContext ( ctx )
if err != nil && ! errors . Is ( err , context . Canceled ) {
2025-09-12 17:10:21 -04:00
signals . RequestShutdown ( pkgerrors . WithMessage ( err , "cloud-controller-manager exited" ) )
2024-01-24 19:44:12 -05:00
}
2025-09-12 17:10:21 -04:00
signals . RequestShutdown ( nil )
2021-06-29 10:28:38 -04:00
} ( )
return nil
}
2021-10-28 20:38:31 -04:00
2025-12-05 19:13:56 -05:00
func ( e * Embedded ) CurrentETCDOptions ( ) ( executor . InitialOptions , error ) {
return executor . InitialOptions { } , nil
}
func ( e * Embedded ) ETCD ( ctx context . Context , wg * sync . WaitGroup , args * executor . ETCDConfig , extraArgs [ ] string , test executor . TestFunc ) error {
// Start a goroutine to call the provided test function until it returns true.
// The test function is reponsible for ensuring that the etcd server is up
// and ready to accept client requests.
if e . etcdReady != nil {
go func ( ) {
for {
if err := test ( ctx , true ) ; err != nil {
logrus . Infof ( "Failed to test etcd connection: %v" , err )
} else {
logrus . Info ( "Connection to etcd is ready" )
close ( e . etcdReady )
return
}
select {
case <- time . After ( 5 * time . Second ) :
case <- ctx . Done ( ) :
return
}
}
} ( )
}
return etcd . StartETCD ( ctx , wg , args , extraArgs )
2022-02-24 17:35:08 -05:00
}
2024-01-08 17:38:36 -05:00
func ( e * Embedded ) Containerd ( ctx context . Context , cfg * daemonconfig . Node ) error {
2025-12-05 19:13:56 -05:00
return executor . CloseIfNilErr ( containerd . Run ( ctx , cfg ) , e . criReady )
2024-01-08 17:38:36 -05:00
}
func ( e * Embedded ) Docker ( ctx context . Context , cfg * daemonconfig . Node ) error {
2025-12-05 19:13:56 -05:00
return executor . CloseIfNilErr ( cridockerd . Run ( ctx , cfg ) , e . criReady )
2024-01-08 17:38:36 -05:00
}
2025-03-05 03:35:58 -05:00
2025-03-06 03:39:32 -05:00
func ( e * Embedded ) CRI ( ctx context . Context , cfg * daemonconfig . Node ) error {
// agentless sets cri socket path to /dev/null to indicate no CRI is needed
if cfg . ContainerRuntimeEndpoint != "/dev/null" {
2025-12-05 19:13:56 -05:00
return executor . CloseIfNilErr ( cri . WaitForService ( ctx , cfg . ContainerRuntimeEndpoint , "CRI" ) , e . criReady )
2025-03-06 03:39:32 -05:00
}
2025-12-05 19:13:56 -05:00
return executor . CloseIfNilErr ( nil , e . criReady )
2025-03-06 03:39:32 -05:00
}
2025-11-25 14:26:54 -05:00
func ( e * Embedded ) CNI ( ctx context . Context , wg * sync . WaitGroup , cfg * daemonconfig . Node ) error {
2025-12-05 18:48:02 -05:00
if cfg . Flannel . Backend != flannel . BackendNone {
if ( cfg . Flannel . ExternalIP ) && ( len ( cfg . AgentConfig . NodeExternalIPs ) == 0 ) {
2025-12-05 17:51:05 -05:00
logrus . Warnf ( "Server has flannel-external-ip flag set but this node does not set node-external-ip. Flannel will use internal address when connecting to this node." )
2025-12-05 18:48:02 -05:00
} else if ( cfg . Flannel . ExternalIP ) && ( cfg . Flannel . Backend != flannel . BackendWireguardNative ) {
logrus . Warnf ( "Flannel is using external addresses with an insecure backend: %v. Please consider using an encrypting flannel backend." , cfg . Flannel . Backend )
2025-12-05 17:51:05 -05:00
}
if err := flannel . Prepare ( ctx , cfg ) ; err != nil {
return err
}
2025-11-25 14:26:54 -05:00
if err := flannel . Run ( ctx , wg , cfg ) ; err != nil {
return err
}
}
if ! cfg . AgentConfig . DisableNPC {
if err := netpol . Run ( ctx , wg , cfg ) ; err != nil {
return err
}
}
return nil
}
2025-03-05 03:35:58 -05:00
func ( e * Embedded ) APIServerReadyChan ( ) <- chan struct { } {
if e . apiServerReady == nil {
panic ( "executor not bootstrapped" )
}
return e . apiServerReady
}
2025-03-06 03:39:32 -05:00
2025-03-14 18:24:49 -04:00
func ( e * Embedded ) ETCDReadyChan ( ) <- chan struct { } {
if e . etcdReady == nil {
panic ( "executor not bootstrapped" )
}
return e . etcdReady
}
2025-03-06 03:39:32 -05:00
func ( e * Embedded ) CRIReadyChan ( ) <- chan struct { } {
if e . criReady == nil {
panic ( "executor not bootstrapped" )
}
return e . criReady
}
2025-09-12 17:10:21 -04:00
func ( e Embedded ) IsSelfHosted ( ) bool {
return false
}