github.com/flavio/docker@v0.1.3-0.20170117145210-f63d1a6eec47/cli/command/swarm/opts.go (about) 1 package swarm 2 3 import ( 4 "encoding/csv" 5 "errors" 6 "fmt" 7 "strings" 8 "time" 9 10 "github.com/docker/docker/api/types/swarm" 11 "github.com/docker/docker/opts" 12 "github.com/spf13/pflag" 13 ) 14 15 const ( 16 defaultListenAddr = "0.0.0.0:2377" 17 18 flagCertExpiry = "cert-expiry" 19 flagDispatcherHeartbeat = "dispatcher-heartbeat" 20 flagListenAddr = "listen-addr" 21 flagAdvertiseAddr = "advertise-addr" 22 flagQuiet = "quiet" 23 flagRotate = "rotate" 24 flagToken = "token" 25 flagTaskHistoryLimit = "task-history-limit" 26 flagExternalCA = "external-ca" 27 flagMaxSnapshots = "max-snapshots" 28 flagSnapshotInterval = "snapshot-interval" 29 flagLockKey = "lock-key" 30 flagAutolock = "autolock" 31 flagAvailability = "availability" 32 ) 33 34 type swarmOptions struct { 35 taskHistoryLimit int64 36 dispatcherHeartbeat time.Duration 37 nodeCertExpiry time.Duration 38 externalCA ExternalCAOption 39 maxSnapshots uint64 40 snapshotInterval uint64 41 autolock bool 42 } 43 44 // NodeAddrOption is a pflag.Value for listening addresses 45 type NodeAddrOption struct { 46 addr string 47 } 48 49 // String prints the representation of this flag 50 func (a *NodeAddrOption) String() string { 51 return a.Value() 52 } 53 54 // Set the value for this flag 55 func (a *NodeAddrOption) Set(value string) error { 56 addr, err := opts.ParseTCPAddr(value, a.addr) 57 if err != nil { 58 return err 59 } 60 a.addr = addr 61 return nil 62 } 63 64 // Type returns the type of this flag 65 func (a *NodeAddrOption) Type() string { 66 return "node-addr" 67 } 68 69 // Value returns the value of this option as addr:port 70 func (a *NodeAddrOption) Value() string { 71 return strings.TrimPrefix(a.addr, "tcp://") 72 } 73 74 // NewNodeAddrOption returns a new node address option 75 func NewNodeAddrOption(addr string) NodeAddrOption { 76 return NodeAddrOption{addr} 77 } 78 79 // NewListenAddrOption returns a NodeAddrOption with default values 80 func NewListenAddrOption() NodeAddrOption { 81 return NewNodeAddrOption(defaultListenAddr) 82 } 83 84 // ExternalCAOption is a Value type for parsing external CA specifications. 85 type ExternalCAOption struct { 86 values []*swarm.ExternalCA 87 } 88 89 // Set parses an external CA option. 90 func (m *ExternalCAOption) Set(value string) error { 91 parsed, err := parseExternalCA(value) 92 if err != nil { 93 return err 94 } 95 96 m.values = append(m.values, parsed) 97 return nil 98 } 99 100 // Type returns the type of this option. 101 func (m *ExternalCAOption) Type() string { 102 return "external-ca" 103 } 104 105 // String returns a string repr of this option. 106 func (m *ExternalCAOption) String() string { 107 externalCAs := []string{} 108 for _, externalCA := range m.values { 109 repr := fmt.Sprintf("%s: %s", externalCA.Protocol, externalCA.URL) 110 externalCAs = append(externalCAs, repr) 111 } 112 return strings.Join(externalCAs, ", ") 113 } 114 115 // Value returns the external CAs 116 func (m *ExternalCAOption) Value() []*swarm.ExternalCA { 117 return m.values 118 } 119 120 // parseExternalCA parses an external CA specification from the command line, 121 // such as protocol=cfssl,url=https://example.com. 122 func parseExternalCA(caSpec string) (*swarm.ExternalCA, error) { 123 csvReader := csv.NewReader(strings.NewReader(caSpec)) 124 fields, err := csvReader.Read() 125 if err != nil { 126 return nil, err 127 } 128 129 externalCA := swarm.ExternalCA{ 130 Options: make(map[string]string), 131 } 132 133 var ( 134 hasProtocol bool 135 hasURL bool 136 ) 137 138 for _, field := range fields { 139 parts := strings.SplitN(field, "=", 2) 140 141 if len(parts) != 2 { 142 return nil, fmt.Errorf("invalid field '%s' must be a key=value pair", field) 143 } 144 145 key, value := parts[0], parts[1] 146 147 switch strings.ToLower(key) { 148 case "protocol": 149 hasProtocol = true 150 if strings.ToLower(value) == string(swarm.ExternalCAProtocolCFSSL) { 151 externalCA.Protocol = swarm.ExternalCAProtocolCFSSL 152 } else { 153 return nil, fmt.Errorf("unrecognized external CA protocol %s", value) 154 } 155 case "url": 156 hasURL = true 157 externalCA.URL = value 158 default: 159 externalCA.Options[key] = value 160 } 161 } 162 163 if !hasProtocol { 164 return nil, errors.New("the external-ca option needs a protocol= parameter") 165 } 166 if !hasURL { 167 return nil, errors.New("the external-ca option needs a url= parameter") 168 } 169 170 return &externalCA, nil 171 } 172 173 func addSwarmFlags(flags *pflag.FlagSet, opts *swarmOptions) { 174 flags.Int64Var(&opts.taskHistoryLimit, flagTaskHistoryLimit, 5, "Task history retention limit") 175 flags.DurationVar(&opts.dispatcherHeartbeat, flagDispatcherHeartbeat, time.Duration(5*time.Second), "Dispatcher heartbeat period (ns|us|ms|s|m|h)") 176 flags.DurationVar(&opts.nodeCertExpiry, flagCertExpiry, time.Duration(90*24*time.Hour), "Validity period for node certificates (ns|us|ms|s|m|h)") 177 flags.Var(&opts.externalCA, flagExternalCA, "Specifications of one or more certificate signing endpoints") 178 flags.Uint64Var(&opts.maxSnapshots, flagMaxSnapshots, 0, "Number of additional Raft snapshots to retain") 179 flags.Uint64Var(&opts.snapshotInterval, flagSnapshotInterval, 10000, "Number of log entries between Raft snapshots") 180 } 181 182 func (opts *swarmOptions) mergeSwarmSpec(spec *swarm.Spec, flags *pflag.FlagSet) { 183 if flags.Changed(flagTaskHistoryLimit) { 184 spec.Orchestration.TaskHistoryRetentionLimit = &opts.taskHistoryLimit 185 } 186 if flags.Changed(flagDispatcherHeartbeat) { 187 spec.Dispatcher.HeartbeatPeriod = opts.dispatcherHeartbeat 188 } 189 if flags.Changed(flagCertExpiry) { 190 spec.CAConfig.NodeCertExpiry = opts.nodeCertExpiry 191 } 192 if flags.Changed(flagExternalCA) { 193 spec.CAConfig.ExternalCAs = opts.externalCA.Value() 194 } 195 if flags.Changed(flagMaxSnapshots) { 196 spec.Raft.KeepOldSnapshots = &opts.maxSnapshots 197 } 198 if flags.Changed(flagSnapshotInterval) { 199 spec.Raft.SnapshotInterval = opts.snapshotInterval 200 } 201 if flags.Changed(flagAutolock) { 202 spec.EncryptionConfig.AutoLockManagers = opts.autolock 203 } 204 } 205 206 func (opts *swarmOptions) ToSpec(flags *pflag.FlagSet) swarm.Spec { 207 var spec swarm.Spec 208 opts.mergeSwarmSpec(&spec, flags) 209 return spec 210 }