THRIFT-5338: Raise minimal supported go version to 1.14.14

Client: go

- Update Dockerfiles used by travis
- Add go.mod file
- Modify error handling code to take advantage of errors package updates
  in go1.13
diff --git a/LANGUAGES.md b/LANGUAGES.md
index 4ea86f7..08c431d 100644
--- a/LANGUAGES.md
+++ b/LANGUAGES.md
@@ -160,7 +160,7 @@
 <td align=left><a href="https://github.com/apache/thrift/blob/master/lib/go/README.md">Go</a></td>
 <!-- Since -----------------><td>0.7.0</td>
 <!-- Build Systems ---------><td><img src="doc/images/cgrn.png" alt="Yes"/></td><td><img src="doc/images/cred.png" alt=""/></td>
-<!-- Language Levels -------><td>1.10.8</td><td>1.13.1</td>
+<!-- Language Levels -------><td>1.14.14</td><td>1.15.7</td>
 <!-- Low-Level Transports --><td><img src="doc/images/cred.png" alt=""/></td><td><img src="doc/images/cred.png" alt=""/></td><td><img src="doc/images/cgrn.png" alt="Yes"/></td><td><img src="doc/images/cred.png" alt=""/></td><td><img src="doc/images/cgrn.png" alt="Yes"/></td><td><img src="doc/images/cgrn.png" alt="Yes"/></td>
 <!-- Transport Wrappers ----><td><img src="doc/images/cgrn.png" alt="Yes"/></td><td><img src="doc/images/cred.png" alt=""/></td><td><img src="doc/images/cgrn.png" alt="Yes"/></td><td><img src="doc/images/cgrn.png" alt="Yes"/></td>
 <!-- Protocols -------------><td><img src="doc/images/cgrn.png" alt="Yes"/></td><td><img src="doc/images/cgrn.png" alt="Yes"/></td><td><img src="doc/images/cgrn.png" alt="Yes"/></td><td><img src="doc/images/cgrn.png" alt="Yes"/></td>
diff --git a/build/docker/README.md b/build/docker/README.md
index e3c2ec1..08023a7 100644
--- a/build/docker/README.md
+++ b/build/docker/README.md
@@ -178,7 +178,7 @@
 | dart      | 2.0.0         | 2.4.0         |       |
 | delphi    |               |               | Not in CI |
 | erlang    | 18.3          | 22.0          |       |
-| go        | 1.10.8        | 1.12.6        |       |
+| go        | 1.14.14       | 1.15.7        |       |
 | haskell   | 7.10.3        | 8.0.2         |       |
 | haxe      | 3.2.1         | 3.4.4         | THRIFT-4352: avoid 3.4.2 |
 | java      | 1.8.0_191     | 11.0.3        |       |
diff --git a/build/docker/ubuntu-bionic/Dockerfile b/build/docker/ubuntu-bionic/Dockerfile
index 7deefcb..c8ecd8e 100644
--- a/build/docker/ubuntu-bionic/Dockerfile
+++ b/build/docker/ubuntu-bionic/Dockerfile
@@ -140,9 +140,9 @@
       libglib2.0-dev
 
 # golang
-ENV GOLANG_VERSION 1.13.1
+ENV GOLANG_VERSION 1.15.7
 ENV GOLANG_DOWNLOAD_URL https://golang.org/dl/go$GOLANG_VERSION.linux-amd64.tar.gz
-ENV GOLANG_DOWNLOAD_SHA256 94f874037b82ea5353f4061e543681a0e79657f787437974214629af8407d124
+ENV GOLANG_DOWNLOAD_SHA256 0d142143794721bb63ce6c8a6180c4062bcf8ef4715e7d6d6609f3a8282629b3
 RUN curl -fsSL "$GOLANG_DOWNLOAD_URL" -o golang.tar.gz && \
       echo "$GOLANG_DOWNLOAD_SHA256  golang.tar.gz" | sha256sum -c - && \
             tar -C /usr/local -xzf golang.tar.gz && \
diff --git a/build/docker/ubuntu-disco/Dockerfile b/build/docker/ubuntu-disco/Dockerfile
index cfa4041..531718c 100644
--- a/build/docker/ubuntu-disco/Dockerfile
+++ b/build/docker/ubuntu-disco/Dockerfile
@@ -140,9 +140,9 @@
       libglib2.0-dev
 
 # golang
-ENV GOLANG_VERSION 1.12.6
+ENV GOLANG_VERSION 1.15.7
 ENV GOLANG_DOWNLOAD_URL https://golang.org/dl/go$GOLANG_VERSION.linux-amd64.tar.gz
