blob: 0e53b6f4e5a946918d42f08fdb6e36b9c7f84d85 [file] [log] [blame]
Alexander Evseev81494492017-09-14 20:42:08 +03001# -*- coding: utf-8 -*-
2'''
3Module for configuring Artifactory.
4===================================
5'''
6
7import json
8import logging
9import requests
Oleh Hryhorovac106192018-01-29 12:24:15 +020010import os
Alexander Evseev81494492017-09-14 20:42:08 +030011
12from collections import OrderedDict
13
14from lxml import etree
15from lxml import objectify
16
17
18log = logging.getLogger(__name__)
19
20
21def _api_call(endpoint, data=None, headers=None, method='GET',
22 **connection_args):
23
Oleh Hryhorovac106192018-01-29 12:24:15 +020024 if endpoint.startswith('/api') == False:
25 endpoint = '/api' + endpoint
26
27 return _rest_call(endpoint=endpoint,
28 data=data,
29 headers=headers,
30 method=method,
31 **connection_args)
32
33def _rest_call(endpoint, data=None, headers=None, method='GET',
34 **connection_args):
35
Alexander Evseev81494492017-09-14 20:42:08 +030036 log.debug('Got connection args: {}'.format(connection_args))
37
38 # Set default values if empty
39 if 'proto' not in connection_args:
40 connection_args['proto'] = 'http'
41 if 'host' not in connection_args:
42 connection_args['host'] = 'localhost'
43 if 'port' not in connection_args:
44 connection_args['port'] = 80
45
46 base_url = connection_args.get(
47 'url',
48 '{proto}://{host}:{port}/artifactory'.format(**connection_args)
49 )
Alexander Evseev81494492017-09-14 20:42:08 +030050
51 username = connection_args.get('user', 'admin')
52 password = connection_args.get('password', 'password')
53 ssl_verify = connection_args.get('ssl_verify', True)
54
55 # Prepare session object
56 api_connection = requests.Session()
57 api_connection.auth = (username, password)
58 api_connection.verify = ssl_verify
59
60 # Override default method if data given
61 if(data and method == 'GET'):
62 method = 'POST'
63
Oleh Hryhorovac106192018-01-29 12:24:15 +020064 endpoint_url = base_url + endpoint
Alexander Evseev81494492017-09-14 20:42:08 +030065 log.debug('Doing {0} request to {1}'.format(method, endpoint_url))
66
Oleh Hryhorovac106192018-01-29 12:24:15 +020067 # REST API call request
Alexander Evseev81494492017-09-14 20:42:08 +030068 resp = api_connection.request(
69 method=method,
70 url=endpoint_url,
71 data=data,
72 headers=headers
73 )
74
Oleh Hryhorovac106192018-01-29 12:24:15 +020075 if resp.ok:
Alexander Evseev81494492017-09-14 20:42:08 +030076 return True, resp.text
77 else:
78 errors = json.loads(resp.text).get('errors')
79 if errors:
80 for error in errors:
81 log.error('%(status)s:%(message)s' % error)
82 else:
83 log.error('%(status)s:%(message)s' % json.loads(resp.text))
84 return False, json.loads(resp.text)
85
Alexander Evseev81494492017-09-14 20:42:08 +030086def get_license(**kwargs):
87 endpoint = '/system/license'
88
89 return _api_call(endpoint, **kwargs)
90
91
92def add_license(license_key, **kwargs):
93 endpoint = '/system/license'
94
95 change_data = {
96 'licenseKey': license_key,
97 }
98
99 return _api_call(
100 endpoint=endpoint,
101 data=json.dumps(change_data),
102 headers={'Content-Type': 'application/json'},
103 **kwargs
104 )
105
106def get_config(**kwargs):
107 endpoint = '/system/configuration'
108
109 return _api_call(endpoint, **kwargs)
110
111
112def set_config(config_data, **kwargs):
113 endpoint = '/system/configuration'
114
115 return _api_call(
116 endpoint=endpoint,
117 data=config_data,
118 headers={'Content-Type': 'application/xml'},
119 **kwargs
120 )
121
122
123def get_ldap_config(name, **kwargs):
124
125 result, config_data = get_config(**kwargs)
126 config = objectify.fromstring(config_data.encode('ascii'))
127
128 # Find existing LDAP settings with specified key ...
129 ldap_config = None
130 for ldap_setting_iter in config.security.ldapSettings.getchildren():
131 if ldap_setting_iter.key.text == name:
132 ldap_config = ldap_setting_iter
133 break
134
135 # ... and create new one if not exists
136 if ldap_config is None:
137 ldap_config = objectify.SubElement(
138 config.security.ldapSettings, 'ldapSetting')
139 objectify.SubElement(ldap_config, 'key')._setText(name)
140
141 return result, etree.tostring(ldap_config)
142
143def set_ldap_config(name, uri, base=None, enabled=True, dn_pattern=None,
144 manager_dn=None, manager_pass=None, search_subtree=True,
145 search_filter='(&(objectClass=inetOrgPerson)(uid={0}))',
146 attr_mail='mail', create_users=True, safe_search=True,
147 **kwargs):
148
149 result, config_data = get_config(**kwargs)
150 config = objectify.fromstring(config_data.encode('ascii'))
151
152 # NOTE! Elements must ber sorted in exact order!
153 key_map = OrderedDict([
154 ('enabled', 'enabled'),
155 ('ldapUrl', 'uri'),
156 ('userDnPattern', 'dn_pattern'),
157 ('search', ''),
158 ('autoCreateUser', 'create_users'),
159 ('emailAttribute', 'attr_mail'),
160 ('ldapPoisoningProtection', 'safe_search'),
161 ])
162
163 key_map_search = OrderedDict([
164 ('searchFilter', 'search_filter'),
165 ('searchBase', 'base'),
166 ('searchSubTree', 'search_subtree'),
167 ('managerDn', 'manager_dn'),
168 ('managerPassword', 'manager_pass'),
169 ])
170
171 # Find existing LDAP settings with specified key ...
172 ldap_config = None
173 for ldap_setting_iter in config.security.ldapSettings.getchildren():
174 if ldap_setting_iter.key.text == name:
175 ldap_config = ldap_setting_iter
176 search_config = ldap_config.search
177 break
178
179 # ... and create new one if not exists
180 if ldap_config is None:
181 ldap_config = objectify.SubElement(
182 config.security.ldapSettings, 'ldapSetting')
183 objectify.SubElement(ldap_config, 'key')._setText(name)
184
185 # LDAP options
186 for xml_key, var_name in key_map.iteritems():
187
188 # Search subtree must follow element order
189 if xml_key == 'search' and not hasattr(ldap_config, 'search'):
190 search_config = objectify.SubElement(ldap_config, 'search')
191 break
192
193 if var_name in locals():
194 # Replace None with empty strings
195 var_value = locals()[var_name] or ''
196 if isinstance(var_value, bool):
197 # Boolean values should be lowercased
198 xml_text = str(var_value).lower()
199 else:
200 xml_text = str(var_value)
201
202 if hasattr(ldap_config, xml_key):
203 ldap_config[xml_key]._setText(xml_text)
204 else:
205 objectify.SubElement(ldap_config, xml_key)._setText(
206 xml_text)
207
208 # Search options (same code as above but using search_config)
209 for xml_key, var_name in key_map_search.iteritems():
210 if var_name in locals():
211 # Replace None with empty strings
212 var_value = locals()[var_name] or ''
213 if isinstance(var_value, bool):
214 # Boolean values should be lowercased
215 xml_text = str(var_value).lower()
216 else:
217 xml_text = str(var_value)
218
219 if hasattr(search_config, xml_key):
220 search_config[xml_key]._setText(xml_text)
221 else:
222 objectify.SubElement(search_config, xml_key)._setText(
223 xml_text)
224
225 change_data = etree.tostring(config)
226
227 return set_config(change_data, **kwargs)
228
229
230def list_repos(**kwargs):
231 endpoint = '/repositories'
232
233 return _api_call(endpoint, **kwargs)
234
235
236def get_repo(name, **kwargs):
237 result, repo_list = list_repos(**kwargs)
238 if name in [r['key'] for r in json.loads(repo_list)]:
239 endpoint = '/repositories/' + name
240 return _api_call(endpoint, **kwargs)
241 else:
242 return True, {}
243
244
245def set_repo(name, repo_config, **kwargs):
246 log.debug('Got repo parameters: {}'.format(repo_config))
247
248 result, repo_list = list_repos(**kwargs)
249 if name in [r['key'] for r in json.loads(repo_list)]:
250 method = 'POST'
251 else:
252 method = 'PUT'
253
254 endpoint = '/repositories/' + name
255
256 return _api_call(
257 endpoint=endpoint,
258 method=method,
259 data=json.dumps(repo_config),
260 headers={'Content-Type': 'application/json'},
261 **kwargs
262 )
Oleh Hryhorovac106192018-01-29 12:24:15 +0200263
264def deploy_artifact(source_file, endpoint, **kwargs):
265
266 endpoint = endpoint + "/" + os.path.basename(source_file)
267 with open(source_file, 'rb') as input_file:
268 result, status = _rest_call(
269 endpoint=endpoint,
270 method='PUT',
271 data=input_file,
272 **kwargs
273 )
274 return result, json.loads(status)
275
276def delete_artifact(item_to_delete, **kwargs):
277
278 return _rest_call(
279 endpoint=item_to_delete,
280 method='DELETE',
281 **kwargs
282 )
283