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  }