All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ai.digital.integration.server.common.cluster.setup.AzureAksHelper.kt Maven / Gradle / Ivy

There is a newer version: 23.3.0-1025.941
Show newest version
package ai.digital.integration.server.common.cluster.setup

import ai.digital.integration.server.common.cluster.Helper
import ai.digital.integration.server.common.constant.ClusterProfileName
import ai.digital.integration.server.common.constant.ProductName
import ai.digital.integration.server.common.domain.profiles.HelmProfile
import ai.digital.integration.server.common.domain.profiles.OperatorProfile
import ai.digital.integration.server.common.domain.profiles.Profile
import ai.digital.integration.server.common.domain.providers.AzureAksProvider
import ai.digital.integration.server.common.util.ProcessUtil
import org.gradle.api.Project
import org.gradle.api.provider.Property

open class AzureAksHelper(project: Project, productName: ProductName, val profile: Profile) : Helper(project, productName) {

    override fun getProvider(): AzureAksProvider {
        return when (val profileName = getProfileName()) {
            ClusterProfileName.OPERATOR.profileName -> {
                val operatorProfile = profile as OperatorProfile
                operatorProfile.azureAks
            }
            ClusterProfileName.HELM.profileName -> {
                val helmProfile = profile as HelmProfile
                helmProfile.azureAks
            }
            else -> {
                throw IllegalArgumentException("Provided profile name `$profileName` is not supported")
            }
        }
    }



    fun launchCluster() {
        val azureAksProvider: AzureAksProvider = getProvider()
        val name = azureAksProvider.name.get()
        val skipExisting = azureAksProvider.skipExisting.get()
        val location = azureAksProvider.location.get()

        validateAzCli()
        loginAzCli(azureAksProvider.getAzUsername(), azureAksProvider.getAzPassword())

        createResourceGroup(name, location, skipExisting)
        createCluster(
            name,
            azureAksProvider.clusterNodeCount,
            azureAksProvider.clusterNodeVmSize,
            azureAksProvider.kubernetesVersion,
            skipExisting
        )
        connectToCluster(name)
        createStorageClass(resourceGroupName(name), azureAksProvider.storageClass.getOrElse(name))
    }

    private fun validateAzCli() {
        val result = ProcessUtil.executeCommand(
            project,
            "az -v", throwErrorOnFailure = false, logOutput = false
        )
        if (!result.contains("azure-cli")) {
            throw RuntimeException("No azure-cli \"az\" in the path. Please verify your installation")
        }
    }

    private fun loginAzCli(username: String?, password: String?) {
        if (username != null && password != null) {
            project.logger.lifecycle("Login user")
            ProcessUtil.executeCommand(
                project,
                "az login -u $username -p $password", throwErrorOnFailure = false, logOutput = false
            )
        }
    }

    private fun createStorageClass(resourceGroupName: String, name: String) {
        val fileStorageClassName = fileStorageClassName(name)
        createStorageClassFromFile(resourceGroupName, fileStorageClassName, "operator/azure-aks/azure-file-sc.yaml")
        val diskStorageClassName = diskStorageClassName(name)
        createStorageClassFromFile(resourceGroupName, diskStorageClassName, "operator/azure-aks/azure-disk-sc.yaml")

        getKubectlHelper().setDefaultStorageClass(fileStorageClassName)
    }

    private fun diskStorageClassName(name: String): String {
        return "${name}-disk-storage-class"
    }

    private fun fileStorageClassName(name: String): String {
        return "${name}-file-storage-class"
    }

    private fun createStorageClassFromFile(resourceGroupName: String, storageClassName: String, filePath: String) {
        if (!getKubectlHelper().hasStorageClass(storageClassName)) {
            project.logger.lifecycle("Create storage class: {}", storageClassName)
            val azureFileScTemplateFile = getTemplate(filePath)
            val azureFileScTemplate = azureFileScTemplateFile.readText(Charsets.UTF_8)
                .replace("{{NAME}}", storageClassName)
                .replace("{{RESOURCE_GROUP}}", resourceGroupName)
            azureFileScTemplateFile.writeText(azureFileScTemplate)
            getKubectlHelper().applyFile(azureFileScTemplateFile)
        } else {
            project.logger.lifecycle("Skipping creation of the existing storage class: {}", storageClassName)
        }
    }

