Skip to content

Commit

Permalink
Merge pull request #2127 from newrelic/gene_chandler_1961
Browse files Browse the repository at this point in the history
CI: Test for the health of all code base URLs
  • Loading branch information
fallwith authored Jul 11, 2023
2 parents 974fa24 + 76c95a8 commit 02bf110
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 5 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci_cron.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ jobs:
env:
VERBOSE_TEST_OUTPUT: true
DB_PORT: ${{ job.services.mysql.ports[3306] }}
CI_CRON: true


multiverse:
Expand Down
2 changes: 2 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1248,6 +1248,7 @@ Style/MethodCallWithArgsParentheses:
AllowedMethods:
- add_dependency
- add_development_dependency
- catch
- expect
- fail
- gem
Expand All @@ -1261,6 +1262,7 @@ Style/MethodCallWithArgsParentheses:
- source
- stub
- stub_const
- throw
- use
AllowedPatterns: [^assert, ^refute]

Expand Down
2 changes: 1 addition & 1 deletion lib/new_relic/agent/configuration/environment_source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def set_key_by_type(config_key, environment_key)
end
else
::NewRelic::Agent.logger.info("#{environment_key} does not have a corresponding configuration setting (#{config_key} does not exist).")
::NewRelic::Agent.logger.info('Run `rake newrelic:config:docs` or visit https://newrelic.com/docs/ruby/ruby-agent-configuration to see a list of available configuration settings.')
::NewRelic::Agent.logger.info('Run `rake newrelic:config:docs` or visit https://docs.newrelic.com/docs/apm/agents/ruby-agent/configuration/ruby-agent-configuration to see a list of available configuration settings.')
self[config_key] = value
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/new_relic/agent/instrumentation/memcache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# each with slightly different APIs and semantics.
# See:
# http://www.deveiate.org/code/Ruby-MemCache/ (Gem: Ruby-MemCache)
# http://seattlerb.rubyforge.org/memcache-client/ (Gem: memcache-client)
# https://github.com/mperham/memcache-client (Gem: memcache-client)
# https://github.com/mperham/dalli (Gem: dalli)

require_relative 'memcache/helper'
Expand Down
2 changes: 1 addition & 1 deletion lib/new_relic/agent/instrumentation/queue_time.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
module NewRelic
module Agent
module Instrumentation
# https://newrelic.com/docs/features/tracking-front-end-time
# https://docs.newrelic.com/docs/features/tracking-front-end-time
# Record queue time metrics based on any of three headers
# which can be set on the request.
module QueueTime
Expand Down
2 changes: 1 addition & 1 deletion lib/new_relic/agent/instrumentation/sequel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def supported_sequel_version?
else
NewRelic::Agent.logger.info('Detected Sequel version %s.' % [Sequel::VERSION])
NewRelic::Agent.logger.info('Please see additional documentation: ' +
'https://newrelic.com/docs/ruby/sequel-instrumentation')
'https://docs.newrelic.com/docs/apm/agents/ruby-agent/frameworks/sequel-instrumentation/')
end

Sequel.synchronize { Sequel::DATABASES.dup }.each do |db|
Expand Down
2 changes: 1 addition & 1 deletion lib/new_relic/agent/pipe_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def initialize(channel_id)
if @pipe && @pipe.parent_pid != $$
@pipe.after_fork_in_child
else
NewRelic::Agent.logger.error('No communication channel to parent process, please see https://newrelic.com/docs/ruby/resque-instrumentation for more information.')
NewRelic::Agent.logger.error('No communication channel to parent process, please see https://docs.newrelic.com/docs/apm/agents/ruby-agent/background-jobs/resque-instrumentation/ for more information.')
end
end

Expand Down
26 changes: 26 additions & 0 deletions test/helpers/misc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,29 @@ def skip_unless_minitest5_or_above

skip 'This test requires MiniTest v5+'
end

def skip_unless_ci_cron
return if ENV['CI_CRON']

skip 'This test only runs as part of the CI cron workflow'
end

def agent_root
@agent_root ||= File.expand_path('../../..', __FILE__).freeze
end

