Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
e2d1ca1
minor refractor on global config
Richard-B18 Jul 4, 2022
af984b4
traverse up recursively and read config file if in child dir of clien…
Richard-B18 Jul 5, 2022
b4863a8
refractor local config file manager
Richard-B18 Jul 5, 2022
4011f3b
minor typo
Richard-B18 Jul 5, 2022
9412cd6
WIP
98sean98 Jul 5, 2022
594072c
change flow of project create command, minor refractor in local config
Richard-B18 Jul 6, 2022
bb58cc4
change flow of cli, if project not found, set local config to none in…
Richard-B18 Jul 6, 2022
4de74b4
bug fix when user initializes a project
Richard-B18 Jul 6, 2022
f853949
change flow of command, create project locally b4 doing it in the bac…
Richard-B18 Jul 6, 2022
a4802ae
refractor for commands using local config
Richard-B18 Jul 6, 2022
d146b37
added a decorator to check if deploifai project found
Richard-B18 Jul 6, 2022
aca1610
change flow of commmand again, bug fix for duplicate local file names
Richard-B18 Jul 6, 2022
af64953
change command flow, no more loops if invalid name
Richard-B18 Jul 7, 2022
0ca958a
minor bug fix
Richard-B18 Jul 7, 2022
7a2e87c
project command: create cloud profile (#17)
Richard-B18 Jul 7, 2022
cf40025
minor refractor on global config
Richard-B18 Jul 4, 2022
d42b138
traverse up recursively and read config file if in child dir of clien…
Richard-B18 Jul 5, 2022
53df07e
refractor local config file manager
Richard-B18 Jul 5, 2022
d817930
minor typo
Richard-B18 Jul 5, 2022
bf22a0e
WIP
98sean98 Jul 5, 2022
3cbca69
change flow of project create command, minor refractor in local config
Richard-B18 Jul 6, 2022
0376fe9
change flow of cli, if project not found, set local config to none in…
Richard-B18 Jul 6, 2022
bf468cc
bug fix when user initializes a project
Richard-B18 Jul 6, 2022
67075b9
change flow of command, create project locally b4 doing it in the bac…
Richard-B18 Jul 6, 2022
18f22dd
refractor for commands using local config
Richard-B18 Jul 6, 2022
6c566a8
added a decorator to check if deploifai project found
Richard-B18 Jul 6, 2022
4b8cdf6
change flow of commmand again, bug fix for duplicate local file names
Richard-B18 Jul 6, 2022
d02f656
change command flow, no more loops if invalid name
Richard-B18 Jul 7, 2022
8a7dc38
minor bug fix
Richard-B18 Jul 7, 2022
a448bf4
Merge branch 'local-config' of https://github.com/deploifai/cli into …
Richard-B18 Jul 7, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 53 additions & 10 deletions deploifai/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from deploifai.api.errors import DeploifaiAPIError
from deploifai.utilities.credentials import get_auth_token
from deploifai.cloud_profile.cloud_profile import CloudProfile
from deploifai.utilities.cloud_profile import CloudProfile
from deploifai.utilities import environment


Expand Down Expand Up @@ -298,6 +298,49 @@ def get_cloud_profiles(self, workspace=None) -> typing.List[CloudProfile]:
]
return cloud_profiles

def create_cloud_profile(self, provider, name, credentials, workspace, fragment):
mutation = """
mutation(
$whereAccount: AccountWhereUniqueInput!
$data: CloudProfileCreateInput!
) {
createCloudProfile(whereAccount: $whereAccount, data: $data) {
...cloud_profile
}
}
"""

variables = {
"whereAccount": {"username": workspace["username"]},
"data": {
"name": name,
"provider": provider.value,
},
}

if provider.value == "AWS":
variables["data"]["awsCredentials"] = credentials
elif provider.value == "AZURE":
variables["data"]["azureCredentials"] = credentials
elif provider.value == "GCP":
variables["data"]["gcpCredentials"] = credentials

try:
r = requests.post(
self.uri,
json={"query": mutation + fragment, "variables": variables},
headers=self.headers,
)

create_mutation_data = r.json()

return create_mutation_data["data"]["createCloudProfile"]["id"]
except TypeError as err:
raise DeploifaiAPIError("Could not create cloud profile. Please try again.")
except KeyError as err:
raise DeploifaiAPIError("Could not create cloud profile. Please try again.")


