github.com/olljanat/moby@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  }