    private fun createCluster(
        name: String,
        clusterNodeCount: Property,
        clusterNodeVmSize: Property,
        kubernetesVersion: Property,
        skipExisting: Boolean
    ) {
        val groupName = resourceGroupName(name)
        val clusterName = aksClusterName(name)
        val shouldSkipExisting = if (skipExisting) {
            val result = ProcessUtil.executeCommand(
                project,
                "az aks list --output tsv | grep $clusterName", throwErrorOnFailure = false, logOutput = false
            )
            result.contains(clusterName)
        } else {
            false
        }
        if (shouldSkipExisting) {
            project.logger.lifecycle("Skipping creation of the existing AKS cluster: {}", clusterName)
        } else {
            project.logger.lifecycle("Create AKS cluster: {}", clusterName)
            val additions = clusterNodeVmSize.map { " --node-vm-size \"$it\"" }.getOrElse("") +
                    kubernetesVersion.map { " --kubernetes-version \"$it\"" }.getOrElse("")
            ProcessUtil.executeCommand(
                project,
                "az aks create --resource-group $groupName --name $clusterName --node-count ${
                    clusterNodeCount.getOrElse(2)
                } " +
                        "--generate-ssh-keys --enable-addons monitoring $additions"
            )
        }
    }

    private fun connectToCluster(name: String) {
        ProcessUtil.executeCommand(
            project,
            "az aks get-credentials --resource-group ${resourceGroupName(name)} --name ${aksClusterName(name)} --overwrite-existing"
        )
    }

    private fun createResourceGroup(name: String, location: String, skipExisting: Boolean) {
        val groupName = resourceGroupName(name)
        val shouldSkipExisting = if (skipExisting) {
            existsResourceGroup(groupName, location)
        } else {
            false
        }
        if (shouldSkipExisting) {
            project.logger.lifecycle("Skipping creation of the existing resource group: {}", groupName)
        } else {
            project.logger.lifecycle("Create resource group: {}", groupName)
            ProcessUtil.executeCommand(
                project,
                "az group create --name $groupName --location $location"
            )
        }
    }

    fun resourceGroupName(name: String): String {
        return "${name}-group"
    }

    fun existsResourceGroup(groupName: String, location: String): Boolean {
        val result = ProcessUtil.executeCommand(
            project,
            "az group list --query \"[?location=='$location']\" --output tsv | grep $groupName",
            throwErrorOnFailure = false,
            logOutput = false
        )
        return result.contains(groupName)
    }

    private fun aksClusterName(name: String): String {
        return name
    }

    private fun deleteResourceGroup(name: String, groupName: String, location: String) {
        val clusterName = aksClusterName(name)
        project.logger.lifecycle("Delete resource group {} and AKS cluster {} ", groupName, clusterName)
        if (existsResourceGroup(groupName, location)) {
            project.logger.lifecycle("Delete resource group: {}", groupName)
            ProcessUtil.executeCommand(
                project,
                "az group delete --name $groupName --yes"
            )
        } else {
            project.logger.lifecycle("Skipping delete of the resource group: {}", groupName)
        }
    }

    private fun logoutAzCli(username: String?, password: String?) {
        if (username != null && password != null) {
            project.logger.lifecycle("Logout user")
            ProcessUtil.executeCommand(
                project,
                "az logout", throwErrorOnFailure = false
            )
        }
    }

    fun destroyClusterOnShutdown(existsResourceGroup: Boolean, name: String, groupName: String, location: String) {
        val azureAksProvider: AzureAksProvider = getProvider()

        if (azureAksProvider.destroyClusterOnShutdown.get()) {
            if (existsResourceGroup) {
                deleteResourceGroup(name, groupName, location)
            }

            getKubectlHelper().deleteCurrentContext()
            logoutAzCli(azureAksProvider.getAzUsername(), azureAksProvider.getAzPassword())
        }
    }

    override fun getFqdn(): String {
        val azureAksProvider: AzureAksProvider = getProvider()
        val location = azureAksProvider.location.get()
        return "${getHost()}.${location}.cloudapp.azure.com"
    }

    override fun getStorageClass(): String {
        return fileStorageClassName(getProvider().storageClass.getOrElse(getProvider().name.get()))
    }

    override fun getDbStorageClass(): String {
        return diskStorageClassName(getProvider().storageClass.getOrElse(getProvider().name.get()))
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy