Skip to content

Commit f0f7281

Browse files
zelimapdelboca
andauthored
Feature/deployment logs (#150)
* Intorduce Drone logging * Introduce deployments status * Intoroduce deployment version * Introduce image get and set Co-authored-by: Irakli Mchedlishvili <irakli.mchedlishvili@datopian.com> Co-authored-by: Patricio Del Boca <patriciodelboca@gmail.com>
1 parent 8192746 commit f0f7281

File tree

8 files changed

+211
-1
lines changed

8 files changed

+211
-1
lines changed

ckan_cloud_operator/drivers/helm/driver.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,13 @@ def delete(tiller_namespace, release_name):
8181
tiller_cmd = '' if _check_helm_version() == 3 else f' --tiller-namespace {tiller_namespace}'
8282
subprocess.check_call(f'helm delete --purge --timeout 5 {release_name}' + tiller_cmd, shell=True)
8383

84+
85+
def check_status(instance_id):
86+
subprocess.check_call(f'helm status ckan-cloud-{instance_id} -n {instance_id}', shell=True)
87+
88+
def get_values(instance_id):
89+
return subprocess.check_output(f'helm get values ckan-cloud-{instance_id} -n {instance_id} -o json', shell=True)
90+
91+
8492
def _check_helm_version():
8593
return 3 if 'v3.' in str(subprocess.check_output('helm version -c', shell=True)) else 2
Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,61 @@
11
import click
22

3-
43
from .helm import cli as helm_cli
4+
from .drone import cli as drone_cli
5+
from .drone import manager
6+
from ckan_cloud_operator.providers.ckan.deployment import manager as deployment_manager
57

8+
drone_manager = manager.Drone()
69

710
@click.group()
811
def deployment():
912
"""Manage CKAN instance deployments"""
1013
pass
1114

15+
@click.group()
16+
def image():
17+
"""Manage CKAN instance deployments"""
18+
pass
19+
20+
@deployment.command()
21+
@click.option('--branch', default='develop', help='Source Branch for build [default: develop]')
22+
def logs(branch):
23+
"""See CKAN instances deployment Logs"""
24+
drone_manager.initialize()
25+
drone_manager.builds_logs(branch)
26+
27+
28+
@deployment.command()
29+
@click.argument('instance-id')
30+
def status(instance_id):
31+
"""See CKAN instances deployment status"""
32+
helm_driver.check_status(instance_id)
33+
34+
35+
@deployment.command()
36+
@click.argument('instance-id')
37+
def version(instance_id):
38+
"""See CKAN instances deployment version"""
39+
deployment_manager.get_deployment_version(instance_id)
40+
41+
42+
@image.command()
43+
@click.argument('instance-id')
44+
@click.option('--service', default='ckan', help='Source Branch for build [default: develop]')
45+
def get(instance_id, service):
46+
"""Get instances container image"""
47+
deployment_manager.get_image(instance_id, service=service)
48+
49+
50+
@image.command()
51+
@click.argument('instance-id')
52+
@click.argument('image-name')
53+
@click.option('--service', default='ckan', help='Source Branch for build [default: develop]')
54+
def set(instance_id, image_name, service):
55+
"""Set instances container image"""
56+
deployment_manager.set_image(instance_id, image_name, service=service)
57+
1258

1359
deployment.add_command(helm_cli.helm)
60+
deployment.add_command(drone_cli.drone)
61+
deployment.add_command(image)

ckan_cloud_operator/providers/ckan/deployment/drone/__init__.py

Whitespace-only changes.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import click
2+
3+
from ckan_cloud_operator import logs
4+
5+
from . import manager
6+
7+
8+
drone_manager = manager.Drone()
9+
10+
@click.group()
11+
def drone():
12+
"""Manage Drone CI/CD"""
13+
pass
14+
15+
16+
@drone.command()
17+
@click.option('--force-update', default=False, help='Force update drone configurations [default: develop]', is_flag=True)
18+
def initialize(force_update):
19+
"""Initialize drone"""
20+
drone_manager.initialize(force_update)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
PROVIDER_ID='drone'
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import requests
2+
from urllib.parse import urljoin
3+
4+
from ...env import manager as env_manager
5+
6+
7+
class Drone(object):
8+
9+
def initialize(self, force_prompt=False):
10+
self.conf = env_manager._read_yaml().get('cicd', {})
11+
self.server_url = self.conf.get('serverUrl')
12+
self.api_token = self.conf.get('token')
13+
self.org = self.conf.get('organization')
14+
self.repo = self.conf.get('repo')
15+
if force_prompt or not self.conf:
16+
self.server_url = input('Please enter Drone Server URL: ')
17+
self.api_token = input(f'Please enter Drone API Token. See {self. server_url} acount: ')
18+
self.org = input('Please enter Github organization name: ')
19+
self.repo = input('Please enter Github repository name: ')
20+
self.conf['cicd'] = {
21+
'serverUrl': self.server_url,
22+
'token': self.api_token,
23+
'organization': self.org,
24+
'repo': self.repo
25+
}
26+
env_manager._write_yaml(self.conf)
27+
self.api_url = urljoin(self.server_url, '/'.join(['api/repos', f'{self.org}/{self.repo}', 'builds/']))
28+
self.header = {'Authorization': f'Bearer {self.api_token}'}
29+
print('CCO is configured for Drone ')
30+
31+
32+
def builds_list(self):
33+
resp = requests.get(self.api_url, headers=self.header)
34+
if not _check_for_200(resp):
35+
return []
36+
return resp.json()
37+
38+
39+
def builds_info(self, branch='develop'):
40+
build_number = self.get_build_number(branch=branch)
41+
if build_number is None:
42+
print(f'Build {build_number} not found')
43+
return None
44+
url = urljoin(self.api_url, build_number)
45+
builds_info_resp = requests.get(url, headers=self.header)
46+
if not _check_for_200(builds_info_resp):
47+
return {}
48+
return builds_info_resp.json()
49+
50+
51+
def builds_logs(self, branch='develop'):
52+
build_info = self.builds_info(branch=branch)
53+
if build_info is None:
54+
return None
55+
stages = build_info.get('stages', [])
56+
for stage in stages:
57+
steps = stage.get('steps', [])
58+
stage_name = stage.get('name')
59+
for step in steps:
60+
step_name = step.get('name')
61+
print(f'--- Build Stage: {stage_name} | Builds Step: {step_name}')
62+
url = urljoin(self.api_url, '/'.join([
63+
self.build_number,
64+
'logs',
65+
str(stage.get('number')),
66+
str(step.get('number'))])
67+
)
68+
log_resp = requests.get(url, headers=self.header)
69+
log_list = log_resp.json()
70+
for _log in log_list:
71+
print(_log.get('out').rstrip('\n'))
72+
print(f'Showing logs for "{branch}" Branch')
73+
74+
75+
def get_build_number(self, branch='develop'):
76+
for build in self.builds_list():
77+
if build.get('source') == branch:
78+
self.build_number = str(build.get('number'))
79+
return str(build.get('number'))
80+
print(f'No build found for branch {branch}')
81+
return None
82+
83+
84+
def _check_for_200(resp):
85+
if resp.status_code == 200:
86+
return True
87+
if resp.status_code == 401:
88+
print("Seems like you are not authorized")
89+
print("Please run `cco ckan deployment drone initialize --force-update` and rerun")
90+
return False
91+
return False

ckan_cloud_operator/providers/ckan/deployment/manager.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
import json
2+
import requests
3+
from urllib.parse import urljoin
4+
5+
from ckan_cloud_operator import kubectl
6+
from ckan_cloud_operator.drivers.helm import driver as helm_driver
7+
8+
19
def initialize(interactive=False):
210
from .helm.manager import initialize as ckan_helm_initialize
311
ckan_helm_initialize(interactive=interactive)
@@ -32,6 +40,34 @@ def pre_update_hook(instance_id, instance_type, instance, override_spec, skip_ro
3240
dry_run=dry_run)
3341

3442

43+
def get_deployment_version(instance_id):
44+
values = helm_driver.get_values(instance_id)
45+
values = json.loads(values)
46+
site_url = values.get('siteUrl')
47+
print('Current deployment version: ', requests.get(urljoin(site_url, 'version')).text)
48+
49+
50+
def get_image(instance_id, service='ckan'):
51+
deployment_info = kubectl.get('deployment', namespace=instance_id)
52+
image_name = None
53+
for item in deployment_info.get('items', []):
54+
if item['metadata'].get('name') == service:
55+
containers = item.get('spec', {}).get('template', {}).get('spec', {}).get('containers', [])
56+
for container in containers:
57+
image_name = container.get('image')
58+
print(image_name)
59+
if image_name is None:
60+
print(f'Not able to find image for service "{service}", please make sure service name spelled correctly')
61+
62+
63+
def set_image(instance_id, image_name, service='ckan', container_name=None):
64+
cont_name = container_name or service
65+
deployment_info = kubectl.call(
66+
f'set image deployment/{service} {cont_name}={image_name}',
67+
namespace=instance_id
68+
)
69+
70+
3571
def create_ckan_admin_user(instance_id, instance_type, instance, user):
3672
_get_deployment_provider(instance_type).create_ckan_admin_user(instance_id, instance, user)
3773

ckan_cloud_operator/providers/ckan/env/manager.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,12 @@ def _write_yaml(data):
163163
_file.close()
164164

165165

166+
def _read_yaml():
167+
with open(_get_config_file_name()) as _file:
168+
yaml_conf = yaml.load(_file, Loader=yaml.FullLoader)
169+
return yaml_conf
170+
171+
166172
def _mkconfdir():
167173
dir = _get_config_dir()
168174
Path(f'{dir}').mkdir(parents=True, exist_ok=True)

0 commit comments

Comments
 (0)