Split Docker provider
This commit is contained in:
parent
2cebd0a083
commit
466d7461b7
71 changed files with 2677 additions and 1190 deletions
211
pkg/provider/docker/shared.go
Normal file
211
pkg/provider/docker/shared.go
Normal file
|
@ -0,0 +1,211 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/cli/connhelper"
|
||||
dockertypes "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/docker/go-connections/sockets"
|
||||
"github.com/rs/zerolog/log"
|
||||
ptypes "github.com/traefik/paerser/types"
|
||||
"github.com/traefik/traefik/v3/pkg/provider"
|
||||
"github.com/traefik/traefik/v3/pkg/types"
|
||||
"github.com/traefik/traefik/v3/pkg/version"
|
||||
)
|
||||
|
||||
// DefaultTemplateRule The default template for the default rule.
|
||||
const DefaultTemplateRule = "Host(`{{ normalize .Name }}`)"
|
||||
|
||||
type Shared struct {
|
||||
ExposedByDefault bool `description:"Expose containers by default." json:"exposedByDefault,omitempty" toml:"exposedByDefault,omitempty" yaml:"exposedByDefault,omitempty" export:"true"`
|
||||
Constraints string `description:"Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container." json:"constraints,omitempty" toml:"constraints,omitempty" yaml:"constraints,omitempty" export:"true"`
|
||||
AllowEmptyServices bool `description:"Disregards the Docker containers health checks with respect to the creation or removal of the corresponding services." json:"allowEmptyServices,omitempty" toml:"allowEmptyServices,omitempty" yaml:"allowEmptyServices,omitempty" export:"true"`
|
||||
Network string `description:"Default Docker network used." json:"network,omitempty" toml:"network,omitempty" yaml:"network,omitempty" export:"true"`
|
||||
UseBindPortIP bool `description:"Use the ip address from the bound port, rather than from the inner network." json:"useBindPortIP,omitempty" toml:"useBindPortIP,omitempty" yaml:"useBindPortIP,omitempty" export:"true"`
|
||||
|
||||
Watch bool `description:"Watch Docker events." json:"watch,omitempty" toml:"watch,omitempty" yaml:"watch,omitempty" export:"true"`
|
||||
DefaultRule string `description:"Default rule." json:"defaultRule,omitempty" toml:"defaultRule,omitempty" yaml:"defaultRule,omitempty"`
|
||||
|
||||
defaultRuleTpl *template.Template
|
||||
}
|
||||
|
||||
func inspectContainers(ctx context.Context, dockerClient client.ContainerAPIClient, containerID string) dockerData {
|
||||
containerInspected, err := dockerClient.ContainerInspect(ctx, containerID)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Warn().Err(err).Msgf("Failed to inspect container %s", containerID)
|
||||
return dockerData{}
|
||||
}
|
||||
|
||||
// This condition is here to avoid to have empty IP https://github.com/traefik/traefik/issues/2459
|
||||
// We register only container which are running
|
||||
if containerInspected.ContainerJSONBase != nil && containerInspected.ContainerJSONBase.State != nil && containerInspected.ContainerJSONBase.State.Running {
|
||||
return parseContainer(containerInspected)
|
||||
}
|
||||
|
||||
return dockerData{}
|
||||
}
|
||||
|
||||
func parseContainer(container dockertypes.ContainerJSON) dockerData {
|
||||
dData := dockerData{
|
||||
NetworkSettings: networkSettings{},
|
||||
}
|
||||
|
||||
if container.ContainerJSONBase != nil {
|
||||
dData.ID = container.ContainerJSONBase.ID
|
||||
dData.Name = container.ContainerJSONBase.Name
|
||||
dData.ServiceName = dData.Name // Default ServiceName to be the container's Name.
|
||||
dData.Node = container.ContainerJSONBase.Node
|
||||
|
||||
if container.ContainerJSONBase.HostConfig != nil {
|
||||
dData.NetworkSettings.NetworkMode = container.ContainerJSONBase.HostConfig.NetworkMode
|
||||
}
|
||||
|
||||
if container.State != nil && container.State.Health != nil {
|
||||
dData.Health = container.State.Health.Status
|
||||
}
|
||||
}
|
||||
|
||||
if container.Config != nil && container.Config.Labels != nil {
|
||||
dData.Labels = container.Config.Labels
|
||||
}
|
||||
|
||||
if container.NetworkSettings != nil {
|
||||
if container.NetworkSettings.Ports != nil {
|
||||
dData.NetworkSettings.Ports = container.NetworkSettings.Ports
|
||||
}
|
||||
if container.NetworkSettings.Networks != nil {
|
||||
dData.NetworkSettings.Networks = make(map[string]*networkData)
|
||||
for name, containerNetwork := range container.NetworkSettings.Networks {
|
||||
addr := containerNetwork.IPAddress
|
||||
if addr == "" {
|
||||
addr = containerNetwork.GlobalIPv6Address
|
||||
}
|
||||
|
||||
dData.NetworkSettings.Networks[name] = &networkData{
|
||||
ID: containerNetwork.NetworkID,
|
||||
Name: name,
|
||||
Addr: addr,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return dData
|
||||
}
|
||||
|
||||
type ClientConfig struct {
|
||||
apiVersion string
|
||||
|
||||
Endpoint string `description:"Docker server endpoint. Can be a TCP or a Unix socket endpoint." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"`
|
||||
TLS *types.ClientTLS `description:"Enable Docker TLS support." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
|
||||
HTTPClientTimeout ptypes.Duration `description:"Client timeout for HTTP connections." json:"httpClientTimeout,omitempty" toml:"httpClientTimeout,omitempty" yaml:"httpClientTimeout,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
func createClient(ctx context.Context, cfg ClientConfig) (*client.Client, error) {
|
||||
opts, err := getClientOpts(ctx, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
httpHeaders := map[string]string{
|
||||
"User-Agent": "Traefik " + version.Version,
|
||||
}
|
||||
|
||||
opts = append(opts,
|
||||
client.WithHTTPHeaders(httpHeaders),
|
||||
client.WithVersion(cfg.apiVersion))
|
||||
|
||||
return client.NewClientWithOpts(opts...)
|
||||
}
|
||||
|
||||
func getClientOpts(ctx context.Context, cfg ClientConfig) ([]client.Opt, error) {
|
||||
helper, err := connhelper.GetConnectionHelper(cfg.Endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// SSH
|
||||
if helper != nil {
|
||||
// https://github.com/docker/cli/blob/ebca1413117a3fcb81c89d6be226dcec74e5289f/cli/context/docker/load.go#L112-L123
|
||||
|
||||
httpClient := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: helper.Dialer,
|
||||
},
|
||||
}
|
||||
|
||||
return []client.Opt{
|
||||
client.WithHTTPClient(httpClient),
|
||||
client.WithTimeout(time.Duration(cfg.HTTPClientTimeout)),
|
||||
client.WithHost(helper.Host), // To avoid 400 Bad Request: malformed Host header daemon error
|
||||
client.WithDialContext(helper.Dialer),
|
||||
}, nil
|
||||
}
|
||||
|
||||
opts := []client.Opt{
|
||||
client.WithHost(cfg.Endpoint),
|
||||
client.WithTimeout(time.Duration(cfg.HTTPClientTimeout)),
|
||||
}
|
||||
|
||||
if cfg.TLS != nil {
|
||||
conf, err := cfg.TLS.CreateTLSConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create client TLS configuration: %w", err)
|
||||
}
|
||||
|
||||
hostURL, err := client.ParseHostURL(cfg.Endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: conf,
|
||||
}
|
||||
|
||||
if err := sockets.ConfigureTransport(tr, hostURL.Scheme, hostURL.Host); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
opts = append(opts, client.WithHTTPClient(&http.Client{Transport: tr, Timeout: time.Duration(cfg.HTTPClientTimeout)}))
|
||||
}
|
||||
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
func getPort(container dockerData, serverPort string) string {
|
||||
if len(serverPort) > 0 {
|
||||
return serverPort
|
||||
}
|
||||
|
||||
var ports []nat.Port
|
||||
for port := range container.NetworkSettings.Ports {
|
||||
ports = append(ports, port)
|
||||
}
|
||||
|
||||
less := func(i, j nat.Port) bool {
|
||||
return i.Int() < j.Int()
|
||||
}
|
||||
nat.Sort(ports, less)
|
||||
|
||||
if len(ports) > 0 {
|
||||
min := ports[0]
|
||||
return min.Port()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func getServiceName(container dockerData) string {
|
||||
serviceName := container.ServiceName
|
||||
|
||||
if values, err := getStringMultipleStrict(container.Labels, labelDockerComposeProject, labelDockerComposeService); err == nil {
|
||||
serviceName = values[labelDockerComposeService] + "_" + values[labelDockerComposeProject]
|
||||
}
|
||||
|
||||
return provider.Normalize(serviceName)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue