Skip to content
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

Add support for timestamped split registry endpoint #97

Merged
merged 16 commits into from
Apr 15, 2020

Conversation

reggieag
Copy link
Contributor

/domain @Betterment/test_track_core
/platform @aburgel @jmileham @effron @smudge

This PR adds the build timestamp to the rails client. We decided to push the timestamp generation and validation up to the build of the client so the app code can assume that the timestamp exists.

@nanda-prbot
Copy link

Needs somebody from @Betterment/test_track_core to claim domain review
Needs somebody from @aburgel, @jmileham, @effron, and @smudge to claim platform review

Use the shovel operator to claim, e.g.:

@myname << domain && platform

HOW TO: Claim a Review

@aburgel
Copy link
Collaborator

aburgel commented Feb 1, 2020

<< platform

@nanda-prbot
Copy link

Needs somebody from @Betterment/test_track_core to claim domain review

Use the shovel operator to claim, e.g.:

@myname << domain && platform

HOW TO: Claim a Review

@@ -15,7 +15,7 @@ def instance
if faked?
new(fake_instance_attributes(nil))
else
get('/api/v2/split_registry')
get("/api/v3/builds/#{TestTrack::BuildTimestamp}/split_registry")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may be able to pass in the timestamp as an argument to get. Though we don’t need url encoding since the timestamp shouldn’t have any funky characters, I think it’s a good practice.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we're doing string interpolation in the other instances of get with args. Should we stick with that pattern or forge our own path?

elsif File.exist?(BUILD_TIMESTAMP_FILE_PATH) && File.readable?(BUILD_TIMESTAMP_FILE_PATH) && !File.zero?(BUILD_TIMESTAMP_FILE_PATH)
TestTrack::BuildTimestamp = File.read(BUILD_TIMESTAMP_FILE_PATH)
else
raise Exception.new "Failed to read timestamp"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could use more context. If the app fails to startup we should provide plenty of details on why and how to fix.

Something like: “TestTrack failed to load the required build timestamp. Ensure testtrack-cli build-timestamp is run and the timestamp file is present”

Something like that but better 😁

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason we're raising a bare exception instead of just the text, which would result in a StandardError?

if Rails.env.test? || Rails.env.development?
TestTrack::BuildTimestamp = Time.zone.now.iso8601
elsif File.exist?(BUILD_TIMESTAMP_FILE_PATH) && File.readable?(BUILD_TIMESTAMP_FILE_PATH) && !File.zero?(BUILD_TIMESTAMP_FILE_PATH)
TestTrack::BuildTimestamp = File.read(BUILD_TIMESTAMP_FILE_PATH)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think some basic validation of the file content is worthwhile, like is it in iso format. @jmileham does the iOS client do any validation?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, but agreed. Also, this camelization isn't conventional for ruby - it looks like a class. We should either have a property of TestTrack, a la TestTrack.build_timestamp, or it should be using constant case, i.e. TestTrack::BUILD_TIMESTAMP. If we don't intend for it to be mutable, we should do that. Also, the timestamp file should probably be newline terminated, and if so, this should probably be chomping the results during its validation/normalization phase.

next if cli.skip_testtrack_cli?

if cli.project_initialized?
result = cli.call('generate_timestamp')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, the naming of this command and the rake task is inconsistent. I like the more explicit name of the rake task, so probably should change the name of this command to generate_build_timestamp.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so update the command in the cli?

@@ -31,3 +42,4 @@ end
task 'db:schema:load' => ['test_track:schema:load']
task 'db:structure:load' => ['test_track:schema:load']
task 'db:migrate' => ['test_track:migrate']
task 'deploy:assets:precompile' => ['test_track:generate_build_timestamp']
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to add a test for this since it’s now pretty critical to building the apps, not sure if we have any examples of testing rake tasks.

@@ -0,0 +1,9 @@
BUILD_TIMESTAMP_FILE_PATH = 'testtrack/build_timestamp'.freeze

if Rails.env.test? || Rails.env.development?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getting some tests around this behavior would be great too. Not sure if you can test an initializer. But we could pull this logic into a function, test that and then just call that here

