Merge pull request #1050 from dod-ccpo/cloud-provision-interface
Update CloudProviderInterface for provision job consumption
This commit is contained in:
commit
85f8c8f9e0
@ -1,84 +1,147 @@
|
||||
from typing import Dict
|
||||
from uuid import uuid4
|
||||
|
||||
from atst.models.environment_role import CSPRole
|
||||
from atst.models.user import User
|
||||
from atst.models.environment import Environment
|
||||
from atst.models.environment_role import EnvironmentRole
|
||||
|
||||
|
||||
class CloudProviderInterface:
|
||||
def create_application(self, name): # pragma: no cover
|
||||
"""Create an application in the cloud with the provided name. Returns
|
||||
the ID of the created object.
|
||||
def create_environment(
|
||||
self, auth_credentials: Dict, user: User, environment: Environment
|
||||
) -> str:
|
||||
"""Create a new environment in the CSP.
|
||||
|
||||
Arguments:
|
||||
auth_credentials -- Object containing CSP account credentials
|
||||
user -- ATAT user authorizing the environment creation
|
||||
environment -- ATAT Environment model
|
||||
|
||||
Returns:
|
||||
string: ID of created environment
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def delete_application(self, cloud_id): # pragma: no cover
|
||||
"""Delete an application in the cloud with the provided cloud_id. Returns
|
||||
True for success or raises an error.
|
||||
def create_atat_admin_user(
|
||||
self, auth_credentials: Dict, csp_environment_id: str
|
||||
) -> Dict:
|
||||
"""Creates a new, programmatic user in the CSP. Grants this user full permissions to administer
|
||||
the CSP.
|
||||
|
||||
Arguments:
|
||||
auth_credentials -- Object containing CSP account credentials
|
||||
csp_environment_id -- ID of the CSP Environment the admin user should be created in
|
||||
|
||||
Returns:
|
||||
object: Object representing new remote admin user, including credentials
|
||||
Something like:
|
||||
{
|
||||
"user_id": string,
|
||||
"credentials": dict, # structure TBD based on csp
|
||||
}
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def create_user(self, user): # pragma: no cover
|
||||
"""Create an account in the CSP for specified user. Returns the ID of
|
||||
the created user.
|
||||
def create_environment_baseline(
|
||||
self, auth_credentials: Dict, csp_environment_id: str
|
||||
) -> Dict:
|
||||
"""Provision the necessary baseline entities (such as roles) in the given environment
|
||||
|
||||
Arguments:
|
||||
auth_credentials -- Object containing CSP account credentials
|
||||
csp_environment_id -- ID of the CSP Environment to provision roles against.
|
||||
|
||||
Returns:
|
||||
dict: Returns dict that associates the resource identities with their ATAT representations.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def create_role(self, environment_role): # pragma: no cover
|
||||
"""Takes an `atst.model.EnvironmentRole` object and allows the
|
||||
specified user access to the specified cloud entity.
|
||||
def create_or_update_user(
|
||||
self, auth_credentials: Dict, user_info: EnvironmentRole, csp_role_id: str
|
||||
) -> str:
|
||||
"""Creates a user or updates an existing user's role.
|
||||
|
||||
This _does not_ return a token, but is intended to perform any necessary
|
||||
setup to allow a token to be generated in the future (for example,
|
||||
add the user to the cloud entity in some fashion).
|
||||
Arguments:
|
||||
auth_credentials -- Object containing CSP account credentials
|
||||
user_info -- instance of EnvironmentRole containing user data
|
||||
if it has a csp_user_id it will try to update that user
|
||||
csp_role_id -- The id of the role the user should be given in the CSP
|
||||
|
||||
Returns:
|
||||
string: Returns the interal csp_user_id of the created/updated user account
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def delete_role(self, environment_role): # pragma: no cover
|
||||
"""Takes an `atst.model.EnvironmentRole` object and performs any action
|
||||
necessary in the CSP to remove the specified user from the specified
|
||||
environment. This method does not return anything.
|
||||
def suspend_user(self, auth_credentials: Dict, csp_user_id: str) -> bool:
|
||||
"""Revoke all privileges for a user. Used to prevent user access while a full
|
||||
delete is being processed.
|
||||
|
||||
Arguments:
|
||||
auth_credentials -- Object containing CSP account credentials
|
||||
csp_user_id -- CSP internal user identifier
|
||||
|
||||
Returns:
|
||||
bool -- True on success
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_access_token(self, environment_role): # pragma: no cover
|
||||
"""Takes an `atst.model.EnvironmentRole` object and returns a federated
|
||||
access token that gives the specified user access to the specified
|
||||
environment with the proper permissions.
|
||||
def delete_user(self, auth_credentials: Dict, csp_user_id: str) -> bool:
|
||||
"""Given the csp-internal id for a user, initiate user deletion.
|
||||
|
||||
Arguments:
|
||||
auth_credentials -- Object containing CSP account credentials
|
||||
csp_user_id -- CSP internal user identifier
|
||||
|
||||
Returns:
|
||||
bool -- True on success
|
||||
|
||||
Raises:
|
||||
TBDException: Some part of user deletion failed
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def calculator_url(self): # pragma: no cover
|
||||
"""Returns a URL for the CSP's estimate calculator."""
|
||||
def get_calculator_url(self) -> str:
|
||||
"""Returns the calculator url for the CSP.
|
||||
This will likely be a static property elsewhere once a CSP is chosen.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_environment_login_url(self, environment) -> str:
|
||||
"""Returns the login url for a given environment
|
||||
This may move to be a computed property on the Environment domain object
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class MockCloudProvider(CloudProviderInterface):
|
||||
def create_application(self, name):
|
||||
"""Returns an id that represents what would be an application in the
|
||||
cloud."""
|
||||
def create_environment(self, auth_credentials, user, environment):
|
||||
return uuid4().hex
|
||||
|
||||
def delete_application(self, name):
|
||||
"""Returns an id that represents what would be an application in the
|
||||
cloud."""
|
||||
return True
|
||||
def create_atat_admin_user(self, auth_credentials, csp_environment_id):
|
||||
return {"id": uuid4().hex, "credentials": {}}
|
||||
|
||||
def create_user(self, user):
|
||||
"""Returns an id that represents what would be an user in the cloud."""
|
||||
return uuid4().hex
|
||||
def create_environment_baseline(self, auth_credentials, csp_environment_id):
|
||||
return {
|
||||
CSPRole.BASIC_ACCESS: uuid4().hex,
|
||||
CSPRole.NETWORK_ADMIN: uuid4().hex,
|
||||
CSPRole.BUSINESS_READ: uuid4().hex,
|
||||
CSPRole.TECHNICAL_READ: uuid4().hex,
|
||||
}
|
||||
|
||||
def create_role(self, environment_role):
|
||||
# Currently, there is nothing to mock out, so just do nothing.
|
||||
def create_or_update_user(self, auth_credentials, user_info, csp_role_id):
|
||||
return {"id": uuid4().hex}
|
||||
|
||||
def suspend_user(self, auth_credentials, csp_user_id):
|
||||
pass
|
||||
|
||||
def delete_role(self, environment_role):
|
||||
# Currently nothing to do.
|
||||
def delete_user(self, auth_credentials, csp_user_id):
|
||||
pass
|
||||
|
||||
def get_access_token(self, environment_role):
|
||||
# for now, just create a mock token using the user and environment
|
||||
# cloud IDs and the name of the role in the environment
|
||||
user_id = environment_role.application_role.user.cloud_id or ""
|
||||
env_id = environment_role.environment.cloud_id or ""
|
||||
role_details = environment_role.role
|
||||
return "::".join([user_id, env_id, role_details])
|
||||
|
||||
def calculator_url(self):
|
||||
def get_calculator_url(self):
|
||||
return "https://www.rackspace.com/en-us/calculator"
|
||||
|
||||
def get_environment_login_url(self, environment):
|
||||
"""Returns the login url for a given environment
|
||||
"""
|
||||
return "https://www.mycloud.com/my-env-login"
|
||||
|
@ -1,5 +1,3 @@
|
||||
from flask import current_app as app
|
||||
|
||||
from atst.database import db
|
||||
from atst.models import EnvironmentRole, ApplicationRole
|
||||
|
||||
@ -10,10 +8,6 @@ class EnvironmentRoles(object):
|
||||
env_role = EnvironmentRole(
|
||||
application_role=application_role, environment=environment, role=role
|
||||
)
|
||||
# TODO: move cloud_id behavior to invitation acceptance
|
||||
# if not user.cloud_id:
|
||||
# user.cloud_id = app.csp.cloud.create_user(user)
|
||||
app.csp.cloud.create_role(env_role)
|
||||
return env_role
|
||||
|
||||
@classmethod
|
||||
@ -45,7 +39,7 @@ class EnvironmentRoles(object):
|
||||
def delete(cls, application_role_id, environment_id):
|
||||
existing_env_role = EnvironmentRoles.get(application_role_id, environment_id)
|
||||
if existing_env_role:
|
||||
app.csp.cloud.delete_role(existing_env_role)
|
||||
# TODO: Set status to pending_delete
|
||||
db.session.delete(existing_env_role)
|
||||
db.session.commit()
|
||||
return True
|
||||
|
@ -1,4 +1,3 @@
|
||||
from flask import current_app as app
|
||||
from sqlalchemy.orm.exc import NoResultFound
|
||||
|
||||
from atst.database import db
|
||||
@ -13,7 +12,6 @@ class Environments(object):
|
||||
@classmethod
|
||||
def create(cls, application, name):
|
||||
environment = Environment(application=application, name=name)
|
||||
environment.cloud_id = app.csp.cloud.create_application(environment.name)
|
||||
db.session.add(environment)
|
||||
db.session.commit()
|
||||
return environment
|
||||
@ -101,6 +99,6 @@ class Environments(object):
|
||||
if commit:
|
||||
db.session.commit()
|
||||
|
||||
app.csp.cloud.delete_application(environment.cloud_id)
|
||||
# TODO: How do we work around environment deletion being a largely manual process in the CSPs
|
||||
|
||||
return environment
|
||||
|
@ -131,4 +131,4 @@ def csp_environment_access():
|
||||
|
||||
@bp.route("/jedi-csp-calculator")
|
||||
def jedi_csp_calculator():
|
||||
return redirect(app.csp.cloud.calculator_url())
|
||||
return redirect(app.csp.cloud.get_calculator_url())
|
||||
|
@ -30,6 +30,6 @@ def access_environment(environment_id):
|
||||
env_role = EnvironmentRoles.get_by_user_and_environment(
|
||||
g.current_user.id, environment_id
|
||||
)
|
||||
token = app.csp.cloud.get_access_token(env_role)
|
||||
login_url = app.csp.cloud.get_environment_login_url(env_role.environment)
|
||||
|
||||
return redirect(url_for("atst.csp_environment_access", token=token))
|
||||
return redirect(url_for("atst.csp_environment_access", login_url=login_url))
|
||||
|
@ -1,5 +1,4 @@
|
||||
import pytest
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from atst.domain.environment_roles import EnvironmentRoles
|
||||
|
||||
@ -19,10 +18,6 @@ def environment(application_role):
|
||||
|
||||
|
||||
def test_create(application_role, environment, monkeypatch):
|
||||
mock_create_role = MagicMock()
|
||||
monkeypatch.setattr(
|
||||
"atst.domain.environment_roles.app.csp.cloud.create_role", mock_create_role
|
||||
)
|
||||
|
||||
environment_role = EnvironmentRoles.create(
|
||||
application_role, environment, "network admin"
|
||||
@ -30,7 +25,6 @@ def test_create(application_role, environment, monkeypatch):
|
||||
assert environment_role.application_role == application_role
|
||||
assert environment_role.environment == environment
|
||||
assert environment_role.role == "network admin"
|
||||
mock_create_role.assert_called_with(environment_role)
|
||||
|
||||
|
||||
def test_get(application_role, environment):
|
||||
@ -55,16 +49,10 @@ def test_get_by_user_and_environment(application_role, environment):
|
||||
|
||||
|
||||
def test_delete(application_role, environment, monkeypatch):
|
||||
mock_delete_role = MagicMock()
|
||||
monkeypatch.setattr(
|
||||
"atst.domain.environment_roles.app.csp.cloud.delete_role", mock_delete_role
|
||||
)
|
||||
|
||||
environment_role = EnvironmentRoleFactory.create(
|
||||
EnvironmentRoleFactory.create(
|
||||
application_role=application_role, environment=environment
|
||||
)
|
||||
assert EnvironmentRoles.delete(application_role.id, environment.id)
|
||||
mock_delete_role.assert_called_with(environment_role)
|
||||
assert not EnvironmentRoles.delete(application_role.id, environment.id)
|
||||
|
||||
|
||||
|
@ -15,6 +15,7 @@ from tests.factories import (
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Reinstate and update once jobs api is up")
|
||||
def test_create_environments():
|
||||
application = ApplicationFactory.create()
|
||||
environments = Environments.create_many(application, ["Staging", "Production"])
|
||||
|
Loading…
x
Reference in New Issue
Block a user