mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Add a dummy implementation of proxyutil.LineBuffer
Rather than actually assembling all of the rules we aren't going to use, just count them and throw them away.
This commit is contained in:
		@@ -288,10 +288,10 @@ func NewProxier(ipFamily v1.IPFamily,
 | 
				
			|||||||
		precomputedProbabilities: make([]string, 0, 1001),
 | 
							precomputedProbabilities: make([]string, 0, 1001),
 | 
				
			||||||
		iptablesData:             bytes.NewBuffer(nil),
 | 
							iptablesData:             bytes.NewBuffer(nil),
 | 
				
			||||||
		existingFilterChainsData: bytes.NewBuffer(nil),
 | 
							existingFilterChainsData: bytes.NewBuffer(nil),
 | 
				
			||||||
		filterChains:             proxyutil.LineBuffer{},
 | 
							filterChains:             proxyutil.NewLineBuffer(),
 | 
				
			||||||
		filterRules:              proxyutil.LineBuffer{},
 | 
							filterRules:              proxyutil.NewLineBuffer(),
 | 
				
			||||||
		natChains:                proxyutil.LineBuffer{},
 | 
							natChains:                proxyutil.NewLineBuffer(),
 | 
				
			||||||
		natRules:                 proxyutil.LineBuffer{},
 | 
							natRules:                 proxyutil.NewLineBuffer(),
 | 
				
			||||||
		localhostNodePorts:       localhostNodePorts,
 | 
							localhostNodePorts:       localhostNodePorts,
 | 
				
			||||||
		nodePortAddresses:        nodePortAddresses,
 | 
							nodePortAddresses:        nodePortAddresses,
 | 
				
			||||||
		networkInterfacer:        proxyutil.RealNetwork{},
 | 
							networkInterfacer:        proxyutil.RealNetwork{},
 | 
				
			||||||
@@ -411,8 +411,8 @@ func CleanupLeftovers(ipt utiliptables.Interface) (encounteredError bool) {
 | 
				
			|||||||
		encounteredError = true
 | 
							encounteredError = true
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		existingNATChains := utiliptables.GetChainsFromTable(iptablesData.Bytes())
 | 
							existingNATChains := utiliptables.GetChainsFromTable(iptablesData.Bytes())
 | 
				
			||||||
		natChains := &proxyutil.LineBuffer{}
 | 
							natChains := proxyutil.NewLineBuffer()
 | 
				
			||||||
		natRules := &proxyutil.LineBuffer{}
 | 
							natRules := proxyutil.NewLineBuffer()
 | 
				
			||||||
		natChains.Write("*nat")
 | 
							natChains.Write("*nat")
 | 
				
			||||||
		// Start with chains we know we need to remove.
 | 
							// Start with chains we know we need to remove.
 | 
				
			||||||
		for _, chain := range []utiliptables.Chain{kubeServicesChain, kubeNodePortsChain, kubePostroutingChain} {
 | 
							for _, chain := range []utiliptables.Chain{kubeServicesChain, kubeNodePortsChain, kubePostroutingChain} {
 | 
				
			||||||
@@ -448,8 +448,8 @@ func CleanupLeftovers(ipt utiliptables.Interface) (encounteredError bool) {
 | 
				
			|||||||
		encounteredError = true
 | 
							encounteredError = true
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		existingFilterChains := utiliptables.GetChainsFromTable(iptablesData.Bytes())
 | 
							existingFilterChains := utiliptables.GetChainsFromTable(iptablesData.Bytes())
 | 
				
			||||||
		filterChains := &proxyutil.LineBuffer{}
 | 
							filterChains := proxyutil.NewLineBuffer()
 | 
				
			||||||
		filterRules := &proxyutil.LineBuffer{}
 | 
							filterRules := proxyutil.NewLineBuffer()
 | 
				
			||||||
		filterChains.Write("*filter")
 | 
							filterChains.Write("*filter")
 | 
				
			||||||
		for _, chain := range []utiliptables.Chain{kubeServicesChain, kubeExternalServicesChain, kubeForwardChain, kubeNodePortsChain} {
 | 
							for _, chain := range []utiliptables.Chain{kubeServicesChain, kubeExternalServicesChain, kubeForwardChain, kubeNodePortsChain} {
 | 
				
			||||||
			if _, found := existingFilterChains[chain]; found {
 | 
								if _, found := existingFilterChains[chain]; found {
 | 
				
			||||||
@@ -852,8 +852,8 @@ func (proxier *Proxier) syncProxyRules() {
 | 
				
			|||||||
	proxier.natChains.Reset()
 | 
						proxier.natChains.Reset()
 | 
				
			||||||
	proxier.natRules.Reset()
 | 
						proxier.natRules.Reset()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	skippedNatChains := &proxyutil.LineBuffer{}
 | 
						skippedNatChains := proxyutil.NewDiscardLineBuffer()
 | 
				
			||||||
	skippedNatRules := &proxyutil.LineBuffer{}
 | 
						skippedNatRules := proxyutil.NewDiscardLineBuffer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Write chain lines for all the "top-level" chains we'll be filling in
 | 
						// Write chain lines for all the "top-level" chains we'll be filling in
 | 
				
			||||||
	for _, chainName := range []utiliptables.Chain{kubeServicesChain, kubeExternalServicesChain, kubeForwardChain, kubeNodePortsChain, kubeProxyFirewallChain} {
 | 
						for _, chainName := range []utiliptables.Chain{kubeServicesChain, kubeExternalServicesChain, kubeForwardChain, kubeNodePortsChain, kubeProxyFirewallChain} {
 | 
				
			||||||
@@ -1069,9 +1069,9 @@ func (proxier *Proxier) syncProxyRules() {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		filterRules := &proxier.filterRules
 | 
							filterRules := proxier.filterRules
 | 
				
			||||||
		natChains := &proxier.natChains
 | 
							natChains := proxier.natChains
 | 
				
			||||||
		natRules := &proxier.natRules
 | 
							natRules := proxier.natRules
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Capture the clusterIP.
 | 
							// Capture the clusterIP.
 | 
				
			||||||
		if hasInternalEndpoints {
 | 
							if hasInternalEndpoints {
 | 
				
			||||||
@@ -1562,7 +1562,7 @@ func (proxier *Proxier) syncProxyRules() {
 | 
				
			|||||||
	conntrack.CleanStaleEntries(proxier.iptables.IsIPv6(), proxier.exec, proxier.svcPortMap, serviceUpdateResult, endpointUpdateResult)
 | 
						conntrack.CleanStaleEntries(proxier.iptables.IsIPv6(), proxier.exec, proxier.svcPortMap, serviceUpdateResult, endpointUpdateResult)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (proxier *Proxier) writeServiceToEndpointRules(natRules *proxyutil.LineBuffer, svcPortNameString string, svcInfo proxy.ServicePort, svcChain utiliptables.Chain, endpoints []proxy.Endpoint, args []string) {
 | 
					func (proxier *Proxier) writeServiceToEndpointRules(natRules proxyutil.LineBuffer, svcPortNameString string, svcInfo proxy.ServicePort, svcChain utiliptables.Chain, endpoints []proxy.Endpoint, args []string) {
 | 
				
			||||||
	// First write session affinity rules, if applicable.
 | 
						// First write session affinity rules, if applicable.
 | 
				
			||||||
	if svcInfo.SessionAffinityType() == v1.ServiceAffinityClientIP {
 | 
						if svcInfo.SessionAffinityType() == v1.ServiceAffinityClientIP {
 | 
				
			||||||
		for _, ep := range endpoints {
 | 
							for _, ep := range endpoints {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -324,10 +324,10 @@ func NewFakeProxier(ipt utiliptables.Interface) *Proxier {
 | 
				
			|||||||
		precomputedProbabilities: make([]string, 0, 1001),
 | 
							precomputedProbabilities: make([]string, 0, 1001),
 | 
				
			||||||
		iptablesData:             bytes.NewBuffer(nil),
 | 
							iptablesData:             bytes.NewBuffer(nil),
 | 
				
			||||||
		existingFilterChainsData: bytes.NewBuffer(nil),
 | 
							existingFilterChainsData: bytes.NewBuffer(nil),
 | 
				
			||||||
		filterChains:             proxyutil.LineBuffer{},
 | 
							filterChains:             proxyutil.NewLineBuffer(),
 | 
				
			||||||
		filterRules:              proxyutil.LineBuffer{},
 | 
							filterRules:              proxyutil.NewLineBuffer(),
 | 
				
			||||||
		natChains:                proxyutil.LineBuffer{},
 | 
							natChains:                proxyutil.NewLineBuffer(),
 | 
				
			||||||
		natRules:                 proxyutil.LineBuffer{},
 | 
							natRules:                 proxyutil.NewLineBuffer(),
 | 
				
			||||||
		nodeIP:                   netutils.ParseIPSloppy(testNodeIP),
 | 
							nodeIP:                   netutils.ParseIPSloppy(testNodeIP),
 | 
				
			||||||
		localhostNodePorts:       true,
 | 
							localhostNodePorts:       true,
 | 
				
			||||||
		nodePortAddresses:        proxyutil.NewNodePortAddresses(ipfamily, nil),
 | 
							nodePortAddresses:        proxyutil.NewNodePortAddresses(ipfamily, nil),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -440,10 +440,10 @@ func NewProxier(ipFamily v1.IPFamily,
 | 
				
			|||||||
		ipvsScheduler:         scheduler,
 | 
							ipvsScheduler:         scheduler,
 | 
				
			||||||
		iptablesData:          bytes.NewBuffer(nil),
 | 
							iptablesData:          bytes.NewBuffer(nil),
 | 
				
			||||||
		filterChainsData:      bytes.NewBuffer(nil),
 | 
							filterChainsData:      bytes.NewBuffer(nil),
 | 
				
			||||||
		natChains:             proxyutil.LineBuffer{},
 | 
							natChains:             proxyutil.NewLineBuffer(),
 | 
				
			||||||
		natRules:              proxyutil.LineBuffer{},
 | 
							natRules:              proxyutil.NewLineBuffer(),
 | 
				
			||||||
		filterChains:          proxyutil.LineBuffer{},
 | 
							filterChains:          proxyutil.NewLineBuffer(),
 | 
				
			||||||
		filterRules:           proxyutil.LineBuffer{},
 | 
							filterRules:           proxyutil.NewLineBuffer(),
 | 
				
			||||||
		netlinkHandle:         NewNetLinkHandle(ipFamily == v1.IPv6Protocol),
 | 
							netlinkHandle:         NewNetLinkHandle(ipFamily == v1.IPv6Protocol),
 | 
				
			||||||
		ipset:                 ipset,
 | 
							ipset:                 ipset,
 | 
				
			||||||
		nodePortAddresses:     nodePortAddresses,
 | 
							nodePortAddresses:     nodePortAddresses,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -163,10 +163,10 @@ func NewFakeProxier(ipt utiliptables.Interface, ipvs utilipvs.Interface, ipset u
 | 
				
			|||||||
		ipvsScheduler:         defaultScheduler,
 | 
							ipvsScheduler:         defaultScheduler,
 | 
				
			||||||
		iptablesData:          bytes.NewBuffer(nil),
 | 
							iptablesData:          bytes.NewBuffer(nil),
 | 
				
			||||||
		filterChainsData:      bytes.NewBuffer(nil),
 | 
							filterChainsData:      bytes.NewBuffer(nil),
 | 
				
			||||||
		natChains:             proxyutil.LineBuffer{},
 | 
							natChains:             proxyutil.NewLineBuffer(),
 | 
				
			||||||
		natRules:              proxyutil.LineBuffer{},
 | 
							natRules:              proxyutil.NewLineBuffer(),
 | 
				
			||||||
		filterChains:          proxyutil.LineBuffer{},
 | 
							filterChains:          proxyutil.NewLineBuffer(),
 | 
				
			||||||
		filterRules:           proxyutil.LineBuffer{},
 | 
							filterRules:           proxyutil.NewLineBuffer(),
 | 
				
			||||||
		netlinkHandle:         netlinkHandle,
 | 
							netlinkHandle:         netlinkHandle,
 | 
				
			||||||
		ipsetList:             ipsetList,
 | 
							ipsetList:             ipsetList,
 | 
				
			||||||
		nodePortAddresses:     proxyutil.NewNodePortAddresses(ipFamily, nil),
 | 
							nodePortAddresses:     proxyutil.NewNodePortAddresses(ipFamily, nil),
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										150
									
								
								pkg/proxy/util/linebuffer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								pkg/proxy/util/linebuffer.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,150 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2023 The Kubernetes Authors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LineBuffer is an interface for writing lines of input to a bytes.Buffer
 | 
				
			||||||
 | 
					type LineBuffer interface {
 | 
				
			||||||
 | 
						// Write takes a list of arguments, each a string or []string, joins all the
 | 
				
			||||||
 | 
						// individual strings with spaces, terminates with newline, and writes them to the
 | 
				
			||||||
 | 
						// buffer. Any other argument type will panic.
 | 
				
			||||||
 | 
						Write(args ...interface{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// WriteBytes writes bytes to the buffer, and terminates with newline.
 | 
				
			||||||
 | 
						WriteBytes(bytes []byte)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Reset clears the buffer
 | 
				
			||||||
 | 
						Reset()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Bytes returns the contents of the buffer as a []byte
 | 
				
			||||||
 | 
						Bytes() []byte
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// String returns the contents of the buffer as a string
 | 
				
			||||||
 | 
						String() string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Lines returns the number of lines in the buffer. Note that more precisely, this
 | 
				
			||||||
 | 
						// returns the number of times Write() or WriteBytes() was called; it assumes that
 | 
				
			||||||
 | 
						// you never wrote any newlines to the buffer yourself.
 | 
				
			||||||
 | 
						Lines() int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type realLineBuffer struct {
 | 
				
			||||||
 | 
						b     bytes.Buffer
 | 
				
			||||||
 | 
						lines int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewLineBuffer returns a new "real" LineBuffer
 | 
				
			||||||
 | 
					func NewLineBuffer() LineBuffer {
 | 
				
			||||||
 | 
						return &realLineBuffer{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Write is part of LineBuffer
 | 
				
			||||||
 | 
					func (buf *realLineBuffer) Write(args ...interface{}) {
 | 
				
			||||||
 | 
						for i, arg := range args {
 | 
				
			||||||
 | 
							if i > 0 {
 | 
				
			||||||
 | 
								buf.b.WriteByte(' ')
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							switch x := arg.(type) {
 | 
				
			||||||
 | 
							case string:
 | 
				
			||||||
 | 
								buf.b.WriteString(x)
 | 
				
			||||||
 | 
							case []string:
 | 
				
			||||||
 | 
								for j, s := range x {
 | 
				
			||||||
 | 
									if j > 0 {
 | 
				
			||||||
 | 
										buf.b.WriteByte(' ')
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									buf.b.WriteString(s)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								panic(fmt.Sprintf("unknown argument type: %T", x))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						buf.b.WriteByte('\n')
 | 
				
			||||||
 | 
						buf.lines++
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WriteBytes is part of LineBuffer
 | 
				
			||||||
 | 
					func (buf *realLineBuffer) WriteBytes(bytes []byte) {
 | 
				
			||||||
 | 
						buf.b.Write(bytes)
 | 
				
			||||||
 | 
						buf.b.WriteByte('\n')
 | 
				
			||||||
 | 
						buf.lines++
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reset is part of LineBuffer
 | 
				
			||||||
 | 
					func (buf *realLineBuffer) Reset() {
 | 
				
			||||||
 | 
						buf.b.Reset()
 | 
				
			||||||
 | 
						buf.lines = 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Bytes is part of LineBuffer
 | 
				
			||||||
 | 
					func (buf *realLineBuffer) Bytes() []byte {
 | 
				
			||||||
 | 
						return buf.b.Bytes()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// String is part of LineBuffer
 | 
				
			||||||
 | 
					func (buf *realLineBuffer) String() string {
 | 
				
			||||||
 | 
						return buf.b.String()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Lines is part of LineBuffer
 | 
				
			||||||
 | 
					func (buf *realLineBuffer) Lines() int {
 | 
				
			||||||
 | 
						return buf.lines
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type discardLineBuffer struct {
 | 
				
			||||||
 | 
						lines int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewDiscardLineBuffer returns a dummy LineBuffer that counts the number of writes but
 | 
				
			||||||
 | 
					// throws away the data. (This is used for iptables proxy partial syncs, to keep track of
 | 
				
			||||||
 | 
					// how many rules we managed to avoid having to sync.)
 | 
				
			||||||
 | 
					func NewDiscardLineBuffer() LineBuffer {
 | 
				
			||||||
 | 
						return &discardLineBuffer{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Write is part of LineBuffer
 | 
				
			||||||
 | 
					func (buf *discardLineBuffer) Write(args ...interface{}) {
 | 
				
			||||||
 | 
						buf.lines++
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WriteBytes is part of LineBuffer
 | 
				
			||||||
 | 
					func (buf *discardLineBuffer) WriteBytes(bytes []byte) {
 | 
				
			||||||
 | 
						buf.lines++
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Reset is part of LineBuffer
 | 
				
			||||||
 | 
					func (buf *discardLineBuffer) Reset() {
 | 
				
			||||||
 | 
						buf.lines = 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Bytes is part of LineBuffer
 | 
				
			||||||
 | 
					func (buf *discardLineBuffer) Bytes() []byte {
 | 
				
			||||||
 | 
						return []byte{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// String is part of LineBuffer
 | 
				
			||||||
 | 
					func (buf *discardLineBuffer) String() string {
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Lines is part of LineBuffer
 | 
				
			||||||
 | 
					func (buf *discardLineBuffer) Lines() int {
 | 
				
			||||||
 | 
						return buf.lines
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										168
									
								
								pkg/proxy/util/linebuffer_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								pkg/proxy/util/linebuffer_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,168 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					Copyright 2023 The Kubernetes Authors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					You may obtain a copy of the License at
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					limitations under the License.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"math/rand"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestLineBufferWrite(t *testing.T) {
 | 
				
			||||||
 | 
						testCases := []struct {
 | 
				
			||||||
 | 
							name     string
 | 
				
			||||||
 | 
							input    []interface{}
 | 
				
			||||||
 | 
							expected string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "none",
 | 
				
			||||||
 | 
								input:    []interface{}{},
 | 
				
			||||||
 | 
								expected: "\n",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "one string",
 | 
				
			||||||
 | 
								input:    []interface{}{"test1"},
 | 
				
			||||||
 | 
								expected: "test1\n",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "one slice",
 | 
				
			||||||
 | 
								input:    []interface{}{[]string{"test1", "test2"}},
 | 
				
			||||||
 | 
								expected: "test1 test2\n",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "mixed",
 | 
				
			||||||
 | 
								input:    []interface{}{"s1", "s2", []string{"s3", "s4"}, "", "s5", []string{}, []string{"s6"}, "s7"},
 | 
				
			||||||
 | 
								expected: "s1 s2 s3 s4  s5  s6 s7\n",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						testBuffer := NewLineBuffer()
 | 
				
			||||||
 | 
						for _, testCase := range testCases {
 | 
				
			||||||
 | 
							t.Run(testCase.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								testBuffer.Reset()
 | 
				
			||||||
 | 
								testBuffer.Write(testCase.input...)
 | 
				
			||||||
 | 
								if want, got := testCase.expected, testBuffer.String(); !strings.EqualFold(want, got) {
 | 
				
			||||||
 | 
									t.Fatalf("write word is %v\n expected: %q, got: %q", testCase.input, want, got)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if testBuffer.Lines() != 1 {
 | 
				
			||||||
 | 
									t.Fatalf("expected 1 line, got: %d", testBuffer.Lines())
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestLineBufferWritePanic(t *testing.T) {
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							if r := recover(); r == nil {
 | 
				
			||||||
 | 
								t.Errorf("did not panic")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
						testBuffer := NewLineBuffer()
 | 
				
			||||||
 | 
						testBuffer.Write("string", []string{"a", "slice"}, 1234)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestLineBufferWriteBytes(t *testing.T) {
 | 
				
			||||||
 | 
						testCases := []struct {
 | 
				
			||||||
 | 
							name     string
 | 
				
			||||||
 | 
							bytes    []byte
 | 
				
			||||||
 | 
							expected string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "empty bytes",
 | 
				
			||||||
 | 
								bytes:    []byte{},
 | 
				
			||||||
 | 
								expected: "\n",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "test bytes",
 | 
				
			||||||
 | 
								bytes:    []byte("test write bytes line"),
 | 
				
			||||||
 | 
								expected: "test write bytes line\n",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						testBuffer := NewLineBuffer()
 | 
				
			||||||
 | 
						for _, testCase := range testCases {
 | 
				
			||||||
 | 
							t.Run(testCase.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								testBuffer.Reset()
 | 
				
			||||||
 | 
								testBuffer.WriteBytes(testCase.bytes)
 | 
				
			||||||
 | 
								if want, got := testCase.expected, testBuffer.String(); !strings.EqualFold(want, got) {
 | 
				
			||||||
 | 
									t.Fatalf("write bytes is %v\n expected: %s, got: %s", testCase.bytes, want, got)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// obtained from https://stackoverflow.com/a/22892986
 | 
				
			||||||
 | 
					var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func randSeq() string {
 | 
				
			||||||
 | 
						b := make([]rune, 30)
 | 
				
			||||||
 | 
						for i := range b {
 | 
				
			||||||
 | 
							b[i] = letters[rand.Intn(len(letters))]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return string(b)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestWriteCountLines(t *testing.T) {
 | 
				
			||||||
 | 
						testCases := []struct {
 | 
				
			||||||
 | 
							name     string
 | 
				
			||||||
 | 
							expected int
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "write no line",
 | 
				
			||||||
 | 
								expected: 0,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "write one line",
 | 
				
			||||||
 | 
								expected: 1,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "write 100 lines",
 | 
				
			||||||
 | 
								expected: 100,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "write 1000 lines",
 | 
				
			||||||
 | 
								expected: 1000,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "write 10000 lines",
 | 
				
			||||||
 | 
								expected: 10000,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "write 100000 lines",
 | 
				
			||||||
 | 
								expected: 100000,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						testBuffer := NewLineBuffer()
 | 
				
			||||||
 | 
						discardBuffer := NewDiscardLineBuffer()
 | 
				
			||||||
 | 
						for _, testCase := range testCases {
 | 
				
			||||||
 | 
							t.Run(testCase.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								testBuffer.Reset()
 | 
				
			||||||
 | 
								discardBuffer.Reset()
 | 
				
			||||||
 | 
								for i := 0; i < testCase.expected; i++ {
 | 
				
			||||||
 | 
									testBuffer.Write(randSeq())
 | 
				
			||||||
 | 
									discardBuffer.Write(randSeq())
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								n := testBuffer.Lines()
 | 
				
			||||||
 | 
								if n != testCase.expected {
 | 
				
			||||||
 | 
									t.Fatalf("lines expected: %d, got: %d", testCase.expected, n)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								n = discardBuffer.Lines()
 | 
				
			||||||
 | 
								if n != testCase.expected {
 | 
				
			||||||
 | 
									t.Fatalf("discardBuffer lines expected: %d, got: %d", testCase.expected, n)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -17,7 +17,6 @@ limitations under the License.
 | 
				
			|||||||
package util
 | 
					package util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
@@ -321,67 +320,6 @@ func GetClusterIPByFamily(ipFamily v1.IPFamily, service *v1.Service) string {
 | 
				
			|||||||
	return ""
 | 
						return ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type LineBuffer struct {
 | 
					 | 
				
			||||||
	b     bytes.Buffer
 | 
					 | 
				
			||||||
	lines int
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Write takes a list of arguments, each a string or []string, joins all the
 | 
					 | 
				
			||||||
// individual strings with spaces, terminates with newline, and writes to buf.
 | 
					 | 
				
			||||||
// Any other argument type will panic.
 | 
					 | 
				
			||||||
func (buf *LineBuffer) Write(args ...interface{}) {
 | 
					 | 
				
			||||||
	for i, arg := range args {
 | 
					 | 
				
			||||||
		if i > 0 {
 | 
					 | 
				
			||||||
			buf.b.WriteByte(' ')
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		switch x := arg.(type) {
 | 
					 | 
				
			||||||
		case string:
 | 
					 | 
				
			||||||
			buf.b.WriteString(x)
 | 
					 | 
				
			||||||
		case []string:
 | 
					 | 
				
			||||||
			for j, s := range x {
 | 
					 | 
				
			||||||
				if j > 0 {
 | 
					 | 
				
			||||||
					buf.b.WriteByte(' ')
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				buf.b.WriteString(s)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		default:
 | 
					 | 
				
			||||||
			panic(fmt.Sprintf("unknown argument type: %T", x))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	buf.b.WriteByte('\n')
 | 
					 | 
				
			||||||
	buf.lines++
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// WriteBytes writes bytes to buffer, and terminates with newline.
 | 
					 | 
				
			||||||
func (buf *LineBuffer) WriteBytes(bytes []byte) {
 | 
					 | 
				
			||||||
	buf.b.Write(bytes)
 | 
					 | 
				
			||||||
	buf.b.WriteByte('\n')
 | 
					 | 
				
			||||||
	buf.lines++
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Reset clears buf
 | 
					 | 
				
			||||||
func (buf *LineBuffer) Reset() {
 | 
					 | 
				
			||||||
	buf.b.Reset()
 | 
					 | 
				
			||||||
	buf.lines = 0
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Bytes returns the contents of buf as a []byte
 | 
					 | 
				
			||||||
func (buf *LineBuffer) Bytes() []byte {
 | 
					 | 
				
			||||||
	return buf.b.Bytes()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// String returns the contents of buf as a string
 | 
					 | 
				
			||||||
func (buf *LineBuffer) String() string {
 | 
					 | 
				
			||||||
	return buf.b.String()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Lines returns the number of lines in buf. Note that more precisely, this returns the
 | 
					 | 
				
			||||||
// number of times Write() or WriteBytes() was called; it assumes that you never wrote
 | 
					 | 
				
			||||||
// any newlines to the buffer yourself.
 | 
					 | 
				
			||||||
func (buf *LineBuffer) Lines() int {
 | 
					 | 
				
			||||||
	return buf.lines
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// RevertPorts is closing ports in replacementPortsMap but not in originalPortsMap. In other words, it only
 | 
					// RevertPorts is closing ports in replacementPortsMap but not in originalPortsMap. In other words, it only
 | 
				
			||||||
// closes the ports opened in this sync.
 | 
					// closes the ports opened in this sync.
 | 
				
			||||||
func RevertPorts(replacementPortsMap, originalPortsMap map[netutils.LocalPort]netutils.Closeable) {
 | 
					func RevertPorts(replacementPortsMap, originalPortsMap map[netutils.LocalPort]netutils.Closeable) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,10 +17,8 @@ limitations under the License.
 | 
				
			|||||||
package util
 | 
					package util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"math/rand"
 | 
					 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	v1 "k8s.io/api/core/v1"
 | 
						v1 "k8s.io/api/core/v1"
 | 
				
			||||||
@@ -707,145 +705,6 @@ func TestRevertPorts(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestLineBufferWrite(t *testing.T) {
 | 
					 | 
				
			||||||
	testCases := []struct {
 | 
					 | 
				
			||||||
		name     string
 | 
					 | 
				
			||||||
		input    []interface{}
 | 
					 | 
				
			||||||
		expected string
 | 
					 | 
				
			||||||
	}{
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:     "none",
 | 
					 | 
				
			||||||
			input:    []interface{}{},
 | 
					 | 
				
			||||||
			expected: "\n",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:     "one string",
 | 
					 | 
				
			||||||
			input:    []interface{}{"test1"},
 | 
					 | 
				
			||||||
			expected: "test1\n",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:     "one slice",
 | 
					 | 
				
			||||||
			input:    []interface{}{[]string{"test1", "test2"}},
 | 
					 | 
				
			||||||
			expected: "test1 test2\n",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:     "mixed",
 | 
					 | 
				
			||||||
			input:    []interface{}{"s1", "s2", []string{"s3", "s4"}, "", "s5", []string{}, []string{"s6"}, "s7"},
 | 
					 | 
				
			||||||
			expected: "s1 s2 s3 s4  s5  s6 s7\n",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	testBuffer := LineBuffer{}
 | 
					 | 
				
			||||||
	for _, testCase := range testCases {
 | 
					 | 
				
			||||||
		t.Run(testCase.name, func(t *testing.T) {
 | 
					 | 
				
			||||||
			testBuffer.Reset()
 | 
					 | 
				
			||||||
			testBuffer.Write(testCase.input...)
 | 
					 | 
				
			||||||
			if want, got := testCase.expected, testBuffer.String(); !strings.EqualFold(want, got) {
 | 
					 | 
				
			||||||
				t.Fatalf("write word is %v\n expected: %q, got: %q", testCase.input, want, got)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if testBuffer.Lines() != 1 {
 | 
					 | 
				
			||||||
				t.Fatalf("expected 1 line, got: %d", testBuffer.Lines())
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestLineBufferWritePanic(t *testing.T) {
 | 
					 | 
				
			||||||
	defer func() {
 | 
					 | 
				
			||||||
		if r := recover(); r == nil {
 | 
					 | 
				
			||||||
			t.Errorf("did not panic")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}()
 | 
					 | 
				
			||||||
	testBuffer := LineBuffer{}
 | 
					 | 
				
			||||||
	testBuffer.Write("string", []string{"a", "slice"}, 1234)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestLineBufferWriteBytes(t *testing.T) {
 | 
					 | 
				
			||||||
	testCases := []struct {
 | 
					 | 
				
			||||||
		name     string
 | 
					 | 
				
			||||||
		bytes    []byte
 | 
					 | 
				
			||||||
		expected string
 | 
					 | 
				
			||||||
	}{
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:     "empty bytes",
 | 
					 | 
				
			||||||
			bytes:    []byte{},
 | 
					 | 
				
			||||||
			expected: "\n",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:     "test bytes",
 | 
					 | 
				
			||||||
			bytes:    []byte("test write bytes line"),
 | 
					 | 
				
			||||||
			expected: "test write bytes line\n",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	testBuffer := LineBuffer{}
 | 
					 | 
				
			||||||
	for _, testCase := range testCases {
 | 
					 | 
				
			||||||
		t.Run(testCase.name, func(t *testing.T) {
 | 
					 | 
				
			||||||
			testBuffer.Reset()
 | 
					 | 
				
			||||||
			testBuffer.WriteBytes(testCase.bytes)
 | 
					 | 
				
			||||||
			if want, got := testCase.expected, testBuffer.String(); !strings.EqualFold(want, got) {
 | 
					 | 
				
			||||||
				t.Fatalf("write bytes is %v\n expected: %s, got: %s", testCase.bytes, want, got)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func TestWriteCountLines(t *testing.T) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	testCases := []struct {
 | 
					 | 
				
			||||||
		name     string
 | 
					 | 
				
			||||||
		expected int
 | 
					 | 
				
			||||||
	}{
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:     "write no line",
 | 
					 | 
				
			||||||
			expected: 0,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:     "write one line",
 | 
					 | 
				
			||||||
			expected: 1,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:     "write 100 lines",
 | 
					 | 
				
			||||||
			expected: 100,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:     "write 1000 lines",
 | 
					 | 
				
			||||||
			expected: 1000,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:     "write 10000 lines",
 | 
					 | 
				
			||||||
			expected: 10000,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			name:     "write 100000 lines",
 | 
					 | 
				
			||||||
			expected: 100000,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	testBuffer := LineBuffer{}
 | 
					 | 
				
			||||||
	for _, testCase := range testCases {
 | 
					 | 
				
			||||||
		t.Run(testCase.name, func(t *testing.T) {
 | 
					 | 
				
			||||||
			testBuffer.Reset()
 | 
					 | 
				
			||||||
			for i := 0; i < testCase.expected; i++ {
 | 
					 | 
				
			||||||
				testBuffer.Write(randSeq())
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			n := testBuffer.Lines()
 | 
					 | 
				
			||||||
			if n != testCase.expected {
 | 
					 | 
				
			||||||
				t.Fatalf("lines expected: %d, got: %d", testCase.expected, n)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// obtained from https://stackoverflow.com/a/22892986
 | 
					 | 
				
			||||||
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func randSeq() string {
 | 
					 | 
				
			||||||
	b := make([]rune, 30)
 | 
					 | 
				
			||||||
	for i := range b {
 | 
					 | 
				
			||||||
		b[i] = letters[rand.Intn(len(letters))]
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return string(b)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func mustParseIPAddr(str string) net.Addr {
 | 
					func mustParseIPAddr(str string) net.Addr {
 | 
				
			||||||
	a, err := net.ResolveIPAddr("ip", str)
 | 
						a, err := net.ResolveIPAddr("ip", str)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user