go.uber.org/yarpc@v1.72.1/x/yarpctest/types/port.go (about) 1 // Copyright (c) 2022 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package types 22 23 import ( 24 "net" 25 "strconv" 26 "strings" 27 "testing" 28 29 "github.com/stretchr/testify/require" 30 "go.uber.org/yarpc/x/yarpctest/api" 31 ) 32 33 // NewPortProvider creates an object that can be used to synchronize ports in 34 // yarpctest infrastructure. Ports can be acquired through the "Port" function 35 // which will create new ports for the test based on the id passed into the 36 // function. 37 func NewPortProvider(t testing.TB) *PortProvider { 38 return &PortProvider{ 39 idToPort: make(map[string]*Port), 40 t: t, 41 } 42 } 43 44 // PortProvider maintains a list of IDs to Ports. 45 type PortProvider struct { 46 idToPort map[string]*Port 47 t testing.TB 48 } 49 50 // NamedPort will return a *Port object that exists for the passed in 'id', or 51 // it will create a *Port object if one does not already exist. 52 func (p *PortProvider) NamedPort(id string) *Port { 53 port, ok := p.idToPort[id] 54 if !ok { 55 port = newPort(p.t) 56 p.idToPort[id] = port 57 } 58 return port 59 } 60 61 func newPort(t testing.TB) *Port { 62 listener, err := net.Listen("tcp", "127.0.0.1:0") 63 require.NoError(t, err) 64 pieces := strings.Split(listener.Addr().String(), ":") 65 port, err := strconv.ParseInt(pieces[len(pieces)-1], 10, 0) 66 require.NoError(t, err) 67 return &Port{ 68 Listener: listener, 69 Port: uint16(port), 70 } 71 } 72 73 // Port is an option injectable primitive for synchronizing port numbers between 74 // requests and services. 75 type Port struct { 76 api.NoopLifecycle 77 Listener net.Listener 78 Port uint16 79 } 80 81 // ApplyService implements api.ServiceOption. 82 func (n *Port) ApplyService(opts *api.ServiceOpts) { 83 opts.Listener = n.Listener 84 opts.Port = n.Port 85 } 86 87 // ApplyRequest implements api.RequestOption 88 func (n *Port) ApplyRequest(opts *api.RequestOpts) { 89 opts.Port = n.Port 90 } 91 92 // ApplyClientStreamRequest implements ClientStreamRequestOption 93 func (n *Port) ApplyClientStreamRequest(opts *api.ClientStreamRequestOpts) { 94 opts.Port = n.Port 95 }