{"action":"created","comment":{"url":"https://api.github.com/repos/opscode/opscode-omnibus/pulls/comments/22957340","id":22957340,"diff_hunk":"@@ -0,0 +1,255 @@\n+# Copyright (c) 2014 Chef Software, Inc.\n+# All Rights Reserved\n+\n+require \"sequel\"\n+require \"openssl\"\n+require \"time\"\n+\n+cmd_args = ARGV[3..-1]\n+\n+# Pull all the DB params from config; the database name seems to be hardcoded,\n+# however -- I found it in the postgresql.rb recipe in the private-chef cookbooks;\n+# I'm not sure why we hardcode it instead of making it configurable, but I assume\n+# there are reasons?\n+db_user = running_config['private_chef']['postgresql']['sql_user']\n+db_password = running_config['private_chef']['postgresql']['sql_password']\n+db_host = running_config['private_chef']['postgresql']['listen_address']\n+\n+# set convert_infinite_timestamps to a float so that Sequel doesn't crash out on querying an infinity timestamp\n+# see this commit for more details\n+# https://github.com/jeremyevans/sequel/commit/2440e98b4a7063a90d8337e57b1c3e3d3a1cbd0b\n+DB = Sequel.connect(:adapter => 'postgres', :host => db_host,\n+  :database => 'opscode_chef', :user => db_user, :password => db_password, :convert_infinite_timestamps => :float)\n+\n+# Helper Class\n+class PublicKeyOutputter\n+  def output_key_results(results, hide_public_keys)\n+    results.each do |row|\n+      puts \"key_id: #{row[:key_id]}\"\n+      puts \"    id: #{row[:id]}\"\n+      unless hide_public_keys\n+        puts \"        public_key:\"\n+        row[:public_key].each_line do |line|\n+          puts \"        #{line}\"\n+        end\n+      end\n+    end\n+  end\n+end\n+\n+class ArgParser\n+  def find_arg_after_identifier(args, identifier)\n+    result = nil\n+    args.each_with_index do |arg, i|\n+      if arg == identifier\n+        result = args[i+1]\n+        break\n+      end\n+    end\n+    result\n+  end\n+end\n+\n+class AddKey\n+  def check_org_args(cmd_args, usage_msg)\n+    unless cmd_args.length == 2 || \\\n+      (cmd_args.length == 4 && (cmd_args[2] == \"--expiration-date\" || cmd_args[2] == \"--key-name\")) || \\\n+      (cmd_args.length == 6 && ((cmd_args[2] == \"--expiration-date\" && cmd_args[4] == \"--key-name\") || (cmd_args[4] == \"--expiration-date\" && cmd_args[2] == \"--key-name\")))\n+      STDERR.puts usage_msg\n+      STDERR.puts \"Usage: Expiration date defaults to infinite. Pass an ISO 8601 fomatted string: YYYY-MM-DDTHH:MM:SS e.g. 2013-12-24T21:00:00\"\n+      STDERR.puts \"Usage: Default name used is the fingerprint of the key passed.\"\n+      exit 1\n+    end\n+  end\n+\n+  def check_client_args(cmd_args, usage_msg)\n+    unless cmd_args.length == 3 || \\\n+      (cmd_args.length == 5 && (cmd_args[3] == \"--expiration-date\" || cmd_args[3] == \"--key-name\")) || \\\n+      (cmd_args.length == 7 && ((cmd_args[3] == \"--expiration-date\" && cmd_args[5] == \"--key-name\") || (cmd_args[5] == \"--expiration-date\" && cmd_args[3] == \"--key-name\")))\n+      STDERR.puts usage_msg\n+      STDERR.puts \"Usage: Expiration date defaults to infinite. Pass an ISO 8601 fomatted string: YYYY-MM-DDTHH:MM:SS e.g. 2013-12-24T21:00:00\"\n+      STDERR.puts \"Usage: Default name used is the fingerprint of the key passed.\"\n+      exit 1\n+    end\n+  end\n+\n+  def add_key(cmd_args, id)\n+    # TODO should keyname be optional and default the key fingerprint?\n+\n+    # this is guaranteed to work because it would have crashed above if not\n+    # expiration_date and key_name will be nil if they weren't passed\n+    arg_parser = ArgParser.new\n+    expiration_date = arg_parser.find_arg_after_identifier(cmd_args, \"--expiration-date\")\n+    key_name        = arg_parser.find_arg_after_identifier(cmd_args, \"--key-name\")\n+    user = cmd_args[0]\n+    key = File.read(cmd_args[1])\n+\n+    unless /-----BEGIN PUBLIC KEY-----/.match(key)\n+      STDERR.puts \"Error: Invalid public key passed. Key must begin with:\"\n+      STDERR.puts \"Error: -----BEGIN PUBLIC KEY-----\"\n+      exit 1\n+    end\n+\n+    # if a key_name was not specified, use the fingerprint of the key\n+    unless key_name\n+      begin\n+        openssl_key_object = OpenSSL::PKey::RSA.new(key)\n+        data_string = OpenSSL::ASN1::Sequence([\n+                                               OpenSSL::ASN1::Integer.new(openssl_key_object.public_key.n),\n+                                               OpenSSL::ASN1::Integer.new(openssl_key_object.public_key.e)\n+                                              ])\n+        key_name = OpenSSL::Digest::SHA1.hexdigest(data_string.to_der).scan(/../).join(':')\n+      rescue\n+        STDERR.puts \"Error: Could not parse fingerprint for public key #{cmd_args[1]}.\"\n+        STDERR.puts \"Error: If optional --key-name arg is not specified, then fingerprint of key is used by default as the key_id.\"\n+        exit 1\n+      end\n+    end\n+\n+\n+    if expiration_date\n+      begin\n+        Time.parse(expiration_date)\n+      rescue\n+        STDERR.puts \"Error: Invalid date format inserted.\"\n+        STDERR.puts \"Error: You inserted #{expiration_date}.\"\n+        STDERR.puts \"Error: Please a date in format YYYY-MM-DDTHH:MM:SS e.g. 2013-12-24T21:00:00.\"\n+        exit 1\n+      end\n+    else\n+      expiration_date = \"infinity\"\n+    end\n+\n+    object = {\n+      'id'          => id,\n+      'key_id'      => key_name,\n+      'public_key'  => key,\n+      'key_version' => 0,\n+      'created_at'  => Time.now.utc,\n+      'expires_at'  => expiration_date\n+    }\n+\n+    begin\n+      DB[:keys].insert(object)\n+    rescue Sequel::UniqueConstraintViolation\n+      puts \"Error: The key_name you passed (#{key_name}) was not unique for your object (#{cmd_args[0]}).\"\n+      exit 1\n+    end\n+  end\n+end\n+\n+add_command_under_category \"add-client-key\", \"key-rotation\", \"Create a new client key\", 2 do\n+  addkey = AddKey.new\n+  addkey.check_client_args(cmd_args, \"Usage: chef-server-ctl add-client-key <orgname> <clientname> <path_to_public_key> [--expiration-date <date>] [--key-name <string>]\")\n+\n+  id = nil\n+  DB.fetch(\"SELECT id FROM clients WHERE name='#{cmd_args[1]}'\") do |row|\n+    id = row[:id]\n+  end\n+  unless id\n+    STDERR.puts \"Error: client could not be found for clientname #{cmd_args[1]}\"\n+    exit 1\n+  end\n+\n+  org_id = nil\n+  DB.fetch(\"SELECT id FROM orgs WHERE name='#{cmd_args[0]}'\") do |row|\n+    org_id = row[:id]\n+  end\n+  unless org_id\n+    STDERR.puts \"Error: organization could not be found for orgname #{cmd_args[0]}\"\n+    exit 1\n+  end\n+\n+  # delete orgname from the cmd_args so that it has the same interface as add-user-key\n+  cmd_args.delete_at(0)\n+\n+  addkey.add_key(cmd_args, id)\n+end\n+\n+add_command_under_category \"add-user-key\", \"key-rotation\", \"Create a new user key\", 2 do\n+  addkey = AddKey.new\n+  addkey.check_org_args(cmd_args, \"Usage: chef-server-ctl add-user-key <username> <path_to_public_key> [--expiration-date <date>] [--key-name <string>]\")\n+  id = nil\n+  DB.fetch(\"SELECT id FROM users WHERE username='#{cmd_args[0]}'\") do |row|\n+    id = row[:id]\n+  end\n+  unless id\n+    STDERR.puts \"Error: user could not be found for username #{cmd_args[0]}\"\n+    exit 1\n+  end\n+  AddKey.new.add_key(cmd_args, id)\n+end\n+\n+\n+add_command_under_category \"delete-key\", \"key-rotation\", \"Delete a key\", 2 do\n+    unless cmd_args.length == 2 || \\\n+      (cmd_args.length == 4 && (cmd_args[2] == \"--expiration-date\" || cmd_args[2] == \"--key-name\")) || \\\n+      (cmd_args.length == 6 && ((cmd_args[2] == \"--expiration-date\" && cmd_args[4] == \"--key-name\") || (cmd_args[4] == \"--expiration-date\" && cmd_args[2] == \"--key-name\")))\n+      STDERR.puts usage_msg\n+      STDERR.puts \"Usage: Expiration date defaults to infinite. Pass an ISO 8601 fomatted string: YYYY-MM-DDTHH:MM:SS e.g. 2013-12-24T21:00:00\"\n+      STDERR.puts \"Usage: Default name used is the fingerprint of the key passed.\"\n+      exit 1\n+    end\n+\n+  unless cmd_args.length == 2\n+    STDERR.puts \"Usage: chef-server-ctl delete-key <id> <key_id>\"\n+    exit 1\n+  end\n+\n+  results = DB[:keys].where(\"id='#{cmd_args[0]}' AND key_id='#{cmd_args[1]}'\")","path":"files/private-chef-ctl-commands/key-control.rb","position":199,"original_position":199,"commit_id":"53750a5591676edf4b400bf48212197641f6e1c1","original_commit_id":"53750a5591676edf4b400bf48212197641f6e1c1","user":{"login":"tylercloke","id":1899715,"avatar_url":"https://avatars.githubusercontent.com/u/1899715?v=3","gravatar_id":"","url":"https://api.github.com/users/tylercloke","html_url":"https://github.com/tylercloke","followers_url":"https://api.github.com/users/tylercloke/followers","following_url":"https://api.github.com/users/tylercloke/following{/other_user}","gists_url":"https://api.github.com/users/tylercloke/gists{/gist_id}","starred_url":"https://api.github.com/users/tylercloke/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tylercloke/subscriptions","organizations_url":"https://api.github.com/users/tylercloke/orgs","repos_url":"https://api.github.com/users/tylercloke/repos","events_url":"https://api.github.com/users/tylercloke/events{/privacy}","received_events_url":"https://api.github.com/users/tylercloke/received_events","type":"User","site_admin":false},"body":"I agree that passing the key_id is shitty, and I would expect us to change this interface once we have an API up and running. I was trying to find the right line between quality and brevity with this code, because we are going to throw it all away once we have an API.","created_at":"2015-01-14T18:24:19Z","updated_at":"2015-01-14T18:24:19Z","html_url":"https://github.com/opscode/opscode-omnibus/pull/653#discussion_r22957340","pull_request_url":"https://api.github.com/repos/opscode/opscode-omnibus/pulls/653","_links":{"self":{"href":"https://api.github.com/repos/opscode/opscode-omnibus/pulls/comments/22957340"},"html":{"href":"https://github.com/opscode/opscode-omnibus/pull/653#discussion_r22957340"},"pull_request":{"href":"https://api.github.com/repos/opscode/opscode-omnibus/pulls/653"}}},"pull_request":{"url":"https://api.github.com/repos/opscode/opscode-omnibus/pulls/653","id":26856601,"html_url":"https://github.com/opscode/opscode-omnibus/pull/653","diff_url":"https://github.com/opscode/opscode-omnibus/pull/653.diff","patch_url":"https://github.com/opscode/opscode-omnibus/pull/653.patch","issue_url":"https://api.github.com/repos/opscode/opscode-omnibus/issues/653","number":653,"state":"open","locked":false,"title":"chef-server-ctl key commands","user":{"login":"tylercloke","id":1899715,"avatar_url":"https://avatars.githubusercontent.com/u/1899715?v=3","gravatar_id":"","url":"https://api.github.com/users/tylercloke","html_url":"https://github.com/tylercloke","followers_url":"https://api.github.com/users/tylercloke/followers","following_url":"https://api.github.com/users/tylercloke/following{/other_user}","gists_url":"https://api.github.com/users/tylercloke/gists{/gist_id}","starred_url":"https://api.github.com/users/tylercloke/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tylercloke/subscriptions","organizations_url":"https://api.github.com/users/tylercloke/orgs","repos_url":"https://api.github.com/users/tylercloke/repos","events_url":"https://api.github.com/users/tylercloke/events{/privacy}","received_events_url":"https://api.github.com/users/tylercloke/received_events","type":"User","site_admin":false},"body":"Need to clean up SQL statements to not execute raw user inputted strings lol, but otherwise ready to review.","created_at":"2015-01-05T22:36:37Z","updated_at":"2015-01-14T18:24:19Z","closed_at":null,"merged_at":null,"merge_commit_sha":"34f77db0f71362a9f8ec2ce7ae03513e979f42ab","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/opscode/opscode-omnibus/pulls/653/commits","review_comments_url":"https://api.github.com/repos/opscode/opscode-omnibus/pulls/653/comments","review_comment_url":"https://api.github.com/repos/opscode/opscode-omnibus/pulls/comments/{number}","comments_url":"https://api.github.com/repos/opscode/opscode-omnibus/issues/653/comments","statuses_url":"https://api.github.com/repos/opscode/opscode-omnibus/statuses/53750a5591676edf4b400bf48212197641f6e1c1","head":{"label":"opscode:dt/key-commands","ref":"dt/key-commands","sha":"53750a5591676edf4b400bf48212197641f6e1c1","user":{"login":"opscode","id":29740,"avatar_url":"https://avatars.githubusercontent.com/u/29740?v=3","gravatar_id":"","url":"https://api.github.com/users/opscode","html_url":"https://github.com/opscode","followers_url":"https://api.github.com/users/opscode/followers","following_url":"https://api.github.com/users/opscode/following{/other_user}","gists_url":"https://api.github.com/users/opscode/gists{/gist_id}","starred_url":"https://api.github.com/users/opscode/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/opscode/subscriptions","organizations_url":"https://api.github.com/users/opscode/orgs","repos_url":"https://api.github.com/users/opscode/repos","events_url":"https://api.github.com/users/opscode/events{/privacy}","received_events_url":"https://api.github.com/users/opscode/received_events","type":"Organization","site_admin":false},"repo":{"id":3444034,"name":"opscode-omnibus","full_name":"opscode/opscode-omnibus","owner":{"login":"opscode","id":29740,"avatar_url":"https://avatars.githubusercontent.com/u/29740?v=3","gravatar_id":"","url":"https://api.github.com/users/opscode","html_url":"https://github.com/opscode","followers_url":"https://api.github.com/users/opscode/followers","following_url":"https://api.github.com/users/opscode/following{/other_user}","gists_url":"https://api.github.com/users/opscode/gists{/gist_id}","starred_url":"https://api.github.com/users/opscode/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/opscode/subscriptions","organizations_url":"https://api.github.com/users/opscode/orgs","repos_url":"https://api.github.com/users/opscode/repos","events_url":"https://api.github.com/users/opscode/events{/privacy}","received_events_url":"https://api.github.com/users/opscode/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/opscode/opscode-omnibus","description":"Omnibus packaging for Chef Server","fork":false,"url":"https://api.github.com/repos/opscode/opscode-omnibus","forks_url":"https://api.github.com/repos/opscode/opscode-omnibus/forks","keys_url":"https://api.github.com/repos/opscode/opscode-omnibus/keys{/key_id}","collaborators_url":"https://api.github.com/repos/opscode/opscode-omnibus/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/opscode/opscode-omnibus/teams","hooks_url":"https://api.github.com/repos/opscode/opscode-omnibus/hooks","issue_events_url":"https://api.github.com/repos/opscode/opscode-omnibus/issues/events{/number}","events_url":"https://api.github.com/repos/opscode/opscode-omnibus/events","assignees_url":"https://api.github.com/repos/opscode/opscode-omnibus/assignees{/user}","branches_url":"https://api.github.com/repos/opscode/opscode-omnibus/branches{/branch}","tags_url":"https://api.github.com/repos/opscode/opscode-omnibus/tags","blobs_url":"https://api.github.com/repos/opscode/opscode-omnibus/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/opscode/opscode-omnibus/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/opscode/opscode-omnibus/git/refs{/sha}","trees_url":"https://api.github.com/repos/opscode/opscode-omnibus/git/trees{/sha}","statuses_url":"https://api.github.com/repos/opscode/opscode-omnibus/statuses/{sha}","languages_url":"https://api.github.com/repos/opscode/opscode-omnibus/languages","stargazers_url":"https://api.github.com/repos/opscode/opscode-omnibus/stargazers","contributors_url":"https://api.github.com/repos/opscode/opscode-omnibus/contributors","subscribers_url":"https://api.github.com/repos/opscode/opscode-omnibus/subscribers","subscription_url":"https://api.github.com/repos/opscode/opscode-omnibus/subscription","commits_url":"https://api.github.com/repos/opscode/opscode-omnibus/commits{/sha}","git_commits_url":"https://api.github.com/repos/opscode/opscode-omnibus/git/commits{/sha}","comments_url":"https://api.github.com/repos/opscode/opscode-omnibus/comments{/number}","issue_comment_url":"https://api.github.com/repos/opscode/opscode-omnibus/issues/comments/{number}","contents_url":"https://api.github.com/repos/opscode/opscode-omnibus/contents/{+path}","compare_url":"https://api.github.com/repos/opscode/opscode-omnibus/compare/{base}...{head}","merges_url":"https://api.github.com/repos/opscode/opscode-omnibus/merges","archive_url":"https://api.github.com/repos/opscode/opscode-omnibus/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/opscode/opscode-omnibus/downloads","issues_url":"https://api.github.com/repos/opscode/opscode-omnibus/issues{/number}","pulls_url":"https://api.github.com/repos/opscode/opscode-omnibus/pulls{/number}","milestones_url":"https://api.github.com/repos/opscode/opscode-omnibus/milestones{/number}","notifications_url":"https://api.github.com/repos/opscode/opscode-omnibus/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/opscode/opscode-omnibus/labels{/name}","releases_url":"https://api.github.com/repos/opscode/opscode-omnibus/releases{/id}","created_at":"2012-02-14T21:16:03Z","updated_at":"2015-01-14T17:58:48Z","pushed_at":"2015-01-14T18:02:29Z","git_url":"git://github.com/opscode/opscode-omnibus.git","ssh_url":"git@github.com:opscode/opscode-omnibus.git","clone_url":"https://github.com/opscode/opscode-omnibus.git","svn_url":"https://github.com/opscode/opscode-omnibus","homepage":"","size":14343,"stargazers_count":11,"watchers_count":11,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":6,"mirror_url":null,"open_issues_count":35,"forks":6,"open_issues":35,"watchers":11,"default_branch":"master"}},"base":{"label":"opscode:master","ref":"master","sha":"3c3693ff37819979581799a3e31a0f1fa2b49a11","user":{"login":"opscode","id":29740,"avatar_url":"https://avatars.githubusercontent.com/u/29740?v=3","gravatar_id":"","url":"https://api.github.com/users/opscode","html_url":"https://github.com/opscode","followers_url":"https://api.github.com/users/opscode/followers","following_url":"https://api.github.com/users/opscode/following{/other_user}","gists_url":"https://api.github.com/users/opscode/gists{/gist_id}","starred_url":"https://api.github.com/users/opscode/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/opscode/subscriptions","organizations_url":"https://api.github.com/users/opscode/orgs","repos_url":"https://api.github.com/users/opscode/repos","events_url":"https://api.github.com/users/opscode/events{/privacy}","received_events_url":"https://api.github.com/users/opscode/received_events","type":"Organization","site_admin":false},"repo":{"id":3444034,"name":"opscode-omnibus","full_name":"opscode/opscode-omnibus","owner":{"login":"opscode","id":29740,"avatar_url":"https://avatars.githubusercontent.com/u/29740?v=3","gravatar_id":"","url":"https://api.github.com/users/opscode","html_url":"https://github.com/opscode","followers_url":"https://api.github.com/users/opscode/followers","following_url":"https://api.github.com/users/opscode/following{/other_user}","gists_url":"https://api.github.com/users/opscode/gists{/gist_id}","starred_url":"https://api.github.com/users/opscode/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/opscode/subscriptions","organizations_url":"https://api.github.com/users/opscode/orgs","repos_url":"https://api.github.com/users/opscode/repos","events_url":"https://api.github.com/users/opscode/events{/privacy}","received_events_url":"https://api.github.com/users/opscode/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/opscode/opscode-omnibus","description":"Omnibus packaging for Chef Server","fork":false,"url":"https://api.github.com/repos/opscode/opscode-omnibus","forks_url":"https://api.github.com/repos/opscode/opscode-omnibus/forks","keys_url":"https://api.github.com/repos/opscode/opscode-omnibus/keys{/key_id}","collaborators_url":"https://api.github.com/repos/opscode/opscode-omnibus/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/opscode/opscode-omnibus/teams","hooks_url":"https://api.github.com/repos/opscode/opscode-omnibus/hooks","issue_events_url":"https://api.github.com/repos/opscode/opscode-omnibus/issues/events{/number}","events_url":"https://api.github.com/repos/opscode/opscode-omnibus/events","assignees_url":"https://api.github.com/repos/opscode/opscode-omnibus/assignees{/user}","branches_url":"https://api.github.com/repos/opscode/opscode-omnibus/branches{/branch}","tags_url":"https://api.github.com/repos/opscode/opscode-omnibus/tags","blobs_url":"https://api.github.com/repos/opscode/opscode-omnibus/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/opscode/opscode-omnibus/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/opscode/opscode-omnibus/git/refs{/sha}","trees_url":"https://api.github.com/repos/opscode/opscode-omnibus/git/trees{/sha}","statuses_url":"https://api.github.com/repos/opscode/opscode-omnibus/statuses/{sha}","languages_url":"https://api.github.com/repos/opscode/opscode-omnibus/languages","stargazers_url":"https://api.github.com/repos/opscode/opscode-omnibus/stargazers","contributors_url":"https://api.github.com/repos/opscode/opscode-omnibus/contributors","subscribers_url":"https://api.github.com/repos/opscode/opscode-omnibus/subscribers","subscription_url":"https://api.github.com/repos/opscode/opscode-omnibus/subscription","commits_url":"https://api.github.com/repos/opscode/opscode-omnibus/commits{/sha}","git_commits_url":"https://api.github.com/repos/opscode/opscode-omnibus/git/commits{/sha}","comments_url":"https://api.github.com/repos/opscode/opscode-omnibus/comments{/number}","issue_comment_url":"https://api.github.com/repos/opscode/opscode-omnibus/issues/comments/{number}","contents_url":"https://api.github.com/repos/opscode/opscode-omnibus/contents/{+path}","compare_url":"https://api.github.com/repos/opscode/opscode-omnibus/compare/{base}...{head}","merges_url":"https://api.github.com/repos/opscode/opscode-omnibus/merges","archive_url":"https://api.github.com/repos/opscode/opscode-omnibus/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/opscode/opscode-omnibus/downloads","issues_url":"https://api.github.com/repos/opscode/opscode-omnibus/issues{/number}","pulls_url":"https://api.github.com/repos/opscode/opscode-omnibus/pulls{/number}","milestones_url":"https://api.github.com/repos/opscode/opscode-omnibus/milestones{/number}","notifications_url":"https://api.github.com/repos/opscode/opscode-omnibus/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/opscode/opscode-omnibus/labels{/name}","releases_url":"https://api.github.com/repos/opscode/opscode-omnibus/releases{/id}","created_at":"2012-02-14T21:16:03Z","updated_at":"2015-01-14T17:58:48Z","pushed_at":"2015-01-14T18:02:29Z","git_url":"git://github.com/opscode/opscode-omnibus.git","ssh_url":"git@github.com:opscode/opscode-omnibus.git","clone_url":"https://github.com/opscode/opscode-omnibus.git","svn_url":"https://github.com/opscode/opscode-omnibus","homepage":"","size":14343,"stargazers_count":11,"watchers_count":11,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":6,"mirror_url":null,"open_issues_count":35,"forks":6,"open_issues":35,"watchers":11,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/opscode/opscode-omnibus/pulls/653"},"html":{"href":"https://github.com/opscode/opscode-omnibus/pull/653"},"issue":{"href":"https://api.github.com/repos/opscode/opscode-omnibus/issues/653"},"comments":{"href":"https://api.github.com/repos/opscode/opscode-omnibus/issues/653/comments"},"review_comments":{"href":"https://api.github.com/repos/opscode/opscode-omnibus/pulls/653/comments"},"review_comment":{"href":"https://api.github.com/repos/opscode/opscode-omnibus/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/opscode/opscode-omnibus/pulls/653/commits"},"statuses":{"href":"https://api.github.com/repos/opscode/opscode-omnibus/statuses/53750a5591676edf4b400bf48212197641f6e1c1"}}}}