github.com/khulnasoft/cli@v0.0.0-20240402070845-01bcad7beefa/opts/network.go (about)

     1  package opts
     2  
     3  import (
     4  	"encoding/csv"
     5  	"fmt"
     6  	"regexp"
     7  	"strings"
     8  )
     9  
    10  const (
    11  	networkOptName        = "name"
    12  	networkOptAlias       = "alias"
    13  	networkOptIPv4Address = "ip"
    14  	networkOptIPv6Address = "ip6"
    15  	networkOptMacAddress  = "mac-address"
    16  	networkOptLinkLocalIP = "link-local-ip"
    17  	driverOpt             = "driver-opt"
    18  )
    19  
    20  // NetworkAttachmentOpts represents the network options for endpoint creation
    21  type NetworkAttachmentOpts struct {
    22  	Target       string
    23  	Aliases      []string
    24  	DriverOpts   map[string]string
    25  	Links        []string // TODO add support for links in the csv notation of `--network`
    26  	IPv4Address  string
    27  	IPv6Address  string
    28  	LinkLocalIPs []string
    29  	MacAddress   string
    30  }
    31  
    32  // NetworkOpt represents a network config in swarm mode.
    33  type NetworkOpt struct {
    34  	options []NetworkAttachmentOpts
    35  }
    36  
    37  // Set networkopts value
    38  func (n *NetworkOpt) Set(value string) error { //nolint:gocyclo
    39  	longSyntax, err := regexp.MatchString(`\w+=\w+(,\w+=\w+)*`, value)
    40  	if err != nil {
    41  		return err
    42  	}
    43  
    44  	var netOpt NetworkAttachmentOpts
    45  	if longSyntax {
    46  		csvReader := csv.NewReader(strings.NewReader(value))
    47  		fields, err := csvReader.Read()
    48  		if err != nil {
    49  			return err
    50  		}
    51  
    52  		netOpt.Aliases = []string{}
    53  		for _, field := range fields {
    54  			// TODO(thaJeztah): these options should not be case-insensitive.
    55  			key, val, ok := strings.Cut(strings.ToLower(field), "=")
    56  			if !ok || key == "" {
    57  				return fmt.Errorf("invalid field %s", field)
    58  			}
    59  
    60  			key = strings.TrimSpace(key)
    61  			val = strings.TrimSpace(val)
    62  
    63  			switch key {
    64  			case networkOptName:
    65  				netOpt.Target = val
    66  			case networkOptAlias:
    67  				netOpt.Aliases = append(netOpt.Aliases, val)
    68  			case networkOptIPv4Address:
    69  				netOpt.IPv4Address = val
    70  			case networkOptIPv6Address:
    71  				netOpt.IPv6Address = val
    72  			case networkOptMacAddress:
    73  				netOpt.MacAddress = val
    74  			case networkOptLinkLocalIP:
    75  				netOpt.LinkLocalIPs = append(netOpt.LinkLocalIPs, val)
    76  			case driverOpt:
    77  				key, val, err = parseDriverOpt(val)
    78  				if err != nil {
    79  					return err
    80  				}
    81  				if netOpt.DriverOpts == nil {
    82  					netOpt.DriverOpts = make(map[string]string)
    83  				}
    84  				netOpt.DriverOpts[key] = val
    85  			default:
    86  				return fmt.Errorf("invalid field key %s", key)
    87  			}
    88  		}
    89  		if len(netOpt.Target) == 0 {
    90  			return fmt.Errorf("network name/id is not specified")
    91  		}
    92  	} else {
    93  		netOpt.Target = value
    94  	}
    95  	n.options = append(n.options, netOpt)
    96  	return nil
    97  }
    98  
    99  // Type returns the type of this option
   100  func (n *NetworkOpt) Type() string {
   101  	return "network"
   102  }
   103  
   104  // Value returns the networkopts
   105  func (n *NetworkOpt) Value() []NetworkAttachmentOpts {
   106  	return n.options
   107  }
   108  
   109  // String returns the network opts as a string
   110  func (n *NetworkOpt) String() string {
   111  	return ""
   112  }
   113  
   114  // NetworkMode return the network mode for the network option
   115  func (n *NetworkOpt) NetworkMode() string {
   116  	networkIDOrName := "default"
   117  	netOptVal := n.Value()
   118  	if len(netOptVal) > 0 {
   119  		networkIDOrName = netOptVal[0].Target
   120  	}
   121  	return networkIDOrName
   122  }
   123  
   124  func parseDriverOpt(driverOpt string) (string, string, error) {
   125  	// TODO(thaJeztah): these options should not be case-insensitive.
   126  	// TODO(thaJeztah): should value be converted to lowercase as well, or only the key?
   127  	key, value, ok := strings.Cut(strings.ToLower(driverOpt), "=")
   128  	if !ok || key == "" {
   129  		return "", "", fmt.Errorf("invalid key value pair format in driver options")
   130  	}
   131  	key = strings.TrimSpace(key)
   132  	value = strings.TrimSpace(value)
   133  	return key, value, nil
   134  }