-
Notifications
You must be signed in to change notification settings - Fork 189
[v7r2] HTTPs services within DIRAC #4677
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
5cff556
Initial introduction of HTTPS service
chaen dab73e8
Adapt Louis' changes and 4241
chaen d491326
some doc fixing
chaen 17462d8
TornadoService: properly load remote credentials
chaen 52533d6
TornadoBaseClient: better handling of error
chaen 483c48e
Add possibility to stream, and equivalent of TransferFile
chaen 63ff4ee
Tests: ignore test directory in finding DBs
chaen 8f96687
X509Chain.getCredentials returns a DN entry
chaen d021d5d
Tests: ignore Tornado services when doing installation in CI
chaen 30fd649
Component installer can install Tornado services
chaen c500558
Add TornadoFileCatalogHandler
chaen adcf718
Make DFC client compatible with Tornado service
chaen 449b477
Run FileCatalog tests with https
chaen de91e64
Move Tornado deprecated tests
chaen 3b8d5ef
Addapt a bit the doc
chaen bb8c03a
Implement minor review comments
chaen b2a6edf
refactor the stream
chaen 0d903b0
Implement minor review comments and py3 compatibility
chaen 62a900f
Better use of HTTPError
chaen ed821bb
Factorize
chaen 4233c58
Tornado: make service initialization thread safe
chaen 624bfac
Factorize RPCClientSelector and TransferClientSelector into ClientSel…
chaen a0230d7
Typo in comment
chaen a0012a7
Point to DIRACGrid instead of chaen repo
chaen 916ced6
Update environments.yml and requirements.txt to point to the correct …
chaen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| from __future__ import absolute_import | ||
| from __future__ import division | ||
| from __future__ import print_function | ||
|
|
||
| __RCSID__ = "$Id$" | ||
|
|
||
|
|
||
| from base64 import b64encode, b64decode | ||
|
|
||
| from DIRAC.Core.Tornado.Client.TornadoClient import TornadoClient | ||
| from DIRAC.Core.Base.Client import Client | ||
|
|
||
|
|
||
| class CSJSONClient(TornadoClient): | ||
| """ | ||
| The specific Tornado client for configuration system. | ||
| To avoid JSON limitation the HTTPS handler encode data in base64 | ||
| before sending them, this class only decode the base64 | ||
|
|
||
| An exception is made with CommitNewData which ENCODE in base64 | ||
| """ | ||
|
|
||
| def getCompressedData(self): | ||
|
chaen marked this conversation as resolved.
|
||
| """ | ||
| Transmit request to service and get data in base64, | ||
| it decode base64 before returning | ||
|
|
||
| :returns: Configuration data, compressed | ||
| :rtype: str | ||
| """ | ||
| retVal = self.executeRPC('getCompressedData') | ||
| if retVal['OK']: | ||
| retVal['Value'] = b64decode(retVal['Value']) | ||
| return retVal | ||
|
|
||
| def getCompressedDataIfNewer(self, sClientVersion): | ||
| """ | ||
| Transmit request to service and get data in base64, | ||
| it decode base64 before returning. | ||
|
|
||
| :returns: Configuration data, if changed, compressed | ||
| """ | ||
| retVal = self.executeRPC('getCompressedDataIfNewer', sClientVersion) | ||
| if retVal['OK'] and 'data' in retVal['Value']: | ||
| retVal['Value']['data'] = b64decode(retVal['Value']['data']) | ||
| return retVal | ||
|
|
||
| def commitNewData(self, sData): | ||
| """ | ||
| Transmit request to service by encoding data in base64. | ||
|
|
||
| :param sData: Data to commit, you may call this method with CSAPI and Modificator | ||
| """ | ||
| return self.executeRPC('commitNewData', b64encode(sData)) | ||
|
|
||
|
|
||
| class ConfigurationClient(Client): | ||
| """ | ||
| Specific client to speak with ConfigurationServer. | ||
|
|
||
| This class must contain at least the JSON decoder dedicated to | ||
| the Configuration Server. | ||
|
|
||
| You can implement more logic or function to the client here if needed, | ||
| RPCCall can be made inside this class with executeRPC method. | ||
| """ | ||
|
|
||
| # The JSON decoder for Configuration Server | ||
| httpsClient = CSJSONClient | ||
|
|
||
| def __init__(self, **kwargs): | ||
| # By default we use Configuration/Server as url, client do the resolution | ||
| # In some case url has to be static (when a slave register to the master server for example) | ||
| # It's why we can use 'url' as keyword arguments | ||
| if 'url' not in kwargs: | ||
| kwargs['url'] = 'Configuration/Server' | ||
| super(ConfigurationClient, self).__init__(**kwargs) | ||
148 changes: 148 additions & 0 deletions
148
ConfigurationSystem/Service/TornadoConfigurationHandler.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| """ The CS! (Configuration Service) | ||
|
|
||
| Modified to work with Tornado | ||
| Encode data in base64 because of JSON limitations | ||
| In client side you must use a specific client | ||
|
|
||
| """ | ||
|
|
||
| from __future__ import absolute_import | ||
| from __future__ import division | ||
| from __future__ import print_function | ||
|
|
||
| __RCSID__ = "$Id$" | ||
|
|
||
| from base64 import b64encode, b64decode | ||
|
|
||
| from DIRAC import S_OK, S_ERROR, gLogger | ||
| from DIRAC.ConfigurationSystem.private.ServiceInterfaceTornado import ServiceInterfaceTornado as ServiceInterface | ||
| from DIRAC.Core.Utilities import DErrno | ||
| from DIRAC.Core.Tornado.Server.TornadoService import TornadoService | ||
|
|
||
| sLog = gLogger.getSubLogger(__name__) | ||
|
|
||
|
|
||
| class TornadoConfigurationHandler(TornadoService): | ||
| """ | ||
| The CS handler | ||
| """ | ||
| ServiceInterface = None | ||
| PilotSynchronizer = None | ||
|
|
||
| @classmethod | ||
| def initializeHandler(cls, serviceInfo): | ||
| """ | ||
| Initialize the configuration server | ||
| Behind it starts thread which refresh configuration | ||
| """ | ||
| cls.ServiceInterface = ServiceInterface(serviceInfo['URL']) | ||
| return S_OK() | ||
|
|
||
| def export_getVersion(self): | ||
| """ | ||
| Returns the version of the configuration | ||
| """ | ||
| return S_OK(self.ServiceInterface.getVersion()) | ||
|
|
||
| def export_getCompressedData(self): | ||
| """ | ||
| Returns the configuration | ||
| """ | ||
| sData = self.ServiceInterface.getCompressedConfigurationData() | ||
| return S_OK(b64encode(sData)) | ||
|
|
||
| def export_getCompressedDataIfNewer(self, sClientVersion): | ||
| """ | ||
| Returns the configuration if a newer configuration exists, if not just returns the version | ||
|
|
||
| :param sClientVersion: Version used by client | ||
| """ | ||
| sVersion = self.ServiceInterface.getVersion() | ||
| retDict = {'newestVersion': sVersion} | ||
| if sClientVersion < sVersion: | ||
| retDict['data'] = b64encode(self.ServiceInterface.getCompressedConfigurationData()) | ||
| return S_OK(retDict) | ||
|
|
||
| def export_publishSlaveServer(self, sURL): | ||
| """ | ||
| Used by slave server to register as a slave server. | ||
|
|
||
| :param sURL: The url of the slave server. | ||
| """ | ||
| self.ServiceInterface.publishSlaveServer(sURL) | ||
| return S_OK() | ||
|
|
||
| def export_commitNewData(self, sData): | ||
| """ | ||
| Write the new configuration | ||
| """ | ||
| credDict = self.getRemoteCredentials() | ||
| if 'DN' not in credDict or 'username' not in credDict: | ||
| return S_ERROR("You must be authenticated!") | ||
| sData = b64decode(sData) | ||
| res = self.ServiceInterface.updateConfiguration(sData, credDict['username']) | ||
| if not res['OK']: | ||
| return res | ||
|
|
||
| # Check the flag for updating the pilot 3 JSON file | ||
| if self.srv_getCSOption('UpdatePilotCStoJSONFile', False) and self.ServiceInterface.isMaster(): | ||
| if self.PilotSynchronizer is None: | ||
| try: | ||
| # This import is only needed for the Master CS service, making it conditional avoids | ||
| # dependency on the git client preinstalled on all the servers running CS slaves | ||
| from DIRAC.WorkloadManagementSystem.Utilities.PilotCStoJSONSynchronizer import PilotCStoJSONSynchronizer | ||
| except ImportError as exc: | ||
| sLog.exception("Failed to import PilotCStoJSONSynchronizer", repr(exc)) | ||
| return S_ERROR(DErrno.EIMPERR, 'Failed to import PilotCStoJSONSynchronizer') | ||
| self.PilotSynchronizer = PilotCStoJSONSynchronizer() | ||
| return self.PilotSynchronizer.sync() | ||
|
|
||
| return res | ||
|
|
||
| def export_writeEnabled(self): | ||
| """ | ||
| Used to know if we can change the configuration on this server | ||
| """ | ||
| return S_OK(self.ServiceInterface.isMaster()) | ||
|
|
||
| def export_getCommitHistory(self, limit=100): | ||
| """ | ||
| Get the history of modifications in the configuration | ||
| """ | ||
| if limit > 100: | ||
| limit = 100 | ||
| history = self.ServiceInterface.getCommitHistory() | ||
| if limit: | ||
| history = history[:limit] | ||
| return S_OK(history) | ||
|
|
||
| def export_getVersionContents(self, versionList): | ||
| """ | ||
| Get an old version of the configuration, can also be used to get more than one version. | ||
|
|
||
| :param versionList: List of the version we are trying to get | ||
| """ | ||
| contentsList = [] | ||
| for version in versionList: | ||
| retVal = self.ServiceInterface.getVersionContents(version) | ||
| if retVal['OK']: | ||
| contentsList.append(retVal['Value']) | ||
| else: | ||
| return S_ERROR("Can't get contents for version %s: %s" % (version, retVal['Message'])) | ||
| return S_OK(contentsList) | ||
|
|
||
| def export_rollbackToVersion(self, version): | ||
| """ | ||
| Rollback to an older version of the configuration | ||
|
|
||
| :param version: The version we want to apply | ||
| """ | ||
| retVal = self.ServiceInterface.getVersionContents(version) | ||
| if not retVal['OK']: | ||
| return S_ERROR("Can't get contents for version %s: %s" % (version, retVal['Message'])) | ||
| credDict = self.getRemoteCredentials() | ||
| if 'DN' not in credDict or 'username' not in credDict: | ||
| return S_ERROR("You must be authenticated!") | ||
| return self.ServiceInterface.updateConfiguration(retVal['Value'], | ||
| credDict['username'], | ||
| updateVersionOption=True) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.