From 72e42b3c0bad27910f1f7f7ba8ae338a7dffe0dd Mon Sep 17 00:00:00 2001 From: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> Date: Wed, 26 Mar 2025 10:31:03 +0000 Subject: [PATCH 1/7] Add monitors enable command Signed-off-by: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> --- docs/generated/errors-list.md | 9 +- docs/generated/galasactl.md | 1 + docs/generated/galasactl_monitors.md | 29 +++ docs/generated/galasactl_monitors_enable.md | 33 ++++ pkg/cmd/commandCollection.go | 26 +++ pkg/cmd/monitors.go | 93 ++++++++++ pkg/cmd/monitorsEnable.go | 139 ++++++++++++++ pkg/cmd/monitorsEnable_test.go | 80 +++++++++ pkg/cmd/monitors_test.go | 59 ++++++ pkg/errors/errorMessage.go | 12 ++ pkg/monitors/monitors.go | 24 +++ pkg/monitors/monitorsEnable.go | 81 +++++++++ pkg/monitors/monitorsEnable_test.go | 190 ++++++++++++++++++++ 13 files changed, 775 insertions(+), 1 deletion(-) create mode 100644 docs/generated/galasactl_monitors.md create mode 100644 docs/generated/galasactl_monitors_enable.md create mode 100644 pkg/cmd/monitors.go create mode 100644 pkg/cmd/monitorsEnable.go create mode 100644 pkg/cmd/monitorsEnable_test.go create mode 100644 pkg/cmd/monitors_test.go create mode 100644 pkg/monitors/monitors.go create mode 100644 pkg/monitors/monitorsEnable.go create mode 100644 pkg/monitors/monitorsEnable_test.go diff --git a/docs/generated/errors-list.md b/docs/generated/errors-list.md index 08062578..56086191 100644 --- a/docs/generated/errors-list.md +++ b/docs/generated/errors-list.md @@ -214,7 +214,14 @@ The `galasactl` tool can generate the following errors: - GAL1215E: An attempt to update a user '{}' failed. Unexpected http status code {} received from the server. Error details from the server are not in a valid json format. Cause: '{}' - GAL1216E: An attempt to update a user '{}' failed. Unexpected http status code {} received from the server. Error details from the server are: '{}' - GAL1217E: An attempt to update a user '{}' failed. Unexpected http status code {} received from the server. Error details from the server are not in the json format. -- GAL1225E: Failed to open file '{}' cause: {}. Check that this file exists, and that you have read permissions. +- GAL1218E: Failed to update a monitor named '{}'. Sending the put request to the Galasa service failed. Cause is {} +- GAL1219E: Failed to update a monitor named '{}'. Unexpected http status code {} received from the server. +- GAL1220E: Failed to update a monitor named '{}'. Unexpected http status code {} received from the server. Error details from the server could not be read. Cause: {} +- GAL1221E: Failed to update a monitor named '{}'. Unexpected http status code {} received from the server. Error details from the server are not in a valid json format. Cause: '{}' +- GAL1222E: Failed to update a monitor named '{}'. Unexpected http status code {} received from the server. Error details from the server are: '{}' +- GAL1223E: Failed to update a monitor named '{}'. Unexpected http status code {} received from the server. Error details from the server are not in the json format. +- GAL1224E: Galasa Monitor named {} is not known on the Galasa service. +- GAL1225E: Invalid monitor name provided. The name provided with the --name flag cannot be empty and must only contain characters in the following ranges: 'a'-'z', 'A'-'Z', '0'-'9', '-' (dash), '_' (underscore). - GAL1226E: Internal failure. Contents of gzip could be read, but not decoded. New gzip reader failed: file: {} error: {} - GAL1227E: Internal failure. Contents of gzip could not be decoded. {} error: {} - GAL1228E: Internal failure. Contents of gzip could not be encoded and compressed. {} error: {} diff --git a/docs/generated/galasactl.md b/docs/generated/galasactl.md index c3b73e23..139f2054 100644 --- a/docs/generated/galasactl.md +++ b/docs/generated/galasactl.md @@ -18,6 +18,7 @@ A tool for controlling Galasa resources using the command-line. * [galasactl auth](galasactl_auth.md) - Manages authentication with a Galasa ecosystem * [galasactl local](galasactl_local.md) - Manipulate local system +* [galasactl monitors](galasactl_monitors.md) - Manage monitors in the Galasa service * [galasactl project](galasactl_project.md) - Manipulate local project source code * [galasactl properties](galasactl_properties.md) - Manages properties in an ecosystem * [galasactl resources](galasactl_resources.md) - Manages resources in an ecosystem diff --git a/docs/generated/galasactl_monitors.md b/docs/generated/galasactl_monitors.md new file mode 100644 index 00000000..34ae1ede --- /dev/null +++ b/docs/generated/galasactl_monitors.md @@ -0,0 +1,29 @@ +## galasactl monitors + +Manage monitors in the Galasa service + +### Synopsis + +The parent command for operations to manipulate monitors in the Galasa service + +### Options + +``` + -b, --bootstrap string Bootstrap URL. Should start with 'http://' or 'file://'. If it starts with neither, it is assumed to be a fully-qualified path. If missing, it defaults to use the 'bootstrap.properties' file in your GALASA_HOME. Example: http://example.com/bootstrap, file:///user/myuserid/.galasa/bootstrap.properties , file://C:/Users/myuserid/.galasa/bootstrap.properties + -h, --help Displays the options for the 'monitors' command. + --rate-limit-retries int The maximum number of retries that should be made when requests to the Galasa Service fail due to rate limits being exceeded. Must be a whole number. Defaults to 3 retries (default 3) + --rate-limit-retry-backoff-secs float The amount of time in seconds to wait before retrying a command if it failed due to rate limits being exceeded. Defaults to 1 second. (default 1) +``` + +### Options inherited from parent commands + +``` + --galasahome string Path to a folder where Galasa will read and write files and configuration settings. The default is '${HOME}/.galasa'. This overrides the GALASA_HOME environment variable which may be set instead. + -l, --log string File to which log information will be sent. Any folder referred to must exist. An existing file will be overwritten. Specify "-" to log to stderr. Defaults to not logging. +``` + +### SEE ALSO + +* [galasactl](galasactl.md) - CLI for Galasa +* [galasactl monitors enable](galasactl_monitors_enable.md) - Enable a monitor in the Galasa service + diff --git a/docs/generated/galasactl_monitors_enable.md b/docs/generated/galasactl_monitors_enable.md new file mode 100644 index 00000000..caf8ab33 --- /dev/null +++ b/docs/generated/galasactl_monitors_enable.md @@ -0,0 +1,33 @@ +## galasactl monitors enable + +Enable a monitor in the Galasa service + +### Synopsis + +Enables a given monitor in the Galasa service + +``` +galasactl monitors enable [flags] +``` + +### Options + +``` + -h, --help Displays the options for the 'monitors enable' command. + --name string A mandatory flag that identifies the monitor to be manipulated by name. +``` + +### Options inherited from parent commands + +``` + -b, --bootstrap string Bootstrap URL. Should start with 'http://' or 'file://'. If it starts with neither, it is assumed to be a fully-qualified path. If missing, it defaults to use the 'bootstrap.properties' file in your GALASA_HOME. Example: http://example.com/bootstrap, file:///user/myuserid/.galasa/bootstrap.properties , file://C:/Users/myuserid/.galasa/bootstrap.properties + --galasahome string Path to a folder where Galasa will read and write files and configuration settings. The default is '${HOME}/.galasa'. This overrides the GALASA_HOME environment variable which may be set instead. + -l, --log string File to which log information will be sent. Any folder referred to must exist. An existing file will be overwritten. Specify "-" to log to stderr. Defaults to not logging. + --rate-limit-retries int The maximum number of retries that should be made when requests to the Galasa Service fail due to rate limits being exceeded. Must be a whole number. Defaults to 3 retries (default 3) + --rate-limit-retry-backoff-secs float The amount of time in seconds to wait before retrying a command if it failed due to rate limits being exceeded. Defaults to 1 second. (default 1) +``` + +### SEE ALSO + +* [galasactl monitors](galasactl_monitors.md) - Manage monitors in the Galasa service + diff --git a/pkg/cmd/commandCollection.go b/pkg/cmd/commandCollection.go index 84beabf4..6490073c 100644 --- a/pkg/cmd/commandCollection.go +++ b/pkg/cmd/commandCollection.go @@ -39,6 +39,8 @@ const ( COMMAND_NAME_PROJECT_CREATE = "project create" COMMAND_NAME_LOCAL = "local" COMMAND_NAME_LOCAL_INIT = "local init" + COMMAND_NAME_MONITORS = "monitors" + COMMAND_NAME_MONITORS_ENABLE = "monitors enable" COMMAND_NAME_PROPERTIES = "properties" COMMAND_NAME_PROPERTIES_GET = "properties get" COMMAND_NAME_PROPERTIES_SET = "properties set" @@ -137,6 +139,10 @@ func (commands *commandCollectionImpl) init(factory spi.Factory) error { err = commands.addLocalCommands(factory, rootCommand) } + if err == nil { + err = commands.addMonitorsCommands(factory, rootCommand, commsFlagSet) + } + if err == nil { err = commands.addProjectCommands(factory, rootCommand) } @@ -430,6 +436,26 @@ func (commands *commandCollectionImpl) addSecretsCommands(factory spi.Factory, r return err } +func (commands *commandCollectionImpl) addMonitorsCommands(factory spi.Factory, rootCommand spi.GalasaCommand, commsFlagSet GalasaFlagSet) error { + + var err error + var monitorsCommand spi.GalasaCommand + var monitorsEnableCommand spi.GalasaCommand + + monitorsCommand, err = NewMonitorsCmd(rootCommand, commsFlagSet) + + if err == nil { + monitorsEnableCommand, err = NewMonitorsEnableCommand(factory, monitorsCommand, commsFlagSet) + } + + if err == nil { + commands.commandMap[monitorsCommand.Name()] = monitorsCommand + commands.commandMap[monitorsEnableCommand.Name()] = monitorsEnableCommand + } + + return err +} + func (commands *commandCollectionImpl) addUsersCommands(factory spi.Factory, rootCommand spi.GalasaCommand, commsFlagSet GalasaFlagSet) error { var err error diff --git a/pkg/cmd/monitors.go b/pkg/cmd/monitors.go new file mode 100644 index 00000000..1dd50d20 --- /dev/null +++ b/pkg/cmd/monitors.go @@ -0,0 +1,93 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package cmd + +import ( + "github.com/galasa-dev/cli/pkg/spi" + "github.com/spf13/cobra" +) + +type MonitorsCmdValues struct { + name string +} + +type MonitorsCommand struct { + cobraCommand *cobra.Command + values *MonitorsCmdValues +} + +// ------------------------------------------------------------------------------------------------ +// Constructors +// ------------------------------------------------------------------------------------------------ + +func NewMonitorsCmd(rootCommand spi.GalasaCommand, commsFlagSet GalasaFlagSet) (spi.GalasaCommand, error) { + cmd := new(MonitorsCommand) + err := cmd.init(rootCommand, commsFlagSet) + return cmd, err +} + +// ------------------------------------------------------------------------------------------------ +// Public functions +// ------------------------------------------------------------------------------------------------ + +func (cmd *MonitorsCommand) Name() string { + return COMMAND_NAME_MONITORS +} + +func (cmd *MonitorsCommand) CobraCommand() *cobra.Command { + return cmd.cobraCommand +} + +func (cmd *MonitorsCommand) Values() interface{} { + return cmd.values +} + +// ------------------------------------------------------------------------------------------------ +// Private functions +// ------------------------------------------------------------------------------------------------ + +func (cmd *MonitorsCommand) init(rootCommand spi.GalasaCommand, commsFlagSet GalasaFlagSet) error { + + var err error + + cmd.values = &MonitorsCmdValues{} + cmd.cobraCommand, err = cmd.createCobraCommand(rootCommand, commsFlagSet) + + return err +} + +func (cmd *MonitorsCommand) createCobraCommand(rootCommand spi.GalasaCommand, commsFlagSet GalasaFlagSet) (*cobra.Command, error) { + + var err error + + monitorsCobraCmd := &cobra.Command{ + Use: "monitors", + Short: "Manage monitors in the Galasa service", + Long: "The parent command for operations to manipulate monitors in the Galasa service", + } + + monitorsCobraCmd.PersistentFlags().AddFlagSet(commsFlagSet.Flags()) + rootCommand.CobraCommand().AddCommand(monitorsCobraCmd) + + return monitorsCobraCmd, err +} + +func addMonitorNameFlag(cmd *cobra.Command, isMandatory bool, monitorsCmdValues *MonitorsCmdValues) { + + flagName := "name" + var description string + if isMandatory { + description = "A mandatory flag that identifies the monitor to be manipulated by name." + } else { + description = "An optional flag that identifies the monitor to be retrieved by name." + } + + cmd.Flags().StringVar(&monitorsCmdValues.name, flagName, "", description) + + if isMandatory { + cmd.MarkFlagRequired(flagName) + } +} diff --git a/pkg/cmd/monitorsEnable.go b/pkg/cmd/monitorsEnable.go new file mode 100644 index 00000000..ea5870dc --- /dev/null +++ b/pkg/cmd/monitorsEnable.go @@ -0,0 +1,139 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package cmd + +import ( + "log" + + "github.com/galasa-dev/cli/pkg/api" + "github.com/galasa-dev/cli/pkg/galasaapi" + "github.com/galasa-dev/cli/pkg/monitors" + "github.com/galasa-dev/cli/pkg/spi" + "github.com/galasa-dev/cli/pkg/utils" + "github.com/spf13/cobra" +) + +type MonitorsEnableCmdValues struct { +} + +type MonitorsEnableCommand struct { + values *MonitorsEnableCmdValues + cobraCommand *cobra.Command +} + +// ------------------------------------------------------------------------------------------------ +// Constructors methods +// ------------------------------------------------------------------------------------------------ +func NewMonitorsEnableCommand( + factory spi.Factory, + monitorsEnableCommand spi.GalasaCommand, + commsFlagSet GalasaFlagSet, +) (spi.GalasaCommand, error) { + + cmd := new(MonitorsEnableCommand) + + err := cmd.init(factory, monitorsEnableCommand, commsFlagSet) + return cmd, err +} + +// ------------------------------------------------------------------------------------------------ +// Public methods +// ------------------------------------------------------------------------------------------------ +func (cmd *MonitorsEnableCommand) Name() string { + return COMMAND_NAME_MONITORS_ENABLE +} + +func (cmd *MonitorsEnableCommand) CobraCommand() *cobra.Command { + return cmd.cobraCommand +} + +func (cmd *MonitorsEnableCommand) Values() interface{} { + return cmd.values +} + +// ------------------------------------------------------------------------------------------------ +// Private methods +// ------------------------------------------------------------------------------------------------ +func (cmd *MonitorsEnableCommand) init(factory spi.Factory, monitorsCommand spi.GalasaCommand, commsFlagSet GalasaFlagSet) error { + var err error + + cmd.values = &MonitorsEnableCmdValues{} + cmd.cobraCommand, err = cmd.createCobraCmd(factory, monitorsCommand, commsFlagSet.Values().(*CommsFlagSetValues)) + + return err +} + +func (cmd *MonitorsEnableCommand) createCobraCmd( + factory spi.Factory, + monitorsCommand spi.GalasaCommand, + commsFlagSetValues *CommsFlagSetValues, +) (*cobra.Command, error) { + + var err error + + monitorsCommandValues := monitorsCommand.Values().(*MonitorsCmdValues) + monitorsEnableCobraCmd := &cobra.Command{ + Use: "enable", + Short: "Enable a monitor in the Galasa service", + Long: "Enables a given monitor in the Galasa service", + Aliases: []string{COMMAND_NAME_MONITORS_ENABLE}, + RunE: func(cobraCommand *cobra.Command, args []string) error { + return cmd.executeMonitorsEnable(factory, monitorsCommand.Values().(*MonitorsCmdValues), commsFlagSetValues) + }, + } + + addMonitorNameFlag(monitorsEnableCobraCmd, true, monitorsCommandValues) + + monitorsCommand.CobraCommand().AddCommand(monitorsEnableCobraCmd) + + return monitorsEnableCobraCmd, err +} + +func (cmd *MonitorsEnableCommand) executeMonitorsEnable( + factory spi.Factory, + monitorsCmdValues *MonitorsCmdValues, + commsFlagSetValues *CommsFlagSetValues, +) error { + + var err error + // Operations on the file system will all be relative to the current folder. + fileSystem := factory.GetFileSystem() + + err = utils.CaptureLog(fileSystem, commsFlagSetValues.logFileName) + if err == nil { + commsFlagSetValues.isCapturingLogs = true + + log.Println("Galasa CLI - Enable monitors from the Galasa service") + + env := factory.GetEnvironment() + + var galasaHome spi.GalasaHome + galasaHome, err = utils.NewGalasaHome(fileSystem, env, commsFlagSetValues.CmdParamGalasaHomePath) + if err == nil { + + var commsClient api.APICommsClient + commsClient, err = api.NewAPICommsClient( + commsFlagSetValues.bootstrap, + commsFlagSetValues.maxRetries, + commsFlagSetValues.retryBackoffSeconds, + factory, + galasaHome, + ) + + if err == nil { + + byteReader := factory.GetByteReader() + + enableMonitorsFunc := func(apiClient *galasaapi.APIClient) error { + return monitors.EnableMonitor(monitorsCmdValues.name, apiClient, byteReader) + } + err = commsClient.RunAuthenticatedCommandWithRateLimitRetries(enableMonitorsFunc) + } + } + } + + return err +} diff --git a/pkg/cmd/monitorsEnable_test.go b/pkg/cmd/monitorsEnable_test.go new file mode 100644 index 00000000..5c439aca --- /dev/null +++ b/pkg/cmd/monitorsEnable_test.go @@ -0,0 +1,80 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package cmd + +import ( + "testing" + + "github.com/galasa-dev/cli/pkg/utils" + "github.com/stretchr/testify/assert" +) + +func TestCommandListContainsMonitorsEnableCommand(t *testing.T) { + /// Given... + factory := utils.NewMockFactory() + commands, _ := NewCommandCollection(factory) + + // When... + monitorsCommand, err := commands.GetCommand(COMMAND_NAME_MONITORS_ENABLE) + assert.Nil(t, err) + + // Then... + assert.NotNil(t, monitorsCommand) + assert.Equal(t, COMMAND_NAME_MONITORS_ENABLE, monitorsCommand.Name()) + assert.NotNil(t, monitorsCommand.Values()) + assert.IsType(t, &MonitorsEnableCmdValues{}, monitorsCommand.Values()) +} + +func TestMonitorsEnableHelpFlagSetCorrectly(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_ENABLE, factory, t) + + var args []string = []string{"monitors", "enable", "--help"} + + // When... + err := commandCollection.Execute(args) + + // Then... + checkOutput("Enables a given monitor in the Galasa service", "", factory, t) + + assert.Nil(t, err) +} + +func TestMonitorsEnableWithNameFlagReturnsOk(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_ENABLE, factory, t) + + var args []string = []string{"monitors", "enable", "--name", "myMonitor"} + + // When... + err := commandCollection.Execute(args) + + // Then... + assert.Nil(t, err) + + // Check what the user saw is reasonable. + checkOutput("", "", factory, t) +} + +func TestMonitorsEnableNoFlagsReturnsErrorMessage(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_ENABLE, factory, t) + + var args []string = []string{"monitors", "enable"} + + // When... + err := commandCollection.Execute(args) + + // Then... + assert.NotNil(t, err) + + // Check what the user saw is reasonable. + checkOutput("", `Error: required flag(s) "name" not set`, factory, t) +} + diff --git a/pkg/cmd/monitors_test.go b/pkg/cmd/monitors_test.go new file mode 100644 index 00000000..24cad76c --- /dev/null +++ b/pkg/cmd/monitors_test.go @@ -0,0 +1,59 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package cmd + +import ( + "testing" + + "github.com/galasa-dev/cli/pkg/utils" + "github.com/stretchr/testify/assert" +) + +func TestCommandListContainsMonitorsCommand(t *testing.T) { + /// Given... + factory := utils.NewMockFactory() + commands, _ := NewCommandCollection(factory) + + // When... + monitorsCommand, err := commands.GetCommand(COMMAND_NAME_MONITORS) + assert.Nil(t, err) + + // Then... + assert.NotNil(t, monitorsCommand) + assert.Equal(t, COMMAND_NAME_MONITORS, monitorsCommand.Name()) + assert.NotNil(t, monitorsCommand.Values()) + assert.IsType(t, &MonitorsCmdValues{}, monitorsCommand.Values()) +} + +func TestMonitorsHelpFlagSetCorrectly(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + + var args []string = []string{"monitors", "--help"} + + // When... + err := Execute(factory, args) + + // Then... + // Check what the user saw is reasonable. + checkOutput("The parent command for operations to manipulate monitors in the Galasa service", "", factory, t) + + assert.Nil(t, err) +} + +func TestMonitorsNoCommandsProducesUsageReport(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + var args []string = []string{"monitors"} + + // When... + err := Execute(factory, args) + + // Then... + assert.Nil(t, err) + + checkOutput("Usage:\n galasactl monitors [command]", "", factory, t) +} diff --git a/pkg/errors/errorMessage.go b/pkg/errors/errorMessage.go index 2cb18afc..1c5e4f5e 100644 --- a/pkg/errors/errorMessage.go +++ b/pkg/errors/errorMessage.go @@ -419,6 +419,18 @@ var ( GALASA_ERROR_UPDATE_USER_SERVER_REPORTED_ERROR = NewMessageType("GAL1216E: An attempt to update a user '%s' failed. Unexpected http status code %v received from the server. Error details from the server are: '%s'", 1216, STACK_TRACE_NOT_WANTED) GALASA_ERROR_UPDATE_USER_EXPLANATION_NOT_JSON = NewMessageType("GAL1217E: An attempt to update a user '%s' failed. Unexpected http status code %v received from the server. Error details from the server are not in the json format.", 1217, STACK_TRACE_NOT_WANTED) + // When updating a monitor... + GALASA_ERROR_UPDATE_MONITOR_REQUEST_FAILED = NewMessageType("GAL1218E: Failed to update a monitor named '%s'. Sending the put request to the Galasa service failed. Cause is %v", 1218, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_UPDATE_MONITOR_NO_RESPONSE_CONTENT = NewMessageType("GAL1219E: Failed to update a monitor named '%s'. Unexpected http status code %v received from the server.", 1219, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_UPDATE_MONITOR_RESPONSE_BODY_UNREADABLE = NewMessageType("GAL1220E: Failed to update a monitor named '%s'. Unexpected http status code %v received from the server. Error details from the server could not be read. Cause: %s", 1220, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_UPDATE_MONITOR_UNPARSEABLE_CONTENT = NewMessageType("GAL1221E: Failed to update a monitor named '%s'. Unexpected http status code %v received from the server. Error details from the server are not in a valid json format. Cause: '%s'", 1221, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_UPDATE_MONITOR_SERVER_REPORTED_ERROR = NewMessageType("GAL1222E: Failed to update a monitor named '%s'. Unexpected http status code %v received from the server. Error details from the server are: '%s'", 1222, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_UPDATE_MONITOR_EXPLANATION_NOT_JSON = NewMessageType("GAL1223E: Failed to update a monitor named '%s'. Unexpected http status code %v received from the server. Error details from the server are not in the json format.", 1223, STACK_TRACE_NOT_WANTED) + + // Getting a single monitor by name... + GALASA_ERROR_MONITOR_NAME_NOT_FOUND = NewMessageType("GAL1224E: Galasa Monitor named %v is not known on the Galasa service.", 1224, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_INVALID_MONITOR_NAME = NewMessageType("GAL1225E: Invalid monitor name provided. The name provided with the --name flag cannot be empty and must only contain characters in the following ranges: 'a'-'z', 'A'-'Z', '0'-'9', '-' (dash), '_' (underscore).", 1225, STACK_TRACE_NOT_WANTED) + // Warnings... GALASA_WARNING_MAVEN_NO_GALASA_OBR_REPO = NewMessageType("GAL2000W: Warning: Maven configuration file settings.xml should contain a reference to a Galasa repository so that the galasa OBR can be resolved. The official release repository is '%s', and 'pre-release' repository is '%s'", 2000, STACK_TRACE_WANTED) diff --git a/pkg/monitors/monitors.go b/pkg/monitors/monitors.go new file mode 100644 index 00000000..465beefb --- /dev/null +++ b/pkg/monitors/monitors.go @@ -0,0 +1,24 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package monitors + +import ( + "strings" + + galasaErrors "github.com/galasa-dev/cli/pkg/errors" + "github.com/galasa-dev/cli/pkg/utils" +) + +func validateMonitorName(monitorName string) (string, error) { + var err error + monitorName = strings.TrimSpace(monitorName) + + if monitorName == "" || !utils.IsNameValid(monitorName) { + err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_INVALID_MONITOR_NAME) + } + return monitorName, err +} diff --git a/pkg/monitors/monitorsEnable.go b/pkg/monitors/monitorsEnable.go new file mode 100644 index 00000000..a4f745e0 --- /dev/null +++ b/pkg/monitors/monitorsEnable.go @@ -0,0 +1,81 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package monitors + +import ( + "context" + "log" + "net/http" + + "github.com/galasa-dev/cli/pkg/embedded" + galasaErrors "github.com/galasa-dev/cli/pkg/errors" + "github.com/galasa-dev/cli/pkg/galasaapi" + "github.com/galasa-dev/cli/pkg/spi" +) + +func EnableMonitor( + monitorName string, + apiClient *galasaapi.APIClient, + byteReader spi.ByteReader, +) error { + var err error + + monitorName, err = validateMonitorName(monitorName) + if err == nil { + desiredEnabledState := true + err = setMonitorIsEnabledState(monitorName, desiredEnabledState, apiClient, byteReader) + } + + log.Printf("EnableMonitor exiting. err is %v\n", err) + return err +} + +func setMonitorIsEnabledState( + monitorName string, + isEnabled bool, + apiClient *galasaapi.APIClient, + byteReader spi.ByteReader, +) error { + var err error + var httpResponse *http.Response + var context context.Context = context.Background() + var restApiVersion string + + restApiVersion, err = embedded.GetGalasactlRestApiVersion() + + if err == nil { + requestBody := *galasaapi.NewUpdateGalasaMonitorRequest() + requestBody.SetIsEnabled(isEnabled) + + httpResponse, err = apiClient.MonitorsAPIApi.SetMonitorStatus(context, monitorName). + UpdateGalasaMonitorRequest(requestBody). + ClientApiVersion(restApiVersion). + Execute() + + if httpResponse != nil { + defer httpResponse.Body.Close() + } + + if err != nil { + if httpResponse == nil { + err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_UPDATE_MONITOR_REQUEST_FAILED, err.Error()) + } else { + err = galasaErrors.HttpResponseToGalasaError( + httpResponse, + monitorName, + byteReader, + galasaErrors.GALASA_ERROR_UPDATE_MONITOR_NO_RESPONSE_CONTENT, + galasaErrors.GALASA_ERROR_UPDATE_MONITOR_RESPONSE_BODY_UNREADABLE, + galasaErrors.GALASA_ERROR_UPDATE_MONITOR_UNPARSEABLE_CONTENT, + galasaErrors.GALASA_ERROR_UPDATE_MONITOR_SERVER_REPORTED_ERROR, + galasaErrors.GALASA_ERROR_UPDATE_MONITOR_EXPLANATION_NOT_JSON, + ) + } + } + } + return err +} diff --git a/pkg/monitors/monitorsEnable_test.go b/pkg/monitors/monitorsEnable_test.go new file mode 100644 index 00000000..7ea2af31 --- /dev/null +++ b/pkg/monitors/monitorsEnable_test.go @@ -0,0 +1,190 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package monitors + +import ( + "encoding/json" + "io" + "net/http" + "strconv" + "testing" + + "github.com/galasa-dev/cli/pkg/api" + "github.com/galasa-dev/cli/pkg/galasaapi" + "github.com/galasa-dev/cli/pkg/utils" + "github.com/stretchr/testify/assert" +) + +func readMonitorRequestBody(req *http.Request) galasaapi.UpdateGalasaMonitorRequest { + var monitorUpdateRequest galasaapi.UpdateGalasaMonitorRequest + requestBodyBytes, _ := io.ReadAll(req.Body) + defer req.Body.Close() + + _ = json.Unmarshal(requestBodyBytes, &monitorUpdateRequest) + return monitorUpdateRequest +} + +func TestCanEnableAMonitor(t *testing.T) { + // Given... + monitorName := "customManagerCleanup" + + // Create the expected HTTP interactions with the API server + putMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) + putMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + requestBody := readMonitorRequestBody(req) + assert.Equal(t, requestBody.GetIsEnabled(), true) + + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusNoContent) + } + + interactions := []utils.HttpInteraction{ + putMonitorInteraction, + } + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiServerUrl := server.Server.URL + apiClient := api.InitialiseAPI(apiServerUrl) + mockByteReader := utils.NewMockByteReader() + + // When... + err := EnableMonitor( + monitorName, + apiClient, + mockByteReader) + + // Then... + assert.Nil(t, err, "EnableMonitor returned an unexpected error") +} + +func TestEnableMonitorWithBlankNameDisplaysError(t *testing.T) { + // Given... + monitorName := " " + + // The client-side validation should fail, so no HTTP interactions will be performed + interactions := []utils.HttpInteraction{} + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiServerUrl := server.Server.URL + apiClient := api.InitialiseAPI(apiServerUrl) + mockByteReader := utils.NewMockByteReader() + + // When... + err := EnableMonitor( + monitorName, + apiClient, + mockByteReader) + + // Then... + assert.NotNil(t, err, "EnableMonitor did not return an error as expected") + consoleOutputText := err.Error() + assert.Contains(t, consoleOutputText, "GAL1225E") + assert.Contains(t, consoleOutputText, " Invalid monitor name provided") +} + +func TestEnableNonExistantMonitorDisplaysError(t *testing.T) { + // Given... + nonExistantMonitor := "monitorDoesNotExist123" + + // Create the expected HTTP interactions with the API server + enableMonitorInteraction := utils.NewHttpInteraction("/monitors/" + nonExistantMonitor, http.MethodPut) + enableMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusNotFound) + writer.Write([]byte(`{ "error_message": "No such monitor exists" }`)) + } + + interactions := []utils.HttpInteraction{ enableMonitorInteraction } + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiServerUrl := server.Server.URL + apiClient := api.InitialiseAPI(apiServerUrl) + mockByteReader := utils.NewMockByteReader() + + // When... + err := EnableMonitor( + nonExistantMonitor, + apiClient, + mockByteReader) + + // Then... + assert.NotNil(t, err, "EnableMonitor did not return an error but it should have") + assert.ErrorContains(t, err, "GAL1222E") +} + +func TestEnableMonitorFailsWithNoExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { + // Given... + monitorName := "myMonitor" + + // Create the expected HTTP interactions with the API server + enableMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) + enableMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + writer.WriteHeader(http.StatusInternalServerError) + } + + interactions := []utils.HttpInteraction{ + enableMonitorInteraction, + } + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiServerUrl := server.Server.URL + apiClient := api.InitialiseAPI(apiServerUrl) + mockByteReader := utils.NewMockByteReader() + + // When... + err := EnableMonitor( + monitorName, + apiClient, + mockByteReader) + + // Then... + assert.NotNil(t, err, "EnableMonitor did not return an error but it should have") + assert.ErrorContains(t, err, "GAL1219E") +} + +func TestEnableMonitorFailsWithNonJsonContentTypeExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { + // Given... + monitorName := "myMonitor" + + // Create the expected HTTP interactions with the API server + enableMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) + enableMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + writer.WriteHeader(http.StatusInternalServerError) + writer.Header().Set("Content-Type", "application/notJsonOnPurpose") + writer.Write([]byte("something not json but non-zero-length.")) + } + + interactions := []utils.HttpInteraction{ + enableMonitorInteraction, + } + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiServerUrl := server.Server.URL + apiClient := api.InitialiseAPI(apiServerUrl) + mockByteReader := utils.NewMockByteReader() + + // When... + err := EnableMonitor( + monitorName, + apiClient, + mockByteReader) + + // Then... + assert.NotNil(t, err, "EnableMonitor did not return an error but it should have") + assert.ErrorContains(t, err, strconv.Itoa(http.StatusInternalServerError)) + assert.ErrorContains(t, err, "GAL1223E") + assert.ErrorContains(t, err, "Error details from the server are not in the json format") +} From 6ea46e29a329bd5ccc501678113c6501ef491ba5 Mon Sep 17 00:00:00 2001 From: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> Date: Wed, 26 Mar 2025 10:45:35 +0000 Subject: [PATCH 2/7] Add monitors disable command Signed-off-by: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> --- docs/generated/galasactl_monitors.md | 1 + docs/generated/galasactl_monitors_disable.md | 33 ++++ docs/generated/galasactl_monitors_enable.md | 2 +- pkg/cmd/commandCollection.go | 4 + pkg/cmd/monitorsDisable.go | 139 +++++++++++++++ pkg/cmd/monitorsDisable_test.go | 80 +++++++++ pkg/cmd/monitorsEnable.go | 2 +- pkg/cmd/monitorsEnable_test.go | 2 +- pkg/monitors/monitorsDisable.go | 27 +++ pkg/monitors/monitorsDisable_test.go | 178 +++++++++++++++++++ pkg/monitors/monitorsEnable.go | 24 ++- 11 files changed, 483 insertions(+), 9 deletions(-) create mode 100644 docs/generated/galasactl_monitors_disable.md create mode 100644 pkg/cmd/monitorsDisable.go create mode 100644 pkg/cmd/monitorsDisable_test.go create mode 100644 pkg/monitors/monitorsDisable.go create mode 100644 pkg/monitors/monitorsDisable_test.go diff --git a/docs/generated/galasactl_monitors.md b/docs/generated/galasactl_monitors.md index 34ae1ede..a86f2e6c 100644 --- a/docs/generated/galasactl_monitors.md +++ b/docs/generated/galasactl_monitors.md @@ -25,5 +25,6 @@ The parent command for operations to manipulate monitors in the Galasa service ### SEE ALSO * [galasactl](galasactl.md) - CLI for Galasa +* [galasactl monitors disable](galasactl_monitors_disable.md) - Disable a monitor in the Galasa service * [galasactl monitors enable](galasactl_monitors_enable.md) - Enable a monitor in the Galasa service diff --git a/docs/generated/galasactl_monitors_disable.md b/docs/generated/galasactl_monitors_disable.md new file mode 100644 index 00000000..e8f2ffe2 --- /dev/null +++ b/docs/generated/galasactl_monitors_disable.md @@ -0,0 +1,33 @@ +## galasactl monitors disable + +Disable a monitor in the Galasa service + +### Synopsis + +Disables a monitor with the given name in the Galasa service + +``` +galasactl monitors disable [flags] +``` + +### Options + +``` + -h, --help Displays the options for the 'monitors disable' command. + --name string A mandatory flag that identifies the monitor to be manipulated by name. +``` + +### Options inherited from parent commands + +``` + -b, --bootstrap string Bootstrap URL. Should start with 'http://' or 'file://'. If it starts with neither, it is assumed to be a fully-qualified path. If missing, it defaults to use the 'bootstrap.properties' file in your GALASA_HOME. Example: http://example.com/bootstrap, file:///user/myuserid/.galasa/bootstrap.properties , file://C:/Users/myuserid/.galasa/bootstrap.properties + --galasahome string Path to a folder where Galasa will read and write files and configuration settings. The default is '${HOME}/.galasa'. This overrides the GALASA_HOME environment variable which may be set instead. + -l, --log string File to which log information will be sent. Any folder referred to must exist. An existing file will be overwritten. Specify "-" to log to stderr. Defaults to not logging. + --rate-limit-retries int The maximum number of retries that should be made when requests to the Galasa Service fail due to rate limits being exceeded. Must be a whole number. Defaults to 3 retries (default 3) + --rate-limit-retry-backoff-secs float The amount of time in seconds to wait before retrying a command if it failed due to rate limits being exceeded. Defaults to 1 second. (default 1) +``` + +### SEE ALSO + +* [galasactl monitors](galasactl_monitors.md) - Manage monitors in the Galasa service + diff --git a/docs/generated/galasactl_monitors_enable.md b/docs/generated/galasactl_monitors_enable.md index caf8ab33..61b4b23b 100644 --- a/docs/generated/galasactl_monitors_enable.md +++ b/docs/generated/galasactl_monitors_enable.md @@ -4,7 +4,7 @@ Enable a monitor in the Galasa service ### Synopsis -Enables a given monitor in the Galasa service +Enables a monitor with the given name in the Galasa service ``` galasactl monitors enable [flags] diff --git a/pkg/cmd/commandCollection.go b/pkg/cmd/commandCollection.go index 6490073c..3dd383ee 100644 --- a/pkg/cmd/commandCollection.go +++ b/pkg/cmd/commandCollection.go @@ -41,6 +41,7 @@ const ( COMMAND_NAME_LOCAL_INIT = "local init" COMMAND_NAME_MONITORS = "monitors" COMMAND_NAME_MONITORS_ENABLE = "monitors enable" + COMMAND_NAME_MONITORS_DISABLE = "monitors disable" COMMAND_NAME_PROPERTIES = "properties" COMMAND_NAME_PROPERTIES_GET = "properties get" COMMAND_NAME_PROPERTIES_SET = "properties set" @@ -441,16 +442,19 @@ func (commands *commandCollectionImpl) addMonitorsCommands(factory spi.Factory, var err error var monitorsCommand spi.GalasaCommand var monitorsEnableCommand spi.GalasaCommand + var monitorsDisableCommand spi.GalasaCommand monitorsCommand, err = NewMonitorsCmd(rootCommand, commsFlagSet) if err == nil { monitorsEnableCommand, err = NewMonitorsEnableCommand(factory, monitorsCommand, commsFlagSet) + monitorsDisableCommand, err = NewMonitorsDisableCommand(factory, monitorsCommand, commsFlagSet) } if err == nil { commands.commandMap[monitorsCommand.Name()] = monitorsCommand commands.commandMap[monitorsEnableCommand.Name()] = monitorsEnableCommand + commands.commandMap[monitorsDisableCommand.Name()] = monitorsDisableCommand } return err diff --git a/pkg/cmd/monitorsDisable.go b/pkg/cmd/monitorsDisable.go new file mode 100644 index 00000000..6cad647e --- /dev/null +++ b/pkg/cmd/monitorsDisable.go @@ -0,0 +1,139 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package cmd + +import ( + "log" + + "github.com/galasa-dev/cli/pkg/api" + "github.com/galasa-dev/cli/pkg/galasaapi" + "github.com/galasa-dev/cli/pkg/monitors" + "github.com/galasa-dev/cli/pkg/spi" + "github.com/galasa-dev/cli/pkg/utils" + "github.com/spf13/cobra" +) + +type MonitorsDisableCmdValues struct { +} + +type MonitorsDisableCommand struct { + values *MonitorsDisableCmdValues + cobraCommand *cobra.Command +} + +// ------------------------------------------------------------------------------------------------ +// Constructors methods +// ------------------------------------------------------------------------------------------------ +func NewMonitorsDisableCommand( + factory spi.Factory, + monitorsDisableCommand spi.GalasaCommand, + commsFlagSet GalasaFlagSet, +) (spi.GalasaCommand, error) { + + cmd := new(MonitorsDisableCommand) + + err := cmd.init(factory, monitorsDisableCommand, commsFlagSet) + return cmd, err +} + +// ------------------------------------------------------------------------------------------------ +// Public methods +// ------------------------------------------------------------------------------------------------ +func (cmd *MonitorsDisableCommand) Name() string { + return COMMAND_NAME_MONITORS_DISABLE +} + +func (cmd *MonitorsDisableCommand) CobraCommand() *cobra.Command { + return cmd.cobraCommand +} + +func (cmd *MonitorsDisableCommand) Values() interface{} { + return cmd.values +} + +// ------------------------------------------------------------------------------------------------ +// Private methods +// ------------------------------------------------------------------------------------------------ +func (cmd *MonitorsDisableCommand) init(factory spi.Factory, monitorsCommand spi.GalasaCommand, commsFlagSet GalasaFlagSet) error { + var err error + + cmd.values = &MonitorsDisableCmdValues{} + cmd.cobraCommand, err = cmd.createCobraCmd(factory, monitorsCommand, commsFlagSet.Values().(*CommsFlagSetValues)) + + return err +} + +func (cmd *MonitorsDisableCommand) createCobraCmd( + factory spi.Factory, + monitorsCommand spi.GalasaCommand, + commsFlagSetValues *CommsFlagSetValues, +) (*cobra.Command, error) { + + var err error + + monitorsCommandValues := monitorsCommand.Values().(*MonitorsCmdValues) + monitorsDisableCobraCmd := &cobra.Command{ + Use: "disable", + Short: "Disable a monitor in the Galasa service", + Long: "Disables a monitor with the given name in the Galasa service", + Aliases: []string{COMMAND_NAME_MONITORS_DISABLE}, + RunE: func(cobraCommand *cobra.Command, args []string) error { + return cmd.executeMonitorsDisable(factory, monitorsCommand.Values().(*MonitorsCmdValues), commsFlagSetValues) + }, + } + + addMonitorNameFlag(monitorsDisableCobraCmd, true, monitorsCommandValues) + + monitorsCommand.CobraCommand().AddCommand(monitorsDisableCobraCmd) + + return monitorsDisableCobraCmd, err +} + +func (cmd *MonitorsDisableCommand) executeMonitorsDisable( + factory spi.Factory, + monitorsCmdValues *MonitorsCmdValues, + commsFlagSetValues *CommsFlagSetValues, +) error { + + var err error + // Operations on the file system will all be relative to the current folder. + fileSystem := factory.GetFileSystem() + + err = utils.CaptureLog(fileSystem, commsFlagSetValues.logFileName) + if err == nil { + commsFlagSetValues.isCapturingLogs = true + + log.Println("Galasa CLI - Disable monitors from the Galasa service") + + env := factory.GetEnvironment() + + var galasaHome spi.GalasaHome + galasaHome, err = utils.NewGalasaHome(fileSystem, env, commsFlagSetValues.CmdParamGalasaHomePath) + if err == nil { + + var commsClient api.APICommsClient + commsClient, err = api.NewAPICommsClient( + commsFlagSetValues.bootstrap, + commsFlagSetValues.maxRetries, + commsFlagSetValues.retryBackoffSeconds, + factory, + galasaHome, + ) + + if err == nil { + + byteReader := factory.GetByteReader() + + disableMonitorsFunc := func(apiClient *galasaapi.APIClient) error { + return monitors.DisableMonitor(monitorsCmdValues.name, apiClient, byteReader) + } + err = commsClient.RunAuthenticatedCommandWithRateLimitRetries(disableMonitorsFunc) + } + } + } + + return err +} diff --git a/pkg/cmd/monitorsDisable_test.go b/pkg/cmd/monitorsDisable_test.go new file mode 100644 index 00000000..29ae601d --- /dev/null +++ b/pkg/cmd/monitorsDisable_test.go @@ -0,0 +1,80 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package cmd + +import ( + "testing" + + "github.com/galasa-dev/cli/pkg/utils" + "github.com/stretchr/testify/assert" +) + +func TestCommandListContainsMonitorsDisableCommand(t *testing.T) { + /// Given... + factory := utils.NewMockFactory() + commands, _ := NewCommandCollection(factory) + + // When... + monitorsCommand, err := commands.GetCommand(COMMAND_NAME_MONITORS_DISABLE) + assert.Nil(t, err) + + // Then... + assert.NotNil(t, monitorsCommand) + assert.Equal(t, COMMAND_NAME_MONITORS_DISABLE, monitorsCommand.Name()) + assert.NotNil(t, monitorsCommand.Values()) + assert.IsType(t, &MonitorsDisableCmdValues{}, monitorsCommand.Values()) +} + +func TestMonitorsDisableHelpFlagSetCorrectly(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_DISABLE, factory, t) + + var args []string = []string{"monitors", "disable", "--help"} + + // When... + err := commandCollection.Execute(args) + + // Then... + checkOutput("Disables a monitor with the given name in the Galasa service", "", factory, t) + + assert.Nil(t, err) +} + +func TestMonitorsDisableWithNameFlagReturnsOk(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_DISABLE, factory, t) + + var args []string = []string{"monitors", "disable", "--name", "myMonitor"} + + // When... + err := commandCollection.Execute(args) + + // Then... + assert.Nil(t, err) + + // Check what the user saw is reasonable. + checkOutput("", "", factory, t) +} + +func TestMonitorsDisableNoFlagsReturnsErrorMessage(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_DISABLE, factory, t) + + var args []string = []string{"monitors", "disable"} + + // When... + err := commandCollection.Execute(args) + + // Then... + assert.NotNil(t, err) + + // Check what the user saw is reasonable. + checkOutput("", `Error: required flag(s) "name" not set`, factory, t) +} + diff --git a/pkg/cmd/monitorsEnable.go b/pkg/cmd/monitorsEnable.go index ea5870dc..6685df79 100644 --- a/pkg/cmd/monitorsEnable.go +++ b/pkg/cmd/monitorsEnable.go @@ -78,7 +78,7 @@ func (cmd *MonitorsEnableCommand) createCobraCmd( monitorsEnableCobraCmd := &cobra.Command{ Use: "enable", Short: "Enable a monitor in the Galasa service", - Long: "Enables a given monitor in the Galasa service", + Long: "Enables a monitor with the given name in the Galasa service", Aliases: []string{COMMAND_NAME_MONITORS_ENABLE}, RunE: func(cobraCommand *cobra.Command, args []string) error { return cmd.executeMonitorsEnable(factory, monitorsCommand.Values().(*MonitorsCmdValues), commsFlagSetValues) diff --git a/pkg/cmd/monitorsEnable_test.go b/pkg/cmd/monitorsEnable_test.go index 5c439aca..de3b6d3a 100644 --- a/pkg/cmd/monitorsEnable_test.go +++ b/pkg/cmd/monitorsEnable_test.go @@ -39,7 +39,7 @@ func TestMonitorsEnableHelpFlagSetCorrectly(t *testing.T) { err := commandCollection.Execute(args) // Then... - checkOutput("Enables a given monitor in the Galasa service", "", factory, t) + checkOutput("Enables a monitor with the given name in the Galasa service", "", factory, t) assert.Nil(t, err) } diff --git a/pkg/monitors/monitorsDisable.go b/pkg/monitors/monitorsDisable.go new file mode 100644 index 00000000..8aae66a1 --- /dev/null +++ b/pkg/monitors/monitorsDisable.go @@ -0,0 +1,27 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package monitors + +import ( + "log" + "github.com/galasa-dev/cli/pkg/galasaapi" + "github.com/galasa-dev/cli/pkg/spi" +) + +func DisableMonitor( + monitorName string, + apiClient *galasaapi.APIClient, + byteReader spi.ByteReader, +) error { + var err error + + desiredEnabledState := false + err = setMonitorIsEnabledState(monitorName, desiredEnabledState, apiClient, byteReader) + + log.Printf("DisableMonitor exiting. err is %v\n", err) + return err +} diff --git a/pkg/monitors/monitorsDisable_test.go b/pkg/monitors/monitorsDisable_test.go new file mode 100644 index 00000000..5e0ee6f2 --- /dev/null +++ b/pkg/monitors/monitorsDisable_test.go @@ -0,0 +1,178 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package monitors + +import ( + "net/http" + "strconv" + "testing" + + "github.com/galasa-dev/cli/pkg/api" + "github.com/galasa-dev/cli/pkg/utils" + "github.com/stretchr/testify/assert" +) + +func TestCanDisableAMonitor(t *testing.T) { + // Given... + monitorName := "customManagerCleanup" + + // Create the expected HTTP interactions with the API server + putMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) + putMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + requestBody := readMonitorRequestBody(req) + assert.Equal(t, requestBody.GetIsEnabled(), false) + + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusNoContent) + } + + interactions := []utils.HttpInteraction{ + putMonitorInteraction, + } + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiServerUrl := server.Server.URL + apiClient := api.InitialiseAPI(apiServerUrl) + mockByteReader := utils.NewMockByteReader() + + // When... + err := DisableMonitor( + monitorName, + apiClient, + mockByteReader) + + // Then... + assert.Nil(t, err, "DisableMonitor returned an unexpected error") +} + +func TestDisableMonitorWithBlankNameDisplaysError(t *testing.T) { + // Given... + monitorName := " " + + // The client-side validation should fail, so no HTTP interactions will be performed + interactions := []utils.HttpInteraction{} + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiServerUrl := server.Server.URL + apiClient := api.InitialiseAPI(apiServerUrl) + mockByteReader := utils.NewMockByteReader() + + // When... + err := DisableMonitor( + monitorName, + apiClient, + mockByteReader) + + // Then... + assert.NotNil(t, err, "DisableMonitor did not return an error as expected") + consoleOutputText := err.Error() + assert.Contains(t, consoleOutputText, "GAL1225E") + assert.Contains(t, consoleOutputText, " Invalid monitor name provided") +} + +func TestDisableNonExistantMonitorDisplaysError(t *testing.T) { + // Given... + nonExistantMonitor := "monitorDoesNotExist123" + + // Create the expected HTTP interactions with the API server + disableMonitorInteraction := utils.NewHttpInteraction("/monitors/" + nonExistantMonitor, http.MethodPut) + disableMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusNotFound) + writer.Write([]byte(`{ "error_message": "No such monitor exists" }`)) + } + + interactions := []utils.HttpInteraction{ disableMonitorInteraction } + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiServerUrl := server.Server.URL + apiClient := api.InitialiseAPI(apiServerUrl) + mockByteReader := utils.NewMockByteReader() + + // When... + err := DisableMonitor( + nonExistantMonitor, + apiClient, + mockByteReader) + + // Then... + assert.NotNil(t, err, "DisableMonitor did not return an error but it should have") + assert.ErrorContains(t, err, "GAL1222E") +} + +func TestDisableMonitorFailsWithNoExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { + // Given... + monitorName := "myMonitor" + + // Create the expected HTTP interactions with the API server + disableMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) + disableMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + writer.WriteHeader(http.StatusInternalServerError) + } + + interactions := []utils.HttpInteraction{ + disableMonitorInteraction, + } + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiServerUrl := server.Server.URL + apiClient := api.InitialiseAPI(apiServerUrl) + mockByteReader := utils.NewMockByteReader() + + // When... + err := DisableMonitor( + monitorName, + apiClient, + mockByteReader) + + // Then... + assert.NotNil(t, err, "DisableMonitor did not return an error but it should have") + assert.ErrorContains(t, err, "GAL1219E") +} + +func TestDisableMonitorFailsWithNonJsonContentTypeExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { + // Given... + monitorName := "myMonitor" + + // Create the expected HTTP interactions with the API server + disableMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) + disableMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + writer.WriteHeader(http.StatusInternalServerError) + writer.Header().Set("Content-Type", "application/notJsonOnPurpose") + writer.Write([]byte("something not json but non-zero-length.")) + } + + interactions := []utils.HttpInteraction{ + disableMonitorInteraction, + } + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiServerUrl := server.Server.URL + apiClient := api.InitialiseAPI(apiServerUrl) + mockByteReader := utils.NewMockByteReader() + + // When... + err := DisableMonitor( + monitorName, + apiClient, + mockByteReader) + + // Then... + assert.NotNil(t, err, "DisableMonitor did not return an error but it should have") + assert.ErrorContains(t, err, strconv.Itoa(http.StatusInternalServerError)) + assert.ErrorContains(t, err, "GAL1223E") + assert.ErrorContains(t, err, "Error details from the server are not in the json format") +} diff --git a/pkg/monitors/monitorsEnable.go b/pkg/monitors/monitorsEnable.go index a4f745e0..af1bc17f 100644 --- a/pkg/monitors/monitorsEnable.go +++ b/pkg/monitors/monitorsEnable.go @@ -24,17 +24,30 @@ func EnableMonitor( ) error { var err error + desiredEnabledState := true + err = setMonitorIsEnabledState(monitorName, desiredEnabledState, apiClient, byteReader) + + log.Printf("EnableMonitor exiting. err is %v\n", err) + return err +} + +func setMonitorIsEnabledState( + monitorName string, + isEnabled bool, + apiClient *galasaapi.APIClient, + byteReader spi.ByteReader, +) error { + var err error + monitorName, err = validateMonitorName(monitorName) if err == nil { - desiredEnabledState := true - err = setMonitorIsEnabledState(monitorName, desiredEnabledState, apiClient, byteReader) + err = sendUpdateMonitorStateRequest(monitorName, isEnabled, apiClient, byteReader) } - log.Printf("EnableMonitor exiting. err is %v\n", err) return err } -func setMonitorIsEnabledState( +func sendUpdateMonitorStateRequest( monitorName string, isEnabled bool, apiClient *galasaapi.APIClient, @@ -44,9 +57,8 @@ func setMonitorIsEnabledState( var httpResponse *http.Response var context context.Context = context.Background() var restApiVersion string - restApiVersion, err = embedded.GetGalasactlRestApiVersion() - + if err == nil { requestBody := *galasaapi.NewUpdateGalasaMonitorRequest() requestBody.SetIsEnabled(isEnabled) From 5a140cb5703cdfd543e61ca71bf77d6ae58946fc Mon Sep 17 00:00:00 2001 From: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> Date: Wed, 26 Mar 2025 13:40:29 +0000 Subject: [PATCH 3/7] Add monitors enable and disable commands to README Signed-off-by: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> --- README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README.md b/README.md index 16381df5..d06aa501 100644 --- a/README.md +++ b/README.md @@ -457,6 +457,33 @@ The run "C1234" can be cancelled using the following command: galasactl runs cancel --name C1234 ``` +## monitors enable + +This command can be used to enable a monitor in the Galasa service. The name of the monitor to be enabled must be provided using the `--name` flag. + +### Examples + +To enable a monitor named "myCustomMonitor": + +``` +galasactl monitors enable --name myCustomMonitor +``` + +For a complete list of supported parameters see [here](./docs/generated/galasactl_monitors_enable.md). + +## monitors disable + +This command can be used to disable a monitor in the Galasa service. The name of the monitor to be disabled must be provided using the `--name` flag. + +### Examples + +To disable a monitor named "myCustomMonitor": + +``` +galasactl monitors disable --name myCustomMonitor +``` + +For a complete list of supported parameters see [here](./docs/generated/galasactl_monitors_disable.md). ## properties get This command retrieves details of properties in a namespace. From fefd27940c480f27810e1253c4342f7b1930cdb4 Mon Sep 17 00:00:00 2001 From: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> Date: Thu, 27 Mar 2025 14:43:09 +0000 Subject: [PATCH 4/7] Address review comments Signed-off-by: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> --- README.md | 18 +- docs/generated/errors-list.md | 2 +- docs/generated/galasactl_monitors.md | 3 +- docs/generated/galasactl_monitors_set.md | 34 ++++ pkg/cmd/commandCollection.go | 12 +- pkg/cmd/monitorsDisable.go | 139 -------------- pkg/cmd/monitorsDisable_test.go | 80 -------- pkg/cmd/{monitorsEnable.go => monitorsSet.go} | 69 ++++--- ...torsEnable_test.go => monitorsSet_test.go} | 45 +++-- pkg/errors/errorMessage.go | 2 + pkg/monitors/monitorsDisable.go | 27 --- pkg/monitors/monitorsDisable_test.go | 178 ------------------ .../{monitorsEnable.go => monitorsSet.go} | 30 ++- ...torsEnable_test.go => monitorsSet_test.go} | 63 ++++++- 14 files changed, 195 insertions(+), 507 deletions(-) create mode 100644 docs/generated/galasactl_monitors_set.md delete mode 100644 pkg/cmd/monitorsDisable.go delete mode 100644 pkg/cmd/monitorsDisable_test.go rename pkg/cmd/{monitorsEnable.go => monitorsSet.go} (58%) rename pkg/cmd/{monitorsEnable_test.go => monitorsSet_test.go} (51%) delete mode 100644 pkg/monitors/monitorsDisable.go delete mode 100644 pkg/monitors/monitorsDisable_test.go rename pkg/monitors/{monitorsEnable.go => monitorsSet.go} (73%) rename pkg/monitors/{monitorsEnable_test.go => monitorsSet_test.go} (77%) diff --git a/README.md b/README.md index d06aa501..b1405b7a 100644 --- a/README.md +++ b/README.md @@ -457,33 +457,25 @@ The run "C1234" can be cancelled using the following command: galasactl runs cancel --name C1234 ``` -## monitors enable +## monitors set -This command can be used to enable a monitor in the Galasa service. The name of the monitor to be enabled must be provided using the `--name` flag. +This command can be used to update a monitor in the Galasa service. The name of the monitor to be enabled must be provided using the `--name` flag. ### Examples To enable a monitor named "myCustomMonitor": ``` -galasactl monitors enable --name myCustomMonitor +galasactl monitors set --name myCustomMonitor --is-enabled true ``` -For a complete list of supported parameters see [here](./docs/generated/galasactl_monitors_enable.md). - -## monitors disable - -This command can be used to disable a monitor in the Galasa service. The name of the monitor to be disabled must be provided using the `--name` flag. - -### Examples - To disable a monitor named "myCustomMonitor": ``` -galasactl monitors disable --name myCustomMonitor +galasactl monitors set --name myCustomMonitor --is-enabled false ``` -For a complete list of supported parameters see [here](./docs/generated/galasactl_monitors_disable.md). +For a complete list of supported parameters see [here](./docs/generated/galasactl_monitors_set.md). ## properties get This command retrieves details of properties in a namespace. diff --git a/docs/generated/errors-list.md b/docs/generated/errors-list.md index 56086191..9654a85f 100644 --- a/docs/generated/errors-list.md +++ b/docs/generated/errors-list.md @@ -222,7 +222,7 @@ The `galasactl` tool can generate the following errors: - GAL1223E: Failed to update a monitor named '{}'. Unexpected http status code {} received from the server. Error details from the server are not in the json format. - GAL1224E: Galasa Monitor named {} is not known on the Galasa service. - GAL1225E: Invalid monitor name provided. The name provided with the --name flag cannot be empty and must only contain characters in the following ranges: 'a'-'z', 'A'-'Z', '0'-'9', '-' (dash), '_' (underscore). -- GAL1226E: Internal failure. Contents of gzip could be read, but not decoded. New gzip reader failed: file: {} error: {} +- GAL1226E: Invalid '--is-enabled' value provided. Supported values are 'true' and 'false'. Check your command parameters and try again. - GAL1227E: Internal failure. Contents of gzip could not be decoded. {} error: {} - GAL1228E: Internal failure. Contents of gzip could not be encoded and compressed. {} error: {} - GAL1229E: Internal failure. Contents of gzip could not be flushed while encoding and compressing. {} error: {} diff --git a/docs/generated/galasactl_monitors.md b/docs/generated/galasactl_monitors.md index a86f2e6c..2d4b9cf3 100644 --- a/docs/generated/galasactl_monitors.md +++ b/docs/generated/galasactl_monitors.md @@ -25,6 +25,5 @@ The parent command for operations to manipulate monitors in the Galasa service ### SEE ALSO * [galasactl](galasactl.md) - CLI for Galasa -* [galasactl monitors disable](galasactl_monitors_disable.md) - Disable a monitor in the Galasa service -* [galasactl monitors enable](galasactl_monitors_enable.md) - Enable a monitor in the Galasa service +* [galasactl monitors set](galasactl_monitors_set.md) - Update a monitor in the Galasa service diff --git a/docs/generated/galasactl_monitors_set.md b/docs/generated/galasactl_monitors_set.md new file mode 100644 index 00000000..d6444d34 --- /dev/null +++ b/docs/generated/galasactl_monitors_set.md @@ -0,0 +1,34 @@ +## galasactl monitors set + +Update a monitor in the Galasa service + +### Synopsis + +Updates a monitor with the given name in the Galasa service + +``` +galasactl monitors set [flags] +``` + +### Options + +``` + -h, --help Displays the options for the 'monitors set' command. + --is-enabled string A boolean flag that determines whether the given monitor should be enabled or disabled. Supported values are 'true' and 'false'. + --name string A mandatory flag that identifies the monitor to be manipulated by name. +``` + +### Options inherited from parent commands + +``` + -b, --bootstrap string Bootstrap URL. Should start with 'http://' or 'file://'. If it starts with neither, it is assumed to be a fully-qualified path. If missing, it defaults to use the 'bootstrap.properties' file in your GALASA_HOME. Example: http://example.com/bootstrap, file:///user/myuserid/.galasa/bootstrap.properties , file://C:/Users/myuserid/.galasa/bootstrap.properties + --galasahome string Path to a folder where Galasa will read and write files and configuration settings. The default is '${HOME}/.galasa'. This overrides the GALASA_HOME environment variable which may be set instead. + -l, --log string File to which log information will be sent. Any folder referred to must exist. An existing file will be overwritten. Specify "-" to log to stderr. Defaults to not logging. + --rate-limit-retries int The maximum number of retries that should be made when requests to the Galasa Service fail due to rate limits being exceeded. Must be a whole number. Defaults to 3 retries (default 3) + --rate-limit-retry-backoff-secs float The amount of time in seconds to wait before retrying a command if it failed due to rate limits being exceeded. Defaults to 1 second. (default 1) +``` + +### SEE ALSO + +* [galasactl monitors](galasactl_monitors.md) - Manage monitors in the Galasa service + diff --git a/pkg/cmd/commandCollection.go b/pkg/cmd/commandCollection.go index 3dd383ee..9e3cf947 100644 --- a/pkg/cmd/commandCollection.go +++ b/pkg/cmd/commandCollection.go @@ -40,8 +40,7 @@ const ( COMMAND_NAME_LOCAL = "local" COMMAND_NAME_LOCAL_INIT = "local init" COMMAND_NAME_MONITORS = "monitors" - COMMAND_NAME_MONITORS_ENABLE = "monitors enable" - COMMAND_NAME_MONITORS_DISABLE = "monitors disable" + COMMAND_NAME_MONITORS_SET = "monitors set" COMMAND_NAME_PROPERTIES = "properties" COMMAND_NAME_PROPERTIES_GET = "properties get" COMMAND_NAME_PROPERTIES_SET = "properties set" @@ -441,20 +440,17 @@ func (commands *commandCollectionImpl) addMonitorsCommands(factory spi.Factory, var err error var monitorsCommand spi.GalasaCommand - var monitorsEnableCommand spi.GalasaCommand - var monitorsDisableCommand spi.GalasaCommand + var monitorsSetCommand spi.GalasaCommand monitorsCommand, err = NewMonitorsCmd(rootCommand, commsFlagSet) if err == nil { - monitorsEnableCommand, err = NewMonitorsEnableCommand(factory, monitorsCommand, commsFlagSet) - monitorsDisableCommand, err = NewMonitorsDisableCommand(factory, monitorsCommand, commsFlagSet) + monitorsSetCommand, err = NewMonitorsSetCommand(factory, monitorsCommand, commsFlagSet) } if err == nil { commands.commandMap[monitorsCommand.Name()] = monitorsCommand - commands.commandMap[monitorsEnableCommand.Name()] = monitorsEnableCommand - commands.commandMap[monitorsDisableCommand.Name()] = monitorsDisableCommand + commands.commandMap[monitorsSetCommand.Name()] = monitorsSetCommand } return err diff --git a/pkg/cmd/monitorsDisable.go b/pkg/cmd/monitorsDisable.go deleted file mode 100644 index 6cad647e..00000000 --- a/pkg/cmd/monitorsDisable.go +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright contributors to the Galasa project - * - * SPDX-License-Identifier: EPL-2.0 - */ -package cmd - -import ( - "log" - - "github.com/galasa-dev/cli/pkg/api" - "github.com/galasa-dev/cli/pkg/galasaapi" - "github.com/galasa-dev/cli/pkg/monitors" - "github.com/galasa-dev/cli/pkg/spi" - "github.com/galasa-dev/cli/pkg/utils" - "github.com/spf13/cobra" -) - -type MonitorsDisableCmdValues struct { -} - -type MonitorsDisableCommand struct { - values *MonitorsDisableCmdValues - cobraCommand *cobra.Command -} - -// ------------------------------------------------------------------------------------------------ -// Constructors methods -// ------------------------------------------------------------------------------------------------ -func NewMonitorsDisableCommand( - factory spi.Factory, - monitorsDisableCommand spi.GalasaCommand, - commsFlagSet GalasaFlagSet, -) (spi.GalasaCommand, error) { - - cmd := new(MonitorsDisableCommand) - - err := cmd.init(factory, monitorsDisableCommand, commsFlagSet) - return cmd, err -} - -// ------------------------------------------------------------------------------------------------ -// Public methods -// ------------------------------------------------------------------------------------------------ -func (cmd *MonitorsDisableCommand) Name() string { - return COMMAND_NAME_MONITORS_DISABLE -} - -func (cmd *MonitorsDisableCommand) CobraCommand() *cobra.Command { - return cmd.cobraCommand -} - -func (cmd *MonitorsDisableCommand) Values() interface{} { - return cmd.values -} - -// ------------------------------------------------------------------------------------------------ -// Private methods -// ------------------------------------------------------------------------------------------------ -func (cmd *MonitorsDisableCommand) init(factory spi.Factory, monitorsCommand spi.GalasaCommand, commsFlagSet GalasaFlagSet) error { - var err error - - cmd.values = &MonitorsDisableCmdValues{} - cmd.cobraCommand, err = cmd.createCobraCmd(factory, monitorsCommand, commsFlagSet.Values().(*CommsFlagSetValues)) - - return err -} - -func (cmd *MonitorsDisableCommand) createCobraCmd( - factory spi.Factory, - monitorsCommand spi.GalasaCommand, - commsFlagSetValues *CommsFlagSetValues, -) (*cobra.Command, error) { - - var err error - - monitorsCommandValues := monitorsCommand.Values().(*MonitorsCmdValues) - monitorsDisableCobraCmd := &cobra.Command{ - Use: "disable", - Short: "Disable a monitor in the Galasa service", - Long: "Disables a monitor with the given name in the Galasa service", - Aliases: []string{COMMAND_NAME_MONITORS_DISABLE}, - RunE: func(cobraCommand *cobra.Command, args []string) error { - return cmd.executeMonitorsDisable(factory, monitorsCommand.Values().(*MonitorsCmdValues), commsFlagSetValues) - }, - } - - addMonitorNameFlag(monitorsDisableCobraCmd, true, monitorsCommandValues) - - monitorsCommand.CobraCommand().AddCommand(monitorsDisableCobraCmd) - - return monitorsDisableCobraCmd, err -} - -func (cmd *MonitorsDisableCommand) executeMonitorsDisable( - factory spi.Factory, - monitorsCmdValues *MonitorsCmdValues, - commsFlagSetValues *CommsFlagSetValues, -) error { - - var err error - // Operations on the file system will all be relative to the current folder. - fileSystem := factory.GetFileSystem() - - err = utils.CaptureLog(fileSystem, commsFlagSetValues.logFileName) - if err == nil { - commsFlagSetValues.isCapturingLogs = true - - log.Println("Galasa CLI - Disable monitors from the Galasa service") - - env := factory.GetEnvironment() - - var galasaHome spi.GalasaHome - galasaHome, err = utils.NewGalasaHome(fileSystem, env, commsFlagSetValues.CmdParamGalasaHomePath) - if err == nil { - - var commsClient api.APICommsClient - commsClient, err = api.NewAPICommsClient( - commsFlagSetValues.bootstrap, - commsFlagSetValues.maxRetries, - commsFlagSetValues.retryBackoffSeconds, - factory, - galasaHome, - ) - - if err == nil { - - byteReader := factory.GetByteReader() - - disableMonitorsFunc := func(apiClient *galasaapi.APIClient) error { - return monitors.DisableMonitor(monitorsCmdValues.name, apiClient, byteReader) - } - err = commsClient.RunAuthenticatedCommandWithRateLimitRetries(disableMonitorsFunc) - } - } - } - - return err -} diff --git a/pkg/cmd/monitorsDisable_test.go b/pkg/cmd/monitorsDisable_test.go deleted file mode 100644 index 29ae601d..00000000 --- a/pkg/cmd/monitorsDisable_test.go +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright contributors to the Galasa project - * - * SPDX-License-Identifier: EPL-2.0 - */ -package cmd - -import ( - "testing" - - "github.com/galasa-dev/cli/pkg/utils" - "github.com/stretchr/testify/assert" -) - -func TestCommandListContainsMonitorsDisableCommand(t *testing.T) { - /// Given... - factory := utils.NewMockFactory() - commands, _ := NewCommandCollection(factory) - - // When... - monitorsCommand, err := commands.GetCommand(COMMAND_NAME_MONITORS_DISABLE) - assert.Nil(t, err) - - // Then... - assert.NotNil(t, monitorsCommand) - assert.Equal(t, COMMAND_NAME_MONITORS_DISABLE, monitorsCommand.Name()) - assert.NotNil(t, monitorsCommand.Values()) - assert.IsType(t, &MonitorsDisableCmdValues{}, monitorsCommand.Values()) -} - -func TestMonitorsDisableHelpFlagSetCorrectly(t *testing.T) { - // Given... - factory := utils.NewMockFactory() - commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_DISABLE, factory, t) - - var args []string = []string{"monitors", "disable", "--help"} - - // When... - err := commandCollection.Execute(args) - - // Then... - checkOutput("Disables a monitor with the given name in the Galasa service", "", factory, t) - - assert.Nil(t, err) -} - -func TestMonitorsDisableWithNameFlagReturnsOk(t *testing.T) { - // Given... - factory := utils.NewMockFactory() - commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_DISABLE, factory, t) - - var args []string = []string{"monitors", "disable", "--name", "myMonitor"} - - // When... - err := commandCollection.Execute(args) - - // Then... - assert.Nil(t, err) - - // Check what the user saw is reasonable. - checkOutput("", "", factory, t) -} - -func TestMonitorsDisableNoFlagsReturnsErrorMessage(t *testing.T) { - // Given... - factory := utils.NewMockFactory() - commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_DISABLE, factory, t) - - var args []string = []string{"monitors", "disable"} - - // When... - err := commandCollection.Execute(args) - - // Then... - assert.NotNil(t, err) - - // Check what the user saw is reasonable. - checkOutput("", `Error: required flag(s) "name" not set`, factory, t) -} - diff --git a/pkg/cmd/monitorsEnable.go b/pkg/cmd/monitorsSet.go similarity index 58% rename from pkg/cmd/monitorsEnable.go rename to pkg/cmd/monitorsSet.go index 6685df79..21296dda 100644 --- a/pkg/cmd/monitorsEnable.go +++ b/pkg/cmd/monitorsSet.go @@ -16,57 +16,58 @@ import ( "github.com/spf13/cobra" ) -type MonitorsEnableCmdValues struct { +type MonitorsSetCmdValues struct { + isEnabledStr string } -type MonitorsEnableCommand struct { - values *MonitorsEnableCmdValues +type MonitorsSetCommand struct { + values *MonitorsSetCmdValues cobraCommand *cobra.Command } // ------------------------------------------------------------------------------------------------ // Constructors methods // ------------------------------------------------------------------------------------------------ -func NewMonitorsEnableCommand( +func NewMonitorsSetCommand( factory spi.Factory, - monitorsEnableCommand spi.GalasaCommand, + monitorsSetCommand spi.GalasaCommand, commsFlagSet GalasaFlagSet, ) (spi.GalasaCommand, error) { - cmd := new(MonitorsEnableCommand) + cmd := new(MonitorsSetCommand) - err := cmd.init(factory, monitorsEnableCommand, commsFlagSet) + err := cmd.init(factory, monitorsSetCommand, commsFlagSet) return cmd, err } // ------------------------------------------------------------------------------------------------ // Public methods // ------------------------------------------------------------------------------------------------ -func (cmd *MonitorsEnableCommand) Name() string { - return COMMAND_NAME_MONITORS_ENABLE +func (cmd *MonitorsSetCommand) Name() string { + return COMMAND_NAME_MONITORS_SET } -func (cmd *MonitorsEnableCommand) CobraCommand() *cobra.Command { +func (cmd *MonitorsSetCommand) CobraCommand() *cobra.Command { return cmd.cobraCommand } -func (cmd *MonitorsEnableCommand) Values() interface{} { +func (cmd *MonitorsSetCommand) Values() interface{} { return cmd.values } // ------------------------------------------------------------------------------------------------ // Private methods // ------------------------------------------------------------------------------------------------ -func (cmd *MonitorsEnableCommand) init(factory spi.Factory, monitorsCommand spi.GalasaCommand, commsFlagSet GalasaFlagSet) error { +func (cmd *MonitorsSetCommand) init(factory spi.Factory, monitorsCommand spi.GalasaCommand, commsFlagSet GalasaFlagSet) error { var err error - cmd.values = &MonitorsEnableCmdValues{} + cmd.values = &MonitorsSetCmdValues{} cmd.cobraCommand, err = cmd.createCobraCmd(factory, monitorsCommand, commsFlagSet.Values().(*CommsFlagSetValues)) return err } -func (cmd *MonitorsEnableCommand) createCobraCmd( +func (cmd *MonitorsSetCommand) createCobraCmd( factory spi.Factory, monitorsCommand spi.GalasaCommand, commsFlagSetValues *CommsFlagSetValues, @@ -75,24 +76,31 @@ func (cmd *MonitorsEnableCommand) createCobraCmd( var err error monitorsCommandValues := monitorsCommand.Values().(*MonitorsCmdValues) - monitorsEnableCobraCmd := &cobra.Command{ - Use: "enable", - Short: "Enable a monitor in the Galasa service", - Long: "Enables a monitor with the given name in the Galasa service", - Aliases: []string{COMMAND_NAME_MONITORS_ENABLE}, + monitorsSetCobraCmd := &cobra.Command{ + Use: "set", + Short: "Update a monitor in the Galasa service", + Long: "Updates a monitor with the given name in the Galasa service", + Aliases: []string{COMMAND_NAME_MONITORS_SET}, RunE: func(cobraCommand *cobra.Command, args []string) error { - return cmd.executeMonitorsEnable(factory, monitorsCommand.Values().(*MonitorsCmdValues), commsFlagSetValues) + return cmd.executeMonitorsSet(factory, monitorsCommand.Values().(*MonitorsCmdValues), commsFlagSetValues) }, } - addMonitorNameFlag(monitorsEnableCobraCmd, true, monitorsCommandValues) + addMonitorNameFlag(monitorsSetCobraCmd, true, monitorsCommandValues) + isEnabledFlag := "is-enabled" - monitorsCommand.CobraCommand().AddCommand(monitorsEnableCobraCmd) + monitorsSetCobraCmd.Flags().StringVar(&cmd.values.isEnabledStr, isEnabledFlag, "", "A boolean flag that determines whether the given monitor should be enabled or disabled. Supported values are 'true' and 'false'.") - return monitorsEnableCobraCmd, err + monitorsSetCobraCmd.MarkFlagsOneRequired( + isEnabledFlag, + ) + + monitorsCommand.CobraCommand().AddCommand(monitorsSetCobraCmd) + + return monitorsSetCobraCmd, err } -func (cmd *MonitorsEnableCommand) executeMonitorsEnable( +func (cmd *MonitorsSetCommand) executeMonitorsSet( factory spi.Factory, monitorsCmdValues *MonitorsCmdValues, commsFlagSetValues *CommsFlagSetValues, @@ -106,7 +114,7 @@ func (cmd *MonitorsEnableCommand) executeMonitorsEnable( if err == nil { commsFlagSetValues.isCapturingLogs = true - log.Println("Galasa CLI - Enable monitors from the Galasa service") + log.Println("Galasa CLI - Update monitors in the Galasa service") env := factory.GetEnvironment() @@ -127,10 +135,15 @@ func (cmd *MonitorsEnableCommand) executeMonitorsEnable( byteReader := factory.GetByteReader() - enableMonitorsFunc := func(apiClient *galasaapi.APIClient) error { - return monitors.EnableMonitor(monitorsCmdValues.name, apiClient, byteReader) + setMonitorsFunc := func(apiClient *galasaapi.APIClient) error { + return monitors.SetMonitor( + monitorsCmdValues.name, + cmd.values.isEnabledStr, + apiClient, + byteReader, + ) } - err = commsClient.RunAuthenticatedCommandWithRateLimitRetries(enableMonitorsFunc) + err = commsClient.RunAuthenticatedCommandWithRateLimitRetries(setMonitorsFunc) } } } diff --git a/pkg/cmd/monitorsEnable_test.go b/pkg/cmd/monitorsSet_test.go similarity index 51% rename from pkg/cmd/monitorsEnable_test.go rename to pkg/cmd/monitorsSet_test.go index de3b6d3a..bbe398c4 100644 --- a/pkg/cmd/monitorsEnable_test.go +++ b/pkg/cmd/monitorsSet_test.go @@ -12,44 +12,44 @@ import ( "github.com/stretchr/testify/assert" ) -func TestCommandListContainsMonitorsEnableCommand(t *testing.T) { +func TestCommandListContainsMonitorsSetCommand(t *testing.T) { /// Given... factory := utils.NewMockFactory() commands, _ := NewCommandCollection(factory) // When... - monitorsCommand, err := commands.GetCommand(COMMAND_NAME_MONITORS_ENABLE) + monitorsCommand, err := commands.GetCommand(COMMAND_NAME_MONITORS_SET) assert.Nil(t, err) // Then... assert.NotNil(t, monitorsCommand) - assert.Equal(t, COMMAND_NAME_MONITORS_ENABLE, monitorsCommand.Name()) + assert.Equal(t, COMMAND_NAME_MONITORS_SET, monitorsCommand.Name()) assert.NotNil(t, monitorsCommand.Values()) - assert.IsType(t, &MonitorsEnableCmdValues{}, monitorsCommand.Values()) + assert.IsType(t, &MonitorsSetCmdValues{}, monitorsCommand.Values()) } -func TestMonitorsEnableHelpFlagSetCorrectly(t *testing.T) { +func TestMonitorsSetHelpFlagSetCorrectly(t *testing.T) { // Given... factory := utils.NewMockFactory() - commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_ENABLE, factory, t) + commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_SET, factory, t) - var args []string = []string{"monitors", "enable", "--help"} + var args []string = []string{"monitors", "set", "--help"} // When... err := commandCollection.Execute(args) // Then... - checkOutput("Enables a monitor with the given name in the Galasa service", "", factory, t) + checkOutput("Updates a monitor with the given name in the Galasa service", "", factory, t) assert.Nil(t, err) } -func TestMonitorsEnableWithNameFlagReturnsOk(t *testing.T) { +func TestMonitorsSetWithNameAndIsEnabledFlagsReturnsOk(t *testing.T) { // Given... factory := utils.NewMockFactory() - commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_ENABLE, factory, t) + commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_SET, factory, t) - var args []string = []string{"monitors", "enable", "--name", "myMonitor"} + var args []string = []string{"monitors", "set", "--name", "myMonitor", "--is-enabled", "true"} // When... err := commandCollection.Execute(args) @@ -61,12 +61,12 @@ func TestMonitorsEnableWithNameFlagReturnsOk(t *testing.T) { checkOutput("", "", factory, t) } -func TestMonitorsEnableNoFlagsReturnsErrorMessage(t *testing.T) { +func TestMonitorsSetNoFlagsReturnsErrorMessage(t *testing.T) { // Given... factory := utils.NewMockFactory() - commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_ENABLE, factory, t) + commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_SET, factory, t) - var args []string = []string{"monitors", "enable"} + var args []string = []string{"monitors", "set"} // When... err := commandCollection.Execute(args) @@ -78,3 +78,20 @@ func TestMonitorsEnableNoFlagsReturnsErrorMessage(t *testing.T) { checkOutput("", `Error: required flag(s) "name" not set`, factory, t) } +func TestMonitorsSetWithNameFlagOnlyReturnsErrorMessage(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_SET, factory, t) + + var args []string = []string{"monitors", "set", "--name", "myCustomMonitor"} + + // When... + err := commandCollection.Execute(args) + + // Then... + assert.NotNil(t, err) + + // Check what the user saw is reasonable. + checkOutput("", `Error: at least one of the flags in the group [is-enabled] is required`, factory, t) +} + diff --git a/pkg/errors/errorMessage.go b/pkg/errors/errorMessage.go index 1c5e4f5e..8d91194b 100644 --- a/pkg/errors/errorMessage.go +++ b/pkg/errors/errorMessage.go @@ -431,6 +431,8 @@ var ( GALASA_ERROR_MONITOR_NAME_NOT_FOUND = NewMessageType("GAL1224E: Galasa Monitor named %v is not known on the Galasa service.", 1224, STACK_TRACE_NOT_WANTED) GALASA_ERROR_INVALID_MONITOR_NAME = NewMessageType("GAL1225E: Invalid monitor name provided. The name provided with the --name flag cannot be empty and must only contain characters in the following ranges: 'a'-'z', 'A'-'Z', '0'-'9', '-' (dash), '_' (underscore).", 1225, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_INVALID_IS_ENABLED_FLAG = NewMessageType("GAL1226E: Invalid '--is-enabled' value provided. Supported values are 'true' and 'false'. Check your command parameters and try again.", 1226, STACK_TRACE_NOT_WANTED) + // Warnings... GALASA_WARNING_MAVEN_NO_GALASA_OBR_REPO = NewMessageType("GAL2000W: Warning: Maven configuration file settings.xml should contain a reference to a Galasa repository so that the galasa OBR can be resolved. The official release repository is '%s', and 'pre-release' repository is '%s'", 2000, STACK_TRACE_WANTED) diff --git a/pkg/monitors/monitorsDisable.go b/pkg/monitors/monitorsDisable.go deleted file mode 100644 index 8aae66a1..00000000 --- a/pkg/monitors/monitorsDisable.go +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright contributors to the Galasa project - * - * SPDX-License-Identifier: EPL-2.0 - */ - -package monitors - -import ( - "log" - "github.com/galasa-dev/cli/pkg/galasaapi" - "github.com/galasa-dev/cli/pkg/spi" -) - -func DisableMonitor( - monitorName string, - apiClient *galasaapi.APIClient, - byteReader spi.ByteReader, -) error { - var err error - - desiredEnabledState := false - err = setMonitorIsEnabledState(monitorName, desiredEnabledState, apiClient, byteReader) - - log.Printf("DisableMonitor exiting. err is %v\n", err) - return err -} diff --git a/pkg/monitors/monitorsDisable_test.go b/pkg/monitors/monitorsDisable_test.go deleted file mode 100644 index 5e0ee6f2..00000000 --- a/pkg/monitors/monitorsDisable_test.go +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright contributors to the Galasa project - * - * SPDX-License-Identifier: EPL-2.0 - */ -package monitors - -import ( - "net/http" - "strconv" - "testing" - - "github.com/galasa-dev/cli/pkg/api" - "github.com/galasa-dev/cli/pkg/utils" - "github.com/stretchr/testify/assert" -) - -func TestCanDisableAMonitor(t *testing.T) { - // Given... - monitorName := "customManagerCleanup" - - // Create the expected HTTP interactions with the API server - putMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) - putMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { - requestBody := readMonitorRequestBody(req) - assert.Equal(t, requestBody.GetIsEnabled(), false) - - writer.Header().Set("Content-Type", "application/json") - writer.WriteHeader(http.StatusNoContent) - } - - interactions := []utils.HttpInteraction{ - putMonitorInteraction, - } - - server := utils.NewMockHttpServer(t, interactions) - defer server.Server.Close() - - apiServerUrl := server.Server.URL - apiClient := api.InitialiseAPI(apiServerUrl) - mockByteReader := utils.NewMockByteReader() - - // When... - err := DisableMonitor( - monitorName, - apiClient, - mockByteReader) - - // Then... - assert.Nil(t, err, "DisableMonitor returned an unexpected error") -} - -func TestDisableMonitorWithBlankNameDisplaysError(t *testing.T) { - // Given... - monitorName := " " - - // The client-side validation should fail, so no HTTP interactions will be performed - interactions := []utils.HttpInteraction{} - - server := utils.NewMockHttpServer(t, interactions) - defer server.Server.Close() - - apiServerUrl := server.Server.URL - apiClient := api.InitialiseAPI(apiServerUrl) - mockByteReader := utils.NewMockByteReader() - - // When... - err := DisableMonitor( - monitorName, - apiClient, - mockByteReader) - - // Then... - assert.NotNil(t, err, "DisableMonitor did not return an error as expected") - consoleOutputText := err.Error() - assert.Contains(t, consoleOutputText, "GAL1225E") - assert.Contains(t, consoleOutputText, " Invalid monitor name provided") -} - -func TestDisableNonExistantMonitorDisplaysError(t *testing.T) { - // Given... - nonExistantMonitor := "monitorDoesNotExist123" - - // Create the expected HTTP interactions with the API server - disableMonitorInteraction := utils.NewHttpInteraction("/monitors/" + nonExistantMonitor, http.MethodPut) - disableMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { - writer.Header().Set("Content-Type", "application/json") - writer.WriteHeader(http.StatusNotFound) - writer.Write([]byte(`{ "error_message": "No such monitor exists" }`)) - } - - interactions := []utils.HttpInteraction{ disableMonitorInteraction } - - server := utils.NewMockHttpServer(t, interactions) - defer server.Server.Close() - - apiServerUrl := server.Server.URL - apiClient := api.InitialiseAPI(apiServerUrl) - mockByteReader := utils.NewMockByteReader() - - // When... - err := DisableMonitor( - nonExistantMonitor, - apiClient, - mockByteReader) - - // Then... - assert.NotNil(t, err, "DisableMonitor did not return an error but it should have") - assert.ErrorContains(t, err, "GAL1222E") -} - -func TestDisableMonitorFailsWithNoExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { - // Given... - monitorName := "myMonitor" - - // Create the expected HTTP interactions with the API server - disableMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) - disableMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { - writer.WriteHeader(http.StatusInternalServerError) - } - - interactions := []utils.HttpInteraction{ - disableMonitorInteraction, - } - - server := utils.NewMockHttpServer(t, interactions) - defer server.Server.Close() - - apiServerUrl := server.Server.URL - apiClient := api.InitialiseAPI(apiServerUrl) - mockByteReader := utils.NewMockByteReader() - - // When... - err := DisableMonitor( - monitorName, - apiClient, - mockByteReader) - - // Then... - assert.NotNil(t, err, "DisableMonitor did not return an error but it should have") - assert.ErrorContains(t, err, "GAL1219E") -} - -func TestDisableMonitorFailsWithNonJsonContentTypeExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { - // Given... - monitorName := "myMonitor" - - // Create the expected HTTP interactions with the API server - disableMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) - disableMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { - writer.WriteHeader(http.StatusInternalServerError) - writer.Header().Set("Content-Type", "application/notJsonOnPurpose") - writer.Write([]byte("something not json but non-zero-length.")) - } - - interactions := []utils.HttpInteraction{ - disableMonitorInteraction, - } - - server := utils.NewMockHttpServer(t, interactions) - defer server.Server.Close() - - apiServerUrl := server.Server.URL - apiClient := api.InitialiseAPI(apiServerUrl) - mockByteReader := utils.NewMockByteReader() - - // When... - err := DisableMonitor( - monitorName, - apiClient, - mockByteReader) - - // Then... - assert.NotNil(t, err, "DisableMonitor did not return an error but it should have") - assert.ErrorContains(t, err, strconv.Itoa(http.StatusInternalServerError)) - assert.ErrorContains(t, err, "GAL1223E") - assert.ErrorContains(t, err, "Error details from the server are not in the json format") -} diff --git a/pkg/monitors/monitorsEnable.go b/pkg/monitors/monitorsSet.go similarity index 73% rename from pkg/monitors/monitorsEnable.go rename to pkg/monitors/monitorsSet.go index af1bc17f..1b24451b 100644 --- a/pkg/monitors/monitorsEnable.go +++ b/pkg/monitors/monitorsSet.go @@ -10,6 +10,7 @@ import ( "context" "log" "net/http" + "strconv" "github.com/galasa-dev/cli/pkg/embedded" galasaErrors "github.com/galasa-dev/cli/pkg/errors" @@ -17,15 +18,20 @@ import ( "github.com/galasa-dev/cli/pkg/spi" ) -func EnableMonitor( +func SetMonitor( monitorName string, + isEnabledStr string, apiClient *galasaapi.APIClient, byteReader spi.ByteReader, ) error { var err error - desiredEnabledState := true - err = setMonitorIsEnabledState(monitorName, desiredEnabledState, apiClient, byteReader) + monitorName, err = validateMonitorName(monitorName) + if err == nil { + if isEnabledStr != "" { + err = setMonitorIsEnabledState(monitorName, isEnabledStr, apiClient, byteReader) + } + } log.Printf("EnableMonitor exiting. err is %v\n", err) return err @@ -33,15 +39,18 @@ func EnableMonitor( func setMonitorIsEnabledState( monitorName string, - isEnabled bool, + isEnabledStr string, apiClient *galasaapi.APIClient, byteReader spi.ByteReader, ) error { var err error + var desiredEnabledState bool - monitorName, err = validateMonitorName(monitorName) + desiredEnabledState, err = strconv.ParseBool(isEnabledStr) if err == nil { - err = sendUpdateMonitorStateRequest(monitorName, isEnabled, apiClient, byteReader) + err = sendUpdateMonitorStateRequest(monitorName, desiredEnabledState, apiClient, byteReader) + } else { + err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_INVALID_IS_ENABLED_FLAG) } return err @@ -60,11 +69,14 @@ func sendUpdateMonitorStateRequest( restApiVersion, err = embedded.GetGalasactlRestApiVersion() if err == nil { - requestBody := *galasaapi.NewUpdateGalasaMonitorRequest() - requestBody.SetIsEnabled(isEnabled) + requestBody := *galasaapi.NewGalasaMonitor() + monitorData := *galasaapi.NewGalasaMonitorData() + monitorData.SetIsEnabled(isEnabled) + + requestBody.SetData(monitorData) httpResponse, err = apiClient.MonitorsAPIApi.SetMonitorStatus(context, monitorName). - UpdateGalasaMonitorRequest(requestBody). + GalasaMonitor(requestBody). ClientApiVersion(restApiVersion). Execute() diff --git a/pkg/monitors/monitorsEnable_test.go b/pkg/monitors/monitorsSet_test.go similarity index 77% rename from pkg/monitors/monitorsEnable_test.go rename to pkg/monitors/monitorsSet_test.go index 7ea2af31..26601dcb 100644 --- a/pkg/monitors/monitorsEnable_test.go +++ b/pkg/monitors/monitorsSet_test.go @@ -18,8 +18,8 @@ import ( "github.com/stretchr/testify/assert" ) -func readMonitorRequestBody(req *http.Request) galasaapi.UpdateGalasaMonitorRequest { - var monitorUpdateRequest galasaapi.UpdateGalasaMonitorRequest +func readMonitorRequestBody(req *http.Request) galasaapi.GalasaMonitor { + var monitorUpdateRequest galasaapi.GalasaMonitor requestBodyBytes, _ := io.ReadAll(req.Body) defer req.Body.Close() @@ -30,12 +30,13 @@ func readMonitorRequestBody(req *http.Request) galasaapi.UpdateGalasaMonitorRequ func TestCanEnableAMonitor(t *testing.T) { // Given... monitorName := "customManagerCleanup" + isEnabled := "true" // Create the expected HTTP interactions with the API server putMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) putMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { requestBody := readMonitorRequestBody(req) - assert.Equal(t, requestBody.GetIsEnabled(), true) + assert.Equal(t, requestBody.Data.GetIsEnabled(), true) writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusNoContent) @@ -53,8 +54,9 @@ func TestCanEnableAMonitor(t *testing.T) { mockByteReader := utils.NewMockByteReader() // When... - err := EnableMonitor( + err := SetMonitor( monitorName, + isEnabled, apiClient, mockByteReader) @@ -65,6 +67,7 @@ func TestCanEnableAMonitor(t *testing.T) { func TestEnableMonitorWithBlankNameDisplaysError(t *testing.T) { // Given... monitorName := " " + isEnabled := "true" // The client-side validation should fail, so no HTTP interactions will be performed interactions := []utils.HttpInteraction{} @@ -77,8 +80,9 @@ func TestEnableMonitorWithBlankNameDisplaysError(t *testing.T) { mockByteReader := utils.NewMockByteReader() // When... - err := EnableMonitor( + err := SetMonitor( monitorName, + isEnabled, apiClient, mockByteReader) @@ -92,6 +96,7 @@ func TestEnableMonitorWithBlankNameDisplaysError(t *testing.T) { func TestEnableNonExistantMonitorDisplaysError(t *testing.T) { // Given... nonExistantMonitor := "monitorDoesNotExist123" + isEnabled := "true" // Create the expected HTTP interactions with the API server enableMonitorInteraction := utils.NewHttpInteraction("/monitors/" + nonExistantMonitor, http.MethodPut) @@ -111,8 +116,9 @@ func TestEnableNonExistantMonitorDisplaysError(t *testing.T) { mockByteReader := utils.NewMockByteReader() // When... - err := EnableMonitor( + err := SetMonitor( nonExistantMonitor, + isEnabled, apiClient, mockByteReader) @@ -124,6 +130,7 @@ func TestEnableNonExistantMonitorDisplaysError(t *testing.T) { func TestEnableMonitorFailsWithNoExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { // Given... monitorName := "myMonitor" + isEnabled := "true" // Create the expected HTTP interactions with the API server enableMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) @@ -143,8 +150,9 @@ func TestEnableMonitorFailsWithNoExplanationErrorPayloadGivesCorrectMessage(t *t mockByteReader := utils.NewMockByteReader() // When... - err := EnableMonitor( + err := SetMonitor( monitorName, + isEnabled, apiClient, mockByteReader) @@ -156,6 +164,7 @@ func TestEnableMonitorFailsWithNoExplanationErrorPayloadGivesCorrectMessage(t *t func TestEnableMonitorFailsWithNonJsonContentTypeExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { // Given... monitorName := "myMonitor" + isEnabled := "true" // Create the expected HTTP interactions with the API server enableMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) @@ -177,8 +186,9 @@ func TestEnableMonitorFailsWithNonJsonContentTypeExplanationErrorPayloadGivesCor mockByteReader := utils.NewMockByteReader() // When... - err := EnableMonitor( + err := SetMonitor( monitorName, + isEnabled, apiClient, mockByteReader) @@ -188,3 +198,40 @@ func TestEnableMonitorFailsWithNonJsonContentTypeExplanationErrorPayloadGivesCor assert.ErrorContains(t, err, "GAL1223E") assert.ErrorContains(t, err, "Error details from the server are not in the json format") } + +func TestCanDisableAMonitor(t *testing.T) { + // Given... + monitorName := "customManagerCleanup" + isEnabled := "false" + + // Create the expected HTTP interactions with the API server + putMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) + putMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + requestBody := readMonitorRequestBody(req) + assert.Equal(t, requestBody.Data.GetIsEnabled(), false) + + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusNoContent) + } + + interactions := []utils.HttpInteraction{ + putMonitorInteraction, + } + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiServerUrl := server.Server.URL + apiClient := api.InitialiseAPI(apiServerUrl) + mockByteReader := utils.NewMockByteReader() + + // When... + err := SetMonitor( + monitorName, + isEnabled, + apiClient, + mockByteReader) + + // Then... + assert.Nil(t, err, "DisableMonitor returned an unexpected error") +} From 74c645329da1f734b4ed5f0e8d37f54d20640a0b Mon Sep 17 00:00:00 2001 From: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> Date: Thu, 27 Mar 2025 15:23:41 +0000 Subject: [PATCH 5/7] Use separate bean for updating monitors Signed-off-by: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> --- pkg/monitors/monitorsSet.go | 6 +++--- pkg/monitors/monitorsSet_test.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/monitors/monitorsSet.go b/pkg/monitors/monitorsSet.go index 1b24451b..9fb54bef 100644 --- a/pkg/monitors/monitorsSet.go +++ b/pkg/monitors/monitorsSet.go @@ -69,14 +69,14 @@ func sendUpdateMonitorStateRequest( restApiVersion, err = embedded.GetGalasactlRestApiVersion() if err == nil { - requestBody := *galasaapi.NewGalasaMonitor() - monitorData := *galasaapi.NewGalasaMonitorData() + requestBody := *galasaapi.NewUpdateGalasaMonitorRequest() + monitorData := *galasaapi.NewUpdateGalasaMonitorRequestData() monitorData.SetIsEnabled(isEnabled) requestBody.SetData(monitorData) httpResponse, err = apiClient.MonitorsAPIApi.SetMonitorStatus(context, monitorName). - GalasaMonitor(requestBody). + UpdateGalasaMonitorRequest(requestBody). ClientApiVersion(restApiVersion). Execute() diff --git a/pkg/monitors/monitorsSet_test.go b/pkg/monitors/monitorsSet_test.go index 26601dcb..ab2d9385 100644 --- a/pkg/monitors/monitorsSet_test.go +++ b/pkg/monitors/monitorsSet_test.go @@ -18,8 +18,8 @@ import ( "github.com/stretchr/testify/assert" ) -func readMonitorRequestBody(req *http.Request) galasaapi.GalasaMonitor { - var monitorUpdateRequest galasaapi.GalasaMonitor +func readMonitorRequestBody(req *http.Request) galasaapi.UpdateGalasaMonitorRequest { + var monitorUpdateRequest galasaapi.UpdateGalasaMonitorRequest requestBodyBytes, _ := io.ReadAll(req.Body) defer req.Body.Close() From d62e4f09b3618b04e55f653893215808f3cf2510 Mon Sep 17 00:00:00 2001 From: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> Date: Fri, 28 Mar 2025 13:54:58 +0000 Subject: [PATCH 6/7] Replace mentions of EnableMonitor with SetMonitor Signed-off-by: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> --- pkg/monitors/monitorsSet.go | 2 +- pkg/monitors/monitorsSet_test.go | 36 ++++++++++++++++---------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/pkg/monitors/monitorsSet.go b/pkg/monitors/monitorsSet.go index 9fb54bef..fb486523 100644 --- a/pkg/monitors/monitorsSet.go +++ b/pkg/monitors/monitorsSet.go @@ -33,7 +33,7 @@ func SetMonitor( } } - log.Printf("EnableMonitor exiting. err is %v\n", err) + log.Printf("SetMonitor exiting. err is %v\n", err) return err } diff --git a/pkg/monitors/monitorsSet_test.go b/pkg/monitors/monitorsSet_test.go index 3f63796e..dcc4bfba 100644 --- a/pkg/monitors/monitorsSet_test.go +++ b/pkg/monitors/monitorsSet_test.go @@ -61,10 +61,10 @@ func TestCanEnableAMonitor(t *testing.T) { mockByteReader) // Then... - assert.Nil(t, err, "EnableMonitor returned an unexpected error") + assert.Nil(t, err, "SetMonitor returned an unexpected error") } -func TestEnableMonitorWithBlankNameDisplaysError(t *testing.T) { +func TestSetMonitorWithBlankNameDisplaysError(t *testing.T) { // Given... monitorName := " " isEnabled := "true" @@ -87,7 +87,7 @@ func TestEnableMonitorWithBlankNameDisplaysError(t *testing.T) { mockByteReader) // Then... - assert.NotNil(t, err, "EnableMonitor did not return an error as expected") + assert.NotNil(t, err, "SetMonitor did not return an error as expected") consoleOutputText := err.Error() assert.Contains(t, consoleOutputText, "GAL1225E") assert.Contains(t, consoleOutputText, " Invalid monitor name provided") @@ -99,14 +99,14 @@ func TestEnableNonExistantMonitorDisplaysError(t *testing.T) { isEnabled := "true" // Create the expected HTTP interactions with the API server - enableMonitorInteraction := utils.NewHttpInteraction("/monitors/" + nonExistantMonitor, http.MethodPut) - enableMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + setMonitorInteraction := utils.NewHttpInteraction("/monitors/" + nonExistantMonitor, http.MethodPut) + setMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusNotFound) writer.Write([]byte(`{ "error_message": "No such monitor exists" }`)) } - interactions := []utils.HttpInteraction{ enableMonitorInteraction } + interactions := []utils.HttpInteraction{ setMonitorInteraction } server := utils.NewMockHttpServer(t, interactions) defer server.Server.Close() @@ -123,23 +123,23 @@ func TestEnableNonExistantMonitorDisplaysError(t *testing.T) { mockByteReader) // Then... - assert.NotNil(t, err, "EnableMonitor did not return an error but it should have") + assert.NotNil(t, err, "SetMonitor did not return an error but it should have") assert.ErrorContains(t, err, "GAL1231E") } -func TestEnableMonitorFailsWithNoExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { +func TestSetMonitorFailsWithNoExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { // Given... monitorName := "myMonitor" isEnabled := "true" // Create the expected HTTP interactions with the API server - enableMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) - enableMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + setMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) + setMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { writer.WriteHeader(http.StatusInternalServerError) } interactions := []utils.HttpInteraction{ - enableMonitorInteraction, + setMonitorInteraction, } server := utils.NewMockHttpServer(t, interactions) @@ -157,25 +157,25 @@ func TestEnableMonitorFailsWithNoExplanationErrorPayloadGivesCorrectMessage(t *t mockByteReader) // Then... - assert.NotNil(t, err, "EnableMonitor did not return an error but it should have") + assert.NotNil(t, err, "SetMonitor did not return an error but it should have") assert.ErrorContains(t, err, "GAL1228E") } -func TestEnableMonitorFailsWithNonJsonContentTypeExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { +func TestSetMonitorFailsWithNonJsonContentTypeExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { // Given... monitorName := "myMonitor" isEnabled := "true" // Create the expected HTTP interactions with the API server - enableMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) - enableMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + setMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) + setMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { writer.WriteHeader(http.StatusInternalServerError) writer.Header().Set("Content-Type", "application/notJsonOnPurpose") writer.Write([]byte("something not json but non-zero-length.")) } interactions := []utils.HttpInteraction{ - enableMonitorInteraction, + setMonitorInteraction, } server := utils.NewMockHttpServer(t, interactions) @@ -193,7 +193,7 @@ func TestEnableMonitorFailsWithNonJsonContentTypeExplanationErrorPayloadGivesCor mockByteReader) // Then... - assert.NotNil(t, err, "EnableMonitor did not return an error but it should have") + assert.NotNil(t, err, "SetMonitor did not return an error but it should have") assert.ErrorContains(t, err, strconv.Itoa(http.StatusInternalServerError)) assert.ErrorContains(t, err, "GAL1232E") assert.ErrorContains(t, err, "Error details from the server are not in the json format") @@ -233,5 +233,5 @@ func TestCanDisableAMonitor(t *testing.T) { mockByteReader) // Then... - assert.Nil(t, err, "DisableMonitor returned an unexpected error") + assert.Nil(t, err, "SetMonitor returned an unexpected error") } From 2ac8a788d09f52aba2cc76ba28ae020210c78481 Mon Sep 17 00:00:00 2001 From: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> Date: Fri, 28 Mar 2025 14:07:28 +0000 Subject: [PATCH 7/7] Remove generated doc for monitors enable and disable commands Signed-off-by: Eamonn Mansour <47121388+eamansour@users.noreply.github.com> --- docs/generated/galasactl_monitors_disable.md | 33 -------------------- docs/generated/galasactl_monitors_enable.md | 33 -------------------- 2 files changed, 66 deletions(-) delete mode 100644 docs/generated/galasactl_monitors_disable.md delete mode 100644 docs/generated/galasactl_monitors_enable.md diff --git a/docs/generated/galasactl_monitors_disable.md b/docs/generated/galasactl_monitors_disable.md deleted file mode 100644 index e8f2ffe2..00000000 --- a/docs/generated/galasactl_monitors_disable.md +++ /dev/null @@ -1,33 +0,0 @@ -## galasactl monitors disable - -Disable a monitor in the Galasa service - -### Synopsis - -Disables a monitor with the given name in the Galasa service - -``` -galasactl monitors disable [flags] -``` - -### Options - -``` - -h, --help Displays the options for the 'monitors disable' command. - --name string A mandatory flag that identifies the monitor to be manipulated by name. -``` - -### Options inherited from parent commands - -``` - -b, --bootstrap string Bootstrap URL. Should start with 'http://' or 'file://'. If it starts with neither, it is assumed to be a fully-qualified path. If missing, it defaults to use the 'bootstrap.properties' file in your GALASA_HOME. Example: http://example.com/bootstrap, file:///user/myuserid/.galasa/bootstrap.properties , file://C:/Users/myuserid/.galasa/bootstrap.properties - --galasahome string Path to a folder where Galasa will read and write files and configuration settings. The default is '${HOME}/.galasa'. This overrides the GALASA_HOME environment variable which may be set instead. - -l, --log string File to which log information will be sent. Any folder referred to must exist. An existing file will be overwritten. Specify "-" to log to stderr. Defaults to not logging. - --rate-limit-retries int The maximum number of retries that should be made when requests to the Galasa Service fail due to rate limits being exceeded. Must be a whole number. Defaults to 3 retries (default 3) - --rate-limit-retry-backoff-secs float The amount of time in seconds to wait before retrying a command if it failed due to rate limits being exceeded. Defaults to 1 second. (default 1) -``` - -### SEE ALSO - -* [galasactl monitors](galasactl_monitors.md) - Manage monitors in the Galasa service - diff --git a/docs/generated/galasactl_monitors_enable.md b/docs/generated/galasactl_monitors_enable.md deleted file mode 100644 index 61b4b23b..00000000 --- a/docs/generated/galasactl_monitors_enable.md +++ /dev/null @@ -1,33 +0,0 @@ -## galasactl monitors enable - -Enable a monitor in the Galasa service - -### Synopsis - -Enables a monitor with the given name in the Galasa service - -``` -galasactl monitors enable [flags] -``` - -### Options - -``` - -h, --help Displays the options for the 'monitors enable' command. - --name string A mandatory flag that identifies the monitor to be manipulated by name. -``` - -### Options inherited from parent commands - -``` - -b, --bootstrap string Bootstrap URL. Should start with 'http://' or 'file://'. If it starts with neither, it is assumed to be a fully-qualified path. If missing, it defaults to use the 'bootstrap.properties' file in your GALASA_HOME. Example: http://example.com/bootstrap, file:///user/myuserid/.galasa/bootstrap.properties , file://C:/Users/myuserid/.galasa/bootstrap.properties - --galasahome string Path to a folder where Galasa will read and write files and configuration settings. The default is '${HOME}/.galasa'. This overrides the GALASA_HOME environment variable which may be set instead. - -l, --log string File to which log information will be sent. Any folder referred to must exist. An existing file will be overwritten. Specify "-" to log to stderr. Defaults to not logging. - --rate-limit-retries int The maximum number of retries that should be made when requests to the Galasa Service fail due to rate limits being exceeded. Must be a whole number. Defaults to 3 retries (default 3) - --rate-limit-retry-backoff-secs float The amount of time in seconds to wait before retrying a command if it failed due to rate limits being exceeded. Defaults to 1 second. (default 1) -``` - -### SEE ALSO - -* [galasactl monitors](galasactl_monitors.md) - Manage monitors in the Galasa service -