From eac94a0de23dff1d8767b493c68bd318836cb919 Mon Sep 17 00:00:00 2001 From: LanternNassi Date: Mon, 15 Jan 2024 13:34:35 +0300 Subject: [PATCH 1/7] Application rollback --- src/cranecloud/commands/apps.py | 49 ++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/cranecloud/commands/apps.py b/src/cranecloud/commands/apps.py index bded9ff..295ff79 100644 --- a/src/cranecloud/commands/apps.py +++ b/src/cranecloud/commands/apps.py @@ -62,11 +62,20 @@ def get_app_details(app_id): click.echo('Getting app details...\n') try: token = get_token() + + # Getting details response = requests.get( f'{API_BASE_URL}/apps/{app_id}', headers={'Authorization': f'Bearer {token}'}) response.raise_for_status() - if response.status_code == 200: + + # Getting revisions + revisions_response = requests.get( + f'{API_BASE_URL}/apps/{app_id}/revisions', headers={'Authorization': f'Bearer {token}'}) + + + if response.status_code == 200 and revisions_response.status_code == 200: app = response.json()['data']['apps'] + revisions = revisions_response.json()['data']['revisions'] table_data = [ ['ID', app.get('id')], ['Name', app.get('name')], @@ -88,6 +97,7 @@ def get_app_details(app_id): ['Age', app.get('age')], ['Date Created', app.get('date_created')], ['Env Vars', json.dumps(app.get('env_vars'), indent=4)], + ['Revisions' , json.dumps(revisions,indent=4)] ] click.echo(tabulate(table_data, tablefmt='plain')) else: @@ -224,3 +234,40 @@ def update_app(app_id, name, image, command, replicas, port, env): click.echo(f'Failed to connect to the server: {e}') click.echo( 'Please check your internet connection or try again later.') + + + +@apps.command('rollback', help='Rolling back an application') +@click.option('-v', '--version', type=str, help='App revision id of the app to roll back to') +@click.argument('app_id', type=click.UUID) +def rollback_app(app_id , version): + '''Make an app roll back.''' + click.echo('Rolling back app...') + try: + token = get_token() + + + response = requests.post( + f'{API_BASE_URL}/apps/{app_id}/revise/{version}', + headers={'Authorization': f'Bearer {token}'} + ) + response.raise_for_status() + + if response.status_code == 200 : + click.echo('App revised successfully.') + + else : + click.echo('An error occured. Could not revise the application.') + + except requests.RequestException as e: + if e.response not in [None, '']: + click.echo( + click.style(f'Failed to revise the app\n', fg='red') + + e.response.json().get('message')) + else: + click.echo(f'Failed to connect to the server: {e}') + click.echo( + 'Please check your internet connection or try again later.') + + + \ No newline at end of file From dfef04c1db43d4ab5adcca7c2eda34f5446cc170 Mon Sep 17 00:00:00 2001 From: LanternNassi Date: Mon, 15 Jan 2024 14:39:38 +0300 Subject: [PATCH 2/7] Creating a separate option for getting app revisions --- src/cranecloud/commands/apps.py | 50 +++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/src/cranecloud/commands/apps.py b/src/cranecloud/commands/apps.py index 295ff79..0da74ef 100644 --- a/src/cranecloud/commands/apps.py +++ b/src/cranecloud/commands/apps.py @@ -68,14 +68,8 @@ def get_app_details(app_id): f'{API_BASE_URL}/apps/{app_id}', headers={'Authorization': f'Bearer {token}'}) response.raise_for_status() - # Getting revisions - revisions_response = requests.get( - f'{API_BASE_URL}/apps/{app_id}/revisions', headers={'Authorization': f'Bearer {token}'}) - - - if response.status_code == 200 and revisions_response.status_code == 200: + if response.status_code == 200: app = response.json()['data']['apps'] - revisions = revisions_response.json()['data']['revisions'] table_data = [ ['ID', app.get('id')], ['Name', app.get('name')], @@ -97,7 +91,6 @@ def get_app_details(app_id): ['Age', app.get('age')], ['Date Created', app.get('date_created')], ['Env Vars', json.dumps(app.get('env_vars'), indent=4)], - ['Revisions' , json.dumps(revisions,indent=4)] ] click.echo(tabulate(table_data, tablefmt='plain')) else: @@ -236,6 +229,47 @@ def update_app(app_id, name, image, command, replicas, port, env): 'Please check your internet connection or try again later.') +@apps.command('revisions', help='List app revisions') +@click.argument('app_id', type=click.UUID) +@click.option('-p', '--page', type=int, help='Page' , required=False) +def get_revisions(app_id , page): + '''Get application revisions .''' + click.echo('Getting app revisions ... ') + try: + + token = get_token() + + + # Getting revisions + response = requests.get( + f'{API_BASE_URL}/apps/{app_id}/revisions', headers={'Authorization': f'Bearer {token}'} , + params={'page' : page} + ) + + response.raise_for_status() + + if response.status_code == 200: + revisions = response.json()['data']['revisions'] + table_data = [] + + for revision in revisions: + table_data.append( + [revision.get('revision') , revision.get('revision_id') , revision.get('replicas') , revision.get('created_at') , revision.get('image') , revision.get('port') , revision.get('current' , '') , revision.get('command')]) + headers = ['Revision' , 'Revision id' , 'Replicas' , 'Date created' , 'image' , 'Port' , 'Current' , 'Command'] + click.echo(tabulate(table_data, headers, tablefmt='simple')) + + click.echo(f"Page {response.json()['data']['pagination']['page']} of {response.json()['data']['pagination']['pages']} ....") + else: + click.echo('Failed to get app revisions list.') + except requests.RequestException as e: + if e.response or e.response.reason: + click.echo(f'Error: {e.response.reason}') + else: + click.echo(f'Failed to connect to the server: {e}') + click.echo( + 'Please check your internet connection or try again later.') + + @apps.command('rollback', help='Rolling back an application') @click.option('-v', '--version', type=str, help='App revision id of the app to roll back to') From c07b0e2caa188ce2f3dd05c32293cf8d69440597 Mon Sep 17 00:00:00 2001 From: LanternNassi Date: Wed, 17 Jan 2024 19:03:26 +0300 Subject: [PATCH 3/7] Creating a subcommand under apps for revisions --- src/cranecloud/__init__.py | 37 ++++++++++++++++++++++++++------- src/cranecloud/commands/apps.py | 35 ++++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/src/cranecloud/__init__.py b/src/cranecloud/__init__.py index 3a2f4c3..dd09966 100644 --- a/src/cranecloud/__init__.py +++ b/src/cranecloud/__init__.py @@ -1,12 +1,13 @@ -from src.cranecloud.commands.apps import apps_group -from src.cranecloud.commands.clusters import clusters_group +from src.cranecloud.commands.apps import apps , revisions +from src.cranecloud.commands.clusters import clusters import click from os.path import join, dirname from dotenv import load_dotenv from src.cranecloud.utils.config import create_config -from src.cranecloud.commands.user_management import user_group -from src.cranecloud.commands.projects import projects_group -from src.cranecloud.commands.config_management import config_group +from src.cranecloud.commands.user_management import user +from src.cranecloud.commands.projects import projects +from src.cranecloud.commands.config_management import config + dotenv_path = join(dirname(__file__), '.env') @@ -17,10 +18,32 @@ def cli(): pass +#Users Section +cli.add_command(user) + + +#Projects section +cli.add_command(projects) + + +# Apps section +apps.add_command(revisions) +cli.add_command(apps) + +# Clusters section +cli.add_command(clusters) + + +# Config section +cli.add_command(config) + + + -cli = click.CommandCollection( - sources=[user_group, projects_group, apps_group, clusters_group, config_group]) def create_initial_config(): create_config() + +if __name__ == '__main__': + cli() diff --git a/src/cranecloud/commands/apps.py b/src/cranecloud/commands/apps.py index 0da74ef..2acd35f 100644 --- a/src/cranecloud/commands/apps.py +++ b/src/cranecloud/commands/apps.py @@ -19,6 +19,18 @@ def apps(): ''' pass +@click.group() +def revisions_group(): + pass + + +@revisions_group.group(name='revisions') +def revisions(): + ''' + Revisions management commands. + ''' + pass + @apps.command('list', help='List apps in project') @click.option('-p', '--project_id', type=click.UUID, help='Project ID') @@ -106,7 +118,7 @@ def get_app_details(app_id): 'Please check your internet connection or try again later.') -@apps.command('delete', help='Delete App') +@apps.command('delete', help='Delete App [Example : cranecloud apps delete ]') @click.argument('app_id', type=click.UUID) def delete_app(app_id): '''Delete app.''' @@ -229,9 +241,9 @@ def update_app(app_id, name, image, command, replicas, port, env): 'Please check your internet connection or try again later.') -@apps.command('revisions', help='List app revisions') +@revisions.command('list', help='List app revisions') @click.argument('app_id', type=click.UUID) -@click.option('-p', '--page', type=int, help='Page' , required=False) +@click.option('--page', type=int, help='Page' , required=False) def get_revisions(app_id , page): '''Get application revisions .''' click.echo('Getting app revisions ... ') @@ -271,18 +283,18 @@ def get_revisions(app_id , page): -@apps.command('rollback', help='Rolling back an application') -@click.option('-v', '--version', type=str, help='App revision id of the app to roll back to') +@revisions.command('update', help='Update an application to a specific revision id') +@click.option('-r', '--revision', type=str, help='Revision id of the application' , required = True) @click.argument('app_id', type=click.UUID) -def rollback_app(app_id , version): - '''Make an app roll back.''' - click.echo('Rolling back app...') +def update_app(app_id , revision): + '''Make an app update.''' + click.echo(f'Updating application to revision {revision} ...') try: token = get_token() response = requests.post( - f'{API_BASE_URL}/apps/{app_id}/revise/{version}', + f'{API_BASE_URL}/apps/{app_id}/revise/{revision}', headers={'Authorization': f'Bearer {token}'} ) response.raise_for_status() @@ -304,4 +316,7 @@ def rollback_app(app_id , version): 'Please check your internet connection or try again later.') - \ No newline at end of file + + + + From 3ce4a0c8666bd5d077d91402adafeddafd8cf197 Mon Sep 17 00:00:00 2001 From: LanternNassi Date: Wed, 7 Feb 2024 11:30:50 +0300 Subject: [PATCH 4/7] Making config file dynamic --- src/cranecloud/commands/config_management.py | 13 ++++++++++++- src/cranecloud/utils/config.py | 16 +++++++++------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/cranecloud/commands/config_management.py b/src/cranecloud/commands/config_management.py index 33a8305..8163693 100644 --- a/src/cranecloud/commands/config_management.py +++ b/src/cranecloud/commands/config_management.py @@ -2,8 +2,9 @@ from tabulate import SEPARATING_LINE, tabulate from src.cranecloud.commands.projects import set_use_project from src.config import CURRENT_CLUSTER, CURRENT_PROJECT, CURRENT_USER -from src.cranecloud.utils.config import read_config +from src.cranecloud.utils.config import read_config , create_config +import os @click.group() def config_group(): @@ -43,3 +44,13 @@ def get_config(): table_data.append([key, value]) headers = ['Key', 'Value'] click.echo(tabulate(table_data, headers, tablefmt='simple')) + + +@config.command('create-config', help='Create CraneCloud configuration.') +@click.argument('config_path', type=click.STRING) +def change_config(config_path): + print(config_path) + + os.makedirs(config_path, exist_ok=True) + create_config(config_path) + \ No newline at end of file diff --git a/src/cranecloud/utils/config.py b/src/cranecloud/utils/config.py index 55c58a5..fcf3c9d 100644 --- a/src/cranecloud/utils/config.py +++ b/src/cranecloud/utils/config.py @@ -11,22 +11,24 @@ def get_base_dir(): return crane_dir -def create_config(): +def create_config(crane_config_path = get_base_dir()): default_base_url = os.getenv('API_BASE_URL', "https://api.cranecloud.io") - write_config('base_url', default_base_url) + write_config('base_url', default_base_url , crane_dir=crane_config_path) + write_config('CRANE_CONFIG' , crane_config_path , crane_dir = crane_config_path) -def read_config(): + +def read_config(crane_config = get_base_dir()): config = configparser.ConfigParser() - crane_dir = get_base_dir() - config_file = os.path.join(crane_dir, CONFIG_FILE) + #crane_dir = get_base_dir() + config_file = os.path.join(crane_config, CONFIG_FILE) config.read(config_file) return config -def write_config(key, value, should_update=True): +def write_config(key, value, should_update=True , crane_dir = get_base_dir()): config = configparser.ConfigParser() - crane_dir = get_base_dir() + # crane_dir = get_base_dir() config_file = os.path.join(crane_dir, CONFIG_FILE) os.makedirs(crane_dir, exist_ok=True) From c7d0760430986ca66a1e8c62871c6935f36111e1 Mon Sep 17 00:00:00 2001 From: LanternNassi Date: Mon, 4 Mar 2024 11:44:44 +0300 Subject: [PATCH 5/7] Reverting some changes --- src/cranecloud/__init__.py | 37 +++++++-------------------------- src/cranecloud/commands/apps.py | 6 +----- 2 files changed, 8 insertions(+), 35 deletions(-) diff --git a/src/cranecloud/__init__.py b/src/cranecloud/__init__.py index dd09966..23b9258 100644 --- a/src/cranecloud/__init__.py +++ b/src/cranecloud/__init__.py @@ -1,13 +1,12 @@ -from src.cranecloud.commands.apps import apps , revisions -from src.cranecloud.commands.clusters import clusters import click from os.path import join, dirname from dotenv import load_dotenv from src.cranecloud.utils.config import create_config -from src.cranecloud.commands.user_management import user -from src.cranecloud.commands.projects import projects -from src.cranecloud.commands.config_management import config - +from src.cranecloud.commands.user_management import user_group +from src.cranecloud.commands.projects import projects_group +from src.cranecloud.commands.config_management import config_group +from src.cranecloud.commands.apps import apps_group +from src.cranecloud.commands.clusters import clusters_group dotenv_path = join(dirname(__file__), '.env') @@ -18,32 +17,10 @@ def cli(): pass -#Users Section -cli.add_command(user) - - -#Projects section -cli.add_command(projects) - - -# Apps section -apps.add_command(revisions) -cli.add_command(apps) - -# Clusters section -cli.add_command(clusters) - - -# Config section -cli.add_command(config) - - - - +cli = click.CommandCollection( + sources=[user_group, projects_group, apps_group, clusters_group, config_group]) def create_initial_config(): create_config() -if __name__ == '__main__': - cli() diff --git a/src/cranecloud/commands/apps.py b/src/cranecloud/commands/apps.py index 2acd35f..95cdbab 100644 --- a/src/cranecloud/commands/apps.py +++ b/src/cranecloud/commands/apps.py @@ -19,12 +19,8 @@ def apps(): ''' pass -@click.group() -def revisions_group(): - pass - -@revisions_group.group(name='revisions') +@apps.group(name='revisions') def revisions(): ''' Revisions management commands. From 1393f9f44f1b604fd233e44665259aace24d71e4 Mon Sep 17 00:00:00 2001 From: LanternNassi Date: Tue, 5 Mar 2024 12:32:24 +0300 Subject: [PATCH 6/7] Making config file dynamic --- src/config.py | 4 +- src/cranecloud/__init__.py | 11 ++---- src/cranecloud/commands/config_management.py | 7 ++-- src/cranecloud/utils/config.py | 41 ++++++++++++++++---- 4 files changed, 43 insertions(+), 20 deletions(-) diff --git a/src/config.py b/src/config.py index 350e860..b1d394f 100644 --- a/src/config.py +++ b/src/config.py @@ -1,11 +1,11 @@ -from src.cranecloud.utils.config import create_config, read_config +from src.cranecloud.utils.config import create_config, read_config , create_initial_config # Example usage of the config values config_file = read_config() try: API_BASE_URL = config_file['GlobalSettings']['base_url'] except KeyError: - create_config() + create_initial_config() config_file = read_config() API_BASE_URL = config_file['GlobalSettings'].get('base_url') try: diff --git a/src/cranecloud/__init__.py b/src/cranecloud/__init__.py index 23b9258..84ce574 100644 --- a/src/cranecloud/__init__.py +++ b/src/cranecloud/__init__.py @@ -1,16 +1,14 @@ import click from os.path import join, dirname from dotenv import load_dotenv -from src.cranecloud.utils.config import create_config +from src.cranecloud.utils.config import create_config , write_config , get_base_dir from src.cranecloud.commands.user_management import user_group from src.cranecloud.commands.projects import projects_group from src.cranecloud.commands.config_management import config_group from src.cranecloud.commands.apps import apps_group from src.cranecloud.commands.clusters import clusters_group - - -dotenv_path = join(dirname(__file__), '.env') -load_dotenv(dotenv_path) +from pathlib import Path +from dotenv import set_key @click.group() @@ -21,6 +19,5 @@ def cli(): sources=[user_group, projects_group, apps_group, clusters_group, config_group]) -def create_initial_config(): - create_config() + diff --git a/src/cranecloud/commands/config_management.py b/src/cranecloud/commands/config_management.py index 8163693..9a15057 100644 --- a/src/cranecloud/commands/config_management.py +++ b/src/cranecloud/commands/config_management.py @@ -46,11 +46,10 @@ def get_config(): click.echo(tabulate(table_data, headers, tablefmt='simple')) -@config.command('create-config', help='Create CraneCloud configuration.') +@config.command('use-config', help='Create CraneCloud configuration.') @click.argument('config_path', type=click.STRING) def change_config(config_path): - print(config_path) - os.makedirs(config_path, exist_ok=True) - create_config(config_path) + os.makedirs(os.path.join(config_path, '.crane') , exist_ok=True) + create_config(os.path.join(config_path, '.crane')) \ No newline at end of file diff --git a/src/cranecloud/utils/config.py b/src/cranecloud/utils/config.py index fcf3c9d..f8a3d60 100644 --- a/src/cranecloud/utils/config.py +++ b/src/cranecloud/utils/config.py @@ -1,26 +1,54 @@ import os import configparser +from dotenv import set_key +from pathlib import Path +from os.path import join, dirname +from dotenv import load_dotenv + + CONFIG_FILE = 'config' +dotenv_path = join(dirname(__file__), '.env') +load_dotenv(dotenv_path) + + def get_base_dir(): - home_dir = os.path.expanduser("~") - crane_dir = os.path.join(home_dir, '.crane') + crane_dir = os.getenv('CRANE_CONFIG_DIR') + if not crane_dir: + config_dir = os.path.expanduser("~") + crane_dir = os.path.join(config_dir, '.crane') return crane_dir -def create_config(crane_config_path = get_base_dir()): +def create_config(dir= get_base_dir()): + env_file_path = Path(join(dirname(__file__), '.env')) + env_file_path.touch(mode=0o600, exist_ok=True) + default_base_url = os.getenv('API_BASE_URL', "https://api.cranecloud.io") + set_key(dotenv_path=env_file_path, key_to_set="CRANE_CONFIG_DIR", value_to_set=dir , export=True) + write_config('base_url', default_base_url , crane_dir = dir) + write_config("dotenv_path" , dotenv_path , crane_dir=env_file_path) - write_config('base_url', default_base_url , crane_dir=crane_config_path) - write_config('CRANE_CONFIG' , crane_config_path , crane_dir = crane_config_path) + +def create_initial_config(): + env_file_path = Path(join(dirname(__file__), '.env')) + env_file_path.touch(mode=0o600, exist_ok=True) + + config_dir = os.path.expanduser("~") + crane_dir = os.path.join(config_dir, '.crane') + + set_key(dotenv_path=env_file_path, key_to_set="CRANE_CONFIG_DIR", value_to_set=crane_dir , export=True) + write_config("dotenv_path" , dotenv_path , crane_dir=crane_dir) + + default_base_url = os.getenv('API_BASE_URL', "https://api.cranecloud.io") + write_config('base_url', default_base_url , crane_dir=crane_dir) def read_config(crane_config = get_base_dir()): config = configparser.ConfigParser() - #crane_dir = get_base_dir() config_file = os.path.join(crane_config, CONFIG_FILE) config.read(config_file) return config @@ -28,7 +56,6 @@ def read_config(crane_config = get_base_dir()): def write_config(key, value, should_update=True , crane_dir = get_base_dir()): config = configparser.ConfigParser() - # crane_dir = get_base_dir() config_file = os.path.join(crane_dir, CONFIG_FILE) os.makedirs(crane_dir, exist_ok=True) From 7c0d69cc751acba4a85c631441d8a589807c36e7 Mon Sep 17 00:00:00 2001 From: LanternNassi Date: Tue, 5 Mar 2024 12:34:58 +0300 Subject: [PATCH 7/7] Remove help in apps --- src/cranecloud/commands/apps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cranecloud/commands/apps.py b/src/cranecloud/commands/apps.py index 95cdbab..b2daae0 100644 --- a/src/cranecloud/commands/apps.py +++ b/src/cranecloud/commands/apps.py @@ -114,7 +114,7 @@ def get_app_details(app_id): 'Please check your internet connection or try again later.') -@apps.command('delete', help='Delete App [Example : cranecloud apps delete ]') +@apps.command('delete', help='Delete App') @click.argument('app_id', type=click.UUID) def delete_app(app_id): '''Delete app.'''