-ENV GOLANG_DOWNLOAD_SHA256 dbcf71a3c1ea53b8d54ef1b48c85a39a6c9a935d01fc8291ff2b92028e59913c
+ENV GOLANG_DOWNLOAD_SHA256 0d142143794721bb63ce6c8a6180c4062bcf8ef4715e7d6d6609f3a8282629b3
 RUN curl -fsSL "$GOLANG_DOWNLOAD_URL" -o golang.tar.gz && \
       echo "$GOLANG_DOWNLOAD_SHA256  golang.tar.gz" | sha256sum -c - && \
             tar -C /usr/local -xzf golang.tar.gz && \
diff --git a/build/docker/ubuntu-xenial/Dockerfile b/build/docker/ubuntu-xenial/Dockerfile
index 535db41..e554c53 100644
--- a/build/docker/ubuntu-xenial/Dockerfile
+++ b/build/docker/ubuntu-xenial/Dockerfile
@@ -137,9 +137,9 @@
       libglib2.0-dev
 
 # golang
-ENV GOLANG_VERSION 1.10.8
+ENV GOLANG_VERSION 1.14.14
 ENV GOLANG_DOWNLOAD_URL https://golang.org/dl/go$GOLANG_VERSION.linux-amd64.tar.gz
-ENV GOLANG_DOWNLOAD_SHA256 d8626fb6f9a3ab397d88c483b576be41fa81eefcec2fd18562c87626dbb3c39e
+ENV GOLANG_DOWNLOAD_SHA256 6f1354c9040d65d1622b451f43c324c1e5197aa9242d00c5a117d0e2625f3e0d
 RUN curl -fsSL "$GOLANG_DOWNLOAD_URL" -o golang.tar.gz && \
       echo "$GOLANG_DOWNLOAD_SHA256  golang.tar.gz" | sha256sum -c - && \
       tar -C /usr/local -xzf golang.tar.gz && \