@aburgel
Copy link
Collaborator

aburgel commented Feb 1, 2020

Tafn! This is great. Mostly comments about test coverage. Nice work!

@nanda-prbot
Copy link

Needs somebody from @Betterment/test_track_core to claim domain review

Use the shovel operator to claim, e.g.:

@myname << domain && platform

HOW TO: Claim a Review

* constant name case
* cli task name to match updated go cli
* update cli version
removes from the initializer to write tests. Also relies on some assumptions to simplify the conditional logic.
* renames initialzier method to load_build_timestamp
* vendorizes new version of cli
* adds more error handling around the timestamp loading and reading
* uses corerct assets precompile naming
@phantomphildius
Copy link
Contributor

nanda

@nanda-prbot
Copy link

Needs somebody from @Betterment/test_track_core to claim domain review

Use the shovel operator to claim, e.g.:

@myname << domain && platform

HOW TO: Claim a Review

around { |example| Timecop.freeze(Time.zone.parse('2020-02-01')) { example.run } }

context 'in a test environment' do
it 'assigns BUILD_TIMESTAMP to now' do
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
it 'assigns BUILD_TIMESTAMP to now' do
it 'assigns build_timestamp to now' do

end

context 'in a development environment' do
it 'assigns BUILD_TIMESTAMP to now' do
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
it 'assigns BUILD_TIMESTAMP to now' do
it 'assigns build_timestamp to now' do

end

context 'with an existing build_timestamp file' do
it 'assigns BUILD_TIMESTAMP to file\'s contents' do
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
it 'assigns BUILD_TIMESTAMP to file\'s contents' do
it 'assigns build_timestamp to file\'s contents' do


before do
allow(File).to receive(:exist?).and_return(file_readable)
allow(File).to receive(:read).and_return("2020-02-01T00:00:00Z\n")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since you're freezing time on line 191 to the exact same time as what you're stubbed here, its a hard to be confident that we're actually reading from the file and not from frozen time

elsif File.exist?(BUILD_TIMESTAMP_FILE_PATH)
raise "TestTrack's build_timestamp is not formatted properly." unless BUILD_TIMESTAMP_REGEX.match?(_build_timestamp)

@build_timestamp = _build_timestamp
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very minor nit. you're reading the file twice. on this line and line 61, you could capture build_timestamp in a local var to avoid that

@@ -0,0 +1 @@
TestTrack.load_build_timestamp
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need test_track in the filename? may just load_build_timestamp? since test_track is kinda implied

@build_timestamp = _build_timestamp
else
raise 'TestTrack failed to load the required build timestamp. ' \
'Ensure `testtrack load_build_timestamp` is run and the build timestamp file is present.'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
'Ensure `testtrack load_build_timestamp` is run and the build timestamp file is present.'
'Ensure `test_track:generate_build_timestamp` task is run and the build timestamp file is present.'

if Rails.env.test? || Rails.env.development?
@build_timestamp = Time.zone.now.iso8601
elsif File.exist?(BUILD_TIMESTAMP_FILE_PATH)
raise "TestTrack's build_timestamp is not formatted properly." unless BUILD_TIMESTAMP_REGEX.match?(_build_timestamp)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
raise "TestTrack's build_timestamp is not formatted properly." unless BUILD_TIMESTAMP_REGEX.match?(_build_timestamp)
raise "./testtrack/build_timestamp is not a valid ISO 8601 timestamp, got '#{_build_timestamp}'" unless BUILD_TIMESTAMP_REGEX.match?(_build_timestamp)

end

def build_timestamp
@build_timestamp.presence || raise('build_timestamp is not defined. Ensure `load_build_timestamp` is run.')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@build_timestamp.presence || raise('build_timestamp is not defined. Ensure `load_build_timestamp` is run.')
@build_timestamp.presence || raise('build_timestamp is not defined because `load_build_timestamp` initializer was not run.')

@aburgel
Copy link
Collaborator

aburgel commented Feb 19, 2020

platformlgtm! looking great! dropped in a couple of minor suggestions around the exception messages.

@nanda-prbot
Copy link

Needs somebody from @Betterment/test_track_core to claim domain review

