-
Notifications
You must be signed in to change notification settings - Fork 115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable per-resource timeout customization via an annotation #232
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# frozen_string_literal: true | ||
|
||
module KubernetesDeploy | ||
## | ||
# This class is a less strict extension of ActiveSupport::Duration::ISO8601Parser. | ||
# In addition to full ISO8601 durations, it can parse unprefixed ISO8601 time components (e.g. '1H'). | ||
# It is also case-insensitive. | ||
# For example, this class considers the values "1H", "1h" and "PT1H" to be valid and equivalent. | ||
|
||
class DurationParser | ||
class ParsingError < ArgumentError; end | ||
|
||
def initialize(value) | ||
@iso8601_str = value.to_s.strip.upcase | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe if you add:
You get all of the features you want from just normal The only issue is that the error message on a failed parse becomes You could maybe solve this with a sketchy Find&Replace on the returned message... maybe Or you could do a more complicated thing where you do:
Where you do the blind parse, and if it fails you always try jamming There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the retry-and-append solution! The only thing that bugs me about it is that we'll be retying in what I'm expecting to be the average case. I can't imagine that's meaningfully expensive, but for the purposes of reflecting intent, I'd slightly prefer to have the first try use the |
||
end | ||
|
||
def parse! | ||
if @iso8601_str.blank? | ||
raise ParsingError, "Cannot parse blank value" | ||
end | ||
|
||
parser = ActiveSupport::Duration::ISO8601Parser.new(@iso8601_str) | ||
parser.mode = :time unless @iso8601_str.start_with?("P") | ||
parts = parser.parse! | ||
ActiveSupport::Duration.new(calculate_total_seconds(parts), parts) | ||
rescue ActiveSupport::Duration::ISO8601Parser::ParsingError => e | ||
raise ParsingError, e.message | ||
end | ||
|
||
private | ||
|
||
# https://github.com/rails/rails/blob/19c450d5d99275924254b2041b6ad470fdaa1f93/activesupport/lib/active_support/duration.rb#L79-L83 | ||
def calculate_total_seconds(parts) | ||
parts.inject(0) do |total, (part, value)| | ||
total + value * ActiveSupport::Duration::PARTS_IN_SECONDS[part] | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# frozen_string_literal: true | ||
require 'test_helper' | ||
|
||
class DurationParserTest < KubernetesDeploy::TestCase | ||
def test_parses_correct_iso_durations_with_prefixes | ||
assert_equal 300, new_parser("PT300S").parse! | ||
assert_equal 300, new_parser("PT5M").parse! | ||
assert_equal 900, new_parser("PT0.25H").parse! | ||
assert_equal 110839937, new_parser("P3Y6M4DT12H30M5S").parse! | ||
end | ||
|
||
def test_parses_correct_iso_durations_without_prefixes | ||
assert_equal 300, new_parser("300S").parse! | ||
assert_equal 300, new_parser("5M").parse! | ||
assert_equal 900, new_parser("0.25H").parse! | ||
assert_equal(-60, new_parser("-1M").parse!) | ||
end | ||
|
||
def test_parse_is_case_insensitive | ||
assert_equal 30, new_parser("30S").parse! | ||
assert_equal 30, new_parser("30s").parse! | ||
end | ||
|
||
def test_parse_raises_expected_error_for_blank_values | ||
["", " ", nil].each do |blank_value| | ||
assert_raises_message(KubernetesDeploy::DurationParser::ParsingError, "Cannot parse blank value") do | ||
new_parser(blank_value).parse! | ||
end | ||
end | ||
end | ||
|
||
def test_extra_whitespace_is_stripped_from_values | ||
assert_equal 30, new_parser(" 30S ").parse! | ||
end | ||
|
||
def test_parse_raises_expected_error_when_value_is_invalid | ||
assert_raises_message(KubernetesDeploy::DurationParser::ParsingError, 'Invalid ISO 8601 duration: "FOO"') do | ||
new_parser("foo").parse! | ||
end | ||
end | ||
|
||
private | ||
|
||
def new_parser(value) | ||
KubernetesDeploy::DurationParser.new(value) | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about adding: "This feature does not work as expected for the
deployment
resources because progressDeadlineSeconds takes precedence over hard timeouts and have a server side default value for deployments."There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated with more details on compatibility