def newest_ruby
@newest_ruby ||= begin
hash = YAML.load_file(File.join(agent_root, '.github/workflows/ci_cron.yml'))
version_string = hash['jobs']['unit_tests']['strategy']['matrix']['ruby-version'].sort do |a, b|
Gem::Version.new(a) <=> Gem::Version.new(b)
end.last
Gem::Version.new(version_string)
end
end

def skip_unless_newest_ruby
return if Gem::Version.new(RUBY_VERSION) >= newest_ruby

skip 'This test only runs on the latest CI cron Ruby version'
end
138 changes: 138 additions & 0 deletions test/new_relic/healthy_urls_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

# The unit test class defined herein will verify the health of all URLs found
# in the project source code.
#
# To run the URL health tests by themselves:
# TEST=test/new_relic/healthy_urls_test bundle exec rake test
#
# A file will be scanned for URLs if the file's basename is found in the
# FILENAMES array OR the file's extension is found in the EXTENSIONS array
# unless the file's absolute path matches the IGNORED_FILE_PATTERN regex.
#
# NOTE that CHANGELOG.md is handled with special logic so that only the most
# recent 2 versions mentioned in the changelog are scannned for URLs.
#
# See TIMEOUT for the number of seconds permitted for a GET request to a given
# URL to be completed.
#
# Enable DEBUG for additional verbosity

require_relative '../test_helper'

class HealthyUrlsTest < Minitest::Test
ROOT = File.expand_path('../../..', __FILE__).freeze
FILENAMES = %w[
baselines
Brewfile
Capfile
Dockerfile
Envfile
Gemfile
Guardfile
install_mysql55
LICENSE
mega-runner
newrelic
newrelic_cmd
nrdebug
Rakefile
run_tests
runner
Thorfile
].freeze
EXTENSIONS = %w[
css
erb
gemspec
haml
html
js
json
md
proto
rake
readme
rb
sh
txt
thor
tt
yml
].freeze
FILE_PATTERN = /(?:^(?:#{FILENAMES.join('|')})$)|\.(?:#{EXTENSIONS.join('|')})$/.freeze
IGNORED_FILE_PATTERN = %r{/(?:coverage|test)/}.freeze
URL_PATTERN = %r{(https?://.*?)[^a-zA-Z0-9/\.\-_#]}.freeze
IGNORED_URL_PATTERN = %r{(?:\{|\(|\$|169\.254|\.\.\.|metadata\.google)}
TIMEOUT = 5
DEBUG = false

def test_all_urls
skip_unless_ci_cron
skip_unless_newest_ruby
load_httparty

urls = gather_urls
errors = urls.each_with_object({}) do |(url, _files), hash|
error = verify_url(url)
hash[url] = error if error
end

msg = "#{errors.keys.size} URLs were unreachable!\n\n"
msg += errors.map { |url, error| " #{url} - #{error}\n files: #{urls[url].join(',')}" }.join("\n")

assert_empty errors, msg
end

private

def load_httparty
require 'httparty'
rescue
skip 'Skipping URL health tests in this context, as HTTParty is not available'
end

def real_url?(url)
return false if url.match?(IGNORED_URL_PATTERN)

true
end

def gather_urls
Dir.glob(File.join(ROOT, '**', '*')).each_with_object({}) do |file, urls|
next unless File.file?(file) && File.basename(file).match?(FILE_PATTERN) && !file.match?(IGNORED_FILE_PATTERN)

changelog_entries_seen = 0
File.open(file).each do |line|
changelog_entries_seen += 1 if File.basename(file).eql?('CHANGELOG.md') && line.start_with?('##')
break if changelog_entries_seen > 2
next unless line =~ URL_PATTERN

url = Regexp.last_match(1).sub(%r{(?:/|\.)$}, '')
if real_url?(url)
urls[url] ||= []
urls[url] << file
end
end
end
end

def verify_url(url)
puts "Testing '#{url}'..." if DEBUG
res = HTTParty.get(url, timeout: TIMEOUT)
if res.success?
puts ' OK.' if DEBUG
return
end

msg = "HTTP #{res.code}: #{res.message}"
puts " FAILED. #{msg}" if DEBUG
msg
rescue StandardError => e
msg = "#{e.class}: #{e.message}"
puts " FAILED. #{msg}" if DEBUG
msg
end
end

0 comments on commit 02bf110

Please sign in to comment.