/** * * @param array $farmSettings * @param array $roles * @return bool */ private function isFarmConfigurationValid($farmId, $farmSettings, array $roles = array()) { $this->errors = array('error_count' => 0); $farmVariables = new Scalr_Scripting_GlobalVariables($this->user->getAccountId(), $this->getEnvironmentId(), ScopeInterface::SCOPE_FARM); $farmRoleVariables = new Scalr_Scripting_GlobalVariables($this->user->getAccountId(), $this->getEnvironmentId(), ScopeInterface::SCOPE_FARMROLE); $name = $this->request->stripValue($farmSettings['name']); if (empty($name)) { $this->setBuildError('name', 'Farm name is invalid'); } if ($farmSettings['variables']) { $result = $farmVariables->validateValues(is_array($farmSettings['variables']) ? $farmSettings['variables'] : [], 0, $farmId); if ($result !== TRUE) { $this->setBuildError('variables', $result); } } if (is_numeric($farmSettings['owner'])) { try { $u = (new Scalr_Account_User())->loadById($farmSettings['owner']); if ($u->getAccountId() != $this->user->getAccountId()) { throw new Exception('User not found'); } } catch (Exception $e) { $this->setBuildError('owner', $e->getMessage()); } } if (is_numeric($farmSettings['teamOwner']) && $farmSettings['teamOwner'] > 0) { if ($this->user->canManageAcl()) { $teams = $this->db->getAll('SELECT id, name FROM account_teams WHERE account_id = ?', array($this->user->getAccountId())); } else { $teams = $this->user->getTeams(); } if (!in_array($farmSettings['teamOwner'], array_map(function ($t) { return $t['id']; }, $teams))) { if ($this->db->GetOne('SELECT team_id FROM farms WHERE id = ?', [$farmId]) != $farmSettings['teamOwner']) { $this->setBuildError('teamOwner', 'Team not found'); } } } if (!empty($roles)) { $hasVpcRouter = false; $vpcRouterRequired = false; $governance = new Scalr_Governance($this->getEnvironmentId()); foreach ($roles as $role) { $dbRole = DBRole::loadById($role['role_id']); if (!$this->hasPermissions($dbRole->__getNewRoleObject())) { $this->setBuildError($dbRole->name, 'You don\'t have access to this role'); } try { $dbRole->__getNewRoleObject()->getImage($role['platform'], $role['cloud_location']); } catch (Exception $e) { $this->setBuildError($dbRole->name, sprintf("Role '%s' is not available in %s on %s", $dbRole->name, $role['platform'], $role['cloud_location'])); } if ($role['alias']) { if (!preg_match("/^[[:alnum:]](?:-*[[:alnum:]])*\$/", $role['alias'])) { $this->setBuildError('alias', 'Alias should start and end with letter or number and contain only letters, numbers and dashes.', $role['farm_role_id']); } } // Validate deployments if (isset($role[Scalr_Role_Behavior::ROLE_DM_APPLICATION_ID])) { $application = Scalr_Dm_Application::init()->loadById($role[Scalr_Role_Behavior::ROLE_DM_APPLICATION_ID]); $this->user->getPermissions()->validate($application); if (!$role[Scalr_Role_Behavior::ROLE_DM_REMOTE_PATH]) { $this->setBuildError(Scalr_Role_Behavior::ROLE_DM_REMOTE_PATH, 'Remote path is required for deployment', $role['farm_role_id']); } } if ($dbRole->hasBehavior(ROLE_BEHAVIORS::VPC_ROUTER)) { $hasVpcRouter = true; } if ($dbRole->hasBehavior(ROLE_BEHAVIORS::RABBITMQ)) { $role['settings'][Entity\FarmRoleSetting::SCALING_MAX_INSTANCES] = $role['settings'][Entity\FarmRoleSetting::SCALING_MIN_INSTANCES]; $role['settings'][Scalr_Role_Behavior_RabbitMQ::ROLE_NODES_RATIO] = (int) $role['settings'][Scalr_Role_Behavior_RabbitMQ::ROLE_NODES_RATIO]; if ($role['settings'][Scalr_Role_Behavior_RabbitMQ::ROLE_NODES_RATIO] < 1 || $role['settings'][Scalr_Role_Behavior_RabbitMQ::ROLE_NODES_RATIO] > 100) { $this->setBuildError(Scalr_Role_Behavior_RabbitMQ::ROLE_NODES_RATIO, 'Nodes ratio should be an integer between 1 and 100', $role['farm_role_id']); } else { $this->checkInteger($role['farm_role_id'], Scalr_Role_Behavior_RabbitMQ::ROLE_DATA_STORAGE_EBS_SIZE, $role['settings'][Scalr_Role_Behavior_RabbitMQ::ROLE_DATA_STORAGE_EBS_SIZE], 'Storage size', 1, 1000); } } if ($dbRole->hasBehavior(ROLE_BEHAVIORS::MONGODB)) { if ($role['settings'][Scalr_Role_Behavior_MongoDB::ROLE_DATA_STORAGE_ENGINE] == 'ebs') { if ($role['settings'][Scalr_Role_Behavior_MongoDB::ROLE_DATA_STORAGE_EBS_SIZE] < 10 || $role['settings'][Scalr_Role_Behavior_MongoDB::ROLE_DATA_STORAGE_EBS_SIZE] > 1000) { $this->setBuildError(Scalr_Role_Behavior_MongoDB::ROLE_DATA_STORAGE_EBS_SIZE, sprintf("EBS size for mongoDB role should be between 10 and 1000 GB", $dbRole->name), $role['farm_role_id']); } } } if ($dbRole->hasBehavior(ROLE_BEHAVIORS::NGINX)) { $proxies = (array) @json_decode($role['settings'][Scalr_Role_Behavior_Nginx::ROLE_PROXIES], true); foreach ($proxies as $proxyIndex => $proxy) { if ($proxy['ssl'] == 1) { if (empty($proxy['ssl_certificate_id'])) { $this->setBuildError(Scalr_Role_Behavior_Nginx::ROLE_PROXIES, ['message' => 'SSL certificate is required', 'invalidIndex' => $proxyIndex], $role['farm_role_id']); break; } if ($proxy['port'] == $proxy['ssl_port']) { $this->setBuildError(Scalr_Role_Behavior_Nginx::ROLE_PROXIES, ['message' => 'HTTP and HTTPS ports cannot be the same', 'invalidIndex' => $proxyIndex], $role['farm_role_id']); } } if (count($proxy['backends']) > 0) { foreach ($proxy['backends'] as $backend) { if (empty($backend['farm_role_id']) && empty($backend['farm_role_alias']) && empty($backend['host'])) { $this->setBuildError(Scalr_Role_Behavior_Nginx::ROLE_PROXIES, ['message' => 'Destination is required', 'invalidIndex' => $proxyIndex], $role['farm_role_id']); break; } } } } } /* Validate scaling */ if (!$dbRole->hasBehavior(ROLE_BEHAVIORS::VPC_ROUTER) && !$dbRole->hasBehavior(ROLE_BEHAVIORS::MONGODB)) { $minCount = $this->checkInteger($role['farm_role_id'], Entity\FarmRoleSetting::SCALING_MIN_INSTANCES, $role['settings'][Entity\FarmRoleSetting::SCALING_MIN_INSTANCES], 'Min instances', 0, 400); $maxCount = $this->checkInteger($role['farm_role_id'], Entity\FarmRoleSetting::SCALING_MAX_INSTANCES, $role['settings'][Entity\FarmRoleSetting::SCALING_MAX_INSTANCES], 'Max instances', 1, 400); if ($minCount !== false && $maxCount !== false && $maxCount < $minCount) { $this->setBuildError(Entity\FarmRoleSetting::SCALING_MAX_INSTANCES, 'Max instances should be greater than or equal to Min instances', $role['farm_role_id']); } $this->checkInteger($role['farm_role_id'], Entity\FarmRoleSetting::SCALING_POLLING_INTERVAL, $role['settings'][Entity\FarmRoleSetting::SCALING_POLLING_INTERVAL], 'Polling interval', 1, 50); if (array_key_exists(Entity\FarmRoleSetting::SCALING_UPSCALE_TIMEOUT_ENABLED, $role["settings"]) && $role['settings'][Entity\FarmRoleSetting::SCALING_UPSCALE_TIMEOUT_ENABLED] == 1) { $this->checkInteger($role['farm_role_id'], Entity\FarmRoleSetting::SCALING_UPSCALE_TIMEOUT, $role['settings'][Entity\FarmRoleSetting::SCALING_UPSCALE_TIMEOUT], 'Upscale timeout', 1); } if (array_key_exists(Entity\FarmRoleSetting::SCALING_DOWNSCALE_TIMEOUT_ENABLED, $role["settings"]) && $role['settings'][Entity\FarmRoleSetting::SCALING_DOWNSCALE_TIMEOUT_ENABLED] == 1) { $this->checkInteger($role['farm_role_id'], Entity\FarmRoleSetting::SCALING_DOWNSCALE_TIMEOUT, $role['settings'][Entity\FarmRoleSetting::SCALING_DOWNSCALE_TIMEOUT], 'Downscale timeout', 1); } if (is_array($role['scaling'])) { foreach ($role['scaling'] as $metricId => $metricSettings) { $hasError = false; if ($metricId == Entity\ScalingMetric::METRIC_URL_RESPONSE_TIME_ID) { $hasError = Validator::validateUrl($metricSettings['url']) !== true || Validator::validateInteger($metricSettings['min']) !== true || Validator::validateInteger($metricSettings['max']) !== true; } elseif ($metricId == Entity\ScalingMetric::METRIC_SQS_QUEUE_SIZE_ID) { $hasError = Validator::validateNotEmpty($metricSettings['queue_name']) !== true || Validator::validateInteger($metricSettings['min']) !== true || Validator::validateInteger($metricSettings['max']) !== true; } elseif (in_array($metricId, [Entity\ScalingMetric::METRIC_LOAD_AVERAGES_ID, Entity\ScalingMetric::METRIC_FREE_RAM_ID, Entity\ScalingMetric::METRIC_BANDWIDTH_ID])) { $hasError = Validator::validateFloat($metricSettings['min']) !== true || Validator::validateFloat($metricSettings['max']) !== true; } if ($hasError) { $this->setBuildError('scaling', ['message' => 'Scaling metric settings are invalid', 'invalidIndex' => $metricId], $role['farm_role_id']); break; } } } } /* Validate advanced settings */ if (!$dbRole->hasBehavior(ROLE_BEHAVIORS::VPC_ROUTER)) { if (isset($role['settings'][Scalr_Role_Behavior::ROLE_BASE_API_PORT])) { $this->checkInteger($role['farm_role_id'], Scalr_Role_Behavior::ROLE_BASE_API_PORT, $role['settings'][Scalr_Role_Behavior::ROLE_BASE_API_PORT], 'Scalarizr API port', 1, 65535); } if (isset($role['settings'][Scalr_Role_Behavior::ROLE_BASE_MESSAGING_PORT])) { $this->checkInteger($role['farm_role_id'], Scalr_Role_Behavior::ROLE_BASE_MESSAGING_PORT, $role['settings'][Scalr_Role_Behavior::ROLE_BASE_MESSAGING_PORT], 'Scalarizr control port', 1, 65535); } if (isset($role['settings'][Entity\FarmRoleSetting::SYSTEM_REBOOT_TIMEOUT])) { $this->checkInteger($role['farm_role_id'], Entity\FarmRoleSetting::SYSTEM_REBOOT_TIMEOUT, $role['settings'][Entity\FarmRoleSetting::SYSTEM_REBOOT_TIMEOUT], 'Reboot timeout', 1); } if (isset($role['settings'][Entity\FarmRoleSetting::SYSTEM_LAUNCH_TIMEOUT])) { $this->checkInteger($role['farm_role_id'], Entity\FarmRoleSetting::SYSTEM_LAUNCH_TIMEOUT, $role['settings'][Entity\FarmRoleSetting::SYSTEM_LAUNCH_TIMEOUT], 'Launch timeout', 1); } } /* Validate chef settings */ if ($dbRole->hasBehavior(ROLE_BEHAVIORS::CHEF)) { if ($role['settings'][Scalr_Role_Behavior_Chef::ROLE_CHEF_BOOTSTRAP] == 1) { if (empty($role['settings'][Scalr_Role_Behavior_Chef::ROLE_CHEF_COOKBOOK_URL]) && empty($role['settings'][Scalr_Role_Behavior_Chef::ROLE_CHEF_SERVER_ID])) { $this->setBuildError(Scalr_Role_Behavior_Chef::ROLE_CHEF_SERVER_ID, 'Chef Server or Chef Solo must be setup if using Chef to bootstrap Role', $role['farm_role_id']); } elseif ($role['settings'][Scalr_Role_Behavior_Chef::ROLE_CHEF_COOKBOOK_URL_TYPE] == 'http' && !empty($role['settings'][Scalr_Role_Behavior_Chef::ROLE_CHEF_COOKBOOK_URL]) && Validator::validateUrl($role['settings'][Scalr_Role_Behavior_Chef::ROLE_CHEF_COOKBOOK_URL]) !== true) { $this->setBuildError(Scalr_Role_Behavior_Chef::ROLE_CHEF_COOKBOOK_URL, 'Cookbook URL is invalid.', $role['farm_role_id']); } } elseif ($dbRole->getProperty(Scalr_Role_Behavior_Chef::ROLE_CHEF_BOOTSTRAP) == 1 && $dbRole->getProperty(Scalr_Role_Behavior_Chef::ROLE_CHEF_SERVER_ID)) { if (strpos($role['farm_role_id'], "virtual_") !== false) { $chefGovernance = $governance->getValue(Scalr_Governance::CATEGORY_GENERAL, Scalr_Governance::GENERAL_CHEF, 'servers'); if ($chefGovernance !== null && !isset($chefGovernance[$dbRole->getProperty(Scalr_Role_Behavior_Chef::ROLE_CHEF_SERVER_ID)])) { $this->setBuildError(Scalr_Role_Behavior_Chef::ROLE_CHEF_SERVER_ID, 'Chef server is not allowed by Governance.', $role['farm_role_id']); } } if (empty($dbRole->getProperty(Scalr_Role_Behavior_Chef::ROLE_CHEF_ENVIRONMENT)) && empty($role['settings'][Scalr_Role_Behavior_Chef::ROLE_CHEF_ENVIRONMENT])) { $this->setBuildError(Scalr_Role_Behavior_Chef::ROLE_CHEF_ENVIRONMENT, 'Chef Environment is required', $role['farm_role_id']); } } } /** Validate platform specified settings **/ switch ($role['platform']) { case SERVER_PLATFORMS::EC2: if (!empty($role['settings'][Entity\FarmRoleSetting::AWS_TAGS_LIST])) { $reservedBaseCustomTags = ['scalr-meta', 'Name']; $baseCustomTags = @explode("\n", $role['settings'][Entity\FarmRoleSetting::AWS_TAGS_LIST]); foreach ((array) $baseCustomTags as $tag) { $tag = trim($tag); $tagChunks = explode("=", $tag); if (in_array(trim($tagChunks[0]), $reservedBaseCustomTags)) { $this->setBuildError(Entity\FarmRoleSetting::AWS_TAGS_LIST, "Avoid using Scalr-reserved tag names.", $role['farm_role_id']); } } } if ($dbRole->hasBehavior(ROLE_BEHAVIORS::MYSQL)) { if ($role['settings'][Entity\FarmRoleSetting::MYSQL_DATA_STORAGE_ENGINE] == MYSQL_STORAGE_ENGINE::EBS) { if ($dbRole->generation != 2 && isset($role['settings'][Entity\FarmRoleSetting::AWS_AVAIL_ZONE])) { if ($role['settings'][Entity\FarmRoleSetting::AWS_AVAIL_ZONE] == "" || $role['settings'][Entity\FarmRoleSetting::AWS_AVAIL_ZONE] == "x-scalr-diff" || stristr($role['settings'][Entity\FarmRoleSetting::AWS_AVAIL_ZONE], 'x-scalr-custom')) { $this->setBuildError(Entity\FarmRoleSetting::AWS_AVAIL_ZONE, 'Requirement for EBS MySQL data storage is specific \'Placement\' parameter', $role['farm_role_id']); } } } } if ($dbRole->getDbMsrBehavior()) { if ($role['settings'][Scalr_Db_Msr::DATA_STORAGE_ENGINE] == MYSQL_STORAGE_ENGINE::EPH) { if (!$role['settings'][Scalr_Db_Msr::DATA_STORAGE_EPH_DISK] && !$role['settings'][Scalr_Db_Msr::DATA_STORAGE_EPH_DISKS]) { $this->setBuildError(Scalr_Db_Msr::DATA_STORAGE_EPH_DISK, 'Ephemeral disk settings is required', $role['farm_role_id']); } } elseif ($role['settings'][Scalr_Db_Msr::DATA_STORAGE_ENGINE] == MYSQL_STORAGE_ENGINE::EBS) { if (array_key_exists(Scalr_Db_Msr::DATA_STORAGE_EBS_TYPE, $role["settings"])) { if ($role['settings'][Scalr_Db_Msr::DATA_STORAGE_EBS_TYPE] == CreateVolumeRequestData::VOLUME_TYPE_STANDARD) { $this->checkInteger($role['farm_role_id'], Scalr_Db_Msr::DATA_STORAGE_EBS_SIZE, $role['settings'][Scalr_Db_Msr::DATA_STORAGE_EBS_SIZE], 'Storage size', 1, 1024); } elseif ($role['settings'][Scalr_Db_Msr::DATA_STORAGE_EBS_TYPE] == CreateVolumeRequestData::VOLUME_TYPE_GP2) { $this->checkInteger($role['farm_role_id'], Scalr_Db_Msr::DATA_STORAGE_EBS_SIZE, $role['settings'][Scalr_Db_Msr::DATA_STORAGE_EBS_SIZE], 'Storage size', 1, 16384); } elseif ($role['settings'][Scalr_Db_Msr::DATA_STORAGE_EBS_TYPE] == CreateVolumeRequestData::VOLUME_TYPE_IO1) { $this->checkInteger($role['farm_role_id'], Scalr_Db_Msr::DATA_STORAGE_EBS_SIZE, $role['settings'][Scalr_Db_Msr::DATA_STORAGE_EBS_SIZE], 'Storage size', 4, 16384); $this->checkInteger($role['farm_role_id'], Scalr_Db_Msr::DATA_STORAGE_EBS_IOPS, $role['settings'][Scalr_Db_Msr::DATA_STORAGE_EBS_IOPS], 'IOPS', 100, 20000); } } } if (array_key_exists(Scalr_Db_Msr::DATA_STORAGE_EBS_ENABLE_ROTATION, $role["settings"]) && $role['settings'][Scalr_Db_Msr::DATA_STORAGE_EBS_ENABLE_ROTATION] == 1) { $this->checkInteger($role['farm_role_id'], Scalr_Db_Msr::DATA_STORAGE_EBS_ROTATE, $role['settings'][Scalr_Db_Msr::DATA_STORAGE_EBS_ROTATE], 'Snapshot rotation limit', 1); } if ($role['settings'][Scalr_Db_Msr::DATA_STORAGE_ENGINE] == MYSQL_STORAGE_ENGINE::LVM) { if (!$role['settings'][Scalr_Role_DbMsrBehavior::ROLE_DATA_STORAGE_LVM_VOLUMES]) { $this->setBuildError(Scalr_Role_DbMsrBehavior::ROLE_DATA_STORAGE_LVM_VOLUMES, 'LVM storage settings is required', $role['farm_role_id']); } } } if ($role['settings'][Entity\FarmRoleSetting::AWS_AVAIL_ZONE] == 'x-scalr-custom=') { $this->setBuildError(Entity\FarmRoleSetting::AWS_AVAIL_ZONE, 'Availability zone should be selected', $role['farm_role_id']); } if (!empty($farmSettings['vpc_id'])) { $sgs = @json_decode($role['settings'][Entity\FarmRoleSetting::AWS_SECURITY_GROUPS_LIST]); if (!$governance->getValue(SERVER_PLATFORMS::EC2, Scalr_Governance::AWS_SECURITY_GROUPS) && !$dbRole->hasBehavior(ROLE_BEHAVIORS::VPC_ROUTER) && empty($sgs) && empty($role['settings'][Entity\FarmRoleSetting::AWS_SG_LIST])) { $this->setBuildError(Entity\FarmRoleSetting::AWS_SECURITY_GROUPS_LIST, 'Security group(s) should be selected', $role['farm_role_id']); } $subnets = @json_decode($role['settings'][Entity\FarmRoleSetting::AWS_VPC_SUBNET_ID]); if (empty($subnets)) { $this->setBuildError(Entity\FarmRoleSetting::AWS_VPC_SUBNET_ID, 'VPC Subnet(s) should be selected', $role['farm_role_id']); } if (\Scalr::config('scalr.instances_connection_policy') != 'local' && empty($role['settings'][Scalr_Role_Behavior_Router::ROLE_VPC_SCALR_ROUTER_ID])) { try { if (!empty($subnets[0])) { $platform = PlatformFactory::NewPlatform(SERVER_PLATFORMS::EC2); $info = $platform->listSubnets($this->getEnvironment(), $role['cloud_location'], $farmSettings['vpc_id'], true, $subnets[0]); if (!empty($info["type"]) && $info['type'] == 'private') { $vpcRouterRequired = $role['farm_role_id']; } } } catch (Exception $e) { } } } break; case SERVER_PLATFORMS::CLOUDSTACK: if (!$role['settings'][Entity\FarmRoleSetting::CLOUDSTACK_SERVICE_OFFERING_ID]) { $this->setBuildError(Entity\FarmRoleSetting::CLOUDSTACK_SERVICE_OFFERING_ID, 'Service offering should be selected', $role['farm_role_id']); } break; case SERVER_PLATFORMS::RACKSPACE: if (!$role['settings'][Entity\FarmRoleSetting::RS_FLAVOR_ID]) { $this->setBuildError(Entity\FarmRoleSetting::CLOUDSTACK_SERVICE_OFFERING_ID, 'Flavor should be selected', $role['farm_role_id']); } break; case SERVER_PLATFORMS::GCE: if ($dbRole->getDbMsrBehavior()) { if ($role['settings'][Scalr_Db_Msr::DATA_STORAGE_ENGINE] == MYSQL_STORAGE_ENGINE::GCE_PERSISTENT) { $this->checkInteger($role['farm_role_id'], Scalr_Db_Msr::DATA_STORAGE_GCED_SIZE, $role['settings'][Scalr_Db_Msr::DATA_STORAGE_GCED_SIZE], 'Storage size', 1); } } break; } if ($dbRole->getDbMsrBehavior()) { if (array_key_exists(Scalr_Db_Msr::DATA_BUNDLE_ENABLED, $role["settings"]) && $role['settings'][Scalr_Db_Msr::DATA_BUNDLE_ENABLED] == 1) { $this->checkInteger($role['farm_role_id'], Scalr_Db_Msr::DATA_BUNDLE_EVERY, $role['settings'][Scalr_Db_Msr::DATA_BUNDLE_EVERY], 'Bundle period', 1); $this->checkString($role['farm_role_id'], Scalr_Db_Msr::DATA_BUNDLE_TIMEFRAME_START_HH, $role['settings'][Scalr_Db_Msr::DATA_BUNDLE_TIMEFRAME_START_HH], 'Preferred bundle window start HH is invalid', '/^([0-1][0-9])|(2[0-4])$/'); $this->checkString($role['farm_role_id'], Scalr_Db_Msr::DATA_BUNDLE_TIMEFRAME_START_MM, $role['settings'][Scalr_Db_Msr::DATA_BUNDLE_TIMEFRAME_START_MM], 'Preferred bundle window start MM is invalid', '/^[0-5][0-9]$/'); $this->checkString($role['farm_role_id'], Scalr_Db_Msr::DATA_BUNDLE_TIMEFRAME_END_HH, $role['settings'][Scalr_Db_Msr::DATA_BUNDLE_TIMEFRAME_END_HH], 'Preferred bundle window end HH is invalid', '/^([0-1][0-9])|(2[0-4])$/'); $this->checkString($role['farm_role_id'], Scalr_Db_Msr::DATA_BUNDLE_TIMEFRAME_END_MM, $role['settings'][Scalr_Db_Msr::DATA_BUNDLE_TIMEFRAME_END_MM], 'Preferred bundle window end MM is invalid', '/^[0-5][0-9]$/'); } if (array_key_exists(Scalr_Db_Msr::DATA_BACKUP_ENABLED, $role["settings"]) && $role['settings'][Scalr_Db_Msr::DATA_BACKUP_ENABLED] == 1) { $this->checkInteger($role['farm_role_id'], Scalr_Db_Msr::DATA_BACKUP_EVERY, $role['settings'][Scalr_Db_Msr::DATA_BACKUP_EVERY], 'Backup period', 1); $this->checkString($role['farm_role_id'], Scalr_Db_Msr::DATA_BACKUP_TIMEFRAME_START_HH, $role['settings'][Scalr_Db_Msr::DATA_BACKUP_TIMEFRAME_START_HH], 'Preferred backup window start HH is invalid', '/^([0-1][0-9])|(2[0-4])$/'); $this->checkString($role['farm_role_id'], Scalr_Db_Msr::DATA_BACKUP_TIMEFRAME_START_MM, $role['settings'][Scalr_Db_Msr::DATA_BACKUP_TIMEFRAME_START_MM], 'Preferred backup window start MM is invalid', '/^[0-5][0-9]$/'); $this->checkString($role['farm_role_id'], Scalr_Db_Msr::DATA_BACKUP_TIMEFRAME_END_HH, $role['settings'][Scalr_Db_Msr::DATA_BACKUP_TIMEFRAME_END_HH], 'Preferred backup window end HH is invalid', '/^([0-1][0-9])|(2[0-4])$/'); $this->checkString($role['farm_role_id'], Scalr_Db_Msr::DATA_BACKUP_TIMEFRAME_END_MM, $role['settings'][Scalr_Db_Msr::DATA_BACKUP_TIMEFRAME_END_MM], 'Preferred backup window end MM is invalid', '/^[0-5][0-9]$/'); } } if (!empty($role['settings'][Scalr_Role_Behavior::ROLE_BASE_CUSTOM_TAGS]) && PlatformFactory::isOpenstack($role['platform'])) { $reservedBaseCustomTags = ['scalr-meta', 'farmid', 'role', 'httpproto', 'region', 'hash', 'realrolename', 'szr_key', 'serverid', 'p2p_producer_endpoint', 'queryenv_url', 'behaviors', 'farm_roleid', 'roleid', 'env_id', 'platform', 'server_index', 'cloud_server_id', 'cloud_location_zone', 'owner_email']; $baseCustomTags = @explode("\n", $role['settings'][Scalr_Role_Behavior::ROLE_BASE_CUSTOM_TAGS]); foreach ((array) $baseCustomTags as $tag) { $tag = trim($tag); $tagChunks = explode("=", $tag); if (in_array(trim($tagChunks[0]), $reservedBaseCustomTags)) { $this->setBuildError(Scalr_Role_Behavior::ROLE_BASE_CUSTOM_TAGS, "Avoid using Scalr-reserved metadata names.", $role['farm_role_id']); } } } if (!empty($role['settings'][Scalr_Role_Behavior::ROLE_BASE_HOSTNAME_FORMAT])) { if (!preg_match('/^[\\w\\{\\}\\.-]+$/', $role['settings'][Scalr_Role_Behavior::ROLE_BASE_HOSTNAME_FORMAT])) { $this->setBuildError(Scalr_Role_Behavior::ROLE_BASE_HOSTNAME_FORMAT, "server hostname format for role'{$dbRole->name}' should contain only [a-z0-9-] chars. First char should not be hypen.", $role['farm_role_id']); } } if (!empty($role['settings'][Entity\FarmRoleSetting::DNS_CREATE_RECORDS])) { if ($role['settings'][Entity\FarmRoleSetting::DNS_EXT_RECORD_ALIAS]) { if (!preg_match('/^[\\w\\{\\}\\.-]+$/', $role['settings'][Entity\FarmRoleSetting::DNS_EXT_RECORD_ALIAS])) { $this->setBuildError(Entity\FarmRoleSetting::DNS_EXT_RECORD_ALIAS, "ext- record alias for role '{$dbRole->name}' should contain only [A-Za-z0-9-] chars. First and last char should not be hypen.", $role['farm_role_id']); } } if ($role['settings'][Entity\FarmRoleSetting::DNS_INT_RECORD_ALIAS]) { if (!preg_match('/^[\\w\\{\\}\\.-]+$/', $role['settings'][Entity\FarmRoleSetting::DNS_INT_RECORD_ALIAS])) { $this->setBuildError(Entity\FarmRoleSetting::DNS_INT_RECORD_ALIAS, "int- record alias for role '{$dbRole->name}' should contain only [A-Za-z0-9-] chars. First and last char should not by hypen.", $role['farm_role_id']); } } } // Validate Global variables if (!strstr($role['farm_role_id'], 'virtual_')) { $farmRole = DBFarmRole::LoadByID($role['farm_role_id']); } else { $farmRole = null; if ($dbRole->isDeprecated == 1) { $this->setBuildError('roleId', 'This role has been deprecated and cannot be added', $role['farm_role_id']); } if (!empty($envs = $dbRole->__getNewRoleObject()->getAllowedEnvironments())) { if (!in_array($this->getEnvironmentId(), $envs)) { $this->setBuildError('roleId', "You don't have access to this role", $role['farm_role_id']); } } } if (isset($role['storages']['configs'])) { // TODO: refactor, get rid of using DBFarmRole in constructor $fr = $farmRole ? $farmRole : new DBFarmRole(0); foreach ($fr->getStorage()->validateConfigs($role['storages']['configs']) as $index => $message) { $this->setBuildError('storages', ['message' => $message, 'invalidIndex' => $index], $role['farm_role_id']); break; } } $result = $farmRoleVariables->validateValues(is_array($role['variables']) ? $role['variables'] : [], $dbRole->id, $farmId, $farmRole ? $farmRole->ID : 0); if ($result !== TRUE) { $this->setBuildError('variables', $result, $role['farm_role_id']); } } } if ($farmSettings['vpc_id']) { if (!$hasVpcRouter && $vpcRouterRequired) { $this->setBuildError(Entity\FarmRoleSetting::AWS_VPC_SUBNET_ID, 'You must select a VPC Router for Farm Roles launched in a Private VPC Subnet', $vpcRouterRequired); } } if ($this->getContainer()->analytics->enabled) { if ($farmSettings['projectId']) { $project = $this->getContainer()->analytics->projects->get($farmSettings['projectId']); if (!$project) { $this->setBuildError('projectId', 'Project not found', null); } else { if ($project->ccId != $this->getEnvironment()->getPlatformConfigValue(Scalr_Environment::SETTING_CC_ID)) { $this->setBuildError('projectId', 'Invalid project identifier. Project should correspond to the Environment\'s cost center.', null); } } } else { $this->setBuildError('projectId', 'Project field is required', null); } } return $this->errors['error_count'] == 0 ? true : false; }