Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Azure STS deployment support #10869

Merged
merged 7 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 26 additions & 0 deletions conf/deployment/azure/ipi_3az_rhcos_sts_3m_3w.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
DEPLOYMENT:
openshift_install_timeout: 4800
allow_lower_instance_requirements: false
sts_enabled: true
subscription_plan_approval: "Manual"
ENV_DATA:
platform: 'azure'
deployment_type: 'ipi'
region: 'eastus'
azure_base_domain_resource_group_name: 'odfqe'
worker_availability_zones:
- '1'
- '2'
- '3'
master_availability_zones:
- '1'
- '2'
- '3'
worker_replicas: 3
master_replicas: 3
master_instance_type: 'Standard_D8s_v3'
worker_instance_type: 'Standard_D16s_v3'
REPORTING:
polarion:
deployment_id: 'OCS-6319'
20 changes: 20 additions & 0 deletions docs/supported_platforms.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,26 @@ own domain name.

#### UPI

#### IPI

##### STS Enabled

In order to enable STS (short-lived token auth) for a cluster deployed to Azure,
you will need to use a config like `conf/deployment/azure/ipi_3az_rhcos_sts_3m_3w.yaml`.

In addition to the standard Azure IPI config items, the following are important here:

```yaml
---
DEPLOYMENT:
sts_enabled: true
subscription_plan_approval: "Manual"
ENV_DATA:
azure_base_domain_resource_group_name: 'odfqe'
```

`sts_enabled: true` is also important for teardown in order to clean up resources in Azure.

## IBM Cloud

