vendor: initial vendor
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
6
vendor/github.com/docker/cli/cli/context/docker/constants.go
generated
vendored
Normal file
6
vendor/github.com/docker/cli/cli/context/docker/constants.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
package docker
|
||||
|
||||
const (
|
||||
// DockerEndpoint is the name of the docker endpoint in a stored context
|
||||
DockerEndpoint = "docker"
|
||||
)
|
||||
166
vendor/github.com/docker/cli/cli/context/docker/load.go
generated
vendored
Normal file
166
vendor/github.com/docker/cli/cli/context/docker/load.go
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/cli/connhelper"
|
||||
"github.com/docker/cli/cli/context"
|
||||
"github.com/docker/cli/cli/context/store"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// EndpointMeta is a typed wrapper around a context-store generic endpoint describing
|
||||
// a Docker Engine endpoint, without its tls config
|
||||
type EndpointMeta = context.EndpointMetaBase
|
||||
|
||||
// Endpoint is a typed wrapper around a context-store generic endpoint describing
|
||||
// a Docker Engine endpoint, with its tls data
|
||||
type Endpoint struct {
|
||||
EndpointMeta
|
||||
TLSData *context.TLSData
|
||||
TLSPassword string
|
||||
}
|
||||
|
||||
// WithTLSData loads TLS materials for the endpoint
|
||||
func WithTLSData(s store.Store, contextName string, m EndpointMeta) (Endpoint, error) {
|
||||
tlsData, err := context.LoadTLSData(s, contextName, DockerEndpoint)
|
||||
if err != nil {
|
||||
return Endpoint{}, err
|
||||
}
|
||||
return Endpoint{
|
||||
EndpointMeta: m,
|
||||
TLSData: tlsData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// tlsConfig extracts a context docker endpoint TLS config
|
||||
func (c *Endpoint) tlsConfig() (*tls.Config, error) {
|
||||
if c.TLSData == nil && !c.SkipTLSVerify {
|
||||
// there is no specific tls config
|
||||
return nil, nil
|
||||
}
|
||||
var tlsOpts []func(*tls.Config)
|
||||
if c.TLSData != nil && c.TLSData.CA != nil {
|
||||
certPool := x509.NewCertPool()
|
||||
if !certPool.AppendCertsFromPEM(c.TLSData.CA) {
|
||||
return nil, errors.New("failed to retrieve context tls info: ca.pem seems invalid")
|
||||
}
|
||||
tlsOpts = append(tlsOpts, func(cfg *tls.Config) {
|
||||
cfg.RootCAs = certPool
|
||||
})
|
||||
}
|
||||
if c.TLSData != nil && c.TLSData.Key != nil && c.TLSData.Cert != nil {
|
||||
keyBytes := c.TLSData.Key
|
||||
pemBlock, _ := pem.Decode(keyBytes)
|
||||
if pemBlock == nil {
|
||||
return nil, fmt.Errorf("no valid private key found")
|
||||
}
|
||||
|
||||
var err error
|
||||
if x509.IsEncryptedPEMBlock(pemBlock) {
|
||||
keyBytes, err = x509.DecryptPEMBlock(pemBlock, []byte(c.TLSPassword))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "private key is encrypted, but could not decrypt it")
|
||||
}
|
||||
keyBytes = pem.EncodeToMemory(&pem.Block{Type: pemBlock.Type, Bytes: keyBytes})
|
||||
}
|
||||
|
||||
x509cert, err := tls.X509KeyPair(c.TLSData.Cert, keyBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to retrieve context tls info")
|
||||
}
|
||||
tlsOpts = append(tlsOpts, func(cfg *tls.Config) {
|
||||
cfg.Certificates = []tls.Certificate{x509cert}
|
||||
})
|
||||
}
|
||||
if c.SkipTLSVerify {
|
||||
tlsOpts = append(tlsOpts, func(cfg *tls.Config) {
|
||||
cfg.InsecureSkipVerify = true
|
||||
})
|
||||
}
|
||||
return tlsconfig.ClientDefault(tlsOpts...), nil
|
||||
}
|
||||
|
||||
// ClientOpts returns a slice of Client options to configure an API client with this endpoint
|
||||
func (c *Endpoint) ClientOpts() ([]func(*client.Client) error, error) {
|
||||
var result []func(*client.Client) error
|
||||
if c.Host != "" {
|
||||
helper, err := connhelper.GetConnectionHelper(c.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if helper == nil {
|
||||
tlsConfig, err := c.tlsConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result,
|
||||
client.WithHost(c.Host),
|
||||
withHTTPClient(tlsConfig),
|
||||
)
|
||||
|
||||
} else {
|
||||
httpClient := &http.Client{
|
||||
// No tls
|
||||
// No proxy
|
||||
Transport: &http.Transport{
|
||||
DialContext: helper.Dialer,
|
||||
},
|
||||
}
|
||||
result = append(result,
|
||||
client.WithHTTPClient(httpClient),
|
||||
client.WithHost(helper.Host),
|
||||
client.WithDialContext(helper.Dialer),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
version := os.Getenv("DOCKER_API_VERSION")
|
||||
if version != "" {
|
||||
result = append(result, client.WithVersion(version))
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func withHTTPClient(tlsConfig *tls.Config) func(*client.Client) error {
|
||||
return func(c *client.Client) error {
|
||||
if tlsConfig == nil {
|
||||
// Use the default HTTPClient
|
||||
return nil
|
||||
}
|
||||
|
||||
httpClient := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
DialContext: (&net.Dialer{
|
||||
KeepAlive: 30 * time.Second,
|
||||
Timeout: 30 * time.Second,
|
||||
}).DialContext,
|
||||
},
|
||||
CheckRedirect: client.CheckRedirect,
|
||||
}
|
||||
return client.WithHTTPClient(httpClient)(c)
|
||||
}
|
||||
}
|
||||
|
||||
// EndpointFromContext parses a context docker endpoint metadata into a typed EndpointMeta structure
|
||||
func EndpointFromContext(metadata store.ContextMetadata) (EndpointMeta, error) {
|
||||
ep, ok := metadata.Endpoints[DockerEndpoint]
|
||||
if !ok {
|
||||
return EndpointMeta{}, errors.New("cannot find docker endpoint in context")
|
||||
}
|
||||
typed, ok := ep.(EndpointMeta)
|
||||
if !ok {
|
||||
return EndpointMeta{}, errors.Errorf("endpoint %q is not of type EndpointMeta", DockerEndpoint)
|
||||
}
|
||||
return typed, nil
|
||||
}
|
||||
7
vendor/github.com/docker/cli/cli/context/endpoint.go
generated
vendored
Normal file
7
vendor/github.com/docker/cli/cli/context/endpoint.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
package context
|
||||
|
||||
// EndpointMetaBase contains fields we expect to be common for most context endpoints
|
||||
type EndpointMetaBase struct {
|
||||
Host string `json:",omitempty"`
|
||||
SkipTLSVerify bool
|
||||
}
|
||||
6
vendor/github.com/docker/cli/cli/context/kubernetes/constants.go
generated
vendored
Normal file
6
vendor/github.com/docker/cli/cli/context/kubernetes/constants.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
package kubernetes
|
||||
|
||||
const (
|
||||
// KubernetesEndpoint is the kubernetes endpoint name in a stored context
|
||||
KubernetesEndpoint = "kubernetes"
|
||||
)
|
||||
95
vendor/github.com/docker/cli/cli/context/kubernetes/load.go
generated
vendored
Normal file
95
vendor/github.com/docker/cli/cli/context/kubernetes/load.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"github.com/docker/cli/cli/context"
|
||||
"github.com/docker/cli/cli/context/store"
|
||||
"github.com/docker/cli/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
// EndpointMeta is a typed wrapper around a context-store generic endpoint describing
|
||||
// a Kubernetes endpoint, without TLS data
|
||||
type EndpointMeta struct {
|
||||
context.EndpointMetaBase
|
||||
DefaultNamespace string `json:",omitempty"`
|
||||
AuthProvider *clientcmdapi.AuthProviderConfig `json:",omitempty"`
|
||||
Exec *clientcmdapi.ExecConfig `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Endpoint is a typed wrapper around a context-store generic endpoint describing
|
||||
// a Kubernetes endpoint, with TLS data
|
||||
type Endpoint struct {
|
||||
EndpointMeta
|
||||
TLSData *context.TLSData
|
||||
}
|
||||
|
||||
// WithTLSData loads TLS materials for the endpoint
|
||||
func (c *EndpointMeta) WithTLSData(s store.Store, contextName string) (Endpoint, error) {
|
||||
tlsData, err := context.LoadTLSData(s, contextName, KubernetesEndpoint)
|
||||
if err != nil {
|
||||
return Endpoint{}, err
|
||||
}
|
||||
return Endpoint{
|
||||
EndpointMeta: *c,
|
||||
TLSData: tlsData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// KubernetesConfig creates the kubernetes client config from the endpoint
|
||||
func (c *Endpoint) KubernetesConfig() clientcmd.ClientConfig {
|
||||
cfg := clientcmdapi.NewConfig()
|
||||
cluster := clientcmdapi.NewCluster()
|
||||
cluster.Server = c.Host
|
||||
cluster.InsecureSkipTLSVerify = c.SkipTLSVerify
|
||||
authInfo := clientcmdapi.NewAuthInfo()
|
||||
if c.TLSData != nil {
|
||||
cluster.CertificateAuthorityData = c.TLSData.CA
|
||||
authInfo.ClientCertificateData = c.TLSData.Cert
|
||||
authInfo.ClientKeyData = c.TLSData.Key
|
||||
}
|
||||
authInfo.AuthProvider = c.AuthProvider
|
||||
authInfo.Exec = c.Exec
|
||||
cfg.Clusters["cluster"] = cluster
|
||||
cfg.AuthInfos["authInfo"] = authInfo
|
||||
ctx := clientcmdapi.NewContext()
|
||||
ctx.AuthInfo = "authInfo"
|
||||
ctx.Cluster = "cluster"
|
||||
ctx.Namespace = c.DefaultNamespace
|
||||
cfg.Contexts["context"] = ctx
|
||||
cfg.CurrentContext = "context"
|
||||
return clientcmd.NewDefaultClientConfig(*cfg, &clientcmd.ConfigOverrides{})
|
||||
}
|
||||
|
||||
// EndpointFromContext extracts kubernetes endpoint info from current context
|
||||
func EndpointFromContext(metadata store.ContextMetadata) *EndpointMeta {
|
||||
ep, ok := metadata.Endpoints[KubernetesEndpoint]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
typed, ok := ep.(EndpointMeta)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return &typed
|
||||
}
|
||||
|
||||
// ConfigFromContext resolves a kubernetes client config for the specified context.
|
||||
// If kubeconfigOverride is specified, use this config file instead of the context defaults.ConfigFromContext
|
||||
// if command.ContextDockerHost is specified as the context name, fallsback to the default user's kubeconfig file
|
||||
func ConfigFromContext(name string, s store.Store) (clientcmd.ClientConfig, error) {
|
||||
ctxMeta, err := s.GetContextMetadata(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
epMeta := EndpointFromContext(ctxMeta)
|
||||
if epMeta != nil {
|
||||
ep, err := epMeta.WithTLSData(s, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ep.KubernetesConfig(), nil
|
||||
}
|
||||
// context has no kubernetes endpoint
|
||||
return kubernetes.NewKubernetesConfig(""), nil
|
||||
}
|
||||
61
vendor/github.com/docker/cli/cli/context/kubernetes/save.go
generated
vendored
Normal file
61
vendor/github.com/docker/cli/cli/context/kubernetes/save.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/docker/cli/cli/context"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
// FromKubeConfig creates a Kubernetes endpoint from a Kubeconfig file
|
||||
func FromKubeConfig(kubeconfig, kubeContext, namespaceOverride string) (Endpoint, error) {
|
||||
cfg := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
|
||||
&clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig},
|
||||
&clientcmd.ConfigOverrides{CurrentContext: kubeContext, Context: clientcmdapi.Context{Namespace: namespaceOverride}})
|
||||
ns, _, err := cfg.Namespace()
|
||||
if err != nil {
|
||||
return Endpoint{}, err
|
||||
}
|
||||
clientcfg, err := cfg.ClientConfig()
|
||||
if err != nil {
|
||||
return Endpoint{}, err
|
||||
}
|
||||
var ca, key, cert []byte
|
||||
if ca, err = readFileOrDefault(clientcfg.CAFile, clientcfg.CAData); err != nil {
|
||||
return Endpoint{}, err
|
||||
}
|
||||
if key, err = readFileOrDefault(clientcfg.KeyFile, clientcfg.KeyData); err != nil {
|
||||
return Endpoint{}, err
|
||||
}
|
||||
if cert, err = readFileOrDefault(clientcfg.CertFile, clientcfg.CertData); err != nil {
|
||||
return Endpoint{}, err
|
||||
}
|
||||
var tlsData *context.TLSData
|
||||
if ca != nil || cert != nil || key != nil {
|
||||
tlsData = &context.TLSData{
|
||||
CA: ca,
|
||||
Cert: cert,
|
||||
Key: key,
|
||||
}
|
||||
}
|
||||
return Endpoint{
|
||||
EndpointMeta: EndpointMeta{
|
||||
EndpointMetaBase: context.EndpointMetaBase{
|
||||
Host: clientcfg.Host,
|
||||
SkipTLSVerify: clientcfg.Insecure,
|
||||
},
|
||||
DefaultNamespace: ns,
|
||||
AuthProvider: clientcfg.AuthProvider,
|
||||
Exec: clientcfg.ExecProvider,
|
||||
},
|
||||
TLSData: tlsData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func readFileOrDefault(path string, defaultValue []byte) ([]byte, error) {
|
||||
if path != "" {
|
||||
return ioutil.ReadFile(path)
|
||||
}
|
||||
return defaultValue, nil
|
||||
}
|
||||
22
vendor/github.com/docker/cli/cli/context/store/doc.go
generated
vendored
Normal file
22
vendor/github.com/docker/cli/cli/context/store/doc.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// Package store provides a generic way to store credentials to connect to virtually any kind of remote system.
|
||||
// The term `context` comes from the similar feature in Kubernetes kubectl config files.
|
||||
//
|
||||
// Conceptually, a context is a set of metadata and TLS data, that can be used to connect to various endpoints
|
||||
// of a remote system. TLS data and metadata are stored separately, so that in the future, we will be able to store sensitive
|
||||
// information in a more secure way, depending on the os we are running on (e.g.: on Windows we could use the user Certificate Store, on Mac OS the user Keychain...).
|
||||
//
|
||||
// Current implementation is purely file based with the following structure:
|
||||
// ${CONTEXT_ROOT}
|
||||
// - meta/
|
||||
// - <context id>/meta.json: contains context medata (key/value pairs) as well as a list of endpoints (themselves containing key/value pair metadata)
|
||||
// - tls/
|
||||
// - <context id>/endpoint1/: directory containing TLS data for the endpoint1 in the corresponding context
|
||||
//
|
||||
// The context store itself has absolutely no knowledge about what a docker or a kubernetes endpoint should contain in term of metadata or TLS config.
|
||||
// Client code is responsible for generating and parsing endpoint metadata and TLS files.
|
||||
// The multi-endpoints approach of this package allows to combine many different endpoints in the same "context" (e.g., the Docker CLI
|
||||
// is able for a single context to define both a docker endpoint and a Kubernetes endpoint for the same cluster, and also specify which
|
||||
// orchestrator to use by default when deploying a compose stack on this cluster).
|
||||
//
|
||||
// Context IDs are actually SHA256 hashes of the context name, and are there only to avoid dealing with special characters in context names.
|
||||
package store
|
||||
153
vendor/github.com/docker/cli/cli/context/store/metadatastore.go
generated
vendored
Normal file
153
vendor/github.com/docker/cli/cli/context/store/metadatastore.go
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"vbom.ml/util/sortorder"
|
||||
)
|
||||
|
||||
const (
|
||||
metadataDir = "meta"
|
||||
metaFile = "meta.json"
|
||||
)
|
||||
|
||||
type metadataStore struct {
|
||||
root string
|
||||
config Config
|
||||
}
|
||||
|
||||
func (s *metadataStore) contextDir(id contextdir) string {
|
||||
return filepath.Join(s.root, string(id))
|
||||
}
|
||||
|
||||
func (s *metadataStore) createOrUpdate(meta ContextMetadata) error {
|
||||
contextDir := s.contextDir(contextdirOf(meta.Name))
|
||||
if err := os.MkdirAll(contextDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
bytes, err := json.Marshal(&meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(filepath.Join(contextDir, metaFile), bytes, 0644)
|
||||
}
|
||||
|
||||
func parseTypedOrMap(payload []byte, getter TypeGetter) (interface{}, error) {
|
||||
if len(payload) == 0 || string(payload) == "null" {
|
||||
return nil, nil
|
||||
}
|
||||
if getter == nil {
|
||||
var res map[string]interface{}
|
||||
if err := json.Unmarshal(payload, &res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
typed := getter()
|
||||
if err := json.Unmarshal(payload, typed); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return reflect.ValueOf(typed).Elem().Interface(), nil
|
||||
}
|
||||
|
||||
func (s *metadataStore) get(id contextdir) (ContextMetadata, error) {
|
||||
contextDir := s.contextDir(id)
|
||||
bytes, err := ioutil.ReadFile(filepath.Join(contextDir, metaFile))
|
||||
if err != nil {
|
||||
return ContextMetadata{}, convertContextDoesNotExist(err)
|
||||
}
|
||||
var untyped untypedContextMetadata
|
||||
r := ContextMetadata{
|
||||
Endpoints: make(map[string]interface{}),
|
||||
}
|
||||
if err := json.Unmarshal(bytes, &untyped); err != nil {
|
||||
return ContextMetadata{}, err
|
||||
}
|
||||
r.Name = untyped.Name
|
||||
if r.Metadata, err = parseTypedOrMap(untyped.Metadata, s.config.contextType); err != nil {
|
||||
return ContextMetadata{}, err
|
||||
}
|
||||
for k, v := range untyped.Endpoints {
|
||||
if r.Endpoints[k], err = parseTypedOrMap(v, s.config.endpointTypes[k]); err != nil {
|
||||
return ContextMetadata{}, err
|
||||
}
|
||||
}
|
||||
return r, err
|
||||
}
|
||||
|
||||
func (s *metadataStore) remove(id contextdir) error {
|
||||
contextDir := s.contextDir(id)
|
||||
return os.RemoveAll(contextDir)
|
||||
}
|
||||
|
||||
func (s *metadataStore) list() ([]ContextMetadata, error) {
|
||||
ctxDirs, err := listRecursivelyMetadataDirs(s.root)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
var res []ContextMetadata
|
||||
for _, dir := range ctxDirs {
|
||||
c, err := s.get(contextdir(dir))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, c)
|
||||
}
|
||||
sort.Slice(res, func(i, j int) bool {
|
||||
return sortorder.NaturalLess(res[i].Name, res[j].Name)
|
||||
})
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func isContextDir(path string) bool {
|
||||
s, err := os.Stat(filepath.Join(path, metaFile))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return !s.IsDir()
|
||||
}
|
||||
|
||||
func listRecursivelyMetadataDirs(root string) ([]string, error) {
|
||||
fis, err := ioutil.ReadDir(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result []string
|
||||
for _, fi := range fis {
|
||||
if fi.IsDir() {
|
||||
if isContextDir(filepath.Join(root, fi.Name())) {
|
||||
result = append(result, fi.Name())
|
||||
}
|
||||
subs, err := listRecursivelyMetadataDirs(filepath.Join(root, fi.Name()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, s := range subs {
|
||||
result = append(result, fmt.Sprintf("%s/%s", fi.Name(), s))
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func convertContextDoesNotExist(err error) error {
|
||||
if os.IsNotExist(err) {
|
||||
return &contextDoesNotExistError{}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type untypedContextMetadata struct {
|
||||
Metadata json.RawMessage `json:"metadata,omitempty"`
|
||||
Endpoints map[string]json.RawMessage `json:"endpoints,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
349
vendor/github.com/docker/cli/cli/context/store/store.go
generated
vendored
Normal file
349
vendor/github.com/docker/cli/cli/context/store/store.go
generated
vendored
Normal file
@@ -0,0 +1,349 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
_ "crypto/sha256" // ensure ids can be computed
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/errdefs"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// Store provides a context store for easily remembering endpoints configuration
|
||||
type Store interface {
|
||||
ListContexts() ([]ContextMetadata, error)
|
||||
CreateOrUpdateContext(meta ContextMetadata) error
|
||||
RemoveContext(name string) error
|
||||
GetContextMetadata(name string) (ContextMetadata, error)
|
||||
ResetContextTLSMaterial(name string, data *ContextTLSData) error
|
||||
ResetContextEndpointTLSMaterial(contextName string, endpointName string, data *EndpointTLSData) error
|
||||
ListContextTLSFiles(name string) (map[string]EndpointFiles, error)
|
||||
GetContextTLSData(contextName, endpointName, fileName string) ([]byte, error)
|
||||
GetContextStorageInfo(contextName string) ContextStorageInfo
|
||||
}
|
||||
|
||||
// ContextMetadata contains metadata about a context and its endpoints
|
||||
type ContextMetadata struct {
|
||||
Name string `json:",omitempty"`
|
||||
Metadata interface{} `json:",omitempty"`
|
||||
Endpoints map[string]interface{} `json:",omitempty"`
|
||||
}
|
||||
|
||||
// ContextStorageInfo contains data about where a given context is stored
|
||||
type ContextStorageInfo struct {
|
||||
MetadataPath string
|
||||
TLSPath string
|
||||
}
|
||||
|
||||
// EndpointTLSData represents tls data for a given endpoint
|
||||
type EndpointTLSData struct {
|
||||
Files map[string][]byte
|
||||
}
|
||||
|
||||
// ContextTLSData represents tls data for a whole context
|
||||
type ContextTLSData struct {
|
||||
Endpoints map[string]EndpointTLSData
|
||||
}
|
||||
|
||||
// New creates a store from a given directory.
|
||||
// If the directory does not exist or is empty, initialize it
|
||||
func New(dir string, cfg Config) Store {
|
||||
metaRoot := filepath.Join(dir, metadataDir)
|
||||
tlsRoot := filepath.Join(dir, tlsDir)
|
||||
|
||||
return &store{
|
||||
meta: &metadataStore{
|
||||
root: metaRoot,
|
||||
config: cfg,
|
||||
},
|
||||
tls: &tlsStore{
|
||||
root: tlsRoot,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type store struct {
|
||||
meta *metadataStore
|
||||
tls *tlsStore
|
||||
}
|
||||
|
||||
func (s *store) ListContexts() ([]ContextMetadata, error) {
|
||||
return s.meta.list()
|
||||
}
|
||||
|
||||
func (s *store) CreateOrUpdateContext(meta ContextMetadata) error {
|
||||
return s.meta.createOrUpdate(meta)
|
||||
}
|
||||
|
||||
func (s *store) RemoveContext(name string) error {
|
||||
id := contextdirOf(name)
|
||||
if err := s.meta.remove(id); err != nil {
|
||||
return patchErrContextName(err, name)
|
||||
}
|
||||
return patchErrContextName(s.tls.removeAllContextData(id), name)
|
||||
}
|
||||
|
||||
func (s *store) GetContextMetadata(name string) (ContextMetadata, error) {
|
||||
res, err := s.meta.get(contextdirOf(name))
|
||||
patchErrContextName(err, name)
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (s *store) ResetContextTLSMaterial(name string, data *ContextTLSData) error {
|
||||
id := contextdirOf(name)
|
||||
if err := s.tls.removeAllContextData(id); err != nil {
|
||||
return patchErrContextName(err, name)
|
||||
}
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
for ep, files := range data.Endpoints {
|
||||
for fileName, data := range files.Files {
|
||||
if err := s.tls.createOrUpdate(id, ep, fileName, data); err != nil {
|
||||
return patchErrContextName(err, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *store) ResetContextEndpointTLSMaterial(contextName string, endpointName string, data *EndpointTLSData) error {
|
||||
id := contextdirOf(contextName)
|
||||
if err := s.tls.removeAllEndpointData(id, endpointName); err != nil {
|
||||
return patchErrContextName(err, contextName)
|
||||
}
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
for fileName, data := range data.Files {
|
||||
if err := s.tls.createOrUpdate(id, endpointName, fileName, data); err != nil {
|
||||
return patchErrContextName(err, contextName)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *store) ListContextTLSFiles(name string) (map[string]EndpointFiles, error) {
|
||||
res, err := s.tls.listContextData(contextdirOf(name))
|
||||
return res, patchErrContextName(err, name)
|
||||
}
|
||||
|
||||
func (s *store) GetContextTLSData(contextName, endpointName, fileName string) ([]byte, error) {
|
||||
res, err := s.tls.getData(contextdirOf(contextName), endpointName, fileName)
|
||||
return res, patchErrContextName(err, contextName)
|
||||
}
|
||||
|
||||
func (s *store) GetContextStorageInfo(contextName string) ContextStorageInfo {
|
||||
dir := contextdirOf(contextName)
|
||||
return ContextStorageInfo{
|
||||
MetadataPath: s.meta.contextDir(dir),
|
||||
TLSPath: s.tls.contextDir(dir),
|
||||
}
|
||||
}
|
||||
|
||||
// Export exports an existing namespace into an opaque data stream
|
||||
// This stream is actually a tarball containing context metadata and TLS materials, but it does
|
||||
// not map 1:1 the layout of the context store (don't try to restore it manually without calling store.Import)
|
||||
func Export(name string, s Store) io.ReadCloser {
|
||||
reader, writer := io.Pipe()
|
||||
go func() {
|
||||
tw := tar.NewWriter(writer)
|
||||
defer tw.Close()
|
||||
defer writer.Close()
|
||||
meta, err := s.GetContextMetadata(name)
|
||||
if err != nil {
|
||||
writer.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
metaBytes, err := json.Marshal(&meta)
|
||||
if err != nil {
|
||||
writer.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
if err = tw.WriteHeader(&tar.Header{
|
||||
Name: metaFile,
|
||||
Mode: 0644,
|
||||
Size: int64(len(metaBytes)),
|
||||
}); err != nil {
|
||||
writer.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
if _, err = tw.Write(metaBytes); err != nil {
|
||||
writer.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
tlsFiles, err := s.ListContextTLSFiles(name)
|
||||
if err != nil {
|
||||
writer.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
if err = tw.WriteHeader(&tar.Header{
|
||||
Name: "tls",
|
||||
Mode: 0700,
|
||||
Size: 0,
|
||||
Typeflag: tar.TypeDir,
|
||||
}); err != nil {
|
||||
writer.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
for endpointName, endpointFiles := range tlsFiles {
|
||||
if err = tw.WriteHeader(&tar.Header{
|
||||
Name: path.Join("tls", endpointName),
|
||||
Mode: 0700,
|
||||
Size: 0,
|
||||
Typeflag: tar.TypeDir,
|
||||
}); err != nil {
|
||||
writer.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
for _, fileName := range endpointFiles {
|
||||
data, err := s.GetContextTLSData(name, endpointName, fileName)
|
||||
if err != nil {
|
||||
writer.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
if err = tw.WriteHeader(&tar.Header{
|
||||
Name: path.Join("tls", endpointName, fileName),
|
||||
Mode: 0600,
|
||||
Size: int64(len(data)),
|
||||
}); err != nil {
|
||||
writer.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
if _, err = tw.Write(data); err != nil {
|
||||
writer.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return reader
|
||||
}
|
||||
|
||||
// Import imports an exported context into a store
|
||||
func Import(name string, s Store, reader io.Reader) error {
|
||||
tr := tar.NewReader(reader)
|
||||
tlsData := ContextTLSData{
|
||||
Endpoints: map[string]EndpointTLSData{},
|
||||
}
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hdr.Typeflag == tar.TypeDir {
|
||||
// skip this entry, only taking files into account
|
||||
continue
|
||||
}
|
||||
if hdr.Name == metaFile {
|
||||
data, err := ioutil.ReadAll(tr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var meta ContextMetadata
|
||||
if err := json.Unmarshal(data, &meta); err != nil {
|
||||
return err
|
||||
}
|
||||
meta.Name = name
|
||||
if err := s.CreateOrUpdateContext(meta); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if strings.HasPrefix(hdr.Name, "tls/") {
|
||||
relative := strings.TrimPrefix(hdr.Name, "tls/")
|
||||
parts := strings.SplitN(relative, "/", 2)
|
||||
if len(parts) != 2 {
|
||||
return errors.New("archive format is invalid")
|
||||
}
|
||||
endpointName := parts[0]
|
||||
fileName := parts[1]
|
||||
data, err := ioutil.ReadAll(tr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := tlsData.Endpoints[endpointName]; !ok {
|
||||
tlsData.Endpoints[endpointName] = EndpointTLSData{
|
||||
Files: map[string][]byte{},
|
||||
}
|
||||
}
|
||||
tlsData.Endpoints[endpointName].Files[fileName] = data
|
||||
}
|
||||
}
|
||||
return s.ResetContextTLSMaterial(name, &tlsData)
|
||||
}
|
||||
|
||||
type setContextName interface {
|
||||
setContext(name string)
|
||||
}
|
||||
|
||||
type contextDoesNotExistError struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (e *contextDoesNotExistError) Error() string {
|
||||
return fmt.Sprintf("context %q does not exist", e.name)
|
||||
}
|
||||
|
||||
func (e *contextDoesNotExistError) setContext(name string) {
|
||||
e.name = name
|
||||
}
|
||||
|
||||
// NotFound satisfies interface github.com/docker/docker/errdefs.ErrNotFound
|
||||
func (e *contextDoesNotExistError) NotFound() {}
|
||||
|
||||
type tlsDataDoesNotExist interface {
|
||||
errdefs.ErrNotFound
|
||||
IsTLSDataDoesNotExist()
|
||||
}
|
||||
|
||||
type tlsDataDoesNotExistError struct {
|
||||
context, endpoint, file string
|
||||
}
|
||||
|
||||
func (e *tlsDataDoesNotExistError) Error() string {
|
||||
return fmt.Sprintf("tls data for %s/%s/%s does not exist", e.context, e.endpoint, e.file)
|
||||
}
|
||||
|
||||
func (e *tlsDataDoesNotExistError) setContext(name string) {
|
||||
e.context = name
|
||||
}
|
||||
|
||||
// NotFound satisfies interface github.com/docker/docker/errdefs.ErrNotFound
|
||||
func (e *tlsDataDoesNotExistError) NotFound() {}
|
||||
|
||||
// IsTLSDataDoesNotExist satisfies tlsDataDoesNotExist
|
||||
func (e *tlsDataDoesNotExistError) IsTLSDataDoesNotExist() {}
|
||||
|
||||
// IsErrContextDoesNotExist checks if the given error is a "context does not exist" condition
|
||||
func IsErrContextDoesNotExist(err error) bool {
|
||||
_, ok := err.(*contextDoesNotExistError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsErrTLSDataDoesNotExist checks if the given error is a "context does not exist" condition
|
||||
func IsErrTLSDataDoesNotExist(err error) bool {
|
||||
_, ok := err.(tlsDataDoesNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
type contextdir string
|
||||
|
||||
func contextdirOf(name string) contextdir {
|
||||
return contextdir(digest.FromString(name).Encoded())
|
||||
}
|
||||
|
||||
func patchErrContextName(err error, name string) error {
|
||||
if typed, ok := err.(setContextName); ok {
|
||||
typed.setContext(name)
|
||||
}
|
||||
return err
|
||||
}
|
||||
43
vendor/github.com/docker/cli/cli/context/store/storeconfig.go
generated
vendored
Normal file
43
vendor/github.com/docker/cli/cli/context/store/storeconfig.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package store
|
||||
|
||||
// TypeGetter is a func used to determine the concrete type of a context or
|
||||
// endpoint metadata by returning a pointer to an instance of the object
|
||||
// eg: for a context of type DockerContext, the corresponding TypeGetter should return new(DockerContext)
|
||||
type TypeGetter func() interface{}
|
||||
|
||||
// NamedTypeGetter is a TypeGetter associated with a name
|
||||
type NamedTypeGetter struct {
|
||||
name string
|
||||
typeGetter TypeGetter
|
||||
}
|
||||
|
||||
// EndpointTypeGetter returns a NamedTypeGetter with the spcecified name and getter
|
||||
func EndpointTypeGetter(name string, getter TypeGetter) NamedTypeGetter {
|
||||
return NamedTypeGetter{
|
||||
name: name,
|
||||
typeGetter: getter,
|
||||
}
|
||||
}
|
||||
|
||||
// Config is used to configure the metadata marshaler of the context store
|
||||
type Config struct {
|
||||
contextType TypeGetter
|
||||
endpointTypes map[string]TypeGetter
|
||||
}
|
||||
|
||||
// SetEndpoint set an endpoint typing information
|
||||
func (c Config) SetEndpoint(name string, getter TypeGetter) {
|
||||
c.endpointTypes[name] = getter
|
||||
}
|
||||
|
||||
// NewConfig creates a config object
|
||||
func NewConfig(contextType TypeGetter, endpoints ...NamedTypeGetter) Config {
|
||||
res := Config{
|
||||
contextType: contextType,
|
||||
endpointTypes: make(map[string]TypeGetter),
|
||||
}
|
||||
for _, e := range endpoints {
|
||||
res.endpointTypes[e.name] = e.typeGetter
|
||||
}
|
||||
return res
|
||||
}
|
||||
99
vendor/github.com/docker/cli/cli/context/store/tlsstore.go
generated
vendored
Normal file
99
vendor/github.com/docker/cli/cli/context/store/tlsstore.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const tlsDir = "tls"
|
||||
|
||||
type tlsStore struct {
|
||||
root string
|
||||
}
|
||||
|
||||
func (s *tlsStore) contextDir(id contextdir) string {
|
||||
return filepath.Join(s.root, string(id))
|
||||
}
|
||||
|
||||
func (s *tlsStore) endpointDir(contextID contextdir, name string) string {
|
||||
return filepath.Join(s.root, string(contextID), name)
|
||||
}
|
||||
|
||||
func (s *tlsStore) filePath(contextID contextdir, endpointName, filename string) string {
|
||||
return filepath.Join(s.root, string(contextID), endpointName, filename)
|
||||
}
|
||||
|
||||
func (s *tlsStore) createOrUpdate(contextID contextdir, endpointName, filename string, data []byte) error {
|
||||
epdir := s.endpointDir(contextID, endpointName)
|
||||
parentOfRoot := filepath.Dir(s.root)
|
||||
if err := os.MkdirAll(parentOfRoot, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(epdir, 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(s.filePath(contextID, endpointName, filename), data, 0600)
|
||||
}
|
||||
|
||||
func (s *tlsStore) getData(contextID contextdir, endpointName, filename string) ([]byte, error) {
|
||||
data, err := ioutil.ReadFile(s.filePath(contextID, endpointName, filename))
|
||||
if err != nil {
|
||||
return nil, convertTLSDataDoesNotExist(endpointName, filename, err)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (s *tlsStore) remove(contextID contextdir, endpointName, filename string) error {
|
||||
err := os.Remove(s.filePath(contextID, endpointName, filename))
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *tlsStore) removeAllEndpointData(contextID contextdir, endpointName string) error {
|
||||
return os.RemoveAll(s.endpointDir(contextID, endpointName))
|
||||
}
|
||||
|
||||
func (s *tlsStore) removeAllContextData(contextID contextdir) error {
|
||||
return os.RemoveAll(s.contextDir(contextID))
|
||||
}
|
||||
|
||||
func (s *tlsStore) listContextData(contextID contextdir) (map[string]EndpointFiles, error) {
|
||||
epFSs, err := ioutil.ReadDir(s.contextDir(contextID))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return map[string]EndpointFiles{}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
r := make(map[string]EndpointFiles)
|
||||
for _, epFS := range epFSs {
|
||||
if epFS.IsDir() {
|
||||
epDir := s.endpointDir(contextID, epFS.Name())
|
||||
fss, err := ioutil.ReadDir(epDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var files EndpointFiles
|
||||
for _, fs := range fss {
|
||||
if !fs.IsDir() {
|
||||
files = append(files, fs.Name())
|
||||
}
|
||||
}
|
||||
r[epFS.Name()] = files
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// EndpointFiles is a slice of strings representing file names
|
||||
type EndpointFiles []string
|
||||
|
||||
func convertTLSDataDoesNotExist(endpoint, file string, err error) error {
|
||||
if os.IsNotExist(err) {
|
||||
return &tlsDataDoesNotExistError{endpoint: endpoint, file: file}
|
||||
}
|
||||
return err
|
||||
}
|
||||
98
vendor/github.com/docker/cli/cli/context/tlsdata.go
generated
vendored
Normal file
98
vendor/github.com/docker/cli/cli/context/tlsdata.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/docker/cli/cli/context/store"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
caKey = "ca.pem"
|
||||
certKey = "cert.pem"
|
||||
keyKey = "key.pem"
|
||||
)
|
||||
|
||||
// TLSData holds ca/cert/key raw data
|
||||
type TLSData struct {
|
||||
CA []byte
|
||||
Key []byte
|
||||
Cert []byte
|
||||
}
|
||||
|
||||
// ToStoreTLSData converts TLSData to the store representation
|
||||
func (data *TLSData) ToStoreTLSData() *store.EndpointTLSData {
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
result := store.EndpointTLSData{
|
||||
Files: make(map[string][]byte),
|
||||
}
|
||||
if data.CA != nil {
|
||||
result.Files[caKey] = data.CA
|
||||
}
|
||||
if data.Cert != nil {
|
||||
result.Files[certKey] = data.Cert
|
||||
}
|
||||
if data.Key != nil {
|
||||
result.Files[keyKey] = data.Key
|
||||
}
|
||||
return &result
|
||||
}
|
||||
|
||||
// LoadTLSData loads TLS data from the store
|
||||
func LoadTLSData(s store.Store, contextName, endpointName string) (*TLSData, error) {
|
||||
tlsFiles, err := s.ListContextTLSFiles(contextName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to retrieve context tls files for context %q", contextName)
|
||||
}
|
||||
if epTLSFiles, ok := tlsFiles[endpointName]; ok {
|
||||
var tlsData TLSData
|
||||
for _, f := range epTLSFiles {
|
||||
data, err := s.GetContextTLSData(contextName, endpointName, f)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to retrieve context tls data for file %q of context %q", f, contextName)
|
||||
}
|
||||
switch f {
|
||||
case caKey:
|
||||
tlsData.CA = data
|
||||
case certKey:
|
||||
tlsData.Cert = data
|
||||
case keyKey:
|
||||
tlsData.Key = data
|
||||
default:
|
||||
logrus.Warnf("unknown file %s in context %s tls bundle", f, contextName)
|
||||
}
|
||||
}
|
||||
return &tlsData, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// TLSDataFromFiles reads files into a TLSData struct (or returns nil if all paths are empty)
|
||||
func TLSDataFromFiles(caPath, certPath, keyPath string) (*TLSData, error) {
|
||||
var (
|
||||
ca, cert, key []byte
|
||||
err error
|
||||
)
|
||||
if caPath != "" {
|
||||
if ca, err = ioutil.ReadFile(caPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if certPath != "" {
|
||||
if cert, err = ioutil.ReadFile(certPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if keyPath != "" {
|
||||
if key, err = ioutil.ReadFile(keyPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if ca == nil && cert == nil && key == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return &TLSData{CA: ca, Cert: cert, Key: key}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user