azurecontainerapps.ts (379 lines of code) (raw):

import * as fs from 'fs'; import * as path from 'path'; import { ContainerAppHelper } from './src/ContainerAppHelper'; import { ContainerRegistryHelper } from './src/ContainerRegistryHelper'; import { TelemetryHelper } from './src/TelemetryHelper'; import { Utility } from './src/Utility'; import { GitHubActionsToolHelper } from './src/GitHubActionsToolHelper'; const buildArgumentRegex = /"[^"]*"|\S+/g; const buildpackEnvironmentNameRegex = /^"?(BP|ORYX)_[-._a-zA-Z0-9]+"?$/ export class azurecontainerapps { public static async runMain(): Promise<void> { this.initializeHelpers(); try { // Validate that the arguments provided can be used for one of the supported scenarios this.validateSupportedScenarioArguments(); // Set up the Azure CLI to be used for this task await this.setupAzureCli(); // Set up the resources required to deploy a Container App await this.setupResources(); // If a Container Registry URL was provided, try to authenticate against it if (!this.util.isNullOrEmpty(this.registryUrl)) { await this.authenticateContainerRegistryAsync(); } // If an Azure Container Registry name was provided, try to authenticate against it if (!this.util.isNullOrEmpty(this.acrName)) { await this.authenticateAzureContainerRegistryAsync(); } // Set up property to determine if the internal registry should be used this.useInternalRegistry = this.util.isNullOrEmpty(this.registryUrl); // If the application source was provided, build a runnable application image from it if (!this.useInternalRegistry && !this.util.isNullOrEmpty(this.appSourcePath)) { await this.buildAndPushImageAsync(); } // If no application source was provided, set up the scenario for deploying an existing image if (this.util.isNullOrEmpty(this.appSourcePath)) { this.setupExistingImageScenario(); } // If no YAML configuration file was provided, set up the Container App properties if (this.util.isNullOrEmpty(this.yamlConfigPath)) { this.setupContainerAppProperties(); } // Create/update the Container App await this.createOrUpdateContainerApp(); // If telemetry is enabled, log that the task completed successfully this.telemetryHelper.setSuccessfulResult(); } catch (err) { this.toolHelper.setFailed(err.message); this.telemetryHelper.setFailedResult(err.message); } finally { // If telemetry is enabled, will log metadata for this task run await this.telemetryHelper.sendLogs(); } } // Build-specific properties private static buildId: string; private static buildNumber: string; // Supported scenario properties private static appSourcePath: string; private static acrName: string; private static imageToDeploy: string; private static yamlConfigPath: string; // Resource properties private static containerAppName: string; private static containerAppExists: boolean; private static location: string; private static resourceGroup: string; private static containerAppEnvironment: string; private static ingressEnabled: boolean; // Container Registry properties private static adminCredentialsProvided: boolean; private static registryUsername: string; private static registryPassword: string; private static registryUrl: string; // Command line arguments private static commandLineArgs: string[]; // Helper properties private static telemetryHelper: TelemetryHelper; private static appHelper: ContainerAppHelper; private static registryHelper: ContainerRegistryHelper; private static util: Utility; private static toolHelper: GitHubActionsToolHelper; // Miscellaneous properties private static imageToBuild: string; private static ingress: string; private static targetPort: string; private static buildArguments: string; private static noIngressUpdate: boolean; private static useInternalRegistry: boolean; /** * Initializes the helpers used by this task. * @param disableTelemetry - Whether or not to disable telemetry for this task. */ private static initializeHelpers() { // Set up Utility for managing miscellaneous calls this.util = new Utility(); // Set up toolHelper for managing calls to the GitHub Actions toolkit this.toolHelper = new GitHubActionsToolHelper(); let disableTelemetry = this.toolHelper.getInput('disableTelemetry').toLowerCase() === 'true'; // Get buildId this.buildId = this.toolHelper.getBuildId(); // Get buildNumber this.buildNumber = this.toolHelper.getBuildNumber(); // Set up TelemetryHelper for managing telemetry calls this.telemetryHelper = new TelemetryHelper(disableTelemetry); // Set up ContainerAppHelper for managing calls around the Container App this.appHelper = new ContainerAppHelper(disableTelemetry); // Set up ContainerRegistryHelper for managing calls around the Container Registry this.registryHelper = new ContainerRegistryHelper(); } /** * Validates the arguments provided to the task for supported scenarios. * @throws Error if a valid combination of the support scenario arguments is not provided. */ private static validateSupportedScenarioArguments() { // Get the path to the application source to build and run, if provided this.appSourcePath = this.toolHelper.getInput('appSourcePath', false) as string; // Get the name of the ACR instance to push images to, if provided this.acrName = this.toolHelper.getInput('acrName', false) as string; // Get the name of the RegistryUrl to push images to, if provided this.registryUrl = this.toolHelper.getInput('registryUrl', false) as string; // Get the previously built image to deploy, if provided this.imageToDeploy = this.toolHelper.getInput('imageToDeploy', false) as string; // Get the YAML configuration file, if provided this.yamlConfigPath = this.toolHelper.getInput('yamlConfigPath', false) as string; // Get the name of the image to build if it was provided, or generate it from build variables this.imageToBuild = this.toolHelper.getInput('imageToBuild', false); // Get the user defined build arguments, if provided this.buildArguments = this.toolHelper.getInput('buildArguments', false); // Ensure that one of appSourcePath, imageToDeploy, or yamlConfigPath is provided if (this.util.isNullOrEmpty(this.appSourcePath) && this.util.isNullOrEmpty(this.imageToDeploy) && this.util.isNullOrEmpty(this.yamlConfigPath)) { let requiredArgumentMessage = `One of the following arguments must be provided: 'appSourcePath', 'imageToDeploy', or 'yamlConfigPath'.`; this.toolHelper.writeError(requiredArgumentMessage); throw Error(requiredArgumentMessage); } // Ensure that an ACR name and registry URL are not both provided if (!this.util.isNullOrEmpty(this.acrName) && !this.util.isNullOrEmpty(this.registryUrl)) { let conflictingArgumentsMessage = `The 'acrName' and 'registryUrl' arguments cannot both be provided.`; this.toolHelper.writeError(conflictingArgumentsMessage); throw Error(conflictingArgumentsMessage); } // Set up the build arguments to pass to the Dockerfile or builder if (!this.util.isNullOrEmpty(this.buildArguments)) { // Ensure that the build arguments are in the format 'key1=value1 key2=value2' const buildArguments = this.buildArguments.match(buildArgumentRegex); let invalidBuildArgumentsMessage = `The 'buildArguments' argument must be in the format 'key1=value1 key2=value2'.`; const invalidBuildArguments = buildArguments.some(variable => { if (!this.util.isNullOrEmpty(variable)) { return variable.indexOf('=') === -1; } else { return false; } }); if (invalidBuildArguments) { this.toolHelper.writeError(invalidBuildArgumentsMessage); throw Error(invalidBuildArgumentsMessage); } } } /** * Sets up the Azure CLI to be used for this task by logging in to Azure with the provided service connection and * setting the Azure CLI to install missing extensions. */ private static async setupAzureCli() { // Set the Azure CLI to install missing extensions await this.util.installAzureCliExtension(); } /** * Sets up the resources required to deploy a Container App. This includes the following: * - Getting or generating the Container App name * - Getting or discovering the location to deploy resources to * - Getting or creating the resource group * - Getting or creating the Container App Environment */ private static async setupResources() { // Get the Container App name if it was provided, or generate it from build variables this.containerAppName = this.getContainerAppName(); // Get the location to deploy resources to, if provided, or use the default location this.location = await this.getLocation(); // Get the resource group to deploy to if it was provided, or generate it from the Container App name this.resourceGroup = await this.getOrCreateResourceGroup(this.containerAppName, this.location); // Determine if the Container Appp currently exists this.containerAppExists = await this.appHelper.doesContainerAppExist(this.containerAppName, this.resourceGroup); // If the Container App doesn't exist, get/create the Container App Environment to use for the Container App if (!this.containerAppExists) { this.containerAppEnvironment = await this.getOrCreateContainerAppEnvironment(this.containerAppName, this.resourceGroup, this.location); } } /** * Gets the name of the Container App to use for the task. If the 'containerAppName' argument is not provided, * then a default name will be generated in the form 'gh-action-app-<buildId>-<buildNumber>'. * @returns The name of the Container App to use for the task. */ private static getContainerAppName(): string { let containerAppName: string = this.toolHelper.getInput('containerAppName', false); if (this.util.isNullOrEmpty(containerAppName)) { return this.toolHelper.getDefaultContainerAppName(containerAppName); } return containerAppName; } /** * Gets the location to deploy resources to. If the 'location' argument is not provided, then the default location * for the Container App service will be used. * @returns The location to deploy resources to. */ private static async getLocation(): Promise<string> { // Set deployment location, if provided let location: string = this.toolHelper.getInput('location', false); if (!this.util.isNullOrEmpty(location)) { return location; } // If no location was provided, attempt to discover the location of the existing Container App Environment linked to the Container App // or Container App Environment provided in the resource group or use the default location. // Get the resource group if it was provided let resourceGroup: string = this.toolHelper.getInput('resourceGroup', false); if (!this.util.isNullOrEmpty(resourceGroup)) { // Check if Container App exists in the resource group provided and get the location from the Container App Environment linked to it let containerAppExists = await this.appHelper.doesContainerAppExist(this.containerAppName, resourceGroup); if (containerAppExists) { // Get the name of the Container App Environment linked to the Container App var environmentName = await this.appHelper.getExistingContainerAppEnvironmentName(this.containerAppName, resourceGroup); // Check if environment exists in the resource group provided and get the location var containerAppEnvironmentExistsInResourceGroup = !this.util.isNullOrEmpty(environmentName) ? await this.appHelper.doesContainerAppEnvironmentExist(environmentName, resourceGroup) : false; if (containerAppEnvironmentExistsInResourceGroup) { // Get the location of the Container App Environment linked to the Container App location = await this.appHelper.getExistingContainerAppEnvironmentLocation(environmentName, resourceGroup); return location; } } // Get the Container App Environment name if it was provided let containerAppEnvironment: string = this.toolHelper.getInput('containerAppEnvironment', false); // Check if Container App Environment is provided and exits in the resource group provided and get the location let containerAppEnvironmentExists = !this.util.isNullOrEmpty(containerAppEnvironment) ? await this.appHelper.doesContainerAppEnvironmentExist(containerAppEnvironment, resourceGroup) : false; if (containerAppEnvironmentExists) { location = await this.appHelper.getExistingContainerAppEnvironmentLocation(containerAppEnvironment, resourceGroup); return location; } } // Get the default location if the Container App or Container App Environment was not found in the resource group provided. location = await this.appHelper.getDefaultContainerAppLocation(); return location; } /** * Gets the name of the resource group to use for the task. If the 'resourceGroup' argument is not provided, * then a default name will be generated in the form '<containerAppName>-rg'. If the generated resource group does * not exist, it will be created. * @param containerAppName - The name of the Container App to use for the task. * @param location - The location to deploy resources to. * @returns The name of the resource group to use for the task. */ private static async getOrCreateResourceGroup(containerAppName: string, location: string): Promise<string> { // Get the resource group to deploy to if it was provided, or generate it from the Container App name let resourceGroup: string = this.toolHelper.getInput('resourceGroup', false); if (this.util.isNullOrEmpty(resourceGroup)) { resourceGroup = `${containerAppName}-rg`; this.toolHelper.writeInfo(`Default resource group name: ${resourceGroup}`); // Ensure that the resource group that the Container App will be created in exists const resourceGroupExists = await this.appHelper.doesResourceGroupExist(resourceGroup); if (!resourceGroupExists) { await this.appHelper.createResourceGroup(resourceGroup, location); } } return resourceGroup; } /** * Gets the name of the Container App Environment to use for the task. If the 'containerAppEnvironment' argument * is not provided, then the task will attempt to discover an existing Container App Environment in the resource * group. If no existing Container App Environment is found, then a default name will be generated in the form * '<containerAppName>-env'. If the Container App Environment does not exist, it will be created. * @param containerAppName - The name of the Container App to use for the task. * @param resourceGroup - The name of the resource group to use for the task. * @param location - The location to deploy resources to. * @returns The name of the Container App Environment to use for the task. */ private static async getOrCreateContainerAppEnvironment( containerAppName: string, resourceGroup: string, location: string): Promise<string> { // Get the Container App environment if it was provided let containerAppEnvironment: string = this.toolHelper.getInput('containerAppEnvironment', false); // See if we can reuse an existing Container App environment found in the resource group if (this.util.isNullOrEmpty(containerAppEnvironment)) { const existingContainerAppEnvironment: string = await this.appHelper.getExistingContainerAppEnvironment(resourceGroup); if (!this.util.isNullOrEmpty(existingContainerAppEnvironment)) { this.toolHelper.writeInfo(`Existing Container App environment found in resource group: ${existingContainerAppEnvironment}`); return existingContainerAppEnvironment } } // Generate the Container App environment name if it was not provided if (this.util.isNullOrEmpty(containerAppEnvironment)) { containerAppEnvironment = `${containerAppName}-env`; this.toolHelper.writeInfo(`Default Container App environment name: ${containerAppEnvironment}`); } // Determine if the Container App environment currently exists and create one if it doesn't const containerAppEnvironmentExists: boolean = await this.appHelper.doesContainerAppEnvironmentExist(containerAppEnvironment, resourceGroup); if (!containerAppEnvironmentExists) { await this.appHelper.createContainerAppEnvironment(containerAppEnvironment, resourceGroup, location); } return containerAppEnvironment; } /** * Authenticates calls to the provided Azure Container Registry. */ private static async authenticateAzureContainerRegistryAsync() { this.registryUsername = this.toolHelper.getInput('acrUsername', false); this.registryPassword = this.toolHelper.getInput('acrPassword', false); this.registryUrl = `${this.acrName}.azurecr.io`; // Login to ACR if credentials were provided if (!this.util.isNullOrEmpty(this.registryUsername) && !this.util.isNullOrEmpty(this.registryPassword)) { this.toolHelper.writeInfo(`Logging in to ACR instance "${this.acrName}" with username and password credentials`); await this.registryHelper.loginContainerRegistryWithUsernamePassword(this.registryUrl, this.registryUsername, this.registryPassword); } else { this.toolHelper.writeInfo(`No ACR credentials provided; attempting to log in to ACR instance "${this.acrName}" with access token`); await this.registryHelper.loginAcrWithAccessTokenAsync(this.acrName); } } /** * Authenticates calls to the provided Container Registry. */ private static async authenticateContainerRegistryAsync() { this.registryUsername = this.toolHelper.getInput('registryUsername', false); this.registryPassword = this.toolHelper.getInput('registryPassword', false); // Login to Container Registry if credentials were provided if (!this.util.isNullOrEmpty(this.registryUsername) && !this.util.isNullOrEmpty(this.registryPassword)) { this.toolHelper.writeInfo(`Logging in to Container Registry "${this.registryUrl}" with username and password credentials`); await this.registryHelper.loginContainerRegistryWithUsernamePassword(this.registryUrl, this.registryUsername, this.registryPassword); } } /** * Sets up the scenario where an existing image is used for the Container App. */ private static setupExistingImageScenario() { // If telemetry is enabled, log that the previously built image scenario was targeted for this task this.telemetryHelper.setImageScenario(); } /** * Builds a runnable application image using a Dockerfile or the builder and pushes it to the Container Registry. */ private static async buildAndPushImageAsync() { // Get the name of the image to build if it was provided, or generate it from build variables this.imageToBuild = this.toolHelper.getInput('imageToBuild', false); if (this.util.isNullOrEmpty(this.imageToBuild)) { const imageRepository = this.toolHelper.getDefaultImageRepository() // Constructs the image to build based on the provided registry URL, image repository, build ID, and build number. this.imageToBuild = `${this.registryUrl}/${imageRepository}:${this.buildId}.${this.buildNumber}`; this.toolHelper.writeInfo(`Default image to build: ${this.imageToBuild}`); } // Get the name of the image to deploy if it was provided, or set it to the value of 'imageToBuild' if (this.util.isNullOrEmpty(this.imageToDeploy)) { this.imageToDeploy = this.imageToBuild; this.toolHelper.writeInfo(`Default image to deploy: ${this.imageToDeploy}`); } // Get the build arguments to pass to the Dockerfile or builder let buildArguments: string[] = []; if (!this.util.isNullOrEmpty(this.buildArguments)) { this.buildArguments.match(buildArgumentRegex).forEach((buildArg) => { buildArguments.push(buildArg); }); } // Get Dockerfile to build, if provided, or check if one exists at the root of the provided application let dockerfilePath: string = this.toolHelper.getInput('dockerfilePath', false); if (this.util.isNullOrEmpty(dockerfilePath)) { this.toolHelper.writeInfo(`No Dockerfile path provided; checking for Dockerfile at root of application source.`); const rootDockerfilePath = path.join(this.appSourcePath, 'Dockerfile'); if (fs.existsSync(rootDockerfilePath)) { this.toolHelper.writeInfo(`Dockerfile found at root of application source.`) dockerfilePath = rootDockerfilePath; } else { // No Dockerfile found or provided, build the image using the builder await this.buildImageFromBuilderAsync(this.appSourcePath, this.imageToBuild, buildArguments); } } else { dockerfilePath = path.join(this.appSourcePath, dockerfilePath); } if (!this.util.isNullOrEmpty(dockerfilePath)) { // Build the image from the provided/discovered Dockerfile await this.buildImageFromDockerfile(this.appSourcePath, dockerfilePath, this.imageToBuild, buildArguments); } // Push the image to the Container Registry await this.registryHelper.pushImageToContainerRegistry(this.imageToBuild); } /** * Builds a runnable application image using the builder. * @param appSourcePath - The path to the application source code. * @param imageToBuild - The name of the image to build. * @param buildArguments - The build arguments to pass to the pack command via environment variables. */ private static async buildImageFromBuilderAsync(appSourcePath: string, imageToBuild: string, buildArguments: string[]) { if (buildArguments.length > 0) { buildArguments.forEach((buildArg) => { const nameAndValue = buildArg.split('='); const isNameValid = nameAndValue[0].match(buildpackEnvironmentNameRegex); if (!isNameValid) { const invalidBuildArgumentsMessage = `Build environment variable name must consist of alphanumeric characters, numbers, '_', '.' or '-', start with 'BP_' or 'ORYX_'.`; this.toolHelper.writeError(invalidBuildArgumentsMessage); throw Error(invalidBuildArgumentsMessage); } }); } // Install the pack CLI await this.appHelper.installPackCliAsync(); this.toolHelper.writeInfo(`Successfully installed the pack CLI.`); // Enable experimental features for the pack CLI await this.appHelper.enablePackCliExperimentalFeaturesAsync(); this.toolHelper.writeInfo(`Successfully enabled experimental features for the pack CLI.`); // Define the environment variables that should be propagated to the builder let environmentVariables: string[] = [] // Parse the given runtime stack input and export the platform and version to environment variables const runtimeStack = this.toolHelper.getInput('runtimeStack', false); if (!this.util.isNullOrEmpty(runtimeStack)) { const runtimeStackSplit = runtimeStack.split(':'); const platformName = runtimeStackSplit[0] == "dotnetcore" ? "dotnet" : runtimeStackSplit[0]; const platformVersion = runtimeStackSplit[1]; environmentVariables.push(`ORYX_PLATFORM_NAME=${platformName}`); environmentVariables.push(`ORYX_PLATFORM_VERSION=${platformVersion}`); } // Check if the user provided a builder stack to use const builderStack = this.toolHelper.getInput('builderStack', false); // Set the target port on the image produced by the builder if (!this.util.isNullOrEmpty(this.targetPort)) { environmentVariables.push(`ORYX_RUNTIME_PORT=${this.targetPort}`); } // Add user-specified build environment variables if (buildArguments.length > 0) { buildArguments.forEach((buildArg) => { environmentVariables.push(buildArg); }); } this.toolHelper.writeInfo(`Building image "${imageToBuild}" using the Oryx++ Builder`); // Set the Oryx++ Builder as the default builder locally await this.appHelper.setDefaultBuilder(); // Create a runnable application image await this.appHelper.createRunnableAppImage(imageToBuild, appSourcePath, environmentVariables, builderStack); // If telemetry is enabled, log that the builder scenario was targeted for this task this.telemetryHelper.setBuilderScenario(); } /** * Builds a runnable application image using a provided or discovered Dockerfile. * @param appSourcePath - The path to the application source code. * @param dockerfilePath - The path to the Dockerfile to build. * @param imageToBuild - The name of the image to build. * @param buildArguments - The build arguments to pass to the docker build command. */ private static async buildImageFromDockerfile( appSourcePath: string, dockerfilePath: string, imageToBuild: string, buildArguments: string[]) { this.toolHelper.writeInfo(`Building image "${imageToBuild}" using the provided Dockerfile`); await this.appHelper.createRunnableAppImageFromDockerfile(imageToBuild, appSourcePath, dockerfilePath, buildArguments); // If telemetry is enabled, log that the Dockerfile scenario was targeted for this task this.telemetryHelper.setDockerfileScenario(); } /** * Sets up the Container App properties that will be passed through to the Azure CLI when a YAML configuration * file is not provided. */ private static setupContainerAppProperties() { this.commandLineArgs = []; // Get the ingress inputs this.ingress = this.toolHelper.getInput('ingress', false); this.targetPort = this.toolHelper.getInput('targetPort', false); // If both ingress and target port were not provided for an existing Container App, or if ingress is to be disabled, // use the 'update' command, otherwise we should use the 'up' command that performs a PATCH operation on the ingress properties. this.noIngressUpdate = this.containerAppExists && this.util.isNullOrEmpty(this.targetPort) && (this.util.isNullOrEmpty(this.ingress) || this.ingress == 'disabled'); // Pass the Container Registry credentials when creating a Container App or updating a Container App via the 'up' command if (!this.util.isNullOrEmpty(this.registryUrl) && !this.util.isNullOrEmpty(this.registryUsername) && !this.util.isNullOrEmpty(this.registryPassword) && (!this.containerAppExists || (this.containerAppExists && !this.noIngressUpdate))) { this.adminCredentialsProvided = true; this.commandLineArgs.push( `--registry-server ${this.registryUrl}`, `--registry-username ${this.registryUsername}`, `--registry-password ${this.registryPassword}`); } // Determine default values only for the 'create' scenario to avoid overriding existing values for the 'update' scenario if (!this.containerAppExists) { this.ingressEnabled = true; // Set the ingress value to 'external' if it was not provided if (this.util.isNullOrEmpty(this.ingress)) { this.ingress = 'external'; this.toolHelper.writeInfo(`Default ingress value: ${this.ingress}`); } // Set the value of ingressEnabled to 'false' if ingress was provided as 'disabled' if (this.ingress == 'disabled') { this.ingressEnabled = false; this.toolHelper.writeInfo(`Ingress is disabled for this Container App.`); } // Handle setup for ingress values when enabled if (this.ingressEnabled) { // Get the target port if provided, or set it to the default value this.targetPort = this.toolHelper.getInput('targetPort', false); // Set the target port to 80 if it was not provided if (this.util.isNullOrEmpty(this.targetPort)) { this.targetPort = '80'; this.toolHelper.writeInfo(`Default target port: ${this.targetPort}`); } // Add the ingress value and target port to the optional arguments array // Note: this step should be skipped if we're updating an existing Container App (ingress is enabled via a separate command) this.commandLineArgs.push(`--ingress ${this.ingress}`); this.commandLineArgs.push(`--target-port ${this.targetPort}`); } } const environmentVariables: string = this.toolHelper.getInput('environmentVariables', false); const isCappUpdateCommandUsed: boolean = this.noIngressUpdate || (!this.noIngressUpdate && !this.adminCredentialsProvided) // Add user-specified environment variables if (!this.util.isNullOrEmpty(environmentVariables)) { // The --replace-env-vars flag is only used for the 'update' command, // otherwise --env-vars is used for 'create' and 'up' if (isCappUpdateCommandUsed) { this.commandLineArgs.push(`--replace-env-vars ${environmentVariables}`); } else { this.commandLineArgs.push(`--env-vars ${environmentVariables}`); } } // Ensure '-i' argument and '--source' argument are not both provided if (!this.util.isNullOrEmpty(this.imageToDeploy)) { this.commandLineArgs.push(`-i ${this.imageToDeploy}`); } else if (!this.util.isNullOrEmpty(this.appSourcePath) && this.useInternalRegistry) { this.commandLineArgs.push(`--source ${this.appSourcePath}`); } } /** * Creates or updates the Container App. */ private static async createOrUpdateContainerApp() { if (!this.containerAppExists) { if (!this.util.isNullOrEmpty(this.yamlConfigPath)) { // Create the Container App from the YAML configuration file await this.appHelper.createContainerAppFromYaml(this.containerAppName, this.resourceGroup, this.yamlConfigPath); } else { // Create the Container App from command line arguments await this.appHelper.createContainerApp(this.containerAppName, this.resourceGroup, this.containerAppEnvironment, this.commandLineArgs); } return; } if (!this.util.isNullOrEmpty(this.yamlConfigPath)) { // Update the Container App from the YAML configuration file await this.appHelper.updateContainerAppFromYaml(this.containerAppName, this.resourceGroup, this.yamlConfigPath); return; } if (this.noIngressUpdate) { // Update the Container Registry details on the existing Container App, if provided as an input if (!this.util.isNullOrEmpty(this.registryUrl) && !this.util.isNullOrEmpty(this.registryUsername) && !this.util.isNullOrEmpty(this.registryPassword)) { await this.appHelper.updateContainerAppRegistryDetails(this.containerAppName, this.resourceGroup, this.registryUrl, this.registryUsername, this.registryPassword); } // Update the Container App using the 'update' command await this.appHelper.updateContainerApp(this.containerAppName, this.resourceGroup, this.commandLineArgs); } else if (this.adminCredentialsProvided && !this.noIngressUpdate) { // Update the Container App with `up` command when admin credentials are provided and ingress is manually provided. await this.appHelper.updateContainerAppWithUp(this.containerAppName, this.resourceGroup, this.commandLineArgs, this.ingress, this.targetPort); } else { // Update the Container App using the 'containerapp update' and 'ingress update' commands await this.appHelper.updateContainerApp(this.containerAppName, this.resourceGroup, this.commandLineArgs) await this.appHelper.updateContainerAppIngress(this.containerAppName, this.resourceGroup, this.ingress, this.targetPort); } // Disable ingress on the existing Container App, if provided as an input if (this.ingress == 'disabled') { await this.appHelper.disableContainerAppIngress(this.containerAppName, this.resourceGroup); } } } azurecontainerapps.runMain();