forked from toolshed/abra
		
	
		
			
				
	
	
		
			148 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			148 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package opts
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/csv"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"regexp"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	networkOptName        = "name"
 | 
						|
	networkOptAlias       = "alias"
 | 
						|
	networkOptIPv4Address = "ip"
 | 
						|
	networkOptIPv6Address = "ip6"
 | 
						|
	networkOptMacAddress  = "mac-address"
 | 
						|
	networkOptLinkLocalIP = "link-local-ip"
 | 
						|
	driverOpt             = "driver-opt"
 | 
						|
	gwPriorityOpt         = "gw-priority"
 | 
						|
)
 | 
						|
 | 
						|
// NetworkAttachmentOpts represents the network options for endpoint creation
 | 
						|
type NetworkAttachmentOpts struct {
 | 
						|
	Target       string
 | 
						|
	Aliases      []string
 | 
						|
	DriverOpts   map[string]string
 | 
						|
	Links        []string // TODO add support for links in the csv notation of `--network`
 | 
						|
	IPv4Address  string
 | 
						|
	IPv6Address  string
 | 
						|
	LinkLocalIPs []string
 | 
						|
	MacAddress   string
 | 
						|
	GwPriority   int
 | 
						|
}
 | 
						|
 | 
						|
// NetworkOpt represents a network config in swarm mode.
 | 
						|
type NetworkOpt struct {
 | 
						|
	options []NetworkAttachmentOpts
 | 
						|
}
 | 
						|
 | 
						|
// Set networkopts value
 | 
						|
func (n *NetworkOpt) Set(value string) error { //nolint:gocyclo
 | 
						|
	longSyntax, err := regexp.MatchString(`\w+=\w+(,\w+=\w+)*`, value)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	var netOpt NetworkAttachmentOpts
 | 
						|
	if longSyntax {
 | 
						|
		csvReader := csv.NewReader(strings.NewReader(value))
 | 
						|
		fields, err := csvReader.Read()
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		netOpt.Aliases = []string{}
 | 
						|
		for _, field := range fields {
 | 
						|
			// TODO(thaJeztah): these options should not be case-insensitive.
 | 
						|
			key, val, ok := strings.Cut(strings.ToLower(field), "=")
 | 
						|
			if !ok || key == "" {
 | 
						|
				return fmt.Errorf("invalid field %s", field)
 | 
						|
			}
 | 
						|
 | 
						|
			key = strings.TrimSpace(key)
 | 
						|
			val = strings.TrimSpace(val)
 | 
						|
 | 
						|
			switch key {
 | 
						|
			case networkOptName:
 | 
						|
				netOpt.Target = val
 | 
						|
			case networkOptAlias:
 | 
						|
				netOpt.Aliases = append(netOpt.Aliases, val)
 | 
						|
			case networkOptIPv4Address:
 | 
						|
				netOpt.IPv4Address = val
 | 
						|
			case networkOptIPv6Address:
 | 
						|
				netOpt.IPv6Address = val
 | 
						|
			case networkOptMacAddress:
 | 
						|
				netOpt.MacAddress = val
 | 
						|
			case networkOptLinkLocalIP:
 | 
						|
				netOpt.LinkLocalIPs = append(netOpt.LinkLocalIPs, val)
 | 
						|
			case driverOpt:
 | 
						|
				key, val, err = parseDriverOpt(val)
 | 
						|
				if err != nil {
 | 
						|
					return err
 | 
						|
				}
 | 
						|
				if netOpt.DriverOpts == nil {
 | 
						|
					netOpt.DriverOpts = make(map[string]string)
 | 
						|
				}
 | 
						|
				netOpt.DriverOpts[key] = val
 | 
						|
			case gwPriorityOpt:
 | 
						|
				netOpt.GwPriority, err = strconv.Atoi(val)
 | 
						|
				if err != nil {
 | 
						|
					var numErr *strconv.NumError
 | 
						|
					if errors.As(err, &numErr) {
 | 
						|
						err = numErr.Err
 | 
						|
					}
 | 
						|
					return fmt.Errorf("invalid gw-priority (%s): %w", val, err)
 | 
						|
				}
 | 
						|
			default:
 | 
						|
				return errors.New("invalid field key " + key)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if len(netOpt.Target) == 0 {
 | 
						|
			return errors.New("network name/id is not specified")
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		netOpt.Target = value
 | 
						|
	}
 | 
						|
	n.options = append(n.options, netOpt)
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// Type returns the type of this option
 | 
						|
func (*NetworkOpt) Type() string {
 | 
						|
	return "network"
 | 
						|
}
 | 
						|
 | 
						|
// Value returns the networkopts
 | 
						|
func (n *NetworkOpt) Value() []NetworkAttachmentOpts {
 | 
						|
	return n.options
 | 
						|
}
 | 
						|
 | 
						|
// String returns the network opts as a string
 | 
						|
func (*NetworkOpt) String() string {
 | 
						|
	return ""
 | 
						|
}
 | 
						|
 | 
						|
// NetworkMode return the network mode for the network option
 | 
						|
func (n *NetworkOpt) NetworkMode() string {
 | 
						|
	networkIDOrName := "default"
 | 
						|
	netOptVal := n.Value()
 | 
						|
	if len(netOptVal) > 0 {
 | 
						|
		networkIDOrName = netOptVal[0].Target
 | 
						|
	}
 | 
						|
	return networkIDOrName
 | 
						|
}
 | 
						|
 | 
						|
func parseDriverOpt(driverOpt string) (string, string, error) {
 | 
						|
	// TODO(thaJeztah): these options should not be case-insensitive.
 | 
						|
	// TODO(thaJeztah): should value be converted to lowercase as well, or only the key?
 | 
						|
	key, value, ok := strings.Cut(strings.ToLower(driverOpt), "=")
 | 
						|
	if !ok || key == "" {
 | 
						|
		return "", "", errors.New("invalid key value pair format in driver options")
 | 
						|
	}
 | 
						|
	key = strings.TrimSpace(key)
 | 
						|
	value = strings.TrimSpace(value)
 | 
						|
	return key, value, nil
 | 
						|
}
 |