Skip to content

Commit

Permalink
Merge pull request #2310 from yarpc/antonio.alors/release
Browse files Browse the repository at this point in the history
Preparing release v1.75.0
  • Loading branch information
biosvs authored Oct 15, 2024
2 parents 23485b3 + fc911ab commit d99af20
Show file tree
Hide file tree
Showing 11 changed files with 376 additions and 4 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

=======
## [1.75.0] - 2024-10-15
### Added
- Added multiaddress passthrough resolver
### Fixed
- Return correct error code for ctx Cancelled error in http outbound.
- Make tchannel outbound satisfy Namer interface
- Bump staticcheck version to v0.5.1 which support full support for iterators / range-over-func in [email protected]

## [1.73.2] - 2024-09-09
### Added
- `OriginalHeader` accessor in `encoding.Call` to get an original header value for a request from context object with zero-copy, as opposed to accessing it via `OriginalHeaders()`
Expand Down Expand Up @@ -1516,6 +1524,7 @@ This release requires regeneration of ThriftRW code.
## 0.1.0 - 2016-08-31
- Initial release.
[1.75.0]: https://github.com/yarpc/yarpc-go/compare/v1.73.2...1.75.0
[1.73.2]: https://github.com/yarpc/yarpc-go/compare/v1.73.1...1.73.2
[1.73.1]: https://github.com/yarpc/yarpc-go/compare/v1.73.0...1.73.1
[1.73.0]: https://github.com/yarpc/yarpc-go/compare/v1.72.1...1.73.0
Expand Down
2 changes: 1 addition & 1 deletion encoding/thrift/thriftrw-plugin-yarpc/gomock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ func assertErrorsMatch(t *testing.T, wantErrorsLike, errors []string) {
for _, m := range wantErrorsLike {
msg += "\n - " + indentTail(4, m)
}
t.Errorf(msg)
t.Error(msg)
}

