|
| 1 | +require 'open-uri' |
| 2 | +require 'json' |
| 3 | +require 'tmpdir' |
| 4 | + |
| 5 | +module Fluent |
| 6 | + module Plugin |
| 7 | + module FluentPackage |
| 8 | + class UpdateChecker |
| 9 | + DEFAULT_PACKAGE_CONFIG_PATH = "/opt/fluent/share/config" |
| 10 | + def initialize(options={}) |
| 11 | + @logger = options[:logger] |
| 12 | + @options = options |
| 13 | + @newer_versions = [] |
| 14 | + @newer_lts_versions = [] |
| 15 | + @major_lts_updates = [] |
| 16 | + @major_updates = [] |
| 17 | + @tmp_dir = Dir.mktmpdir("fluent_package_update_notifier") |
| 18 | + begin |
| 19 | + require ENV["FLUENT_PACKAGE_CONFIG"] || DEFAULT_PACKAGE_CONFIG_PATH |
| 20 | + rescue LoadError |
| 21 | + @logger.error "Failed to load #{ENV["FLUENT_PACKAGE_CONFIG"] || DEFAULT_PACKAGE_CONFIG_PATH}" |
| 22 | + end |
| 23 | + end |
| 24 | + |
| 25 | + def tags_cached? |
| 26 | + File.exist?(cached_tags_path) and |
| 27 | + (Time.now - 60 * 60 * 24) < File.mtime(cached_tags_path) |
| 28 | + end |
| 29 | + |
| 30 | + def cached_tags_path |
| 31 | + ENV["FLUENT_PACKAGE_TAGS_PATH"] ? |
| 32 | + ENV["FLUENT_PACKAGE_TAGS_PATH"] : "#{@tmp_dir}/fluent-package-tags.json" |
| 33 | + end |
| 34 | + |
| 35 | + def release_tags_url |
| 36 | + "https://api.github.com/repos/fluent/fluent-package-builder/tags" |
| 37 | + end |
| 38 | + |
| 39 | + def fetch_tags |
| 40 | + begin |
| 41 | + if tags_cached? |
| 42 | + yield JSON.parse(File.open(cached_tags_path).read) |
| 43 | + else |
| 44 | + URI.open(release_tags_url) do |resource| |
| 45 | + File.open(cached_tags_path, "w+") do |f| |
| 46 | + json = resource.read |
| 47 | + f.write(json) |
| 48 | + yield JSON.parse(json) |
| 49 | + end |
| 50 | + end |
| 51 | + end |
| 52 | + rescue => e |
| 53 | + @logger.error "Failed to fetch tags", error: e |
| 54 | + end |
| 55 | + end |
| 56 | + |
| 57 | + def same_lts_series?(base_version, target_version) |
| 58 | + # compare [major, 0] pair information |
| 59 | + @options[:lts] and [base_version.segments.first, 0] == target_version.segments[..1] |
| 60 | + end |
| 61 | + |
| 62 | + def major_lts_update?(base_version, target_version) |
| 63 | + @options[:lts] and |
| 64 | + base_version.segments.first < target_version.segments.first and |
| 65 | + [base_version.segments[1], target_version.segments[1]] == [0, 0] |
| 66 | + end |
| 67 | + |
| 68 | + def check_update_versions |
| 69 | + current_version = Gem::Version.new("#{PACKAGE_VERSION}") |
| 70 | + fetch_tags do |releases| |
| 71 | + releases.each do |release| |
| 72 | + version = release["name"] |
| 73 | + next if version.include?("-") # skip test artifacts |
| 74 | + target_version = Gem::Version.new(version.delete("v")) |
| 75 | + if target_version > current_version |
| 76 | + major = current_version.segments.first |
| 77 | + if same_lts_series?(current_version, target_version) |
| 78 | + @newer_lts_versions << version |
| 79 | + elsif version.start_with?("v#{major}") |
| 80 | + @newer_versions << version |
| 81 | + else |
| 82 | + # major upgrade standard/LTS version |
| 83 | + if major_lts_update?(current_version, target_version) |
| 84 | + # LTS |
| 85 | + @major_lts_updates << version |
| 86 | + else |
| 87 | + @major_updates << version |
| 88 | + end |
| 89 | + end |
| 90 | + end |
| 91 | + end |
| 92 | + end |
| 93 | + FileUtils.rm_rf(@tmp_dir) |
| 94 | + end |
| 95 | + |
| 96 | + def notify_update_log |
| 97 | + candidates = nil |
| 98 | + if @options[:lts] |
| 99 | + if @options[:notify_major_upgrade] |
| 100 | + candidates = if @major_lts_updates.count > 0 |
| 101 | + @major_lts_updates |
| 102 | + elsif @newer_lts_versions.count > 0 |
| 103 | + @newer_lts_versions |
| 104 | + end |
| 105 | + else |
| 106 | + candidates = @newer_lts_versions |
| 107 | + end |
| 108 | + else |
| 109 | + if @options[:notify_major_upgrade] |
| 110 | + candidates = if @major_updates.count > 0 |
| 111 | + @major_updates |
| 112 | + elsif @newer_versions.count > 0 |
| 113 | + @newer_versions |
| 114 | + end |
| 115 | + else |
| 116 | + candidates = @newer_versions |
| 117 | + end |
| 118 | + end |
| 119 | + if candidates |
| 120 | + # Assume the version information are already sorted in descendant order by GitHub API |
| 121 | + url = "https://github.com/fluent/fluent-package-builder/releases/tag/#{candidates.first}" |
| 122 | + message = "fluent-package #{candidates.first} is available! See #{url} in details." |
| 123 | + else |
| 124 | + message = "No update for fluent-package #{PACKAGE_VERSION}" |
| 125 | + end |
| 126 | + case @options[:notify_level] |
| 127 | + when :warn |
| 128 | + @logger.warn(message) |
| 129 | + when :info |
| 130 | + @logger.info(message) |
| 131 | + end |
| 132 | + end |
| 133 | + |
| 134 | + def run |
| 135 | + check_update_versions |
| 136 | + notify_update_log |
| 137 | + end |
| 138 | + end |
| 139 | + end |
| 140 | + end |
| 141 | +end |
0 commit comments