This platform supports deployment of OCP cluster + OCS cluster on top of it.
Expand Down
2 changes: 1 addition & 1 deletion ocs_ci/deployment/aws.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ def sts_setup(self):
release_image = get_ocp_release_image_from_installer()
cco_image = cco.get_cco_container_image(release_image, pull_secret_path)
cco.extract_ccoctl_binary(cco_image, pull_secret_path)
cco.extract_credentials_requests_aws(
cco.extract_credentials_requests(
release_image,
install_config,
pull_secret_path,
Expand Down
78 changes: 74 additions & 4 deletions ocs_ci/deployment/azure.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@
on Azure platform.
"""

import logging
import json
import logging
import os
import shutil

from ocs_ci.framework import config
from ocs_ci.deployment.cloud import CloudDeploymentBase
from ocs_ci.deployment.cloud import IPIOCPDeployment
from ocs_ci.deployment.ocp import OCPDeployment as BaseOCPDeployment
from ocs_ci.utility import version
from ocs_ci.ocs import constants
from ocs_ci.utility import cco, version
from ocs_ci.utility.azure_utils import AZURE as AzureUtil, AzureAroUtil
from ocs_ci.utility.deployment import get_ocp_release_image_from_installer
from ocs_ci.utility.utils import exec_cmd

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -78,12 +82,78 @@ class AZUREIPI(AZUREBase):
A class to handle Azure IPI specific deployment.
"""

OCPDeployment = IPIOCPDeployment

def __init__(self):
self.name = self.__class__.__name__
super(AZUREIPI, self).__init__()

class OCPDeployment(IPIOCPDeployment):
def deploy_prereq(self):
super().deploy_prereq()
if config.DEPLOYMENT.get("sts_enabled"):
self.sts_setup()

def sts_setup(self):
"""
Perform setup procedure for STS Mode deployments.
"""
cluster_path = config.ENV_DATA["cluster_path"]
output_dir = os.path.join(cluster_path, "output-dir")
pull_secret_path = os.path.join(constants.DATA_DIR, "pull-secret")
credentials_requests_dir = os.path.join(cluster_path, "creds_reqs")
install_config = os.path.join(cluster_path, "install-config.yaml")

release_image = get_ocp_release_image_from_installer()
cco_image = cco.get_cco_container_image(release_image, pull_secret_path)
cco.extract_ccoctl_binary(cco_image, pull_secret_path)
cco.extract_credentials_requests(
release_image,
install_config,
pull_secret_path,
credentials_requests_dir,
)
cco.set_credentials_mode_manual(install_config)
cco.set_resource_group_name(install_config, self.cluster_name)
cco.create_manifests(self.installer, cluster_path)
azure_util = AzureUtil()
azure_util.set_auth_env_vars()
cco.process_credentials_requests_azure(
self.cluster_name,
config.ENV_DATA["region"],
credentials_requests_dir,
output_dir,
config.AUTH["azure_auth"]["subscription_id"],
config.ENV_DATA["azure_base_domain_resource_group_name"],
config.AUTH["azure_auth"]["tenant_id"],
)
manifests_source_dir = os.path.join(output_dir, "manifests")
manifests_target_dir = os.path.join(cluster_path, "manifests")
file_names = os.listdir(manifests_source_dir)
for file_name in file_names:
shutil.move(
os.path.join(manifests_source_dir, file_name), manifests_target_dir
)

tls_source_dir = os.path.join(output_dir, "tls")
tls_target_dir = os.path.join(cluster_path, "tls")
shutil.move(tls_source_dir, tls_target_dir)

def destroy_cluster(self, log_level="DEBUG"):
"""
Destroy OCP cluster specific to Azure IPI

Args:
log_level (str): log level openshift-installer (default: DEBUG)

"""
if config.DEPLOYMENT.get("sts_enabled"):
self.azure_util.set_auth_env_vars()
cco.delete_oidc_resource_group(
self.cluster_name,
config.ENV_DATA["region"],
config.AUTH["azure_auth"]["subscription_id"],
)
super(AZUREIPI, self).destroy_cluster(log_level)

# For Azure IPI there is no need to implement custom:
# - deploy_ocp() method (as long as we don't tweak host network)

Expand Down
32 changes: 29 additions & 3 deletions ocs_ci/deployment/deployment.py
Original file line number Diff line number Diff line change
Expand Up @@ -901,8 +901,16 @@ def subscribe_ocs(self):

"""
live_deployment = config.DEPLOYMENT.get("live_deployment")
platform = config.ENV_DATA["platform"]
aws_sts_deployment = (
config.DEPLOYMENT.get("sts_enabled") and platform == constants.AWS_PLATFORM
)
azure_sts_deployment = (
config.DEPLOYMENT.get("sts_enabled")
and platform == constants.AZURE_PLATFORM
)
managed_ibmcloud = (
config.ENV_DATA["platform"] == constants.IBMCLOUD_PLATFORM
platform == constants.IBMCLOUD_PLATFORM
and config.ENV_DATA["deployment_type"] == "managed"
)
if managed_ibmcloud and not live_deployment:
Expand Down Expand Up @@ -944,14 +952,28 @@ def subscribe_ocs(self):
subscription_yaml_data["spec"]["source"] = config.DEPLOYMENT.get(
"live_content_source", defaults.LIVE_CONTENT_SOURCE
)
if config.DEPLOYMENT.get("sts_enabled"):
if aws_sts_deployment:
if "config" not in subscription_yaml_data["spec"]:
subscription_yaml_data["spec"]["config"] = {}
role_arn_data = {"name": "ROLEARN", "value": self.sts_role_arn}
if "env" not in subscription_yaml_data["spec"]["config"]:
subscription_yaml_data["spec"]["config"]["env"] = [role_arn_data]
else:
subscription_yaml_data["spec"]["config"]["env"].append([role_arn_data])
elif azure_sts_deployment:
if "config" not in subscription_yaml_data["spec"]:
subscription_yaml_data["spec"]["config"] = {}
azure_auth_data = config.AUTH["azure_auth"]
azure_sub_data = [
{"name": "CLIENTID", "value": azure_auth_data["client_id"]},
{"name": "TENANTID", "value": azure_auth_data["tenant_id"]},
{"name": "SUBSCRIPTIONID", "value": azure_auth_data["subscription_id"]},
]
if "env" not in subscription_yaml_data["spec"]["config"]:
clacroix12 marked this conversation as resolved.
Show resolved Hide resolved
subscription_yaml_data["spec"]["config"]["env"] = azure_sub_data
else:
subscription_yaml_data["spec"]["config"]["env"].append(azure_sub_data)

subscription_yaml_data["metadata"]["namespace"] = self.namespace
subscription_manifest = tempfile.NamedTemporaryFile(
mode="w+", prefix="subscription_manifest", delete=False
Expand Down Expand Up @@ -1062,6 +1084,10 @@ def deploy_ocs_via_operator(self, image=None):
live_deployment = config.DEPLOYMENT.get("live_deployment")
arbiter_deployment = config.DEPLOYMENT.get("arbiter_deployment")
local_storage = config.DEPLOYMENT.get("local_storage")
platform = config.ENV_DATA.get("platform").lower()
aws_sts_deployment = (
config.DEPLOYMENT.get("sts_enabled") and platform == constants.AWS_PLATFORM
)

if ui_deployment and ui_deployment_conditions():
log_step("Start ODF deployment with UI")
Expand All @@ -1072,7 +1098,7 @@ def deploy_ocs_via_operator(self, image=None):
log_step("Deployment of OCS via OCS operator")
self.label_and_taint_nodes()

if config.DEPLOYMENT.get("sts_enabled"):
if aws_sts_deployment:
log_step("Create STS role and attach AmazonS3FullAccess Policy")
role_data = create_and_attach_sts_role()
self.sts_role_arn = role_data["Role"]["Arn"]
Expand Down
13 changes: 11 additions & 2 deletions ocs_ci/utility/azure_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
)
from ocs_ci.utility.ssl_certs import configure_ingress_and_api_certificates

logger = logging.getLogger(name=__file__)
logger = logging.getLogger(__name__)


# default location of files with necessary azure cluster details
Expand Down Expand Up @@ -228,7 +228,7 @@ def resource_client(self):
"""
if not self._resource_client:
self._resource_client = ResourceManagementClient(
credentials=self.credentials, subscription_id=self._subscription_id
credential=self.credentials, subscription_id=self._subscription_id
)
return self._resource_client

Expand Down Expand Up @@ -484,6 +484,15 @@ def az_login(self):
)
exec_cmd(login_cmd, secrets=[self._client_secret, self._tenant_id])

def set_auth_env_vars(self):
"""
Set environment variables containing auth information.
"""
logger.info("Setting Azure environment variables")
os.environ["AZURE_TENANT_ID"] = self._tenant_id
os.environ["AZURE_CLIENT_ID"] = self._client_id
os.environ["AZURE_CLIENT_SECRET"] = self._client_secret


class AzureAroUtil(AZURE):
"""
Expand Down
76 changes: 74 additions & 2 deletions ocs_ci/utility/cco.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import logging
import os
import re
import shutil
import yaml

Expand Down Expand Up @@ -70,11 +71,11 @@ def extract_credentials_requests_ibmcloud(
exec_cmd(cmd)


def extract_credentials_requests_aws(
def extract_credentials_requests(
release_image, install_config, pull_secret, credentials_requests_dir
):
"""
Extract the CredentialsRequests (AWS STS variant).
Extract the CredentialsRequests (AWS and Azure STS variant).

Args:
release_image (str): Release image from the openshift installer
Expand Down Expand Up @@ -204,6 +205,40 @@ def process_credentials_requests_aws(
exec_cmd(cmd)


def process_credentials_requests_azure(
name,
azure_region,
credentials_requests_dir,
output_dir,
subscription_id,
dns_zone_group_name,
tenant_id,
):
"""
Process all CredentialsRequest objects.

Args:
name (str): Name used to tag any created cloud resources
azure_region (str): Region to create cloud resources
credentials_requests_dir (str): Path to the CredentialsRequest directory
output_dir (str): Path to the output directory
subscription_id (str): Service Principal Subscription ID
dns_zone_group_name (str): Name of the DNS Zone
tenant_id (str): Service Principal Tenant ID

"""
logger.info("Processing all CredentialsRequest objects")
storage_account_name = re.sub(r"\W+", "", name) # Strip non-alphanumeric characters
cmd = (
f"ccoctl azure create-all --name={name} --output-dir={output_dir} "
f"--region={azure_region} --subscription-id={subscription_id} "
f"--credentials-requests-dir={credentials_requests_dir} "
f"--dnszone-resource-group-name={dns_zone_group_name} --tenant-id={tenant_id} "
f"--storage-account-name={storage_account_name}"
)
exec_cmd(cmd)


def set_credentials_mode_manual(install_config):
"""
Set credentialsMode to Manual in the install-config.yaml
Expand All @@ -214,3 +249,40 @@ def set_credentials_mode_manual(install_config):
install_config_data["credentialsMode"] = "Manual"
with open(install_config, "w") as f:
yaml.dump(install_config_data, f)


def set_resource_group_name(install_config, name):
"""
Set resourceGroupName to Manual in the install-config.yaml for Azure deployments.

Args:
install_config (str): Path to the install-config.yaml
name (str): Name of the Resource Group

"""
logger.info("Set resourceGroupName")
with open(install_config, "r") as f:
install_config_data = yaml.safe_load(f)
install_config_data["platform"]["azure"]["resourceGroupName"] = name
with open(install_config, "w") as f:
yaml.dump(install_config_data, f)


def delete_oidc_resource_group(name, region, subscription_id):
"""
Delete the Azure resources that ccoctl created.

Args:
name (str): Name used to tag any created cloud resources
region (str): Region to create cloud resources
subscription_id (str): Service Principal Subscription ID

"""
logger.info("Deleting OIDC resource group")
storage_account_name = re.sub(r"\W+", "", name) # Strip non-alphanumeric characters
cmd = (
f"ccoctl azure delete --name={name} --region={region} "
f"--subscription-id={subscription_id} --delete-oidc-resource-group "
f"--storage-account-name={storage_account_name}"
)
exec_cmd(cmd)
10 changes: 5 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@
"python-ipmi==0.4.2",
"scipy==1.12.0",
"PrettyTable==0.7.2",
"azure-common==1.1.25",
"azure-mgmt-compute==12.0.0",
"azure-mgmt-network==10.2.0",
"azure-mgmt-resource==10.0.0",
"azure-storage-blob==12.19.0",
"azure-common==1.1.28",
"azure-mgmt-compute==33.0.0",
"azure-mgmt-network==28.0.0",
"azure-mgmt-resource==23.2.0",
"azure-storage-blob==12.23.1",
"msrestazure==0.6.3",
"python-novaclient==17.1.0",
"python-cinderclient==7.1.0",
Expand Down
Loading