104 lines
3.0 KiB
Go
104 lines
3.0 KiB
Go
package commandconn
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"net/url"
|
|
|
|
sshPkg "coopcloud.tech/abra/pkg/ssh"
|
|
"github.com/docker/cli/cli/connhelper"
|
|
"github.com/docker/cli/cli/connhelper/ssh"
|
|
"github.com/docker/cli/cli/context/docker"
|
|
dCliContextStore "github.com/docker/cli/cli/context/store"
|
|
dClient "github.com/docker/docker/client"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// GetConnectionHelper returns Docker-specific connection helper for the given URL.
|
|
// GetConnectionHelper returns nil without error when no helper is registered for the scheme.
|
|
//
|
|
// ssh://<user>@<host> URL requires Docker 18.09 or later on the remote host.
|
|
func GetConnectionHelper(daemonURL string) (*connhelper.ConnectionHelper, error) {
|
|
return getConnectionHelper(daemonURL, []string{"-o ConnectTimeout=5"})
|
|
}
|
|
|
|
func getConnectionHelper(daemonURL string, sshFlags []string) (*connhelper.ConnectionHelper, error) {
|
|
url, err := url.Parse(daemonURL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch scheme := url.Scheme; scheme {
|
|
case "ssh":
|
|
ctxConnDetails, err := ssh.ParseURL(daemonURL)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "ssh host connection is not valid")
|
|
}
|
|
|
|
if err := sshPkg.EnsureHostKey(ctxConnDetails.Host); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
hostConfig, err := sshPkg.GetHostConfig(
|
|
ctxConnDetails.Host,
|
|
ctxConnDetails.User,
|
|
ctxConnDetails.Port,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if hostConfig.IdentityFile != "" {
|
|
msg := "discovered %s as identity file for %s, using for ssh connection"
|
|
logrus.Debugf(msg, hostConfig.IdentityFile, ctxConnDetails.Host)
|
|
sshFlags = append(sshFlags, fmt.Sprintf("-o IdentityFile=%s", hostConfig.IdentityFile))
|
|
}
|
|
|
|
return &connhelper.ConnectionHelper{
|
|
Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
return New(ctx, "ssh", append(sshFlags, ctxConnDetails.Args("docker", "system", "dial-stdio")...)...)
|
|
},
|
|
Host: "http://docker.example.com",
|
|
}, nil
|
|
}
|
|
// Future version may support plugins via ~/.docker/config.json. e.g. "dind"
|
|
// See docker/cli#889 for the previous discussion.
|
|
return nil, err
|
|
}
|
|
|
|
func NewConnectionHelper(daemonURL string) *connhelper.ConnectionHelper {
|
|
helper, err := GetConnectionHelper(daemonURL)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
return helper
|
|
}
|
|
|
|
func getDockerEndpoint(host string) (docker.Endpoint, error) {
|
|
skipTLSVerify := false
|
|
ep := docker.Endpoint{
|
|
EndpointMeta: docker.EndpointMeta{
|
|
Host: host,
|
|
SkipTLSVerify: skipTLSVerify,
|
|
},
|
|
}
|
|
// try to resolve a docker client, validating the configuration
|
|
opts, err := ep.ClientOpts()
|
|
if err != nil {
|
|
return docker.Endpoint{}, err
|
|
}
|
|
if _, err := dClient.NewClientWithOpts(opts...); err != nil {
|
|
return docker.Endpoint{}, err
|
|
}
|
|
return ep, nil
|
|
}
|
|
|
|
func GetDockerEndpointMetadataAndTLS(host string) (docker.EndpointMeta, *dCliContextStore.EndpointTLSData, error) {
|
|
ep, err := getDockerEndpoint(host)
|
|
if err != nil {
|
|
return docker.EndpointMeta{}, nil, err
|
|
}
|
|
return ep.EndpointMeta, ep.TLSData.ToStoreTLSData(), nil
|
|
}
|