feat(consumer-group): add reset-offset command#876
Conversation
c0cd238 to
9f22784
Compare
| one = 'Partitions on which reset-offset is to be carried upon (space separated integers)' | ||
|
|
||
| [kafka.consumerGroup.resetOffset.flag.output] | ||
| one = 'Output in which to display reset offset result' |
There was a problem hiding this comment.
| one = 'Output in which to display reset offset result' | |
| one = 'Format in which to display reset offset result (choose from: "json", "yml", "yaml")' |
| one = 'Custom offset value (required when offset is absolute or timestamp)' | ||
|
|
||
| [kafka.consumerGroup.resetOffset.flag.partitions] | ||
| one = 'Partitions on which reset-offset is to be carried upon (space separated integers)' |
There was a problem hiding this comment.
| one = 'Partitions on which reset-offset is to be carried upon (space separated integers)' | |
| one = 'Partitions on which to reset the consumer group offset (space separated integers)' |
| one = 'Skip confirmation to forcibly reset-offset of a consumer group' | ||
|
|
||
| [kafka.consumerGroup.resetOffset.flag.offset] | ||
| one = 'Offset type (earliest, latest, absolute, timestamp)' |
There was a problem hiding this comment.
| one = 'Offset type (earliest, latest, absolute, timestamp)' | |
| one = 'Offset type (choose from: "earliest", "latest", "absolute", "timestamp")' |
| one = 'Select topic for which consumer group offsets are to be reset' | ||
|
|
||
| [kafka.consumerGroup.resetOffset.flag.yes] | ||
| one = 'Skip confirmation to forcibly reset-offset of a consumer group' |
There was a problem hiding this comment.
| one = 'Skip confirmation to forcibly reset-offset of a consumer group' | |
| one = 'Skip confirmation to forcibly reset the offset for the consumer group' |
| @@ -0,0 +1,43 @@ | |||
| [kafka.consumerGroup.resetOffset.cmd.shortDescription] | |||
| one = 'Reset offset of a consumer group' | |||
There was a problem hiding this comment.
| one = 'Reset offset of a consumer group' | |
| one = 'Reset offset for a consumer group' |
|
|
||
| [kafka.consumerGroup.resetOffset.cmd.longDescription] | ||
| one = ''' | ||
| Reset offset for a consumer group from the Kafka instance. |
There was a problem hiding this comment.
| Reset offset for a consumer group from the Kafka instance. | |
| Reset the offset for a consumer group. |
I think the fact that this applies to a Kafka instance should be clear without stating it.
| one = 'Offset type (earliest, latest, absolute, timestamp)' | ||
|
|
||
| [kafka.consumerGroup.resetOffset.flag.value] | ||
| one = 'Custom offset value (required when offset is absolute or timestamp)' |
There was a problem hiding this comment.
| one = 'Custom offset value (required when offset is absolute or timestamp)' | |
| one = 'Custom offset value (required when offset is "absolute" or "timestamp")' |
| one = 'Are you sure you want to reset the offset for consumer group "{{.ID}}"?' | ||
|
|
||
| [kafka.consumerGroup.resetOffset.log.debug.cancelledReset] | ||
| one = 'You have chosen to not reset the consumer group offset.' |
There was a problem hiding this comment.
| one = 'You have chosen to not reset the consumer group offset.' | |
| one = 'You have chosen not to reset the consumer group offset.' |
bhardesty
left a comment
There was a problem hiding this comment.
Just made a few minor help text suggestions.
|
|
||
| defer httpRes.Body.Close() | ||
|
|
||
| logger.Info(opts.localizer.MustLocalize("kafka.consumerGroup.resetOffset.log.info.successful", localize.NewEntry("ConsumerGroupID", opts.id), localize.NewEntry("InstanceName", kafkaInstance.GetName()))) |
There was a problem hiding this comment.
I wonder should be start breaking long logs onto multiple lines (the code, not the content).
| localizer localize.Localizer | ||
| } | ||
|
|
||
| type UpdatedConsumerRow struct { |
There was a problem hiding this comment.
Any reason this was made public?
|
I think it would be better if there was earlier validation of the flag values. For example, if I set |
|
Does this require the mocks to test? |
Yes, I will be updating verification steps. |
03b7219 to
b2c6300
Compare
craicoverflow
left a comment
There was a problem hiding this comment.
- I entered a random value for "id" and "topic" but was allowed to go through the steps as if they existed:
❯ ./rhoas kafka consumer-group reset-offset --partitions 1 --partitions 2 --offset latest --id 1 --topic hello
? Are you sure you want to reset the offset for consumer group "1"? Yes
Error: 400 Bad Request. Run the command in verbose mode using the -v flag to see more information- I think we ought to start extracting the error messages from the body:
The error displays "400 Bad Request" but this won't help the user know what to do.
{"code":400,"error_message":"Topic topic1, partition 1 is not valid","class":"IllegalArgumentException"}
Error: 400 Bad Request. Run the command in verbose mode using the -v flag to see more **information**- When I enter an invalid topic, the "Are you sure" dialog gets stuck (hangs). This seems to be an issue on the backend though - the request is sent but it is not responding. I still think we should check if the topic exists on the client-side first.
❯ ./rhoas kafka consumer-group reset-offset --offset earliest --id consumer-group-0 --topic mytopic
? Are you sure you want to reset the offset for consumer group "consumer-group-0"? Yes-
Do you think maybe it is a good idea to print the same columns/data that you see in
consumer-group describe? There are columns like "log end offset", "current offset" which users may expect to see. (Up for discussion). -
Passing an inval;id value for
--valueis not handled by the API. We might need to log some bugs/work with the team to get this resolved.
./rhoas kafka consumer-group reset-offset \
--id consumer-group-0 \
--topic topic1 -y \
--offset absolute --value "hello" \
-yv- It would be cool to validate the "value" value when the "offset" is "timestamp". The format should be "Timestamp must be in format 'yyyy-MM-dd'T'HH:mm:ssz" but the user cannot tell this from anywhere in the CLI so it needs to be documented.
Overall, good PR but this needs to be tested more extensively as it is an important command with many option combinations possible.
pkg/cmd/flag/validation.go
Outdated
| func ValidateOffset(v string) error { | ||
| isValid := flagutil.IsValidInput(v, flagutil.ValidOffsets...) | ||
|
|
||
| if isValid { | ||
| return nil | ||
| } | ||
|
|
||
| return InvalidValueError("output", v, flagutil.ValidOffsets...) | ||
| } |
There was a problem hiding this comment.
I see pkg/cmd/flag/validation.go as a place for generic, reusable flag helpers. Offset is specific to consumer groups, and so it should not be here.
There was a problem hiding this comment.
Can we have another method with signature ValidateInput(v string, validValues string[], flagName string) ?
There was a problem hiding this comment.
Yeah, if you think that would be an in improvement just go ahead and implement it 😄
| } | ||
| } | ||
|
|
||
| if opts.value == "" && (opts.offset == "absolute" || opts.offset == "timestamp") { |
There was a problem hiding this comment.
Let's create some constants for the different possible values for "offset".
| cmd.Flags().BoolVarP(&opts.skipConfirm, "yes", "y", false, opts.localizer.MustLocalize("kafka.consumerGroup.resetOffset.flag.yes")) | ||
| cmd.Flags().StringVar(&opts.id, "id", "", opts.localizer.MustLocalize("kafka.consumerGroup.common.flag.id.description", localize.NewEntry("Action", "reset-offset"))) | ||
| cmd.Flags().StringVar(&opts.value, "value", "", opts.localizer.MustLocalize("kafka.consumerGroup.resetOffset.flag.value")) | ||
| cmd.Flags().StringVar(&opts.offset, "offset", "", opts.localizer.MustLocalize("kafka.consumerGroup.resetOffset.flag.offset")) |
There was a problem hiding this comment.
I wonder if we should be using a default? Does the UI default to anything? From some research I see that it can be configured from Kafka itself, and the default value is "latest". I am not sure whether the backend produces some default.
There was a problem hiding this comment.
UI doesn't have a default, renders "select" in the beginning.
| switch opts.output { | ||
| case "json": | ||
| data, _ := json.Marshal(updatedConsumers) | ||
| _ = dump.JSON(opts.IO.Out, data) |
There was a problem hiding this comment.
Use the PrintDataInFormat helper defined in the dump package.
| ) | ||
|
|
||
| switch opts.output { | ||
| case "json": |
There was a problem hiding this comment.
Use the constants defined in dump.
pkg/cmdutil/flags/flags.go
Outdated
| var ( | ||
| ValidOutputFormats = []string{dump.JSONFormat, dump.YAMLFormat, dump.YMLFormat} | ||
| CredentialsOutputFormats = []string{"env", "json", "properties"} | ||
| ValidOffsets = []string{"timestamp", "absolute", "latest", "earliest"} |
There was a problem hiding this comment.
Use the constants which will be created :)
|
|
||
| [kafka.consumerGroup.resetOffset.cmd.longDescription] | ||
| one = ''' | ||
| Reset the offset for a consumer group. |
There was a problem hiding this comment.
This should probably have some more detail?
| cmd.Flags().StringVar(&opts.value, "value", "", opts.localizer.MustLocalize("kafka.consumerGroup.resetOffset.flag.value")) | ||
| cmd.Flags().StringVar(&opts.offset, "offset", "", opts.localizer.MustLocalize("kafka.consumerGroup.resetOffset.flag.offset")) | ||
| cmd.Flags().StringVar(&opts.topic, "topic", "", opts.localizer.MustLocalize("kafka.consumerGroup.resetOffset.flag.topic")) | ||
| cmd.Flags().Int32SliceVar(&opts.partitions, "partitions", []int32{}, opts.localizer.MustLocalize("kafka.consumerGroup.resetOffset.flag.partitions")) |
There was a problem hiding this comment.
| cmd.Flags().Int32SliceVar(&opts.partitions, "partitions", []int32{}, opts.localizer.MustLocalize("kafka.consumerGroup.resetOffset.flag.partitions")) | |
| cmd.Flags().Int32SliceVar(&opts.partitions, "partition", []int32{}, opts.localizer.MustLocalize("kafka.consumerGroup.resetOffset.flag.partitions")) |
There was a problem hiding this comment.
I think partitions 1,2,3 will look better than --partition 1 --partition 2 --partition 3. Will become a drag as the number of partitions to pass will increase.
There was a problem hiding this comment.
Ah neat, I did not know that Int32SliceVar allowed for both input types to be valid. That is fine then.
| one = 'Custom offset value (required when offset is "absolute" or "timestamp")' | ||
|
|
||
| [kafka.consumerGroup.resetOffset.flag.partitions] | ||
| one = 'Partitions on which to reset the consumer group offset (space separated integers)' |
There was a problem hiding this comment.
"(space separated integers)" is that right?
There was a problem hiding this comment.
If it is right, it should be "space-separated" :-)
There was a problem hiding this comment.
It should be comma separated as of now, used to be space separated before.
865975a to
9051583
Compare
craicoverflow
left a comment
There was a problem hiding this comment.
A couple of nits, but happy for those to be addressed in a follow up.
pkg/kafka/consumergroup/util.go
Outdated
| EarliestOffset = "earliest" | ||
| TimestampOffset = "timestamp" | ||
| LatestOffset = "latest" | ||
| OffsetAbssolute = "absolute" |
There was a problem hiding this comment.
| OffsetAbssolute = "absolute" | |
| OffsetAbsolute |
typo.
pkg/kafka/consumergroup/util.go
Outdated
|
|
||
| var ValidOffsets = []string{OffsetAbssolute, OffsetEarliest, OffsetTimestamp, OffsetLatest} | ||
|
|
||
| var timestampOffsetRegExp = regexp.MustCompile(`^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}-\d{2}:\d{2})$`) |
There was a problem hiding this comment.
It could be better to use an iso8601 validator lib for this:
There was a problem hiding this comment.
Move to validators package.
pkg/kafka/consumergroup/util.go
Outdated
| } | ||
|
|
||
| // ValidateOffset checks if value v is a valid value for --offset | ||
| func ValidateOffset(v string) error { |
There was a problem hiding this comment.
nit: Ideally this should go into the validators package
|
Next steps once this is merged @rkpattnaik780 - add an example to the guides repo. |
Will be doing these in a follow up along with tests for consumer group validator. |
|
Is this feature deployed to prod? |
Will check in prod and get back to you, it is there in stage though. |
|
Please do not bother. I would try to play with this on stage. Going to play with this for a while |
I think that it is yes. |
| [discrete] | ||
| == Synopsis | ||
|
|
||
| Reset the offset for consumers in a consumer group reading from a given topic. |
There was a problem hiding this comment.
We need more information.
List offset types.
We need to list 2 modes (partition mode is only for advanced use cases)
| .... | ||
|
|
||
| [discrete] | ||
| == Examples |
There was a problem hiding this comment.
Examples should reflect all the modes and types.
Should we create a new release (0.29.0) before updating the guides? |
You should have the pull request ready to go as a draft, and merge it once 0.29.0 is released. |
Implement
rhoas kafka consumer-group reset-offsetcommand.Closes #763
Verification Steps against mock
Verification Steps against stage (WIP)
Type of change
Checklist