Use the shovel operator to claim, e.g.:

@myname << domain && platform

HOW TO: Claim a Review

@phantomphildius
Copy link
Contributor

nanda

@nanda-prbot
Copy link

Needs somebody from @Betterment/test_track_core to claim domain review

Use the shovel operator to claim, e.g.:

@myname << domain && platform

HOW TO: Claim a Review

end

def build_timestamp
@build_timestamp.presence || raise('build_timestamp is not defined because `load_build_timestamp` initializer was not run.')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this should note that it normally runs as part of assets:precompile

Copy link
Member

@smudge smudge Feb 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(same comment for the error above)

def load_build_timestamp # rubocop:disable Metrics/MethodLength
if Rails.env.test? || Rails.env.development?
@build_timestamp = Time.zone.now.iso8601
elsif File.exist?(BUILD_TIMESTAMP_FILE_PATH)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking we can maybe combine the error cases so we only have to raise once (regardless of whether the file exists)

    def load_build_timestamp
      if Rails.env.test? || Rails.env.development?
        @build_timestamp = Time.zone.now.iso8601
      elsif _build_timestamp
        unless BUILD_TIMESTAMP_REGEX.match?(timestamp)
          raise "./testtrack/build_timestamp is not a valid ISO 8601 timestamp, got '#{timestamp}'"
        end

        @build_timestamp = _build_timestamp
      else
        raise '...'
      end
    end

    attr_reader :build_timestamp

  # ...

  def _build_timestamp
    File.read(BUILD_TIMESTAMP_FILE_PATH).chomp.presence if File.exist?(BUILD_TIMESTAMP_FILE_PATH)
  end

Main thing there is that by calling .presence sooner and sharing the raise during the initializer code, we can make build_timestamp an attr_reader and read straight off the ivar.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I'm thinking this is a safe strategy because the gem itself defines the initializer and owns load_build_timestamp, so unless we suspect that somehow folks are gonna skip initialization of the app?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Alternatively, I sort of wonder if we could rename load_build_timestamp to build_timestamp -- have it fetch all the things internally, run all the checks, etc -- and then basically just pre-heat it from the initializer.)

@smudge
Copy link
Member

smudge commented Feb 22, 2020

<< domain LGTM - this looks totally correct to me. I don't want to block you from merging, but I did leave a pile o' structural suggestions, take them or leave them. (Also I'm thinking we could mention the assets:precompile behavior in the error messages.)

@nanda-prbot
Copy link

Approved! 🌮 💥 💰

@@ -31,3 +39,4 @@ end
task 'db:schema:load' => ['test_track:schema:load']
task 'db:structure:load' => ['test_track:schema:load']
task 'db:migrate' => ['test_track:migrate']
task 'assets:precompile' => ['test_track:generate_build_timestamp']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

going to try reordering the tasks and adding a flag to not run the initializers until this is done here

@phantomphildius
Copy link
Contributor

nanda

@nanda-prbot
Copy link

Approved! ✨ 💥 🎯

@@ -0,0 +1 @@
TestTrack.load_build_timestamp unless ENV['SKIP_TIMESTAMP_INIT'] == '1'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
TestTrack.load_build_timestamp unless ENV['SKIP_TIMESTAMP_INIT'] == '1'
TestTrack.load_build_timestamp unless ENV['SKIP_TESTTRACK_LOAD_BUILD_TIMESTAMP'] == '1'

@@ -10,6 +10,19 @@ namespace :test_track do
end
end

desc 'Generates build timestamp for consuming applications to build versioned requests to split registry'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
desc 'Generates build timestamp for consuming applications to build versioned requests to split registry'
desc 'Generates build timestamp for fetching point-in-time split registries'

lib/tasks/test_track_rails_client_tasks.rake Show resolved Hide resolved
@aburgel aburgel changed the title Add build timestamp to rails client Add support for timestamped split registry endpoint Apr 15, 2020
@aburgel aburgel merged commit e0b7039 into Betterment:master Apr 15, 2020
@phantomphildius phantomphildius deleted the rg/add-timestamp branch April 24, 2023 20:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants