prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  SecretInVault secret provider integration tests no longer work
  The test `buildbot.test.integration.test_integration_secrets_with_vault.SecretsConfig.test_secret` no longer works.
  
  See https://travis-ci.org/buildbot/buildbot/jobs/464401540.
  
  Looks like the default kv engine shipping with the `vault` engine is now v2 which we don't support yet.
  
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `master/buildbot/secrets/providers/vault.py`
  Content:
  ```
  1 # This file is part of Buildbot.  Buildbot is free software: you can
  2 # redistribute it and/or modify it under the terms of the GNU General Public
  3 # License as published by the Free Software Foundation, version 2.
  4 #
  5 # This program is distributed in the hope that it will be useful, but WITHOUT
  6 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  7 # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
  8 # details.
  9 #
  10 # You should have received a copy of the GNU General Public License along with
  11 # this program; if not, write to the Free Software Foundation, Inc., 51
  12 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  13 #
  14 # Copyright Buildbot Team Members
  15 """
  16 vault based providers
  17 """
  18 
  19 from __future__ import absolute_import
  20 from __future__ import print_function
  21 
  22 from twisted.internet import defer
  23 
  24 from buildbot import config
  25 from buildbot.secrets.providers.base import SecretProviderBase
  26 from buildbot.util import httpclientservice
  27 
  28 
  29 class HashiCorpVaultSecretProvider(SecretProviderBase):
  30     """
  31     basic provider where each secret is stored in Vault
  32     """
  33 
  34     name = 'SecretInVault'
  35 
  36     def checkConfig(self, vaultServer=None, vaultToken=None, secretsmount=None):
  37         if not isinstance(vaultServer, str):
  38             config.error("vaultServer must be a string while it is %s" % (type(vaultServer,)))
  39         if not isinstance(vaultToken, str):
  40             config.error("vaultToken must be a string while it is %s" % (type(vaultToken,)))
  41 
  42     @defer.inlineCallbacks
  43     def reconfigService(self, vaultServer=None, vaultToken=None, secretsmount=None):
  44         if secretsmount is None:
  45             self.secretsmount = "secret"
  46         else:
  47             self.secretsmount = secretsmount
  48         self.vaultServer = vaultServer
  49         self.vaultToken = vaultToken
  50         if vaultServer.endswith('/'):
  51             vaultServer = vaultServer[:-1]
  52         self._http = yield httpclientservice.HTTPClientService.getService(
  53             self.master, self.vaultServer, headers={'X-Vault-Token': self.vaultToken})
  54 
  55     @defer.inlineCallbacks
  56     def get(self, entry):
  57         """
  58         get the value from vault secret backend
  59         """
  60         path = self.secretsmount + '/' + entry
  61         proj = yield self._http.get('/v1/{0}'.format(path))
  62         code = yield proj.code
  63         if code != 200:
  64             raise KeyError("The key %s does not exist in Vault provider: request"
  65                            " return code:%d." % (entry, code))
  66         json = yield proj.json()
  67         defer.returnValue(json.get(u'data', {}).get('value'))
  68
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/master/buildbot/secrets/providers/vault.py b/master/buildbot/secrets/providers/vault.py
  --- a/master/buildbot/secrets/providers/vault.py
  +++ b/master/buildbot/secrets/providers/vault.py
  @@ -28,25 +28,30 @@
   
   class HashiCorpVaultSecretProvider(SecretProviderBase):
       """
  -    basic provider where each secret is stored in Vault
  +    basic provider where each secret is stored in Vault KV secret engine
       """
   
       name = 'SecretInVault'
   
  -    def checkConfig(self, vaultServer=None, vaultToken=None, secretsmount=None):
  +    def checkConfig(self, vaultServer=None, vaultToken=None, secretsmount=None,
  +                    apiVersion=1):
           if not isinstance(vaultServer, str):
               config.error("vaultServer must be a string while it is %s" % (type(vaultServer,)))
           if not isinstance(vaultToken, str):
               config.error("vaultToken must be a string while it is %s" % (type(vaultToken,)))
  +        if apiVersion not in [1, 2]:
  +            config.error("apiVersion %s is not supported" % apiVersion)
   
       @defer.inlineCallbacks
  -    def reconfigService(self, vaultServer=None, vaultToken=None, secretsmount=None):
  +    def reconfigService(self, vaultServer=None, vaultToken=None, secretsmount=None,
  +                        apiVersion=1):
           if secretsmount is None:
               self.secretsmount = "secret"
           else:
               self.secretsmount = secretsmount
           self.vaultServer = vaultServer
           self.vaultToken = vaultToken
  +        self.apiVersion = apiVersion
           if vaultServer.endswith('/'):
               vaultServer = vaultServer[:-1]
           self._http = yield httpclientservice.HTTPClientService.getService(
  @@ -57,11 +62,23 @@
           """
           get the value from vault secret backend
           """
  -        path = self.secretsmount + '/' + entry
  +        if self.apiVersion == 1:
  +            path = self.secretsmount + '/' + entry
  +        else:
  +            path = self.secretsmount + '/data/' + entry
  +
  +        # note that the HTTP path contains v1 for both versions of the key-value
  +        # secret engine. Different versions of the key-value engine are
  +        # effectively separate secret engines in vault, with the same base HTTP
  +        # API, but with different paths within it.
           proj = yield self._http.get('/v1/{0}'.format(path))
           code = yield proj.code
           if code != 200:
               raise KeyError("The key %s does not exist in Vault provider: request"
                              " return code:%d." % (entry, code))
           json = yield proj.json()
  -        defer.returnValue(json.get(u'data', {}).get('value'))
  +        if self.apiVersion == 1:
  +            ret = json.get(u'data', {}).get('value')
  +        else:
  +            ret = json.get(u'data', {}).get(u'data', {}).get('value')
  +        defer.returnValue(ret)
  
verification_info: {"golden_diff": "diff --git a/master/buildbot/secrets/providers/vault.py b/master/buildbot/secrets/providers/vault.py\n--- a/master/buildbot/secrets/providers/vault.py\n+++ b/master/buildbot/secrets/providers/vault.py\n@@ -28,25 +28,30 @@\n \n class HashiCorpVaultSecretProvider(SecretProviderBase):\n     \"\"\"\n-    basic provider where each secret is stored in Vault\n+    basic provider where each secret is stored in Vault KV secret engine\n     \"\"\"\n \n     name = 'SecretInVault'\n \n-    def checkConfig(self, vaultServer=None, vaultToken=None, secretsmount=None):\n+    def checkConfig(self, vaultServer=None, vaultToken=None, secretsmount=None,\n+                    apiVersion=1):\n         if not isinstance(vaultServer, str):\n             config.error(\"vaultServer must be a string while it is %s\" % (type(vaultServer,)))\n         if not isinstance(vaultToken, str):\n             config.error(\"vaultToken must be a string while it is %s\" % (type(vaultToken,)))\n+        if apiVersion not in [1, 2]:\n+            config.error(\"apiVersion %s is not supported\" % apiVersion)\n \n     @defer.inlineCallbacks\n-    def reconfigService(self, vaultServer=None, vaultToken=None, secretsmount=None):\n+    def reconfigService(self, vaultServer=None, vaultToken=None, secretsmount=None,\n+                        apiVersion=1):\n         if secretsmount is None:\n             self.secretsmount = \"secret\"\n         else:\n             self.secretsmount = secretsmount\n         self.vaultServer = vaultServer\n         self.vaultToken = vaultToken\n+        self.apiVersion = apiVersion\n         if vaultServer.endswith('/'):\n             vaultServer = vaultServer[:-1]\n         self._http = yield httpclientservice.HTTPClientService.getService(\n@@ -57,11 +62,23 @@\n         \"\"\"\n         get the value from vault secret backend\n         \"\"\"\n-        path = self.secretsmount + '/' + entry\n+        if self.apiVersion == 1:\n+            path = self.secretsmount + '/' + entry\n+        else:\n+            path = self.secretsmount + '/data/' + entry\n+\n+        # note that the HTTP path contains v1 for both versions of the key-value\n+        # secret engine. Different versions of the key-value engine are\n+        # effectively separate secret engines in vault, with the same base HTTP\n+        # API, but with different paths within it.\n         proj = yield self._http.get('/v1/{0}'.format(path))\n         code = yield proj.code\n         if code != 200:\n             raise KeyError(\"The key %s does not exist in Vault provider: request\"\n                            \" return code:%d.\" % (entry, code))\n         json = yield proj.json()\n-        defer.returnValue(json.get(u'data', {}).get('value'))\n+        if self.apiVersion == 1:\n+            ret = json.get(u'data', {}).get('value')\n+        else:\n+            ret = json.get(u'data', {}).get(u'data', {}).get('value')\n+        defer.returnValue(ret)\n", "issue": "SecretInVault secret provider integration tests no longer work\nThe test `buildbot.test.integration.test_integration_secrets_with_vault.SecretsConfig.test_secret` no longer works.\r\n\r\nSee https://travis-ci.org/buildbot/buildbot/jobs/464401540.\r\n\r\nLooks like the default kv engine shipping with the `vault` engine is now v2 which we don't support yet.\r\n\r\n\n", "before_files": [{"content": "# This file is part of Buildbot.  Buildbot is free software: you can\n# redistribute it and/or modify it under the terms of the GNU General Public\n# License as published by the Free Software Foundation, version 2.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51\n# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n#\n# Copyright Buildbot Team Members\n\"\"\"\nvault based providers\n\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import print_function\n\nfrom twisted.internet import defer\n\nfrom buildbot import config\nfrom buildbot.secrets.providers.base import SecretProviderBase\nfrom buildbot.util import httpclientservice\n\n\nclass HashiCorpVaultSecretProvider(SecretProviderBase):\n    \"\"\"\n    basic provider where each secret is stored in Vault\n    \"\"\"\n\n    name = 'SecretInVault'\n\n    def checkConfig(self, vaultServer=None, vaultToken=None, secretsmount=None):\n        if not isinstance(vaultServer, str):\n            config.error(\"vaultServer must be a string while it is %s\" % (type(vaultServer,)))\n        if not isinstance(vaultToken, str):\n            config.error(\"vaultToken must be a string while it is %s\" % (type(vaultToken,)))\n\n    @defer.inlineCallbacks\n    def reconfigService(self, vaultServer=None, vaultToken=None, secretsmount=None):\n        if secretsmount is None:\n            self.secretsmount = \"secret\"\n        else:\n            self.secretsmount = secretsmount\n        self.vaultServer = vaultServer\n        self.vaultToken = vaultToken\n        if vaultServer.endswith('/'):\n            vaultServer = vaultServer[:-1]\n        self._http = yield httpclientservice.HTTPClientService.getService(\n            self.master, self.vaultServer, headers={'X-Vault-Token': self.vaultToken})\n\n    @defer.inlineCallbacks\n    def get(self, entry):\n        \"\"\"\n        get the value from vault secret backend\n        \"\"\"\n        path = self.secretsmount + '/' + entry\n        proj = yield self._http.get('/v1/{0}'.format(path))\n        code = yield proj.code\n        if code != 200:\n            raise KeyError(\"The key %s does not exist in Vault provider: request\"\n                           \" return code:%d.\" % (entry, code))\n        json = yield proj.json()\n        defer.returnValue(json.get(u'data', {}).get('value'))\n", "path": "master/buildbot/secrets/providers/vault.py"}], "after_files": [{"content": "# This file is part of Buildbot.  Buildbot is free software: you can\n# redistribute it and/or modify it under the terms of the GNU General Public\n# License as published by the Free Software Foundation, version 2.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51\n# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n#\n# Copyright Buildbot Team Members\n\"\"\"\nvault based providers\n\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import print_function\n\nfrom twisted.internet import defer\n\nfrom buildbot import config\nfrom buildbot.secrets.providers.base import SecretProviderBase\nfrom buildbot.util import httpclientservice\n\n\nclass HashiCorpVaultSecretProvider(SecretProviderBase):\n    \"\"\"\n    basic provider where each secret is stored in Vault KV secret engine\n    \"\"\"\n\n    name = 'SecretInVault'\n\n    def checkConfig(self, vaultServer=None, vaultToken=None, secretsmount=None,\n                    apiVersion=1):\n        if not isinstance(vaultServer, str):\n            config.error(\"vaultServer must be a string while it is %s\" % (type(vaultServer,)))\n        if not isinstance(vaultToken, str):\n            config.error(\"vaultToken must be a string while it is %s\" % (type(vaultToken,)))\n        if apiVersion not in [1, 2]:\n            config.error(\"apiVersion %s is not supported\" % apiVersion)\n\n    @defer.inlineCallbacks\n    def reconfigService(self, vaultServer=None, vaultToken=None, secretsmount=None,\n                        apiVersion=1):\n        if secretsmount is None:\n            self.secretsmount = \"secret\"\n        else:\n            self.secretsmount = secretsmount\n        self.vaultServer = vaultServer\n        self.vaultToken = vaultToken\n        self.apiVersion = apiVersion\n        if vaultServer.endswith('/'):\n            vaultServer = vaultServer[:-1]\n        self._http = yield httpclientservice.HTTPClientService.getService(\n            self.master, self.vaultServer, headers={'X-Vault-Token': self.vaultToken})\n\n    @defer.inlineCallbacks\n    def get(self, entry):\n        \"\"\"\n        get the value from vault secret backend\n        \"\"\"\n        if self.apiVersion == 1:\n            path = self.secretsmount + '/' + entry\n        else:\n            path = self.secretsmount + '/data/' + entry\n\n        # note that the HTTP path contains v1 for both versions of the key-value\n        # secret engine. Different versions of the key-value engine are\n        # effectively separate secret engines in vault, with the same base HTTP\n        # API, but with different paths within it.\n        proj = yield self._http.get('/v1/{0}'.format(path))\n        code = yield proj.code\n        if code != 200:\n            raise KeyError(\"The key %s does not exist in Vault provider: request\"\n                           \" return code:%d.\" % (entry, code))\n        json = yield proj.json()\n        if self.apiVersion == 1:\n            ret = json.get(u'data', {}).get('value')\n        else:\n            ret = json.get(u'data', {}).get(u'data', {}).get('value')\n        defer.returnValue(ret)\n", "path": "master/buildbot/secrets/providers/vault.py"}]}