diff --git a/api/alerts.go b/api/alerts.go index 7b6a51d..95dd423 100644 --- a/api/alerts.go +++ b/api/alerts.go @@ -20,7 +20,7 @@ type Alert struct { Actions []string `graphql:"actions" yaml:"actions" json:"actions"` Labels []string `graphql:"labels" yaml:"labels,omitempty" json:"labels,omitempty"` LastError string `graphql:"lastError" yaml:"lastError" json:"lastError"` - RunAsUserID string `graphql:"runAsUserId" yaml:"runAsUserId,omitempty" json:"runAsUserID,omitempty"` + RunAsUserID string `graphql:"runAsUserId" yaml:"runAsUserId,omitempty" json:"runAsUserId,omitempty"` QueryOwnershipType string `graphql:"queryOwnershipType" yaml:"queryOwnershipType,omitempty" json:"queryOwnershipType,omitempty"` } diff --git a/api/error.go b/api/error.go index 09ff5d7..75dab0e 100644 --- a/api/error.go +++ b/api/error.go @@ -1,13 +1,16 @@ package api -import "fmt" +import ( + "fmt" +) type EntityType string const ( - EntityTypeParser EntityType = "parser" - EntityTypeAction EntityType = "action" - EntityTypeAlert EntityType = "alert" + EntityTypeParser EntityType = "parser" + EntityTypeAction EntityType = "action" + EntityTypeAlert EntityType = "alert" + EntityTypeFilterAlert EntityType = "filter-alert" ) func (e EntityType) String() string { @@ -51,3 +54,10 @@ func AlertNotFound(name string) error { key: name, } } + +func FilterAlertNotFound(name string) error { + return EntityNotFound{ + entityType: EntityTypeFilterAlert, + key: name, + } +} diff --git a/api/filter-alerts.go b/api/filter-alerts.go new file mode 100644 index 0000000..6e80ed3 --- /dev/null +++ b/api/filter-alerts.go @@ -0,0 +1,222 @@ +package api + +import ( + "fmt" + graphql "github.com/cli/shurcooL-graphql" + "github.com/humio/cli/api/internal/humiographql" +) + +type FilterAlert 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"` + ActionNames []string `graphql:"actionNames" yaml:"actionNames" json:"actionNames"` + Labels []string `graphql:"labels" yaml:"labels" json:"labels"` + Enabled bool `graphql:"enabled" yaml:"enabled" json:"enabled"` + QueryOwnershipType string `graphql:"queryOwnership" yaml:"queryOwnershipType" json:"queryOwnershipType"` + RunAsUserID string `graphql:"runAsUserId" yaml:"runAsUserId,omitempty" json:"runAsUserId,omitempty"` +} + +type FilterAlerts struct { + client *Client +} + +func (c *Client) FilterAlerts() *FilterAlerts { return &FilterAlerts{client: c} } + +func (fa *FilterAlerts) List(viewName string) ([]FilterAlert, error) { + var query struct { + SearchDomain struct { + FilterAlerts []humiographql.FilterAlert `graphql:"filterAlerts"` + } `graphql:"searchDomain(name: $viewName)"` + } + + variables := map[string]any{ + "viewName": graphql.String(viewName), + } + + err := fa.client.Query(&query, variables) + if err != nil { + return nil, err + } + + var filterAlerts = make([]FilterAlert, len(query.SearchDomain.FilterAlerts)) + for i := range query.SearchDomain.FilterAlerts { + filterAlerts[i] = mapHumioGraphqlFilterAlertToFilterAlert(query.SearchDomain.FilterAlerts[i]) + } + + return filterAlerts, err +} + +func (fa *FilterAlerts) Update(viewName string, updatedFilterAlert *FilterAlert) (*FilterAlert, error) { + if updatedFilterAlert == nil { + return nil, fmt.Errorf("updatedFilterAlert must not be nil") + } + + if updatedFilterAlert.ID == "" { + return nil, fmt.Errorf("updatedFilterAlert must have non-empty ID") + } + + var mutation struct { + humiographql.FilterAlert `graphql:"updateFilterAlert(input: $input)"` + } + + actionNames := make([]graphql.String, len(updatedFilterAlert.ActionNames)) + for i, actionName := range updatedFilterAlert.ActionNames { + actionNames[i] = graphql.String(actionName) + } + + labels := make([]graphql.String, len(updatedFilterAlert.Labels)) + for i, label := range updatedFilterAlert.Labels { + labels[i] = graphql.String(label) + } + + updateAlert := humiographql.UpdateFilterAlert{ + ViewName: humiographql.RepoOrViewName(viewName), + ID: graphql.String(updatedFilterAlert.ID), + Name: graphql.String(updatedFilterAlert.Name), + Description: graphql.String(updatedFilterAlert.Description), + QueryString: graphql.String(updatedFilterAlert.QueryString), + ActionIdsOrNames: actionNames, + Labels: labels, + Enabled: graphql.Boolean(updatedFilterAlert.Enabled), + RunAsUserID: graphql.String(updatedFilterAlert.RunAsUserID), + QueryOwnershipType: humiographql.QueryOwnershipType(updatedFilterAlert.QueryOwnershipType), + } + + variables := map[string]any{ + "input": updateAlert, + } + + err := fa.client.Mutate(&mutation, variables) + if err != nil { + return nil, err + } + + filterAlert := mapHumioGraphqlFilterAlertToFilterAlert(mutation.FilterAlert) + + return &filterAlert, nil +} + +func (fa *FilterAlerts) Create(viewName string, newFilterAlert *FilterAlert) (*FilterAlert, error) { + if newFilterAlert == nil { + return nil, fmt.Errorf("newFilterAlert must not be nil") + } + + var mutation struct { + humiographql.FilterAlert `graphql:"createFilterAlert(input: $input)"` + } + + actionNames := make([]graphql.String, len(newFilterAlert.ActionNames)) + for i, actionName := range newFilterAlert.ActionNames { + actionNames[i] = graphql.String(actionName) + } + + labels := make([]graphql.String, len(newFilterAlert.Labels)) + for i, label := range newFilterAlert.Labels { + labels[i] = graphql.String(label) + } + + createFilterAlert := humiographql.CreateFilterAlert{ + ViewName: humiographql.RepoOrViewName(viewName), + Name: graphql.String(newFilterAlert.Name), + Description: graphql.String(newFilterAlert.Description), + QueryString: graphql.String(newFilterAlert.QueryString), + ActionIdsOrNames: actionNames, + Labels: labels, + Enabled: graphql.Boolean(newFilterAlert.Enabled), + RunAsUserID: graphql.String(newFilterAlert.RunAsUserID), + QueryOwnershipType: humiographql.QueryOwnershipType(newFilterAlert.QueryOwnershipType), + } + + variables := map[string]any{ + "input": createFilterAlert, + } + + err := fa.client.Mutate(&mutation, variables) + if err != nil { + return nil, err + } + + filterAlert := mapHumioGraphqlFilterAlertToFilterAlert(mutation.FilterAlert) + + return &filterAlert, nil +} + +func (fa *FilterAlerts) Delete(viewName, filterAlertID string) error { + if filterAlertID == "" { + return fmt.Errorf("filterAlertID is empty") + } + + var mutation struct { + DidDelete bool `graphql:"deleteFilterAlert(input: { viewName: $viewName, id: $id })"` + } + + variables := map[string]any{ + "viewName": humiographql.RepoOrViewName(viewName), + "id": graphql.String(filterAlertID), + } + + err := fa.client.Mutate(&mutation, variables) + + if mutation.DidDelete == false { + return fmt.Errorf("unable to remove filter alert in repo/view '%s' with id '%s'", viewName, filterAlertID) + } + + return err +} + +func (fa *FilterAlerts) Get(viewName string, filterAlertID string) (*FilterAlert, error) { + var query struct { + SearchDomain struct { + FilterAlert humiographql.FilterAlert `graphql:"filterAlert(id: $filterAlertId)"` + } `graphql:"searchDomain(name: $viewName) "` + } + + variables := map[string]any{ + "viewName": graphql.String(viewName), + "filterAlertId": graphql.String(filterAlertID), + } + + err := fa.client.Query(&query, variables) + if err != nil { + return nil, err + } + + filterAlert := mapHumioGraphqlFilterAlertToFilterAlert(query.SearchDomain.FilterAlert) + + return &filterAlert, nil +} + +func mapHumioGraphqlFilterAlertToFilterAlert(input humiographql.FilterAlert) FilterAlert { + 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 FilterAlert{ + ID: string(input.ID), + Name: string(input.Name), + Description: string(input.Description), + QueryString: string(input.QueryString), + ActionNames: actionNames, + Labels: labels, + Enabled: bool(input.Enabled), + QueryOwnershipType: queryOwnershipType, + RunAsUserID: runAsUserID, + } +} diff --git a/api/internal/humiographql/alerts.go b/api/internal/humiographql/alerts.go index 0612896..52eb6da 100644 --- a/api/internal/humiographql/alerts.go +++ b/api/internal/humiographql/alerts.go @@ -16,18 +16,12 @@ type Alert struct { Actions []graphql.String `graphql:"actions"` Labels []graphql.String `graphql:"labels"` LastError graphql.String `graphql:"lastError"` - QueryOwnership struct { - ID graphql.String `graphql:"id"` - QueryOwnershipTypeName QueryOwnershipTypeName `graphql:"__typename"` - } `graphql:"queryOwnership"` - RunAsUser struct { + QueryOwnership QueryOwnership `graphql:"queryOwnership"` + RunAsUser struct { ID graphql.String `graphql:"id"` } `graphql:"runAsUser"` } -type QueryOwnership struct { -} - type CreateAlert struct { ViewName graphql.String `json:"viewName"` Name graphql.String `json:"name"` @@ -58,19 +52,3 @@ type UpdateAlert struct { Labels []graphql.String `json:"labels"` QueryOwnershipType QueryOwnershipType `json:"queryOwnershipType,omitempty"` } - -type Long int64 - -type QueryOwnershipTypeName string - -const ( - QueryOwnershipTypeNameOrganization QueryOwnershipTypeName = "OrganizationOwnership" - QueryOwnershipTypeNameUser QueryOwnershipTypeName = "UserOwnership" -) - -type QueryOwnershipType string - -const ( - QueryOwnershipTypeUser QueryOwnershipType = "User" - QueryOwnershipTypeOrganization QueryOwnershipType = "Organization" -) diff --git a/api/internal/humiographql/filter-alerts.go b/api/internal/humiographql/filter-alerts.go new file mode 100644 index 0000000..bd458c0 --- /dev/null +++ b/api/internal/humiographql/filter-alerts.go @@ -0,0 +1,43 @@ +package humiographql + +import graphql "github.com/cli/shurcooL-graphql" + +type FilterAlert struct { + ID graphql.String `graphql:"id"` + Name graphql.String `graphql:"name"` + Description graphql.String `graphql:"description"` + QueryString graphql.String `graphql:"queryString"` + Actions []Action `graphql:"actions"` + Labels []graphql.String `graphql:"labels"` + Enabled graphql.Boolean `graphql:"enabled"` + QueryOwnership QueryOwnership `graphql:"queryOwnership"` +} + +type Action struct { + Name graphql.String `graphql:"name"` +} + +type CreateFilterAlert struct { + ViewName RepoOrViewName `json:"viewName"` + Name graphql.String `json:"name"` + Description graphql.String `json:"description,omitempty"` + QueryString graphql.String `json:"queryString"` + 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"` +} + +type UpdateFilterAlert 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"` + 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"` +} diff --git a/api/internal/humiographql/queryownership.go b/api/internal/humiographql/queryownership.go new file mode 100644 index 0000000..0bfd6bb --- /dev/null +++ b/api/internal/humiographql/queryownership.go @@ -0,0 +1,22 @@ +package humiographql + +import graphql "github.com/cli/shurcooL-graphql" + +type QueryOwnershipTypeName string + +const ( + QueryOwnershipTypeNameOrganization QueryOwnershipTypeName = "OrganizationOwnership" + QueryOwnershipTypeNameUser QueryOwnershipTypeName = "UserOwnership" +) + +type QueryOwnershipType string + +const ( + QueryOwnershipTypeUser QueryOwnershipType = "User" + QueryOwnershipTypeOrganization QueryOwnershipType = "Organization" +) + +type QueryOwnership struct { + ID graphql.String `graphql:"id"` + QueryOwnershipTypeName QueryOwnershipTypeName `graphql:"__typename"` +} diff --git a/api/internal/humiographql/scalars.go b/api/internal/humiographql/scalars.go new file mode 100644 index 0000000..7411074 --- /dev/null +++ b/api/internal/humiographql/scalars.go @@ -0,0 +1,5 @@ +package humiographql + +type RepoOrViewName string + +type Long int64 diff --git a/cmd/humioctl/alerts_install.go b/cmd/humioctl/alerts_install.go index dab15e9..b750321 100644 --- a/cmd/humioctl/alerts_install.go +++ b/cmd/humioctl/alerts_install.go @@ -16,8 +16,6 @@ package main import ( "fmt" - "io" - "net/http" "os" "github.com/humio/cli/api" @@ -52,9 +50,9 @@ The install command allows you to install alerts from a URL or from a local file // if we only got you must supply --file or --url. if l := len(args); l == 1 { if filePath != "" { - content, err = getAlertFromFile(filePath) + content, err = getBytesFromFile(filePath) } else if url != "" { - content, err = getURLAlert(url) + content, err = getBytesFromURL(url) } else { cmd.Printf("You must specify a path using --file or --url\n") os.Exit(1) @@ -86,22 +84,3 @@ The install command allows you to install alerts from a URL or from a local file return &cmd } - -func getAlertFromFile(filePath string) ([]byte, error) { - // #nosec G304 - return os.ReadFile(filePath) -} - -func getURLAlert(url string) ([]byte, error) { - // #nosec G107 - response, err := http.Get(url) - - if err != nil { - return nil, err - } - - defer func() { - _ = response.Body.Close() - }() - return io.ReadAll(response.Body) -} diff --git a/cmd/humioctl/filter_alerts.go b/cmd/humioctl/filter_alerts.go new file mode 100644 index 0000000..f0500dc --- /dev/null +++ b/cmd/humioctl/filter_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 newFilterAlertsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "filter-alerts", + Short: "Manage filter alerts", + } + + cmd.AddCommand(newFilterAlertsListCmd()) + cmd.AddCommand(newFilterAlertsInstallCmd()) + cmd.AddCommand(newFilterAlertsExportCmd()) + cmd.AddCommand(newFilterAlertsRemoveCmd()) + cmd.AddCommand(newFilterAlertsShowCmd()) + + return cmd +} diff --git a/cmd/humioctl/filter_alerts_export.go b/cmd/humioctl/filter_alerts_export.go new file mode 100644 index 0000000..d83c1a5 --- /dev/null +++ b/cmd/humioctl/filter_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 newFilterAlertsExportCmd() *cobra.Command { + var outputName string + + cmd := cobra.Command{ + Use: "export [flags] ", + Short: "Export a filter alert in to a file.", + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + view := args[0] + filterAlertName := args[1] + client := NewApiClient(cmd) + + if outputName == "" { + outputName = filterAlertName + } + + filterAlerts, err := client.FilterAlerts().List(view) + exitOnError(cmd, err, "Could not list filter alerts") + + var filterAlert api.FilterAlert + for _, fa := range filterAlerts { + if fa.Name == filterAlertName { + filterAlert = fa + } + } + + if filterAlert.ID == "" { + exitOnError(cmd, api.FilterAlertNotFound(filterAlertName), "Could not find filter alert") + } + + yamlData, err := yaml.Marshal(&filterAlert) + exitOnError(cmd, err, "Failed to serialize the filter alert") + + outFilePath := outputName + ".yaml" + err = os.WriteFile(outFilePath, yamlData, 0600) + exitOnError(cmd, err, "Error saving the filter alert file") + }, + } + + cmd.Flags().StringVarP(&outputName, "output", "o", "", "The file path where the filter alert should be written. Defaults to ./.yaml") + + return &cmd +} diff --git a/cmd/humioctl/filter_alerts_install.go b/cmd/humioctl/filter_alerts_install.go new file mode 100644 index 0000000..9987127 --- /dev/null +++ b/cmd/humioctl/filter_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 newFilterAlertsInstallCmd() *cobra.Command { + var ( + filePath, url string + ) + + cmd := cobra.Command{ + Use: "install [flags] ", + Short: "Installs a filter alert in a view", + Long: `Install a filter alert from a URL or from a local file. + +The install command allows you to install filter alerts from a URL or from a local file, e.g. + + $ humioctl filter-alerts install viewName --url=https://example.com/acme/filter-alert.yaml + + $ humioctl filter-alerts install viewName --file=./filter-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 filter alert") + + client := NewApiClient(cmd) + viewName := args[0] + + var filterAlert api.FilterAlert + err = yaml.Unmarshal(content, &filterAlert) + exitOnError(cmd, err, "Could not unmarshal the filter alert") + + _, err = client.FilterAlerts().Create(viewName, &filterAlert) + exitOnError(cmd, err, "Could not create the filter alert") + + fmt.Fprintln(cmd.OutOrStdout(), "Filter alert created") + }, + } + + cmd.Flags().StringVar(&filePath, "file", "", "The local file path to the filter alert to install.") + cmd.Flags().StringVar(&url, "url", "", "A URL to fetch the filter alert file from.") + cmd.MarkFlagsMutuallyExclusive("file", "url") + return &cmd +} diff --git a/cmd/humioctl/filter_alerts_list.go b/cmd/humioctl/filter_alerts_list.go new file mode 100644 index 0000000..4a70b23 --- /dev/null +++ b/cmd/humioctl/filter_alerts_list.go @@ -0,0 +1,62 @@ +// 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 ( + "strings" + + "github.com/humio/cli/cmd/internal/format" + "github.com/spf13/cobra" +) + +func newFilterAlertsListCmd() *cobra.Command { + cmd := cobra.Command{ + Use: "list ", + Short: "List all filter alerts in a view.", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + view := args[0] + client := NewApiClient(cmd) + + filterAlerts, err := client.FilterAlerts().List(view) + exitOnError(cmd, err, "Error fetching filter alerts") + + var rows = make([][]format.Value, len(filterAlerts)) + for i := range filterAlerts { + filterAlert := filterAlerts[i] + + var actionNames = make([]string, len(filterAlert.ActionNames)) + for j := range filterAlert.ActionNames { + actionNames[j] = filterAlert.ActionNames[j] + } + + rows[i] = []format.Value{ + format.String(filterAlert.ID), + format.String(filterAlert.Name), + format.Bool(filterAlert.Enabled), + format.String(filterAlert.Description), + format.String(strings.Join(actionNames, ", ")), + format.String(strings.Join(filterAlert.Labels, ", ")), + format.String(filterAlert.RunAsUserID), + format.String(filterAlert.QueryOwnershipType), + } + } + + printOverviewTable(cmd, []string{"ID", "Name", "Enabled", "Description", "Actions", "Labels", "Run As User ID", "Query Ownership Type"}, rows) + }, + } + + return &cmd +} diff --git a/cmd/humioctl/filter_alerts_remove.go b/cmd/humioctl/filter_alerts_remove.go new file mode 100644 index 0000000..e128973 --- /dev/null +++ b/cmd/humioctl/filter_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 newFilterAlertsRemoveCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "remove ", + Short: "Removes a filter alert.", + Long: `Removes the filter alert with name '' in the view with name ''.`, + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + viewName := args[0] + filterAlertName := args[1] + client := NewApiClient(cmd) + + filterAlerts, err := client.FilterAlerts().List(viewName) + exitOnError(cmd, err, "Could not list filter alerts") + + var filterAlert api.FilterAlert + for _, fa := range filterAlerts { + if fa.Name == filterAlertName { + filterAlert = fa + } + } + + if filterAlert.ID == "" { + exitOnError(cmd, api.FilterAlertNotFound(filterAlertName), "Could not find filter alert") + } + + err = client.FilterAlerts().Delete(viewName, filterAlert.ID) + exitOnError(cmd, err, "Could not remove alert") + + fmt.Fprintf(cmd.OutOrStdout(), "Successfully removed filter alert %q from view %q\n", filterAlertName, viewName) + }, + } + + return cmd +} diff --git a/cmd/humioctl/filter_alerts_show.go b/cmd/humioctl/filter_alerts_show.go new file mode 100644 index 0000000..aef51fa --- /dev/null +++ b/cmd/humioctl/filter_alerts_show.go @@ -0,0 +1,66 @@ +// 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" + "strings" + + "github.com/humio/cli/cmd/internal/format" + "github.com/spf13/cobra" +) + +func newFilterAlertsShowCmd() *cobra.Command { + cmd := cobra.Command{ + Use: "show ", + Short: "Show details about a filter alert in a view.", + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + view := args[0] + name := args[1] + client := NewApiClient(cmd) + + filterAlerts, err := client.FilterAlerts().List(view) + exitOnError(cmd, err, "Could not list filter alert") + + var filterAlert api.FilterAlert + for _, fa := range filterAlerts { + if fa.Name == name { + filterAlert = fa + } + } + + if filterAlert.ID == "" { + exitOnError(cmd, api.FilterAlertNotFound(name), "Could not find filter alert") + } + + details := [][]format.Value{ + {format.String("ID"), format.String(filterAlert.ID)}, + {format.String("Name"), format.String(filterAlert.Name)}, + {format.String("Enabled"), format.Bool(filterAlert.Enabled)}, + {format.String("Description"), format.String(filterAlert.Description)}, + {format.String("Query String"), format.String(filterAlert.QueryString)}, + {format.String("Labels"), format.String(strings.Join(filterAlert.Labels, ", "))}, + {format.String("Actions"), format.String(strings.Join(filterAlert.ActionNames, ", "))}, + {format.String("Run As User ID"), format.String(filterAlert.RunAsUserID)}, + {format.String("Query Ownership Type"), format.String(filterAlert.QueryOwnershipType)}, + } + + printDetailsTable(cmd, details) + }, + } + + return &cmd +} diff --git a/cmd/humioctl/root.go b/cmd/humioctl/root.go index efe8ac0..5598f98 100644 --- a/cmd/humioctl/root.go +++ b/cmd/humioctl/root.go @@ -121,6 +121,7 @@ Common Management Commands: rootCmd.AddCommand(newClusterCmd()) rootCmd.AddCommand(newActionsCmd()) rootCmd.AddCommand(newAlertsCmd()) + rootCmd.AddCommand(newFilterAlertsCmd()) rootCmd.AddCommand(newPackagesCmd()) rootCmd.AddCommand(newGroupsCmd()) rootCmd.AddCommand(newFilesCmd()) diff --git a/cmd/humioctl/util.go b/cmd/humioctl/util.go index d765cc2..319bf48 100644 --- a/cmd/humioctl/util.go +++ b/cmd/humioctl/util.go @@ -3,6 +3,8 @@ package main import ( "errors" "fmt" + "io" + "net/http" "net/url" "os" "strconv" @@ -137,3 +139,22 @@ func printDetailsTable(cmd *cobra.Command, data [][]format.Value) { func printOverviewTable(cmd *cobra.Command, header []string, data [][]format.Value) { format.FormatterFromCommand(cmd).Table(header, data) } + +func getBytesFromFile(filePath string) ([]byte, error) { + // #nosec G304 + return os.ReadFile(filePath) +} + +func getBytesFromURL(url string) ([]byte, error) { + // #nosec G107 + response, err := http.Get(url) + + if err != nil { + return nil, err + } + + defer func() { + _ = response.Body.Close() + }() + return io.ReadAll(response.Body) +}