lib/chef/knife/github_base.rb in knife-github-0.0.3 vs lib/chef/knife/github_base.rb in knife-github-0.0.6

- old
+ new

@@ -15,20 +15,22 @@ # See the License for the specific language governing permissions and # limitations under the License. # # require 'chef/knife' +require "knife-github/version" class Chef class Knife module GithubBase def self.included(includer) includer.class_eval do deps do require 'chef/mixin/shell_out' + require 'mixlib/versioning' end option :github_url, :long => "--github_url URL", :description => "URL of the github enterprise appliance" @@ -49,83 +51,283 @@ option :github_ssl_verify_mode, :long => "--github_ssl_verify_mode", :description => "SSL verify mode: verify_peer, verify_none (default: verify_peer)", :boolean => true - option :github_cache, - :long => "--github_cache MIN", - :description => "Max life-time for local cache files in minutes (default: 900)" + option :github_tmp, + :long => "--github_tmp PATH", + :description => "A path where temporary files for the diff function will be made default: /tmp/gitdiff)" + option :github_no_update, + :long => "--github_no_update", + :description => "Turn github update checking off", + :boolean => true + def validate_base_options unless locate_config_value('github_url') ui.error "Github URL not specified" exit 1 end unless locate_config_value('github_organizations') ui.error "Github organization(s) not specified" exit 1 end + unless locate_config_value('github_no_update') + check_gem_version + end @github_url = locate_config_value("github_url") @github_organizations = locate_config_value("github_organizations") - @github_cache = (locate_config_value("github_cache") || 900).to_i @github_link = locate_config_value("github_link") || 'ssh' @github_api_version = locate_config_value("github_api_version") || 'v3' @github_ssl_verify_mode = locate_config_value("github_ssl_verify_mode") || 'verify_peer' + @github_tmp = locate_config_value("github_tmp") || '/var/tmp/gitdiff' + @github_tmp = "#{@github_tmp}#{Process.pid}" end + def check_gem_version + url = 'http://rubygems.org/api/v1/gems/knife-github.json' + result = `curl -L -s #{url}` + begin + json = JSON.parse(result) + webversion = Mixlib::Versioning.parse(json['version']) + thisversion = Mixlib::Versioning.parse(::Knife::Github::VERSION) + if webversion > thisversion + ui.info "INFO: New version (#{webversion.to_s}) of knife-github is available!" + ui.info "INFO: Turn off this message with --github_no_update or add knife[:github_no_update] = true to your configuration" + end + Chef::Log.debug("local_gem_version : " + thisversion.to_s) + Chef::Log.debug("repo_gem_version : " + webversion.to_s) + Chef::Log.debug("repo_downloads : " + json['version_downloads'].to_s) + Chef::Log.debug("repo_total_downloads : " + json['downloads'].to_s) + + rescue + ui.info "INFO: Cannot verify gem version information from rubygems.org" + ui.info "INFO: Turn off this message with --github_no_update or add knife[:github_no_update] = true to your configuration" + end + end + def display_debug_info - Chef::Log.debug("github_url: " + @github_url.to_s) - Chef::Log.debug("github_org: " + @github_organizations.to_s) - Chef::Log.debug("github_api: " + @github_api_version.to_s) - Chef::Log.debug("github_link: " + @github_link.to_s) - Chef::Log.debug("github_cache: " + @github_cache.to_s) - Chef::Log.debug("github_ssl_mode: " + @github_ssl_verify_mode.to_s) + Chef::Log.debug("github_url : " + @github_url.to_s) + Chef::Log.debug("github_org : " + @github_organizations.to_s) + Chef::Log.debug("github_api : " + @github_api_version.to_s) + Chef::Log.debug("github_link : " + @github_link.to_s) + Chef::Log.debug("github_ssl_mode : " + @github_ssl_verify_mode.to_s) end def locate_config_value(key) key = key.to_sym config[key] || Chef::Config[:knife][key] end - def get_github_link(link) - git_link = case link + def get_repo_clone_link + link = locate_config_value('github_link') + repo_link = case link when 'ssh' then 'ssh_url' when 'http' then 'clone_url' when 'https' then 'clone_url' when 'svn' then 'svn_url' when 'html' then 'html_url' when 'git' then 'git_url' else 'ssh_url' end - git_link + return repo_link end + def get_all_repos(orgs) + # Parse every org and merge all into one hash + repos = {} + orgs.each do |org| + get_repos(org).each { |repo| name = repo['name'] ; repos["#{name}"] = repo } + end + repos + end + + def get_repos(org) + dns_name = get_dns_name(@github_url) + file_cache = "#{ENV['HOME']}/.chef/.#{dns_name.downcase}_#{org.downcase}" + + if File.exists?(file_cache + ".json") + json = JSON.parse(File.read(file_cache + ".json")) + json_updated = Time.parse(json['updated_at']) + Chef::Log.info("#{org} - cache created at : " + json_updated.to_s) + repo_updated = get_org_updated_time(org) + Chef::Log.info("#{org} - repos updated at : " + repo_updated.to_s) + + unless json_updated >= repo_updated + # update cache file + create_cache_file(file_cache + ".cache", org) + create_cache_json(file_cache + ".json", org) + end + else + create_cache_file(file_cache + ".cache", org) + create_cache_json(file_cache + ".json", org) + end + + # use cache files + JSON.parse(File.read(file_cache + ".cache")) + end + + def create_cache_json(file, org) + Chef::Log.debug("Updating the cache file: #{file}") + url = @github_url + "/api/" + @github_api_version + "/orgs/" + org + params = {'response' => 'json'} + result = send_request(url, params) + File.open(file, 'w') { |f| f.write(JSON.pretty_generate(result)) } + end + + def create_cache_file(file, org) + Chef::Log.debug("Updating the cache file: #{file}") + result = get_repos_github(org) + File.open(file, 'w') { |f| f.write(JSON.pretty_generate(result)) } + end + + def get_org_updated_time(org) + url = @github_url + "/api/" + @github_api_version + "/orgs/" + org + params = {'response' => 'json'} + result = send_request(url, params) + Time.parse(result['updated_at']) + end + + def get_repos_github(org) + # Get all repo's for the org from github + arr = [] + page = 1 + url = @github_url + "/api/" + @github_api_version + "/orgs/" + org + "/repos" + while true + params = {'response' => 'json', 'page' => page } + result = send_request(url, params) + break if result.nil? || result.count < 1 + result.each { |key| + if key['tags_url'] + tags = get_tags(key) + key['tags'] = tags unless tags.nil? || tags.empty? + key['latest_tag'] = get_latest_tag(tags) + arr << key + else + arr << key + end + } + page = page + 1 + end + arr + end + + def get_tags(repo) + params = {'response' => 'json'} + tags = send_request(repo['tags_url'], params) + tags + end + + def get_latest_tag(tags) + return "" if tags.nil? || tags.empty? + tags_arr =[] + tags.each do |tag| + tags_arr.push(Mixlib::Versioning.parse(tag['name'])) if tag['name'] =~ /^(\d*)\.(\d*)\.(\d*)$/ + end + return "" if tags_arr.nil? || tags_arr.empty? + return tags_arr.sort.last.to_s + end + + def get_dns_name(url) + url = url.downcase.gsub("http://","") if url.downcase.start_with?("http://") + url = url.downcase.gsub("https://","") if url.downcase.start_with?("https://") + url + end + def send_request(url, params = {}) - params['response'] = 'json' + unless params.empty? + params_arr = [] + params.sort.each { |elem| + params_arr << elem[0].to_s + '=' + CGI.escape(elem[1].to_s).gsub('+', '%20').gsub(' ','%20') + } + data = params_arr.join('&') + url = "#{url}?#{data}" + end - params_arr = [] - params.sort.each { |elem| - params_arr << elem[0].to_s + '=' + CGI.escape(elem[1].to_s).gsub('+', '%20').gsub(' ','%20') - } - data = params_arr.join('&') - - github_url = "#{url}?#{data}" - # Chef::Log.debug("URL: #{github_url}") + if @github_ssl_verify_mode == "verify_none" + config[:ssl_verify_mode] = :verify_none + elsif @github_ssl_verify_mode == "verify_peer" + config[:ssl_verify_mode] = :verify_peer + end - uri = URI.parse(github_url) + Chef::Log.debug("URL: " + url.to_s) + + uri = URI.parse(url) req_body = Net::HTTP::Get.new(uri.request_uri) request = Chef::REST::RESTRequest.new("GET", uri, req_body, headers={}) - + response = request.call - - if !response.is_a?(Net::HTTPOK) then + + unless response.is_a?(Net::HTTPOK) then puts "Error #{response.code}: #{response.message}" puts JSON.pretty_generate(JSON.parse(response.body)) puts "URL: #{url}" exit 1 end - json = JSON.parse(response.body) + + begin + json = JSON.parse(response.body) + rescue + ui.warn "The result on the RESTRequest is not in json format" + ui.warn "Output: " + response.body + exit 1 + end + return json + end + + def get_clone(url, cookbook) + if ! File.directory? @github_tmp + Dir.mkdir("#{@github_tmp}") + end + Dir.mkdir("#{@github_tmp}/git") + ui.info("Getting #{@cookbook_name} from #{url}") + output = `git clone #{url} #{@github_tmp}/git/#{cookbook} 2>&1` + if $?.exitstatus != 0 + Chef::Log.error("Could not clone the repository for: #{cookbook}") + FileUtils.remove_entry(@github_tmp) + exit 1 + end + return true + end + + def add_tag(version) + cpath = cookbook_path_valid?(@cookbook_name, false) + Dir.chdir(cpath) + Chef::Log.debug "Adding tag" + output = `git tag -a "#{version}" -m "Added tag #{version}" 2>&1` + if $?.exitstatus != 0 + Chef::Log.error("Could not add tag for: #{@cookbook_name}") + FileUtils.remove_entry(@github_tmp) + exit 1 + end + end + + def cookbook_path_valid?(cookbook_name, check_exists) + cookbook_path = config[:cookbook_path] || Chef::Config[:cookbook_path] + if cookbook_path.nil? || cookbook_path.empty? + Chef::Log.error("Please specify a cookbook path") + exit 1 + end + + unless File.exists?(cookbook_path.first) && File.directory?(cookbook_path.first) + Chef::Log.error("Cannot find the directory: #{cookbook_path.first}") + exit 1 + end + + cookbook_path = File.join(cookbook_path.first,cookbook_name) + if check_exists + if File.exists?(cookbook_path) + ui.info("Processing [S] #{cookbook_name}") + Chef::Log.info("Path to #{cookbook_path} already exists, skipping.") + return nil + end + else + if ! File.exists?(cookbook_path) + return nil + end + end + return cookbook_path end end end end