{"action":"created","comment":{"url":"https://api.github.com/repos/zendesk/samson/pulls/comments/83330839","pull_request_review_id":3988484,"id":83330839,"diff_hunk":"@@ -23,121 +15,83 @@ class VaultClient < Vault::Client\n         read_timeout: 2\n       }.freeze\n \n-      def self.auth_token(vault_server)\n-        uri = URI.parse(vault_server)\n-        @http = Net::HTTP.start(uri.host, uri.port, DEFAULT_CLIENT_OPTIONS)\n-        response = @http.request(Net::HTTP::Post.new(CERT_AUTH_PATH))\n-        if response.code == \"200\"\n-          JSON.parse(response.body).fetch(\"auth\")[\"client_token\"]\n-        else\n-          raise \"Failed to get auth token from vault server\"\n-        end\n-      end\n-\n       def self.client\n         @client ||= new\n       end\n \n       def initialize\n-        return if Rails.env.test?\n-        VaultClient.ensure_config_exists\n-        # since we have a bunch of servers, don't use the client singlton to\n-        # talk to them\n-        # as we configure each client, check to see if the config has a token in it,\n-        # if it does, then we don't need to worry about going and getting one\n         @vaults = {}\n-        vault_hosts.each do |vault_server|\n-          instance = vault_server.fetch(\"vault_instance\")\n-          if vault_server[\"vault_token\"].present?\n-            tls_verification = vault_server.fetch(\"tls_verify\")\n-            @vaults[instance] = Vault::Client.new(DEFAULT_CLIENT_OPTIONS.merge(ssl_verify: tls_verification))\n-            @vaults[instance].token = File.read(vault_server.fetch(\"vault_token\")).strip\n-          else\n-            pemfile = File.read(vault_server.fetch(\"vault_auth_pem\"))\n-            client_cert_options = {\n-              cert: OpenSSL::X509::Certificate.new(pemfile),\n-              key: OpenSSL::PKey::RSA.new(pemfile),\n-              ssl_pem_file: vault_server.fetch(\"vault_auth_pem\"),\n-              verify_mode: vault_server.fetch(\"tls_verify\")\n-            }\n-            writer = Vault::Client.new(DEFAULT_CLIENT_OPTIONS.merge(client_cert_options))\n-            writer.token = VaultClient.auth_token(vault_server[\"vault_address\"])\n-          end\n-          @vaults[instance].address = vault_server.fetch(\"vault_address\")\n-          @vaults[instance].ssl_ca_cert = vault_server.fetch(\"ca_cert\") if vault_server.fetch(\"ca_cert\", false)\n+        VaultServer.all.each do |vault_server|\n+          @vaults[vault_server.name] = Vault::Client.new(\n+            DEFAULT_CLIENT_OPTIONS.merge(\n+              ssl_verify: vault_server.tls_verify,\n+              token: vault_server.token,\n+              address: vault_server.address,\n+              ssl_cert_store: vault_server.cert_store\n+            )\n+          )\n         end\n       end\n \n-      # normally we'll only get a single instance back.  if it's global, just read it once\n+      # all servers should have the same data, so read from the first\n       def read(key)\n-        vault_instances(key).first.logical.read(key)\n+        vault = clients(key).first\n+        with_retries { vault.logical.read(key) }\n       end\n \n+      # different servers have different keys so combine all\n       def list(path)\n-        @vaults.each_value.flat_map { |vault| vault.logical.list(path) }\n+        all = @vaults.each_value.flat_map do |vault|\n+          with_retries { vault.logical.list(path) }\n+        end\n+        all.uniq!\n+        all\n       end\n \n-      # make darn sure on deletes and writes that we try a couple of times.\n-      # not going to catch any other excpeptions as we want this to blow up\n-      # if anything fails\n+      # write to all servers that need this key\n       def write(key, data)\n-        Vault.with_retries(Vault::HTTPConnectionError, attempts: 5) do\n-          vault_instances(key).each { |v| v.logical.write(key, data) }\n+        clients(key).each do |v|\n+          with_retries { v.logical.write(key, data) }\n         end\n       end\n \n+      # delete from all servers that hold this key\n       def delete(key)\n-        Vault.with_retries(Vault::HTTPConnectionError, attempts: 5) do\n-          vault_instances(key).each { |v| v.logical.delete(key) }\n+        clients(key).each do |v|\n+          with_retries { v.logical.delete(key) }\n         end\n       end\n \n-      def self.available_instances\n-        VaultClient.ensure_config_exists\n-        JSON.parse(File.read(VaultClient.vault_config_file)).map { |v| v[\"vault_instance\"] }\n-      end\n-\n-      def config_for(instance_name)\n-        vault_hosts.detect { |vault| vault[\"vault_instance\"] == instance_name }\n-      end\n-\n-      def self.ensure_config_exists\n-        unless File.exist?(VaultClient.vault_config_file)\n-          raise \"VAULT_CONFIG_FILE or config/vault.json is required for #{ENV[\"SECRET_STORAGE_BACKEND\"]}\"\n+      def client(deploy_group)\n+        unless name = deploy_group.vault_instance.presence\n+          raise \"deploy group #{deploy_group.permalink} has no vault_instance configured\"\n         end\n-      end\n-\n-      def self.vault_config_file\n-        ENV['VAULT_CONFIG_FILE'] || Rails.root.join(\"config/vault.json\")\n+        unless client = @vaults[name]\n+          raise \"no vault server found with name #{name}\"\n+        end\n+        client\n       end\n \n       private\n \n-      def vault_hosts\n-        @vault_hosts ||= JSON.parse(File.read(VaultClient.vault_config_file))\n+      def with_retries(&block)\n+        Vault.with_retries(Vault::HTTPConnectionError, attempts: 3, &block)\n       end\n \n-      # get back the vault instance that's required for this pod.  we'll take\n-      # the key, and look up the instance or return all of 'em if it's a global key\n-      def vault_instances(key)\n-        # parse_secret_key doesn't know about the namespace etc, so strip it off\n-        deploy_group = SecretStorage.parse_secret_key(key.split('/', 3).last).fetch(:deploy_group_permalink)\n-        if deploy_group == 'global'\n-          @vaults.map(&:pop)\n+      # local server for deploy-group specific key and all for global key\n+      def clients(key)","path":"lib/samson/secrets/vault_client.rb","position":160,"original_position":160,"commit_id":"b3507ed2fd6db78fc9886331693e6f27fb6ad6d7","original_commit_id":"b3507ed2fd6db78fc9886331693e6f27fb6ad6d7","user":{"login":"jonmoter","id":1056506,"avatar_url":"https://avatars.githubusercontent.com/u/1056506?v=3","gravatar_id":"","url":"https://api.github.com/users/jonmoter","html_url":"https://github.com/jonmoter","followers_url":"https://api.github.com/users/jonmoter/followers","following_url":"https://api.github.com/users/jonmoter/following{/other_user}","gists_url":"https://api.github.com/users/jonmoter/gists{/gist_id}","starred_url":"https://api.github.com/users/jonmoter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jonmoter/subscriptions","organizations_url":"https://api.github.com/users/jonmoter/orgs","repos_url":"https://api.github.com/users/jonmoter/repos","events_url":"https://api.github.com/users/jonmoter/events{/privacy}","received_events_url":"https://api.github.com/users/jonmoter/received_events","type":"User","site_admin":false},"body":"Having both `client` and `clients` methods that look up different ways is a little confusing.","created_at":"2016-10-13T23:00:54Z","updated_at":"2016-10-13T23:04:00Z","html_url":"https://github.com/zendesk/samson/pull/1364#discussion_r83330839","pull_request_url":"https://api.github.com/repos/zendesk/samson/pulls/1364","_links":{"self":{"href":"https://api.github.com/repos/zendesk/samson/pulls/comments/83330839"},"html":{"href":"https://github.com/zendesk/samson/pull/1364#discussion_r83330839"},"pull_request":{"href":"https://api.github.com/repos/zendesk/samson/pulls/1364"}}},"pull_request":{"url":"https://api.github.com/repos/zendesk/samson/pulls/1364","id":89100564,"html_url":"https://github.com/zendesk/samson/pull/1364","diff_url":"https://github.com/zendesk/samson/pull/1364.diff","patch_url":"https://github.com/zendesk/samson/pull/1364.patch","issue_url":"https://api.github.com/repos/zendesk/samson/issues/1364","number":1364,"state":"open","locked":false,"title":"PAAS-451 read vault servers from the db","user":{"login":"grosser","id":11367,"avatar_url":"https://avatars.githubusercontent.com/u/11367?v=3","gravatar_id":"","url":"https://api.github.com/users/grosser","html_url":"https://github.com/grosser","followers_url":"https://api.github.com/users/grosser/followers","following_url":"https://api.github.com/users/grosser/following{/other_user}","gists_url":"https://api.github.com/users/grosser/gists{/gist_id}","starred_url":"https://api.github.com/users/grosser/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/grosser/subscriptions","organizations_url":"https://api.github.com/users/grosser/orgs","repos_url":"https://api.github.com/users/grosser/repos","events_url":"https://api.github.com/users/grosser/events{/privacy}","received_events_url":"https://api.github.com/users/grosser/received_events","type":"User","site_admin":false},"body":" - bump vault-ruby to get `ssl_cert_store` patch\r\n - add UI to edit vault servers and view history\r\n - encrypted vault token + papertrail\r\n - make all vault calls retry on error\r\n - remove mock vault client and work with the real implementation\r\n - test real vault client\r\n - remove `vault server 0.6.2` patch ... was not needed and not working anyway after https://github.com/zendesk/samson/pull/1359\r\n\r\n... after merge (to keep this PR manageable) solve TODOs ... migrate to vault_server_id instead of string column","created_at":"2016-10-12T21:49:16Z","updated_at":"2016-10-13T23:04:00Z","closed_at":null,"merged_at":null,"merge_commit_sha":"871c4b4ba20afe594701e34c8eb2362fad66d048","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/zendesk/samson/pulls/1364/commits","review_comments_url":"https://api.github.com/repos/zendesk/samson/pulls/1364/comments","review_comment_url":"https://api.github.com/repos/zendesk/samson/pulls/comments{/number}","comments_url":"https://api.github.com/repos/zendesk/samson/issues/1364/comments","statuses_url":"https://api.github.com/repos/zendesk/samson/statuses/b3507ed2fd6db78fc9886331693e6f27fb6ad6d7","head":{"label":"zendesk:grosser/vault-instance","ref":"grosser/vault-instance","sha":"b3507ed2fd6db78fc9886331693e6f27fb6ad6d7","user":{"login":"zendesk","id":11525,"avatar_url":"https://avatars.githubusercontent.com/u/11525?v=3","gravatar_id":"","url":"https://api.github.com/users/zendesk","html_url":"https://github.com/zendesk","followers_url":"https://api.github.com/users/zendesk/followers","following_url":"https://api.github.com/users/zendesk/following{/other_user}","gists_url":"https://api.github.com/users/zendesk/gists{/gist_id}","starred_url":"https://api.github.com/users/zendesk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zendesk/subscriptions","organizations_url":"https://api.github.com/users/zendesk/orgs","repos_url":"https://api.github.com/users/zendesk/repos","events_url":"https://api.github.com/users/zendesk/events{/privacy}","received_events_url":"https://api.github.com/users/zendesk/received_events","type":"Organization","site_admin":false},"repo":{"id":20208662,"name":"samson","full_name":"zendesk/samson","owner":{"login":"zendesk","id":11525,"avatar_url":"https://avatars.githubusercontent.com/u/11525?v=3","gravatar_id":"","url":"https://api.github.com/users/zendesk","html_url":"https://github.com/zendesk","followers_url":"https://api.github.com/users/zendesk/followers","following_url":"https://api.github.com/users/zendesk/following{/other_user}","gists_url":"https://api.github.com/users/zendesk/gists{/gist_id}","starred_url":"https://api.github.com/users/zendesk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zendesk/subscriptions","organizations_url":"https://api.github.com/users/zendesk/orgs","repos_url":"https://api.github.com/users/zendesk/repos","events_url":"https://api.github.com/users/zendesk/events{/privacy}","received_events_url":"https://api.github.com/users/zendesk/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/zendesk/samson","description":"Web interface for deployments","fork":false,"url":"https://api.github.com/repos/zendesk/samson","forks_url":"https://api.github.com/repos/zendesk/samson/forks","keys_url":"https://api.github.com/repos/zendesk/samson/keys{/key_id}","collaborators_url":"https://api.github.com/repos/zendesk/samson/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/zendesk/samson/teams","hooks_url":"https://api.github.com/repos/zendesk/samson/hooks","issue_events_url":"https://api.github.com/repos/zendesk/samson/issues/events{/number}","events_url":"https://api.github.com/repos/zendesk/samson/events","assignees_url":"https://api.github.com/repos/zendesk/samson/assignees{/user}","branches_url":"https://api.github.com/repos/zendesk/samson/branches{/branch}","tags_url":"https://api.github.com/repos/zendesk/samson/tags","blobs_url":"https://api.github.com/repos/zendesk/samson/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/zendesk/samson/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/zendesk/samson/git/refs{/sha}","trees_url":"https://api.github.com/repos/zendesk/samson/git/trees{/sha}","statuses_url":"https://api.github.com/repos/zendesk/samson/statuses/{sha}","languages_url":"https://api.github.com/repos/zendesk/samson/languages","stargazers_url":"https://api.github.com/repos/zendesk/samson/stargazers","contributors_url":"https://api.github.com/repos/zendesk/samson/contributors","subscribers_url":"https://api.github.com/repos/zendesk/samson/subscribers","subscription_url":"https://api.github.com/repos/zendesk/samson/subscription","commits_url":"https://api.github.com/repos/zendesk/samson/commits{/sha}","git_commits_url":"https://api.github.com/repos/zendesk/samson/git/commits{/sha}","comments_url":"https://api.github.com/repos/zendesk/samson/comments{/number}","issue_comment_url":"https://api.github.com/repos/zendesk/samson/issues/comments{/number}","contents_url":"https://api.github.com/repos/zendesk/samson/contents/{+path}","compare_url":"https://api.github.com/repos/zendesk/samson/compare/{base}...{head}","merges_url":"https://api.github.com/repos/zendesk/samson/merges","archive_url":"https://api.github.com/repos/zendesk/samson/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/zendesk/samson/downloads","issues_url":"https://api.github.com/repos/zendesk/samson/issues{/number}","pulls_url":"https://api.github.com/repos/zendesk/samson/pulls{/number}","milestones_url":"https://api.github.com/repos/zendesk/samson/milestones{/number}","notifications_url":"https://api.github.com/repos/zendesk/samson/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/zendesk/samson/labels{/name}","releases_url":"https://api.github.com/repos/zendesk/samson/releases{/id}","deployments_url":"https://api.github.com/repos/zendesk/samson/deployments","created_at":"2014-05-27T05:55:57Z","updated_at":"2016-10-13T17:12:53Z","pushed_at":"2016-10-13T21:59:45Z","git_url":"git://github.com/zendesk/samson.git","ssh_url":"git@github.com:zendesk/samson.git","clone_url":"https://github.com/zendesk/samson.git","svn_url":"https://github.com/zendesk/samson","homepage":null,"size":212074,"stargazers_count":765,"watchers_count":765,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":150,"mirror_url":null,"open_issues_count":54,"forks":150,"open_issues":54,"watchers":765,"default_branch":"master"}},"base":{"label":"zendesk:master","ref":"master","sha":"3ba9b3091086c234227c5d7c260b1bb7d6367935","user":{"login":"zendesk","id":11525,"avatar_url":"https://avatars.githubusercontent.com/u/11525?v=3","gravatar_id":"","url":"https://api.github.com/users/zendesk","html_url":"https://github.com/zendesk","followers_url":"https://api.github.com/users/zendesk/followers","following_url":"https://api.github.com/users/zendesk/following{/other_user}","gists_url":"https://api.github.com/users/zendesk/gists{/gist_id}","starred_url":"https://api.github.com/users/zendesk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zendesk/subscriptions","organizations_url":"https://api.github.com/users/zendesk/orgs","repos_url":"https://api.github.com/users/zendesk/repos","events_url":"https://api.github.com/users/zendesk/events{/privacy}","received_events_url":"https://api.github.com/users/zendesk/received_events","type":"Organization","site_admin":false},"repo":{"id":20208662,"name":"samson","full_name":"zendesk/samson","owner":{"login":"zendesk","id":11525,"avatar_url":"https://avatars.githubusercontent.com/u/11525?v=3","gravatar_id":"","url":"https://api.github.com/users/zendesk","html_url":"https://github.com/zendesk","followers_url":"https://api.github.com/users/zendesk/followers","following_url":"https://api.github.com/users/zendesk/following{/other_user}","gists_url":"https://api.github.com/users/zendesk/gists{/gist_id}","starred_url":"https://api.github.com/users/zendesk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zendesk/subscriptions","organizations_url":"https://api.github.com/users/zendesk/orgs","repos_url":"https://api.github.com/users/zendesk/repos","events_url":"https://api.github.com/users/zendesk/events{/privacy}","received_events_url":"https://api.github.com/users/zendesk/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/zendesk/samson","description":"Web interface for deployments","fork":false,"url":"https://api.github.com/repos/zendesk/samson","forks_url":"https://api.github.com/repos/zendesk/samson/forks","keys_url":"https://api.github.com/repos/zendesk/samson/keys{/key_id}","collaborators_url":"https://api.github.com/repos/zendesk/samson/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/zendesk/samson/teams","hooks_url":"https://api.github.com/repos/zendesk/samson/hooks","issue_events_url":"https://api.github.com/repos/zendesk/samson/issues/events{/number}","events_url":"https://api.github.com/repos/zendesk/samson/events","assignees_url":"https://api.github.com/repos/zendesk/samson/assignees{/user}","branches_url":"https://api.github.com/repos/zendesk/samson/branches{/branch}","tags_url":"https://api.github.com/repos/zendesk/samson/tags","blobs_url":"https://api.github.com/repos/zendesk/samson/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/zendesk/samson/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/zendesk/samson/git/refs{/sha}","trees_url":"https://api.github.com/repos/zendesk/samson/git/trees{/sha}","statuses_url":"https://api.github.com/repos/zendesk/samson/statuses/{sha}","languages_url":"https://api.github.com/repos/zendesk/samson/languages","stargazers_url":"https://api.github.com/repos/zendesk/samson/stargazers","contributors_url":"https://api.github.com/repos/zendesk/samson/contributors","subscribers_url":"https://api.github.com/repos/zendesk/samson/subscribers","subscription_url":"https://api.github.com/repos/zendesk/samson/subscription","commits_url":"https://api.github.com/repos/zendesk/samson/commits{/sha}","git_commits_url":"https://api.github.com/repos/zendesk/samson/git/commits{/sha}","comments_url":"https://api.github.com/repos/zendesk/samson/comments{/number}","issue_comment_url":"https://api.github.com/repos/zendesk/samson/issues/comments{/number}","contents_url":"https://api.github.com/repos/zendesk/samson/contents/{+path}","compare_url":"https://api.github.com/repos/zendesk/samson/compare/{base}...{head}","merges_url":"https://api.github.com/repos/zendesk/samson/merges","archive_url":"https://api.github.com/repos/zendesk/samson/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/zendesk/samson/downloads","issues_url":"https://api.github.com/repos/zendesk/samson/issues{/number}","pulls_url":"https://api.github.com/repos/zendesk/samson/pulls{/number}","milestones_url":"https://api.github.com/repos/zendesk/samson/milestones{/number}","notifications_url":"https://api.github.com/repos/zendesk/samson/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/zendesk/samson/labels{/name}","releases_url":"https://api.github.com/repos/zendesk/samson/releases{/id}","deployments_url":"https://api.github.com/repos/zendesk/samson/deployments","created_at":"2014-05-27T05:55:57Z","updated_at":"2016-10-13T17:12:53Z","pushed_at":"2016-10-13T21:59:45Z","git_url":"git://github.com/zendesk/samson.git","ssh_url":"git@github.com:zendesk/samson.git","clone_url":"https://github.com/zendesk/samson.git","svn_url":"https://github.com/zendesk/samson","homepage":null,"size":212074,"stargazers_count":765,"watchers_count":765,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":150,"mirror_url":null,"open_issues_count":54,"forks":150,"open_issues":54,"watchers":765,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/zendesk/samson/pulls/1364"},"html":{"href":"https://github.com/zendesk/samson/pull/1364"},"issue":{"href":"https://api.github.com/repos/zendesk/samson/issues/1364"},"comments":{"href":"https://api.github.com/repos/zendesk/samson/issues/1364/comments"},"review_comments":{"href":"https://api.github.com/repos/zendesk/samson/pulls/1364/comments"},"review_comment":{"href":"https://api.github.com/repos/zendesk/samson/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/zendesk/samson/pulls/1364/commits"},"statuses":{"href":"https://api.github.com/repos/zendesk/samson/statuses/b3507ed2fd6db78fc9886331693e6f27fb6ad6d7"}}}}
{"actor":{"display_login":"jonmoter"}}