Skip to content

Commit

Permalink
Publish prof container master (#35)
Browse files Browse the repository at this point in the history
* Added support for container apps with publish profile

* Renamed publish profile container provider

* refactor

* Addressed review comments

* Integrated diagnostics runtime API to get appOS

* updated error message

* updated error messages

* updated error message

* updated unit tests for publish profile container

* quotes fix

* changes in PublishProfileContainerWebAppValidator

* refactor

* added package-lock.json

* adding lib
  • Loading branch information
aksm-ms authored Jun 27, 2020
1 parent 28ffdfe commit 20000ad
Show file tree
Hide file tree
Showing 20 changed files with 271 additions and 49 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const Validations_1 = require("../Validations");
const actionparameters_1 = require("../../actionparameters");
class PublishProfileContainerWebAppValidator {
validate() {
return __awaiter(this, void 0, void 0, function* () {
const actionParams = actionparameters_1.ActionParameters.getActionParams();
Validations_1.packageNotAllowed(actionParams.packageInput);
yield Validations_1.windowsContainerAppNotAllowedForPublishProfile();
Validations_1.multiContainerNotAllowed(actionParams.multiContainerConfigFile);
Validations_1.startupCommandNotAllowed(actionParams.startupCommand);
Validations_1.validateAppDetails();
Validations_1.validateSingleContainerInputs();
});
}
}
exports.PublishProfileContainerWebAppValidator = PublishProfileContainerWebAppValidator;
31 changes: 25 additions & 6 deletions lib/ActionInputValidator/Validations.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
result["default"] = mod;
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(require("@actions/core"));
const packageUtility_1 = require("azure-actions-utility/packageUtility");
const PublishProfile_1 = require("../Utilities/PublishProfile");
const RuntimeConstants_1 = __importDefault(require("../RuntimeConstants"));
const actionparameters_1 = require("../actionparameters");
const fs = require("fs");
// Error is app-name is not provided
Expand All @@ -31,12 +35,7 @@ exports.appNameIsRequired = appNameIsRequired;
// Error if image info is provided
function containerInputsNotAllowed(images, configFile, isPublishProfile = false) {
if (!!images || !!configFile) {
if (!!isPublishProfile) {
throw new Error("Container Deployment is not supported with publish profile credentails. Instead add an Azure login action before this action. For more details refer https://github.com/azure/login");
}
else {
throw new Error(`This is not a container web app. Please remove inputs like images and configuration-file which are only relevant for container deployment.`);
}
throw new Error(`This is not a container web app. Please remove inputs like images and configuration-file which are only relevant for container deployment.`);
}
}
exports.containerInputsNotAllowed = containerInputsNotAllowed;
Expand Down Expand Up @@ -75,6 +74,14 @@ function multiContainerNotAllowed(configFile) {
}
}
exports.multiContainerNotAllowed = multiContainerNotAllowed;
// Error if image name is not provided
function validateSingleContainerInputs() {
const actionParams = actionparameters_1.ActionParameters.getActionParams();
if (!actionParams.images) {
throw new Error("Image name not provided for container. Provide a valid image name");
}
}
exports.validateSingleContainerInputs = validateSingleContainerInputs;
// Validate container inputs
function validateContainerInputs() {
let actionParams = actionparameters_1.ActionParameters.getActionParams();
Expand Down Expand Up @@ -116,3 +123,15 @@ function validatePackageInput() {
});
}
exports.validatePackageInput = validatePackageInput;
// windows container app not allowed for publish profile auth scheme
function windowsContainerAppNotAllowedForPublishProfile() {
return __awaiter(this, void 0, void 0, function* () {
const actionParams = actionparameters_1.ActionParameters.getActionParams();
const publishProfile = PublishProfile_1.PublishProfile.getPublishProfile(actionParams.publishProfileContent);
const appOS = yield publishProfile.getAppOS();
if (appOS.includes(RuntimeConstants_1.default.Windows) || appOS.includes(RuntimeConstants_1.default.Windows.toLowerCase())) {
throw new Error("Publish profile auth scheme is not supported for Windows container Apps.");
}
});
}
exports.windowsContainerAppNotAllowedForPublishProfile = windowsContainerAppNotAllowedForPublishProfile;
23 changes: 21 additions & 2 deletions lib/ActionInputValidator/ValidatorFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,34 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const actionparameters_1 = require("../actionparameters");
const AzureResourceFilterUtility_1 = require("azure-actions-appservice-rest/Utilities/AzureResourceFilterUtility");
const BaseWebAppDeploymentProvider_1 = require("../DeploymentProvider/Providers/BaseWebAppDeploymentProvider");
const PublishProfileWebAppValidator_1 = require("./ActionValidators/PublishProfileWebAppValidator");
const PublishProfileContainerWebAppValidator_1 = require("./ActionValidators/PublishProfileContainerWebAppValidator");
const SpnLinuxContainerWebAppValidator_1 = require("./ActionValidators/SpnLinuxContainerWebAppValidator");
const SpnLinuxWebAppValidator_1 = require("./ActionValidators/SpnLinuxWebAppValidator");
const SpnWindowsContainerWebAppValidator_1 = require("./ActionValidators/SpnWindowsContainerWebAppValidator");
const SpnWindowsWebAppValidator_1 = require("./ActionValidators/SpnWindowsWebAppValidator");
const Validations_1 = require("./Validations");
const PublishProfile_1 = require("../Utilities/PublishProfile");
const RuntimeConstants_1 = __importDefault(require("../RuntimeConstants"));
class ValidatorFactory {
static getValidator(type) {
return __awaiter(this, void 0, void 0, function* () {
let actionParams = actionparameters_1.ActionParameters.getActionParams();
if (type == BaseWebAppDeploymentProvider_1.DEPLOYMENT_PROVIDER_TYPES.PUBLISHPROFILE) {
return new PublishProfileWebAppValidator_1.PublishProfileWebAppValidator();
if (type === BaseWebAppDeploymentProvider_1.DEPLOYMENT_PROVIDER_TYPES.PUBLISHPROFILE) {
yield this.setResourceDetails(actionParams);
if (!!actionParams.images) {
return new PublishProfileContainerWebAppValidator_1.PublishProfileContainerWebAppValidator();
}
else {
return new PublishProfileWebAppValidator_1.PublishProfileWebAppValidator();
}
}
else if (type == BaseWebAppDeploymentProvider_1.DEPLOYMENT_PROVIDER_TYPES.SPN) {
// app-name is required to get resource details
Expand Down Expand Up @@ -56,5 +68,12 @@ class ValidatorFactory {
params.isLinux = params.realKind.indexOf("linux") > -1;
});
}
static setResourceDetails(actionParams) {
return __awaiter(this, void 0, void 0, function* () {
const publishProfile = PublishProfile_1.PublishProfile.getPublishProfile(actionParams.publishProfileContent);
const appOS = yield publishProfile.getAppOS();
actionParams.isLinux = appOS.includes(RuntimeConstants_1.default.Unix) || appOS.includes(RuntimeConstants_1.default.Unix.toLowerCase());
});
}
}
exports.ValidatorFactory = ValidatorFactory;
9 changes: 7 additions & 2 deletions lib/DeploymentProvider/DeploymentProviderFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ const actionparameters_1 = require("../actionparameters");
const BaseWebAppDeploymentProvider_1 = require("./Providers/BaseWebAppDeploymentProvider");
const WebAppContainerDeployment_1 = require("./Providers/WebAppContainerDeployment");
const WebAppDeploymentProvider_1 = require("./Providers/WebAppDeploymentProvider");
const PublishProfileWebAppContainerDeploymentProvider_1 = require("./Providers/PublishProfileWebAppContainerDeploymentProvider");
class DeploymentProviderFactory {
static getDeploymentProvider(type) {
// For publish profile type app kind is not available so we directly return WebAppDeploymentProvider
if (type === BaseWebAppDeploymentProvider_1.DEPLOYMENT_PROVIDER_TYPES.PUBLISHPROFILE) {
return new WebAppDeploymentProvider_1.WebAppDeploymentProvider(type);
if (!!actionparameters_1.ActionParameters.getActionParams().images) {
return new PublishProfileWebAppContainerDeploymentProvider_1.PublishProfileWebAppContainerDeploymentProvider(type);
}
else {
return new WebAppDeploymentProvider_1.WebAppDeploymentProvider(type);
}
}
else if (type == BaseWebAppDeploymentProvider_1.DEPLOYMENT_PROVIDER_TYPES.SPN) {
let kind = actionparameters_1.ActionParameters.getActionParams().kind;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const PublishProfile_1 = require("../../Utilities/PublishProfile");
const actionparameters_1 = require("../../actionparameters");
const azure_app_service_1 = require("azure-actions-appservice-rest/Arm/azure-app-service");
const AzureAppServiceUtility_1 = require("azure-actions-appservice-rest/Utilities/AzureAppServiceUtility");
const azure_app_kudu_service_1 = require("azure-actions-appservice-rest/Kudu/azure-app-kudu-service");
const KuduServiceUtility_1 = require("azure-actions-appservice-rest/Utilities/KuduServiceUtility");
const AnnotationUtility_1 = require("azure-actions-appservice-rest/Utilities/AnnotationUtility");
class BaseWebAppDeploymentProvider {
Expand Down Expand Up @@ -68,9 +67,8 @@ class BaseWebAppDeploymentProvider {
}
initializeForPublishProfile() {
return __awaiter(this, void 0, void 0, function* () {
let publishProfile = PublishProfile_1.PublishProfile.getPublishProfile(this.actionParams.publishProfileContent);
let scmCreds = publishProfile.creds;
this.kuduService = new azure_app_kudu_service_1.Kudu(scmCreds.uri, scmCreds.username, scmCreds.password);
const publishProfile = PublishProfile_1.PublishProfile.getPublishProfile(this.actionParams.publishProfileContent);
this.kuduService = publishProfile.kuduService;
this.kuduServiceUtility = new KuduServiceUtility_1.KuduServiceUtility(this.kuduService);
this.applicationURL = publishProfile.appUrl;
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const BaseWebAppDeploymentProvider_1 = require("./BaseWebAppDeploymentProvider");
class PublishProfileWebAppContainerDeploymentProvider extends BaseWebAppDeploymentProvider_1.BaseWebAppDeploymentProvider {
DeployWebAppStep() {
return __awaiter(this, void 0, void 0, function* () {
const appName = this.actionParams.appName;
const images = this.actionParams.images;
const isLinux = this.actionParams.isLinux;
yield this.kuduServiceUtility.deployWebAppImage(appName, images, isLinux);
});
}
}
exports.PublishProfileWebAppContainerDeploymentProvider = PublishProfileWebAppContainerDeploymentProvider;
9 changes: 9 additions & 0 deletions lib/RuntimeConstants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class RuntimeConstants {
}
exports.default = RuntimeConstants;
RuntimeConstants.system = "system";
RuntimeConstants.osName = "os_name";
RuntimeConstants.Windows = "Windows";
RuntimeConstants.Unix = "Unix";
33 changes: 33 additions & 0 deletions lib/Utilities/PublishProfile.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var core = require("@actions/core");
const actions_secret_parser_1 = require("actions-secret-parser");
const azure_app_kudu_service_1 = require("azure-actions-appservice-rest/Kudu/azure-app-kudu-service");
const RuntimeConstants_1 = __importDefault(require("../RuntimeConstants"));
class PublishProfile {
constructor(publishProfileContent) {
try {
Expand All @@ -16,6 +30,7 @@ class PublishProfile {
throw new Error("Publish profile does not contain kudu URL");
}
this._creds.uri = `https://${this._creds.uri}`;
this._kuduService = new azure_app_kudu_service_1.Kudu(this._creds.uri, this._creds.username, this._creds.password);
}
catch (error) {
core.error("Failed to fetch credentials from Publish Profile. For more details on how to set publish profile credentials refer https://aka.ms/create-secrets-for-GitHub-workflows");
Expand All @@ -34,5 +49,23 @@ class PublishProfile {
get appUrl() {
return this._appUrl;
}
get kuduService() {
return this._kuduService;
}
getAppOS() {
return __awaiter(this, void 0, void 0, function* () {
try {
if (!this._appOS) {
const appRuntimeDetails = yield this._kuduService.getAppRuntime();
this._appOS = appRuntimeDetails[RuntimeConstants_1.default.system][RuntimeConstants_1.default.osName];
core.debug(`App Runtime OS: ${this._appOS}`);
}
}
catch (error) {
throw Error("Internal Server Error. Please try again\n" + error);
}
return this._appOS;
});
}
}
exports.PublishProfile = PublishProfile;
7 changes: 3 additions & 4 deletions lib/tests/main.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ const ValidatorFactory_1 = require("../ActionInputValidator/ValidatorFactory");
const DeploymentProviderFactory_1 = require("../DeploymentProvider/DeploymentProviderFactory");
const actionparameters_1 = require("../actionparameters");
const PublishProfileWebAppValidator_1 = require("../ActionInputValidator/ActionValidators/PublishProfileWebAppValidator");
;
const WebAppDeploymentProvider_1 = require("../DeploymentProvider/Providers/WebAppDeploymentProvider");
jest.mock('@actions/core');
jest.mock('../actionparameters');
Expand All @@ -49,9 +48,9 @@ describe('Test azure-webapps-deploy', () => {
}
return '';
});
let getValidatorFactorySpy = jest.spyOn(ValidatorFactory_1.ValidatorFactory, 'getValidator');
let getValidatorFactorySpy = jest.spyOn(ValidatorFactory_1.ValidatorFactory, 'getValidator').mockImplementation((_type) => __awaiter(void 0, void 0, void 0, function* () { return new PublishProfileWebAppValidator_1.PublishProfileWebAppValidator(); }));
let ValidatorFactoryValidateSpy = jest.spyOn(PublishProfileWebAppValidator_1.PublishProfileWebAppValidator.prototype, 'validate');
let getDeploymentProviderSpy = jest.spyOn(DeploymentProviderFactory_1.DeploymentProviderFactory, 'getDeploymentProvider');
let getDeploymentProviderSpy = jest.spyOn(DeploymentProviderFactory_1.DeploymentProviderFactory, 'getDeploymentProvider').mockImplementation(type => new WebAppDeploymentProvider_1.WebAppDeploymentProvider(type));
let deployWebAppStepSpy = jest.spyOn(WebAppDeploymentProvider_1.WebAppDeploymentProvider.prototype, 'DeployWebAppStep');
let updateDeploymentStatusSpy = jest.spyOn(WebAppDeploymentProvider_1.WebAppDeploymentProvider.prototype, 'UpdateDeploymentStatus');
try {
Expand All @@ -61,7 +60,7 @@ describe('Test azure-webapps-deploy', () => {
console.log(e);
}
expect(getAuthorizerSpy).not.toHaveBeenCalled(); // When publish profile is given as input getAuthorizer is not called
expect(getActionParamsSpy).toHaveBeenCalledTimes(2);
expect(getActionParamsSpy).toHaveBeenCalledTimes(1);
expect(getInputSpy).toHaveBeenCalledTimes(1);
expect(getValidatorFactorySpy).toHaveBeenCalledTimes(1);
expect(ValidatorFactoryValidateSpy).toHaveBeenCalledTimes(1);
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"dependencies": {
"@actions/core": "^1.2.1",
"actions-secret-parser": "^1.0.3",
"azure-actions-appservice-rest": "^1.0.9",
"azure-actions-appservice-rest": "^1.2.9",
"azure-actions-utility": "^1.0.3",
"azure-actions-webclient": "^1.0.11"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { packageNotAllowed, windowsContainerAppNotAllowedForPublishProfile, multiContainerNotAllowed, startupCommandNotAllowed, validateSingleContainerInputs, validateAppDetails } from "../Validations";
import { ActionParameters } from "../../actionparameters";
import { IValidator } from "./IValidator";

export class PublishProfileContainerWebAppValidator implements IValidator {
async validate(): Promise<void> {
const actionParams: ActionParameters = ActionParameters.getActionParams();

packageNotAllowed(actionParams.packageInput);

await windowsContainerAppNotAllowedForPublishProfile();

multiContainerNotAllowed(actionParams.multiContainerConfigFile);

startupCommandNotAllowed(actionParams.startupCommand);

validateAppDetails();

validateSingleContainerInputs();
}

}
Loading

0 comments on commit 20000ad

Please sign in to comment.