You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ke...@apache.org on 2019/12/28 09:29:22 UTC

[skywalking-cli] branch feature/timezone created (now 729821b)

This is an automated email from the ASF dual-hosted git repository.

kezhenxu94 pushed a change to branch feature/timezone
in repository https://gitbox.apache.org/repos/asf/skywalking-cli.git.


      at 729821b  Make use of server timezone API when possible

This branch includes the following new commits:

     new 729821b  Make use of server timezone API when possible

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[skywalking-cli] 01/01: Make use of server timezone API when possible

Posted by ke...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kezhenxu94 pushed a commit to branch feature/timezone
in repository https://gitbox.apache.org/repos/asf/skywalking-cli.git

commit 729821ba34758389b5fec33ae5082afc0377c1d7
Author: kezhenxu94 <ke...@163.com>
AuthorDate: Sat Dec 28 17:29:11 2019 +0800

    Make use of server timezone API when possible
---
 commands/interceptor/duration.go      | 17 ++++++++++---
 commands/interceptor/duration_test.go |  2 +-
 commands/interceptor/timezone.go      | 46 +++++++++++++++++++++++++++++++++++
 commands/service/list.go              |  1 +
 graphql/client/client.go              | 23 ++++++++++++------
 graphql/metadata/metadata.go          | 42 ++++++++++++++++++++++++++++++++
 swctl/main.go                         |  6 +++++
 7 files changed, 125 insertions(+), 12 deletions(-)

