From e0f8624b9bfdd4c4a26b5bc0bce86777ce638292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frederik=20Jerl=C3=B8v?= Date: Thu, 27 Jun 2024 09:49:31 +0200 Subject: [PATCH] Scheduled searches --- api/error.go | 16 +- api/internal/humiographql/alerts.go | 4 +- api/internal/humiographql/scheduled-search.go | 56 ++++ api/internal/humiographql/user.go | 7 + api/scheduled-search.go | 242 ++++++++++++++++++ cmd/humioctl/root.go | 1 + cmd/humioctl/scheduled_searches.go | 34 +++ cmd/humioctl/scheduled_searches_export.go | 67 +++++ cmd/humioctl/scheduled_searches_install.go | 79 ++++++ cmd/humioctl/scheduled_searches_list.go | 60 +++++ cmd/humioctl/scheduled_searches_remove.go | 57 +++++ cmd/humioctl/scheduled_searches_show.go | 70 +++++ 12 files changed, 686 insertions(+), 7 deletions(-) create mode 100644 api/internal/humiographql/scheduled-search.go create mode 100644 api/internal/humiographql/user.go create mode 100644 api/scheduled-search.go create mode 100644 cmd/humioctl/scheduled_searches.go create mode 100644 cmd/humioctl/scheduled_searches_export.go create mode 100644 cmd/humioctl/scheduled_searches_install.go create mode 100644 cmd/humioctl/scheduled_searches_list.go create mode 100644 cmd/humioctl/scheduled_searches_remove.go create mode 100644 cmd/humioctl/scheduled_searches_show.go diff --git a/api/error.go b/api/error.go index 75dab0e1..258d35d0 100644 --- a/api/error.go +++ b/api/error.go @@ -7,10 +7,11 @@ import ( type EntityType string const ( - EntityTypeParser EntityType = "parser" - EntityTypeAction EntityType = "action" - EntityTypeAlert EntityType = "alert" - EntityTypeFilterAlert EntityType = "filter-alert" + EntityTypeParser EntityType = "parser" + EntityTypeAction EntityType = "action" + EntityTypeAlert EntityType = "alert" + EntityTypeFilterAlert EntityType = "filter-alert" + EntityTypeScheduledSearch EntityType = "scheduled-search" ) func (e EntityType) String() string { @@ -61,3 +62,10 @@ func FilterAlertNotFound(name string) error { key: name, } } + +func ScheduledSearchNotFound(name string) error { + return EntityNotFound{ + entityType: EntityTypeScheduledSearch, + key: name, + } +} \ No newline at end of file diff --git a/api/internal/humiographql/alerts.go b/api/internal/humiographql/alerts.go index 52eb6da5..8a4ed73b 100644 --- a/api/internal/humiographql/alerts.go +++ b/api/internal/humiographql/alerts.go @@ -17,9 +17,7 @@ type Alert struct { Labels []graphql.String `graphql:"labels"` LastError graphql.String `graphql:"lastError"` QueryOwnership QueryOwnership `graphql:"queryOwnership"` - RunAsUser struct { - ID graphql.String `graphql:"id"` - } `graphql:"runAsUser"` + RunAsUser User `graphql:"runAsUser"` } type CreateAlert struct { diff --git a/api/internal/humiographql/scheduled-search.go b/api/internal/humiographql/scheduled-search.go new file mode 100644 index 00000000..5577f77e --- /dev/null +++ b/api/internal/humiographql/scheduled-search.go @@ -0,0 +1,56 @@ +package humiographql + +import graphql "github.com/cli/shurcooL-graphql" + +type ScheduledSearch struct { + ID graphql.String `json:"id"` + Name graphql.String `json:"name"` + Description graphql.String `json:"description,omitempty"` + QueryString graphql.String `json:"queryString"` + Start graphql.String `json:"start"` + End graphql.String `json:"end"` + TimeZone graphql.String `json:"timezone"` + Schedule graphql.String `json:"schedule"` + BackfillLimit graphql.Int `json:"backfillLimit"` + Enabled graphql.Boolean `json:"enabled"` + ActionsV2 []Action `json:"actionsV2"` + RunAsUser User `json:"runAsUser,omitempty"` + TimeOfNextPlannedExecution Long `json:"timeOfNextPlannedExecution"` + Labels []graphql.String `json:"labels"` + QueryOwnership QueryOwnership `json:"queryOwnership"` +} + +type CreateScheduledSearch struct { + ViewName graphql.String `json:"viewName"` + Name graphql.String `json:"name"` + Description graphql.String `json:"description,omitempty"` + QueryString graphql.String `json:"queryString"` + QueryStart graphql.String `json:"queryStart"` + QueryEnd graphql.String `json:"queryEnd"` + Schedule graphql.String `json:"schedule"` + TimeZone graphql.String `json:"timeZone"` + BackfillLimit graphql.Int `json:"backfillLimit"` + Enabled graphql.Boolean `json:"enabled"` + ActionsIdsOrNames []graphql.String `json:"actions"` + Labels []graphql.String `json:"labels,omitempty"` + RunAsUserID graphql.String `json:"runAsUserId,omitempty"` + QueryOwnership QueryOwnershipType `json:"queryOwnershipType,omitempty"` +} + +type UpdateScheduledSearch struct { + ViewName graphql.String `json:"viewName"` + ID graphql.String `json:"id"` + Name graphql.String `json:"name"` + Description graphql.String `json:"description,omitempty"` + QueryString graphql.String `json:"queryString"` + QueryStart graphql.String `json:"queryStart"` + QueryEnd graphql.String `json:"queryEnd"` + Schedule graphql.String `json:"schedule"` + TimeZone graphql.String `json:"timeZone"` + BackfillLimit graphql.Int `json:"backfillLimit"` + Enabled graphql.Boolean `json:"enabled"` + ActionsIdsOrNames []graphql.String `json:"actions"` + Labels []graphql.String `json:"labels"` + RunAsUserID graphql.String `json:"runAsUserId,omitempty"` + QueryOwnership QueryOwnershipType `json:"queryOwnershipType"` +} diff --git a/api/internal/humiographql/user.go b/api/internal/humiographql/user.go new file mode 100644 index 00000000..3bbc0c8f --- /dev/null +++ b/api/internal/humiographql/user.go @@ -0,0 +1,7 @@ +package humiographql + +import graphql "github.com/cli/shurcooL-graphql" + +type User struct { + ID graphql.String `graphql:"id"` +} diff --git a/api/scheduled-search.go b/api/scheduled-search.go new file mode 100644 index 00000000..cba1ade1 --- /dev/null +++ b/api/scheduled-search.go @@ -0,0 +1,242 @@ +package api + +import ( + "fmt" + graphql "github.com/cli/shurcooL-graphql" + "github.com/humio/cli/api/internal/humiographql" +) + +type ScheduledSearch 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"` + QueryStart string `graphql:"queryStart" yaml:"queryStart" json:"queryStart"` + QueryEnd string `graphql:"queryEnd" yaml:"queryEnd" json:"queryEnd"` + TimeZone string `graphql:"timeZone" yaml:"timeZone" json:"timeZone"` + Schedule string `graphql:"schedule" yaml:"schedule" json:"schedule"` + BackfillLimit int `graphql:"backfillLimit" yaml:"backfillLimit" json:"backfillLimit"` + Enabled bool `graphql:"enabled" yaml:"enabled" json:"enabled"` + ActionNames []string `graphql:"actionNames" yaml:"actionNames" json:"actionNames"` + RunAsUserID string `graphql:"runAsUserId" yaml:"runAsUserId,omitempty" json:"runAsUserId,omitempty"` + Labels []string `graphql:"labels" yaml:"labels" json:"labels"` + QueryOwnershipType string `graphql:"queryOwnership" yaml:"queryOwnershipType" json:"queryOwnershipType"` +} + +type ScheduledSearches struct { + client *Client +} + +func (c *Client) ScheduledSearches() *ScheduledSearches { return &ScheduledSearches{client: c} } + +func (a *ScheduledSearches) List(viewName string) ([]ScheduledSearch, error) { + var query struct { + SearchDomain struct { + ScheduledSearches []humiographql.ScheduledSearch `graphql:"scheduledSearches"` + } `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 scheduledSearches = make([]ScheduledSearch, len(query.SearchDomain.ScheduledSearches)) + for i := range query.SearchDomain.ScheduledSearches { + scheduledSearches[i] = mapHumioGraphqlScheduledSearchToScheduledSearch(query.SearchDomain.ScheduledSearches[i]) + } + + return scheduledSearches, err +} + +func (a *ScheduledSearches) Update(viewName string, updateScheduledSearch *ScheduledSearch) (*ScheduledSearch, error) { + if updateScheduledSearch == nil { + return nil, fmt.Errorf("updateScheduledSearch must not be nil") + } + + if updateScheduledSearch.ID == "" { + return nil, fmt.Errorf("updateScheduledSearch must have non-empty ID") + } + + var mutation struct { + humiographql.ScheduledSearch `graphql:"updateScheduledSearch(input: $input)"` + } + + actionNames := make([]graphql.String, len(updateScheduledSearch.ActionNames)) + for i, actionName := range updateScheduledSearch.ActionNames { + actionNames[i] = graphql.String(actionName) + } + + labels := make([]graphql.String, len(updateScheduledSearch.Labels)) + for i, label := range updateScheduledSearch.Labels { + labels[i] = graphql.String(label) + } + + updateAlert := humiographql.UpdateScheduledSearch{ + ViewName: graphql.String(viewName), + ID: graphql.String(updateScheduledSearch.ID), + Name: graphql.String(updateScheduledSearch.Name), + Description: graphql.String(updateScheduledSearch.Description), + QueryString: graphql.String(updateScheduledSearch.QueryString), + QueryStart: graphql.String(updateScheduledSearch.QueryStart), + QueryEnd: graphql.String(updateScheduledSearch.QueryEnd), + Schedule: graphql.String(updateScheduledSearch.Schedule), + TimeZone: graphql.String(updateScheduledSearch.TimeZone), + BackfillLimit: graphql.Int(updateScheduledSearch.BackfillLimit), + Enabled: graphql.Boolean(updateScheduledSearch.Enabled), + ActionsIdsOrNames: actionNames, + Labels: labels, + RunAsUserID: graphql.String(updateScheduledSearch.RunAsUserID), + QueryOwnership: humiographql.QueryOwnershipType(updateScheduledSearch.QueryOwnershipType), + } + + variables := map[string]any{ + "input": updateAlert, + } + + err := a.client.Mutate(&mutation, variables) + if err != nil { + return nil, err + } + + scheduledSearch := mapHumioGraphqlScheduledSearchToScheduledSearch(mutation.ScheduledSearch) + + return &scheduledSearch, nil +} + +func (a *ScheduledSearches) Create(viewName string, newScheduledSearch *ScheduledSearch) (*ScheduledSearch, error) { + if newScheduledSearch == nil { + return nil, fmt.Errorf("newScheduledSearch must not be nil") + } + + var mutation struct { + humiographql.ScheduledSearch `graphql:"createScheduledSearch(input: $input)"` + } + + actionNames := make([]graphql.String, len(newScheduledSearch.ActionNames)) + for i, actionName := range newScheduledSearch.ActionNames { + actionNames[i] = graphql.String(actionName) + } + + labels := make([]graphql.String, len(newScheduledSearch.Labels)) + for i, label := range newScheduledSearch.Labels { + labels[i] = graphql.String(label) + } + + createScheduledSearch := humiographql.CreateScheduledSearch{ + ViewName: graphql.String(viewName), + Name: graphql.String(newScheduledSearch.Name), + Description: graphql.String(newScheduledSearch.Description), + QueryString: graphql.String(newScheduledSearch.QueryString), + QueryStart: graphql.String(newScheduledSearch.QueryStart), + QueryEnd: graphql.String(newScheduledSearch.QueryEnd), + Schedule: graphql.String(newScheduledSearch.Schedule), + TimeZone: graphql.String(newScheduledSearch.TimeZone), + BackfillLimit: graphql.Int(newScheduledSearch.BackfillLimit), + Enabled: graphql.Boolean(newScheduledSearch.Enabled), + ActionsIdsOrNames: actionNames, + Labels: labels, + RunAsUserID: graphql.String(newScheduledSearch.RunAsUserID), + QueryOwnership: humiographql.QueryOwnershipType(newScheduledSearch.QueryOwnershipType), + } + + variables := map[string]any{ + "input": createScheduledSearch, + } + + err := a.client.Mutate(&mutation, variables) + if err != nil { + return nil, err + } + + scheduledSearch := mapHumioGraphqlScheduledSearchToScheduledSearch(mutation.ScheduledSearch) + + return &scheduledSearch, nil +} + +func (a *ScheduledSearches) Delete(viewName, scheduledSearchID string) error { + if scheduledSearchID == "" { + return fmt.Errorf("scheduledSearchID is empty") + } + + var mutation struct { + DidDelete bool `graphql:"deleteScheduledSearch(input: { viewName: $viewName, id: $id })"` + } + + variables := map[string]any{ + "viewName": graphql.String(viewName), + "id": graphql.String(scheduledSearchID), + } + + err := a.client.Mutate(&mutation, variables) + + if !mutation.DidDelete { + return fmt.Errorf("unable to remove scheduled search in repo/view '%s' with id '%s'", viewName, scheduledSearchID) + } + + return err +} + +func (a *ScheduledSearches) Get(viewName string, scheduledSearchId string) (*ScheduledSearch, error) { + var query struct { + SearchDomain struct { + ScheduledSearch humiographql.ScheduledSearch `graphql:"scheduledSearch(id: $scheduledSearchId)"` + } `graphql:"searchDomain(name: $viewName) "` + } + + variables := map[string]any{ + "viewName": graphql.String(viewName), + "scheduledSearchId": graphql.String(scheduledSearchId), + } + + err := a.client.Query(&query, variables) + if err != nil { + return nil, err + } + + scheduledSearch := mapHumioGraphqlScheduledSearchToScheduledSearch(query.SearchDomain.ScheduledSearch) + + return &scheduledSearch, nil +} + +func mapHumioGraphqlScheduledSearchToScheduledSearch(input humiographql.ScheduledSearch) ScheduledSearch { + 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.ActionsV2)) + for i := range input.ActionsV2 { + actionNames[i] = string(input.ActionsV2[i].Name) + } + + var labels = make([]string, len(input.Labels)) + for i := range input.Labels { + labels[i] = string(input.Labels[i]) + } + + return ScheduledSearch{ + ID: string(input.ID), + Name: string(input.Name), + Description: string(input.Description), + QueryString: string(input.QueryString), + QueryStart: string(input.Start), + QueryEnd: string(input.End), + TimeZone: string(input.TimeZone), + Schedule: string(input.Schedule), + BackfillLimit: int(input.BackfillLimit), + ActionNames: actionNames, + Labels: labels, + Enabled: bool(input.Enabled), + QueryOwnershipType: queryOwnershipType, + RunAsUserID: runAsUserID, + } +} diff --git a/cmd/humioctl/root.go b/cmd/humioctl/root.go index 5598f989..912909ac 100644 --- a/cmd/humioctl/root.go +++ b/cmd/humioctl/root.go @@ -122,6 +122,7 @@ Common Management Commands: rootCmd.AddCommand(newActionsCmd()) rootCmd.AddCommand(newAlertsCmd()) rootCmd.AddCommand(newFilterAlertsCmd()) + rootCmd.AddCommand(newScheduledSearchesCmd()) rootCmd.AddCommand(newPackagesCmd()) rootCmd.AddCommand(newGroupsCmd()) rootCmd.AddCommand(newFilesCmd()) diff --git a/cmd/humioctl/scheduled_searches.go b/cmd/humioctl/scheduled_searches.go new file mode 100644 index 00000000..34868705 --- /dev/null +++ b/cmd/humioctl/scheduled_searches.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 newScheduledSearchesCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "scheduled-searches", + Short: "Manage scheduled searches", + } + + cmd.AddCommand(newScheduledSearchesListCmd()) + cmd.AddCommand(newScheduledSearchesInstallCmd()) + cmd.AddCommand(newScheduledSearchesExportCmd()) + cmd.AddCommand(newScheduledSearchesRemoveCmd()) + cmd.AddCommand(newScheduledSearchesShowCmd()) + + return cmd +} diff --git a/cmd/humioctl/scheduled_searches_export.go b/cmd/humioctl/scheduled_searches_export.go new file mode 100644 index 00000000..9dcee503 --- /dev/null +++ b/cmd/humioctl/scheduled_searches_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 newScheduledSearchesExportCmd() *cobra.Command { + var outputName string + + cmd := cobra.Command{ + Use: "export [flags] ", + Short: "Export a scheduled search in to a file.", + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + view := args[0] + scheduledSearchName := args[1] + client := NewApiClient(cmd) + + if outputName == "" { + outputName = scheduledSearchName + } + + scheduledSearches, err := client.ScheduledSearches().List(view) + exitOnError(cmd, err, "Could not list scheduled searches") + + var scheduledSearch api.ScheduledSearch + for _, ss := range scheduledSearches { + if ss.Name == scheduledSearchName { + scheduledSearch = ss + } + } + + if scheduledSearch.ID == "" { + exitOnError(cmd, api.ScheduledSearchNotFound(scheduledSearchName), "Could not find scheduled search") + } + + yamlData, err := yaml.Marshal(&scheduledSearch) + exitOnError(cmd, err, "Failed to serialize the scheduled search") + + outFilePath := outputName + ".yaml" + err = os.WriteFile(outFilePath, yamlData, 0600) + exitOnError(cmd, err, "Error saving the scheduled search file") + }, + } + + cmd.Flags().StringVarP(&outputName, "output", "o", "", "The file path where the scheduled search should be written. Defaults to ./.yaml") + + return &cmd +} diff --git a/cmd/humioctl/scheduled_searches_install.go b/cmd/humioctl/scheduled_searches_install.go new file mode 100644 index 00000000..2fd6f2d3 --- /dev/null +++ b/cmd/humioctl/scheduled_searches_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 newScheduledSearchesInstallCmd() *cobra.Command { + var ( + filePath, url string + ) + + cmd := cobra.Command{ + Use: "install [flags] ", + Short: "Installs a scheduled search in a view", + Long: `Install a scheduled search from a URL or from a local file. + +The install command allows you to install scheduled searches from a URL or from a local file, e.g. + + $ humioctl scheduled-searches install viewName --url=https://example.com/acme/scheduled-search.yaml + + $ humioctl scheduled-searches install viewName --file=./scheduled-searches.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 scheduled search") + + client := NewApiClient(cmd) + viewName := args[0] + + var scheduledSearch api.ScheduledSearch + err = yaml.Unmarshal(content, &scheduledSearch) + exitOnError(cmd, err, "Could not unmarshal the scheduled search") + + _, err = client.ScheduledSearches().Create(viewName, &scheduledSearch) + exitOnError(cmd, err, "Could not create the scheduled search") + + fmt.Fprintln(cmd.OutOrStdout(), "Scheduled search created") + }, + } + + cmd.Flags().StringVar(&filePath, "file", "", "The local file path to the scheduled search to install.") + cmd.Flags().StringVar(&url, "url", "", "A URL to fetch the scheduled search file from.") + cmd.MarkFlagsMutuallyExclusive("file", "url") + return &cmd +} diff --git a/cmd/humioctl/scheduled_searches_list.go b/cmd/humioctl/scheduled_searches_list.go new file mode 100644 index 00000000..cfb9e537 --- /dev/null +++ b/cmd/humioctl/scheduled_searches_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 newScheduledSearchesListCmd() *cobra.Command { + cmd := cobra.Command{ + Use: "list ", + Short: "List all scheduled searches in a view.", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + view := args[0] + client := NewApiClient(cmd) + + scheduledSearches, err := client.ScheduledSearches().List(view) + exitOnError(cmd, err, "Error fetching scheduled searches") + + var rows = make([][]format.Value, len(scheduledSearches)) + for i := range scheduledSearches { + scheduledSearch := scheduledSearches[i] + rows[i] = []format.Value{ + format.String(scheduledSearch.ID), + format.String(scheduledSearch.Name), + format.String(scheduledSearch.Description), + format.String(scheduledSearch.QueryStart), + format.String(scheduledSearch.QueryEnd), + format.String(scheduledSearch.TimeZone), + format.String(scheduledSearch.Schedule), + format.Int(scheduledSearch.BackfillLimit), + format.String(strings.Join(scheduledSearch.ActionNames, ", ")), + format.String(strings.Join(scheduledSearch.Labels, ", ")), + format.Bool(scheduledSearch.Enabled), + format.String(scheduledSearch.RunAsUserID), + format.String(scheduledSearch.QueryOwnershipType), + } + } + + printOverviewTable(cmd, []string{"ID", "Name", "Description", "Query Start", "Query End", "Time Zone", "Schedule", "Backfill Limit", "Action Names", "Labels", "Enabled", "Run As User ID", "Query Ownership Type"}, rows) + }, + } + + return &cmd +} diff --git a/cmd/humioctl/scheduled_searches_remove.go b/cmd/humioctl/scheduled_searches_remove.go new file mode 100644 index 00000000..57322e64 --- /dev/null +++ b/cmd/humioctl/scheduled_searches_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 newScheduledSearchesRemoveCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "remove ", + Short: "Removes a scheduled search.", + Long: `Removes the scheduled search with name '' in the view with name ''.`, + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + viewName := args[0] + scheduledSearchName := args[1] + client := NewApiClient(cmd) + + scheduledSearches, err := client.ScheduledSearches().List(viewName) + exitOnError(cmd, err, "Could not list scheduled searches") + + var scheduledSearch api.ScheduledSearch + for _, ss := range scheduledSearches { + if ss.Name == scheduledSearchName { + scheduledSearch = ss + } + } + + if scheduledSearch.ID == "" { + exitOnError(cmd, api.ScheduledSearchNotFound(scheduledSearchName), "Could not find scheduled search") + } + + err = client.ScheduledSearches().Delete(viewName, scheduledSearch.ID) + exitOnError(cmd, err, "Could not remove scheduled search") + + fmt.Fprintf(cmd.OutOrStdout(), "Successfully removed scheduled search %q from view %q\n", scheduledSearchName, viewName) + }, + } + + return cmd +} diff --git a/cmd/humioctl/scheduled_searches_show.go b/cmd/humioctl/scheduled_searches_show.go new file mode 100644 index 00000000..abba39fe --- /dev/null +++ b/cmd/humioctl/scheduled_searches_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 newScheduledSearchesShowCmd() *cobra.Command { + cmd := cobra.Command{ + Use: "show ", + Short: "Show details about a scheduled search in a view.", + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + view := args[0] + name := args[1] + client := NewApiClient(cmd) + + scheduledSearches, err := client.ScheduledSearches().List(view) + exitOnError(cmd, err, "Could not list scheduled searches") + + var scheduledSearch api.ScheduledSearch + for _, ss := range scheduledSearches { + if ss.Name == name { + scheduledSearch = ss + } + } + + if scheduledSearch.ID == "" { + exitOnError(cmd, api.ScheduledSearchNotFound(name), "Could not find scheduled search") + } + + details := [][]format.Value{ + {format.String("ID"), format.String(scheduledSearch.ID)}, + {format.String("Name"), format.String(scheduledSearch.Name)}, + {format.String("Description"), format.String(scheduledSearch.Description)}, + {format.String("Query String"), format.String(scheduledSearch.QueryString)}, + {format.String("Query Start"), format.String(scheduledSearch.QueryStart)}, + {format.String("Query End"), format.String(scheduledSearch.QueryEnd)}, + {format.String("TimeZone"), format.String(scheduledSearch.TimeZone)}, + {format.String("Schedule"), format.String(scheduledSearch.Schedule)}, + {format.String("Backfill Limit"), format.Int(scheduledSearch.BackfillLimit)}, + {format.String("Enabled"), format.Bool(scheduledSearch.Enabled)}, + {format.String("Actions"), format.String(strings.Join(scheduledSearch.ActionNames, ", "))}, + {format.String("Run As User ID"), format.String(scheduledSearch.RunAsUserID)}, + {format.String("Labels"), format.String(strings.Join(scheduledSearch.Labels, ", "))}, + {format.String("QueryOwnershipType"), format.String(scheduledSearch.QueryOwnershipType)}, + } + + printDetailsTable(cmd, details) + }, + } + + return &cmd +}