in src/Utils/YamlValidationUtil.ts [9:167]
export function validateYamlConfig(givenYaml : any) : ValidationModel {
if(!isDictionary(givenYaml)) {
return {valid : false,error :`Invalid YAML syntax.`};
}
let unSupportedKeys : string[] = [];
let supportedKeys : string[] = Object.keys(new YamlConfig());
unSupportedKeys = Object.keys(givenYaml).filter(element => supportedKeys.indexOf(element) == -1);
if(unSupportedKeys.length) {
const result = unSupportedKeys.map(element => `${element}`).join(", ");
return {valid : false, error : `The YAML file provided has unsupported field(s) "${result}".`};
}
if(isNullOrUndefined(givenYaml.testName) && isNullOrUndefined(givenYaml.testId)){
return {valid : false, error : "The required field testId is missing in the load test YAML file."};
}
let testId = '';
if(!isNullOrUndefined(givenYaml.testName)){
testId = givenYaml.testName;
}
if(!isNullOrUndefined(givenYaml.testId)){
testId = givenYaml.testId;
}
testId = testId.toLowerCase();
if(typeof(testId) != "string" || invalidName(testId)){
return {valid : false, error : `The value "${testId}" for testId is not a valid string. Allowed characters are [a-zA-Z0-9-_] and the length must be between 2 to 50 characters.`};
}
if(givenYaml.displayName && (typeof givenYaml.displayName != 'string' || invalidDisplayName(givenYaml.displayName))){
return {valid : false, error : `The value "${givenYaml.displayName}" for displayName is invalid. Display name must be a string of length between 2 to 50.`};
}
if(givenYaml.description && (typeof givenYaml.description != 'string' || invalidDescription(givenYaml.description))){
return {valid : false, error : `The value "${givenYaml.description}" for description is invalid. Description must be a string of length less than 100.`};
}
if(isNullOrUndefined(givenYaml.testPlan)){
return {valid : false, error : "The required field testPlan is missing in the load test YAML file."};
}
if(givenYaml.engineInstances && (isNaN(givenYaml.engineInstances) || inValidEngineInstances(givenYaml.engineInstances))){
return {valid : false, error : `The value "${givenYaml.engineInstances}" for engineInstances is invalid. The value should be an integer between 1 and 400.`};
}
let kind : TestKind = givenYaml.testType ?? TestKind.JMX;
if(!isValidTestKind(kind)){
return {valid : false, error : `The value "${kind}" for testType is invalid. Acceptable values are ${EngineUtil.Resources.Strings.allFrameworksFriendly}.`};
}
let framework : BaseLoadTestFrameworkModel = EngineUtil.getLoadTestFrameworkModelFromKind(kind);
if(givenYaml.testType as TestKind == TestKind.URL){
if(!checkFileType(givenYaml.testPlan,'json')) {
return {valid : false, error : "The testPlan for a URL test should of type \"json\"."};
}
}
else if(!checkFileType(givenYaml.testPlan, framework.testScriptFileExtension)) {
return {valid : false, error : `The testPlan for a ${kind} test should of type "${framework.testScriptFileExtension}".`};
}
if(givenYaml.subnetId && (typeof givenYaml.subnetId!= 'string' || isInValidSubnet(givenYaml.subnetId))){
return {valid : false, error : `The value "${givenYaml.subnetId}" for subnetId is invalid. The value should be a string of the format: "/subscriptions/{subscriptionId}/resourceGroups/{rgName}/providers/Microsoft.Network/virtualNetworks/{vnetName}/subnets/{subnetName}".`};
}
if(givenYaml.keyVaultReferenceIdentity && (typeof givenYaml.keyVaultReferenceIdentity!= 'string' || isInvalidManagedIdentityId(givenYaml.keyVaultReferenceIdentity))){
return {valid : false, error : `The value "${givenYaml.keyVaultReferenceIdentity}" for keyVaultReferenceIdentity is invalid. The value should be a string of the format: "/subscriptions/{subsId}/resourceGroups/{rgName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}".`};
}
if(givenYaml.keyVaultReferenceIdentityType != undefined && givenYaml.keyVaultReferenceIdentityType != null && !isValidManagedIdentityType(givenYaml.keyVaultReferenceIdentityType)){
return {valid : false, error : `The value "${givenYaml.keyVaultReferenceIdentityType}" for keyVaultReferenceIdentityType is invalid. Allowed values are "SystemAssigned" and "UserAssigned".`};
}
if(!isNullOrUndefined(givenYaml.referenceIdentities)) {
if(!Array.isArray(givenYaml.referenceIdentities)){
return {valid : false, error : `The value "${givenYaml.referenceIdentities.toString()}" for referenceIdentities is invalid. Provide a valid list of reference identities.`};
}
let result = validateReferenceIdentities(givenYaml.referenceIdentities);
if(result?.valid == false){
return result;
}
try {
if(givenYaml.keyVaultReferenceIdentityType || givenYaml.keyVaultReferenceIdentity){
validateAndGetSegregatedManagedIdentities(givenYaml.referenceIdentities as Array<{[key: string] : string}>, true);
} else {
validateAndGetSegregatedManagedIdentities(givenYaml.referenceIdentities as Array<{[key: string] : string}>);
}
} catch (error : any) {
return {valid : false, error : error.message};
}
}
if(!isNullOrUndefined(givenYaml.keyVaultReferenceIdentity) && givenYaml.keyVaultReferenceIdentityType == ManagedIdentityType.SystemAssigned){
return {valid : false, error : `The "keyVaultReferenceIdentity" should omitted or set to null when using the "SystemAssigned" identity type.`};
}
if(isNullOrUndefined(givenYaml.keyVaultReferenceIdentity) && givenYaml.keyVaultReferenceIdentityType == ManagedIdentityType.UserAssigned){
return {valid : false, error : `"The value for 'keyVaultReferenceIdentity' cannot be null when using the 'UserAssigned' identity type. Provide a valid identity reference for 'keyVaultReferenceIdentity'."`};
}
if(givenYaml.publicIPDisabled && typeof givenYaml.publicIPDisabled!= 'boolean'){
return {valid : false, error : `The value "${givenYaml.publicIPDisabled}" for publicIPDisabled is invalid. The value should be either true or false.`};
}
if(givenYaml.publicIPDisabled && isNullOrUndefined(givenYaml.subnetId)){
return {valid : false, error : `Public IP deployment can only be disabled for tests against private endpoints. For public endpoints, set publicIPDisabled to False.`}
}
if(givenYaml.configurationFiles && !isArrayOfStrings(givenYaml.configurationFiles)){
return {valid : false, error : `The value "${givenYaml.configurationFiles}" for configurationFiles is invalid. Provide a valid list of strings.`};
}
if(givenYaml.zipArtifacts && !isArrayOfStrings(givenYaml.zipArtifacts)){
return {valid : false, error : `The value "${givenYaml.zipArtifacts}" for zipArtifacts is invalid. Provide a valid list of strings.`};
}
if(givenYaml.splitAllCSVs && typeof givenYaml.splitAllCSVs!= 'boolean'){
return {valid : false, error : `The value "${givenYaml.splitAllCSVs}" for splitAllCSVs is invalid. The value should be either true or false`};
}
if(givenYaml.properties != undefined && givenYaml.properties.userPropertyFile != undefined){
if(isNull(givenYaml.properties.userPropertyFile) || typeof givenYaml.properties.userPropertyFile != 'string' || !checkFileTypes(givenYaml.properties.userPropertyFile, framework.userPropertyFileExtensions)){
return {valid : false, error : `The value "${givenYaml.properties.userPropertyFile}" for userPropertyFile is invalid. Provide a valid file path of type ${framework.ClientResources.userPropertyFileExtensionsFriendly}. Refer to the YAML syntax at https://learn.microsoft.com/azure/load-testing/reference-test-config-yaml#properties-configuration.`}
}
}
if(givenYaml.appComponents) {
if(!Array.isArray(givenYaml.appComponents)){
return {valid : false, error : `The value "${givenYaml.appComponents}" for appComponents is invalid. Provide a valid list of application components.`};
}
let validationAppComponents = validateAppComponentAndServerMetricsConfig(givenYaml.appComponents);
if(validationAppComponents.valid == false){
return validationAppComponents;
}
}
if(givenYaml.autoStop){
let validation = validateAutoStop(givenYaml.autoStop);
if(validation.valid == false){
return validation;
}
}
if(givenYaml.failureCriteria != undefined) {
let result = validateFailureCriteria(givenYaml.failureCriteria);
if(result.valid == false){
return result;
}
}
if(givenYaml.regionalLoadTestConfig){
if(!Array.isArray(givenYaml.regionalLoadTestConfig)){
return {valid : false, error : `The value "${givenYaml.regionalLoadTestConfig?.toString()}" for regionalLoadTestConfig is invalid. Provide a valid list of region configuration for Multi-region load test.`};
}
if(givenYaml.regionalLoadTestConfig.length < 2){
return {valid : false, error : `Multi-region load tests should contain a minimum of 2 geographic regions in the configuration.`};
}
var totalEngineCount = 0;
for(let i = 0; i < givenYaml.regionalLoadTestConfig.length; i++){
if(isNullOrUndefined(givenYaml.regionalLoadTestConfig[i].region) || typeof givenYaml.regionalLoadTestConfig[i].region != 'string' || givenYaml.regionalLoadTestConfig[i].region == ""){
return {valid : false, error : `The value "${givenYaml.regionalLoadTestConfig[i].region}" for region in regionalLoadTestConfig is invalid. Provide a valid string.`};
}
if(isNullOrUndefined(givenYaml.regionalLoadTestConfig[i].engineInstances) || isNaN(givenYaml.regionalLoadTestConfig[i].engineInstances) || inValidEngineInstances(givenYaml.regionalLoadTestConfig[i].engineInstances)){
return {valid : false, error : `The value "${givenYaml.regionalLoadTestConfig[i].engineInstances}" for engineInstances in regionalLoadTestConfig is invalid. The value should be an integer between 1 and 400.`};
}
totalEngineCount += givenYaml.regionalLoadTestConfig[i].engineInstances;
}
let engineInstances = givenYaml.engineInstances ?? 1;
if(totalEngineCount != givenYaml.engineInstances){
return {valid : false, error : `The sum of engineInstances in regionalLoadTestConfig should be equal to the value of totalEngineInstances "${engineInstances}" in the test configuration.`};
}
}
return {valid : true, error : ""};
}