istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/echo/server/endpoint/udp.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package endpoint 16 17 import ( 18 "fmt" 19 "net" 20 "net/http" 21 "os" 22 "strconv" 23 "strings" 24 25 "github.com/google/uuid" 26 27 "istio.io/istio/pkg/test/echo" 28 "istio.io/istio/pkg/test/util/retry" 29 ) 30 31 var _ Instance = &udpInstance{} 32 33 type udpInstance struct { 34 Config 35 l net.PacketConn 36 } 37 38 func newUDP(config Config) Instance { 39 return &udpInstance{ 40 Config: config, 41 } 42 } 43 44 func (s *udpInstance) GetConfig() Config { 45 return s.Config 46 } 47 48 func (s *udpInstance) Start(onReady OnReadyFunc) error { 49 var listener net.PacketConn 50 var port int 51 var err error 52 if s.Port.TLS { 53 return fmt.Errorf("TLS not supported for UDP") 54 } 55 // Listen on the given port and update the port if it changed from what was passed in. 56 listener, port, err = listenUDPAddress(s.ListenerIP, s.Port.Port) 57 // Store the actual listening port back to the argument. 58 s.Port.Port = port 59 if err != nil { 60 return err 61 } 62 63 s.l = listener 64 epLog.Infof("Listening UDP on %v\n", port) 65 66 // Start serving UDP traffic. 67 go func() { 68 buf := make([]byte, 2048) 69 for { 70 _, remote, err := listener.ReadFrom(buf) 71 if err != nil { 72 epLog.Warn("UDP read failed: " + err.Error()) 73 return 74 } 75 76 id := uuid.New() 77 epLog.WithLabels("remote", remote, "id", id).Infof("UDP Request") 78 79 responseFields := s.getResponseFields(remote) 80 if _, err := listener.WriteTo([]byte(responseFields), remote); err != nil { 81 epLog.WithLabels("id", id).Warnf("UDP failed writing echo response: %v", err) 82 } 83 } 84 }() 85 86 // Notify the WaitGroup once the port has transitioned to ready. 87 go s.awaitReady(onReady, listener.LocalAddr().String()) 88 return nil 89 } 90 91 func (s *udpInstance) getResponseFields(conn net.Addr) string { 92 ip, _, _ := net.SplitHostPort(conn.String()) 93 // Write non-request fields specific to the instance 94 out := &strings.Builder{} 95 echo.StatusCodeField.Write(out, strconv.Itoa(http.StatusOK)) 96 echo.ClusterField.WriteNonEmpty(out, s.Cluster) 97 echo.IstioVersionField.WriteNonEmpty(out, s.IstioVersion) 98 echo.NamespaceField.WriteNonEmpty(out, s.Namespace) 99 echo.ServiceVersionField.Write(out, s.Version) 100 echo.ServicePortField.Write(out, strconv.Itoa(s.Port.Port)) 101 echo.IPField.Write(out, ip) 102 echo.ProtocolField.Write(out, "TCP") 103 104 if hostname, err := os.Hostname(); err == nil { 105 echo.HostnameField.Write(out, hostname) 106 } 107 return out.String() 108 } 109 110 func (s *udpInstance) Close() error { 111 if s.l != nil { 112 _ = s.l.Close() 113 } 114 return nil 115 } 116 117 func (s *udpInstance) awaitReady(onReady OnReadyFunc, address string) { 118 defer onReady() 119 120 err := retry.UntilSuccess(func() error { 121 conn, err := net.Dial("udp", address) 122 if err != nil { 123 return err 124 } 125 defer func() { _ = conn.Close() }() 126 127 // Server is up now, we're ready. 128 return nil 129 }, retry.Timeout(readyTimeout), retry.Delay(readyInterval)) 130 131 if err != nil { 132 epLog.Errorf("readiness failed for endpoint %s: %v", address, err) 133 } else { 134 epLog.Infof("ready for UDP endpoint %s", address) 135 } 136 }