diff --git a/assets/graphqls/alarm/alarms.graphql b/assets/graphqls/alarm/alarms.graphql new file mode 100644 index 00000000..8974fd4f --- /dev/null +++ b/assets/graphqls/alarm/alarms.graphql @@ -0,0 +1,44 @@ +# 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. + +query ($duration: Duration!, $scope: Scope, $keyword: String, $paging: Pagination!, $tags: [AlarmTag]) { + result: getAlarm(duration: $duration, scope: $scope, keyword: $keyword, paging: $paging, tags: $tags) { + total + msgs { + startTime + scope + id + message + tags { + key, value + } + events { + name + source { + service serviceInstance endpoint + } + startTime + endTime + message + parameters { + key value + } + uuid + } + } + } +} diff --git a/cmd/swctl/main.go b/cmd/swctl/main.go index 2f544155..65b7bdbd 100644 --- a/cmd/swctl/main.go +++ b/cmd/swctl/main.go @@ -22,6 +22,7 @@ import ( "os" "runtime" + "github.com/apache/skywalking-cli/internal/commands/alarm" "github.com/apache/skywalking-cli/internal/commands/browser" "github.com/apache/skywalking-cli/internal/commands/completion" "github.com/apache/skywalking-cli/internal/commands/dashboard" @@ -97,6 +98,7 @@ services, service instances, etc.` profile.Command, completion.Command, dependency.Command, + alarm.Command, } app.Before = interceptor.BeforeChain( diff --git a/internal/commands/alarm/alarm.go b/internal/commands/alarm/alarm.go new file mode 100644 index 00000000..ac25b400 --- /dev/null +++ b/internal/commands/alarm/alarm.go @@ -0,0 +1,30 @@ +// 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 alarm + +import ( + "github.com/urfave/cli/v2" +) + +var Command = &cli.Command{ + Name: "alarm", + Usage: "Alarm related sub-command", + Subcommands: []*cli.Command{ + listCommand, + }, +} diff --git a/internal/commands/alarm/list.go b/internal/commands/alarm/list.go new file mode 100644 index 00000000..0419db22 --- /dev/null +++ b/internal/commands/alarm/list.go @@ -0,0 +1,124 @@ +// 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 alarm + +import ( + "fmt" + "strings" + + "github.com/urfave/cli/v2" + + "github.com/apache/skywalking-cli/internal/commands/interceptor" + "github.com/apache/skywalking-cli/internal/flags" + "github.com/apache/skywalking-cli/internal/model" + "github.com/apache/skywalking-cli/pkg/display" + "github.com/apache/skywalking-cli/pkg/display/displayable" + "github.com/apache/skywalking-cli/pkg/graphql/alarm" + + api "skywalking.apache.org/repo/goapi/query" +) + +const DefaultPageSize = 15 + +var listCommand = &cli.Command{ + Name: "list", + Aliases: []string{"ls"}, + Usage: "List alarms", + UsageText: `List alarms + +Examples: +1. List all alarms: +$ swctl alarm list +`, + Flags: flags.Flags( + flags.DurationFlags, + []cli.Flag{ + &cli.StringFlag{ + Name: "keyword", + Usage: "alarm keyword", + Required: false, + }, + &cli.StringFlag{ + Name: "tags", + Usage: "`tags` of the alarm, in form of `key=value,key=value`", + Required: false, + }, + &cli.GenericFlag{ + Name: "scope", + Usage: "the `scope` of the alarm entity", + Value: &model.ScopeEnumValue{ + Enum: api.AllScope, + Default: "", + Selected: "", + }, + }, + }, + ), + Before: interceptor.BeforeChain( + interceptor.DurationInterceptor, + ), + Action: func(ctx *cli.Context) error { + start := ctx.String("start") + end := ctx.String("end") + step := ctx.Generic("step") + + keyword := ctx.String("keyword") + tagStr := ctx.String("tags") + scope := ctx.Generic("scope").(*model.ScopeEnumValue).Selected + + duration := api.Duration{ + Start: start, + End: end, + Step: step.(*model.StepEnumValue).Selected, + } + + var tags []*api.AlarmTag + if tagStr != "" { + tagArr := strings.Split(tagStr, ",") + for _, tag := range tagArr { + kv := strings.SplitN(tag, "=", 2) + if len(kv) != 2 { + return fmt.Errorf("invalid tag, cannot be splitted into 2 parts. %s", tag) + } + tags = append(tags, &api.AlarmTag{Key: kv[0], Value: &kv[1]}) + } + } + + pageNum := 1 + needTotal := true + paging := api.Pagination{ + PageNum: &pageNum, + PageSize: DefaultPageSize, + NeedTotal: &needTotal, + } + + condition := &alarm.ListAlarmCondition{ + Duration: &duration, + Keyword: keyword, + Scope: scope, + Tags: tags, + Paging: &paging, + } + alarms, err := alarm.Alarms(ctx, condition) + if err != nil { + return err + } + + return display.Display(ctx, &displayable.Displayable{Data: alarms, Condition: condition}) + }, +} diff --git a/pkg/graphql/alarm/alarm.go b/pkg/graphql/alarm/alarm.go new file mode 100644 index 00000000..3ac6defa --- /dev/null +++ b/pkg/graphql/alarm/alarm.go @@ -0,0 +1,54 @@ +// 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 alarm + +import ( + "github.com/apache/skywalking-cli/assets" + "github.com/apache/skywalking-cli/pkg/graphql/client" + + "github.com/machinebox/graphql" + + "github.com/urfave/cli/v2" + + api "skywalking.apache.org/repo/goapi/query" +) + +type ListAlarmCondition struct { + Duration *api.Duration + Keyword string + Scope api.Scope + Tags []*api.AlarmTag + Paging *api.Pagination +} + +func Alarms(ctx *cli.Context, condition *ListAlarmCondition) (api.Alarms, error) { + var response map[string]api.Alarms + + request := graphql.NewRequest(assets.Read("graphqls/alarm/alarms.graphql")) + request.Var("paging", condition.Paging) + request.Var("tags", condition.Tags) + request.Var("duration", condition.Duration) + request.Var("keyword", condition.Keyword) + if condition.Scope != "" { + request.Var("scope", condition.Scope) + } + + err := client.ExecuteQuery(ctx, request, &response) + + return response["result"], err +}