diff --git a/commands/interceptor/duration.go b/commands/interceptor/duration.go
index 186c3fe..33ea49f 100644
--- a/commands/interceptor/duration.go
+++ b/commands/interceptor/duration.go
@@ -18,6 +18,7 @@
 package interceptor
 
 import (
+	"strconv"
 	"time"
 
 	"github.com/urfave/cli"
@@ -43,8 +44,9 @@ func tryParseTime(unparsed string) (schema.Step, time.Time, error) {
 func DurationInterceptor(ctx *cli.Context) error {
 	start := ctx.String("start")
 	end := ctx.String("end")
+	timezone := ctx.GlobalString("timezone")
 
-	startTime, endTime, step := ParseDuration(start, end)
+	startTime, endTime, step := ParseDuration(start, end, timezone)
 
 	if err := ctx.Set("start", startTime.Format(schema.StepFormats[step])); err != nil {
 		return err
@@ -66,11 +68,20 @@ func DurationInterceptor(ctx *cli.Context) error {
 // NOTE that when either(both) `start` or `end` is(are) given, there is no timezone info
 // in the format, (e.g. 2019-11-09 1001), so they'll be considered as UTC-based,
 // and generate the missing `start`(`end`) based on the same timezone, UTC
-func ParseDuration(start, end string) (startTime, endTime time.Time, step schema.Step) {
-	logger.Log.Debugln("Start time:", start, "end time:", end)
+func ParseDuration(start, end, timezone string) (startTime, endTime time.Time, step schema.Step) {
+	logger.Log.Debugln("Start time:", start, "end time:", end, "timezone:", timezone)
 
 	now := time.Now()
 
+	if timezone != "" {
+		if offset, err := strconv.Atoi(timezone); err == nil {
+			// `offset` is in form of "+1300", while `time.FixedZone` takes offset in seconds
+			now = now.In(time.FixedZone("", offset/100*60*60))
+
+			logger.Log.Debugln("Now:", now, "with server timezone:", timezone)
+		}
+	}
+
 	// both are absent
 	if start == "" && end == "" {
 		return now.Add(-30 * time.Minute), now, schema.StepMinute
diff --git a/commands/interceptor/duration_test.go b/commands/interceptor/duration_test.go
index e8720ae..b4f8c90 100644
--- a/commands/interceptor/duration_test.go
+++ b/commands/interceptor/duration_test.go
@@ -82,7 +82,7 @@ func TestParseDuration(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			gotStartTime, gotEndTime, gotStep := ParseDuration(tt.args.start, tt.args.end)
+			gotStartTime, gotEndTime, gotStep := ParseDuration(tt.args.start, tt.args.end, "")
 			current := gotStartTime.Truncate(time.Minute).Format(timeFormat)
 			spec := tt.wantedStartTime.Truncate(time.Minute).Format(timeFormat)
 			if !reflect.DeepEqual(current, spec) {
diff --git a/commands/interceptor/timezone.go b/commands/interceptor/timezone.go
new file mode 100644
index 0000000..a02cc26
--- /dev/null
+++ b/commands/interceptor/timezone.go
@@ -0,0 +1,46 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package interceptor
+
+import (
+	"strconv"
+
+	"github.com/urfave/cli"
+
+	"github.com/apache/skywalking-cli/graphql/metadata"
+	"github.com/apache/skywalking-cli/logger"
+)
+
+// TimezoneInterceptor sets the server timezone if the server supports the API,
+// otherwise, sets to local timezone
+func TimezoneInterceptor(ctx *cli.Context) error {
+	serverTimeInfo, err := metadata.ServerTimeInfo(ctx)
+
+	if err != nil {
+		logger.Log.Debugf("Failed to get server time info: %v\n", err)
+		return nil
+	}
+
+	if timezone := serverTimeInfo.Timezone; timezone != nil {
+		if _, err := strconv.Atoi(*timezone); err == nil {
+			return ctx.GlobalSet("timezone", *timezone)
+		}
+	}
+
+	return nil
+}
diff --git a/commands/service/list.go b/commands/service/list.go
index feacb89..8400a47 100644
--- a/commands/service/list.go
+++ b/commands/service/list.go
@@ -36,6 +36,7 @@ var ListCommand = cli.Command{
 	Description: "list all services if no <service name> is given, otherwise, only list the given service",
 	Flags:       flags.DurationFlags,
 	Before: interceptor.BeforeChain([]cli.BeforeFunc{
+		interceptor.TimezoneInterceptor,
 		interceptor.DurationInterceptor,
 	}),
 	Action: func(ctx *cli.Context) error {
diff --git a/graphql/client/client.go b/graphql/client/client.go
index d33df92..a0766ef 100644
--- a/graphql/client/client.go
+++ b/graphql/client/client.go
@@ -37,7 +37,14 @@ func newClient(cliCtx *cli.Context) (client *graphql.Client) {
 	return
 }
 
-func executeQuery(cliCtx *cli.Context, request *graphql.Request, response interface{}) {
+func ExecuteQuery(cliCtx *cli.Context, request *graphql.Request, response interface{}) error {
+	client := newClient(cliCtx)
+	ctx := context.Background()
+	err := client.Run(ctx, request, response)
+	return err
+}
+
+func ExecuteQueryOrFail(cliCtx *cli.Context, request *graphql.Request, response interface{}) {
 	client := newClient(cliCtx)
 	ctx := context.Background()
 	if err := client.Run(ctx, request, response); err != nil {
@@ -56,7 +63,7 @@ func Services(cliCtx *cli.Context, duration schema.Duration) []schema.Service {
 	`)
 	request.Var("duration", duration)
 
-	executeQuery(cliCtx, request, &response)
+	ExecuteQueryOrFail(cliCtx, request, &response)
 	return response["services"]
 }
 
@@ -73,7 +80,7 @@ func SearchEndpoints(cliCtx *cli.Context, serviceID, keyword string, limit int)
 	request.Var("keyword", keyword)
 	request.Var("limit", limit)
 
-	executeQuery(cliCtx, request, &response)
+	ExecuteQueryOrFail(cliCtx, request, &response)
 	return response["endpoints"]
 }
 
@@ -88,7 +95,7 @@ func GetEndpointInfo(cliCtx *cli.Context, endpointID string) schema.Endpoint {
 	`)
 	request.Var("endpointId", endpointID)
 
-	executeQuery(cliCtx, request, &response)
+	ExecuteQueryOrFail(cliCtx, request, &response)
 	return response["endpoint"]
 }
 
@@ -111,7 +118,7 @@ func Instances(cliCtx *cli.Context, serviceID string, duration schema.Duration)
 	request.Var("serviceId", serviceID)
 	request.Var("duration", duration)
 
-	executeQuery(cliCtx, request, &response)
+	ExecuteQueryOrFail(cliCtx, request, &response)
 	return response["instances"]
 }
 
@@ -126,7 +133,7 @@ func SearchService(cliCtx *cli.Context, serviceCode string) (service schema.Serv
 	`)
 	request.Var("serviceCode", serviceCode)
 
-	executeQuery(cliCtx, request, &response)
+	ExecuteQueryOrFail(cliCtx, request, &response)
 	service = response["service"]
 	if service.ID == "" {
 		return service, fmt.Errorf("no such service [%s]", serviceCode)
@@ -147,7 +154,7 @@ func LinearIntValues(ctx *cli.Context, condition schema.MetricCondition, duratio
 	request.Var("metric", condition)
 	request.Var("duration", duration)
 
-	executeQuery(ctx, request, &response)
+	ExecuteQueryOrFail(ctx, request, &response)
 
 	values := metricsToMap(duration, response["metrics"].Values)
 
@@ -167,7 +174,7 @@ func IntValues(ctx *cli.Context, condition schema.BatchMetricConditions, duratio
 	request.Var("metric", condition)
 	request.Var("duration", duration)
 
-	executeQuery(ctx, request, &response)
+	ExecuteQueryOrFail(ctx, request, &response)
 
 	return response["metrics"].Values
 }
diff --git a/graphql/metadata/metadata.go b/graphql/metadata/metadata.go
new file mode 100644
index 0000000..55a1bc7
--- /dev/null
+++ b/graphql/metadata/metadata.go
@@ -0,0 +1,42 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package metadata
+
+import (
+	"github.com/machinebox/graphql"
+	"github.com/urfave/cli"
+
+	"github.com/apache/skywalking-cli/graphql/client"
+	"github.com/apache/skywalking-cli/graphql/schema"
+)
+
+func ServerTimeInfo(cliCtx *cli.Context) (schema.TimeInfo, error) {
+	request := graphql.NewRequest(`
+		query {
+			timeInfo: getTimeInfo {
+				timezone, currentTimestamp
+			}
+		}
+	`)
+
+	var response map[string]schema.TimeInfo
+	if err := client.ExecuteQuery(cliCtx, request, &response); err != nil {
+		return schema.TimeInfo{}, err
+	}
+	return response["timeInfo"], nil
+}
diff --git a/swctl/main.go b/swctl/main.go
index 113c2bb..0cf3177 100644
--- a/swctl/main.go
+++ b/swctl/main.go
@@ -60,6 +60,12 @@ func main() {
 			Usage:    "base `url` of the OAP backend graphql",
 			Value:    "http://127.0.0.1:12800/graphql",
 		}),
+		altsrc.NewStringFlag(cli.StringFlag{
+			Name:     "timezone",
+			Required: false,
+			Hidden:   true,
+			Usage:    "the timezone of the server side",
+		}),
 		altsrc.NewBoolFlag(cli.BoolFlag{
 			Name:     "debug",
 			Required: false,