From 62faeffd4d229b9e6ef7d699075970e4987e9a74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frederik=20Jerl=C3=B8v?= Date: Tue, 26 Mar 2024 13:03:20 +0100 Subject: [PATCH] Aggregate alerts --- api/aggregate-alerts.go | 242 ++++++++++++++++++ api/error.go | 8 + api/internal/humiographql/action.go | 7 + api/internal/humiographql/aggregate-alerts.go | 54 ++++ api/internal/humiographql/filter-alerts.go | 4 - .../humiographql/query-timestamp-type.go | 8 + api/internal/humiographql/trigger-mode.go | 8 + cmd/humioctl/aggregate_alerts.go | 34 +++ cmd/humioctl/aggregate_alerts_export.go | 67 +++++ cmd/humioctl/aggregate_alerts_install.go | 79 ++++++ cmd/humioctl/aggregate_alerts_list.go | 60 +++++ cmd/humioctl/aggregate_alerts_remove.go | 57 +++++ cmd/humioctl/aggregate_alerts_show.go | 70 +++++ cmd/humioctl/root.go | 1 + 14 files changed, 695 insertions(+), 4 deletions(-) create mode 100644 api/aggregate-alerts.go create mode 100644 api/internal/humiographql/action.go create mode 100644 api/internal/humiographql/aggregate-alerts.go create mode 100644 api/internal/humiographql/query-timestamp-type.go create mode 100644 api/internal/humiographql/trigger-mode.go create mode 100644 cmd/humioctl/aggregate_alerts.go create mode 100644 cmd/humioctl/aggregate_alerts_export.go create mode 100644 cmd/humioctl/aggregate_alerts_install.go create mode 100644 cmd/humioctl/aggregate_alerts_list.go create mode 100644 cmd/humioctl/aggregate_alerts_remove.go create mode 100644 cmd/humioctl/aggregate_alerts_show.go diff --git a/api/aggregate-alerts.go b/api/aggregate-alerts.go new file mode 100644 index 0000000..e4609fc --- /dev/null +++ b/api/aggregate-alerts.go @@ -0,0 +1,242 @@ +package api + +import ( + "fmt" + graphql "github.com/cli/shurcooL-graphql" + "github.com/humio/cli/api/internal/humiographql" +) + +type AggregateAlert struct { + ID string `graphql:"id" yaml:"-" json:"id"` + Name string `graphql:"name" yaml:"name" json:"name"` + Description string `graphql:"description" yaml:"description,omitempty" json:"description,omitempty"` + QueryString string `graphql:"queryString" yaml:"queryString" json:"queryString"` + SearchIntervalSeconds int `graphql:"searchIntervalSeconds" yaml:"searchIntervalSeconds" json:"searchIntervalSeconds"` + ActionNames []string `graphql:"actionNames" yaml:"actionNames" json:"actionNames"` + Labels []string `graphql:"labels" yaml:"labels" json:"labels"` + Enabled bool `graphql:"enabled" yaml:"enabled" json:"enabled"` + ThrottleField string `graphql:"throttleField" yaml:"throttleField" json:"throttleField"` + ThrottleTimeSeconds int `graphql:"throttleTimeSeconds" yaml:"throttleTimeSeconds" json:"throttleTimeSeconds"` + QueryOwnershipType string `graphql:"queryOwnership" yaml:"queryOwnershipType" json:"queryOwnershipType"` + TriggerMode string `graphql:"triggerMode" yaml:"triggerMode" json:"triggerMode"` + QueryTimestampType string `graphql:"queryTimestampType" yaml:"queryTimestampType" json:"queryTimestampType"` + RunAsUserID string `graphql:"runAsUserId" yaml:"runAsUserId,omitempty" json:"runAsUserId,omitempty"` +} + +type AggregateAlerts struct { + client *Client +} + +func (c *Client) AggregateAlerts() *AggregateAlerts { return &AggregateAlerts{client: c} } + +func (a *AggregateAlerts) List(viewName string) ([]AggregateAlert, error) { + var query struct { + SearchDomain struct { + AggregateAlerts []humiographql.AggregateAlert `graphql:"aggregateAlerts"` + } `graphql:"searchDomain(name: $viewName)"` + } + + variables := map[string]any{ + "viewName": graphql.String(viewName), + } + + err := a.client.Query(&query, variables) + if err != nil { + return nil, err + } + + var aggregateAlerts = make([]AggregateAlert, len(query.SearchDomain.AggregateAlerts)) + for i := range query.SearchDomain.AggregateAlerts { + aggregateAlerts[i] = mapHumioGraphqlAggregateAlertToAggregateAlert(query.SearchDomain.AggregateAlerts[i]) + } + + return aggregateAlerts, err +} + +func (a *AggregateAlerts) Update(viewName string, updatedAggregateAlert *AggregateAlert) (*AggregateAlert, error) { + if updatedAggregateAlert == nil { + return nil, fmt.Errorf("updatedAggregateAlert must not be nil") + } + + if updatedAggregateAlert.ID == "" { + return nil, fmt.Errorf("updatedAggregateAlert must have non-empty ID") + } + + var mutation struct { + humiographql.AggregateAlert `graphql:"updateAggregateAlert(input: $input)"` + } + + actionNames := make([]graphql.String, len(updatedAggregateAlert.ActionNames)) + for i, actionName := range updatedAggregateAlert.ActionNames { + actionNames[i] = graphql.String(actionName) + } + + labels := make([]graphql.String, len(updatedAggregateAlert.Labels)) + for i, label := range updatedAggregateAlert.Labels { + labels[i] = graphql.String(label) + } + + updateAlert := humiographql.UpdateAggregateAlert{ + ViewName: humiographql.RepoOrViewName(viewName), + ID: graphql.String(updatedAggregateAlert.ID), + Name: graphql.String(updatedAggregateAlert.Name), + Description: graphql.String(updatedAggregateAlert.Description), + QueryString: graphql.String(updatedAggregateAlert.QueryString), + SearchIntervalSeconds: humiographql.Long(updatedAggregateAlert.SearchIntervalSeconds), + ActionIdsOrNames: actionNames, + Labels: labels, + Enabled: graphql.Boolean(updatedAggregateAlert.Enabled), + RunAsUserID: graphql.String(updatedAggregateAlert.RunAsUserID), + ThrottleField: graphql.String(updatedAggregateAlert.ThrottleField), + ThrottleTimeSeconds: humiographql.Long(updatedAggregateAlert.ThrottleTimeSeconds), + TriggerMode: humiographql.TriggerMode(updatedAggregateAlert.TriggerMode), + QueryTimestampType: humiographql.QueryTimestampType(updatedAggregateAlert.QueryTimestampType), + QueryOwnershipType: humiographql.QueryOwnershipType(updatedAggregateAlert.QueryOwnershipType), + } + + variables := map[string]any{ + "input": updateAlert, + } + + err := a.client.Mutate(&mutation, variables) + if err != nil { + return nil, err + } + + aggregateAlert := mapHumioGraphqlAggregateAlertToAggregateAlert(mutation.AggregateAlert) + + return &aggregateAlert, nil +} + +func (a *AggregateAlerts) Create(viewName string, newAggregateAlert *AggregateAlert) (*AggregateAlert, error) { + if newAggregateAlert == nil { + return nil, fmt.Errorf("newAggregateAlert must not be nil") + } + + var mutation struct { + humiographql.AggregateAlert `graphql:"createAggregateAlert(input: $input)"` + } + + actionNames := make([]graphql.String, len(newAggregateAlert.ActionNames)) + for i, actionName := range newAggregateAlert.ActionNames { + actionNames[i] = graphql.String(actionName) + } + + labels := make([]graphql.String, len(newAggregateAlert.Labels)) + for i, label := range newAggregateAlert.Labels { + labels[i] = graphql.String(label) + } + + createAggregateAlert := humiographql.CreateAggregateAlert{ + ViewName: humiographql.RepoOrViewName(viewName), + Name: graphql.String(newAggregateAlert.Name), + Description: graphql.String(newAggregateAlert.Description), + QueryString: graphql.String(newAggregateAlert.QueryString), + SearchIntervalSeconds: humiographql.Long(newAggregateAlert.SearchIntervalSeconds), + ActionIdsOrNames: actionNames, + Labels: labels, + Enabled: graphql.Boolean(newAggregateAlert.Enabled), + ThrottleField: graphql.String(newAggregateAlert.ThrottleField), + ThrottleTimeSeconds: humiographql.Long(newAggregateAlert.ThrottleTimeSeconds), + RunAsUserID: graphql.String(newAggregateAlert.RunAsUserID), + TriggerMode: humiographql.TriggerMode(newAggregateAlert.TriggerMode), + QueryTimestampType: humiographql.QueryTimestampType(newAggregateAlert.QueryTimestampType), + QueryOwnershipType: humiographql.QueryOwnershipType(newAggregateAlert.QueryOwnershipType), + } + + variables := map[string]any{ + "input": createAggregateAlert, + } + + err := a.client.Mutate(&mutation, variables) + if err != nil { + return nil, err + } + + aggregateAlert := mapHumioGraphqlAggregateAlertToAggregateAlert(mutation.AggregateAlert) + + return &aggregateAlert, nil +} + +func (a *AggregateAlerts) Delete(viewName, aggregateAlertID string) error { + if aggregateAlertID == "" { + return fmt.Errorf("aggregateAlertID is empty") + } + + var mutation struct { + DidDelete bool `graphql:"deleteAggregateAlert(input: { viewName: $viewName, id: $id })"` + } + + variables := map[string]any{ + "viewName": humiographql.RepoOrViewName(viewName), + "id": graphql.String(aggregateAlertID), + } + + err := a.client.Mutate(&mutation, variables) + + if !mutation.DidDelete { + return fmt.Errorf("unable to remove aggregate alert in repo/view '%s' with id '%s'", viewName, aggregateAlertID) + } + + return err +} + +func (a *AggregateAlerts) Get(viewName string, aggregateAlertID string) (*AggregateAlert, error) { + var query struct { + SearchDomain struct { + AggregateAlert humiographql.AggregateAlert `graphql:"aggregateAlert(id: $aggregateAlertId)"` + } `graphql:"searchDomain(name: $viewName) "` + } + + variables := map[string]any{ + "viewName": graphql.String(viewName), + "aggregateAlertId": graphql.String(aggregateAlertID), + } + + err := a.client.Query(&query, variables) + if err != nil { + return nil, err + } + + aggregateAlert := mapHumioGraphqlAggregateAlertToAggregateAlert(query.SearchDomain.AggregateAlert) + + return &aggregateAlert, nil +} + +func mapHumioGraphqlAggregateAlertToAggregateAlert(input humiographql.AggregateAlert) AggregateAlert { + var queryOwnershipType, runAsUserID string + switch input.QueryOwnership.QueryOwnershipTypeName { + case humiographql.QueryOwnershipTypeNameOrganization: + queryOwnershipType = QueryOwnershipTypeOrganization + case humiographql.QueryOwnershipTypeNameUser: + queryOwnershipType = QueryOwnershipTypeUser + runAsUserID = string(input.QueryOwnership.ID) + } + + var actionNames = make([]string, len(input.Actions)) + for i := range input.Actions { + actionNames[i] = string(input.Actions[i].Name) + } + + var labels = make([]string, len(input.Labels)) + for i := range input.Labels { + labels[i] = string(input.Labels[i]) + } + + return AggregateAlert{ + ID: string(input.ID), + Name: string(input.Name), + Description: string(input.Description), + QueryString: string(input.QueryString), + SearchIntervalSeconds: int(input.SearchIntervalSeconds), + ActionNames: actionNames, + Labels: labels, + Enabled: bool(input.Enabled), + ThrottleField: string(input.ThrottleField), + ThrottleTimeSeconds: int(input.ThrottleTimeSeconds), + QueryOwnershipType: queryOwnershipType, + TriggerMode: string(input.TriggerMode), + QueryTimestampType: string(input.QueryTimestampType), + RunAsUserID: runAsUserID, + } +} diff --git a/api/error.go b/api/error.go index 258d35d..10f0bd1 100644 --- a/api/error.go +++ b/api/error.go @@ -12,6 +12,7 @@ const ( EntityTypeAlert EntityType = "alert" EntityTypeFilterAlert EntityType = "filter-alert" EntityTypeScheduledSearch EntityType = "scheduled-search" + EntityTypeAggregateAlert EntityType = "aggregate-alert" ) func (e EntityType) String() string { @@ -68,4 +69,11 @@ func ScheduledSearchNotFound(name string) error { entityType: EntityTypeScheduledSearch, key: name, } +} + +func AggregateAlertNotFound(name string) error { + return EntityNotFound{ + entityType: EntityTypeAggregateAlert, + key: name, + } } \ No newline at end of file diff --git a/api/internal/humiographql/action.go b/api/internal/humiographql/action.go new file mode 100644 index 0000000..7dfb32b --- /dev/null +++ b/api/internal/humiographql/action.go @@ -0,0 +1,7 @@ +package humiographql + +import graphql "github.com/cli/shurcooL-graphql" + +type Action struct { + Name graphql.String `graphql:"name"` +} diff --git a/api/internal/humiographql/aggregate-alerts.go b/api/internal/humiographql/aggregate-alerts.go new file mode 100644 index 0000000..c915a49 --- /dev/null +++ b/api/internal/humiographql/aggregate-alerts.go @@ -0,0 +1,54 @@ +package humiographql + +import graphql "github.com/cli/shurcooL-graphql" + +type AggregateAlert struct { + ID graphql.String `json:"id"` + Name graphql.String `json:"name"` + Description graphql.String `json:"description,omitempty"` + QueryString graphql.String `json:"queryString"` + SearchIntervalSeconds Long `json:"searchIntervalSeconds"` + ThrottleTimeSeconds Long `json:"throttleTimeSeconds"` + ThrottleField graphql.String `json:"throttleField,omitempty"` + Actions []Action `json:"actionIdsOrNames"` + Labels []graphql.String `json:"labels"` + Enabled graphql.Boolean `json:"enabled"` + QueryOwnership QueryOwnership `json:"queryOwnershipType"` + TriggerMode TriggerMode `json:"triggerMode"` + QueryTimestampType QueryTimestampType `json:"queryTimestampType"` +} + +type CreateAggregateAlert struct { + ViewName RepoOrViewName `json:"viewName"` + Name graphql.String `json:"name"` + Description graphql.String `json:"description,omitempty"` + QueryString graphql.String `json:"queryString"` + SearchIntervalSeconds Long `json:"searchIntervalSeconds"` + ThrottleTimeSeconds Long `json:"throttleTimeSeconds"` + ThrottleField graphql.String `json:"throttleField,omitempty"` + ActionIdsOrNames []graphql.String `json:"actionIdsOrNames"` + Labels []graphql.String `json:"labels"` + Enabled graphql.Boolean `json:"enabled"` + RunAsUserID graphql.String `json:"runAsUserId,omitempty"` + QueryOwnershipType QueryOwnershipType `json:"queryOwnershipType"` + TriggerMode TriggerMode `json:"triggerMode,omitempty"` + QueryTimestampType QueryTimestampType `json:"queryTimestampType"` +} + +type UpdateAggregateAlert struct { + ViewName RepoOrViewName `json:"viewName"` + ID graphql.String `json:"id"` + Name graphql.String `json:"name"` + Description graphql.String `json:"description,omitempty"` + QueryString graphql.String `json:"queryString"` + SearchIntervalSeconds Long `json:"searchIntervalSeconds"` + ThrottleTimeSeconds Long `json:"throttleTimeSeconds"` + ThrottleField graphql.String `json:"throttleField,omitempty"` + ActionIdsOrNames []graphql.String `json:"actionIdsOrNames"` + Labels []graphql.String `json:"labels"` + Enabled graphql.Boolean `json:"enabled"` + RunAsUserID graphql.String `json:"runAsUserId,omitempty"` + QueryOwnershipType QueryOwnershipType `json:"queryOwnershipType"` + TriggerMode TriggerMode `json:"triggerMode"` + QueryTimestampType QueryTimestampType `json:"queryTimestampType"` +} diff --git a/api/internal/humiographql/filter-alerts.go b/api/internal/humiographql/filter-alerts.go index 99eb9b7..b4c413f 100644 --- a/api/internal/humiographql/filter-alerts.go +++ b/api/internal/humiographql/filter-alerts.go @@ -17,10 +17,6 @@ type FilterAlert struct { QueryOwnership QueryOwnership `graphql:"queryOwnership"` } -type Action struct { - Name graphql.String `graphql:"name"` -} - type CreateFilterAlert struct { ViewName RepoOrViewName `json:"viewName"` Name graphql.String `json:"name"` diff --git a/api/internal/humiographql/query-timestamp-type.go b/api/internal/humiographql/query-timestamp-type.go new file mode 100644 index 0000000..b0fb044 --- /dev/null +++ b/api/internal/humiographql/query-timestamp-type.go @@ -0,0 +1,8 @@ +package humiographql + +type QueryTimestampType string + +const ( + QueryTimestampTypeIngestTimestamp QueryTimestampType = "IngestTimestamp" + QueryTimestampTypeEventTimestamp QueryTimestampType = "EventTimestamp" +) diff --git a/api/internal/humiographql/trigger-mode.go b/api/internal/humiographql/trigger-mode.go new file mode 100644 index 0000000..c0e7163 --- /dev/null +++ b/api/internal/humiographql/trigger-mode.go @@ -0,0 +1,8 @@ +package humiographql + +type TriggerMode string + +const ( + TriggerModeCompleteMode TriggerMode = "CompleteMode" + TriggerModeImmediateMode TriggerMode = "ImmediateMode" +) diff --git a/cmd/humioctl/aggregate_alerts.go b/cmd/humioctl/aggregate_alerts.go new file mode 100644 index 0000000..f5d69c1 --- /dev/null +++ b/cmd/humioctl/aggregate_alerts.go @@ -0,0 +1,34 @@ +// Copyright © 2024 CrowdStrike +// +// Licensed 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 main + +import ( + "github.com/spf13/cobra" +) + +func newAggregateAlertsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "aggregate-alerts", + Short: "Manage aggregate alerts", + } + + cmd.AddCommand(newAggregateAlertsListCmd()) + cmd.AddCommand(newAggregateAlertsInstallCmd()) + cmd.AddCommand(newAggregateAlertsExportCmd()) + cmd.AddCommand(newAggregateAlertsRemoveCmd()) + cmd.AddCommand(newAggregateAlertsShowCmd()) + + return cmd +} diff --git a/cmd/humioctl/aggregate_alerts_export.go b/cmd/humioctl/aggregate_alerts_export.go new file mode 100644 index 0000000..a0fa09c --- /dev/null +++ b/cmd/humioctl/aggregate_alerts_export.go @@ -0,0 +1,67 @@ +// Copyright © 2024 CrowdStrike +// +// Licensed 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 main + +import ( + "github.com/humio/cli/api" + "os" + + "github.com/spf13/cobra" + "gopkg.in/yaml.v2" +) + +func newAggregateAlertsExportCmd() *cobra.Command { + var outputName string + + cmd := cobra.Command{ + Use: "export [flags] ", + Short: "Export an aggregate alert in to a file.", + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + view := args[0] + aggregateAlertName := args[1] + client := NewApiClient(cmd) + + if outputName == "" { + outputName = aggregateAlertName + } + + aggregateAlerts, err := client.AggregateAlerts().List(view) + exitOnError(cmd, err, "Could not list aggregate alerts") + + var aggregateAlert api.AggregateAlert + for _, fa := range aggregateAlerts { + if fa.Name == aggregateAlertName { + aggregateAlert = fa + } + } + + if aggregateAlert.ID == "" { + exitOnError(cmd, api.AggregateAlertNotFound(aggregateAlertName), "Could not find aggregate alert") + } + + yamlData, err := yaml.Marshal(&aggregateAlert) + exitOnError(cmd, err, "Failed to serialize the aggregate alert") + + outFilePath := outputName + ".yaml" + err = os.WriteFile(outFilePath, yamlData, 0600) + exitOnError(cmd, err, "Error saving the aggregate alert file") + }, + } + + cmd.Flags().StringVarP(&outputName, "output", "o", "", "The file path where the aggregate alert should be written. Defaults to ./.yaml") + + return &cmd +} diff --git a/cmd/humioctl/aggregate_alerts_install.go b/cmd/humioctl/aggregate_alerts_install.go new file mode 100644 index 0000000..80b860f --- /dev/null +++ b/cmd/humioctl/aggregate_alerts_install.go @@ -0,0 +1,79 @@ +// Copyright © 2024 CrowdStrike +// +// Licensed 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 main + +import ( + "fmt" + "os" + + "github.com/humio/cli/api" + "github.com/spf13/cobra" + "gopkg.in/yaml.v2" +) + +func newAggregateAlertsInstallCmd() *cobra.Command { + var ( + filePath, url string + ) + + cmd := cobra.Command{ + Use: "install [flags] ", + Short: "Installs an aggregate alert in a view", + Long: `Install an aggregate alert from a URL or from a local file. + +The install command allows you to install aggregate alerts from a URL or from a local file, e.g. + + $ humioctl aggregate-alerts install viewName --url=https://example.com/acme/aggregate-alert.yaml + + $ humioctl aggregate-alerts install viewName --file=./aggregate-alert.yaml +`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + var content []byte + var err error + + // Check that we got the right number of argument + // if we only got you must supply --file or --url. + if l := len(args); l == 1 { + if filePath != "" { + content, err = getBytesFromFile(filePath) + } else if url != "" { + content, err = getBytesFromURL(url) + } else { + cmd.Printf("You must specify a path using --file or --url\n") + os.Exit(1) + } + } + exitOnError(cmd, err, "Could to load the aggregate alert") + + client := NewApiClient(cmd) + viewName := args[0] + + var aggregateAlert api.AggregateAlert + err = yaml.Unmarshal(content, &aggregateAlert) + exitOnError(cmd, err, "Could not unmarshal the aggregate alert") + + _, err = client.AggregateAlerts().Create(viewName, &aggregateAlert) + exitOnError(cmd, err, "Could not create the aggregate alert") + + fmt.Fprintln(cmd.OutOrStdout(), "Aggregate alert created") + }, + } + + cmd.Flags().StringVar(&filePath, "file", "", "The local file path to the aggregate alert to install.") + cmd.Flags().StringVar(&url, "url", "", "A URL to fetch the aggregate alert file from.") + cmd.MarkFlagsMutuallyExclusive("file", "url") + return &cmd +} diff --git a/cmd/humioctl/aggregate_alerts_list.go b/cmd/humioctl/aggregate_alerts_list.go new file mode 100644 index 0000000..1927450 --- /dev/null +++ b/cmd/humioctl/aggregate_alerts_list.go @@ -0,0 +1,60 @@ +// Copyright © 2024 CrowdStrike +// +// Licensed 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 main + +import ( + "github.com/humio/cli/cmd/internal/format" + "github.com/spf13/cobra" + "strings" +) + +func newAggregateAlertsListCmd() *cobra.Command { + cmd := cobra.Command{ + Use: "list ", + Short: "List all aggregate alerts in a view.", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + view := args[0] + client := NewApiClient(cmd) + + aggregateAlerts, err := client.AggregateAlerts().List(view) + exitOnError(cmd, err, "Error fetching aggregate alerts") + + var rows = make([][]format.Value, len(aggregateAlerts)) + for i := range aggregateAlerts { + aggregateAlert := aggregateAlerts[i] + rows[i] = []format.Value{ + format.String(aggregateAlert.ID), + format.String(aggregateAlert.Name), + format.String(aggregateAlert.Description), + format.String(strings.Join(aggregateAlert.ActionNames, ", ")), + format.String(strings.Join(aggregateAlert.Labels, ", ")), + format.Bool(aggregateAlert.Enabled), + format.String(aggregateAlert.ThrottleField), + format.Int(aggregateAlert.ThrottleTimeSeconds), + format.Int(aggregateAlert.SearchIntervalSeconds), + format.String(aggregateAlert.QueryTimestampType), + format.String(aggregateAlert.TriggerMode), + format.String(aggregateAlert.RunAsUserID), + format.String(aggregateAlert.QueryOwnershipType), + } + } + + printOverviewTable(cmd, []string{"ID", "Name", "Description", "Action Names", "Labels", "Enabled", "Throttle Field", "Throttle Time Seconds", "Search Interval Seconds", "Query Timestamp Type", "Trigger Mode", "Run As UserID", "Query Ownership Type"}, rows) + }, + } + + return &cmd +} diff --git a/cmd/humioctl/aggregate_alerts_remove.go b/cmd/humioctl/aggregate_alerts_remove.go new file mode 100644 index 0000000..6c63b41 --- /dev/null +++ b/cmd/humioctl/aggregate_alerts_remove.go @@ -0,0 +1,57 @@ +// Copyright © 2024 CrowdStrike +// +// Licensed 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 main + +import ( + "fmt" + "github.com/humio/cli/api" + + "github.com/spf13/cobra" +) + +func newAggregateAlertsRemoveCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "remove ", + Short: "Removes an aggregate alert.", + Long: `Removes the aggregate alert with name '' in the view with name ''.`, + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + viewName := args[0] + aggregateAlertName := args[1] + client := NewApiClient(cmd) + + aggregateAlerts, err := client.AggregateAlerts().List(viewName) + exitOnError(cmd, err, "Could not list aggregate alerts") + + var aggregateAlert api.AggregateAlert + for _, fa := range aggregateAlerts { + if fa.Name == aggregateAlertName { + aggregateAlert = fa + } + } + + if aggregateAlert.ID == "" { + exitOnError(cmd, api.AggregateAlertNotFound(aggregateAlertName), "Could not find aggregate alert") + } + + err = client.AggregateAlerts().Delete(viewName, aggregateAlert.ID) + exitOnError(cmd, err, "Could not remove alert") + + fmt.Fprintf(cmd.OutOrStdout(), "Successfully removed aggregate alert %q from view %q\n", aggregateAlertName, viewName) + }, + } + + return cmd +} diff --git a/cmd/humioctl/aggregate_alerts_show.go b/cmd/humioctl/aggregate_alerts_show.go new file mode 100644 index 0000000..4e1eff9 --- /dev/null +++ b/cmd/humioctl/aggregate_alerts_show.go @@ -0,0 +1,70 @@ +// Copyright © 2024 CrowdStrike +// +// Licensed 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 main + +import ( + "github.com/humio/cli/api" + "github.com/humio/cli/cmd/internal/format" + "github.com/spf13/cobra" + "strings" +) + +func newAggregateAlertsShowCmd() *cobra.Command { + cmd := cobra.Command{ + Use: "show ", + Short: "Show details about an aggregate alert in a view.", + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + view := args[0] + name := args[1] + client := NewApiClient(cmd) + + aggregateAlerts, err := client.AggregateAlerts().List(view) + exitOnError(cmd, err, "Could not list aggregate alert") + + var aggregateAlert api.AggregateAlert + for _, fa := range aggregateAlerts { + if fa.Name == name { + aggregateAlert = fa + } + } + + if aggregateAlert.ID == "" { + exitOnError(cmd, api.AggregateAlertNotFound(name), "Could not find aggregate alert") + } + + details := [][]format.Value{ + {format.String("ID"), format.String(aggregateAlert.ID)}, + {format.String("Name"), format.String(aggregateAlert.Name)}, + {format.String("Description"), format.String(aggregateAlert.Description)}, + {format.String("Query String"), format.String(aggregateAlert.QueryString)}, + {format.String("Search Interval Seconds"), format.Int(aggregateAlert.SearchIntervalSeconds)}, + {format.String("Actions"), format.String(strings.Join(aggregateAlert.ActionNames, ", "))}, + {format.String("Labels"), format.String(strings.Join(aggregateAlert.Labels, ", "))}, + {format.String("Enabled"), format.Bool(aggregateAlert.Enabled)}, + {format.String("Throttle Field"), format.String(aggregateAlert.ThrottleField)}, + {format.String("Throttle Time Seconds"), format.Int(aggregateAlert.ThrottleTimeSeconds)}, + {format.String("Query Timestamp Type"), format.String(aggregateAlert.QueryTimestampType)}, + {format.String("Trigger Mode"), format.String(aggregateAlert.TriggerMode)}, + {format.String("Run As User ID"), format.String(aggregateAlert.RunAsUserID)}, + {format.String("Query Ownership Type"), format.String(aggregateAlert.QueryOwnershipType)}, + } + + printDetailsTable(cmd, details) + }, + } + + return &cmd +} diff --git a/cmd/humioctl/root.go b/cmd/humioctl/root.go index 912909a..8ebef86 100644 --- a/cmd/humioctl/root.go +++ b/cmd/humioctl/root.go @@ -123,6 +123,7 @@ Common Management Commands: rootCmd.AddCommand(newAlertsCmd()) rootCmd.AddCommand(newFilterAlertsCmd()) rootCmd.AddCommand(newScheduledSearchesCmd()) + rootCmd.AddCommand(newAggregateAlertsCmd()) rootCmd.AddCommand(newPackagesCmd()) rootCmd.AddCommand(newGroupsCmd()) rootCmd.AddCommand(newFilesCmd())