PageRenderTime 59ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/support/pushsources/photonpublish.py

https://gitlab.com/unofficial-mirrors/photon
Python | 297 lines | 223 code | 45 blank | 29 comment | 31 complexity | 6f185b39d971e4ae2109bb6360003e5a MD5 | raw file
  1. #! /usr/bin/python2
  2. #
  3. # Copyright (C) 2015 VMware, Inc. All rights reserved.
  4. # photonpublish.py
  5. # Allows pushing rpms and other artifacts to
  6. # a bintray repository.
  7. # Allows queying a repository to get existing
  8. # file details.
  9. #
  10. # Author(s): Priyesh Padmavilasom
  11. #
  12. import sys
  13. import getopt
  14. import json
  15. import glob
  16. import os
  17. import hashlib
  18. import requests
  19. from requests.auth import HTTPBasicAuth
  20. from publishutils import publishUtils
  21. from publishconst import publishConst
  22. const = publishConst()
  23. class photonPublish:
  24. def __init__(self, context):
  25. self._context = context
  26. self._config = {}
  27. self.loadConfig()
  28. def loadConfig(self):
  29. confFile = self._context['config']
  30. if(len(confFile) == 0):
  31. return
  32. with open(confFile) as jsonFile:
  33. self._config = json.load(jsonFile)
  34. #override cmdline supplied params
  35. if('user' in self._context and len(self._context['user']) > 0):
  36. self._config['user'] = self._context['user']
  37. if('apikey' in self._context and len(self._context['apikey']) > 0):
  38. self._config['apikey'] = self._context['apikey']
  39. #get details of existing files in remote
  40. def getPackages(self):
  41. auth = HTTPBasicAuth(self._config['user'], self._config['apikey'])
  42. #form url: https://api.com/content/vmware/photon/releases/0.9 for eg.
  43. url = '%s/packages/%s/%s/%s/versions/%s/files?include_unpublished=1' % \
  44. (self._config['baseurl'],\
  45. self._config['subject'],\
  46. self._config['repo'],\
  47. self._config['package'],\
  48. self._config['version'])
  49. req = requests.get(url, auth=auth)
  50. if(req.status_code >= 300):
  51. raise Exception(req.text)
  52. return req.json()
  53. #return list of unpublished content
  54. #works with details from config file
  55. def getUnpublished(self):
  56. result = []
  57. pkgs = self.getPackages()
  58. for pkg in pkgs:
  59. if not pkg[const.published]:
  60. result.append(pkg)
  61. return result
  62. #Check if the local path has any diffs with the
  63. #remote repo. Compare sha1 hash
  64. def check(self, pkgsRoot):
  65. result = {
  66. const.updates:[],
  67. const.new:[],
  68. const.obsoletes:[],
  69. const.verified:[]
  70. }
  71. localFiles = publishUtils.getFilesWithRelativePath(pkgsRoot)
  72. pkgs = self.getPackages()
  73. for pkg in pkgs:
  74. remotePath = pkg[const.path]
  75. if(remotePath in localFiles):
  76. localFiles.remove(remotePath)
  77. localPath = os.path.join(pkgsRoot, remotePath)
  78. if(os.path.isfile(localPath)):
  79. sha1 = publishUtils.sha1OfFile(localPath)
  80. if(sha1 == pkg[const.sha1]):
  81. result[const.verified].append(pkg)
  82. else:
  83. result[const.updates].append(pkg)
  84. else:
  85. result[const.obsoletes].append(pkg)
  86. for newFile in localFiles:
  87. result[const.new].append({const.path:newFile})
  88. return result
  89. #exec results from a check against remote
  90. #this will make remote and local in sync
  91. def syncRemote(self, root, checkResults):
  92. updateCount = len(checkResults[const.updates])
  93. newCount = len(checkResults[const.new])
  94. if(updateCount + newCount == 0):
  95. print 'Remote is up to date.'
  96. return
  97. #push updates
  98. print 'Updating %d files' % updateCount
  99. for new in checkResults[const.updates]:
  100. filePath = new[const.path]
  101. fileName = os.path.basename(filePath)
  102. fullPath = os.path.join(root, filePath)
  103. pathName = filePath.rstrip(fileName).rstrip('/')
  104. self.updateFile(fullPath, pathName)
  105. #push new files
  106. print 'Pushing %d new files' % newCount
  107. for new in checkResults[const.new]:
  108. filePath = new[const.path]
  109. fileName = os.path.basename(filePath)
  110. fullPath = os.path.join(root, filePath)
  111. pathName = filePath.rstrip(fileName).rstrip('/')
  112. self.pushFile(fullPath, pathName)
  113. #push a folder with rpms to a specified pathname
  114. #in the remote repo
  115. def push(self, filePath, pathName):
  116. results = []
  117. filesToPush = glob.glob(filePath)
  118. for fileToPush in filesToPush:
  119. result = self.pushFile(fileToPush, pathName)
  120. print result
  121. results.append(result)
  122. def pushFile(self, fileToPush, pathName):
  123. print 'Pushing file %s to path %s' % (fileToPush, pathName)
  124. result = {}
  125. auth = HTTPBasicAuth(self._config['user'], self._config['apikey'])
  126. fileName = os.path.basename(fileToPush)
  127. if(len(pathName) > 0):
  128. pathAndFileName = '%s/%s' % (pathName, fileName)
  129. else:
  130. pathAndFileName = fileName
  131. #form url: https://api.com/content/vmware/photon/releases/0.9 for eg.
  132. url = '%s/content/%s/%s/%s/%s/%s' % \
  133. (self._config['baseurl'],\
  134. self._config['subject'],\
  135. self._config['repo'],\
  136. self._config['package'],\
  137. self._config['version'],\
  138. pathAndFileName)
  139. headers={'Content-Type': 'application/octet-stream'}
  140. with open(fileToPush, 'rb') as chunkedData:
  141. req = requests.put(url,
  142. auth=auth,
  143. data=chunkedData,
  144. headers=headers)
  145. if(req.status_code >= 300):
  146. raise Exception(req.text)
  147. result['destPath'] = pathAndFileName
  148. result['sourcePath'] = fileToPush
  149. result['returnCode'] = req.status_code
  150. result['msg'] = req.text
  151. return result
  152. def updateFile(self, fileToPush, pathName):
  153. print 'Updating file %s at path %s' % (fileToPush, pathName)
  154. result = {}
  155. auth = HTTPBasicAuth(self._config['user'], self._config['apikey'])
  156. fileName = os.path.basename(fileToPush)
  157. if(len(pathName) > 0):
  158. pathAndFileName = '%s/%s' % (pathName, fileName)
  159. else:
  160. pathAndFileName = fileName
  161. #form url: https://api.com/content/vmware/photon/releases/0.9 for eg.
  162. url = '%s/content/%s/%s/%s/%s/%s?override=1' % \
  163. (self._config['baseurl'],\
  164. self._config['subject'],\
  165. self._config['repo'],\
  166. self._config['package'],\
  167. self._config['version'],\
  168. pathAndFileName)
  169. headers={'Content-Type': 'application/octet-stream'}
  170. with open(fileToPush, 'rb') as chunkedData:
  171. req = requests.put(url,
  172. auth=auth,
  173. data=chunkedData,
  174. headers=headers)
  175. if(req.status_code >= 300):
  176. raise Exception(req.text)
  177. result['destPath'] = pathAndFileName
  178. result['sourcePath'] = fileToPush
  179. result['returnCode'] = req.status_code
  180. result['msg'] = req.text
  181. return result
  182. #publishes pending content. Works with details from conf file.
  183. def publish(self):
  184. print 'Publishing pending files to %s/%s/%s' \
  185. % (self._config['repo'],
  186. self._config['package'],
  187. self._config['version'])
  188. result = {}
  189. auth = HTTPBasicAuth(self._config['user'], self._config['apikey'])
  190. #form url: https://api.com/content/vmware/photon/releases/0.9 for eg.
  191. url = '%s/content/%s/%s/%s/%s/publish' % \
  192. (self._config['baseurl'],\
  193. self._config['subject'],\
  194. self._config['repo'],\
  195. self._config['package'],\
  196. self._config['version'])
  197. req = requests.post(url, auth=auth)
  198. if(req.status_code >= 300):
  199. raise Exception(req.text)
  200. return req.json()
  201. def showUsage():
  202. print 'photonpublish.py --files /rpms/*.rpm'
  203. print 'if you need to override config, --config /conf.conf'
  204. print 'if you need to override user/apikey, provide'
  205. print '--user username --apikey apikey'
  206. def validate(context):
  207. return len(context['config']) > 0 and len(context['files']) > 0
  208. #test photonPublish
  209. def main(argv):
  210. try:
  211. context = {
  212. 'config':'/etc/photonpublish.conf',
  213. 'user':'',
  214. 'apikey':'',
  215. 'files':'',
  216. 'path':'',
  217. 'mode':''
  218. }
  219. opts, args = getopt.getopt(
  220. sys.argv[1:],
  221. '',
  222. ['config=','user=','apikey=','files=','path=','mode='])
  223. for opt, arg in opts:
  224. if opt == '--config':
  225. context['config'] = arg
  226. elif opt == '--user':
  227. context['user'] = arg
  228. elif opt == '--apikey':
  229. context['apikey'] = arg
  230. elif opt == '--files':
  231. context['files'] = arg
  232. elif opt == '--path':
  233. context['path'] = arg
  234. elif opt == '--mode':
  235. context['mode'] = arg
  236. if(not validate(context)):
  237. showUsage()
  238. sys.exit(1)
  239. publish = photonPublish(context)
  240. if(context['mode'] == 'upload'):
  241. publish.push(context['files'], context['path'])
  242. elif(context['mode'] == 'check'):
  243. print publish.check(context['files'])
  244. except Exception, e:
  245. print "Error: %s" % e
  246. sys.exit(1)
  247. if __name__ == "__main__":
  248. main(sys.argv)