2016-09-08 13:11:39 -04:00
package swarm
import (
"encoding/csv"
"errors"
"fmt"
"strings"
"time"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/opts"
"github.com/spf13/pflag"
)
const (
defaultListenAddr = "0.0.0.0:2377"
flagCertExpiry = "cert-expiry"
flagDispatcherHeartbeat = "dispatcher-heartbeat"
flagListenAddr = "listen-addr"
flagAdvertiseAddr = "advertise-addr"
flagQuiet = "quiet"
flagRotate = "rotate"
flagToken = "token"
flagTaskHistoryLimit = "task-history-limit"
flagExternalCA = "external-ca"
2016-11-02 15:29:51 -04:00
flagMaxSnapshots = "max-snapshots"
flagSnapshotInterval = "snapshot-interval"
2016-10-21 21:07:55 -04:00
flagLockKey = "lock-key"
2016-10-27 21:50:49 -04:00
flagAutolock = "autolock"
2016-09-08 13:11:39 -04:00
)
type swarmOptions struct {
taskHistoryLimit int64
dispatcherHeartbeat time . Duration
nodeCertExpiry time . Duration
externalCA ExternalCAOption
2016-11-02 15:29:51 -04:00
maxSnapshots uint64
snapshotInterval uint64
2016-10-27 21:50:49 -04:00
autolock bool
2016-09-08 13:11:39 -04:00
}
2016-11-01 15:11:38 -04:00
// NodeAddrOption is a pflag.Value for listening addresses
2016-09-08 13:11:39 -04:00
type NodeAddrOption struct {
addr string
}
// String prints the representation of this flag
func ( a * NodeAddrOption ) String ( ) string {
return a . Value ( )
}
// Set the value for this flag
func ( a * NodeAddrOption ) Set ( value string ) error {
addr , err := opts . ParseTCPAddr ( value , a . addr )
if err != nil {
return err
}
a . addr = addr
return nil
}
// Type returns the type of this flag
func ( a * NodeAddrOption ) Type ( ) string {
return "node-addr"
}
// Value returns the value of this option as addr:port
func ( a * NodeAddrOption ) Value ( ) string {
return strings . TrimPrefix ( a . addr , "tcp://" )
}
// NewNodeAddrOption returns a new node address option
func NewNodeAddrOption ( addr string ) NodeAddrOption {
return NodeAddrOption { addr }
}
// NewListenAddrOption returns a NodeAddrOption with default values
func NewListenAddrOption ( ) NodeAddrOption {
return NewNodeAddrOption ( defaultListenAddr )
}
// ExternalCAOption is a Value type for parsing external CA specifications.
type ExternalCAOption struct {
values [ ] * swarm . ExternalCA
}
// Set parses an external CA option.
func ( m * ExternalCAOption ) Set ( value string ) error {
parsed , err := parseExternalCA ( value )
if err != nil {
return err
}
m . values = append ( m . values , parsed )
return nil
}
// Type returns the type of this option.
func ( m * ExternalCAOption ) Type ( ) string {
return "external-ca"
}
// String returns a string repr of this option.
func ( m * ExternalCAOption ) String ( ) string {
externalCAs := [ ] string { }
for _ , externalCA := range m . values {
repr := fmt . Sprintf ( "%s: %s" , externalCA . Protocol , externalCA . URL )
externalCAs = append ( externalCAs , repr )
}
return strings . Join ( externalCAs , ", " )
}
// Value returns the external CAs
func ( m * ExternalCAOption ) Value ( ) [ ] * swarm . ExternalCA {
return m . values
}
// parseExternalCA parses an external CA specification from the command line,
// such as protocol=cfssl,url=https://example.com.
func parseExternalCA ( caSpec string ) ( * swarm . ExternalCA , error ) {
csvReader := csv . NewReader ( strings . NewReader ( caSpec ) )
fields , err := csvReader . Read ( )
if err != nil {
return nil , err
}
externalCA := swarm . ExternalCA {
Options : make ( map [ string ] string ) ,
}
var (
hasProtocol bool
hasURL bool
)
for _ , field := range fields {
parts := strings . SplitN ( field , "=" , 2 )
if len ( parts ) != 2 {
return nil , fmt . Errorf ( "invalid field '%s' must be a key=value pair" , field )
}
key , value := parts [ 0 ] , parts [ 1 ]
switch strings . ToLower ( key ) {
case "protocol" :
hasProtocol = true
if strings . ToLower ( value ) == string ( swarm . ExternalCAProtocolCFSSL ) {
externalCA . Protocol = swarm . ExternalCAProtocolCFSSL
} else {
return nil , fmt . Errorf ( "unrecognized external CA protocol %s" , value )
}
case "url" :
hasURL = true
externalCA . URL = value
default :
externalCA . Options [ key ] = value
}
}
if ! hasProtocol {
return nil , errors . New ( "the external-ca option needs a protocol= parameter" )
}
if ! hasURL {
return nil , errors . New ( "the external-ca option needs a url= parameter" )
}
return & externalCA , nil
}
func addSwarmFlags ( flags * pflag . FlagSet , opts * swarmOptions ) {
flags . Int64Var ( & opts . taskHistoryLimit , flagTaskHistoryLimit , 5 , "Task history retention limit" )
2016-10-25 08:22:07 -04:00
flags . DurationVar ( & opts . dispatcherHeartbeat , flagDispatcherHeartbeat , time . Duration ( 5 * time . Second ) , "Dispatcher heartbeat period (ns|us|ms|s|m|h) (default 5s)" )
flags . DurationVar ( & opts . nodeCertExpiry , flagCertExpiry , time . Duration ( 90 * 24 * time . Hour ) , "Validity period for node certificates (ns|us|ms|s|m|h) (default 2160h0m0s)" )
2016-09-08 13:11:39 -04:00
flags . Var ( & opts . externalCA , flagExternalCA , "Specifications of one or more certificate signing endpoints" )
2016-11-02 15:29:51 -04:00
flags . Uint64Var ( & opts . maxSnapshots , flagMaxSnapshots , 0 , "Number of additional Raft snapshots to retain" )
flags . Uint64Var ( & opts . snapshotInterval , flagSnapshotInterval , 10000 , "Number of log entries between Raft snapshots" )
2016-09-08 13:11:39 -04:00
}
2016-11-02 15:29:51 -04:00
func ( opts * swarmOptions ) mergeSwarmSpec ( spec * swarm . Spec , flags * pflag . FlagSet ) {
2016-08-26 00:08:53 -04:00
if flags . Changed ( flagTaskHistoryLimit ) {
spec . Orchestration . TaskHistoryRetentionLimit = & opts . taskHistoryLimit
}
if flags . Changed ( flagDispatcherHeartbeat ) {
spec . Dispatcher . HeartbeatPeriod = opts . dispatcherHeartbeat
}
if flags . Changed ( flagCertExpiry ) {
spec . CAConfig . NodeCertExpiry = opts . nodeCertExpiry
}
if flags . Changed ( flagExternalCA ) {
spec . CAConfig . ExternalCAs = opts . externalCA . Value ( )
}
2016-11-02 15:29:51 -04:00
if flags . Changed ( flagMaxSnapshots ) {
spec . Raft . KeepOldSnapshots = & opts . maxSnapshots
}
if flags . Changed ( flagSnapshotInterval ) {
spec . Raft . SnapshotInterval = opts . snapshotInterval
}
2016-10-27 21:50:49 -04:00
if flags . Changed ( flagAutolock ) {
spec . EncryptionConfig . AutoLockManagers = opts . autolock
}
2016-11-02 15:29:51 -04:00
}
func ( opts * swarmOptions ) ToSpec ( flags * pflag . FlagSet ) swarm . Spec {
var spec swarm . Spec
opts . mergeSwarmSpec ( & spec , flags )
2016-09-08 13:11:39 -04:00
return spec
}