Skip to content

Commit

Permalink
[4.17] Test STS support for MCG clients (red-hat-storage#10117)
Browse files Browse the repository at this point in the history
Signed-off-by: Mahesh Shetty <[email protected]>
Co-authored-by: Mahesh Shetty <[email protected]>
  • Loading branch information
mashetty330 and Mahesh Shetty authored Nov 11, 2024
1 parent d501381 commit 3a04c37
Show file tree
Hide file tree
Showing 7 changed files with 649 additions and 1 deletion.
170 changes: 170 additions & 0 deletions ocs_ci/ocs/bucket_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,61 @@ def craft_s3_command(cmd, mcg_obj=None, api=False, signed_request_creds=None):
return f"{base_command}{cmd}{string_wrapper}"


def craft_sts_command(cmd, mcg_obj=None, signed_request_creds=None):
"""
Crafts the AWS CLI STS command including the
login credentials and command to be ran
Args:
cmd: The AWSCLI STS command to run
mcg_obj: An MCG class instance
signed_request_creds: a dictionary containing AWS S3 creds for a signed request
Returns:
str: The crafted command, ready to be executed on the pod
"""

no_ssl = (
"--no-verify-ssl"
if signed_request_creds and signed_request_creds.get("ssl") is False
else ""
)
if mcg_obj:
if mcg_obj.region:
region = f"AWS_DEFAULT_REGION={mcg_obj.region} "
else:
region = ""
base_command = (
f'sh -c "AWS_CA_BUNDLE={constants.SERVICE_CA_CRT_AWSCLI_PATH} '
f"AWS_ACCESS_KEY_ID={mcg_obj.access_key_id} "
f"AWS_SECRET_ACCESS_KEY={mcg_obj.access_key} "
f"{region}"
f"aws sts "
f"--endpoint={mcg_obj.sts_internal_endpoint} "
)
string_wrapper = '"'
elif signed_request_creds:
if signed_request_creds.get("region"):
region = f'AWS_DEFAULT_REGION={signed_request_creds.get("region")} '
else:
region = ""
base_command = (
f'sh -c "AWS_ACCESS_KEY_ID={signed_request_creds.get("access_key_id")} '
f'AWS_SECRET_ACCESS_KEY={signed_request_creds.get("access_key")} '
f"{region}"
f"aws sts "
f'--endpoint={signed_request_creds.get("endpoint")} '
f"{no_ssl} "
)
string_wrapper = '"'
else:
base_command = "aws sts --no-sign-request "
string_wrapper = ""

return f"{base_command}{cmd}{string_wrapper}"


def craft_s3cmd_command(cmd, mcg_obj=None, signed_request_creds=None):
"""
Crafts the S3cmd CLI command including the
Expand Down Expand Up @@ -2739,3 +2794,118 @@ def map_objects_to_owners(mcg_obj, bucket_name, prefix=""):
"""
response = s3_list_objects_v2(mcg_obj, bucket_name, prefix=prefix, fetch_owner=True)
return {item["Key"]: item["Owner"] for item in response.get("Contents", [])}


def sts_assume_role(
pod_obj,
role_name,
access_key_id_assumed_user,
role_session_name=None,
mcg_obj=None,
signed_request_creds=None,
):
"""
Aws s3 assume role of an User
Args:
role_name (str): Role name of a role attached to the assumed user
access_key_id_assumed_user (str): Access key id of the assumed user
mcg_obj (MCG): MCG object
signed_request_creds (dict): a dictionary containing AWS S3 creds for a signed request
Returns:
Dict: Representing the output of the command which on successful execution
consists of new credentials
"""
if not role_session_name:
role_session_name = f"role-session-{uuid4().hex}"
cmd = (
f"assume-role --role-arn arn:aws:sts::{access_key_id_assumed_user}:role/{role_name} "
f"--role-session-name {role_session_name}"
)
cmd = craft_sts_command(
cmd, mcg_obj=mcg_obj, signed_request_creds=signed_request_creds
)
return pod_obj.exec_cmd_on_pod(command=cmd)


def s3_create_bucket(s3_obj, bucket_name, s3_client=None):
"""
AWS s3 create bucket
Args:
s3_obj (MCG): MCG object
bucket_name (str): Name of the bucket
s3_client (S3.Client): Any S3 client resource
"""
if s3_client:
s3_client.create_bucket(Bucket=bucket_name)
else:
s3_obj.s3_resource.create_bucket(Bucket=bucket_name)


def s3_delete_bucket(s3_obj, bucket_name, s3_client=None):
"""
AWS s3 delete bucket
Args:
s3_obj (MCG): MCG object
bucket_name (str): Name of the bucket
s3_client (S3.Client): Any s3 client resource
"""
if s3_client:
s3_client.delete_bucket(Bucket=bucket_name)
else:
s3_obj.s3_client.delete_bucket(Bucket=bucket_name)


def s3_list_buckets(s3_obj, s3_client=None):
"""
AWS S3 list buckets
Args:
s3_obj (MCG): MCG object
s3_client (S3.Client): Any s3 client resource
Returns:
List: List of buckets
"""

if s3_client:
response = s3_client.list_buckets()
else:
response = s3_obj.s3_client.list_buckets()

return [bucket["Name"] for bucket in response["Buckets"]]


def create_s3client_from_assume_role_creds(mcg_obj, assume_role_creds):
"""
Create s3client from the creds passed and endpoint fetched from MCG object
Args:
mcg_obj (MCG): MCG object
creds (Dict): Dictionary representing the credentials
Returns:
Boto3 s3 client object
"""

assumed_access_key_id = assume_role_creds.get("Credentials").get("AccessKeyId")
assumed_access_key = assume_role_creds.get("Credentials").get("SecretAccessKey")
assumed_session_token = assume_role_creds.get("Credentials").get("SessionToken")

assumed_s3_resource = boto3.resource(
"s3",
verify=retrieve_verification_mode(),
endpoint_url=mcg_obj.s3_endpoint,
aws_access_key_id=assumed_access_key_id,
aws_secret_access_key=assumed_access_key,
aws_session_token=assumed_session_token,
)
return assumed_s3_resource.meta.client
4 changes: 4 additions & 0 deletions ocs_ci/ocs/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@
BUCKET_LOG_REPLICATOR_DELAY_PARAM = CONFIG_JS_PREFIX + "BUCKET_LOG_REPLICATOR_DELAY"
LIFECYCLE_INTERVAL_PARAM = CONFIG_JS_PREFIX + "LIFECYCLE_INTERVAL"
BUCKET_LOG_UPLOADER_DELAY_PARAM = CONFIG_JS_PREFIX + "BUCKET_LOG_UPLOADER_DELAY"
STS_DEFAULT_SESSION_TOKEN_EXPIRY_MS = (
CONFIG_JS_PREFIX + "STS_DEFAULT_SESSION_TOKEN_EXPIRY_MS"
)

# Resources / Kinds
CEPHFILESYSTEM = "CephFileSystem"
Expand Down Expand Up @@ -235,6 +238,7 @@
OPERATOR_KIND = "Operator"
DRIVER = "Driver"
IMAGECONTENTSOURCEPOLICY_KIND = "ImageContentSourcePolicy"
NOOBAA_ACCOUNT = "NoobaaAccount"

# Provisioners
AWS_EFS_PROVISIONER = "openshift.org/aws-efs"
Expand Down
18 changes: 18 additions & 0 deletions ocs_ci/ocs/resources/bucket_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def __init__(
mcg,
name,
email,
allow_bucket_creation=True,
buckets=None,
admin_access=False,
s3_access=True,
Expand All @@ -79,6 +80,7 @@ def __init__(
"""
self.account_name = name
self.email_id = email
self.mcg = mcg
if buckets:
params_dict = {
"email": email,
Expand All @@ -99,6 +101,9 @@ def __init__(
"s3_access": s3_access,
"default_pool": backingstore_name,
}

if not allow_bucket_creation:
params_dict["allow_bucket_creation"] = allow_bucket_creation
(
params_dict
if (version.get_semantic_ocs_version_from_config() < version.VERSION_4_9)
Expand Down Expand Up @@ -128,6 +133,19 @@ def __init__(
aws_secret_access_key=self.access_key,
)

def delete_account(self):
"""
Delete the noobaa account
Returns:
Response for noobaa `delete_account` api call
"""
params_dict = {"email": self.email_id}
return self.mcg.send_rpc_query(
api="account_api", method="delete_account", params=params_dict
)


def gen_bucket_policy(
user_list,
Expand Down
78 changes: 77 additions & 1 deletion ocs_ci/ocs/resources/mcg.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@
UnsupportedPlatformError,
)
from ocs_ci.ocs.ocp import OCP
from ocs_ci.ocs.resources.pod import get_pods_having_label, Pod
from ocs_ci.ocs.resources.pod import (
get_pods_having_label,
Pod,
)
from ocs_ci.utility import templating, version
from ocs_ci.utility.retry import retry
from ocs_ci.utility.utils import (
Expand Down Expand Up @@ -119,6 +122,20 @@ def __init__(self, *args, **kwargs):
.get("serviceS3")
.get("internalDNS")[0]
)
self.sts_endpoint = (
get_noobaa.get("items")[0]
.get("status")
.get("services")
.get("serviceSts")
.get("externalDNS")[0]
)
self.sts_internal_endpoint = (
get_noobaa.get("items")[0]
.get("status")
.get("services")
.get("serviceSts")
.get("internalDNS")[0]
)
self.mgmt_endpoint = (
get_noobaa.get("items")[0]
.get("status")
Expand Down Expand Up @@ -959,6 +976,37 @@ def reset_core_pod(self):
)
wait_for_resource_state(self.core_pod, constants.STATUS_RUNNING)

def reset_endpoint_pods(self):
"""
Delete the noobaa endpoint pod and wait for it to come up again
"""

from ocs_ci.ocs.resources.pod import wait_for_pods_by_label_count

endpoint_pods = [
Pod(**pod_data)
for pod_data in get_pods_having_label(
constants.NOOBAA_ENDPOINT_POD_LABEL, self.namespace
)
]
for pod in endpoint_pods:
pod.delete(wait=True)

wait_for_pods_by_label_count(
label=constants.NOOBAA_ENDPOINT_POD_LABEL,
exptected_count=len(endpoint_pods),
)

endpoint_pods = [
Pod(**pod_data)
for pod_data in get_pods_having_label(
constants.NOOBAA_ENDPOINT_POD_LABEL, self.namespace
)
]
for pod in endpoint_pods:
wait_for_resource_state(pod, constants.STATUS_RUNNING)

def get_noobaa_admin_credentials_from_secret(self):
"""
Get the NooBaa admin credentials from the OCP secret
Expand Down Expand Up @@ -1094,3 +1142,31 @@ def get_default_bc_backingstore_name(self):
.get("tiers")[0]
.get("backingStores")[0]
)

def assign_sts_role(self, account_id, role_config):
"""
Assign STS role to a Noobaa account
Args:
account_id (str): Name/email/id of the noobaa account
role_config (dict): Role config consisting of role name, role policy etc
"""

cmd = f"sts assign-role --email {account_id} --role_config '{str(role_config)}'"
self.exec_mcg_cmd(
cmd=cmd,
)

def remove_sts_role(self, account_id):
"""
Remove STS role from a Noobaa account
Args:
account_id (str): Name/email/id of the noobaa account
"""
cmd = f"sts remove-role --email {account_id}"
self.exec_mcg_cmd(
cmd=cmd,
)
Loading

0 comments on commit 3a04c37

Please sign in to comment.