feat: docker over tls (#178)

This commit is contained in:
Yuzerion
2025-12-23 12:01:11 +08:00
committed by yusing
parent 9acb9fa50f
commit 8340d93ab7
25 changed files with 312 additions and 85 deletions

View File

@@ -13,10 +13,10 @@ type (
PortMapping = map[int]container.Port
Container struct {
DockerHost string `json:"docker_host"`
Image *ContainerImage `json:"image"`
ContainerName string `json:"container_name"`
ContainerID string `json:"container_id"`
DockerCfg DockerProviderConfig `json:"docker_cfg"`
Image *ContainerImage `json:"image"`
ContainerName string `json:"container_name"`
ContainerID string `json:"container_id"`
State container.ContainerState `json:"state"`

View File

@@ -0,0 +1,82 @@
package types
import (
"encoding/json"
"fmt"
"net"
"net/url"
"os"
"strconv"
"github.com/yusing/godoxy/internal/common"
"github.com/yusing/godoxy/internal/serialization"
gperr "github.com/yusing/goutils/errs"
)
type DockerProviderConfig struct {
URL string `json:"url,omitempty"`
TLS *DockerTLSConfig `json:"tls,omitempty"`
} // @name DockerProviderConfig
type DockerProviderConfigDetailed struct {
Scheme string `json:"scheme,omitempty" validate:"required,oneof=http https tls"`
Host string `json:"host,omitempty" validate:"required,hostname|ip"`
Port int `json:"port,omitempty" validate:"required,min=1,max=65535"`
TLS *DockerTLSConfig `json:"tls" validate:"omitempty"`
}
type DockerTLSConfig struct {
CAFile string `json:"ca_file,omitempty" validate:"required"`
CertFile string `json:"cert_file,omitempty" validate:"required"`
KeyFile string `json:"key_file,omitempty" validate:"required"`
} // @name DockerTLSConfig
func (cfg *DockerProviderConfig) MarshalJSON() ([]byte, error) {
return json.Marshal(cfg.URL)
}
func (cfg *DockerProviderConfig) Parse(value string) error {
u, err := url.Parse(value)
if err != nil {
return err
}
switch u.Scheme {
case "http", "https", "tls":
default:
return fmt.Errorf("invalid scheme: %s", u.Scheme)
}
cfg.URL = u.String()
return nil
}
func (cfg *DockerProviderConfig) UnmarshalMap(m map[string]any) gperr.Error {
var tmp DockerProviderConfigDetailed
var err = serialization.MapUnmarshalValidate(m, &tmp)
if err != nil {
return err
}
cfg.URL = fmt.Sprintf("%s://%s", tmp.Scheme, net.JoinHostPort(tmp.Host, strconv.Itoa(tmp.Port)))
cfg.TLS = tmp.TLS
if cfg.TLS != nil {
if err := checkFilesOk(cfg.TLS.CAFile, cfg.TLS.CertFile, cfg.TLS.KeyFile); err != nil {
return gperr.Wrap(err)
}
}
return nil
}
func checkFilesOk(files ...string) error {
if common.IsTest {
return nil
}
var errs gperr.Builder
for _, file := range files {
if _, err := os.Stat(file); err != nil {
errs.Add(err)
}
}
return errs.Error()
}

View File

@@ -0,0 +1,122 @@
package types
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/yusing/godoxy/internal/serialization"
)
func TestDockerProviderConfigUnmarshalMap(t *testing.T) {
t.Run("string", func(t *testing.T) {
var cfg map[string]*DockerProviderConfig
err := serialization.UnmarshalValidateYAML([]byte("test: http://localhost:2375"), &cfg)
assert.NoError(t, err)
assert.Equal(t, &DockerProviderConfig{URL: "http://localhost:2375"}, cfg["test"])
})
t.Run("detailed", func(t *testing.T) {
var cfg map[string]*DockerProviderConfig
err := serialization.UnmarshalValidateYAML([]byte(`
test:
scheme: http
host: localhost
port: 2375
tls:
ca_file: /etc/ssl/ca.crt
cert_file: /etc/ssl/cert.crt
key_file: /etc/ssl/key.crt`), &cfg)
assert.Error(t, err, os.ErrNotExist)
assert.Equal(t, &DockerProviderConfig{URL: "http://localhost:2375", TLS: &DockerTLSConfig{CAFile: "/etc/ssl/ca.crt", CertFile: "/etc/ssl/cert.crt", KeyFile: "/etc/ssl/key.crt"}}, cfg["test"])
})
}
func TestDockerProviderConfigValidation(t *testing.T) {
tests := []struct {
name string
yamlStr string
wantErr bool
}{
{name: "valid url", yamlStr: "test: http://localhost:2375", wantErr: false},
{name: "invalid url", yamlStr: "test: ftp://localhost/2375", wantErr: true},
{name: "valid scheme", yamlStr: `
test:
scheme: http
host: localhost
port: 2375
`, wantErr: false},
{name: "invalid scheme", yamlStr: `
test:
scheme: invalid
host: localhost
port: 2375
`, wantErr: true},
{name: "valid host (ipv4)", yamlStr: `
test:
scheme: http
host: 127.0.0.1
port: 2375
`, wantErr: false},
{name: "valid host (ipv6)", yamlStr: `
test:
scheme: http
host: ::1
port: 2375
`, wantErr: false},
{name: "valid host (hostname)", yamlStr: `
test:
scheme: http
host: example.com
port: 2375
`, wantErr: false},
{name: "invalid host", yamlStr: `
test:
scheme: http
host: invalid:1234
port: 2375
`, wantErr: true},
{name: "valid port", yamlStr: `
test:
scheme: http
host: localhost
port: 2375
`, wantErr: false},
{name: "invalid port", yamlStr: `
test:
scheme: http
host: localhost
port: 65536
`, wantErr: true},
{name: "valid tls", yamlStr: `
test:
scheme: tls
host: localhost
port: 2375
tls:
ca_file: /etc/ssl/ca.crt
cert_file: /etc/ssl/cert.crt
key_file: /etc/ssl/key.crt
`, wantErr: false},
{name: "invalid tls (missing cert file and key file)", yamlStr: `
test:
scheme: tls
host: localhost
port: 2375
tls:
ca_file: /etc/ssl/ca.crt
`, wantErr: true},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var cfg map[string]*DockerProviderConfig
err := serialization.UnmarshalValidateYAML([]byte(test.yamlStr), &cfg)
if test.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}

View File

@@ -38,9 +38,9 @@ type (
ContainerSignal string // @name ContainerSignal
DockerConfig struct {
DockerHost string `json:"docker_host" validate:"required"`
ContainerID string `json:"container_id" validate:"required"`
ContainerName string `json:"container_name" validate:"required"`
DockerCfg DockerProviderConfig `json:"docker_cfg" validate:"required"`
ContainerID string `json:"container_id" validate:"required"`
ContainerName string `json:"container_name" validate:"required"`
} // @name DockerConfig
ProxmoxConfig struct {
Node string `json:"node" validate:"required"`