Skip to content

Commit

Permalink
Add support for Docker TCP URIs
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexGustafsson committed Jan 12, 2025
1 parent bdf1fcc commit 2de1952
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 15 deletions.
7 changes: 7 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,10 @@ docker run --rm -it \
export CUPDATE_OTEL_TARGET=localhost:4317
export CUPDATE_OTEL_INSECURE=true
```

Optionally proxy a Docker socket to test Docker over TCP. Use the proxied port
as the Docker host rather then the one specified in `.env-docker`.

```shell
go run tools/sockproxy/*.go -p 3000 docker.sock
```
2 changes: 1 addition & 1 deletion docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ done using environment variables.
| `CUPDATE_PROCESSING_QUEUE_RATE` | The desired processing rate under normal circumstances. | `1m` |
| `CUPDATE_KUBERNETES_HOST` | The host of the Kubernetes API. For use with proxying. | Required to use Kubernetes. |
| `CUPDATE_KUBERNETES_INCLUDE_OLD_REPLICAS` | Whether or not to include old replica sets when scraping. | `false` |
| `CUPDATE_DOCKER_HOST` | One or more comma-separated Docker host URIs. | Required to use Docker. |
| `CUPDATE_DOCKER_HOST` | One or more comma-separated Docker host URIs. Supports unix://path and tcp://host:port URIs. | Required to use Docker. |
| `CUPDATE_DOCKER_INCLUDE_ALL_CONTAINERS` | Whether or not to include containers in any state, not just running containers. | `false` |
| `CUPDATE_OTEL_TARGET` | Target URL to an Open Telemetry GRPC ingest endpoint. | Required to use Open Telemetry. |
| `CUPDATE_OTEL_INSECURE` | Disable client transport security for the Open Telemetry GRPC connection. | `false` |
51 changes: 37 additions & 14 deletions internal/platform/docker/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"net"
"net/http"
"net/url"
"os"
"strings"
"time"

Expand All @@ -28,25 +29,47 @@ type Options struct {
IncludeAllContainers bool
}

func NewPlatform(ctx context.Context, host string, options *Options) (*Platform, error) {
func NewPlatform(ctx context.Context, dockerURI string, options *Options) (*Platform, error) {
if options == nil {
options = &Options{}
}

if !strings.HasPrefix(host, "unix://") {
return nil, fmt.Errorf("unexpected docker host - expected a unix socket")
}
path := strings.TrimPrefix(host, "unix://")
var transport *http.Transport
if strings.HasPrefix(dockerURI, "unix://") {
host := strings.TrimPrefix(dockerURI, "unix://")

client := &http.Client{
Transport: &http.Transport{
if _, err := os.Stat(host); err != nil {
return nil, err
}

transport = &http.Transport{
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
return (&net.Dialer{
Timeout: 5 * time.Second,
}).DialContext(ctx, "unix", host)
},
}
} else if strings.HasPrefix(dockerURI, "tcp://") {
host := strings.TrimPrefix(dockerURI, "tcp://")

if _, _, err := net.SplitHostPort(host); err != nil {
return nil, err
}

transport = &http.Transport{
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
return (&net.Dialer{
Timeout: 5 * time.Second,
}).DialContext(ctx, "unix", path)
}).DialContext(ctx, "tcp", host)
},
},
Timeout: 10 * time.Second,
}
} else {
return nil, fmt.Errorf("unsupported docker URI: %s", dockerURI)
}

client := &http.Client{
Transport: transport,
Timeout: 10 * time.Second,
}

p := &Platform{
Expand All @@ -55,7 +78,7 @@ func NewPlatform(ctx context.Context, host string, options *Options) (*Platform,
includeAllContainers: options.IncludeAllContainers,
}

// Make sure that we can connect to the socket.
// Make sure that we can connect to the host.
// For now, we probably support most API versions - no need to limit the use
// or pin to specific API versions using docker's versioned path prefix
_, _, err := p.GetVersion(ctx)
Expand All @@ -67,7 +90,7 @@ func NewPlatform(ctx context.Context, host string, options *Options) (*Platform,
}

func (p *Platform) GetVersion(ctx context.Context) (string, string, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://unix/version", nil)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://_/version", nil)
if err != nil {
return "", "", err
}
Expand Down Expand Up @@ -112,7 +135,7 @@ func (p *Platform) GetContainers(ctx context.Context, options *GetContainersOpti
query.Set("filters", string(filters))
}

req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://unix/containers/json?"+query.Encode(), nil)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://_/containers/json?"+query.Encode(), nil)
if err != nil {
return nil, err
}
Expand All @@ -136,7 +159,7 @@ func (p *Platform) GetContainers(ctx context.Context, options *GetContainersOpti
}

func (p *Platform) GetImage(ctx context.Context, nameOrID string) (*Image, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://unix/images/"+url.PathEscape(nameOrID)+"/json", nil)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://_/images/"+url.PathEscape(nameOrID)+"/json", nil)
if err != nil {
return nil, err
}
Expand Down
62 changes: 62 additions & 0 deletions tools/sockproxy/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package main

import (
"context"
"flag"
"fmt"
"io"
"net"
"net/http"
"os"
)

func main() {
port := flag.Int("p", 3000, "port to serve on")
flag.Parse()

path := flag.Arg(0)
if path == "" {
fmt.Fprintf(os.Stderr, "missing required path")
os.Exit(1)
}

client := &http.Client{
Transport: &http.Transport{
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, "unix", path)
},
},
}

err := http.ListenAndServe(fmt.Sprintf("localhost:%d", *port), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
req, err := http.NewRequest(r.Method, fmt.Sprintf("http://_%s?%s", r.URL.Path, r.URL.RawQuery), r.Body)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to proxy request: %v", err)
os.Exit(1)
}

req.Header = r.Header

res, err := client.Do(req)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to proxy request: %v", err)
os.Exit(1)
}
defer res.Body.Close()

header := w.Header()
for k, v := range res.Header {
header[k] = v
}

w.WriteHeader(res.StatusCode)
if _, err := io.Copy(w, res.Body); err != nil {
fmt.Fprintf(os.Stderr, "failed to proxy request: %v", err)
os.Exit(1)
}
}))
if err != nil {
fmt.Fprintf(os.Stderr, "failed to serve: %v", err)
os.Exit(1)
}
}

0 comments on commit 2de1952

Please sign in to comment.