if len(unexpectedErrors) > 0 {
Expand Down
2 changes: 1 addition & 1 deletion etc/make/deps.mk
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ PROGO_GRPC_VERSION := 1.2.0
RAGEL_VERSION := 6.10
ERRCHECK_VERSION := 1.7.0
GOLINT_VERSION := 0.0.0-20210508222113-6edffad5e616
STATICHCHECK_VERSION := 0.4.7
STATICHCHECK_VERSION := 0.5.1
GOIMPORTS_VERSION := 0.24.0

THRIFT_OS := $(UNAME_OS)
Expand Down
3 changes: 2 additions & 1 deletion internal/shard/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package main

import (
"errors"
"fmt"
"io"
"log"
Expand All @@ -40,7 +41,7 @@ func main() {

func do(args []string, writer io.Writer) error {
if len(args) < 2 {
return fmt.Errorf(usage)
return errors.New(usage)
}
shardNum, err := strconv.Atoi(args[0])
if err != nil {
Expand Down
90 changes: 90 additions & 0 deletions pkg/multiaddrpassthrough/multiaddrpassthrough.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) 2024 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package multiaddrpassthrough

import (
"errors"
"strings"

"google.golang.org/grpc/resolver"
)

func init() {
resolver.Register(&multiaddrPassthroughBuilder{})
}

// Scheme is the scheme for the multi address passthrough resolver.
const Scheme = "multi-addr-passthrough"

var (
errMissingAddr = errors.New("missing address")
)

type multiaddrPassthroughBuilder struct{}
type multiaddrPassthroughResolver struct{}

// NewBuilder creates a new multi address passthrough resolver builder.
func NewBuilder() resolver.Builder {
return &multiaddrPassthroughBuilder{}
}

// Build creates and starts a multi address passthrough resolver.
// It expects the target to be a list of addresses on the format:
// multi-addr-passthrough:///192.168.0.1:2345/127.0.0.1:5678
func (*multiaddrPassthroughBuilder) Build(target resolver.Target, cc resolver.ClientConn, _ resolver.BuildOptions) (resolver.Resolver, error) {
addresses, err := parseTarget(target)
if err != nil {
return nil, err
}

err = cc.UpdateState(resolver.State{Addresses: addresses})
if err != nil {
return nil, err
}

return &multiaddrPassthroughResolver{}, nil
}

func (*multiaddrPassthroughBuilder) Scheme() string {
return Scheme
}

// ResolveNow is a noop for the multi address passthrough resolver.
func (*multiaddrPassthroughResolver) ResolveNow(resolver.ResolveNowOptions) {}

// Close is a noop for the multi address passthrough resolver.
func (*multiaddrPassthroughResolver) Close() {}

func parseTarget(target resolver.Target) ([]resolver.Address, error) {
endpoints := strings.Split(target.URL.Path, "/")
addresses := make([]resolver.Address, 0, len(endpoints))

for _, endpoint := range endpoints {
if len(endpoint) > 0 {
addresses = append(addresses, resolver.Address{Addr: endpoint})
}
}

if len(addresses) == 0 {
return nil, errMissingAddr
}
return addresses, nil
}
228 changes: 228 additions & 0 deletions pkg/multiaddrpassthrough/multiaddrpassthrough_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
// Copyright (c) 2024 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package multiaddrpassthrough

import (
"context"
"errors"
"net"
"net/url"
"sync"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/reflection"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/serviceconfig"
)

var _ resolver.ClientConn = (*testClientConn)(nil)

func TestParseTarget(t *testing.T) {

tests := []struct {
msg string
target resolver.Target
addrsWant []resolver.Address
errWant string
}{
{
msg: "Single IPv4",
target: resolver.Target{Endpoint: "1.2.3.4:1234", URL: url.URL{Path: "/1.2.3.4:1234"}},
addrsWant: []resolver.Address{{Addr: "1.2.3.4:1234"}},
},
{
msg: "Single IPv4, leading slash",
target: resolver.Target{Endpoint: "1.2.3.4:1234", URL: url.URL{Path: "/1.2.3.4:1234"}},
addrsWant: []resolver.Address{{Addr: "1.2.3.4:1234"}},
},
{
msg: "Single IPv6",
target: resolver.Target{Endpoint: "[2607:f8b0:400a:801::1001]:9000", URL: url.URL{Path: "/[2607:f8b0:400a:801::1001]:9000"}},
addrsWant: []resolver.Address{{Addr: "[2607:f8b0:400a:801::1001]:9000"}},
},
{
msg: "Multiple IPv4s",
target: resolver.Target{Endpoint: "1.2.3.4:1234/5.6.7.8:1234", URL: url.URL{Path: "/1.2.3.4:1234/5.6.7.8:1234"}},
addrsWant: []resolver.Address{
{Addr: "1.2.3.4:1234"},
{Addr: "5.6.7.8:1234"},
},
},
{
msg: "Multiple IPv4s, double slash",
target: resolver.Target{Endpoint: "1.2.3.4:1234//5.6.7.8:1234", URL: url.URL{Path: "/1.2.3.4:1234/5.6.7.8:1234"}},
addrsWant: []resolver.Address{
{Addr: "1.2.3.4:1234"},
{Addr: "5.6.7.8:1234"},
},
},
{
msg: "Mixed IPv6 and IPv4",
target: resolver.Target{Endpoint: "[2607:f8b0:400a:801::1001]:9000/[2607:f8b0:400a:801::1002]:2345/127.0.0.1:4567", URL: url.URL{Path: "/[2607:f8b0:400a:801::1001]:9000/[2607:f8b0:400a:801::1002]:2345/127.0.0.1:4567"}},
addrsWant: []resolver.Address{
{Addr: "[2607:f8b0:400a:801::1001]:9000"},
{Addr: "[2607:f8b0:400a:801::1002]:2345"},
{Addr: "127.0.0.1:4567"},
},
},
{
msg: "Empty target",
target: resolver.Target{Endpoint: "", URL: url.URL{Path: "/"}},
errWant: errMissingAddr.Error(),
},
{
msg: "Localhost",
target: resolver.Target{Endpoint: "localhost:1000", URL: url.URL{Path: "/localhost:1000"}},
addrsWant: []resolver.Address{
{Addr: "localhost:1000"},
},
},
}

for _, tt := range tests {
t.Run(tt.msg, func(t *testing.T) {
gotAddr, gotErr := parseTarget(tt.target)

if gotErr != nil {
assert.EqualError(t, gotErr, tt.errWant)
}
assert.ElementsMatch(t, gotAddr, tt.addrsWant)
})
}
}

func TestBuild(t *testing.T) {
tests := []struct {
msg string
target resolver.Target
watAddress []resolver.Address
wantErr string
}{
{
msg: "IPv6",
target: resolver.Target{Endpoint: "[2001:db8::1]:http", URL: url.URL{Path: "/[2001:db8::1]:http"}},
watAddress: []resolver.Address{{Addr: "[2001:db8::1]:http"}},
},
{
msg: "Empty target",
target: resolver.Target{Endpoint: "", URL: url.URL{Path: "/"}},
wantErr: errMissingAddr.Error(),
},
}

builder := &multiaddrPassthroughBuilder{}
for _, tt := range tests {
t.Run(tt.msg, func(t *testing.T) {

cc := &testClientConn{target: tt.target.URL.Host}
gotResolver, gotError := builder.Build(tt.target, cc, resolver.BuildOptions{})
if tt.wantErr != "" {
assert.EqualError(t, gotError, tt.wantErr)
} else {
assert.ElementsMatch(t, cc.State.Addresses, tt.watAddress)
gotResolver.Close()
}
})
}
}

func TestClientConnectionIntegration(t *testing.T) {
dest := "127.0.0.1:3456"
wantAddr := []resolver.Address{{Addr: dest}}

b := NewBuilder()

cc := &testClientConn{}
_, err := b.Build(resolver.Target{Endpoint: dest, URL: url.URL{Path: dest}}, cc, resolver.BuildOptions{})
assert.ElementsMatch(t, cc.State.Addresses, wantAddr, "Client connection received the wrong list of addresses")
require.NoError(t, err, "unexpected error building the resolver")

cc.failUpdate = true
_, err = b.Build(resolver.Target{Endpoint: dest, URL: url.URL{Path: dest}}, cc, resolver.BuildOptions{})
require.Error(t, err)

}

func TestGRPCIntegration(t *testing.T) {
ln, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)

s := grpc.NewServer()
reflection.Register(s)
defer s.GracefulStop()

go func() {
err := s.Serve(ln)
require.NoError(t, err)
}()

b := NewBuilder()
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()

conn, err := grpc.DialContext(ctx, b.Scheme()+":///"+ln.Addr().String(), grpc.WithBlock(), grpc.WithTransportCredentials(insecure.NewCredentials()))
assert.NoError(t, err)

defer func() {
err := conn.Close()
require.NoError(t, err)
}()
}

type testClientConn struct {
target string
failUpdate bool
State resolver.State
mu sync.Mutex
addrs []resolver.Address // protected by mu
t *testing.T
}

func (t *testClientConn) ParseServiceConfig(string) *serviceconfig.ParseResult {
return nil
}

func (t *testClientConn) ReportError(error) {
}

func (t *testClientConn) UpdateState(state resolver.State) error {
t.State = state
if t.failUpdate {
return errors.New("failed to update state")
}
return nil
}

func (t *testClientConn) NewAddress(addrs []resolver.Address) {
t.mu.Lock()
defer t.mu.Unlock()
t.addrs = addrs
}

// This shouldn't be called by our code since we don't support this.
func (t *testClientConn) NewServiceConfig(serviceConfig string) {
assert.Fail(t.t, "unexpected call to NewServiceConfig")
}
7 changes: 7 additions & 0 deletions transport/http/outbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,13 @@ func (o *Outbound) doWithPeer(
"client timeout for procedure %q of service %q after %v",
treq.Procedure, treq.Service, end.Sub(start))
}
if err == context.Canceled {
end := time.Now()
return nil, yarpcerrors.Newf(
yarpcerrors.CodeCancelled,
"client canceled request for procedure %q of service %q after %v",
treq.Procedure, treq.Service, end.Sub(start))
}

// Note that the connection may have been lost so the peer connection
// maintenance loop resumes probing for availability.
Expand Down
Loading

0 comments on commit d99af20

Please sign in to comment.