mirror of
https://github.com/k3s-io/k3s.git
synced 2024-06-07 19:41:36 +00:00
9033891f88
Instead of skipping the manifest when listing the directory, we now skip creating it in the first place. This allows users to deploy manifests that replaces the ones bundled, without having to come up with a new name. Fixes #230.
287 lines
7.1 KiB
Go
287 lines
7.1 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io/ioutil"
|
|
net2 "net"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/rancher/k3s/pkg/daemons/config"
|
|
"github.com/rancher/k3s/pkg/daemons/control"
|
|
"github.com/rancher/k3s/pkg/datadir"
|
|
"github.com/rancher/k3s/pkg/deploy"
|
|
"github.com/rancher/k3s/pkg/helm"
|
|
"github.com/rancher/k3s/pkg/servicelb"
|
|
"github.com/rancher/k3s/pkg/static"
|
|
"github.com/rancher/k3s/pkg/tls"
|
|
appsv1 "github.com/rancher/k3s/types/apis/apps/v1"
|
|
batchv1 "github.com/rancher/k3s/types/apis/batch/v1"
|
|
corev1 "github.com/rancher/k3s/types/apis/core/v1"
|
|
v1 "github.com/rancher/k3s/types/apis/k3s.cattle.io/v1"
|
|
rbacv1 "github.com/rancher/k3s/types/apis/rbac.authorization.k8s.io/v1"
|
|
"github.com/rancher/norman"
|
|
"github.com/rancher/norman/pkg/clientaccess"
|
|
"github.com/rancher/norman/pkg/dynamiclistener"
|
|
"github.com/rancher/norman/pkg/resolvehome"
|
|
"github.com/rancher/norman/types"
|
|
"github.com/sirupsen/logrus"
|
|
"k8s.io/apimachinery/pkg/util/net"
|
|
)
|
|
|
|
func resolveDataDir(dataDir string) (string, error) {
|
|
if dataDir == "" {
|
|
if os.Getuid() == 0 {
|
|
dataDir = "/var/lib/rancher/k3s"
|
|
} else {
|
|
dataDir = "${HOME}/.rancher/k3s"
|
|
}
|
|
}
|
|
|
|
dataDir = filepath.Join(dataDir, "server")
|
|
return resolvehome.Resolve(dataDir)
|
|
}
|
|
|
|
func StartServer(ctx context.Context, config *Config) (string, error) {
|
|
if err := setupDataDirAndChdir(&config.ControlConfig); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if err := control.Server(ctx, &config.ControlConfig); err != nil {
|
|
return "", errors.Wrap(err, "starting kubernetes")
|
|
}
|
|
|
|
certs, err := startNorman(ctx, config)
|
|
if err != nil {
|
|
return "", errors.Wrap(err, "starting tls server")
|
|
}
|
|
|
|
ip, err := net.ChooseHostInterface()
|
|
if err != nil {
|
|
ip = net2.ParseIP("127.0.0.1")
|
|
}
|
|
printTokens(certs, ip.String(), &config.TLSConfig, &config.ControlConfig)
|
|
|
|
writeKubeConfig(certs, &config.TLSConfig, &config.ControlConfig)
|
|
|
|
return certs, nil
|
|
}
|
|
|
|
func startNorman(ctx context.Context, config *Config) (string, error) {
|
|
var (
|
|
err error
|
|
tlsServer dynamiclistener.ServerInterface
|
|
tlsConfig = &config.TLSConfig
|
|
controlConfig = &config.ControlConfig
|
|
)
|
|
|
|
tlsConfig.Handler = router(controlConfig, controlConfig.Runtime.Tunnel, func() (string, error) {
|
|
if tlsServer == nil {
|
|
return "", nil
|
|
}
|
|
return tlsServer.CACert()
|
|
})
|
|
|
|
normanConfig := &norman.Config{
|
|
Name: "k3s",
|
|
KubeConfig: controlConfig.Runtime.KubeConfigSystem,
|
|
Clients: []norman.ClientFactory{
|
|
v1.Factory,
|
|
appsv1.Factory,
|
|
corev1.Factory,
|
|
batchv1.Factory,
|
|
rbacv1.Factory,
|
|
},
|
|
Schemas: []*types.Schemas{
|
|
v1.Schemas,
|
|
},
|
|
CRDs: map[*types.APIVersion][]string{
|
|
&v1.APIVersion: {
|
|
v1.ListenerConfigGroupVersionKind.Kind,
|
|
v1.AddonGroupVersionKind.Kind,
|
|
v1.HelmChartGroupVersionKind.Kind,
|
|
},
|
|
},
|
|
IgnoredKubeConfigEnv: true,
|
|
GlobalSetup: func(ctx context.Context) (context.Context, error) {
|
|
tlsServer, err = tls.NewServer(ctx, v1.ClientsFrom(ctx).ListenerConfig, *tlsConfig)
|
|
return ctx, err
|
|
},
|
|
DisableLeaderElection: true,
|
|
MasterControllers: []norman.ControllerRegister{
|
|
helm.Register,
|
|
func(ctx context.Context) error {
|
|
return servicelb.Register(ctx, norman.GetServer(ctx).K8sClient, !config.DisableServiceLB)
|
|
},
|
|
func(ctx context.Context) error {
|
|
dataDir := filepath.Join(controlConfig.DataDir, "static")
|
|
return static.Stage(dataDir)
|
|
},
|
|
func(ctx context.Context) error {
|
|
dataDir := filepath.Join(controlConfig.DataDir, "manifests")
|
|
templateVars := map[string]string{"%{CLUSTER_DNS}%": controlConfig.ClusterDNS.String()}
|
|
if err := deploy.Stage(dataDir, templateVars, controlConfig.Skips); err != nil {
|
|
return err
|
|
}
|
|
if err := deploy.WatchFiles(ctx, dataDir); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
},
|
|
}
|
|
|
|
ctx, _, err = normanConfig.Build(ctx, nil)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
for {
|
|
certs, err := tlsServer.CACert()
|
|
if err != nil {
|
|
logrus.Infof("waiting to generate CA certs")
|
|
time.Sleep(time.Second)
|
|
continue
|
|
}
|
|
return certs, nil
|
|
}
|
|
}
|
|
|
|
func HomeKubeConfig(write bool) (string, error) {
|
|
if write {
|
|
if os.Getuid() == 0 {
|
|
return datadir.GlobalConfig, nil
|
|
}
|
|
return resolvehome.Resolve(datadir.HomeConfig)
|
|
}
|
|
|
|
if _, err := os.Stat(datadir.GlobalConfig); err == nil {
|
|
return datadir.GlobalConfig, nil
|
|
}
|
|
|
|
return resolvehome.Resolve(datadir.HomeConfig)
|
|
}
|
|
|
|
func printTokens(certs, advertiseIP string, tlsConfig *dynamiclistener.UserConfig, config *config.Control) {
|
|
var (
|
|
nodeFile string
|
|
)
|
|
|
|
if advertiseIP == "" {
|
|
advertiseIP = "localhost"
|
|
}
|
|
|
|
if len(config.Runtime.NodeToken) > 0 {
|
|
p := filepath.Join(config.DataDir, "node-token")
|
|
if err := writeToken(config.Runtime.NodeToken, p, certs); err == nil {
|
|
logrus.Infof("Node token is available at %s", p)
|
|
nodeFile = p
|
|
}
|
|
}
|
|
|
|
if len(nodeFile) > 0 {
|
|
printToken(tlsConfig.HTTPSPort, advertiseIP, "To join node to cluster:", "agent")
|
|
}
|
|
|
|
}
|
|
|
|
func writeKubeConfig(certs string, tlsConfig *dynamiclistener.UserConfig, config *config.Control) {
|
|
clientToken := FormatToken(config.Runtime.ClientToken, certs)
|
|
url := fmt.Sprintf("https://localhost:%d", tlsConfig.HTTPSPort)
|
|
kubeConfig, err := HomeKubeConfig(true)
|
|
def := true
|
|
if err != nil {
|
|
kubeConfig = filepath.Join(config.DataDir, "kubeconfig-k3s.yaml")
|
|
def = false
|
|
}
|
|
|
|
if config.KubeConfigOutput != "" {
|
|
kubeConfig = config.KubeConfigOutput
|
|
}
|
|
|
|
if err = clientaccess.AgentAccessInfoToKubeConfig(kubeConfig, url, clientToken); err != nil {
|
|
logrus.Errorf("Failed to generate kubeconfig: %v", err)
|
|
}
|
|
|
|
if config.KubeConfigMode != "" {
|
|
mode, err := strconv.ParseInt(config.KubeConfigMode, 8, 0)
|
|
if err == nil {
|
|
os.Chmod(kubeConfig, os.FileMode(mode))
|
|
} else {
|
|
logrus.Errorf("failed to set %s to mode %s: %v", kubeConfig, os.FileMode(mode), err)
|
|
}
|
|
} else {
|
|
os.Chmod(kubeConfig, os.FileMode(0644))
|
|
}
|
|
|
|
logrus.Infof("Wrote kubeconfig %s", kubeConfig)
|
|
if def {
|
|
logrus.Infof("Run: %s kubectl", filepath.Base(os.Args[0]))
|
|
}
|
|
}
|
|
|
|
func setupDataDirAndChdir(config *config.Control) error {
|
|
var (
|
|
err error
|
|
)
|
|
|
|
config.DataDir, err = resolveDataDir(config.DataDir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
dataDir := config.DataDir
|
|
|
|
if err := os.MkdirAll(dataDir, 0700); err != nil {
|
|
return errors.Wrapf(err, "can not mkdir %s", dataDir)
|
|
}
|
|
|
|
if err := os.Chdir(dataDir); err != nil {
|
|
return errors.Wrapf(err, "can not chdir %s", dataDir)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func printToken(httpsPort int, advertiseIP, prefix, cmd string) {
|
|
ip := advertiseIP
|
|
if ip == "" {
|
|
hostIP, err := net.ChooseHostInterface()
|
|
if err != nil {
|
|
logrus.Error(err)
|
|
}
|
|
ip = hostIP.String()
|
|
}
|
|
|
|
logrus.Infof("%s k3s %s -s https://%s:%d -t ${NODE_TOKEN}", prefix, cmd, ip, httpsPort)
|
|
}
|
|
|
|
func FormatToken(token string, certs string) string {
|
|
if len(token) == 0 {
|
|
return token
|
|
}
|
|
|
|
prefix := "K10"
|
|
if len(certs) > 0 {
|
|
digest := sha256.Sum256([]byte(certs))
|
|
prefix = "K10" + hex.EncodeToString(digest[:]) + "::"
|
|
}
|
|
|
|
return prefix + token
|
|
}
|
|
|
|
func writeToken(token, file, certs string) error {
|
|
if len(token) == 0 {
|
|
return nil
|
|
}
|
|
|
|
token = FormatToken(token, certs)
|
|
return ioutil.WriteFile(file, []byte(token+"\n"), 0600)
|
|
}
|