Commit 905e2ea1 authored by Brad Davidson's avatar Brad Davidson Committed by Brad Davidson

Move container runtime ready channel into executor

Move the container runtime ready channel into the executor interface, instead of passing it awkwardly between server and agent config structs Signed-off-by: 's avatarBrad Davidson <brad.davidson@rancher.com> (cherry picked from commit a8bc4124) Signed-off-by: 's avatarBrad Davidson <brad.davidson@rancher.com>
parent 79e7f711
...@@ -5,10 +5,37 @@ import ( ...@@ -5,10 +5,37 @@ import (
"time" "time"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"google.golang.org/grpc"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
k8sutil "k8s.io/kubernetes/pkg/kubelet/util"
) )
const maxMsgSize = 1024 * 1024 * 16 const maxMsgSize = 1024 * 1024 * 16
// Connection connects to a CRI socket at the given path.
func Connection(ctx context.Context, address string) (*grpc.ClientConn, error) {
addr, dialer, err := k8sutil.GetAddressAndDialer(socketPrefix + address)
if err != nil {
return nil, err
}
conn, err := grpc.Dial(addr, grpc.WithInsecure(), grpc.WithTimeout(3*time.Second), grpc.WithContextDialer(dialer), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMsgSize)))
if err != nil {
return nil, err
}
c := runtimeapi.NewRuntimeServiceClient(conn)
_, err = c.Version(ctx, &runtimeapi.VersionRequest{
Version: "0.1.0",
})
if err != nil {
conn.Close()
return nil, err
}
return conn, nil
}
// WaitForService blocks in a retry loop until the CRI service // WaitForService blocks in a retry loop until the CRI service
// is functional at the provided socket address. It will return only on success, // is functional at the provided socket address. It will return only on success,
// or when the context is cancelled. // or when the context is cancelled.
......
...@@ -3,37 +3,4 @@ ...@@ -3,37 +3,4 @@
package cri package cri
import (
"context"
"time"
"google.golang.org/grpc"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
k8sutil "k8s.io/kubernetes/pkg/kubelet/util"
)
const socketPrefix = "unix://" const socketPrefix = "unix://"
// Connection connects to a CRI socket at the given path.
func Connection(ctx context.Context, address string) (*grpc.ClientConn, error) {
addr, dialer, err := k8sutil.GetAddressAndDialer(socketPrefix + address)
if err != nil {
return nil, err
}
conn, err := grpc.Dial(addr, grpc.WithInsecure(), grpc.WithTimeout(3*time.Second), grpc.WithContextDialer(dialer), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMsgSize)))
if err != nil {
return nil, err
}
c := runtimeapi.NewRuntimeServiceClient(conn)
_, err = c.Version(ctx, &runtimeapi.VersionRequest{
Version: "0.1.0",
})
if err != nil {
conn.Close()
return nil, err
}
return conn, nil
}
...@@ -3,35 +3,4 @@ ...@@ -3,35 +3,4 @@
package cri package cri
import ( const socketPrefix = "npipe://"
"context"
"time"
"google.golang.org/grpc"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
"k8s.io/kubernetes/pkg/kubelet/util"
)
// Connection connects to a CRI socket at the given path.
func Connection(ctx context.Context, address string) (*grpc.ClientConn, error) {
addr, dialer, err := util.GetAddressAndDialer(address)
if err != nil {
return nil, err
}
conn, err := grpc.Dial(addr, grpc.WithInsecure(), grpc.WithTimeout(3*time.Second), grpc.WithContextDialer(dialer), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMsgSize)))
if err != nil {
return nil, err
}
c := runtimeapi.NewRuntimeServiceClient(conn)
_, err = c.Version(ctx, &runtimeapi.VersionRequest{
Version: "0.1.0",
})
if err != nil {
conn.Close()
return nil, err
}
return conn, nil
}
...@@ -158,13 +158,10 @@ func run(ctx context.Context, cfg cmds.Agent, proxy proxy.Proxy) error { ...@@ -158,13 +158,10 @@ func run(ctx context.Context, cfg cmds.Agent, proxy proxy.Proxy) error {
if err := executor.Containerd(ctx, nodeConfig); err != nil { if err := executor.Containerd(ctx, nodeConfig); err != nil {
return err return err
} }
} } else {
// the container runtime is ready to host workloads when containerd is up and the airgap if err := executor.CRI(ctx, nodeConfig); err != nil {
// images have finished loading, as that portion of startup may block for an arbitrary return err
// amount of time depending on how long it takes to import whatever the user has placed }
// in the images directory.
if cfg.ContainerRuntimeReady != nil {
close(cfg.ContainerRuntimeReady)
} }
notifySocket := os.Getenv("NOTIFY_SOCKET") notifySocket := os.Getenv("NOTIFY_SOCKET")
...@@ -282,8 +279,9 @@ func RunStandalone(ctx context.Context, cfg cmds.Agent) error { ...@@ -282,8 +279,9 @@ func RunStandalone(ctx context.Context, cfg cmds.Agent) error {
return err return err
} }
if cfg.ContainerRuntimeReady != nil { // this is a no-op just to get the cri ready channel closed
close(cfg.ContainerRuntimeReady) if err := executor.CRI(ctx, nodeConfig); err != nil {
return err
} }
if err := tunnelSetup(ctx, nodeConfig, cfg, proxy); err != nil { if err := tunnelSetup(ctx, nodeConfig, cfg, proxy); err != nil {
...@@ -513,53 +511,34 @@ func updateAddressAnnotations(nodeConfig *daemonconfig.Node, nodeAnnotations map ...@@ -513,53 +511,34 @@ func updateAddressAnnotations(nodeConfig *daemonconfig.Node, nodeAnnotations map
return result, !equality.Semantic.DeepEqual(nodeAnnotations, result) return result, !equality.Semantic.DeepEqual(nodeAnnotations, result)
} }
// setupTunnelAndRunAgent should start the setup tunnel before starting kubelet and kubeproxy // setupTunnelAndRunAgent starts the agent tunnel, cert expiry monitoring, and
// there are special case for etcd agents, it will wait until it can find the apiaddress from // runs the Agent (cri+kubelet). On etcd-only nodes, an extra goroutine is
// the address channel and update the proxy with the servers addresses, if in rke2 we need to // started to retrieve apiserver addresses from the datastore. On other node
// start the agent before the tunnel is setup to allow kubelet to start first and start the pods // types, this is done later by the tunnel setup, which starts goroutines to
// watch apiserver endpoints.
func setupTunnelAndRunAgent(ctx context.Context, nodeConfig *daemonconfig.Node, cfg cmds.Agent, proxy proxy.Proxy) error { func setupTunnelAndRunAgent(ctx context.Context, nodeConfig *daemonconfig.Node, cfg cmds.Agent, proxy proxy.Proxy) error {
var agentRan bool // only need to get apiserver addresses from the datastore on an etcd-only node that is not being reset
// IsAPIServerLBEnabled is used as a shortcut for detecting RKE2, where the kubelet needs to
// be run earlier in order to manage static pods. This should probably instead query a
// flag on the executor or something.
if !cfg.ClusterReset && cfg.ETCDAgent { if !cfg.ClusterReset && cfg.ETCDAgent {
// ETCDAgent is only set to true on servers that are started with --disable-apiserver. go waitForAPIServerAddresses(ctx, nodeConfig, cfg, proxy)
// In this case, we may be running without an apiserver available in the cluster, and need
// to wait for one to register and post it's address into APIAddressCh so that we can update
// the LB proxy with its address.
if proxy.IsAPIServerLBEnabled() {
// On RKE2, the agent needs to be started early to run the etcd static pod.
if err := agent.Agent(ctx, nodeConfig, proxy); err != nil {
return err
}
agentRan = true
}
if err := waitForAPIServerAddresses(ctx, nodeConfig, cfg, proxy); err != nil {
return err
}
} else if cfg.ClusterReset && proxy.IsAPIServerLBEnabled() {
// If we're doing a cluster-reset on RKE2, the kubelet needs to be started early to clean
// up static pods.
if err := agent.Agent(ctx, nodeConfig, proxy); err != nil {
return err
}
agentRan = true
} }
if err := tunnelSetup(ctx, nodeConfig, cfg, proxy); err != nil { if err := tunnelSetup(ctx, nodeConfig, cfg, proxy); err != nil {
return err return err
} }
if err := certMonitorSetup(ctx, nodeConfig, cfg); err != nil { if err := certMonitorSetup(ctx, nodeConfig, cfg); err != nil {
return err return err
} }
if !agentRan { return agent.Agent(ctx, nodeConfig, proxy)
return agent.Agent(ctx, nodeConfig, proxy)
}
return nil
} }
func waitForAPIServerAddresses(ctx context.Context, nodeConfig *daemonconfig.Node, cfg cmds.Agent, proxy proxy.Proxy) error { // waitForAPIServerAddresses syncs apiserver addresses from the datastore. This
// is also handled by the agent tunnel watch, but on etcd-only nodes we need to
// read apiserver addresses from APIAddressCh before the agent has a
// connection to the apiserver. This does not return until addresses or set,
// or the context is cancelled.
func waitForAPIServerAddresses(ctx context.Context, nodeConfig *daemonconfig.Node, cfg cmds.Agent, proxy proxy.Proxy) {
var localSupervisorDefault bool var localSupervisorDefault bool
if addresses := proxy.SupervisorAddresses(); len(addresses) > 0 { if addresses := proxy.SupervisorAddresses(); len(addresses) > 0 {
host, _, _ := net.SplitHostPort(addresses[0]) host, _, _ := net.SplitHostPort(addresses[0])
...@@ -586,9 +565,9 @@ func waitForAPIServerAddresses(ctx context.Context, nodeConfig *daemonconfig.Nod ...@@ -586,9 +565,9 @@ func waitForAPIServerAddresses(ctx context.Context, nodeConfig *daemonconfig.Nod
proxy.SetSupervisorDefault(addresses[0]) proxy.SetSupervisorDefault(addresses[0])
} }
proxy.Update(addresses) proxy.Update(addresses)
return nil return
case <-ctx.Done(): case <-ctx.Done():
return ctx.Err() return
} }
} }
} }
......
...@@ -8,7 +8,6 @@ import ( ...@@ -8,7 +8,6 @@ import (
"net" "net"
"os" "os"
"strconv" "strconv"
"sync"
"time" "time"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
...@@ -49,6 +48,7 @@ var ( ...@@ -49,6 +48,7 @@ var (
type agentTunnel struct { type agentTunnel struct {
client kubernetes.Interface client kubernetes.Interface
tlsConfig *tls.Config
cidrs cidranger.Ranger cidrs cidranger.Ranger
ports map[string]bool ports map[string]bool
mode string mode string
...@@ -69,6 +69,8 @@ func (p *podEntry) Network() net.IPNet { ...@@ -69,6 +69,8 @@ func (p *podEntry) Network() net.IPNet {
return p.cidr return p.cidr
} }
// Setup sets up the agent tunnel, which is reponsible for connecting websocket tunnels to
// control-plane nodes, syncing endpoints for the tunnel authorizer, and updating proxy endpoints.
func Setup(ctx context.Context, config *daemonconfig.Node, proxy proxy.Proxy) error { func Setup(ctx context.Context, config *daemonconfig.Node, proxy proxy.Proxy) error {
client, err := util.GetClientSet(config.AgentConfig.KubeConfigK3sController) client, err := util.GetClientSet(config.AgentConfig.KubeConfigK3sController)
if err != nil { if err != nil {
...@@ -87,6 +89,7 @@ func Setup(ctx context.Context, config *daemonconfig.Node, proxy proxy.Proxy) er ...@@ -87,6 +89,7 @@ func Setup(ctx context.Context, config *daemonconfig.Node, proxy proxy.Proxy) er
tunnel := &agentTunnel{ tunnel := &agentTunnel{
client: client, client: client,
tlsConfig: tlsConfig,
cidrs: cidranger.NewPCTrieRanger(), cidrs: cidranger.NewPCTrieRanger(),
ports: map[string]bool{}, ports: map[string]bool{},
mode: config.EgressSelectorMode, mode: config.EgressSelectorMode,
...@@ -95,6 +98,14 @@ func Setup(ctx context.Context, config *daemonconfig.Node, proxy proxy.Proxy) er ...@@ -95,6 +98,14 @@ func Setup(ctx context.Context, config *daemonconfig.Node, proxy proxy.Proxy) er
startTime: time.Now().Truncate(time.Second), startTime: time.Now().Truncate(time.Second),
} }
go tunnel.startWatches(ctx, config, proxy)
return nil
}
// startWatches starts watching for changes to endpoints, both for the tunnel authorizer,
// and to sync supervisor addresses into the proxy. This will block until the context is cancelled.
func (a *agentTunnel) startWatches(ctx context.Context, config *daemonconfig.Node, proxy proxy.Proxy) {
rbacReady := make(chan struct{}) rbacReady := make(chan struct{})
go func() { go func() {
<-executor.APIServerReadyChan() <-executor.APIServerReadyChan()
...@@ -113,15 +124,15 @@ func Setup(ctx context.Context, config *daemonconfig.Node, proxy proxy.Proxy) er ...@@ -113,15 +124,15 @@ func Setup(ctx context.Context, config *daemonconfig.Node, proxy proxy.Proxy) er
// signifying that this is an agentless server that will not register a node. // signifying that this is an agentless server that will not register a node.
if config.ContainerRuntimeEndpoint != "/dev/null" { if config.ContainerRuntimeEndpoint != "/dev/null" {
// Allow the kubelet port, as published via our node object. // Allow the kubelet port, as published via our node object.
go tunnel.setKubeletPort(ctx, rbacReady) go a.setKubeletPort(ctx, rbacReady)
switch tunnel.mode { switch a.mode {
case daemonconfig.EgressSelectorModeCluster: case daemonconfig.EgressSelectorModeCluster:
// In Cluster mode, we allow the cluster CIDRs, and any connections to the node's IPs for pods using host network. // In Cluster mode, we allow the cluster CIDRs, and any connections to the node's IPs for pods using host network.
tunnel.clusterAuth(config) a.clusterAuth(config)
case daemonconfig.EgressSelectorModePod: case daemonconfig.EgressSelectorModePod:
// In Pod mode, we watch pods assigned to this node, and allow their addresses, as well as ports used by containers with host network. // In Pod mode, we watch pods assigned to this node, and allow their addresses, as well as ports used by containers with host network.
go tunnel.watchPods(ctx, rbacReady, config) go a.watchPods(ctx, rbacReady, config)
} }
} }
...@@ -150,7 +161,7 @@ func Setup(ctx context.Context, config *daemonconfig.Node, proxy proxy.Proxy) er ...@@ -150,7 +161,7 @@ func Setup(ctx context.Context, config *daemonconfig.Node, proxy proxy.Proxy) er
} }
proxy.Update(addresses) proxy.Update(addresses)
} else { } else {
if endpoint, err := client.CoreV1().Endpoints(metav1.NamespaceDefault).Get(ctx, "kubernetes", metav1.GetOptions{}); err != nil { if endpoint, err := a.client.CoreV1().Endpoints(metav1.NamespaceDefault).Get(ctx, "kubernetes", metav1.GetOptions{}); err != nil {
logrus.Errorf("Failed to get apiserver addresses from kubernetes endpoints: %v", err) logrus.Errorf("Failed to get apiserver addresses from kubernetes endpoints: %v", err)
} else { } else {
addresses := util.GetAddresses(endpoint) addresses := util.GetAddresses(endpoint)
...@@ -162,24 +173,7 @@ func Setup(ctx context.Context, config *daemonconfig.Node, proxy proxy.Proxy) er ...@@ -162,24 +173,7 @@ func Setup(ctx context.Context, config *daemonconfig.Node, proxy proxy.Proxy) er
} }
} }
wg := &sync.WaitGroup{} a.watchEndpoints(ctx, rbacReady, config, proxy)
go tunnel.watchEndpoints(ctx, rbacReady, wg, tlsConfig, config, proxy)
wait := make(chan int, 1)
go func() {
wg.Wait()
wait <- 0
}()
select {
case <-ctx.Done():
logrus.Error("Tunnel context canceled while waiting for connection")
return ctx.Err()
case <-wait:
}
return nil
} }
// setKubeletPort retrieves the configured kubelet port from our node object // setKubeletPort retrieves the configured kubelet port from our node object
...@@ -307,8 +301,8 @@ func (a *agentTunnel) watchPods(ctx context.Context, rbacReady <-chan struct{}, ...@@ -307,8 +301,8 @@ func (a *agentTunnel) watchPods(ctx context.Context, rbacReady <-chan struct{},
// WatchEndpoints attempts to create tunnels to all supervisor addresses. Once the // WatchEndpoints attempts to create tunnels to all supervisor addresses. Once the
// apiserver is up, go into a watch loop, adding and removing tunnels as endpoints come // apiserver is up, go into a watch loop, adding and removing tunnels as endpoints come
// and go from the cluster. // and go from the cluster.
func (a *agentTunnel) watchEndpoints(ctx context.Context, rbacReady <-chan struct{}, wg *sync.WaitGroup, tlsConfig *tls.Config, node *daemonconfig.Node, proxy proxy.Proxy) { func (a *agentTunnel) watchEndpoints(ctx context.Context, rbacReady <-chan struct{}, node *daemonconfig.Node, proxy proxy.Proxy) {
syncProxyAddresses := a.getProxySyncer(ctx, wg, tlsConfig, proxy) syncProxyAddresses := a.getProxySyncer(ctx, proxy)
refreshFromSupervisor := getAPIServersRequester(node, proxy, syncProxyAddresses) refreshFromSupervisor := getAPIServersRequester(node, proxy, syncProxyAddresses)
<-rbacReady <-rbacReady
...@@ -395,17 +389,12 @@ type agentConnection struct { ...@@ -395,17 +389,12 @@ type agentConnection struct {
// connect initiates a connection to the remotedialer server. Incoming dial requests from // connect initiates a connection to the remotedialer server. Incoming dial requests from
// the server will be checked by the authorizer function prior to being fulfilled. // the server will be checked by the authorizer function prior to being fulfilled.
func (a *agentTunnel) connect(rootCtx context.Context, waitGroup *sync.WaitGroup, address string, tlsConfig *tls.Config) agentConnection { func (a *agentTunnel) connect(rootCtx context.Context, address string) agentConnection {
var status loadbalancer.HealthCheckResult var status loadbalancer.HealthCheckResult
wsURL := fmt.Sprintf("wss://%s/v1-"+version.Program+"/connect", address) wsURL := fmt.Sprintf("wss://%s/v1-"+version.Program+"/connect", address)
ws := &websocket.Dialer{ ws := &websocket.Dialer{
TLSClientConfig: tlsConfig, TLSClientConfig: a.tlsConfig,
}
once := sync.Once{}
if waitGroup != nil {
waitGroup.Add(1)
} }
ctx, cancel := context.WithCancel(rootCtx) ctx, cancel := context.WithCancel(rootCtx)
...@@ -416,9 +405,6 @@ func (a *agentTunnel) connect(rootCtx context.Context, waitGroup *sync.WaitGroup ...@@ -416,9 +405,6 @@ func (a *agentTunnel) connect(rootCtx context.Context, waitGroup *sync.WaitGroup
onConnect := func(_ context.Context, _ *remotedialer.Session) error { onConnect := func(_ context.Context, _ *remotedialer.Session) error {
status = loadbalancer.HealthCheckResultOK status = loadbalancer.HealthCheckResultOK
logrus.WithField("url", wsURL).Info("Remotedialer connected to proxy") logrus.WithField("url", wsURL).Info("Remotedialer connected to proxy")
if waitGroup != nil {
once.Do(waitGroup.Done)
}
return nil return nil
} }
...@@ -435,9 +421,6 @@ func (a *agentTunnel) connect(rootCtx context.Context, waitGroup *sync.WaitGroup ...@@ -435,9 +421,6 @@ func (a *agentTunnel) connect(rootCtx context.Context, waitGroup *sync.WaitGroup
} }
// If the context has been cancelled, exit the goroutine instead of retrying // If the context has been cancelled, exit the goroutine instead of retrying
if ctx.Err() != nil { if ctx.Err() != nil {
if waitGroup != nil {
once.Do(waitGroup.Done)
}
return return
} }
} }
...@@ -476,13 +459,13 @@ type proxySyncer func(addresses []string) ...@@ -476,13 +459,13 @@ type proxySyncer func(addresses []string)
// getProxySyncer returns a function that can be called to update the list of supervisors. // getProxySyncer returns a function that can be called to update the list of supervisors.
// This function is responsible for connecting to or disconnecting websocket tunnels, // This function is responsible for connecting to or disconnecting websocket tunnels,
// as well as updating the proxy loadbalancer server list. // as well as updating the proxy loadbalancer server list.
func (a *agentTunnel) getProxySyncer(ctx context.Context, wg *sync.WaitGroup, tlsConfig *tls.Config, proxy proxy.Proxy) proxySyncer { func (a *agentTunnel) getProxySyncer(ctx context.Context, proxy proxy.Proxy) proxySyncer {
disconnect := map[string]context.CancelFunc{} disconnect := map[string]context.CancelFunc{}
// Attempt to connect to inital list of addresses, storing their cancellation // Attempt to connect to inital list of addresses, storing their cancellation
// function for later when we need to disconnect. // function for later when we need to disconnect.
for _, address := range proxy.SupervisorAddresses() { for _, address := range proxy.SupervisorAddresses() {
if _, ok := disconnect[address]; !ok { if _, ok := disconnect[address]; !ok {
conn := a.connect(ctx, wg, address, tlsConfig) conn := a.connect(ctx, address)
disconnect[address] = conn.cancel disconnect[address] = conn.cancel
proxy.SetHealthCheck(address, conn.healthCheck) proxy.SetHealthCheck(address, conn.healthCheck)
} }
...@@ -538,7 +521,7 @@ func (a *agentTunnel) getProxySyncer(ctx context.Context, wg *sync.WaitGroup, tl ...@@ -538,7 +521,7 @@ func (a *agentTunnel) getProxySyncer(ctx context.Context, wg *sync.WaitGroup, tl
// add new servers // add new servers
for address := range addedAddresses { for address := range addedAddresses {
if _, ok := disconnect[address]; !ok { if _, ok := disconnect[address]; !ok {
conn := a.connect(ctx, nil, address, tlsConfig) conn := a.connect(ctx, address)
logrus.Infof("Started tunnel to %s", address) logrus.Infof("Started tunnel to %s", address)
disconnect[address] = conn.cancel disconnect[address] = conn.cancel
proxy.SetHealthCheck(address, conn.healthCheck) proxy.SetHealthCheck(address, conn.healthCheck)
......
...@@ -46,7 +46,7 @@ func commandSetup(app *cli.Context, cfg *cmds.Server, sc *server.Config) (string ...@@ -46,7 +46,7 @@ func commandSetup(app *cli.Context, cfg *cmds.Server, sc *server.Config) (string
cfg.Token = string(bytes.TrimRight(tokenByte, "\n")) cfg.Token = string(bytes.TrimRight(tokenByte, "\n"))
} }
sc.ControlConfig.Token = cfg.Token sc.ControlConfig.Token = cfg.Token
sc.ControlConfig.Runtime = config.NewRuntime(nil) sc.ControlConfig.Runtime = config.NewRuntime()
return dataDir, nil return dataDir, nil
} }
...@@ -287,7 +287,7 @@ func rotateCA(app *cli.Context, cfg *cmds.Server, sync *cmds.CertRotateCA) error ...@@ -287,7 +287,7 @@ func rotateCA(app *cli.Context, cfg *cmds.Server, sync *cmds.CertRotateCA) error
// Set up dummy server config for reading new bootstrap data from disk. // Set up dummy server config for reading new bootstrap data from disk.
tmpServer := &config.Control{ tmpServer := &config.Control{
Runtime: config.NewRuntime(nil), Runtime: config.NewRuntime(),
DataDir: sync.CACertPath, DataDir: sync.CACertPath,
} }
deps.CreateRuntimeCertFiles(tmpServer) deps.CreateRuntimeCertFiles(tmpServer)
......
...@@ -56,7 +56,6 @@ type Agent struct { ...@@ -56,7 +56,6 @@ type Agent struct {
Taints cli.StringSlice Taints cli.StringSlice
ImageCredProvBinDir string ImageCredProvBinDir string
ImageCredProvConfig string ImageCredProvConfig string
ContainerRuntimeReady chan<- struct{}
AgentShared AgentShared
} }
......
...@@ -113,11 +113,9 @@ func run(app *cli.Context, cfg *cmds.Server, leaderControllers server.CustomCont ...@@ -113,11 +113,9 @@ func run(app *cli.Context, cfg *cmds.Server, leaderControllers server.CustomCont
} }
} }
containerRuntimeReady := make(chan struct{})
serverConfig := server.Config{} serverConfig := server.Config{}
serverConfig.DisableAgent = cfg.DisableAgent serverConfig.DisableAgent = cfg.DisableAgent
serverConfig.ControlConfig.Runtime = config.NewRuntime(containerRuntimeReady) serverConfig.ControlConfig.Runtime = config.NewRuntime()
serverConfig.ControlConfig.Token = cfg.Token serverConfig.ControlConfig.Token = cfg.Token
serverConfig.ControlConfig.AgentToken = cfg.AgentToken serverConfig.ControlConfig.AgentToken = cfg.AgentToken
serverConfig.ControlConfig.JoinURL = cfg.ServerURL serverConfig.ControlConfig.JoinURL = cfg.ServerURL
...@@ -525,7 +523,6 @@ func run(app *cli.Context, cfg *cmds.Server, leaderControllers server.CustomCont ...@@ -525,7 +523,6 @@ func run(app *cli.Context, cfg *cmds.Server, leaderControllers server.CustomCont
} }
agentConfig := cmds.AgentConfig agentConfig := cmds.AgentConfig
agentConfig.ContainerRuntimeReady = containerRuntimeReady
agentConfig.Debug = app.GlobalBool("debug") agentConfig.Debug = app.GlobalBool("debug")
agentConfig.DataDir = filepath.Dir(serverConfig.ControlConfig.DataDir) agentConfig.DataDir = filepath.Dir(serverConfig.ControlConfig.DataDir)
agentConfig.ServerURL = url agentConfig.ServerURL = url
......
...@@ -312,7 +312,6 @@ type ControlRuntimeBootstrap struct { ...@@ -312,7 +312,6 @@ type ControlRuntimeBootstrap struct {
type ControlRuntime struct { type ControlRuntime struct {
ControlRuntimeBootstrap ControlRuntimeBootstrap
ContainerRuntimeReady <-chan struct{}
ETCDReady <-chan struct{} ETCDReady <-chan struct{}
StartupHooksWg *sync.WaitGroup StartupHooksWg *sync.WaitGroup
ClusterControllerStarts map[string]leader.Callback ClusterControllerStarts map[string]leader.Callback
...@@ -392,9 +391,8 @@ type CoreFactory interface { ...@@ -392,9 +391,8 @@ type CoreFactory interface {
Start(ctx context.Context, defaultThreadiness int) error Start(ctx context.Context, defaultThreadiness int) error
} }
func NewRuntime(containerRuntimeReady <-chan struct{}) *ControlRuntime { func NewRuntime() *ControlRuntime {
return &ControlRuntime{ return &ControlRuntime{
ContainerRuntimeReady: containerRuntimeReady,
ClusterControllerStarts: map[string]leader.Callback{}, ClusterControllerStarts: map[string]leader.Callback{},
LeaderElectedClusterControllerStarts: map[string]leader.Callback{}, LeaderElectedClusterControllerStarts: map[string]leader.Callback{},
} }
......
...@@ -14,6 +14,7 @@ import ( ...@@ -14,6 +14,7 @@ import (
"time" "time"
"github.com/k3s-io/k3s/pkg/agent/containerd" "github.com/k3s-io/k3s/pkg/agent/containerd"
"github.com/k3s-io/k3s/pkg/agent/cri"
"github.com/k3s-io/k3s/pkg/agent/cridockerd" "github.com/k3s-io/k3s/pkg/agent/cridockerd"
"github.com/k3s-io/k3s/pkg/cli/cmds" "github.com/k3s-io/k3s/pkg/cli/cmds"
daemonconfig "github.com/k3s-io/k3s/pkg/daemons/config" daemonconfig "github.com/k3s-io/k3s/pkg/daemons/config"
...@@ -44,6 +45,7 @@ func init() { ...@@ -44,6 +45,7 @@ func init() {
func (e *Embedded) Bootstrap(ctx context.Context, nodeConfig *daemonconfig.Node, cfg cmds.Agent) error { func (e *Embedded) Bootstrap(ctx context.Context, nodeConfig *daemonconfig.Node, cfg cmds.Agent) error {
e.apiServerReady = util.APIServerReadyChan(ctx, nodeConfig.AgentConfig.KubeConfigK3sController, util.DefaultAPIServerReadyTimeout) e.apiServerReady = util.APIServerReadyChan(ctx, nodeConfig.AgentConfig.KubeConfigK3sController, util.DefaultAPIServerReadyTimeout)
e.criReady = make(chan struct{})
e.nodeConfig = nodeConfig e.nodeConfig = nodeConfig
go func() { go func() {
...@@ -235,16 +237,34 @@ func (e *Embedded) CurrentETCDOptions() (InitialOptions, error) { ...@@ -235,16 +237,34 @@ func (e *Embedded) CurrentETCDOptions() (InitialOptions, error) {
} }
func (e *Embedded) Containerd(ctx context.Context, cfg *daemonconfig.Node) error { func (e *Embedded) Containerd(ctx context.Context, cfg *daemonconfig.Node) error {
defer close(e.criReady)
return containerd.Run(ctx, cfg) return containerd.Run(ctx, cfg)
} }
func (e *Embedded) Docker(ctx context.Context, cfg *daemonconfig.Node) error { func (e *Embedded) Docker(ctx context.Context, cfg *daemonconfig.Node) error {
defer close(e.criReady)
return cridockerd.Run(ctx, cfg) return cridockerd.Run(ctx, cfg)
} }
func (e *Embedded) CRI(ctx context.Context, cfg *daemonconfig.Node) error {
defer close(e.criReady)
// agentless sets cri socket path to /dev/null to indicate no CRI is needed
if cfg.ContainerRuntimeEndpoint != "/dev/null" {
return cri.WaitForService(ctx, cfg.ContainerRuntimeEndpoint, "CRI")
}
return nil
}
func (e *Embedded) APIServerReadyChan() <-chan struct{} { func (e *Embedded) APIServerReadyChan() <-chan struct{} {
if e.apiServerReady == nil { if e.apiServerReady == nil {
panic("executor not bootstrapped") panic("executor not bootstrapped")
} }
return e.apiServerReady return e.apiServerReady
} }
func (e *Embedded) CRIReadyChan() <-chan struct{} {
if e.criReady == nil {
panic("executor not bootstrapped")
}
return e.criReady
}
...@@ -17,6 +17,7 @@ import ( ...@@ -17,6 +17,7 @@ import (
// of the embedded execututor is disabled by build flags // of the embedded execututor is disabled by build flags
type Embedded struct { type Embedded struct {
apiServerReady <-chan struct{} apiServerReady <-chan struct{}
criReady chan struct{}
nodeConfig *daemonconfig.Node nodeConfig *daemonconfig.Node
} }
......
...@@ -33,7 +33,9 @@ type Executor interface { ...@@ -33,7 +33,9 @@ type Executor interface {
CloudControllerManager(ctx context.Context, ccmRBACReady <-chan struct{}, args []string) error CloudControllerManager(ctx context.Context, ccmRBACReady <-chan struct{}, args []string) error
Containerd(ctx context.Context, node *daemonconfig.Node) error Containerd(ctx context.Context, node *daemonconfig.Node) error
Docker(ctx context.Context, node *daemonconfig.Node) error Docker(ctx context.Context, node *daemonconfig.Node) error
CRI(ctx context.Context, node *daemonconfig.Node) error
APIServerReadyChan() <-chan struct{} APIServerReadyChan() <-chan struct{}
CRIReadyChan() <-chan struct{}
} }
type ETCDConfig struct { type ETCDConfig struct {
...@@ -183,6 +185,14 @@ func Docker(ctx context.Context, config *daemonconfig.Node) error { ...@@ -183,6 +185,14 @@ func Docker(ctx context.Context, config *daemonconfig.Node) error {
return executor.Docker(ctx, config) return executor.Docker(ctx, config)
} }
func CRI(ctx context.Context, config *daemonconfig.Node) error {
return executor.CRI(ctx, config)
}
func APIServerReadyChan() <-chan struct{} { func APIServerReadyChan() <-chan struct{} {
return executor.APIServerReadyChan() return executor.APIServerReadyChan()
} }
func CRIReadyChan() <-chan struct{} {
return executor.CRIReadyChan()
}
...@@ -343,7 +343,7 @@ func (e *ETCD) IsInitialized() (bool, error) { ...@@ -343,7 +343,7 @@ func (e *ETCD) IsInitialized() (bool, error) {
func (e *ETCD) Reset(ctx context.Context, rebootstrap func() error) error { func (e *ETCD) Reset(ctx context.Context, rebootstrap func() error) error {
// Wait for etcd to come up as a new single-node cluster, then exit // Wait for etcd to come up as a new single-node cluster, then exit
go func() { go func() {
<-e.config.Runtime.ContainerRuntimeReady <-executor.CRIReadyChan()
t := time.NewTicker(5 * time.Second) t := time.NewTicker(5 * time.Second)
defer t.Stop() defer t.Stop()
for range t.C { for range t.C {
...@@ -497,7 +497,7 @@ func (e *ETCD) Start(ctx context.Context, clientAccessInfo *clientaccess.Info) e ...@@ -497,7 +497,7 @@ func (e *ETCD) Start(ctx context.Context, clientAccessInfo *clientaccess.Info) e
select { select {
case <-time.After(30 * time.Second): case <-time.After(30 * time.Second):
logrus.Infof("Waiting for container runtime to become ready before joining etcd cluster") logrus.Infof("Waiting for container runtime to become ready before joining etcd cluster")
case <-e.config.Runtime.ContainerRuntimeReady: case <-executor.CRIReadyChan():
if err := wait.PollUntilContextCancel(ctx, time.Second, true, func(ctx context.Context) (bool, error) { if err := wait.PollUntilContextCancel(ctx, time.Second, true, func(ctx context.Context) (bool, error) {
if err := e.join(ctx, clientAccessInfo); err != nil { if err := e.join(ctx, clientAccessInfo); err != nil {
// Retry the join if waiting for another member to be promoted, or waiting for peers to connect after promotion // Retry the join if waiting for another member to be promoted, or waiting for peers to connect after promotion
...@@ -726,7 +726,6 @@ func (e *ETCD) handler(next http.Handler) http.Handler { ...@@ -726,7 +726,6 @@ func (e *ETCD) handler(next http.Handler) http.Handler {
} }
// infoHandler returns etcd cluster information. This is used by new members when joining the cluster. // infoHandler returns etcd cluster information. This is used by new members when joining the cluster.
// If we can't retrieve an actual MemberList from etcd, we return a canned response with only the local node listed.
func (e *ETCD) infoHandler() http.Handler { func (e *ETCD) infoHandler() http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodGet { if req.Method != http.MethodGet {
...@@ -734,6 +733,11 @@ func (e *ETCD) infoHandler() http.Handler { ...@@ -734,6 +733,11 @@ func (e *ETCD) infoHandler() http.Handler {
return return
} }
if e.client == nil {
util.SendError(errors.New("failed to get etcd MemberList: etcd not started"), rw, req, http.StatusInternalServerError)
return
}
ctx, cancel := context.WithTimeout(req.Context(), 2*time.Second) ctx, cancel := context.WithTimeout(req.Context(), 2*time.Second)
defer cancel() defer cancel()
...@@ -1157,7 +1161,7 @@ func (e *ETCD) RemovePeer(ctx context.Context, name, address string, allowSelfRe ...@@ -1157,7 +1161,7 @@ func (e *ETCD) RemovePeer(ctx context.Context, name, address string, allowSelfRe
// being promoted to full voting member. The checks only run on the cluster member that is // being promoted to full voting member. The checks only run on the cluster member that is
// the etcd leader. // the etcd leader.
func (e *ETCD) manageLearners(ctx context.Context) { func (e *ETCD) manageLearners(ctx context.Context) {
<-e.config.Runtime.ContainerRuntimeReady <-executor.CRIReadyChan()
t := time.NewTicker(manageTickerTime) t := time.NewTicker(manageTickerTime)
defer t.Stop() defer t.Stop()
......
...@@ -61,7 +61,7 @@ func generateTestConfig() *config.Control { ...@@ -61,7 +61,7 @@ func generateTestConfig() *config.Control {
} }
return &config.Control{ return &config.Control{
ServerNodeName: hostname, ServerNodeName: hostname,
Runtime: config.NewRuntime(containerRuntimeReady), Runtime: config.NewRuntime(),
HTTPSPort: 6443, HTTPSPort: 6443,
SupervisorPort: 6443, SupervisorPort: 6443,
AdvertisePort: 6443, AdvertisePort: 6443,
......
...@@ -56,7 +56,7 @@ func caCertReplace(control *config.Control, buf io.ReadCloser, force bool) error ...@@ -56,7 +56,7 @@ func caCertReplace(control *config.Control, buf io.ReadCloser, force bool) error
} }
defer os.RemoveAll(tmpdir) defer os.RemoveAll(tmpdir)
runtime := config.NewRuntime(nil) runtime := config.NewRuntime()
runtime.EtcdConfig = control.Runtime.EtcdConfig runtime.EtcdConfig = control.Runtime.EtcdConfig
runtime.ServerToken = control.Runtime.ServerToken runtime.ServerToken = control.Runtime.ServerToken
......
...@@ -44,11 +44,7 @@ func CleanupDataDir(cnf *config.Control) { ...@@ -44,11 +44,7 @@ func CleanupDataDir(cnf *config.Control) {
// config.ControlRuntime with all the appropriate certificate keys. // config.ControlRuntime with all the appropriate certificate keys.
func GenerateRuntime(cnf *config.Control) error { func GenerateRuntime(cnf *config.Control) error {
// reuse ready channel from existing runtime if set // reuse ready channel from existing runtime if set
var readyCh <-chan struct{} cnf.Runtime = config.NewRuntime()
if cnf.Runtime != nil {
readyCh = cnf.Runtime.ContainerRuntimeReady
}
cnf.Runtime = config.NewRuntime(readyCh)
if err := GenerateDataDir(cnf); err != nil { if err := GenerateDataDir(cnf); err != nil {
return err return err
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment