refactor: client pkg with new context interaction
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Roxie Gibson 2021-07-22 15:31:39 +01:00
parent fce1ab6c02
commit dd86ec4ca8
Signed by: roxxers
GPG Key ID: 5D0140EDEE123F4D
4 changed files with 139 additions and 132 deletions

View File

@ -1,22 +1,14 @@
package client
import (
"errors"
"fmt"
"net/http"
"os"
dCliCommand "github.com/docker/cli/cli/command"
dCliConfig "github.com/docker/cli/cli/config"
dContext "github.com/docker/cli/cli/context"
"github.com/docker/cli/cli/context/docker"
dCliContextStore "github.com/docker/cli/cli/context/store"
dClient "github.com/docker/docker/client"
"github.com/moby/term"
"github.com/docker/docker/client"
"github.com/sirupsen/logrus"
)
func NewClientWithContext(contextName string) (*dClient.Client, error) {
func NewClientWithContext(contextName string) (*client.Client, error) {
context, err := GetContext(contextName)
if err != nil {
return nil, err
@ -34,142 +26,27 @@ func NewClientWithContext(contextName string) (*dClient.Client, error) {
},
}
var clientOpts []dClient.Opt
var clientOpts []client.Opt
clientOpts = append(clientOpts,
dClient.WithHTTPClient(httpClient),
dClient.WithHost(helper.Host),
dClient.WithDialContext(helper.Dialer),
client.WithHTTPClient(httpClient),
client.WithHost(helper.Host),
client.WithDialContext(helper.Dialer),
)
// FIXME: Maybe don't have this variable here and load it beforehand
version := os.Getenv("DOCKER_API_VERSION")
if version != "" {
clientOpts = append(clientOpts, dClient.WithVersion(version))
clientOpts = append(clientOpts, client.WithVersion(version))
} else {
clientOpts = append(clientOpts, dClient.WithAPIVersionNegotiation())
clientOpts = append(clientOpts, client.WithAPIVersionNegotiation())
}
cl, err := dClient.NewClientWithOpts(clientOpts...)
cl, err := client.NewClientWithOpts(clientOpts...)
if err != nil {
logrus.Fatalf("unable to create Docker client: %s", err)
}
return cl, nil
}
func CreateContext(contextName string, user string, port string) error {
host := contextName
if user != "" {
host = fmt.Sprintf("%s@%s", user, host)
}
if port != "" {
host = fmt.Sprintf("%s:%s", host, port)
}
host = fmt.Sprintf("ssh://%s", host)
if err := createNewContext(contextName, host); err != nil {
return err
}
return nil
}
func createNewContext(name string, host string) error {
s := NewDefaultDockerContextStore()
contextMetadata := newContextMetadata(name)
contextTLSData := dCliContextStore.ContextTLSData{
Endpoints: make(map[string]dCliContextStore.EndpointTLSData),
}
dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(host)
if err != nil {
return err
}
contextMetadata.Endpoints[docker.DockerEndpoint] = dockerEP
if dockerTLS != nil {
contextTLSData.Endpoints[docker.DockerEndpoint] = *dockerTLS
}
if err := s.CreateOrUpdate(contextMetadata); err != nil {
return err
}
if err := s.ResetTLSMaterial(name, &contextTLSData); err != nil {
return err
}
return nil
}
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 newContextMetadata(name string) dCliContextStore.Metadata {
return dCliContextStore.Metadata{
Endpoints: make(map[string]interface{}),
Name: name,
}
}
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
}
func GetContext(contextName string) (dCliContextStore.Metadata, error) {
ctx, err := NewDefaultDockerContextStore().GetMetadata(contextName)
if err != nil {
return dCliContextStore.Metadata{}, err
}
return ctx, nil
}
func GetContextEndpoint(ctx dCliContextStore.Metadata) (string, error) {
// safe to use docker key hardcoded since abra doesn't use k8s... yet...
endpointmeta, ok := ctx.Endpoints["docker"].(dContext.EndpointMetaBase)
if !ok {
err := errors.New("context lacks Docker endpoint")
return "", err
}
return endpointmeta.Host, nil
}
func NewDefaultDockerContextStore() *dCliCommand.ContextStoreWithDefault {
// Grabbing the stderr from Docker commands
// Much easier to fit this into the code we are using to replicate docker cli commands
_, _, stderr := term.StdStreams()
// TODO: Look into custom docker configs in case users want that
dockerConfig := dCliConfig.LoadDefaultConfigFile(stderr)
contextDir := dCliConfig.ContextStoreDir()
storeConfig := dCliCommand.DefaultContextStoreConfig()
store := newContextStore(contextDir, storeConfig)
dockerContextStore := &dCliCommand.ContextStoreWithDefault{
Store: store,
Resolver: func() (*dCliCommand.DefaultContext, error) {
// nil for the Opts because it works without it and its a cli thing
return dCliCommand.ResolveDefaultContext(nil, dockerConfig, storeConfig, stderr)
},
}
return dockerContextStore
}
func newContextStore(dir string, config dCliContextStore.Config) dCliContextStore.Store {
return dCliContextStore.New(dir, config)
}

