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 }