container-apps-deploy-action/azurecontainerapps.ts

672 строки
34 KiB
TypeScript

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();