def get_projects(self, workspace, fragment: str, where_project=None):
query = (
"""
Expand Down Expand Up @@ -327,9 +370,9 @@ def get_projects(self, workspace, fragment: str, where_project=None):
api_data = r.json()

return api_data["data"]["projects"]
except TypeError as err:
except TypeError:
raise DeploifaiAPIError("Could not get projects. Please try again.")
except KeyError as err:
except KeyError:
raise DeploifaiAPIError("Could not get projects. Please try again.")

def get_project(self, project_id: str, fragment: str):
Expand Down Expand Up @@ -357,19 +400,19 @@ def get_project(self, project_id: str, fragment: str):
api_data = r.json()

return api_data["data"]["project"]
except TypeError as err:
except TypeError:
raise DeploifaiAPIError("Could not get project. Please try again.")
except KeyError as err:
except KeyError:
raise DeploifaiAPIError("Could not get project. Please try again.")

def create_project(self, project_name: str, cloud_profile: CloudProfile):
def create_project(self, project_name: str, cloud_profile: CloudProfile, fragment):
mutation = """
mutation(
$whereAccount: AccountWhereUniqueInput!
$data: CreateProjectInput!
) {
createProject(whereAccount: $whereAccount, data: $data) {
id
...project
}
}
"""
Expand All @@ -385,14 +428,14 @@ def create_project(self, project_name: str, cloud_profile: CloudProfile):
try:
r = requests.post(
self.uri,
json={"query": mutation, "variables": variables},
json={"query": mutation + fragment, "variables": variables},
headers=self.headers,
)

create_mutation_data = r.json()

return create_mutation_data["data"]["createProject"]
except TypeError as err:
except TypeError:
raise DeploifaiAPIError("Could not create project. Please try again.")
except KeyError as err:
except KeyError:
raise DeploifaiAPIError("Could not create project. Please try again.")
3 changes: 2 additions & 1 deletion deploifai/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
from .application import application
from .project import project
from .data import data
from .cloud_profile import cloud_profile

commands = {"auth": auth, "project": project, "data": data, "application": application}
commands = {"auth": auth, "project": project, "data": data, "application": application, "cloud-profile": cloud_profile}


@click.group(commands=commands)
Expand Down
13 changes: 13 additions & 0 deletions deploifai/cloud_profile/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import click

from .create import create


@click.group()
def cloud_profile():
"""
Manage user's cloud profiles
"""


cloud_profile.add_command(create)
165 changes: 165 additions & 0 deletions deploifai/cloud_profile/create.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import click
from PyInquirer import prompt
from deploifai.utilities.cloud_profile import Provider
from deploifai.context import (
pass_deploifai_context_obj,
DeploifaiContextObj,
is_authenticated,
)
from deploifai.utilities.user import parse_user_profiles
from deploifai.api import DeploifaiAPIError


@click.command()
@click.option("--name", "-n", help="Cloud profile name", prompt="Choose a cloud profile name")
@click.option("--workspace", "-w", help="Workspace name", type=str)
@pass_deploifai_context_obj
@is_authenticated
def create(context: DeploifaiContextObj, name: str, workspace: str):
"""
Create a new cloud profile
"""
deploifai_api = context.api

user_data = deploifai_api.get_user()
personal_workspace, team_workspaces = parse_user_profiles(user_data)

workspaces_from_api = [personal_workspace] + team_workspaces

# checking validity of workspace, and prompting workspaces choices if not specified
if workspace and len(workspace):
if any(ws["username"] == workspace for ws in workspaces_from_api):
for w in workspaces_from_api:
if w["username"] == workspace:
command_workspace = w
break
else:
# the workspace user input does not match with any of the workspaces the user has access to
click.secho(
f"{workspace} cannot be found. Please put in a workspace you have access to.",
fg="red",
)
raise click.Abort()
else:
_choose_workspace = prompt(
{
"type": "list",
"name": "workspace",
"message": "Choose a workspace",
"choices": [
{"name": ws["username"], "value": ws} for ws in workspaces_from_api
],
}
)
if _choose_workspace == {}:
raise click.Abort()
command_workspace = _choose_workspace["workspace"]

try:
cloud_profiles = deploifai_api.get_cloud_profiles(workspace=command_workspace)
except DeploifaiAPIError as err:
click.secho(err, fg="red")
return

existing_names = [cloud_profile.name for cloud_profile in cloud_profiles]

if name in existing_names:
click.echo(click.style("Cloud profile name taken. Existing names: ", fg="red") + ' '.join(existing_names))
raise click.Abort()

provider = prompt(
{
"type": "list",
"name": "provider",
"message": "Choose a provider for the new cloud profile",
"choices": [{"name": provider.value, "value": provider} for provider in Provider]
}
)["provider"]

cloud_credentials = {}
if provider == Provider.AWS:
cloud_credentials["awsAccessKey"] = prompt(
{
"type": "input",
"name": "awsAccessKey",
"message": "AWS Access Key ID (We'll keep these secured and encrypted)",
}
)["awsAccessKey"]
cloud_credentials["awsSecretAccessKey"] = prompt(
{
"type": "input",
"name": "awsSecretAccessKey",
"message": "AWS Secret Access Key (We'll keep these secured and encrypted)",
}
)["awsSecretAccessKey"]
elif provider == Provider.AZURE:
cloud_credentials["azureSubscriptionId"] = prompt(
{
"type": "input",
"name": "azureSubscriptionId",
"message": "Azure Account Subscription ID (We'll keep these secured and encrypted)",
}
)["azureSubscriptionId"]
cloud_credentials["azureTenantId"] = prompt(
{
"type": "input",
"name": "azureTenantId",
"message": "Azure Active Directory Tenant ID (We'll keep these secured and encrypted)",
}
)["azureTenantId"]
cloud_credentials["azureClientId"] = prompt(
{
"type": "input",
"name": "azureClientId",
"message": "Azure Client ID (We'll keep these secured and encrypted)",
}
)["azureClientId"]
cloud_credentials["azureClientSecret"] = prompt(
{
"type": "input",
"name": "azureClientSecret",
"message": "Azure Client Secret / Password (We'll keep these secured and encrypted)",
}
)["azureClientSecret"]
else:
cloud_credentials["gcpProjectId"] = prompt(
{
"type": "input",
"name": "gcpProjectId",
"message": "GCP Project ID (We'll keep these secured and encrypted)",
}
)["gcpProjectId"]

gcp_service_account_key_file = prompt(
{
"type": "input",
"name": "gcp_service_account_key_file",
"message": "File path for the GCP Service Account Key File (We'll keep these secured and encrypted)",
}
)["gcp_service_account_key_file"]

try:
with open(gcp_service_account_key_file) as gcp_service_account_key_json:
cloud_credentials["gcpServiceAccountKey"] = gcp_service_account_key_json.read()
except FileNotFoundError:
click.secho("File not found. Please input the correct file path.", fg="red")
raise click.Abort()

try:
cloud_profile_fragment = """
fragment cloud_profile on CloudProfile {
id
}
"""
_ = deploifai_api.create_cloud_profile(
provider,
name,
cloud_credentials,
command_workspace,
cloud_profile_fragment
)
except DeploifaiAPIError as err:
click.secho(err, fg="red")
raise click.Abort()

click.secho(f"Successfully created a new cloud profile named {name}.", fg="green")
22 changes: 15 additions & 7 deletions deploifai/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,16 @@ def __init__(self):
pass

def read_config(self):
global_config = configparser.ConfigParser()

# read the global config file
global_config.read(global_config_filepath)
self.global_config.read(global_config_filepath)

# initialise sections if they don't exist already
for section in global_config_sections:
if section not in global_config.sections():
global_config[section] = {}
if section not in self.global_config.sections():
self.global_config[section] = {}

self.debug_msg(f"Read global config file from {global_config_filepath}")

self.global_config = global_config

# read local config file
self.local_config = local_config.read_config_file()

Expand Down Expand Up @@ -145,3 +141,15 @@ def wrapper(click_context, *args, **kwargs):
raise click.Abort()

return functools.update_wrapper(wrapper, f)


def project_found(f):
@pass_context
def wrapper(click_context, *args, **kwargs):
deploifai_context = click_context.find_object(DeploifaiContextObj)
if deploifai_context.local_config is not None:
return click_context.invoke(f, *args, **kwargs)

raise local_config.DeploifaiNotInitialisedError("Deploifai project not found")

return functools.update_wrapper(wrapper, f)
7 changes: 6 additions & 1 deletion deploifai/data/info.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import click

from deploifai.context import pass_deploifai_context_obj, DeploifaiContextObj
from deploifai.context import (
pass_deploifai_context_obj,
DeploifaiContextObj,
project_found,
)


@click.command()
@pass_deploifai_context_obj
@project_found
def info(context: DeploifaiContextObj):
data_storage_config = context.local_config["DATA_STORAGE"]

Expand Down
7 changes: 6 additions & 1 deletion deploifai/project/browse.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@
import requests

from deploifai.api import DeploifaiAPIError
from deploifai.context import pass_deploifai_context_obj, DeploifaiContextObj
from deploifai.context import (
pass_deploifai_context_obj,
DeploifaiContextObj,
project_found,
)
from deploifai.utilities.frontend_routing import get_project_route


@click.command()
@pass_deploifai_context_obj
@project_found
@click.option("--workspace", help="Workspace name", type=str)
@click.option("--project", help="Project name", type=str)
def browse(context: DeploifaiContextObj, project: str, workspace="unassigned"):
Expand Down
Loading