diff --git a/lib/go/test/tests/client_error_test.go b/lib/go/test/tests/client_error_test.go
index a064163..8d720ff 100644
--- a/lib/go/test/tests/client_error_test.go
+++ b/lib/go/test/tests/client_error_test.go
@@ -32,6 +32,23 @@
 // TestCase: Comprehensive call and reply workflow in the client.
 // Setup mock to fail at a certain position. Return true if position exists otherwise false.
 func prepareClientCallReply(protocol *MockTProtocol, failAt int, failWith error) bool {
+	// NOTE: here the number 50 is the same as the last number at the end of
+	// this function. If more function calls are added in the future, this
+	// number needs to be increased accordingly.
+	//
+	// It's needed for go 1.14+. Before go 1.14 we only call gomock
+	// controller's Finish function when this function returns true.
+	// Starting from go 1.14 the gomock will take advantage of
+	// testing.T.Cleanup interface, which means the Finish function will
+	// always be called by t.Cleanup, even if we return false here.
+	// As a result, in the case we need to return false by this function,
+	// we must return before calling any of the
+	// protocol.EXPECT().* functions.
+	const lastFailAt = 50
+	if failAt > lastFailAt {
+		return false
+	}
+
 	var err error = nil
 
 	if failAt == 0 {
diff --git a/lib/go/thrift/exception.go b/lib/go/thrift/exception.go
index 4836479..53bf862 100644
--- a/lib/go/thrift/exception.go
+++ b/lib/go/thrift/exception.go
@@ -34,7 +34,8 @@
 func PrependError(prepend string, err error) error {
 	msg := prepend + err.Error()
 
-	if te, ok := err.(TException); ok {
+	var te TException
+	if errors.As(err, &te) {
 		switch te.TExceptionType() {
 		case TExceptionTypeTransport:
 			if t, ok := err.(TTransportException); ok {
@@ -45,7 +46,8 @@
 				return prependTProtocolException(prepend, t)
 			}
 		case TExceptionTypeApplication:
-			if t, ok := err.(TApplicationException); ok {
+			var t TApplicationException
+			if errors.As(err, &t) {
 				return NewTApplicationException(t.TypeId(), msg)
 			}
 		}
diff --git a/lib/go/thrift/go.mod b/lib/go/thrift/go.mod
new file mode 100644
index 0000000..430c255
--- /dev/null
+++ b/lib/go/thrift/go.mod
@@ -0,0 +1,3 @@
+module github.com/apache/thrift/lib/go/thrift
+
+go 1.14
diff --git a/lib/go/thrift/header_protocol.go b/lib/go/thrift/header_protocol.go
index 5ad48e4..35e0458 100644
--- a/lib/go/thrift/header_protocol.go
+++ b/lib/go/thrift/header_protocol.go
@@ -21,6 +21,7 @@
 
 import (
 	"context"
+	"errors"
 )
 
 // THeaderProtocol is a thrift protocol that implements THeader:
@@ -233,8 +234,8 @@
 	var newProto TProtocol
 	newProto, err = p.transport.Protocol().GetProtocol(p.transport)
 	if err != nil {
-		tAppExc, ok := err.(TApplicationException)
-		if !ok {
+		var tAppExc TApplicationException
+		if !errors.As(err, &tAppExc) {
 			return
 		}
 		if e := p.protocol.WriteMessageBegin(ctx, "", EXCEPTION, seqID); e != nil {
diff --git a/lib/go/thrift/header_transport.go b/lib/go/thrift/header_transport.go
index 1e8e302..f1dc99c 100644
--- a/lib/go/thrift/header_transport.go
+++ b/lib/go/thrift/header_transport.go
@@ -488,7 +488,7 @@
 	headers := make(THeaderMap)
 	for {
 		infoType, err := hp.readVarint32()
-		if err == io.EOF {
+		if errors.Is(err, io.EOF) {
 			break
 		}
 		if err != nil {
diff --git a/lib/go/thrift/http_client.go b/lib/go/thrift/http_client.go
index 26e52b3..1501586 100644
--- a/lib/go/thrift/http_client.go
+++ b/lib/go/thrift/http_client.go
@@ -22,6 +22,7 @@
 import (
 	"bytes"
 	"context"
+	"errors"
 	"io"
 	"io/ioutil"
 	"net/http"
@@ -159,7 +160,7 @@
 		return 0, NewTTransportException(NOT_OPEN, "Response buffer is empty, no request.")
 	}
 	n, err := p.response.Body.Read(buf)
-	if n > 0 && (err == nil || err == io.EOF) {
+	if n > 0 && (err == nil || errors.Is(err, io.EOF)) {
 		return n, nil
 	}
 	return n, NewTTransportExceptionFromError(err)
diff --git a/lib/go/thrift/protocol_exception.go b/lib/go/thrift/protocol_exception.go
index c86a9ff..9dcf4bf 100644
--- a/lib/go/thrift/protocol_exception.go
+++ b/lib/go/thrift/protocol_exception.go
@@ -21,6 +21,7 @@
 
 import (
 	"encoding/base64"
+	"errors"
 )
 
 // Thrift Protocol exception
@@ -76,7 +77,7 @@
 		return e
 	}
 
-	if _, ok := err.(base64.CorruptInputError); ok {
+	if errors.As(err, new(base64.CorruptInputError)) {
 		return NewTProtocolExceptionWithType(INVALID_DATA, err)
 	}
 
diff --git a/lib/go/thrift/rich_transport.go b/lib/go/thrift/rich_transport.go
index 4025beb..83fdf29 100644
--- a/lib/go/thrift/rich_transport.go
+++ b/lib/go/thrift/rich_transport.go
@@ -19,7 +19,10 @@
 
 package thrift
 
-import "io"
+import (
+	"errors"
+	"io"
+)
 
 type RichTransport struct {
 	TTransport
@@ -49,7 +52,7 @@
 func readByte(r io.Reader) (c byte, err error) {
 	v := [1]byte{0}
 	n, err := r.Read(v[0:1])
-	if n > 0 && (err == nil || err == io.EOF) {
+	if n > 0 && (err == nil || errors.Is(err, io.EOF)) {
 		return v[0], nil
 	}
 	if n > 0 && err != nil {
diff --git a/lib/go/thrift/simple_server.go b/lib/go/thrift/simple_server.go
index ca0e61d..563cbfc 100644
--- a/lib/go/thrift/simple_server.go
+++ b/lib/go/thrift/simple_server.go
@@ -243,12 +243,11 @@
 	if err == nil {
 		return nil
 	}
-	// err could be io.EOF wrapped with TProtocolException,
-	// so that err == io.EOF doesn't necessarily work in some cases.
-	if err.Error() == io.EOF.Error() {
+	if errors.Is(err, io.EOF) {
 		return nil
 	}
-	if err, ok := err.(TTransportException); ok && err.TypeId() == END_OF_FILE {
+	var te TTransportException
+	if errors.As(err, &te) && te.TypeId() == END_OF_FILE {
 		return nil
 	}
 	return err
@@ -315,15 +314,14 @@
 		}
 
 		ok, err := processor.Process(ctx, inputProtocol, outputProtocol)
-		// Once we dropped support for pre-go1.13 this can be replaced by:
-		// errors.Is(err, ErrAbandonRequest)
-		if unwrapError(err) == ErrAbandonRequest {
+		if errors.Is(err, ErrAbandonRequest) {
 			return client.Close()
 		}
-		if _, ok := err.(TTransportException); ok && err != nil {
+		if errors.As(err, new(TTransportException)) && err != nil {
 			return err
 		}
-		if err, ok := err.(TApplicationException); ok && err.TypeId() == UNKNOWN_METHOD {
+		var tae TApplicationException
+		if errors.As(err, &tae) && tae.TypeId() == UNKNOWN_METHOD {
 			continue
 		}
 		if !ok {
@@ -332,17 +330,3 @@
 	}
 	return nil
 }
-
-type unwrapper interface {
-	Unwrap() error
-}
-
-func unwrapError(err error) error {
-	for {
-		if u, ok := err.(unwrapper); ok {
-			err = u.Unwrap()
-		} else {
-			return err
-		}
-	}
-}
diff --git a/lib/go/thrift/socket_unix_conn.go b/lib/go/thrift/socket_unix_conn.go
index 98e5a04..f5fab3a 100644
--- a/lib/go/thrift/socket_unix_conn.go
+++ b/lib/go/thrift/socket_unix_conn.go
@@ -22,6 +22,7 @@
 package thrift
 
 import (
+	"errors"
 	"io"
 	"syscall"
 	"time"
@@ -67,7 +68,7 @@
 		return nil
 	}
 
-	if err == syscall.EAGAIN || err == syscall.EWOULDBLOCK {
+	if errors.Is(err, syscall.EAGAIN) || errors.Is(err, syscall.EWOULDBLOCK) {
 		// This means the connection is still open but we don't have
 		// anything to read right now.
 		return nil
diff --git a/lib/go/thrift/transport_exception.go b/lib/go/thrift/transport_exception.go
index 08f7ce2..0a3f076 100644
--- a/lib/go/thrift/transport_exception.go
+++ b/lib/go/thrift/transport_exception.go
@@ -92,23 +92,23 @@
 		return t
 	}
 
-	newTTransportException := func(typeID int, err error, prefix string) TTransportException {
-		return &tTransportException{
-			typeId: typeID,
-			err:    err,
-			msg:    prefix + err.Error(),
-		}
+	te := &tTransportException{
+		typeId: UNKNOWN_TRANSPORT_EXCEPTION,
+		err:    e,
+		msg:    e.Error(),
 	}
 
 	if isTimeoutError(e) {
-		return newTTransportException(TIMED_OUT, e, "")
+		te.typeId = TIMED_OUT
+		return te
 	}
 
-	if e == io.EOF {
-		return newTTransportException(END_OF_FILE, e, "")
+	if errors.Is(e, io.EOF) {
+		te.typeId = END_OF_FILE
+		return te
 	}
 
-	return newTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, e, "")
+	return te
 }
 
 func prependTTransportException(prepend string, e TTransportException) TTransportException {
@@ -119,11 +119,12 @@
 	}
 }
 
-// isTimeoutError returns true when err is a timeout error.
+// isTimeoutError returns true when err is an error caused by timeout.
 //
 // Note that this also includes TTransportException wrapped timeout errors.
 func isTimeoutError(err error) bool {
-	if t, ok := err.(timeoutable); ok {
+	var t timeoutable
+	if errors.As(err, &t) {
 		return t.Timeout()
 	}
 	return false
diff --git a/lib/go/thrift/transport_exception_test.go b/lib/go/thrift/transport_exception_test.go
index 57386cb..0d79409 100644
--- a/lib/go/thrift/transport_exception_test.go
+++ b/lib/go/thrift/transport_exception_test.go
@@ -47,12 +47,8 @@
 		t.Errorf("TypeId was not TIMED_OUT: expected %v, got %v", TIMED_OUT, exception.TypeId())
 	}
 
-	// NOTE: this can also be replaced by errors.Unwrap, but that requires
-	// go 1.13+.
-	if e, ok := exception.(unwrapper); !ok {
-		t.Error("Expected exception to be unwrappable, it is not.")
-	} else if e.Unwrap() != timeout {
-		t.Errorf("Unwrapped exception did not match: expected %v, got %v", timeout, e.Unwrap())
+	if unwrapped := errors.Unwrap(exception); unwrapped != timeout {
+		t.Errorf("Unwrapped exception did not match: expected %v, got %v", timeout, unwrapped)
 	}
 }
 
@@ -66,12 +62,8 @@
 		t.Errorf("TypeId was not END_OF_FILE: expected %v, got %v", END_OF_FILE, exception.TypeId())
 	}
 
-	// NOTE: this can also be replaced by errors.Unwrap, but that requires
-	// go 1.13+.
-	if e, ok := exception.(unwrapper); !ok {
-		t.Error("Expected exception to be unwrappable, it is not.")
-	} else if e.Unwrap() != io.EOF {
-		t.Errorf("Unwrapped exception did not match: expected %v, got %v", io.EOF, e.Unwrap())
+	if unwrapped := errors.Unwrap(exception); unwrapped != io.EOF {
+		t.Errorf("Unwrapped exception did not match: expected %v, got %v", io.EOF, unwrapped)
 	}
 }