mirror of
https://github.com/rancher/rke.git
synced 2025-06-20 20:51:57 +00:00
Problem: When building a hybrid cluster with windows nodes there is only a single set of overrides you can use per service. This limits configuring the node as service args and prefix_path sometimes need to be specific for the different OS. Solution: Add support for `win_` prefixed parameters for cluster level `path_prefix` and service level `extra_args`, `extra_env` and `extra_binds`. Params will work as before, passing in the non `win_` prefixed params, IF you set the `win_` prefixed params it willy only use those meaning you will need to duplicate the params in both config sections of your rke cluster yaml.
172 lines
4.8 KiB
Go
172 lines
4.8 KiB
Go
package hosts
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/docker/docker/client"
|
|
"github.com/rancher/rke/docker"
|
|
"github.com/rancher/rke/log"
|
|
"github.com/rancher/rke/metadata"
|
|
"github.com/rancher/rke/util"
|
|
"github.com/sirupsen/logrus"
|
|
"golang.org/x/crypto/ssh"
|
|
"golang.org/x/crypto/ssh/agent"
|
|
)
|
|
|
|
const (
|
|
DockerAPIVersion = "1.24"
|
|
)
|
|
|
|
func (h *Host) TunnelUp(ctx context.Context, dialerFactory DialerFactory, clusterPrefixPath string, clusterVersion string) error {
|
|
if h.DClient != nil {
|
|
return nil
|
|
}
|
|
log.Infof(ctx, "[dialer] Setup tunnel for host [%s]", h.Address)
|
|
httpClient, err := h.newHTTPClient(dialerFactory)
|
|
if err != nil {
|
|
return fmt.Errorf("Can't establish dialer connection: %v", err)
|
|
}
|
|
// set Docker client
|
|
logrus.Debugf("Connecting to Docker API for host [%s]", h.Address)
|
|
h.DClient, err = client.NewClientWithOpts(
|
|
client.WithVersion(DockerAPIVersion),
|
|
client.WithHTTPClient(httpClient))
|
|
if err != nil {
|
|
return fmt.Errorf("Can't initiate NewClient: %v", err)
|
|
}
|
|
if err := checkDockerVersion(ctx, h, clusterVersion); err != nil {
|
|
return err
|
|
}
|
|
h.SetPrefixPath(clusterPrefixPath)
|
|
return nil
|
|
}
|
|
|
|
func (h *Host) TunnelUpLocal(ctx context.Context, clusterVersion string) error {
|
|
var err error
|
|
if h.DClient != nil {
|
|
return nil
|
|
}
|
|
// set Docker client
|
|
logrus.Debugf("Connecting to Docker API for host [%s]", h.Address)
|
|
h.DClient, err = client.NewEnvClient()
|
|
if err != nil {
|
|
return fmt.Errorf("Can't initiate NewClient: %v", err)
|
|
}
|
|
return checkDockerVersion(ctx, h, clusterVersion)
|
|
}
|
|
|
|
func checkDockerVersion(ctx context.Context, h *Host, clusterVersion string) error {
|
|
info, err := h.DClient.Info(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("Can't retrieve Docker Info: %v", err)
|
|
}
|
|
logrus.Debugf("Docker Info found for host [%s]: %#v", h.Address, info)
|
|
h.DockerInfo = info
|
|
if h.IgnoreDockerVersion {
|
|
return nil
|
|
}
|
|
K8sSemVer, err := util.StrToSemVer(clusterVersion)
|
|
if err != nil {
|
|
return fmt.Errorf("Error while parsing cluster version [%s]: %v", clusterVersion, err)
|
|
}
|
|
K8sVersion := fmt.Sprintf("%d.%d", K8sSemVer.Major, K8sSemVer.Minor)
|
|
isvalid, err := docker.IsSupportedDockerVersion(info, K8sVersion)
|
|
if err != nil {
|
|
return fmt.Errorf("Error while determining supported Docker version [%s]: %v", info.ServerVersion, err)
|
|
}
|
|
|
|
if !isvalid {
|
|
return fmt.Errorf("Unsupported Docker version found [%s] on host [%s], supported versions are %v", info.ServerVersion, h.Address, metadata.K8sVersionToDockerVersions[K8sVersion])
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func parsePrivateKey(keyBuff string) (ssh.Signer, error) {
|
|
return ssh.ParsePrivateKey([]byte(keyBuff))
|
|
}
|
|
|
|
func getSSHConfig(username, sshPrivateKeyString string, sshCertificateString string, useAgentAuth bool) (*ssh.ClientConfig, error) {
|
|
config := &ssh.ClientConfig{
|
|
User: username,
|
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
|
}
|
|
|
|
// Kind of a double check now.
|
|
if useAgentAuth {
|
|
if sshAgentSock := os.Getenv("SSH_AUTH_SOCK"); sshAgentSock != "" {
|
|
sshAgent, err := net.Dial("unix", sshAgentSock)
|
|
if err != nil {
|
|
return config, fmt.Errorf("Cannot connect to SSH Auth socket %q: %s", sshAgentSock, err)
|
|
}
|
|
|
|
config.Auth = append(config.Auth, ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers))
|
|
|
|
logrus.Debugf("using %q SSH_AUTH_SOCK", sshAgentSock)
|
|
return config, nil
|
|
}
|
|
}
|
|
|
|
signer, err := parsePrivateKey(sshPrivateKeyString)
|
|
if err != nil {
|
|
return config, err
|
|
}
|
|
|
|
if len(sshCertificateString) > 0 {
|
|
key, _, _, _, err := ssh.ParseAuthorizedKey([]byte(sshCertificateString))
|
|
if err != nil {
|
|
return config, fmt.Errorf("Unable to parse SSH certificate: %v", err)
|
|
}
|
|
|
|
if _, ok := key.(*ssh.Certificate); !ok {
|
|
return config, fmt.Errorf("Unable to cast public key to SSH Certificate")
|
|
}
|
|
signer, err = ssh.NewCertSigner(key.(*ssh.Certificate), signer)
|
|
if err != nil {
|
|
return config, err
|
|
}
|
|
}
|
|
|
|
config.Auth = append(config.Auth, ssh.PublicKeys(signer))
|
|
|
|
return config, nil
|
|
}
|
|
|
|
func privateKeyPath(sshKeyPath string) (string, error) {
|
|
if sshKeyPath[:2] == "~/" {
|
|
sshKeyPath = filepath.Join(userHome(), sshKeyPath[2:])
|
|
}
|
|
buff, err := ioutil.ReadFile(sshKeyPath)
|
|
if err != nil {
|
|
return "", fmt.Errorf("Error while reading SSH key file: %v", err)
|
|
}
|
|
return string(buff), nil
|
|
}
|
|
|
|
func certificatePath(sshCertPath string) (string, error) {
|
|
if sshCertPath[:2] == "~/" {
|
|
sshCertPath = filepath.Join(userHome(), sshCertPath[2:])
|
|
}
|
|
buff, err := ioutil.ReadFile(sshCertPath)
|
|
if err != nil {
|
|
return "", fmt.Errorf("Error while reading SSH certificate file: %v", err)
|
|
}
|
|
return string(buff), nil
|
|
}
|
|
|
|
func userHome() string {
|
|
if home := os.Getenv("HOME"); home != "" {
|
|
return home
|
|
}
|
|
homeDrive := os.Getenv("HOMEDRIVE")
|
|
homePath := os.Getenv("HOMEPATH")
|
|
if homeDrive != "" && homePath != "" {
|
|
return homeDrive + homePath
|
|
}
|
|
return os.Getenv("USERPROFILE")
|
|
}
|