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://@ 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, false, ) 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 }