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