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