961 lines
31 KiB
Go
961 lines
31 KiB
Go
/*
|
|
* Copyright 2020 The Dragonfly Authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
// Package config holds all options of peerhost.
|
|
package config
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"time"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
|
|
commonv1 "d7y.io/api/v2/pkg/apis/common/v1"
|
|
|
|
"d7y.io/dragonfly/v2/client/util"
|
|
"d7y.io/dragonfly/v2/cmd/dependency/base"
|
|
"d7y.io/dragonfly/v2/pkg/dfnet"
|
|
"d7y.io/dragonfly/v2/pkg/net/ip"
|
|
"d7y.io/dragonfly/v2/pkg/types"
|
|
"d7y.io/dragonfly/v2/pkg/unit"
|
|
)
|
|
|
|
type DaemonConfig = DaemonOption
|
|
type DaemonOption struct {
|
|
base.Options `yaml:",inline" mapstructure:",squash"`
|
|
// AliveTime indicates alive duration for which daemon keeps no accessing by any uploading and download requests,
|
|
// after this period daemon will automatically exit
|
|
// when AliveTime == 0, will run infinitely
|
|
AliveTime util.Duration `mapstructure:"aliveTime" yaml:"aliveTime"`
|
|
GCInterval util.Duration `mapstructure:"gcInterval" yaml:"gcInterval"`
|
|
Metrics string `mapstructure:"metrics" yaml:"metrics"`
|
|
|
|
WorkHome string `mapstructure:"workHome" yaml:"workHome"`
|
|
WorkHomeMode uint32 `mapstructure:"workHomeMode" yaml:"workHomeMode"`
|
|
CacheDir string `mapstructure:"cacheDir" yaml:"cacheDir"`
|
|
CacheDirMode uint32 `mapstructure:"cacheDirMode" yaml:"cacheDirMode"`
|
|
LogDir string `mapstructure:"logDir" yaml:"logDir"`
|
|
PluginDir string `mapstructure:"pluginDir" yaml:"pluginDir"`
|
|
DataDir string `mapstructure:"dataDir" yaml:"dataDir"`
|
|
DataDirMode uint32 `mapstructure:"dataDirMode" yaml:"dataDirMode"`
|
|
KeepStorage bool `mapstructure:"keepStorage" yaml:"keepStorage"`
|
|
|
|
Security GlobalSecurityOption `mapstructure:"security" yaml:"security"`
|
|
Scheduler SchedulerOption `mapstructure:"scheduler" yaml:"scheduler"`
|
|
Host HostOption `mapstructure:"host" yaml:"host"`
|
|
Download DownloadOption `mapstructure:"download" yaml:"download"`
|
|
Proxy *ProxyOption `mapstructure:"proxy" yaml:"proxy"`
|
|
Upload UploadOption `mapstructure:"upload" yaml:"upload"`
|
|
ObjectStorage ObjectStorageOption `mapstructure:"objectStorage" yaml:"objectStorage"`
|
|
Storage StorageOption `mapstructure:"storage" yaml:"storage"`
|
|
Health *HealthOption `mapstructure:"health" yaml:"health"`
|
|
Reload ReloadOption `mapstructure:"reload" yaml:"reload"`
|
|
Network *NetworkOption `mapstructure:"network" yaml:"network"`
|
|
Announcer AnnouncerOption `mapstructure:"announcer" yaml:"announcer"`
|
|
NetworkTopology NetworkTopologyOption `mapstructure:"networkTopology" yaml:"networkTopology"`
|
|
}
|
|
|
|
func NewDaemonConfig() *DaemonOption {
|
|
return peerHostConfig()
|
|
}
|
|
|
|
func (p *DaemonOption) Load(path string) error {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to load peer host configuration from %q [%v]", path, err)
|
|
}
|
|
|
|
switch filepath.Ext(path) {
|
|
case ".json":
|
|
err := json.Unmarshal(data, p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case ".yml", ".yaml":
|
|
err := yaml.Unmarshal(data, p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
return fmt.Errorf("extension of %s is not in 'yml/yaml/json'", path)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *DaemonOption) Convert() error {
|
|
if p.Host.AdvertiseIP == nil {
|
|
if p.Network.EnableIPv6 {
|
|
p.Host.AdvertiseIP = ip.IPv6
|
|
} else {
|
|
p.Host.AdvertiseIP = ip.IPv4
|
|
}
|
|
}
|
|
|
|
if p.Download.PeerGRPC.TCPListen != nil && p.Download.PeerGRPC.TCPListen.Listen == "" {
|
|
if p.Network.EnableIPv6 {
|
|
p.Download.PeerGRPC.TCPListen.Listen = net.IPv6zero.String()
|
|
} else {
|
|
p.Download.PeerGRPC.TCPListen.Listen = net.IPv4zero.String()
|
|
}
|
|
}
|
|
|
|
if p.Upload.TCPListen != nil && p.Upload.TCPListen.Listen == "" {
|
|
if p.Network.EnableIPv6 {
|
|
p.Upload.TCPListen.Listen = net.IPv6zero.String()
|
|
} else {
|
|
p.Upload.TCPListen.Listen = net.IPv4zero.String()
|
|
}
|
|
}
|
|
|
|
if p.ObjectStorage.ListenOption.TCPListen != nil && p.ObjectStorage.ListenOption.TCPListen.Listen == "" {
|
|
if p.Network.EnableIPv6 {
|
|
p.ObjectStorage.ListenOption.TCPListen.Listen = net.IPv6zero.String()
|
|
} else {
|
|
p.ObjectStorage.ListenOption.TCPListen.Listen = net.IPv4zero.String()
|
|
}
|
|
}
|
|
|
|
if p.Proxy.ListenOption.TCPListen != nil && p.Proxy.ListenOption.TCPListen.Listen == "" {
|
|
if p.Network.EnableIPv6 {
|
|
p.Proxy.ListenOption.TCPListen.Listen = net.IPv6zero.String()
|
|
} else {
|
|
p.Proxy.ListenOption.TCPListen.Listen = net.IPv4zero.String()
|
|
}
|
|
}
|
|
|
|
if p.Health.ListenOption.TCPListen != nil && p.Health.ListenOption.TCPListen.Listen == "" {
|
|
if p.Network.EnableIPv6 {
|
|
p.Health.ListenOption.TCPListen.Listen = net.IPv6zero.String()
|
|
} else {
|
|
p.Health.ListenOption.TCPListen.Listen = net.IPv4zero.String()
|
|
}
|
|
}
|
|
|
|
// ScheduleTimeout should not great then AliveTime
|
|
if p.AliveTime.Duration > 0 && p.Scheduler.ScheduleTimeout.Duration > p.AliveTime.Duration {
|
|
p.Scheduler.ScheduleTimeout.Duration = p.AliveTime.Duration - time.Second
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *DaemonOption) Validate() error {
|
|
if p.Scheduler.Manager.Enable {
|
|
if len(p.Scheduler.Manager.NetAddrs) == 0 {
|
|
return errors.New("manager addr is not specified")
|
|
}
|
|
|
|
if p.Scheduler.Manager.RefreshInterval == 0 {
|
|
return errors.New("manager refreshInterval is not specified")
|
|
}
|
|
} else {
|
|
if len(p.Scheduler.NetAddrs) == 0 {
|
|
return errors.New("empty schedulers and config server is not specified")
|
|
}
|
|
}
|
|
|
|
if int64(p.Download.TotalRateLimit.Limit) < DefaultMinRate.ToNumber() {
|
|
return fmt.Errorf("rate limit must be greater than %s", DefaultMinRate.String())
|
|
}
|
|
|
|
if int64(p.Upload.RateLimit.Limit) < DefaultMinRate.ToNumber() {
|
|
return fmt.Errorf("rate limit must be greater than %s", DefaultMinRate.String())
|
|
}
|
|
|
|
if p.ObjectStorage.Enable {
|
|
if p.ObjectStorage.MaxReplicas <= 0 {
|
|
return errors.New("max replicas must be greater than 0")
|
|
}
|
|
}
|
|
|
|
if p.Reload.Interval.Duration > 0 && p.Reload.Interval.Duration < time.Second {
|
|
return errors.New("reload interval too short, must great than 1 second")
|
|
}
|
|
|
|
if p.GCInterval.Duration <= 0 {
|
|
return errors.New("gcInterval must be greater than 0")
|
|
}
|
|
|
|
if p.Security.AutoIssueCert {
|
|
if p.Security.CACert == "" {
|
|
return errors.New("security requires parameter caCert")
|
|
}
|
|
|
|
if len(p.Security.CertSpec.IPAddresses) == 0 {
|
|
return errors.New("certSpec requires parameter ipAddresses")
|
|
}
|
|
|
|
if len(p.Security.CertSpec.DNSNames) == 0 {
|
|
return errors.New("certSpec requires parameter dnsNames")
|
|
}
|
|
|
|
if p.Security.CertSpec.ValidityPeriod <= 0 {
|
|
return errors.New("certSpec requires parameter validityPeriod")
|
|
}
|
|
}
|
|
|
|
if p.NetworkTopology.Enable {
|
|
if p.NetworkTopology.Probe.Interval <= 0 {
|
|
return errors.New("probe requires parameter interval")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type GlobalSecurityOption struct {
|
|
// AutoIssueCert indicates to issue client certificates for all grpc call
|
|
// if AutoIssueCert is false, any other option in Security will be ignored
|
|
AutoIssueCert bool `mapstructure:"autoIssueCert" yaml:"autoIssueCert"`
|
|
// CACert is the root CA certificate for all grpc tls handshake, it can be path or PEM format string
|
|
CACert types.PEMContent `mapstructure:"caCert" yaml:"caCert"`
|
|
// TLSVerify indicates to verify client certificates.
|
|
TLSVerify bool `mapstructure:"tlsVerify" yaml:"tlsVerify"`
|
|
// TLSPolicy controls the grpc shandshake behaviors:
|
|
// force: both ClientHandshake and ServerHandshake are only support tls
|
|
// prefer: ServerHandshake supports tls and insecure (non-tls), ClientHandshake will only support tls
|
|
// default: ServerHandshake supports tls and insecure (non-tls), ClientHandshake will only support insecure (non-tls)
|
|
TLSPolicy string `mapstructure:"tlsPolicy" yaml:"tlsPolicy"`
|
|
// CertSpec is the desired state of certificate.
|
|
CertSpec *CertSpec `mapstructure:"certSpec" yaml:"certSpec"`
|
|
}
|
|
|
|
type CertSpec struct {
|
|
// DNSNames is a list of dns names be set on the certificate.
|
|
DNSNames []string `mapstructure:"dnsNames" yaml:"dnsNames"`
|
|
// IPAddresses is a list of ip addresses be set on the certificate.
|
|
IPAddresses []net.IP `mapstructure:"ipAddresses" yaml:"ipAddresses"`
|
|
// ValidityPeriod is the validity period of certificate.
|
|
ValidityPeriod time.Duration `mapstructure:"validityPeriod" yaml:"validityPeriod"`
|
|
}
|
|
|
|
type SchedulerOption struct {
|
|
// Manager is to get the scheduler configuration remotely.
|
|
Manager ManagerOption `mapstructure:"manager" yaml:"manager"`
|
|
// NetAddrs is scheduler addresses.
|
|
NetAddrs []dfnet.NetAddr `mapstructure:"netAddrs" yaml:"netAddrs"`
|
|
// ScheduleTimeout is request timeout.
|
|
ScheduleTimeout util.Duration `mapstructure:"scheduleTimeout" yaml:"scheduleTimeout"`
|
|
// DisableAutoBackSource indicates not back source normally, only scheduler says back source.
|
|
DisableAutoBackSource bool `mapstructure:"disableAutoBackSource" yaml:"disableAutoBackSource"`
|
|
}
|
|
|
|
type ManagerOption struct {
|
|
// Enable get configuration from manager.
|
|
Enable bool `mapstructure:"enable" yaml:"enable"`
|
|
// NetAddrs is manager addresses.
|
|
NetAddrs []dfnet.NetAddr `mapstructure:"netAddrs" yaml:"netAddrs"`
|
|
// RefreshInterval is the refresh interval.
|
|
RefreshInterval time.Duration `mapstructure:"refreshInterval" yaml:"refreshInterval"`
|
|
// SeedPeer configuration.
|
|
SeedPeer SeedPeerOption `mapstructure:"seedPeer" yaml:"seedPeer"`
|
|
}
|
|
|
|
type SeedPeerOption struct {
|
|
// Enable seed peer mode.
|
|
Enable bool `mapstructure:"enable" yaml:"enable"`
|
|
// Type is seed peer type.
|
|
Type string `mapstructure:"type" yaml:"type"`
|
|
// ClusterID is seed peer cluster id.
|
|
ClusterID uint `mapstructure:"clusterID" yaml:"clusterID"`
|
|
// KeepAlive configuration.
|
|
KeepAlive KeepAliveOption `yaml:"keepAlive" mapstructure:"keepAlive"`
|
|
}
|
|
|
|
type KeepAliveOption struct {
|
|
// Keep alive interval.
|
|
Interval time.Duration `yaml:"interval" mapstructure:"interval"`
|
|
}
|
|
|
|
type HostOption struct {
|
|
// IDC for scheduler
|
|
IDC string `mapstructure:"idc" yaml:"idc"`
|
|
// Location for scheduler
|
|
Location string `mapstructure:"location" yaml:"location"`
|
|
// Hostname is daemon host name
|
|
Hostname string `mapstructure:"hostname" yaml:"hostname"`
|
|
// The ip report to scheduler, normal same with listen ip
|
|
AdvertiseIP net.IP `mapstructure:"advertiseIP" yaml:"advertiseIP"`
|
|
}
|
|
|
|
type DownloadOption struct {
|
|
TotalRateLimit util.RateLimit `mapstructure:"totalRateLimit" yaml:"totalRateLimit"`
|
|
PerPeerRateLimit util.RateLimit `mapstructure:"perPeerRateLimit" yaml:"perPeerRateLimit"`
|
|
TrafficShaperType string `mapstructure:"trafficShaperType" yaml:"trafficShaperType"`
|
|
PieceDownloadTimeout time.Duration `mapstructure:"pieceDownloadTimeout" yaml:"pieceDownloadTimeout"`
|
|
GRPCDialTimeout time.Duration `mapstructure:"grpcDialTimeout" yaml:"grpcDialTimeout"`
|
|
DownloadGRPC ListenOption `mapstructure:"downloadGRPC" yaml:"downloadGRPC"`
|
|
PeerGRPC ListenOption `mapstructure:"peerGRPC" yaml:"peerGRPC"`
|
|
CalculateDigest bool `mapstructure:"calculateDigest" yaml:"calculateDigest"`
|
|
Transport *TransportOption `mapstructure:"transportOption" yaml:"transportOption"`
|
|
GetPiecesMaxRetry int `mapstructure:"getPiecesMaxRetry" yaml:"getPiecesMaxRetry"`
|
|
Prefetch bool `mapstructure:"prefetch" yaml:"prefetch"`
|
|
WatchdogTimeout time.Duration `mapstructure:"watchdogTimeout" yaml:"watchdogTimeout"`
|
|
Concurrent *ConcurrentOption `mapstructure:"concurrent" yaml:"concurrent"`
|
|
SyncPieceViaHTTPS bool `mapstructure:"syncPieceViaHTTPS" yaml:"syncPieceViaHTTPS"`
|
|
SplitRunningTasks bool `mapstructure:"splitRunningTasks" yaml:"splitRunningTasks"`
|
|
// resource clients option
|
|
ResourceClients ResourceClientsOption `mapstructure:"resourceClients" yaml:"resourceClients"`
|
|
|
|
RecursiveConcurrent RecursiveConcurrent `mapstructure:"recursiveConcurrent" yaml:"recursiveConcurrent"`
|
|
CacheRecursiveMetadata time.Duration `mapstructure:"cacheRecursiveMetadata" yaml:"cacheRecursiveMetadata"`
|
|
}
|
|
|
|
type ResourceClientsOption map[string]any
|
|
|
|
type TransportOption struct {
|
|
DialTimeout time.Duration `mapstructure:"dialTimeout" yaml:"dialTimeout"`
|
|
KeepAlive time.Duration `mapstructure:"keepAlive" yaml:"keepAlive"`
|
|
MaxIdleConns int `mapstructure:"maxIdleConns" yaml:"maxIdleConns"`
|
|
IdleConnTimeout time.Duration `mapstructure:"idleConnTimeout" yaml:"idleConnTimeout"`
|
|
ResponseHeaderTimeout time.Duration `mapstructure:"responseHeaderTimeout" yaml:"responseHeaderTimeout"`
|
|
TLSHandshakeTimeout time.Duration `mapstructure:"tlsHandshakeTimeout" yaml:"tlsHandshakeTimeout"`
|
|
ExpectContinueTimeout time.Duration `mapstructure:"expectContinueTimeout" yaml:"expectContinueTimeout"`
|
|
}
|
|
|
|
type ConcurrentOption struct {
|
|
// ThresholdSize indicates the threshold to download pieces concurrently
|
|
ThresholdSize util.Size `mapstructure:"thresholdSize" yaml:"thresholdSize"`
|
|
// ThresholdSpeed indicates the threshold download speed to download pieces concurrently
|
|
ThresholdSpeed unit.Bytes `mapstructure:"thresholdSpeed" yaml:"thresholdSpeed"`
|
|
// GoroutineCount indicates the concurrent goroutine count for every task
|
|
GoroutineCount int `mapstructure:"goroutineCount" yaml:"goroutineCount"`
|
|
// InitBackoff second for every piece failed, default: 0.5
|
|
InitBackoff float64 `mapstructure:"initBackoff" yaml:"initBackoff"`
|
|
// MaxBackoff second for every piece failed, default: 3
|
|
MaxBackoff float64 `mapstructure:"maxBackoff" yaml:"maxBackoff"`
|
|
// MaxAttempts for every piece failed,default: 3
|
|
MaxAttempts int `mapstructure:"maxAttempts" yaml:"maxAttempts"`
|
|
}
|
|
|
|
type RecursiveConcurrent struct {
|
|
// GoroutineCount indicates the concurrent goroutine count for every recursive task
|
|
GoroutineCount int `mapstructure:"goroutineCount" yaml:"goroutineCount"`
|
|
}
|
|
|
|
type ProxyOption struct {
|
|
// WARNING: when add more option, please update ProxyOption.unmarshal function
|
|
ListenOption `mapstructure:",squash" yaml:",inline"`
|
|
BasicAuth *BasicAuth `mapstructure:"basicAuth" yaml:"basicAuth"`
|
|
DefaultFilter string `mapstructure:"defaultFilter" yaml:"defaultFilter"`
|
|
DefaultTag string `mapstructure:"defaultTag" yaml:"defaultTag"`
|
|
DefaultApplication string `mapstructure:"defaultApplication" yaml:"defaultApplication"`
|
|
DefaultPriority commonv1.Priority `mapstructure:"defaultPriority" yaml:"defaultPriority"`
|
|
MaxConcurrency int64 `mapstructure:"maxConcurrency" yaml:"maxConcurrency"`
|
|
RegistryMirror *RegistryMirror `mapstructure:"registryMirror" yaml:"registryMirror"`
|
|
WhiteList []*WhiteList `mapstructure:"whiteList" yaml:"whiteList"`
|
|
ProxyRules []*ProxyRule `mapstructure:"proxies" yaml:"proxies"`
|
|
HijackHTTPS *HijackConfig `mapstructure:"hijackHTTPS" yaml:"hijackHTTPS"`
|
|
DumpHTTPContent bool `mapstructure:"dumpHTTPContent" yaml:"dumpHTTPContent"`
|
|
// ExtraRegistryMirrors add more mirror for different ports
|
|
ExtraRegistryMirrors []*RegistryMirror `mapstructure:"extraRegistryMirrors" yaml:"extraRegistryMirrors"`
|
|
}
|
|
|
|
func (p *ProxyOption) UnmarshalJSON(b []byte) error {
|
|
var v any
|
|
if err := json.Unmarshal(b, &v); err != nil {
|
|
return err
|
|
}
|
|
|
|
switch value := v.(type) {
|
|
case string:
|
|
file, err := os.ReadFile(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := json.Unmarshal(file, p); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case map[string]any:
|
|
if err := p.unmarshal(json.Unmarshal, b); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
default:
|
|
return errors.New("invalid proxy option")
|
|
}
|
|
}
|
|
|
|
func (p *ProxyOption) UnmarshalYAML(node *yaml.Node) error {
|
|
switch node.Kind {
|
|
case yaml.ScalarNode:
|
|
var path string
|
|
if err := node.Decode(&path); err != nil {
|
|
return err
|
|
}
|
|
|
|
file, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := yaml.Unmarshal(file, p); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case yaml.MappingNode:
|
|
var m = make(map[string]any)
|
|
for i := 0; i < len(node.Content); i += 2 {
|
|
var (
|
|
key string
|
|
value any
|
|
)
|
|
if err := node.Content[i].Decode(&key); err != nil {
|
|
return err
|
|
}
|
|
if err := node.Content[i+1].Decode(&value); err != nil {
|
|
return err
|
|
}
|
|
m[key] = value
|
|
}
|
|
|
|
b, err := yaml.Marshal(m)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := p.unmarshal(yaml.Unmarshal, b); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
default:
|
|
return errors.New("invalid proxy")
|
|
}
|
|
}
|
|
|
|
func (p *ProxyOption) unmarshal(unmarshal func(in []byte, out any) (err error), b []byte) error {
|
|
pt := struct {
|
|
ListenOption `mapstructure:",squash" yaml:",inline"`
|
|
BasicAuth *BasicAuth `mapstructure:"basicAuth" yaml:"basicAuth"`
|
|
DefaultFilter string `mapstructure:"defaultFilter" yaml:"defaultFilter"`
|
|
DefaultTag string `mapstructure:"defaultTag" yaml:"defaultTag"`
|
|
DefaultApplication string `mapstructure:"defaultApplication" yaml:"defaultApplication"`
|
|
MaxConcurrency int64 `mapstructure:"maxConcurrency" yaml:"maxConcurrency"`
|
|
RegistryMirror *RegistryMirror `mapstructure:"registryMirror" yaml:"registryMirror"`
|
|
WhiteList []*WhiteList `mapstructure:"whiteList" yaml:"whiteList"`
|
|
Proxies []*ProxyRule `mapstructure:"proxies" yaml:"proxies"`
|
|
HijackHTTPS *HijackConfig `mapstructure:"hijackHTTPS" yaml:"hijackHTTPS"`
|
|
DumpHTTPContent bool `mapstructure:"dumpHTTPContent" yaml:"dumpHTTPContent"`
|
|
ExtraRegistryMirrors []*RegistryMirror `mapstructure:"extraRegistryMirrors" yaml:"extraRegistryMirrors"`
|
|
}{}
|
|
|
|
if err := unmarshal(b, &pt); err != nil {
|
|
return err
|
|
}
|
|
p.ExtraRegistryMirrors = pt.ExtraRegistryMirrors
|
|
p.ListenOption = pt.ListenOption
|
|
p.RegistryMirror = pt.RegistryMirror
|
|
p.ProxyRules = pt.Proxies
|
|
p.HijackHTTPS = pt.HijackHTTPS
|
|
p.WhiteList = pt.WhiteList
|
|
p.MaxConcurrency = pt.MaxConcurrency
|
|
p.DefaultFilter = pt.DefaultFilter
|
|
p.DefaultTag = pt.DefaultTag
|
|
p.DefaultApplication = pt.DefaultApplication
|
|
p.BasicAuth = pt.BasicAuth
|
|
p.DumpHTTPContent = pt.DumpHTTPContent
|
|
|
|
return nil
|
|
}
|
|
|
|
type UploadOption struct {
|
|
ListenOption `yaml:",inline" mapstructure:",squash"`
|
|
RateLimit util.RateLimit `mapstructure:"rateLimit" yaml:"rateLimit"`
|
|
}
|
|
|
|
type ObjectStorageOption struct {
|
|
// Enable object storage.
|
|
Enable bool `mapstructure:"enable" yaml:"enable"`
|
|
// Filter is used to generate a unique Task ID by
|
|
// filtering unnecessary query params in the URL,
|
|
// it is separated by & character.
|
|
Filter string `mapstructure:"filter" yaml:"filter"`
|
|
// MaxReplicas is the maximum number of replicas of an object cache in seed peers.
|
|
MaxReplicas int `mapstructure:"maxReplicas" yaml:"maxReplicas"`
|
|
// ListenOption is object storage service listener.
|
|
ListenOption `yaml:",inline" mapstructure:",squash"`
|
|
}
|
|
|
|
type ListenOption struct {
|
|
Security SecurityOption `mapstructure:"security" yaml:"security"`
|
|
TCPListen *TCPListenOption `mapstructure:"tcpListen,omitempty" yaml:"tcpListen,omitempty"`
|
|
UnixListen *UnixListenOption `mapstructure:"unixListen,omitempty" yaml:"unixListen,omitempty"`
|
|
}
|
|
|
|
type TCPListenOption struct {
|
|
// Listen stands listen interface, like: 0.0.0.0, 192.168.0.1
|
|
Listen string `mapstructure:"listen" yaml:"listen"`
|
|
|
|
// PortRange stands listen port
|
|
// yaml example 1:
|
|
// port: 12345
|
|
// yaml example 2:
|
|
// port:
|
|
// start: 12345
|
|
// end: 12346
|
|
PortRange TCPListenPortRange `mapstructure:"port" yaml:"port"`
|
|
|
|
// Namespace stands the linux net namespace, like /proc/1/ns/net
|
|
// It's useful for running daemon in pod with ip allocated and listen in host
|
|
Namespace string `mapstructure:"namespace" yaml:"namespace"`
|
|
}
|
|
|
|
type TCPListenPortRange struct {
|
|
Start int
|
|
End int
|
|
}
|
|
|
|
func (t *TCPListenPortRange) UnmarshalJSON(b []byte) error {
|
|
var v any
|
|
if err := json.Unmarshal(b, &v); err != nil {
|
|
return err
|
|
}
|
|
return t.unmarshal(v)
|
|
}
|
|
|
|
func (t *TCPListenPortRange) UnmarshalYAML(node *yaml.Node) error {
|
|
var v any
|
|
switch node.Kind {
|
|
case yaml.MappingNode:
|
|
var m = make(map[string]any)
|
|
for i := 0; i < len(node.Content); i += 2 {
|
|
var (
|
|
key string
|
|
value int
|
|
)
|
|
if err := node.Content[i].Decode(&key); err != nil {
|
|
return err
|
|
}
|
|
if err := node.Content[i+1].Decode(&value); err != nil {
|
|
return err
|
|
}
|
|
m[key] = value
|
|
}
|
|
v = m
|
|
case yaml.ScalarNode:
|
|
var i int
|
|
if err := node.Decode(&i); err != nil {
|
|
return err
|
|
}
|
|
v = i
|
|
}
|
|
return t.unmarshal(v)
|
|
}
|
|
|
|
func (t *TCPListenPortRange) unmarshal(v any) error {
|
|
switch value := v.(type) {
|
|
case int:
|
|
t.Start = value
|
|
return nil
|
|
case float64:
|
|
t.Start = int(value)
|
|
return nil
|
|
case map[string]any:
|
|
if s, ok := value["start"]; ok {
|
|
switch start := s.(type) {
|
|
case float64:
|
|
t.Start = int(start)
|
|
case int:
|
|
t.Start = start
|
|
default:
|
|
return errors.New("invalid start port")
|
|
}
|
|
} else {
|
|
return errors.New("empty start port")
|
|
}
|
|
if e, ok := value["end"]; ok {
|
|
switch end := e.(type) {
|
|
case float64:
|
|
t.End = int(end)
|
|
case int:
|
|
t.End = end
|
|
default:
|
|
return errors.New("invalid end port")
|
|
}
|
|
}
|
|
return nil
|
|
default:
|
|
return errors.New("invalid port")
|
|
}
|
|
}
|
|
|
|
type UnixListenOption struct {
|
|
Socket string `mapstructure:"socket" yaml:"socket"`
|
|
}
|
|
|
|
type SecurityOption struct {
|
|
// Insecure indicate enable tls or not
|
|
Insecure bool `mapstructure:"insecure" yaml:"insecure"`
|
|
CACert types.PEMContent `mapstructure:"caCert" yaml:"caCert"`
|
|
Cert types.PEMContent `mapstructure:"cert" yaml:"cert"`
|
|
Key types.PEMContent `mapstructure:"key" yaml:"key"`
|
|
TLSVerify bool `mapstructure:"tlsVerify" yaml:"tlsVerify"`
|
|
TLSConfig *tls.Config `mapstructure:"tlsConfig" yaml:"tlsConfig"`
|
|
}
|
|
|
|
type StorageOption struct {
|
|
// DataPath indicates directory which stores temporary files for p2p uploading
|
|
DataPath string `mapstructure:"dataPath" yaml:"dataPath"`
|
|
// TaskExpireTime indicates caching duration for which cached file keeps no accessed by any process,
|
|
// after this period cache file will be gc
|
|
TaskExpireTime util.Duration `mapstructure:"taskExpireTime" yaml:"taskExpireTime"`
|
|
// DiskGCThreshold indicates the threshold to gc the oldest tasks
|
|
DiskGCThreshold unit.Bytes `mapstructure:"diskGCThreshold" yaml:"diskGCThreshold"`
|
|
// DiskGCThresholdPercent indicates the threshold to gc the oldest tasks according the disk usage
|
|
// Eg, DiskGCThresholdPercent=80, when the disk usage is above 80%, start to gc the oldest tasks
|
|
DiskGCThresholdPercent float64 `mapstructure:"diskGCThresholdPercent" yaml:"diskGCThresholdPercent"`
|
|
// Multiplex indicates reusing underlying storage for same task id
|
|
Multiplex bool `mapstructure:"multiplex" yaml:"multiplex"`
|
|
StoreStrategy StoreStrategy `mapstructure:"strategy" yaml:"strategy"`
|
|
}
|
|
|
|
type StoreStrategy string
|
|
|
|
type HealthOption struct {
|
|
ListenOption `yaml:",inline" mapstructure:",squash"`
|
|
Path string `mapstructure:"path" yaml:"path"`
|
|
}
|
|
|
|
type ReloadOption struct {
|
|
Interval util.Duration `mapstructure:"interval" yaml:"interval"`
|
|
}
|
|
|
|
type tlsConfigFiles struct {
|
|
Cert types.PEMContent `yaml:"cert" json:"cert"`
|
|
Key types.PEMContent `yaml:"key" json:"key"`
|
|
CACert types.PEMContent `yaml:"caCert" json:"caCert"`
|
|
}
|
|
|
|
type TLSConfig struct {
|
|
tls.Config
|
|
}
|
|
|
|
func (t *TLSConfig) UnmarshalJSON(b []byte) error {
|
|
var cf tlsConfigFiles
|
|
err := json.Unmarshal(b, &cf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return t.load(&cf)
|
|
}
|
|
|
|
func (t *TLSConfig) UnmarshalYAML(node *yaml.Node) error {
|
|
var cf tlsConfigFiles
|
|
switch node.Kind {
|
|
case yaml.MappingNode:
|
|
if err := node.Decode(&cf); err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
return errors.New("invalid tls config")
|
|
}
|
|
|
|
return t.load(&cf)
|
|
}
|
|
|
|
func (t *TLSConfig) load(cf *tlsConfigFiles) error {
|
|
pool := x509.NewCertPool()
|
|
if !pool.AppendCertsFromPEM([]byte(cf.CACert)) {
|
|
return errors.New("invalid CA Cert")
|
|
}
|
|
cert, err := tls.X509KeyPair([]byte(cf.Cert), []byte(cf.Key))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
t.Config = tls.Config{
|
|
RootCAs: pool,
|
|
Certificates: []tls.Certificate{cert},
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RegistryMirror configures the mirror of the official docker registry
|
|
type RegistryMirror struct {
|
|
// Remote url for the registry mirror, default is https://index.docker.io
|
|
Remote *URL `yaml:"url" mapstructure:"url"`
|
|
|
|
// DynamicRemote indicates using header "X-Dragonfly-Registry" for remote instead of Remote
|
|
// if header "X-Dragonfly-Registry" does not exist, use Remote by default
|
|
DynamicRemote bool `yaml:"dynamic" mapstructure:"dynamic"`
|
|
|
|
// Optional certificates if the mirror uses self-signed certificates
|
|
Certs *CertPool `yaml:"certs" mapstructure:"certs"`
|
|
|
|
// Whether to ignore certificates errors for the registry
|
|
Insecure bool `yaml:"insecure" mapstructure:"insecure"`
|
|
|
|
// Request the remote registry directly.
|
|
Direct bool `yaml:"direct" mapstructure:"direct"`
|
|
|
|
// Whether to use proxies to decide when to use dragonfly
|
|
UseProxies bool `yaml:"useProxies" mapstructure:"useProxies"`
|
|
}
|
|
|
|
// TLSConfig returns the tls.Config used to communicate with the mirror.
|
|
func (r *RegistryMirror) TLSConfig() *tls.Config {
|
|
if r == nil {
|
|
return nil
|
|
}
|
|
cfg := &tls.Config{
|
|
InsecureSkipVerify: r.Insecure,
|
|
}
|
|
if r.Certs != nil {
|
|
cfg.RootCAs = r.Certs.CertPool
|
|
}
|
|
return cfg
|
|
}
|
|
|
|
// URL is simple wrapper around url.URL to make it unmarshallable from a string.
|
|
type URL struct {
|
|
*url.URL
|
|
}
|
|
|
|
// UnmarshalJSON implements json.Unmarshaler.
|
|
func (u *URL) UnmarshalJSON(b []byte) error {
|
|
return u.unmarshal(func(v any) error { return json.Unmarshal(b, v) })
|
|
}
|
|
|
|
// UnmarshalYAML implements yaml.Unmarshaler.
|
|
func (u *URL) UnmarshalYAML(unmarshal func(any) error) error {
|
|
return u.unmarshal(unmarshal)
|
|
}
|
|
|
|
// MarshalJSON implements json.Marshaller to print the url.
|
|
func (u *URL) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(u.String())
|
|
}
|
|
|
|
// MarshalYAML implements yaml.Marshaller to print the url.
|
|
func (u *URL) MarshalYAML() (any, error) {
|
|
return u.String(), nil
|
|
}
|
|
|
|
func (u *URL) unmarshal(unmarshal func(any) error) error {
|
|
var s string
|
|
if err := unmarshal(&s); err != nil {
|
|
return err
|
|
}
|
|
|
|
parsed, err := url.Parse(s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
u.URL = parsed
|
|
return nil
|
|
}
|
|
|
|
// CertPool is a wrapper around x509.CertPool, which can be unmarshalled and
|
|
// constructed from a list of filenames.
|
|
type CertPool struct {
|
|
Files []string
|
|
*x509.CertPool
|
|
}
|
|
|
|
// UnmarshalJSON implements json.Unmarshaler.
|
|
func (cp *CertPool) UnmarshalJSON(b []byte) error {
|
|
return cp.unmarshal(func(v any) error { return json.Unmarshal(b, v) })
|
|
}
|
|
|
|
// UnmarshalYAML implements yaml.Unmarshaler.
|
|
func (cp *CertPool) UnmarshalYAML(unmarshal func(any) error) error {
|
|
return cp.unmarshal(unmarshal)
|
|
}
|
|
|
|
// MarshalJSON implements json.Marshaller to print the cert pool.
|
|
func (cp *CertPool) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(cp.Files)
|
|
}
|
|
|
|
// MarshalYAML implements yaml.Marshaller to print the cert pool.
|
|
func (cp *CertPool) MarshalYAML() (any, error) {
|
|
return cp.Files, nil
|
|
}
|
|
|
|
func (cp *CertPool) unmarshal(unmarshal func(any) error) error {
|
|
if err := unmarshal(&cp.Files); err != nil {
|
|
return err
|
|
}
|
|
|
|
pool, err := certPoolFromFiles(cp.Files...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
cp.CertPool = pool
|
|
return nil
|
|
}
|
|
|
|
// certPoolFromFiles returns an *x509.CertPool constructed from the given files.
|
|
// If no files are given, (nil, nil) will be returned.
|
|
func certPoolFromFiles(files ...string) (*x509.CertPool, error) {
|
|
if len(files) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
roots := x509.NewCertPool()
|
|
for _, f := range files {
|
|
cert, err := os.ReadFile(f)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read cert file %s: %w", f, err)
|
|
}
|
|
if !roots.AppendCertsFromPEM(cert) {
|
|
return nil, fmt.Errorf("invalid cert: %s", f)
|
|
}
|
|
}
|
|
return roots, nil
|
|
}
|
|
|
|
// ProxyRule describes a regular expression matching rule for how to proxy a request.
|
|
type ProxyRule struct {
|
|
Regx *Regexp `yaml:"regx" mapstructure:"regx"`
|
|
UseHTTPS bool `yaml:"useHTTPS" mapstructure:"useHTTPS"`
|
|
Direct bool `yaml:"direct" mapstructure:"direct"`
|
|
|
|
// Redirect is the host to redirect to, if not empty
|
|
Redirect string `yaml:"redirect" mapstructure:"redirect"`
|
|
}
|
|
|
|
func NewProxyRule(regx string, useHTTPS bool, direct bool, redirect string) (*ProxyRule, error) {
|
|
exp, err := NewRegexp(regx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid regexp: %w", err)
|
|
}
|
|
|
|
return &ProxyRule{
|
|
Regx: exp,
|
|
UseHTTPS: useHTTPS,
|
|
Direct: direct,
|
|
Redirect: redirect,
|
|
}, nil
|
|
}
|
|
|
|
// Match checks if the given url matches the rule.
|
|
func (r *ProxyRule) Match(url string) bool {
|
|
return r.Regx != nil && r.Regx.MatchString(url)
|
|
}
|
|
|
|
// Regexp is a simple wrapper around regexp. Regexp to make it unmarshallable from a string.
|
|
type Regexp struct {
|
|
*regexp.Regexp
|
|
}
|
|
|
|
// NewRegexp returns a new Regexp instance compiled from the given string.
|
|
func NewRegexp(exp string) (*Regexp, error) {
|
|
r, err := regexp.Compile(exp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Regexp{r}, nil
|
|
}
|
|
|
|
// UnmarshalYAML implements yaml.Unmarshaler.
|
|
func (r *Regexp) UnmarshalYAML(unmarshal func(any) error) error {
|
|
return r.unmarshal(unmarshal)
|
|
}
|
|
|
|
// UnmarshalJSON implements json.Unmarshaler.
|
|
func (r *Regexp) UnmarshalJSON(b []byte) error {
|
|
return r.unmarshal(func(v any) error { return json.Unmarshal(b, v) })
|
|
}
|
|
|
|
func (r *Regexp) unmarshal(unmarshal func(any) error) error {
|
|
var s string
|
|
if err := unmarshal(&s); err != nil {
|
|
return err
|
|
}
|
|
exp, err := regexp.Compile(s)
|
|
if err == nil {
|
|
r.Regexp = exp
|
|
}
|
|
return err
|
|
}
|
|
|
|
// MarshalJSON implements json.Marshaller to print the regexp.
|
|
func (r *Regexp) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(r.String())
|
|
}
|
|
|
|
// MarshalYAML implements yaml.Marshaller to print the regexp.
|
|
func (r *Regexp) MarshalYAML() (any, error) {
|
|
return r.String(), nil
|
|
}
|
|
|
|
// HijackConfig represents how dfdaemon hijacks http requests.
|
|
type HijackConfig struct {
|
|
Cert string `yaml:"cert" mapstructure:"cert"`
|
|
Key string `yaml:"key" mapstructure:"key"`
|
|
Hosts []*HijackHost `yaml:"hosts" mapstructure:"hosts"`
|
|
SNI []*TCPListenOption `yaml:"sni" mapstructure:"sni"`
|
|
}
|
|
|
|
// HijackHost is a hijack rule for the hosts that matches Regx.
|
|
type HijackHost struct {
|
|
Regx *Regexp `yaml:"regx" mapstructure:"regx"`
|
|
Insecure bool `yaml:"insecure" mapstructure:"insecure"`
|
|
Certs *CertPool `yaml:"certs" mapstructure:"certs"`
|
|
}
|
|
|
|
// TelemetryOption is the option for telemetry
|
|
type TelemetryOption struct {
|
|
Jaeger string `yaml:"jaeger" mapstructure:"jaeger"`
|
|
}
|
|
|
|
type WhiteList struct {
|
|
Host string `yaml:"host" mapstructure:"host"`
|
|
Regx *Regexp `yaml:"regx" mapstructure:"regx"`
|
|
Ports []string `yaml:"ports" mapstructure:"ports"`
|
|
}
|
|
|
|
type BasicAuth struct {
|
|
Username string `json:"username" yaml:"username"`
|
|
Password string `json:"password" yaml:"password"`
|
|
}
|
|
|
|
type NetworkOption struct {
|
|
// EnableIPv6 enables ipv6 for server.
|
|
EnableIPv6 bool `mapstructure:"enableIPv6" yaml:"enableIPv6"`
|
|
}
|
|
|
|
type AnnouncerOption struct {
|
|
// SchedulerInterval is the interval of announcing scheduler.
|
|
SchedulerInterval time.Duration `mapstructure:"schedulerInterval" yaml:"schedulerInterval"`
|
|
}
|
|
|
|
type NetworkTopologyOption struct {
|
|
// Enable network topology service.
|
|
Enable bool `mapstructure:"enable" yaml:"enable"`
|
|
|
|
// Probe is the configuration of probe.
|
|
Probe ProbeOption `mapstructure:"probe" yaml:"probe"`
|
|
}
|
|
|
|
type ProbeOption struct {
|
|
// Interval is the interval of probing hosts.
|
|
Interval time.Duration `mapstructure:"interval" yaml:"interval"`
|
|
}
|