1/* 2Copyright 2018 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package flag 18 19import ( 20 "fmt" 21 "net" 22 "sort" 23 "strconv" 24 "strings" 25 26 "github.com/spf13/pflag" 27 28 v1 "k8s.io/api/core/v1" 29 "k8s.io/apimachinery/pkg/api/resource" 30 utilnet "k8s.io/apimachinery/pkg/util/net" 31 corev1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper" 32 kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config" 33 utilsnet "k8s.io/utils/net" 34) 35 36// TODO(mikedanese): remove these flag wrapper types when we remove command line flags 37 38var ( 39 _ pflag.Value = &IPVar{} 40 _ pflag.Value = &IPPortVar{} 41 _ pflag.Value = &PortRangeVar{} 42 _ pflag.Value = &ReservedMemoryVar{} 43) 44 45// IPVar is used for validating a command line option that represents an IP. It implements the pflag.Value interface 46type IPVar struct { 47 Val *string 48} 49 50// Set sets the flag value 51func (v IPVar) Set(s string) error { 52 if len(s) == 0 { 53 v.Val = nil 54 return nil 55 } 56 if net.ParseIP(s) == nil { 57 return fmt.Errorf("%q is not a valid IP address", s) 58 } 59 if v.Val == nil { 60 // it's okay to panic here since this is programmer error 61 panic("the string pointer passed into IPVar should not be nil") 62 } 63 *v.Val = s 64 return nil 65} 66 67// String returns the flag value 68func (v IPVar) String() string { 69 if v.Val == nil { 70 return "" 71 } 72 return *v.Val 73} 74 75// Type gets the flag type 76func (v IPVar) Type() string { 77 return "ip" 78} 79 80// IPPortVar is used for validating a command line option that represents an IP and a port. It implements the pflag.Value interface 81type IPPortVar struct { 82 Val *string 83} 84 85// Set sets the flag value 86func (v IPPortVar) Set(s string) error { 87 if len(s) == 0 { 88 v.Val = nil 89 return nil 90 } 91 92 if v.Val == nil { 93 // it's okay to panic here since this is programmer error 94 panic("the string pointer passed into IPPortVar should not be nil") 95 } 96 97 // Both IP and IP:port are valid. 98 // Attempt to parse into IP first. 99 if net.ParseIP(s) != nil { 100 *v.Val = s 101 return nil 102 } 103 104 // Can not parse into IP, now assume IP:port. 105 host, port, err := net.SplitHostPort(s) 106 if err != nil { 107 return fmt.Errorf("%q is not in a valid format (ip or ip:port): %v", s, err) 108 } 109 if net.ParseIP(host) == nil { 110 return fmt.Errorf("%q is not a valid IP address", host) 111 } 112 if _, err := utilsnet.ParsePort(port, true); err != nil { 113 return fmt.Errorf("%q is not a valid number", port) 114 } 115 *v.Val = s 116 return nil 117} 118 119// String returns the flag value 120func (v IPPortVar) String() string { 121 if v.Val == nil { 122 return "" 123 } 124 return *v.Val 125} 126 127// Type gets the flag type 128func (v IPPortVar) Type() string { 129 return "ipport" 130} 131 132// PortRangeVar is used for validating a command line option that represents a port range. It implements the pflag.Value interface 133type PortRangeVar struct { 134 Val *string 135} 136 137// Set sets the flag value 138func (v PortRangeVar) Set(s string) error { 139 if _, err := utilnet.ParsePortRange(s); err != nil { 140 return fmt.Errorf("%q is not a valid port range: %v", s, err) 141 } 142 if v.Val == nil { 143 // it's okay to panic here since this is programmer error 144 panic("the string pointer passed into PortRangeVar should not be nil") 145 } 146 *v.Val = s 147 return nil 148} 149 150// String returns the flag value 151func (v PortRangeVar) String() string { 152 if v.Val == nil { 153 return "" 154 } 155 return *v.Val 156} 157 158// Type gets the flag type 159func (v PortRangeVar) Type() string { 160 return "port-range" 161} 162 163// ReservedMemoryVar is used for validating a command line option that represents a reserved memory. It implements the pflag.Value interface 164type ReservedMemoryVar struct { 165 Value *[]kubeletconfig.MemoryReservation 166 initialized bool // set to true after the first Set call 167} 168 169// Set sets the flag value 170func (v *ReservedMemoryVar) Set(s string) error { 171 if v.Value == nil { 172 return fmt.Errorf("no target (nil pointer to *[]MemoryReservation") 173 } 174 175 if s == "" { 176 v.Value = nil 177 return nil 178 } 179 180 if !v.initialized || *v.Value == nil { 181 *v.Value = make([]kubeletconfig.MemoryReservation, 0) 182 v.initialized = true 183 } 184 185 if s == "" { 186 return nil 187 } 188 189 numaNodeReservation := strings.Split(s, ":") 190 if len(numaNodeReservation) != 2 { 191 return fmt.Errorf("the reserved memory has incorrect format, expected numaNodeID:type=quantity[,type=quantity...], got %s", s) 192 } 193 194 memoryTypeReservations := strings.Split(numaNodeReservation[1], ",") 195 if len(memoryTypeReservations) < 1 { 196 return fmt.Errorf("the reserved memory has incorrect format, expected numaNodeID:type=quantity[,type=quantity...], got %s", s) 197 } 198 199 numaNodeID, err := strconv.Atoi(numaNodeReservation[0]) 200 if err != nil { 201 return fmt.Errorf("failed to convert the NUMA node ID, exptected integer, got %s", numaNodeReservation[0]) 202 } 203 204 memoryReservation := kubeletconfig.MemoryReservation{ 205 NumaNode: int32(numaNodeID), 206 Limits: map[v1.ResourceName]resource.Quantity{}, 207 } 208 209 for _, reservation := range memoryTypeReservations { 210 limit := strings.Split(reservation, "=") 211 if len(limit) != 2 { 212 return fmt.Errorf("the reserved limit has incorrect value, expected type=quantatity, got %s", reservation) 213 } 214 215 resourceName := v1.ResourceName(limit[0]) 216 if resourceName != v1.ResourceMemory && !corev1helper.IsHugePageResourceName(resourceName) { 217 return fmt.Errorf("memory type conversion error, unknown type: %q", resourceName) 218 } 219 220 q, err := resource.ParseQuantity(limit[1]) 221 if err != nil { 222 return fmt.Errorf("failed to parse the quantatity, expected quantatity, got %s", limit[1]) 223 } 224 225 memoryReservation.Limits[v1.ResourceName(limit[0])] = q 226 } 227 228 *v.Value = append(*v.Value, memoryReservation) 229 230 return nil 231} 232 233// String returns the flag value 234func (v *ReservedMemoryVar) String() string { 235 if v == nil || v.Value == nil { 236 return "" 237 } 238 239 var slices []string 240 for _, reservedMemory := range *v.Value { 241 var limits []string 242 for resourceName, q := range reservedMemory.Limits { 243 limits = append(limits, fmt.Sprintf("%s=%s", resourceName, q.String())) 244 } 245 246 sort.Strings(limits) 247 slices = append(slices, fmt.Sprintf("%d:%s", reservedMemory.NumaNode, strings.Join(limits, ","))) 248 } 249 250 sort.Strings(slices) 251 return strings.Join(slices, ",") 252} 253 254// Type gets the flag type 255func (v *ReservedMemoryVar) Type() string { 256 return "reserved-memory" 257} 258