View File

@ -2,6 +2,9 @@ package client
import (
"github.com/docker/cli/cli/connhelper"
"github.com/docker/cli/cli/context/docker"
dCliContextStore "github.com/docker/cli/cli/context/store"
dClient "github.com/docker/docker/client"
"github.com/sirupsen/logrus"
)
@ -13,3 +16,30 @@ func newConnectionHelper(daemonURL string) *connhelper.ConnectionHelper {
}
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
}

100
client/context.go Normal file
View File

@ -0,0 +1,100 @@
package client
import (
"errors"
"fmt"
command "github.com/docker/cli/cli/command"
dConfig "github.com/docker/cli/cli/config"
context "github.com/docker/cli/cli/context"
"github.com/docker/cli/cli/context/docker"
contextStore "github.com/docker/cli/cli/context/store"
"github.com/moby/term"
)
type Context = contextStore.Metadata
func CreateContext(contextName string, user string, port string) error {
host := contextName
if user != "" {
host = fmt.Sprintf("%s@%s", user, host)
}
if port != "" {
host = fmt.Sprintf("%s:%s", host, port)
}
host = fmt.Sprintf("ssh://%s", host)
if err := createContext(contextName, host); err != nil {
return err
}
return nil
}
// createContext interacts with Docker Context to create a Docker context config
func createContext(name string, host string) error {
s := NewDefaultDockerContextStore()
contextMetadata := contextStore.Metadata{
Endpoints: make(map[string]interface{}),
Name: name,
}
contextTLSData := contextStore.ContextTLSData{
Endpoints: make(map[string]contextStore.EndpointTLSData),
}
dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(host)
if err != nil {
return err
}
contextMetadata.Endpoints[docker.DockerEndpoint] = dockerEP
if dockerTLS != nil {
contextTLSData.Endpoints[docker.DockerEndpoint] = *dockerTLS
}
if err := s.CreateOrUpdate(contextMetadata); err != nil {
return err
}
if err := s.ResetTLSMaterial(name, &contextTLSData); err != nil {
return err
}
return nil
}
func GetContext(contextName string) (contextStore.Metadata, error) {
ctx, err := NewDefaultDockerContextStore().GetMetadata(contextName)
if err != nil {
return contextStore.Metadata{}, err
}
return ctx, nil
}
func GetContextEndpoint(ctx contextStore.Metadata) (string, error) {
// safe to use docker key hardcoded since abra doesn't use k8s... yet...
endpointmeta, ok := ctx.Endpoints["docker"].(context.EndpointMetaBase)
if !ok {
err := errors.New("context lacks Docker endpoint")
return "", err
}
return endpointmeta.Host, nil
}
func newContextStore(dir string, config contextStore.Config) contextStore.Store {
return contextStore.New(dir, config)
}
func NewDefaultDockerContextStore() *command.ContextStoreWithDefault {
// Grabbing the stderr from Docker commands
// Much easier to fit this into the code we are using to replicate docker cli commands
_, _, stderr := term.StdStreams()
// TODO: Look into custom docker configs in case users want that
dockerConfig := dConfig.LoadDefaultConfigFile(stderr)
contextDir := dConfig.ContextStoreDir()
storeConfig := command.DefaultContextStoreConfig()
store := newContextStore(contextDir, storeConfig)
dockerContextStore := &command.ContextStoreWithDefault{
Store: store,
Resolver: func() (*command.DefaultContext, error) {
// nil for the Opts because it works without it and its a cli thing
return command.ResolveDefaultContext(nil, dockerConfig, storeConfig, stderr)
},
}
return dockerContextStore
}