diff --git a/.github/workflows/release-on-tag.yaml b/.github/workflows/release-on-tag.yaml index ee8f560..1bd4e95 100644 --- a/.github/workflows/release-on-tag.yaml +++ b/.github/workflows/release-on-tag.yaml @@ -20,6 +20,14 @@ jobs: - name: Checkout uses: actions/checkout@v2 + - name: Check sdk-version file + run: | + if [[ "$(head -n1 version/sdk-version)" != "${{ github.ref }}" ]]; then + echo "File version/sdk-version needs to be updated to ${{ github.ref }}" + exit 1 + fi + shell: bash + - name: Tidy run: | go mod tidy -compat=1.17 @@ -39,7 +47,7 @@ jobs: with: tag_name: ${{ github.ref }} release_name: Release ${{ github.ref }} - body: Automatic go Dapr client release + body: Automatic Go Dapr client release draft: false prerelease: false @@ -49,5 +57,5 @@ jobs: severity: info details: Release ${{ github.ref }} published description: Release - webhookUrl: ${{ secrets.DISCORD_WEBHOOK }} - avatarUrl: https://github.githubassets.com/images/modules/logos_page/Octocat.png \ No newline at end of file + webhookUrl: ${{ secrets.DISCORD_WEBHOOK }} + avatarUrl: https://github.githubassets.com/images/modules/logos_page/Octocat.png diff --git a/.github/workflows/test-on-push.yaml b/.github/workflows/test-on-push.yaml index 4ae8bef..38fbfd8 100644 --- a/.github/workflows/test-on-push.yaml +++ b/.github/workflows/test-on-push.yaml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest env: GOVER: 1.17 - GOLANGCILINT_VER: v1.31 + GOLANGCILINT_VER: v1.48.0 steps: diff --git a/.golangci.yml b/.golangci.yml index 0788fb1..13a2fe7 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -244,9 +244,28 @@ linters: - goerr113 - nestif - nlreturn + - tagliatelle + - ifshort + - forbidigo - exhaustive + - contextcheck + - exhaustruct + - exhaustivestruct + - wrapcheck + - predeclared + - varnamelen + - ireturn - noctx + - forcetypeassert + - cyclop + - errchkjson + - godot - gci + - paralleltest + - nonamedreturns + - nosnakecase + - nilnil + - staticcheck issues: exclude-rules: - path: .*_test.go diff --git a/client/client.go b/client/client.go index 5564dfe..76bc0b0 100644 --- a/client/client.go +++ b/client/client.go @@ -25,6 +25,7 @@ import ( "github.com/dapr/go-sdk/actor" "github.com/dapr/go-sdk/actor/config" + "github.com/dapr/go-sdk/version" "github.com/pkg/errors" "google.golang.org/grpc" @@ -39,13 +40,13 @@ import ( ) const ( - daprPortDefault = "50001" - daprPortEnvVarName = "DAPR_GRPC_PORT" /* #nosec */ - traceparentKey = "traceparent" - apiTokenKey = "dapr-api-token" /* #nosec */ - apiTokenEnvVarName = "DAPR_API_TOKEN" /* #nosec */ - clientDefaultTimoutSeconds = 5 - clientTimoutSecondsEnvVarName = "DAPR_CLIENT_TIMEOUT_SECONDS" + daprPortDefault = "50001" + daprPortEnvVarName = "DAPR_GRPC_PORT" /* #nosec */ + traceparentKey = "traceparent" + apiTokenKey = "dapr-api-token" /* #nosec */ + apiTokenEnvVarName = "DAPR_API_TOKEN" /* #nosec */ + clientDefaultTimeoutSeconds = 5 + clientTimeoutSecondsEnvVarName = "DAPR_CLIENT_TIMEOUT_SECONDS" ) var ( @@ -222,7 +223,7 @@ func NewClientWithPort(port string) (client Client, err error) { // NewClientWithAddress instantiates Dapr using specific address (including port). func NewClientWithAddress(address string) (client Client, err error) { if address == "" { - return nil, errors.New("nil address") + return nil, errors.New("empty address") } logger.Printf("dapr client initializing for: %s", address) @@ -230,28 +231,29 @@ func NewClientWithAddress(address string) (client Client, err error) { if err != nil { return nil, err } - ctx, ctxCancel := context.WithTimeout(context.Background(), time.Duration(timeoutSeconds)*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeoutSeconds)*time.Second) conn, err := grpc.DialContext( ctx, address, grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithUserAgent("dapr-sdk-go/"+version.SDKVersion), grpc.WithBlock(), ) + cancel() if err != nil { - ctxCancel() - return nil, errors.Wrapf(err, "error creating connection to '%s': %v", address, err) + return nil, fmt.Errorf("error creating connection to '%s': %w", address, err) } if hasToken := os.Getenv(apiTokenEnvVarName); hasToken != "" { logger.Println("client uses API token") } - return newClientWithConnectionAndCancelFunc(conn, ctxCancel), nil + return NewClientWithConnection(conn), nil } func getClientTimeoutSeconds() (int, error) { - timeoutStr := os.Getenv(clientTimoutSecondsEnvVarName) + timeoutStr := os.Getenv(clientTimeoutSecondsEnvVarName) if len(timeoutStr) == 0 { - return clientDefaultTimoutSeconds, nil + return clientDefaultTimeoutSeconds, nil } timeoutVar, err := strconv.Atoi(timeoutStr) if err != nil { @@ -269,10 +271,14 @@ func NewClientWithSocket(socket string) (client Client, err error) { return nil, errors.New("nil socket") } logger.Printf("dapr client initializing for: %s", socket) - addr := fmt.Sprintf("unix://%s", socket) - conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) + addr := "unix://" + socket + conn, err := grpc.Dial( + addr, + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithUserAgent("dapr-sdk-go/"+version.SDKVersion), + ) if err != nil { - return nil, errors.Wrapf(err, "error creating connection to '%s': %v", addr, err) + return nil, fmt.Errorf("error creating connection to '%s': %w", addr, err) } if hasToken := os.Getenv(apiTokenEnvVarName); hasToken != "" { logger.Println("client uses API token") @@ -282,32 +288,22 @@ func NewClientWithSocket(socket string) (client Client, err error) { // NewClientWithConnection instantiates Dapr client using specific connection. func NewClientWithConnection(conn *grpc.ClientConn) Client { - return newClientWithConnectionAndCancelFunc(conn, func() {}) -} - -func newClientWithConnectionAndCancelFunc( - conn *grpc.ClientConn, - cancelFunc context.CancelFunc, -) Client { return &GRPCClient{ - connection: conn, - ctxCancelFunc: cancelFunc, - protoClient: pb.NewDaprClient(conn), - authToken: os.Getenv(apiTokenEnvVarName), + connection: conn, + protoClient: pb.NewDaprClient(conn), + authToken: os.Getenv(apiTokenEnvVarName), } } // GRPCClient is the gRPC implementation of Dapr client. type GRPCClient struct { - connection *grpc.ClientConn - ctxCancelFunc context.CancelFunc - protoClient pb.DaprClient - authToken string + connection *grpc.ClientConn + protoClient pb.DaprClient + authToken string } // Close cleans up all resources created by the client. func (c *GRPCClient) Close() { - c.ctxCancelFunc() if c.connection != nil { c.connection.Close() c.connection = nil diff --git a/client/client_test.go b/client/client_test.go index 83a37aa..204f671 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -27,6 +27,7 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/test/bufconn" @@ -85,7 +86,7 @@ func TestNewClient(t *testing.T) { t.Run("new client closed with token", func(t *testing.T) { t.Setenv(apiTokenEnvVarName, "test") c, err := NewClientWithSocket(testSocket) - assert.NoError(t, err) + require.NoError(t, err) defer c.Close() c.WithAuthToken("") }) @@ -101,21 +102,21 @@ func TestNewClient(t *testing.T) { t.Run("new socket client closed with token", func(t *testing.T) { t.Setenv(apiTokenEnvVarName, "test") c, err := NewClientWithSocket(testSocket) - assert.NoError(t, err) + require.NoError(t, err) defer c.Close() c.WithAuthToken("") }) t.Run("new socket client closed with empty token", func(t *testing.T) { c, err := NewClientWithSocket(testSocket) - assert.NoError(t, err) + require.NoError(t, err) defer c.Close() c.WithAuthToken("") }) t.Run("new socket client with trace ID", func(t *testing.T) { c, err := NewClientWithSocket(testSocket) - assert.NoError(t, err) + require.NoError(t, err) defer c.Close() ctx := c.WithTraceID(context.Background(), "") _ = c.WithTraceID(ctx, "test") @@ -197,33 +198,33 @@ func getTestClientWithSocket(ctx context.Context) (client Client, closer func()) func Test_getClientTimeoutSeconds(t *testing.T) { t.Run("empty env var", func(t *testing.T) { - os.Setenv(clientTimoutSecondsEnvVarName, "") + t.Setenv(clientTimeoutSecondsEnvVarName, "") got, err := getClientTimeoutSeconds() assert.NoError(t, err) - assert.Equal(t, clientDefaultTimoutSeconds, got) + assert.Equal(t, clientDefaultTimeoutSeconds, got) }) t.Run("invalid env var", func(t *testing.T) { - os.Setenv(clientTimoutSecondsEnvVarName, "invalid") + t.Setenv(clientTimeoutSecondsEnvVarName, "invalid") _, err := getClientTimeoutSeconds() assert.Error(t, err) }) t.Run("normal env var", func(t *testing.T) { - os.Setenv(clientTimoutSecondsEnvVarName, "7") + t.Setenv(clientTimeoutSecondsEnvVarName, "7") got, err := getClientTimeoutSeconds() assert.NoError(t, err) assert.Equal(t, 7, got) }) t.Run("zero env var", func(t *testing.T) { - os.Setenv(clientTimoutSecondsEnvVarName, "0") + t.Setenv(clientTimeoutSecondsEnvVarName, "0") _, err := getClientTimeoutSeconds() assert.Error(t, err) }) t.Run("negative env var", func(t *testing.T) { - os.Setenv(clientTimoutSecondsEnvVarName, "-3") + t.Setenv(clientTimeoutSecondsEnvVarName, "-3") _, err := getClientTimeoutSeconds() assert.Error(t, err) }) diff --git a/client/configuration.go b/client/configuration.go index edf2374..8c7081a 100644 --- a/client/configuration.go +++ b/client/configuration.go @@ -83,8 +83,8 @@ func (c *GRPCClient) SubscribeConfigurationItems(ctx context.Context, storeName go func() { for { rsp, err := client.Recv() - if err == io.EOF || rsp == nil { - // receive goroutine would close if unsubscribe is called + if errors.Is(err, io.EOF) || rsp == nil { + // receive goroutine would close if unsubscribe is called. fmt.Println("dapr configuration subscribe finished.") close(stopCh) break diff --git a/client/configuration_test.go b/client/configuration_test.go index ce3ed38..bdf7cc2 100644 --- a/client/configuration_test.go +++ b/client/configuration_test.go @@ -70,7 +70,6 @@ func TestUnSubscribeConfigurationItems(t *testing.T) { counter := atomic.Int32{} totalCounter := atomic.Int32{} t.Run("Test unsubscribe configuration items", func(t *testing.T) { - subscribeID := "" subscribeIDChan := make(chan string) go func() { keys := []string{"mykey1", "mykey2", "mykey3"} @@ -88,7 +87,7 @@ func TestUnSubscribeConfigurationItems(t *testing.T) { }) assert.Nil(t, err) }() - subscribeID = <-subscribeIDChan + subscribeID := <-subscribeIDChan time.Sleep(time.Second * 2) time.Sleep(time.Millisecond * 500) err := testClient.UnsubscribeConfigurationItems(ctx, "example-config", subscribeID) diff --git a/service/grpc/invoke_test.go b/service/grpc/invoke_test.go index 3798a8d..a8f5df8 100644 --- a/service/grpc/invoke_test.go +++ b/service/grpc/invoke_test.go @@ -55,7 +55,7 @@ func TestInvokeErrors(t *testing.T) { } func TestInvokeWithToken(t *testing.T) { - _ = os.Setenv(cc.AppAPITokenEnvVar, "app-dapr-token") + t.Setenv(cc.AppAPITokenEnvVar, "app-dapr-token") server := getTestServer() startTestServer(server) methodName := "test" diff --git a/service/grpc/service_test.go b/service/grpc/service_test.go index b41f38f..3830e7e 100644 --- a/service/grpc/service_test.go +++ b/service/grpc/service_test.go @@ -49,6 +49,8 @@ func startTestServer(server *Server) { } func stopTestServer(t *testing.T, server *Server) { + t.Helper() + assert.NotNil(t, server) err := server.Stop() assert.Nilf(t, err, "error stopping server") diff --git a/service/http/invoke_test.go b/service/http/invoke_test.go index 919dd39..5d38ca9 100644 --- a/service/http/invoke_test.go +++ b/service/http/invoke_test.go @@ -39,7 +39,7 @@ func TestInvocationHandlerWithoutHandler(t *testing.T) { func TestInvocationHandlerWithToken(t *testing.T) { data := `{"name": "test", "data": hello}` - _ = os.Setenv(common.AppAPITokenEnvVar, "app-dapr-token") + t.Setenv(common.AppAPITokenEnvVar, "app-dapr-token") s := newServer("", nil) err := s.AddServiceInvocationHandler("/hello", func(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) { if in == nil || in.Data == nil || in.ContentType == "" { diff --git a/service/http/service.go b/service/http/service.go index 7c553de..baa50b3 100644 --- a/service/http/service.go +++ b/service/http/service.go @@ -44,7 +44,7 @@ func newServer(address string, router *mux.Router) *Server { } return &Server{ address: address, - httpServer: &http.Server{ + httpServer: &http.Server{ //nolint:gosec Addr: address, Handler: router, }, diff --git a/service/http/service_test.go b/service/http/service_test.go index b1e91a2..34da670 100644 --- a/service/http/service_test.go +++ b/service/http/service_test.go @@ -14,6 +14,7 @@ limitations under the License. package http import ( + "errors" "io" "net/http" "net/http/httptest" @@ -35,7 +36,7 @@ func TestStoppingStartedService(t *testing.T) { assert.NotNil(t, s) go func() { - if err := s.Start(); err != nil && err != http.ErrServerClosed { + if err := s.Start(); err != nil && !errors.Is(err, http.ErrServerClosed) { panic(err) } }() @@ -70,6 +71,8 @@ func TestSettingOptions(t *testing.T) { } func testRequest(t *testing.T, s *Server, r *http.Request, expectedStatusCode int) { + t.Helper() + rr := httptest.NewRecorder() s.mux.ServeHTTP(rr, r) resp := rr.Result() @@ -79,6 +82,8 @@ func testRequest(t *testing.T, s *Server, r *http.Request, expectedStatusCode in } func testRequestWithResponseBody(t *testing.T, s *Server, r *http.Request, expectedStatusCode int, expectedBody []byte) { + t.Helper() + rr := httptest.NewRecorder() s.mux.ServeHTTP(rr, r) rez := rr.Result() diff --git a/service/http/topic_test.go b/service/http/topic_test.go index 282aabc..f197359 100644 --- a/service/http/topic_test.go +++ b/service/http/topic_test.go @@ -334,18 +334,24 @@ func TestActorHandler(t *testing.T) { } func makeRequest(t *testing.T, s *Server, route, data, method string, expectedStatusCode int) { + t.Helper() + req, err := http.NewRequest(method, route, strings.NewReader(data)) assert.NoErrorf(t, err, "error creating request: %s", data) testRequest(t, s, req, expectedStatusCode) } func makeRequestWithExpectedBody(t *testing.T, s *Server, route, data, method string, expectedStatusCode int, expectedBody []byte) { + t.Helper() + req, err := http.NewRequest(method, route, strings.NewReader(data)) assert.NoErrorf(t, err, "error creating request: %s", data) testRequestWithResponseBody(t, s, req, expectedStatusCode, expectedBody) } func makeEventRequest(t *testing.T, s *Server, route, data string, expectedStatusCode int) { + t.Helper() + req, err := http.NewRequest(http.MethodPost, route, strings.NewReader(data)) assert.NoErrorf(t, err, "error creating request: %s", data) req.Header.Set("Content-Type", "application/json") diff --git a/version/sdk-version b/version/sdk-version new file mode 100644 index 0000000..05f629f --- /dev/null +++ b/version/sdk-version @@ -0,0 +1 @@ +v1.6.0 \ No newline at end of file diff --git a/version/version.go b/version/version.go new file mode 100644 index 0000000..e5c4a11 --- /dev/null +++ b/version/version.go @@ -0,0 +1,11 @@ +package version + +import ( + // Required for go:embed. + _ "embed" +) + +// SDKVersion contains the version of the SDK. +// +//go:embed sdk-version +var SDKVersion string