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  }