С версии: 18.09.2012
Автор: Vitaliy Demidov (vitaliy@scalr.com)
Пример #1
0
 /**
  * Increments the quantity of the processed queries during current client instance
  */
 protected function _incrementQueriesQuantity()
 {
     $this->aws->queriesQuantity++;
     $eventObserver = $this->aws->getEventObserver();
     if (isset($eventObserver) && $eventObserver->isSubscribed(EventType::EVENT_SEND_REQUEST)) {
         $eventObserver->fireEvent(new SendRequestEvent(array('requestNumber' => $this->aws->queriesQuantity, 'apicall' => isset($this->lastApiCall) ? $this->lastApiCall : null)));
     }
 }
Пример #2
0
 /**
  * {@inheritdoc}
  * @see \Scalr\System\Zmq\Cron\TaskInterface::enqueue()
  */
 public function enqueue()
 {
     $queue = new ArrayObject([]);
     if (!\Scalr::getContainer()->analytics->enabled) {
         $this->log("INFO", "Terminating the process as Cost analytics is disabled in the config.");
         exit;
     }
     if (SettingEntity::getValue(SettingEntity::ID_FORBID_AUTOMATIC_UPDATE_AWS_PRICES)) {
         $this->log("INFO", "Terminating the process because of overriding AWS prices has been forbidden by financial admin.");
         exit;
     }
     $now = new DateTime('now', new DateTimeZone('UTC'));
     $urls = array('https://a0.awsstatic.com/pricing/1/ec2/linux-od.min.js', 'https://a0.awsstatic.com/pricing/1/ec2/mswin-od.min.js', 'https://a0.awsstatic.com/pricing/1/ec2/previous-generation/linux-od.min.js', 'https://a0.awsstatic.com/pricing/1/ec2/previous-generation/mswin-od.min.js');
     $availableLocations = Aws::getCloudLocations();
     foreach ($urls as $link) {
         $json = trim(preg_replace('/^.+?callback\\((.+?)\\);\\s*$/sU', '\\1', $this->getPricingContent($link)));
         $data = json_decode(preg_replace('/(\\w+):/', '"\\1":', $json));
         if (!empty($data->config->regions)) {
             foreach ($data->config->regions as $rd) {
                 $rd->url = basename($link);
                 $queue->append($rd);
             }
         }
     }
     return $queue;
 }
Пример #3
0
 /**
  * Constructor
  */
 public function __construct(\Scalr\Service\Aws $aws)
 {
     $this->plugins = new \ArrayObject(array());
     $this->aws = $aws;
     $cfEnabled = $this->aws->getContainer()->config('scalr.aws.plugins.enabled');
     if (!is_array($cfEnabled)) {
         $cfEnabled = (array) $cfEnabled;
     }
     foreach (EventType::getAllowedValues() as $eventName) {
         $this->subscriptions[$eventName] = array();
     }
     $iterator = new \RegexIterator(new \DirectoryIterator(__DIR__ . DIRECTORY_SEPARATOR . 'Handlers'), '/^(.+)Plugin\\.php$/');
     foreach ($iterator as $di) {
         /* @var $di \DirectoryIterator */
         $plugin = substr($di->getFilename(), 0, -4);
         $class = __NAMESPACE__ . '\\Handlers\\' . $plugin;
         $p = new $class();
         if (!$p instanceof PluginInterface) {
             trigger_error(sprintf('AWS client plugin "%s" must implement PluginInterface.', $class), E_USER_WARNING);
             continue;
         }
         $p->setAws($this->aws);
         $p->setContainer($this->aws->getContainer());
         $subscribed = $p->getSubscribedEvents();
         if (empty($subscribed)) {
             continue;
         }
         $lcPlugin = strtolower(substr($plugin, 0, -6));
         foreach ($subscribed as $eventName) {
             if (isset($this->subscriptions[$eventName])) {
                 $this->subscriptions[$eventName][$lcPlugin] = $p->priority;
             } else {
                 throw new Exception\PluginException(sprintf('AWS client plugin "%s" is subscribed to unknown event "%s".', $class, $eventName));
             }
         }
         $this->plugins[$lcPlugin] = $p;
         $this->enabled[$lcPlugin] = in_array($lcPlugin, $cfEnabled) ? true : null;
     }
     //Sorts subscriptions by plugin priority
     foreach ($this->subscriptions as $eventName => $v) {
         if (!empty($v)) {
             arsort($this->subscriptions[$eventName]);
         }
     }
 }
Пример #4
0
 private function saveEc2()
 {
     $pars = [];
     $enabled = false;
     $envAutoEnabled = false;
     $bNew = !$this->env->isPlatformEnabled(SERVER_PLATFORMS::EC2);
     $currentCloudCredentials = $this->env->keychain(SERVER_PLATFORMS::EC2);
     $ccProps = $currentCloudCredentials->properties;
     if ($this->getParam('ec2_is_enabled')) {
         $enabled = true;
         $pars[Entity\CloudCredentialsProperty::AWS_ACCOUNT_TYPE] = trim($this->checkVar(Entity\CloudCredentialsProperty::AWS_ACCOUNT_TYPE, 'string', "AWS Account Type required", SERVER_PLATFORMS::EC2));
         $pars[Entity\CloudCredentialsProperty::AWS_ACCESS_KEY] = trim($this->checkVar(Entity\CloudCredentialsProperty::AWS_ACCESS_KEY, 'string', "AWS Access Key required", SERVER_PLATFORMS::EC2));
         $pars[Entity\CloudCredentialsProperty::AWS_SECRET_KEY] = trim($this->checkVar(Entity\CloudCredentialsProperty::AWS_SECRET_KEY, 'password', "AWS Access Key required", SERVER_PLATFORMS::EC2));
         $pars[Entity\CloudCredentialsProperty::AWS_PRIVATE_KEY] = $this->checkVar(Entity\CloudCredentialsProperty::AWS_PRIVATE_KEY, 'file', '', SERVER_PLATFORMS::EC2);
         $pars[Entity\CloudCredentialsProperty::AWS_CERTIFICATE] = $this->checkVar(Entity\CloudCredentialsProperty::AWS_CERTIFICATE, 'file', '', SERVER_PLATFORMS::EC2);
         if ($this->getContainer()->analytics->enabled) {
             $pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_ENABLED] = $this->checkVar2(Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_ENABLED, 'bool', '', SERVER_PLATFORMS::EC2);
             if (!empty($pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_ENABLED])) {
                 $pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_BUCKET] = $this->checkVar(Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_BUCKET, 'string', "Detailed billing bucket name is required", SERVER_PLATFORMS::EC2);
                 $pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_PAYER_ACCOUNT] = $this->checkVar2(Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_PAYER_ACCOUNT, 'string', '', SERVER_PLATFORMS::EC2);
                 $pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_REGION] = $this->checkVar(Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_REGION, 'string', "Aws region is required", SERVER_PLATFORMS::EC2);
             } else {
                 $pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_BUCKET] = false;
                 $pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_PAYER_ACCOUNT] = false;
                 $pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_REGION] = false;
             }
         }
         // user can mull certificate and private key, check it
         if (strpos($pars[Entity\CloudCredentialsProperty::AWS_PRIVATE_KEY], 'BEGIN CERTIFICATE') !== FALSE && strpos($pars[Entity\CloudCredentialsProperty::AWS_CERTIFICATE], 'BEGIN PRIVATE KEY') !== FALSE) {
             // swap it
             $key = $pars[Entity\CloudCredentialsProperty::AWS_PRIVATE_KEY];
             $pars[Entity\CloudCredentialsProperty::AWS_PRIVATE_KEY] = $pars[Entity\CloudCredentialsProperty::AWS_CERTIFICATE];
             $pars[Entity\CloudCredentialsProperty::AWS_CERTIFICATE] = $key;
         }
         if ($pars[Entity\CloudCredentialsProperty::AWS_ACCOUNT_TYPE] == Entity\CloudCredentialsProperty::AWS_ACCOUNT_TYPE_GOV_CLOUD) {
             $region = \Scalr\Service\Aws::REGION_US_GOV_WEST_1;
         } else {
             if ($pars[Entity\CloudCredentialsProperty::AWS_ACCOUNT_TYPE] == Entity\CloudCredentialsProperty::AWS_ACCOUNT_TYPE_CN_CLOUD) {
                 $region = \Scalr\Service\Aws::REGION_CN_NORTH_1;
             } else {
                 $region = \Scalr\Service\Aws::REGION_US_EAST_1;
             }
         }
         if (!count($this->checkVarError)) {
             if ($pars[Entity\CloudCredentialsProperty::AWS_ACCESS_KEY] != $ccProps[Entity\CloudCredentialsProperty::AWS_ACCESS_KEY] or $pars[Entity\CloudCredentialsProperty::AWS_SECRET_KEY] != $ccProps[Entity\CloudCredentialsProperty::AWS_SECRET_KEY] or $pars[Entity\CloudCredentialsProperty::AWS_PRIVATE_KEY] != $ccProps[Entity\CloudCredentialsProperty::AWS_PRIVATE_KEY] or $pars[Entity\CloudCredentialsProperty::AWS_CERTIFICATE] != $ccProps[Entity\CloudCredentialsProperty::AWS_CERTIFICATE]) {
                 $aws = $this->env->aws($region, $pars[Entity\CloudCredentialsProperty::AWS_ACCESS_KEY], $pars[Entity\CloudCredentialsProperty::AWS_SECRET_KEY], !empty($pars[Entity\CloudCredentialsProperty::AWS_CERTIFICATE]) ? $pars[Entity\CloudCredentialsProperty::AWS_CERTIFICATE] : null, !empty($pars[Entity\CloudCredentialsProperty::AWS_PRIVATE_KEY]) ? $pars[Entity\CloudCredentialsProperty::AWS_PRIVATE_KEY] : null);
                 //Validates private key and certificate if they are provided
                 if (!empty($pars[Entity\CloudCredentialsProperty::AWS_CERTIFICATE]) || !empty($pars[Entity\CloudCredentialsProperty::AWS_PRIVATE_KEY])) {
                     try {
                         //SOAP is not supported anymore
                         //$aws->validateCertificateAndPrivateKey();
                     } catch (Exception $e) {
                         throw new Exception(_("Incorrect format of X.509 certificate or private key. Make sure that you are using files downloaded from AWS profile. ({$e->getMessage()})"));
                     }
                 }
                 //Validates both access and secret keys
                 try {
                     $buckets = $aws->s3->bucket->getList();
                 } catch (Exception $e) {
                     throw new Exception(sprintf(_("Failed to verify your EC2 access key and secret key: %s"), $e->getMessage()));
                 }
                 //Extract AWS Account ID
                 $pars[Entity\CloudCredentialsProperty::AWS_ACCOUNT_ID] = $aws->getAccountNumber();
                 try {
                     if ($ccProps[Entity\CloudCredentialsProperty::AWS_ACCOUNT_ID] != $pars[Entity\CloudCredentialsProperty::AWS_ACCOUNT_ID]) {
                         $this->db->Execute("DELETE FROM client_environment_properties WHERE name LIKE 'ec2.vpc.default%' AND env_id = ?", [$this->env->id]);
                     }
                 } catch (Exception $e) {
                 }
             } else {
                 $pars[Entity\CloudCredentialsProperty::AWS_ACCOUNT_ID] = $ccProps[Entity\CloudCredentialsProperty::AWS_ACCOUNT_ID];
             }
         } else {
             $this->response->failure();
             $this->response->data(['errors' => $this->checkVarError]);
             return;
         }
     }
     if ($enabled && $this->getContainer()->analytics->enabled && !empty($pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_BUCKET])) {
         try {
             $region = $pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_REGION];
             $aws = $this->env->aws($region, $pars[Entity\CloudCredentialsProperty::AWS_ACCESS_KEY], $pars[Entity\CloudCredentialsProperty::AWS_SECRET_KEY]);
             if (!empty($pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_PAYER_ACCOUNT]) && $aws->getAccountNumber() != $pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_PAYER_ACCOUNT]) {
                 $payerCredentials = $this->getUser()->getAccount()->cloudCredentialsList([SERVER_PLATFORMS::EC2], [], [Entity\CloudCredentialsProperty::AWS_ACCOUNT_ID => [['value' => $pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_PAYER_ACCOUNT]]]]);
                 if (count($payerCredentials) == 0) {
                     throw new Exception("Payer account not found!");
                 }
                 $payerCredentials = $payerCredentials->current();
                 $aws = $this->env->aws($region, $payerCredentials->properties[Entity\CloudCredentialsProperty::AWS_ACCESS_KEY], $payerCredentials->properties[Entity\CloudCredentialsProperty::AWS_SECRET_KEY], !empty($payerCredentials->properties[Entity\CloudCredentialsProperty::AWS_CERTIFICATE]) ? $payerCredentials->properties[Entity\CloudCredentialsProperty::AWS_CERTIFICATE] : null, !empty($payerCredentials->properties[Entity\CloudCredentialsProperty::AWS_PRIVATE_KEY]) ? $payerCredentials->properties[Entity\CloudCredentialsProperty::AWS_PRIVATE_KEY] : null);
             }
             try {
                 $bucketObjects = $aws->s3->bucket->listObjects($pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_BUCKET]);
             } catch (ClientException $e) {
                 if ($e->getErrorData() && $e->getErrorData()->getCode() == ErrorData::ERR_AUTHORIZATION_HEADER_MALFORMED && preg_match("/expecting\\s+'(.+?)'/", $e->getMessage(), $matches) && in_array($matches[1], Aws::getCloudLocations())) {
                     $expectingRegion = $matches[1];
                     if (isset($payerCredentials)) {
                         $aws = $this->env->aws($expectingRegion, $payerCredentials->properties[Entity\CloudCredentialsProperty::AWS_ACCESS_KEY], $payerCredentials->properties[Entity\CloudCredentialsProperty::AWS_SECRET_KEY], !empty($payerCredentials->properties[Entity\CloudCredentialsProperty::AWS_CERTIFICATE]) ? $payerCredentials->properties[Entity\CloudCredentialsProperty::AWS_CERTIFICATE] : null, !empty($payerCredentials->properties[Entity\CloudCredentialsProperty::AWS_PRIVATE_KEY]) ? $payerCredentials->properties[Entity\CloudCredentialsProperty::AWS_PRIVATE_KEY] : null);
                     } else {
                         $aws = $this->env->aws($expectingRegion, $pars[Entity\CloudCredentialsProperty::AWS_ACCESS_KEY], $pars[Entity\CloudCredentialsProperty::AWS_SECRET_KEY]);
                     }
                     $bucketObjects = $aws->s3->bucket->listObjects($pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_BUCKET]);
                     $pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_REGION] = $expectingRegion;
                 } else {
                     throw $e;
                 }
             }
             $objectName = (empty($pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_PAYER_ACCOUNT]) ? '' : "{$pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_PAYER_ACCOUNT]}-") . 'aws-billing-detailed-line-items-with-resources-and-tags';
             $objectExists = false;
             $bucketObjectName = null;
             foreach ($bucketObjects as $bucketObject) {
                 /* @var $bucketObject Scalr\Service\Aws\S3\DataType\ObjectData */
                 if (strpos($bucketObject->objectName, $objectName) !== false) {
                     $bucketObjectName = $bucketObject->objectName;
                     $pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_ENABLED] = 1;
                     $objectExists = true;
                     break;
                 }
             }
             if (!$objectExists) {
                 $this->response->failure();
                 $this->response->data(['errors' => [Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_PAYER_ACCOUNT => "Object with name '{$objectName}' does not exist."]]);
                 return;
             }
             $aws->s3->object->getMetadata($pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_BUCKET], $bucketObjectName);
         } catch (Exception $e) {
             $this->response->failure();
             $this->response->data(['errors' => [Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_BUCKET => sprintf("Cannot access billing bucket with name %s. Error: %s", $pars[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_BUCKET], $e->getMessage())]]);
             return;
         }
     }
     $this->db->BeginTrans();
     try {
         $this->env->enablePlatform(SERVER_PLATFORMS::EC2, $enabled);
         if ($enabled) {
             $this->makeCloudCredentials(SERVER_PLATFORMS::EC2, $pars);
             if ($this->getContainer()->analytics->enabled && $bNew) {
                 $this->getContainer()->analytics->notifications->onCloudAdd('ec2', $this->env, $this->user);
             }
         }
         if (!$this->user->getAccount()->getSetting(Scalr_Account::SETTING_DATE_ENV_CONFIGURED)) {
             $this->user->getAccount()->setSetting(Scalr_Account::SETTING_DATE_ENV_CONFIGURED, time());
         }
         //TODO: cloud suspension info must work with cloud credentials
         if ($enabled && $this->env->status == Scalr_Environment::STATUS_INACTIVE && $this->env->getPlatformConfigValue('system.auto-disable-reason')) {
             // env was inactive due invalid keys for amazon, activate it
             $this->env->status = Scalr_Environment::STATUS_ACTIVE;
             $this->env->save();
             $this->env->setPlatformConfig(['system.auto-disable-reason' => NULL]);
             $envAutoEnabled = true;
         }
         $this->db->CommitTrans();
     } catch (Exception $e) {
         $this->db->RollbackTrans();
         throw new Exception(_("Failed to save AWS settings: {$e->getMessage()}"));
     }
     $this->response->success('Cloud credentials have been ' . ($enabled ? 'saved' : 'removed from Scalr'));
     $this->response->data(['enabled' => $enabled, 'demoFarm' => $demoFarm, 'envAutoEnabled' => $envAutoEnabled]);
 }
Пример #5
0
 /**
  * Constructor
  *
  * @param    \Scalr\Service\Aws   $aws  AWS Instance for the specified region and
  *                                      that is associated with this service.
  * @throws   ElbException
  */
 public function __construct(\Scalr\Service\Aws $aws)
 {
     $this->aws = $aws;
     $this->class = get_class($this);
     $this->setApiVersion($this->getCurrentApiVersion());
     $this->em = $aws->getEntityManager();
 }
Пример #6
0
 /**
  * Calls Amazon web service method.
  *
  * It ensures execution of the certain AWS action by transporting the request
  * and receiving response.
  *
  * @param     string    $action           REST METHOD (GET|PUT|DELETE|HEAD etc).
  * @param     array     $options          An options array. It's used to send headers for the HTTP request.
  *                                        extra options: _subdomain, _putData, _putFile
  * @param     string    $path    optional A relative path.
  * @return    ClientResponseInterface
  * @throws    ClientException
  */
 public function call($action, $options, $path = '/')
 {
     $httpRequest = $this->createRequest();
     $httpMethod = $action ?: 'GET';
     if (substr($path, 0, 1) !== '/') {
         $path = '/' . $path;
     }
     $this->lastApiCall = null;
     $eventObserver = $this->getAws()->getEventObserver();
     if ($eventObserver && $eventObserver->isSubscribed(EventType::EVENT_SEND_REQUEST)) {
         foreach (debug_backtrace() as $arr) {
             if (empty($arr['class']) || !preg_match("/\\\\Service\\\\Aws\\\\.+Api\$/", $arr['class']) || $arr['type'] !== '->') {
                 continue;
             }
             $this->lastApiCall = ucfirst($arr['function']);
             break;
         }
     }
     //Wipes out extra options from headers and moves them to separate array.
     //It also collects an x-amz headers.
     $extraOptions = ['region' => null];
     foreach ($options as $key => $val) {
         if (substr($key, 0, 1) === '_') {
             $extraOptions[substr($key, 1)] = $val;
             unset($options[$key]);
         }
     }
     if (!isset($options['Host'])) {
         $options['Host'] = (isset($extraOptions['subdomain']) ? $extraOptions['subdomain'] . '.' : '') . $this->url;
     }
     if (strpos($options['Host'], 'http') === 0) {
         $arr = parse_url($options['Host']);
         $scheme = $arr['scheme'];
         $options['Host'] = $arr['host'] . (isset($arr['port']) ? ':' . $arr['port'] : '');
         $path = (!empty($arr['path']) && $arr['path'] != '/' ? rtrim($arr['path'], '/') : '') . $path;
     } else {
         $scheme = 'https';
     }
     if ($httpMethod === 'PUT' || $httpMethod === 'POST') {
         if (!empty($extraOptions['putData'])) {
             $httpRequest->append($extraOptions['putData']);
             $options['Content-Md5'] = Aws::getMd5Base64Digest($extraOptions['putData']);
         } elseif (!empty($extraOptions['putFile'])) {
             $httpRequest->addFiles([$extraOptions['putFile']]);
             $options['Content-Md5'] = Aws::getMd5Base64DigestFile($extraOptions['putFile']);
         }
     }
     $httpRequest->setRequestUrl($scheme . '://' . $options['Host'] . $path);
     $httpRequest->setRequestMethod($httpMethod);
     $httpRequest->addHeaders($options);
     if (true) {
         $this->signRequestV4($httpRequest, !empty($extraOptions['region']) ? $extraOptions['region'] : null, empty($extraOptions['putFile']) ? null : $extraOptions['putFile']);
     } else {
         $this->signRequestV3($httpRequest, isset($extraOptions['subdomain']) ? $extraOptions['subdomain'] : null);
     }
     $response = $this->tryCall($httpRequest);
     if ($this->getAws() && $this->getAws()->getDebug()) {
         echo "\n", "{$httpRequest}\n", "{$response->getResponse()}\n";
     }
     return $response;
 }
Пример #7
0
 /**
  * {@inheritdoc}
  * @see ApiEntityAdapter::validateEntity()
  */
 public function validateEntity($entity)
 {
     if (!$entity instanceof Farm) {
         throw new InvalidArgumentException(sprintf("First argument must be instance of Scalr\\Model\\Entity\\Farm class"));
     }
     if ($entity->id !== null) {
         if (!Farm::findPk($entity->id)) {
             throw new ApiErrorException(404, ErrorMessage::ERR_OBJECT_NOT_FOUND, sprintf("Could not find out the Farm with ID: %d", $entity->id));
         }
     } else {
         if (empty($entity->name)) {
             throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "Missed property name");
         }
         $criteria = $this->controller->getScopeCriteria();
         $criteria[] = ['name' => $entity->name];
         if (count(Farm::find($criteria))) {
             throw new ApiErrorException(409, ErrorMessage::ERR_UNICITY_VIOLATION, "Farm with name '{$entity->name}' already exists");
         }
     }
     if (!empty($entity->settings[FarmSetting::EC2_VPC_REGION])) {
         $region = $entity->settings[FarmSetting::EC2_VPC_REGION];
         $vpcId = $entity->settings[FarmSetting::EC2_VPC_ID];
         if (!in_array($region, Aws::getCloudLocations())) {
             throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Unknown VPC region");
         }
         $gov = new Scalr_Governance($this->controller->getEnvironment()->id);
         $vpcGovernanceRegions = $gov->getValue(SERVER_PLATFORMS::EC2, Scalr_Governance::AWS_VPC, 'regions');
         if (isset($vpcGovernanceRegions)) {
             if (!array_key_exists($region, $vpcGovernanceRegions)) {
                 $regions = array_keys($vpcGovernanceRegions);
                 throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, sprintf("Only %s %s allowed according to governance settings", ...count($regions) > 1 ? [implode(', ', $regions), 'regions are'] : [array_shift($regions), 'region is']));
             }
             $vpcGovernanceIds = $vpcGovernanceRegions[$region]['ids'];
             if (!empty($vpcGovernanceIds) && !in_array($vpcId, $vpcGovernanceIds)) {
                 throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, sprintf("Only %s %s allowed according to governance settings", ...count($vpcGovernanceIds) > 1 ? [implode(', ', $vpcGovernanceIds), 'vpcs are'] : [array_shift($vpcGovernanceIds), 'vpc is']));
             }
         }
         $found = null;
         /* @var $vpc VpcData */
         //TODO rewrite aws service usage
         foreach ($this->controller->getContainer()->aws($region, $this->controller->getEnvironment())->ec2->vpc->describe() as $vpc) {
             if ($vpcId == $vpc->vpcId) {
                 $found = $vpc;
             }
         }
         if (empty($found)) {
             throw new ApiErrorException(400, ErrorMessage::ERR_OBJECT_NOT_FOUND, "Could not find out the VPC with ID '{$vpcId}' in region '{$region}'");
         }
     } else {
         if (!empty($entity->settings[FarmSetting::EC2_VPC_ID])) {
             throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "Missed property vpc.region");
         }
     }
     if (\Scalr::config('scalr.analytics.enabled')) {
         if (isset($entity->settings[FarmSetting::PROJECT_ID])) {
             if (!$this->controller->getContainer()->analytics->projects->get($entity->settings[FarmSetting::PROJECT_ID])) {
                 throw new ApiErrorException(403, ErrorMessage::ERR_PERMISSION_VIOLATION, "The project is not allowed for you");
             }
         } else {
             throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "Missed property project");
         }
     }
     if (!$this->controller->hasPermissions($entity, true)) {
         //Checks entity level write access permissions
         throw new ApiErrorException(403, ErrorMessage::ERR_PERMISSION_VIOLATION, "Insufficient permissions");
     }
 }
Пример #8
0
 /**
  * @test
  */
 public function testImagesFunctional()
 {
     $testName = str_replace('-', '', $this->getTestName());
     $images = null;
     $uri = self::getUserApiUrl('/images');
     do {
         $query = [];
         if (isset($images->pagination->next)) {
             $parts = parse_url($images->pagination->next);
             parse_str($parts['query'], $query);
         }
         $describe = $this->request($uri, Request::METHOD_GET, $query);
         $this->assertDescribeResponseNotEmpty($describe);
         $images = $describe->getBody();
         foreach ($images->data as $image) {
             $this->assertImageObjectNotEmpty($image);
             if (strpos($image->name, $testName) !== false) {
                 $delete = $this->request($uri . '/' . $image->id, Request::METHOD_DELETE);
                 $this->assertEquals(200, $delete->response->getStatus());
             }
         }
     } while (!empty($images->pagination->next));
     // test create action
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => 'invalid']);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_VALUE, 'Invalid scope');
     $create = $this->request($uri, Request::METHOD_POST);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_STRUCTURE, 'Invalid body');
     $create = $this->request($uri, Request::METHOD_POST, [], ['invalid' => 'value']);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_STRUCTURE, 'You are trying to set');
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => ScopeInterface::SCOPE_ENVIRONMENT, 'name' => 'invalidName^$&&']);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_VALUE, 'Invalid name of the Image');
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => ScopeInterface::SCOPE_ENVIRONMENT, 'architecture' => 'invalid', 'name' => $testName]);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_VALUE, 'Invalid architecture of the Image');
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => ScopeInterface::SCOPE_ENVIRONMENT, 'name' => $testName]);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_STRUCTURE, 'OS must be provided with the request');
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => ScopeInterface::SCOPE_ENVIRONMENT, 'name' => $testName, 'os' => ['id' => 'invalidOsId']]);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_VALUE, 'Specified OS does not exist');
     $os = Os::findOne([['status' => Os::STATUS_ACTIVE]]);
     /* @var $os Os */
     $env = \Scalr_Environment::init()->loadById(static::$testEnvId);
     $platform = \SERVER_PLATFORMS::EC2;
     if ($env->isPlatformEnabled($platform)) {
         $env->setPlatformConfig([$platform . '.is_enabled' => 0]);
     }
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => ScopeInterface::SCOPE_ENVIRONMENT, 'name' => $testName, 'os' => ['id' => $os->id], 'cloudPlatform' => $platform]);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_INVALID_VALUE, $create);
     $this->assertErrorMessageStatusEquals(400, $create);
     $env->setPlatformConfig([$platform . '.is_enabled' => 1]);
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => ScopeInterface::SCOPE_ENVIRONMENT, 'name' => $testName, 'os' => ['invalid'], 'cloudPlatform' => $platform]);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_VALUE, 'Invalid identifier of the OS');
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => ScopeInterface::SCOPE_ENVIRONMENT, 'name' => $testName, 'os' => ['id' => $os->id], 'cloudPlatform' => $platform]);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_VALUE, 'Unable to find the requested image on the cloud');
     $region = null;
     $cloudImageId = null;
     foreach (Aws::getCloudLocations() as $cloudLocation) {
         $cloudImageId = $this->getNewImageId($env, $cloudLocation);
         if (!empty($cloudImageId)) {
             $region = $cloudLocation;
             break;
         }
     }
     $this->assertNotNull($cloudImageId);
     $this->assertNotNull($cloudLocation);
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => ScopeInterface::SCOPE_ENVIRONMENT, 'name' => $testName, 'os' => $os->id, 'cloudPlatform' => $platform, 'cloudLocation' => $region, 'cloudImageId' => $cloudImageId]);
     $this->assertFetchResponseNotEmpty($create);
     $imageBody = $create->getBody();
     $this->assertImageObjectNotEmpty($imageBody->data);
     $this->assertEquals(201, $create->response->getStatus());
     $this->assertNotEmpty($imageBody->data->id);
     $this->assertEquals(ScopeInterface::SCOPE_ENVIRONMENT, $imageBody->data->scope);
     $this->assertEquals($testName, $imageBody->data->name);
     $this->assertEquals($os->id, $imageBody->data->os->id);
     $this->assertEquals($platform, $imageBody->data->cloudPlatform);
     $this->assertEquals($region, $imageBody->data->cloudLocation);
     $this->assertEquals($cloudImageId, $imageBody->data->cloudImageId);
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => ScopeInterface::SCOPE_ENVIRONMENT, 'name' => $testName, 'os' => ['id' => $os->id], 'cloudPlatform' => $platform, 'cloudLocation' => $region, 'cloudImageId' => $cloudImageId]);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_UNICITY_VIOLATION, $create);
     $this->assertErrorMessageStatusEquals(409, $create);
     // test filtering
     $describe = $this->request($uri, Request::METHOD_GET, ['scope' => ScopeInterface::SCOPE_ENVIRONMENT]);
     $this->assertDescribeResponseNotEmpty($describe);
     foreach ($describe->getBody()->data as $data) {
         $this->assertImageObjectNotEmpty($data);
         $this->assertEquals(ScopeInterface::SCOPE_ENVIRONMENT, $data->scope);
     }
     $describe = $this->request($uri, Request::METHOD_GET, ['name' => $testName]);
     $this->assertDescribeResponseNotEmpty($describe);
     foreach ($describe->getBody()->data as $data) {
         $this->assertImageObjectNotEmpty($data);
         $this->assertEquals($testName, $data->name);
     }
     $describe = $this->request($uri, Request::METHOD_GET, ['id' => $imageBody->data->id]);
     $this->assertDescribeResponseNotEmpty($describe);
     foreach ($describe->getBody()->data as $data) {
         $this->assertImageObjectNotEmpty($data);
         $this->assertEquals($imageBody->data->id, $data->id);
     }
     $describe = $this->request($uri, Request::METHOD_GET, ['os' => $os->id]);
     $this->assertDescribeResponseNotEmpty($describe);
     foreach ($describe->getBody()->data as $data) {
         $this->assertImageObjectNotEmpty($data);
         $this->assertEquals($os->id, $data->os->id);
     }
     $describe = $this->request($uri, Request::METHOD_GET, ['os' => 'invalid*&^^%']);
     $this->assertErrorMessageContains($describe, 400, ErrorMessage::ERR_INVALID_VALUE, "Invalid identifier of the OS");
     $describe = $this->request($uri, Request::METHOD_GET, ['cloudPlatform' => $platform, 'cloudLocation' => $region]);
     $this->assertDescribeResponseNotEmpty($describe);
     foreach ($describe->getBody()->data as $data) {
         $this->assertImageObjectNotEmpty($data);
         $this->assertEquals($platform, $data->cloudPlatform);
         $this->assertEquals($region, $data->cloudLocation);
     }
     $describe = $this->request($uri, Request::METHOD_GET, ['cloudLocation' => $region]);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_INVALID_STRUCTURE, $describe);
     $this->assertErrorMessageStatusEquals(400, $describe);
     $describe = $this->request($uri, Request::METHOD_GET, ['cloudImageId' => $cloudImageId]);
     $this->assertDescribeResponseNotEmpty($describe);
     foreach ($describe->getBody()->data as $data) {
         $this->assertImageObjectNotEmpty($data);
         $this->assertEquals($cloudImageId, $data->cloudImageId);
     }
     // test modify action
     $modify = $this->request($uri, Request::METHOD_PATCH, [], ['name' => $testName . 'modify']);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_ENDPOINT_NOT_FOUND, $modify);
     $this->assertErrorMessageStatusEquals(404, $modify);
     $modify = $this->request($uri . '/' . $imageBody->data->id, Request::METHOD_PATCH, [], ['invalid' => $testName . 'modify']);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_INVALID_STRUCTURE, $modify);
     $this->assertErrorMessageStatusEquals(400, $modify);
     $modify = $this->request($uri . '/' . $imageBody->data->id, Request::METHOD_PATCH, [], ['id' => $testName . 'modify']);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_INVALID_STRUCTURE, $modify);
     $this->assertErrorMessageStatusEquals(400, $modify);
     $modify = $this->request($uri . '/' . $imageBody->data->id, Request::METHOD_PATCH, [], ['scope' => $testName . 'modify']);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_INVALID_VALUE, $modify);
     $this->assertErrorMessageStatusEquals(400, $modify);
     $notFoundId = '11111111-1111-1111-1111-111111111111';
     $modify = $this->request($uri . '/' . $notFoundId, Request::METHOD_PATCH, [], ['name' => $testName . 'modify']);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_OBJECT_NOT_FOUND, $modify);
     $this->assertErrorMessageStatusEquals(404, $modify);
     $entity = Image::findOne([['envId' => null], ['status' => Image::STATUS_ACTIVE]]);
     /* @var $entity Image */
     $this->assertNotEmpty($entity);
     $notAccessibleId = $entity->hash;
     $modify = $this->request($uri . '/' . $notAccessibleId, Request::METHOD_PATCH, [], ['name' => $testName . 'modify']);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_SCOPE_VIOLATION, $modify);
     $this->assertErrorMessageStatusEquals(403, $modify);
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => ScopeInterface::SCOPE_ENVIRONMENT, 'name' => $testName, 'os' => ['id' => $entity->osId], 'cloudPlatform' => $entity->platform, 'cloudLocation' => $entity->cloudLocation, 'cloudImageId' => $entity->id]);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_UNICITY_VIOLATION, $create);
     $this->assertErrorMessageStatusEquals(409, $create);
     // test fetch action
     $fetch = $this->request($uri . '/' . $notFoundId, Request::METHOD_GET);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_OBJECT_NOT_FOUND, $fetch);
     $this->assertErrorMessageStatusEquals(404, $fetch);
     $fetch = $this->request($uri . '/' . $imageBody->data->id, Request::METHOD_GET);
     $this->assertFetchResponseNotEmpty($fetch);
     $fetchBody = $fetch->getBody();
     $this->assertImageObjectNotEmpty($fetchBody->data);
     $this->assertEquals($imageBody->data->id, $fetchBody->data->id);
     $fetch = $this->request($uri . '/' . $entity->hash, Request::METHOD_GET);
     $this->assertFetchResponseNotEmpty($fetch);
     $fetchBody = $fetch->getBody();
     $this->assertImageObjectNotEmpty($fetchBody->data);
     $this->assertEquals($entity->hash, $fetchBody->data->id);
     $modify = $this->request($uri . '/' . $imageBody->data->id, Request::METHOD_PATCH, [], ['name' => $testName . 'modify']);
     $this->assertEquals(200, $modify->response->getStatus());
     $this->assertImageObjectNotEmpty($modify->getBody()->data);
     $this->assertEquals($testName . 'modify', $modify->getBody()->data->name);
     // test copy action
     $copy = $this->request($uri . '/' . $imageBody->data->id . '/actions/copy', Request::METHOD_POST);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_INVALID_STRUCTURE, $copy);
     $this->assertErrorMessageStatusEquals(400, $copy);
     $copy = $this->request($uri . '/' . $imageBody->data->id . '/actions/copy', Request::METHOD_POST, [], ['cloudLocation' => 'invalid', 'cloudPlatform' => 'ec2']);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_INVALID_VALUE, $copy);
     $this->assertErrorMessageStatusEquals(400, $copy);
     $copy = $this->request($uri . '/' . $imageBody->data->id . '/actions/copy', Request::METHOD_POST, [], ['cloudLocation' => Aws::REGION_US_EAST_1, 'cloudPlatform' => 'gce']);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_INVALID_VALUE, $copy);
     $this->assertErrorMessageStatusEquals(400, $copy);
     $copy = $this->request($uri . '/' . $imageBody->data->id . '/actions/copy', Request::METHOD_POST, [], ['cloudLocation' => $region, 'cloudPlatform' => 'ec2']);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_BAD_REQUEST, $copy);
     $this->assertErrorMessageStatusEquals(400, $copy);
     $awsRegions = Aws::getCloudLocations();
     $copyTo = null;
     foreach ($awsRegions as $awsRegion) {
         if ($awsRegion != $region) {
             $copyTo = $awsRegion;
             break;
         }
     }
     $this->assertNotNull($copyTo);
     $copy = $this->request($uri . '/' . $notAccessibleId . '/actions/copy', Request::METHOD_POST, [], ['cloudLocation' => $copyTo, 'cloudPlatform' => \SERVER_PLATFORMS::EC2]);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_SCOPE_VIOLATION, $copy);
     $this->assertErrorMessageStatusEquals(403, $copy);
     $copy = $this->request($uri . '/' . $imageBody->data->id . '/actions/copy', Request::METHOD_POST, [], ['cloudLocation' => $copyTo, 'cloudPlatform' => \SERVER_PLATFORMS::EC2]);
     $copyBody = $copy->getBody();
     $this->assertEquals(202, $copy->response->getStatus());
     $this->assertFetchResponseNotEmpty($copy);
     $this->assertImageObjectNotEmpty($copyBody->data);
     $this->assertEquals(\SERVER_PLATFORMS::EC2, $copyBody->data->cloudPlatform);
     $this->assertEquals($copyTo, $copyBody->data->cloudLocation);
     // test delete action
     $delete = $this->request($uri . '/' . $notFoundId, Request::METHOD_DELETE);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_OBJECT_NOT_FOUND, $delete);
     $this->assertErrorMessageStatusEquals(404, $delete);
     $delete = $this->request($uri . '/' . $entity->hash, Request::METHOD_DELETE);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_SCOPE_VIOLATION, $delete);
     $this->assertErrorMessageStatusEquals(403, $delete);
     $delete = $this->request($uri . '/' . $copyBody->data->id, Request::METHOD_DELETE);
     $this->assertEquals(200, $delete->response->getStatus());
     $delete = $this->request($uri . '/' . $imageBody->data->id, Request::METHOD_DELETE);
     $this->assertEquals(200, $delete->response->getStatus());
 }
Пример #9
0
 /**
  * Get db instance details
  *
  * @param Aws                    $aws
  * @param DBInstanceData         $dbinstance
  * @param SecurityGroupList|null $vpcSglist      optional
  * @param DBClusterList|null     $clusters       optional
  * @return mixed
  * @throws Scalr_Exception_Core
  */
 private function getDbInstanceData(Aws $aws, DBInstanceData $dbinstance, $vpcSglist = null, $clusters = null)
 {
     $cloudLocation = $aws->getRegion();
     $createdTime = $dbinstance->instanceCreateTime;
     $dbinstance = $dbinstance->toArray(true);
     foreach ($dbinstance['VpcSecurityGroups'] as &$vpcSg) {
         $vpcSecurityGroupName = null;
         if (isset($vpcSglist)) {
             foreach ($vpcSglist as $vpcSqData) {
                 /* @var $vpcSqData \Scalr\Service\Aws\Ec2\DataType\SecurityGroupData */
                 if ($vpcSqData->groupId == $vpcSg['VpcSecurityGroupId']) {
                     $vpcSecurityGroupName = $vpcSqData->groupName;
                     $vpcId = $vpcSqData->vpcId;
                     break;
                 }
             }
         }
         $vpcSg = ['vpcSecurityGroupId' => $vpcSg['VpcSecurityGroupId'], 'vpcSecurityGroupName' => $vpcSecurityGroupName];
     }
     $dbinstance['VpcId'] = !empty($vpcId) ? $vpcId : null;
     $dbinstance['DBSubnetGroupName'] = isset($dbinstance['DBSubnetGroup']['DBSubnetGroupName']) ? $dbinstance['DBSubnetGroup']['DBSubnetGroupName'] : null;
     foreach ($dbinstance['DBSecurityGroups'] as &$dbSg) {
         $dbSg = $dbSg['DBSecurityGroupName'];
     }
     foreach ($dbinstance['OptionGroupMembership'] as &$member) {
         $dbinstance['OptionGroupName'] = $member['OptionGroupName'];
         break;
     }
     foreach ($dbinstance['DBParameterGroups'] as &$param) {
         $dbinstance['DBParameterGroup'] = $param['DBParameterGroupName'];
         break;
     }
     $dbinstance['Address'] = $dbinstance['Endpoint']['Address'];
     $dbinstance['Port'] = (string) $dbinstance['Endpoint']['Port'];
     $dbinstance['InstanceCreateTime'] = Scalr_Util_DateTime::convertTz($createdTime);
     $dbinstance['AllocatedStorage'] = (string) $dbinstance['AllocatedStorage'];
     if ($dbinstance['StorageEncrypted']) {
         /* @var $key Aws\Kms\DataType\AliasData */
         foreach ($aws->kms->alias->list() as $key) {
             if (str_replace($key->aliasName, "key/{$key->targetKeyId}", $key->aliasArn) == $dbinstance['KmsKeyId']) {
                 $dbinstance['KmsKeyId'] = $key->aliasName;
                 break;
             }
         }
     }
     if (!empty($dbinstance['DBClusterIdentifier']) && isset($clusters)) {
         foreach ($clusters as $cluster) {
             /* @var $cluster DBClusterData */
             if ($cluster->dBClusterIdentifier == $dbinstance['DBClusterIdentifier']) {
                 foreach ($cluster->dBClusterMembers as $member) {
                     /* @var $member ClusterMemberData */
                     if ($dbinstance['DBInstanceIdentifier'] == $member->dBInstanceIdentifier) {
                         $dbinstance['isReplica'] = !$member->isClusterWriter;
                         break;
                     }
                 }
                 $dbinstance['MultiAZ'] = true;
                 break;
             }
         }
     } else {
         $dbinstance['isReplica'] = !empty($dbinstance['ReadReplicaSourceDBInstanceIdentifier']) ? true : false;
         $dbinstance['MultiAZ'] = $dbinstance['MultiAZ'];
     }
     /* @var $cloudResource CloudResource */
     $cloudResource = CloudResource::findPk($dbinstance['DBInstanceIdentifier'], CloudResource::TYPE_AWS_RDS, $this->getEnvironmentId(), \SERVER_PLATFORMS::EC2, $cloudLocation);
     if ($cloudResource) {
         $dbinstance['farmId'] = $cloudResource->farmId;
         $dbinstance['farmName'] = $this->db->GetOne("SELECT name FROM farms WHERE id=? LIMIT 1", [$cloudResource->farmId]);
     }
     return $dbinstance;
 }
Пример #10
0
 /**
  * {@inheritdoc}
  * @see ApiEntityAdapter::validateEntity()
  */
 public function validateEntity($entity)
 {
     if (!$entity instanceof FarmRole) {
         throw new InvalidArgumentException(sprintf("First argument must be instance of Scalr\\Model\\Entity\\FarmRole class"));
     }
     if ($entity->id !== null) {
         if (!FarmRole::findPk($entity->id)) {
             throw new ApiErrorException(404, ErrorMessage::ERR_OBJECT_NOT_FOUND, sprintf("Could not find out the Farm with ID: %d", $entity->id));
         }
     }
     if (empty($entity->farmId)) {
         throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "Missed property farm.id");
     } else {
         /* @var  $farm Farm */
         $farm = $this->controller->getFarm($entity->farmId, true);
     }
     if (empty($entity->alias)) {
         throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "Missed property alias");
     }
     if (!preg_match("/^[[:alnum:]](?:-*[[:alnum:]])*\$/", $entity->alias)) {
         throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Alias should start and end with letter or number and contain only letters, numbers and dashes.");
     }
     if (empty($entity->roleId)) {
         throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "Missed property role.id");
     }
     $roleBehaviors = $entity->getRole()->getBehaviors();
     $uniqueBehaviors = array_intersect($roleBehaviors, array_merge(...array_values(static::$uniqueFarmBehaviors)));
     if (!empty($uniqueBehaviors)) {
         //farm can include only one mysql or percona role
         if (array_intersect($uniqueBehaviors, static::$uniqueFarmBehaviors[ROLE_BEHAVIORS::MYSQL])) {
             $uniqueBehaviors = array_merge($uniqueBehaviors, array_diff(static::$uniqueFarmBehaviors[ROLE_BEHAVIORS::MYSQL], $uniqueBehaviors));
         }
         $farmRoleEntity = new FarmRole();
         $roleEntity = new Role();
         /* @var $conflicts EntityIterator */
         $conflicts = Role::find([AbstractEntity::STMT_FROM => "{$roleEntity->table()} JOIN {$farmRoleEntity->table('fr')} ON {$farmRoleEntity->columnRoleId('fr')} = {$roleEntity->columnId()}", AbstractEntity::STMT_WHERE => "{$farmRoleEntity->columnFarmId('fr')} = {$farmRoleEntity->qstr('farmId', $entity->farmId)} " . (empty($entity->id) ? '' : " AND {$farmRoleEntity->columnId('fr')} <> {$farmRoleEntity->qstr('id', $entity->id)}"), ['behaviors' => ['$regex' => implode('|', $uniqueBehaviors)]]]);
         if ($conflicts->count() > 0) {
             $conflictedBehaviors = [];
             /* @var  $role Role */
             foreach ($conflicts as $role) {
                 $conflictedBehaviors = array_merge($conflictedBehaviors, array_intersect($uniqueBehaviors, $role->getBehaviors()));
             }
             if (!empty(array_intersect($conflictedBehaviors, static::$uniqueFarmBehaviors[ROLE_BEHAVIORS::MYSQL]))) {
                 $conflictedBehaviors = array_diff($conflictedBehaviors, static::$uniqueFarmBehaviors[ROLE_BEHAVIORS::MYSQL]);
                 $conflictedBehaviors[] = 'mysql/percona';
             }
             $conflictedBehaviors = RoleAdapter::behaviorsToData($conflictedBehaviors);
             throw new ApiErrorException(409, ErrorMessage::ERR_UNICITY_VIOLATION, sprintf('Only one [%s] role can be added to farm', implode(', ', $conflictedBehaviors)));
         }
     }
     if (empty($entity->platform)) {
         throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "Missed property platform");
     }
     switch ($entity->platform) {
         case SERVER_PLATFORMS::EC2:
             if (empty($entity->settings[FarmRoleSetting::INSTANCE_TYPE])) {
                 throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Missed property instance.type");
             }
             /* @var $platform Ec2PlatformModule */
             $platform = PlatformFactory::NewPlatform(SERVER_PLATFORMS::EC2);
             if (!in_array($entity->settings[FarmRoleSetting::INSTANCE_TYPE], $platform->getInstanceTypes())) {
                 throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Wrong instance type");
             }
             $gov = new Scalr_Governance($this->controller->getEnvironment()->id);
             $allowGovernanceIns = $gov->getValue(SERVER_PLATFORMS::EC2, Scalr_Governance::INSTANCE_TYPE);
             if (isset($allowGovernanceIns) && !in_array($entity->settings[FarmRoleSetting::INSTANCE_TYPE], $allowGovernanceIns)) {
                 throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, sprintf("Only %s %s allowed according to governance settings", ...count($allowGovernanceIns) > 1 ? [implode(', ', $allowGovernanceIns), 'instances are'] : [array_shift($allowGovernanceIns), 'instance is']));
             }
             if (!in_array($entity->cloudLocation, Aws::getCloudLocations())) {
                 throw new ApiErrorException(404, ErrorMessage::ERR_OBJECT_NOT_FOUND, "Unknown region");
             }
             $vpcGovernanceRegions = $gov->getValue(SERVER_PLATFORMS::EC2, Scalr_Governance::AWS_VPC, 'regions');
             if (isset($vpcGovernanceRegions) && !array_key_exists($entity->cloudLocation, $vpcGovernanceRegions)) {
                 $regions = array_keys($vpcGovernanceRegions);
                 throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, sprintf("Only %s %s allowed according to governance settings", ...count($regions) > 1 ? [implode(', ', $regions), 'regions are'] : [array_shift($regions), 'region is']));
             }
             $env = Scalr_Environment::init()->loadById($this->controller->getEnvironment()->id);
             $aws = $this->controller->getContainer()->aws($entity->cloudLocation, $env);
             if (!empty($entity->settings[FarmRoleSetting::AWS_AVAIL_ZONE]) && $entity->settings[FarmRoleSetting::AWS_AVAIL_ZONE] !== 'x-scalr-diff') {
                 $availZones = explode(":", str_replace("x-scalr-custom=", '', $entity->settings[FarmRoleSetting::AWS_AVAIL_ZONE]));
                 $ec2availabilityZones = [];
                 foreach ($aws->ec2->availabilityZone->describe() as $zone) {
                     /* @var $zone AvailabilityZoneData */
                     if (stristr($zone->zoneState, 'available')) {
                         $ec2availabilityZones[] = $zone->zoneName;
                     }
                 }
                 $diffZones = array_diff($availZones, $ec2availabilityZones);
                 if (!empty($diffZones)) {
                     throw new ApiErrorException(404, ErrorMessage::ERR_OBJECT_NOT_FOUND, sprintf('%s %s available. Available zones are %s', ...count($diffZones) > 1 ? [implode(', ', $diffZones), 'zones are not', implode(', ', $ec2availabilityZones)] : [array_shift($diffZones), 'zone is not', implode(', ', $ec2availabilityZones)]));
                 }
             }
             if (!empty($farm->settings[FarmSetting::EC2_VPC_ID])) {
                 if (empty($entity->settings[FarmRoleSetting::AWS_VPC_SUBNET_ID])) {
                     throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "VPC Subnet(s) should be described");
                 }
                 $vpcId = $farm->settings[FarmSetting::EC2_VPC_ID];
                 $subnets = $platform->listSubnets($env, $entity->cloudLocation, $vpcId, true);
                 $vpcGovernanceIds = $gov->getValue(SERVER_PLATFORMS::EC2, Scalr_Governance::AWS_VPC, 'ids');
                 $subnetType = null;
                 foreach (json_decode($entity->settings[FarmRoleSetting::AWS_VPC_SUBNET_ID]) as $subnetId) {
                     $found = false;
                     foreach ($subnets as $subnet) {
                         if ($subnet['id'] == $subnetId) {
                             if ($subnetType == null) {
                                 $subnetType = $subnet['type'];
                             } else {
                                 if ($subnet['type'] != $subnetType) {
                                     throw new ApiErrorException(409, ErrorMessage::ERR_UNICITY_VIOLATION, "All subnets must be a same type");
                                 }
                             }
                             //check governance subnet settings
                             if (isset($vpcGovernanceIds[$vpcId])) {
                                 if (!empty($vpcGovernanceIds[$vpcId]) && is_array($vpcGovernanceIds[$vpcId]) && !in_array($subnetId, $vpcGovernanceIds[$vpcId])) {
                                     throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, sprintf("Only %s %s allowed by governance settings", ...count($vpcGovernanceIds[$vpcId]) > 1 ? [implode(', ', $vpcGovernanceIds[$vpcId]), 'subnets are'] : [array_shift($vpcGovernanceIds[$vpcId]), 'subnet is']));
                                 } else {
                                     if ($vpcGovernanceIds[$vpcId] == "outbound-only" && $subnetType != 'private') {
                                         throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Only private subnets allowed by governance settings");
                                     } else {
                                         if ($vpcGovernanceIds[$vpcId] == "full" && $subnetType != 'public') {
                                             throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Only public subnets allowed by governance settings");
                                         }
                                     }
                                 }
                             }
                             $found = true;
                         }
                     }
                     if (!$found) {
                         throw new ApiErrorException(404, ErrorMessage::ERR_OBJECT_NOT_FOUND, "Subnet with id '{$subnetId}' not found");
                     }
                 }
                 if (!empty($entity->settings[Scalr_Role_Behavior_Router::ROLE_VPC_SCALR_ROUTER_ID])) {
                     $router = $this->controller->getFarmRole($entity->settings[Scalr_Role_Behavior_Router::ROLE_VPC_SCALR_ROUTER_ID]);
                     if (empty($router->settings[Scalr_Role_Behavior_Router::ROLE_VPC_NID])) {
                         throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Farm-role with id '{$router->id}' is not a valid router");
                     }
                 } else {
                     if (\Scalr::config('scalr.instances_connection_policy') != 'local' && $subnetType == 'private') {
                         throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "You must describe a VPC Router");
                     }
                 }
             }
             break;
         default:
             if (isset(SERVER_PLATFORMS::GetList()[$entity->platform])) {
                 throw new ApiErrorException(501, ErrorMessage::ERR_NOT_IMPLEMENTED, "Platform '{$entity->platform}' is not supported yet");
             } else {
                 throw new ApiErrorException(404, ErrorMessage::ERR_OBJECT_NOT_FOUND, "Unknown platform '{$entity->platform}'");
             }
     }
     if (!$this->controller->hasPermissions($entity, true)) {
         //Checks entity level write access permissions
         throw new ApiErrorException(403, ErrorMessage::ERR_PERMISSION_VIOLATION, "Insufficient permissions");
     }
 }
Пример #11
0
 /**
  * @test
  */
 public function testAccountRolesFunctional()
 {
     $db = \Scalr::getDb();
     $testName = str_replace('-', '', static::getTestName());
     $roles = null;
     $uri = self::getAccountApiUrl('/roles');
     do {
         $query = [];
         if (isset($roles->pagination->next)) {
             $parts = parse_url($roles->pagination->next);
             parse_str($parts['query'], $query);
         }
         $describe = $this->request($uri, Request::METHOD_GET, $query);
         $this->assertDescribeResponseNotEmpty($describe);
         $this->assertNotEmpty($describe->getBody());
         $roles = $describe->getBody();
         foreach ($roles->data as $role) {
             $this->assertRolesObjectNotEmpty($role);
             if ($role->name == $testName) {
                 $delete = $this->request($uri . '/' . $role->id, Request::METHOD_DELETE);
                 $this->assertEquals(200, $delete->status);
             }
         }
     } while (!empty($roles->pagination->next));
     // test create action
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => 'invalid']);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_VALUE, 'Invalid scope');
     $create = $this->request($uri, Request::METHOD_POST);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_STRUCTURE, 'Invalid body');
     $create = $this->request($uri, Request::METHOD_POST, [], ['invalid' => 'value']);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_STRUCTURE, 'You are trying to set');
     $create = $this->request($uri, Request::METHOD_POST, [], ['id' => 'value']);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_VALUE, 'Invalid name');
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => ScopeInterface::SCOPE_ACCOUNT, 'name' => 'invalidName^$&&']);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_VALUE, 'Invalid name of the Role');
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => ScopeInterface::SCOPE_ACCOUNT, 'name' => $testName, 'description' => 'invalidDesc<br/>']);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_VALUE, 'Invalid description');
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => ScopeInterface::SCOPE_ACCOUNT, 'name' => $testName]);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_STRUCTURE, 'Role category should be provided');
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => ScopeInterface::SCOPE_ACCOUNT, 'name' => $testName, 'category' => ['id' => 'not int']]);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_VALUE, 'Invalid identifier of the category');
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => ScopeInterface::SCOPE_ACCOUNT, 'name' => $testName, 'category' => ['id' => -1]]);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_VALUE, 'The Role category does not exist');
     $rolesCat = RoleCategory::findOne();
     /* @var $rolesCat RoleCategory */
     $this->assertNotEmpty($rolesCat);
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => ScopeInterface::SCOPE_ACCOUNT, 'name' => $testName, 'category' => ['id' => $rolesCat->id]]);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_STRUCTURE, "Missed property 'os.id'");
     $os = Os::findOne([['status' => Os::STATUS_ACTIVE], ['family' => 'ubuntu'], ['generation' => '12.04']]);
     /* @var $os Os */
     $this->assertNotEmpty($os);
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => ScopeInterface::SCOPE_ACCOUNT, 'name' => $testName, 'category' => ['id' => $rolesCat->id], 'os' => ['id' => -1]]);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_VALUE, 'Invalid identifier of the OS');
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => ScopeInterface::SCOPE_ACCOUNT, 'name' => $testName, 'category' => ['id' => $rolesCat->id], 'os' => ['id' => 'invalid']]);
     $this->assertErrorMessageContains($create, 400, ErrorMessage::ERR_INVALID_VALUE, "OS with id 'invalid' not found.");
     $create = $this->request($uri, Request::METHOD_POST, [], ['scope' => ScopeInterface::SCOPE_ACCOUNT, 'name' => $testName, 'description' => $testName, 'category' => $rolesCat->id, 'os' => $os->id, 'quickStart' => true, 'deprecated' => true]);
     $body = $create->getBody();
     $this->assertEquals(201, $create->response->getStatus());
     $this->assertFetchResponseNotEmpty($create);
     $this->assertRolesObjectNotEmpty($body->data);
     $this->assertNotEmpty($body->data->id);
     $this->assertEquals($testName, $body->data->name);
     $this->assertEquals($testName, $body->data->description);
     $this->assertEquals(ScopeInterface::SCOPE_ACCOUNT, $body->data->scope);
     $this->assertEquals($rolesCat->id, $body->data->category->id);
     $this->assertEquals($os->id, $body->data->os->id);
     $this->assertEquals(true, $body->data->quickStart);
     $this->assertEquals(true, $body->data->deprecated);
     // test images actions
     $roleId = $body->data->id;
     $imagesUri = $uri . '/' . $roleId . '/images';
     $images = null;
     do {
         $query = [];
         if (isset($images->pagination->next)) {
             $parts = parse_url($images->pagination->next);
             parse_str($parts['query'], $query);
         }
         $describeImages = $this->request($imagesUri, Request::METHOD_GET, $query);
         $this->assertDescribeResponseNotEmpty($describeImages);
         $images = $describeImages->getBody();
         foreach ($images->data as $imageRole) {
             $this->assertRoleImageObjectNotEmpty($imageRole);
             $this->assertEquals($roleId, $imageRole->role->id);
             $image = Image::findPk($imageRole->image->id);
             /* @var $image Image */
             if ($image->name == $testName) {
                 $delete = $this->request($imagesUri . '/' . $imageRole->image->id, Request::METHOD_DELETE);
                 $this->assertEquals(200, $delete->status);
             }
         }
     } while (!empty($images->pagination->next));
     $env = \Scalr_Environment::init()->loadById(static::$testEnvId);
     $platform = \SERVER_PLATFORMS::EC2;
     if (!$env->isPlatformEnabled($platform)) {
         $env->setPlatformConfig([$platform . '.is_enabled' => 1]);
     }
     $region = null;
     $cloudImageId = null;
     foreach (Aws::getCloudLocations() as $cloudLocation) {
         $cloudImageId = $this->getNewImageId($env, $cloudLocation);
         if (!empty($cloudImageId)) {
             $region = $cloudLocation;
             break;
         }
     }
     $this->assertNotNull($cloudImageId);
     $this->assertNotNull($cloudLocation);
     $image = $this->createEntity(new Image(), ['accountId' => $this->getUser()->accountId, 'name' => $testName, 'osId' => $os->id, 'platform' => $platform, 'cloudLocation' => $region, 'id' => $cloudImageId, 'architecture' => 'x86_64', 'source' => Image::SOURCE_MANUAL, 'status' => Image::STATUS_ACTIVE]);
     $createRoleImage = $this->request($imagesUri, Request::METHOD_POST, [], ['role' => ['id' => $roleId + 10], 'image' => ['id' => $image->hash]]);
     $this->assertErrorMessageStatusEquals(400, $createRoleImage);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_INVALID_VALUE, $createRoleImage);
     $createRoleImage = $this->request($imagesUri, Request::METHOD_POST, [], ['role' => ['id' => $roleId]]);
     $this->assertErrorMessageStatusEquals(400, $createRoleImage);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_INVALID_STRUCTURE, $createRoleImage);
     $createRoleImage = $this->request($imagesUri, Request::METHOD_POST, [], ['role' => ['id' => $roleId], 'image' => ['id' => '11111111-1111-1111-1111-111111111111']]);
     $this->assertErrorMessageStatusEquals(404, $createRoleImage);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_INVALID_VALUE, $createRoleImage);
     $createRoleImage = $this->request($imagesUri, Request::METHOD_POST, [], ['role' => ['id' => $roleId], 'image' => ['id' => $image->hash]]);
     $createRoleImageBody = $createRoleImage->getBody();
     $this->assertEquals(201, $createRoleImage->response->getStatus());
     $this->assertFetchResponseNotEmpty($createRoleImage);
     $this->assertRoleImageObjectNotEmpty($createRoleImageBody->data);
     $createRoleImageError = $this->request($imagesUri, Request::METHOD_POST, [], ['role' => ['id' => $roleId], 'image' => ['id' => $image->hash]]);
     $this->assertErrorMessageStatusEquals(400, $createRoleImageError);
     $this->assertErrorMessageErrorEquals(ErrorMessage::ERR_BAD_REQUEST, $createRoleImageError);
     $fetchImage = $this->request($imagesUri . '/' . $createRoleImageBody->data->image->id, Request::METHOD_GET);
     $fetchImageBody = $fetchImage->getBody();
     $this->assertEquals(200, $fetchImage->response->getStatus());
     $this->assertFetchResponseNotEmpty($fetchImage);
     $this->assertImageObjectNotEmpty($fetchImageBody->data);
     $this->assertEquals($cloudImageId, $fetchImageBody->data->cloudImageId);
     $this->assertEquals($testName, $fetchImageBody->data->name);
     // test role images filtering
     $describeRoleImages = $this->request($imagesUri, Request::METHOD_GET, ['role' => $roleId]);
     $this->assertDescribeResponseNotEmpty($describeRoleImages);
     foreach ($describeRoleImages->getBody()->data as $data) {
         $this->assertRoleImageObjectNotEmpty($data);
         $this->assertEquals($roleId, $data->role->id);
     }
     $describeRoleImages = $this->request($imagesUri, Request::METHOD_GET, ['image' => $image->hash]);
     $this->assertDescribeResponseNotEmpty($describeRoleImages);
     foreach ($describeRoleImages->getBody()->data as $data) {
         $this->assertRoleImageObjectNotEmpty($data);
         $this->assertEquals($image->hash, $data->image->id);
     }
     $describeRoleImages = $this->request($imagesUri, Request::METHOD_GET, ['invalid' => 'value']);
     $this->assertErrorMessageContains($describeRoleImages, 400, ErrorMessage::ERR_INVALID_STRUCTURE, 'Unsupported filter');
     $currentRole = Role::findPk($roleId);
     /* @var $currentRole Role */
     $this->assertNotEmpty($currentRole);
     $adminImages = Image::find([['envId' => null], ['status' => Image::STATUS_ACTIVE], ['cloudLocation' => $region]]);
     $this->assertNotEmpty($adminImages);
     $adminImage = null;
     foreach ($adminImages as $aImage) {
         /* @var $aImage Image */
         $imageOs = $aImage->getOs();
         if (!empty($imageOs) && $imageOs->generation == $currentRole->getOs()->generation && $imageOs->family == $currentRole->getOs()->family) {
             $adminImage = $aImage;
             break;
         }
     }
     /* @var $adminImage Image */
     $this->assertNotEmpty($adminImage);
     $this->assertNotEquals($createRoleImageBody->data->image->id, $adminImage->hash);
     $replaceImage = $this->request($imagesUri . '/' . $createRoleImageBody->data->image->id . '/actions/replace', Request::METHOD_POST, [], ['role' => $roleId, 'image' => $adminImage->hash]);
     $replaceImageBody = $replaceImage->getBody();
     $this->assertEquals(200, $replaceImage->response->getStatus());
     $this->assertFetchResponseNotEmpty($replaceImage);
     $this->assertRoleImageObjectNotEmpty($replaceImageBody->data);
     $this->assertEquals($adminImage->hash, $replaceImageBody->data->image->id);
     $deleteImage = $this->request($imagesUri . '/' . $replaceImageBody->data->image->id, Request::METHOD_DELETE);
     $this->assertEquals(200, $deleteImage->response->getStatus());
     $delete = $this->request(static::getAccountApiUrl("images/{$image->hash}"), Request::METHOD_DELETE);
     $this->assertEquals(200, $delete->response->getStatus());
     // test get action
     $notFoundRoleId = 10 + $db->GetOne("SELECT MAX(r.id) FROM roles r");
     $get = $this->request($uri . '/' . $notFoundRoleId, Request::METHOD_GET);
     $this->assertErrorMessageContains($get, 404, ErrorMessage::ERR_OBJECT_NOT_FOUND, "The Role either does not exist or isn't in scope for the current Environment");
     $get = $this->request($uri . '/' . $body->data->id, Request::METHOD_GET);
     $getBody = $get->getBody();
     $this->assertEquals(200, $get->response->getStatus());
     $this->assertFetchResponseNotEmpty($get);
     $this->assertRolesObjectNotEmpty($getBody->data);
     $this->assertEquals($body->data->id, $getBody->data->id);
     $this->assertEquals($testName, $getBody->data->name);
     $this->assertEquals($testName, $getBody->data->description);
     $this->assertEquals(ScopeInterface::SCOPE_ACCOUNT, $getBody->data->scope);
     $this->assertEquals($rolesCat->id, $getBody->data->category->id);
     $this->assertEquals($os->id, $getBody->data->os->id);
     // test filters
     $describe = $this->request($uri, Request::METHOD_GET, ['description' => $testName]);
     $this->assertErrorMessageContains($describe, 400, ErrorMessage::ERR_INVALID_STRUCTURE, 'Unsupported filter');
     $describe = $this->request($uri, Request::METHOD_GET, ['scope' => 'wrong<br>']);
     $this->assertErrorMessageContains($describe, 400, ErrorMessage::ERR_INVALID_VALUE, 'Unexpected scope value');
     $describe = $this->request($uri, Request::METHOD_GET, ['scope' => ScopeInterface::SCOPE_SCALR]);
     $this->assertDescribeResponseNotEmpty($describe);
     foreach ($describe->getBody()->data as $data) {
         $this->assertRolesObjectNotEmpty($data);
     }
     $describe = $this->request($uri, Request::METHOD_GET, ['scope' => ScopeInterface::SCOPE_ACCOUNT]);
     $this->assertDescribeResponseNotEmpty($describe);
     foreach ($describe->getBody()->data as $data) {
         $this->assertRolesObjectNotEmpty($data);
         $this->assertEquals(ScopeInterface::SCOPE_ACCOUNT, $data->scope);
     }
     $describe = $this->request($uri, Request::METHOD_GET, ['name' => $testName]);
     $this->assertDescribeResponseNotEmpty($describe);
     foreach ($describe->getBody()->data as $data) {
         $this->assertRolesObjectNotEmpty($data);
         $this->assertEquals($testName, $data->name);
     }
     $describe = $this->request($uri, Request::METHOD_GET, ['id' => $roleId]);
     $this->assertDescribeResponseNotEmpty($describe);
     foreach ($describe->getBody()->data as $data) {
         $this->assertRolesObjectNotEmpty($data);
         $this->assertEquals($roleId, $data->id);
     }
     $describe = $this->request($uri, Request::METHOD_GET, ['os' => $os->id]);
     $this->assertDescribeResponseNotEmpty($describe);
     foreach ($describe->getBody()->data as $data) {
         $this->assertRolesObjectNotEmpty($data);
         $this->assertEquals($os->id, $data->os->id);
     }
     $describe = $this->request($uri, Request::METHOD_GET, ['os' => 'invalid*&^^%']);
     $this->assertErrorMessageContains($describe, 400, ErrorMessage::ERR_INVALID_VALUE, "Invalid identifier of the OS");
     $describe = $this->request($uri, Request::METHOD_GET, ['category' => $rolesCat->id]);
     $this->assertDescribeResponseNotEmpty($describe);
     foreach ($describe->getBody()->data as $data) {
         $this->assertRolesObjectNotEmpty($data);
         $this->assertEquals($rolesCat->id, $data->category->id);
     }
     $describe = $this->request($uri, Request::METHOD_GET, ['category' => '']);
     $this->assertErrorMessageContains($describe, 400, ErrorMessage::ERR_INVALID_VALUE, "Invalid identifier of the category");
     // test modify action
     $modify = $this->request($uri . '/' . $body->data->id, Request::METHOD_PATCH);
     $this->assertErrorMessageContains($modify, 400, ErrorMessage::ERR_INVALID_STRUCTURE, 'Invalid body');
     $modify = $this->request($uri . '/' . $body->data->id, Request::METHOD_PATCH, [], ['id' => 123]);
     $this->assertErrorMessageContains($modify, 400, ErrorMessage::ERR_INVALID_STRUCTURE);
     $modify = $this->request($uri . '/' . $body->data->id, Request::METHOD_PATCH, [], ['invalid' => 'err']);
     $this->assertErrorMessageContains($modify, 400, ErrorMessage::ERR_INVALID_STRUCTURE, 'You are trying to set');
     $modify = $this->request($uri . '/' . $body->data->id, Request::METHOD_PATCH, [], ['scope' => 'environment']);
     $this->assertErrorMessageContains($modify, 400, ErrorMessage::ERR_INVALID_VALUE);
     $modify = $this->request($uri . '/' . $body->data->id, Request::METHOD_PATCH, [], ['description' => '']);
     $modifyBody = $modify->getBody();
     $this->assertEquals(200, $modify->response->getStatus());
     $this->assertFetchResponseNotEmpty($modify);
     $this->assertRolesObjectNotEmpty($modifyBody->data);
     $this->assertEquals($body->data->id, $modifyBody->data->id);
     $this->assertEquals($testName, $modifyBody->data->name);
     $this->assertEquals('', $modifyBody->data->description);
     $this->assertEquals(ScopeInterface::SCOPE_ACCOUNT, $modifyBody->data->scope);
     $this->assertEquals($rolesCat->id, $modifyBody->data->category->id);
     $this->assertEquals($os->id, $modifyBody->data->os->id);
     // test delete action
     $delete = $this->request(static::getAccountApiUrl("/roles/{$notFoundRoleId}"), Request::METHOD_DELETE);
     $this->assertErrorMessageContains($delete, 404, ErrorMessage::ERR_OBJECT_NOT_FOUND);
     $delete = $this->request($uri . '/' . $body->data->id, Request::METHOD_DELETE);
     $this->assertEquals(200, $delete->status);
     $db->Execute("INSERT INTO roles SET\n            name      = ?,\n            dtadded   = NOW(),\n            env_id\t  = NULL,\n            client_id = NULL,\n            generation = 2\n        ", [$testName]);
     $insertedId = $db->_insertid();
     $db->Execute("INSERT INTO role_images SET\n            role_id = ?,\n            platform = 'ec2',\n            image_id = 'test'\n        ", [$insertedId]);
     $delete = $this->request($uri . '/' . $insertedId, Request::METHOD_DELETE);
     $db->Execute("DELETE FROM roles WHERE name = ? AND id = ?", [$testName, $insertedId]);
     $this->assertErrorMessageContains($delete, 403, ErrorMessage::ERR_SCOPE_VIOLATION);
 }
Пример #12
0
 /**
  * @param string $name
  * @param string $cloudLocation
  * @return array
  */
 protected function listS3Websites($name, $cloudLocation)
 {
     $result = [];
     $buckets = $this->environment->aws($cloudLocation)->s3->bucket->getWebsite($name);
     if ($buckets) {
         $location = $this->environment->aws($cloudLocation)->s3->bucket->getLocation($name);
         if (empty($location)) {
             $location = 'us-east-1';
         }
         $zoneIds = Aws::getCloudLocationsZoneIds();
         $result[] = array('domainName' => $name . 's3-website-' . $location . '.amazonaws.com', 'aliasZoneId' => isset($zoneIds[$location]) ? $zoneIds[$location] : null, 'title' => self::S3_ALIAS_TARGET_TITLE);
     }
     return $result;
 }
Пример #13
0
 /**
  * Copies image to different location
  *
  * @param   string $imageId Unique identifier of the image (uuid)
  * @return \Scalr\Api\DataType\ResultEnvelope
  * @throws  ApiErrorException
  */
 public function copyAction($imageId)
 {
     $this->checkScopedPermissions('IMAGES', 'MANAGE');
     $object = $this->request->getJsonBody();
     if (empty($object->cloudLocation) || empty($object->cloudPlatform)) {
         throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "Invalid body");
     }
     $locations = Aws::getCloudLocations();
     if (!in_array($object->cloudLocation, $locations)) {
         throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Invalid region");
     }
     if ($object->cloudPlatform !== 'ec2') {
         throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Only Ec2 cloud platform is supported");
     }
     $image = $this->getImage($imageId, true);
     $imageAdapter = $this->adapter('image');
     //Re-validates an Entity
     $imageAdapter->validateEntity($image);
     if ($image->cloudLocation == $object->cloudLocation) {
         throw new ApiErrorException(400, ErrorMessage::ERR_BAD_REQUEST, 'Destination region is the same as source one');
     }
     try {
         $newImage = $image->migrateEc2Location($object->cloudLocation, $this->getUser());
     } catch (NotEnabledPlatformException $e) {
         throw new ApiErrorException(400, ErrorMessage::ERR_NOT_ENABLED_PLATFORM, $e->getMessage());
     }
     $this->response->setStatus(202);
     return $this->result($imageAdapter->toData($newImage));
 }
Пример #14
0
 /**
  * {@inheritdoc}
  * @see ApiEntityAdapter::validateEntity()
  */
 public function validateEntity($entity)
 {
     if (!$entity instanceof FarmRole) {
         throw new InvalidArgumentException(sprintf("First argument must be instance of Scalr\\Model\\Entity\\FarmRole class"));
     }
     if ($entity->id !== null) {
         if (!FarmRole::findPk($entity->id)) {
             throw new ApiErrorException(404, ErrorMessage::ERR_OBJECT_NOT_FOUND, sprintf("Could not find out the Farm with ID: %d", $entity->id));
         }
     }
     if (empty($entity->farmId)) {
         throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "Missed property farm.id");
     } else {
         $farm = $this->controller->getFarm($entity->farmId, true);
     }
     if (empty($entity->roleId)) {
         throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "Missed property role.id");
     }
     if (empty($entity->platform)) {
         throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "Missed property platform");
     }
     switch ($entity->platform) {
         case SERVER_PLATFORMS::EC2:
             if (empty($entity->settings[FarmRoleSetting::AWS_INSTANCE_TYPE])) {
                 throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Missed property instance.type");
             }
             /* @var $platform Ec2PlatformModule */
             $platform = PlatformFactory::NewPlatform(SERVER_PLATFORMS::EC2);
             if (!in_array($entity->settings[FarmRoleSetting::AWS_INSTANCE_TYPE], $platform->getInstanceTypes())) {
                 throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Wrong instance type");
             }
             $gov = new Scalr_Governance($this->controller->getEnvironment()->id);
             $allowGovernanceIns = $gov->getValue(SERVER_PLATFORMS::EC2, Scalr_Governance::AWS_INSTANCE_TYPE);
             if (isset($allowGovernanceIns) && !in_array($entity->settings[FarmRoleSetting::AWS_INSTANCE_TYPE], $allowGovernanceIns)) {
                 throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, sprintf("Only %s %s allowed according to governance settings", ...count($allowGovernanceIns) > 1 ? [implode(', ', $allowGovernanceIns), 'instances are'] : [array_shift($allowGovernanceIns), 'instance is']));
             }
             if (!in_array($entity->cloudLocation, Aws::getCloudLocations())) {
                 throw new ApiErrorException(404, ErrorMessage::ERR_OBJECT_NOT_FOUND, "Unknown region");
             }
             $vpcGovernanceRegions = $gov->getValue(SERVER_PLATFORMS::EC2, Scalr_Governance::AWS_VPC, 'regions');
             if (isset($vpcGovernanceRegions) && !array_key_exists($entity->cloudLocation, $vpcGovernanceRegions)) {
                 $regions = array_keys($vpcGovernanceRegions);
                 throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, sprintf("Only %s %s allowed according to governance settings", ...count($regions) > 1 ? [implode(', ', $regions), 'regions are'] : [array_shift($regions), 'region is']));
             }
             $env = Scalr_Environment::init()->loadById($this->controller->getEnvironment()->id);
             $aws = $this->controller->getContainer()->aws($entity->cloudLocation, $env);
             if (!empty($entity->settings[FarmRoleSetting::AWS_AVAIL_ZONE]) && $entity->settings[FarmRoleSetting::AWS_AVAIL_ZONE] !== 'x-scalr-diff') {
                 $availZones = explode(":", str_replace("x-scalr-custom=", '', $entity->settings[FarmRoleSetting::AWS_AVAIL_ZONE]));
                 $ec2availabilityZones = [];
                 foreach ($aws->ec2->availabilityZone->describe() as $zone) {
                     /* @var $zone AvailabilityZoneData */
                     if (stristr($zone->zoneState, 'available')) {
                         $ec2availabilityZones[] = $zone->zoneName;
                     }
                 }
                 $diffZones = array_diff($availZones, $ec2availabilityZones);
                 if (!empty($diffZones)) {
                     throw new ApiErrorException(404, ErrorMessage::ERR_OBJECT_NOT_FOUND, sprintf('%s %s available. Available zones are %s', ...count($diffZones) > 1 ? [implode(', ', $diffZones), 'zones are not', implode(', ', $ec2availabilityZones)] : [array_shift($diffZones), 'zone is not', implode(', ', $ec2availabilityZones)]));
                 }
             }
             if (!empty($entity->settings[FarmRoleSetting::AWS_VPC_SUBNET_ID])) {
                 $vpcId = $farm->settings[FarmSetting::EC2_VPC_ID];
                 $subnets = $platform->listSubnets($env, $entity->cloudLocation, $vpcId, true);
                 $vpcGovernanceIds = $gov->getValue(SERVER_PLATFORMS::EC2, Scalr_Governance::AWS_VPC, 'ids');
                 $subnetType = null;
                 foreach (json_decode($entity->settings[FarmRoleSetting::AWS_VPC_SUBNET_ID]) as $subnetId) {
                     $found = false;
                     foreach ($subnets as $subnet) {
                         if ($subnet['id'] == $subnetId) {
                             if ($subnetType == null) {
                                 $subnetType = $subnet['type'];
                             } else {
                                 if ($subnet['type'] != $subnetType) {
                                     throw new ApiErrorException(409, ErrorMessage::ERR_UNICITY_VIOLATION, "All subnets must be a same type");
                                 }
                             }
                             //check governance subnet settings
                             if (isset($vpcGovernanceIds[$vpcId])) {
                                 if (!empty($vpcGovernanceIds[$vpcId]) && is_array($vpcGovernanceIds[$vpcId]) && !in_array($subnetId, $vpcGovernanceIds[$vpcId])) {
                                     throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, sprintf("Only %s %s allowed by governance settings", ...count($vpcGovernanceIds[$vpcId]) > 1 ? [implode(', ', $vpcGovernanceIds[$vpcId]), 'subnets are'] : [array_shift($vpcGovernanceIds[$vpcId]), 'subnet is']));
                                 } elseif ($vpcGovernanceIds[$vpcId] == "outbound-only" && $subnetType != 'private') {
                                     throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Only private subnets allowed by governance settings");
                                 } elseif ($vpcGovernanceIds[$vpcId] == "full" && $subnetType != 'public') {
                                     throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Only public subnets allowed by governance settings");
                                 }
                             }
                             $found = true;
                         }
                     }
                     if (!$found) {
                         throw new ApiErrorException(404, ErrorMessage::ERR_OBJECT_NOT_FOUND, "Subnet with id '{$subnetId}' not found");
                     }
                 }
                 if (!empty($entity->settings[Scalr_Role_Behavior_Router::ROLE_VPC_SCALR_ROUTER_ID])) {
                     $router = $this->controller->getFarmRole($entity->settings[Scalr_Role_Behavior_Router::ROLE_VPC_SCALR_ROUTER_ID]);
                     if (empty($router->settings[Scalr_Role_Behavior_Router::ROLE_VPC_NID])) {
                         throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, "Farm-role with id '{$router->id}' is not a valid router");
                     }
                 } else {
                     if ($subnetType == 'private') {
                         throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "You must describe a VPC Router");
                     }
                 }
             } else {
                 if ($farm->settings[FarmSetting::EC2_VPC_ID]) {
                     throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_STRUCTURE, "VPC Subnet(s) should be described");
                 }
             }
             break;
         default:
             if (in_array($entity->platform, SERVER_PLATFORMS::GetList())) {
                 throw new ApiErrorException(501, ErrorMessage::ERR_NOT_IMPLEMENTED, "Platform '{$entity->platform}' is not supported yet");
             } else {
                 throw new ApiErrorException(404, ErrorMessage::ERR_OBJECT_NOT_FOUND, "Unknown platform '{$entity->platform}'");
             }
     }
     if (!$this->controller->hasPermissions($entity, true)) {
         //Checks entity level write access permissions
         throw new ApiErrorException(403, ErrorMessage::ERR_PERMISSION_VIOLATION, "Insufficient permissions");
     }
 }
Пример #15
0
 /**
  * {@inheritdoc}
  * @see \Scalr\System\Pcntl\ProcessInterface::OnStartForking()
  */
 public function OnStartForking()
 {
     if (!\Scalr::getContainer()->analytics->enabled) {
         die("Terminating the process as Cost analytics is disabled in the config.\n");
     }
     if (SettingEntity::getValue(SettingEntity::ID_FORBID_AUTOMATIC_UPDATE_AWS_PRICES)) {
         die("Terminating the process because of overriding AWS prices has been forbidden by financial admin.\n");
     }
     $now = new DateTime('now', new DateTimeZone('UTC'));
     $urls = array('https://a0.awsstatic.com/pricing/1/ec2/linux-od.min.js', 'https://a0.awsstatic.com/pricing/1/ec2/mswin-od.min.js');
     $mapping = array('us-east' => 'us-east-1', 'us-west' => 'us-west-1', 'us-west-2' => 'us-west-2', 'eu-ireland' => 'eu-west-1', 'sa-east-1' => 'sa-east-1', 'apac-sin' => 'ap-southeast-1', 'apac-tokyo' => 'ap-northeast-1', 'apac-syd' => 'ap-southeast-2');
     $availableLocations = Aws::getCloudLocations();
     foreach ($urls as $link) {
         $json = trim(preg_replace('/^.+?callback\\((.+?)\\);\\s*$/sU', '\\1', file_get_contents($link)));
         $data = json_decode(preg_replace('/(\\w+):/', '"\\1":', $json));
         if (!empty($data->config->regions)) {
             $cadb = Scalr::getContainer()->cadb;
             foreach ($data->config->regions as $rd) {
                 foreach ($rd->instanceTypes as $it) {
                     if (!isset($mapping[$rd->region])) {
                         throw new Exception(sprintf("Region %s does not exist in the mapping.", $rd->region));
                     }
                     $region = $mapping[$rd->region];
                     $latest = array();
                     //Gets latest prices for all instance types from current region.
                     $res = $cadb->Execute("\n                            SELECT p.instance_type, ph.applied, p.os, p.name, HEX(p.price_id) `price_id`, p.cost\n                            FROM price_history ph\n                            JOIN prices p ON p.price_id = ph.price_id\n                            LEFT JOIN price_history ph2 ON ph2.platform = ph.platform\n                                AND ph2.cloud_location = ph.cloud_location\n                                AND ph2.account_id = ph.account_id\n                                AND ph2.url = ph.url\n                                AND ph2.applied > ph.applied AND ph2.applied <= ?\n                            LEFT JOIN prices p2 ON p2.price_id = ph2.price_id\n                                AND p2.instance_type = p.instance_type\n                                AND p2.os = p.os\n                            WHERE ph.account_id = 0 AND p2.price_id IS NULL\n                            AND ph.platform = 'ec2'\n                            AND ph.cloud_location = ?\n                            AND ph.url = ''\n                            AND ph.applied <= ?\n                        ", array($now->format('Y-m-d'), $region, $now->format('Y-m-d')));
                     while ($rec = $res->FetchRow()) {
                         $latest[$rec['instance_type']][$rec['os']] = array('applied' => $rec['applied'], 'price_id' => $rec['price_id'], 'cost' => $rec['cost']);
                     }
                     $upd = array();
                     $needUpdate = false;
                     foreach ($it->sizes as $sz) {
                         foreach ($sz->valueColumns as $v) {
                             $os = $v->name == 'linux' ? PriceEntity::OS_LINUX : PriceEntity::OS_WINDOWS;
                             if (!isset($latest[$sz->size][$os])) {
                                 $needUpdate = true;
                             } else {
                                 if (abs(($latest[$sz->size][$os]['cost'] - $v->prices->USD) / $v->prices->USD) > 1.0E-6) {
                                     $needUpdate = true;
                                     $latest[$sz->size][$os]['cost'] = $v->prices->USD;
                                 } else {
                                     continue;
                                 }
                             }
                             $latest[$sz->size][$os] = array('cost' => $v->prices->USD);
                         }
                     }
                     if ($needUpdate) {
                         $priceid = $cadb->GetOne("\n                                SELECT HEX(`price_id`) AS `price_id`\n                                FROM price_history\n                                WHERE platform = 'ec2'\n                                AND url = ''\n                                AND cloud_location = ?\n                                AND applied = ?\n                                AND account_id = 0\n                                LIMIT 1\n                            ", array($region, $now->format('Y-m-d')));
                         if (!$priceid) {
                             $priceid = str_replace('-', '', Scalr::GenerateUID());
                             $cadb->Execute("\n                                    INSERT price_history\n                                    SET price_id = UNHEX(?),\n                                        platform = 'ec2',\n                                        url = '',\n                                        cloud_location = ?,\n                                        account_id = 0,\n                                        applied = ?,\n                                        deny_override = 0\n                                ", array($priceid, $region, $now->format('Y-m-d')));
                         }
                         foreach ($latest as $instanceType => $ld) {
                             foreach ($ld as $os => $v) {
                                 $cadb->Execute("\n                                        REPLACE prices\n                                        SET price_id = UNHEX(?),\n                                            instance_type = ?,\n                                            name = ?,\n                                            os = ?,\n                                            cost = ?\n                                    ", array($priceid, $instanceType, $instanceType, $os, $v['cost']));
                             }
                         }
                     }
                 }
             }
         }
     }
     exit;
 }
Пример #16
0
 /**
  * @param Entity\CloudCredentials $entity
  *
  * @throws ApiErrorException
  */
 public function validateEntity($entity)
 {
     $container = $this->controller->getContainer();
     if ($container->analytics->enabled) {
         $ccProps = $entity->properties;
         if (!empty($ccProps[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_BUCKET])) {
             try {
                 $region = $ccProps[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_REGION];
                 $aws = $container->aws($region, $ccProps[Entity\CloudCredentialsProperty::AWS_ACCESS_KEY], $ccProps[Entity\CloudCredentialsProperty::AWS_SECRET_KEY]);
                 if (!empty($ccProps[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_PAYER_ACCOUNT]) && $aws->getAccountNumber() != $ccProps[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_PAYER_ACCOUNT]) {
                     $payerCredentials = $this->controller->getEnvironment()->cloudCredentialsList([SERVER_PLATFORMS::EC2], [['accountId' => $this->controller->getUser()->getAccountId()]], [['name' => Entity\CloudCredentialsProperty::AWS_ACCOUNT_ID], ['value' => $ccProps[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_PAYER_ACCOUNT]]]);
                     $payerCredentials = array_shift($payerCredentials) ?: $entity;
                     $payerCcProps = $payerCredentials->properties;
                     $aws = $container->aws($region, $payerCcProps[Entity\CloudCredentialsProperty::AWS_ACCESS_KEY], $payerCcProps[Entity\CloudCredentialsProperty::AWS_SECRET_KEY], !empty($payerCcProps[Entity\CloudCredentialsProperty::AWS_CERTIFICATE]) ? $payerCcProps[Entity\CloudCredentialsProperty::AWS_CERTIFICATE] : null, !empty($payerCcProps[Entity\CloudCredentialsProperty::AWS_PRIVATE_KEY]) ? $payerCcProps[Entity\CloudCredentialsProperty::AWS_PRIVATE_KEY] : null);
                 }
                 try {
                     $bucketObjects = $aws->s3->bucket->listObjects($ccProps[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_BUCKET]);
                 } catch (Exception $e) {
                     if (strpos($e->getMessage(), 'The authorization header is malformed') !== false) {
                         if (preg_match("/expecting\\s+'(.+?)'/i", $e->getMessage(), $matches) && in_array($matches[1], Aws::getCloudLocations())) {
                             $expectingRegion = $matches[1];
                             if (isset($payerCcProps)) {
                                 $aws = $container->aws($region, $payerCcProps[Entity\CloudCredentialsProperty::AWS_ACCESS_KEY], $payerCcProps[Entity\CloudCredentialsProperty::AWS_SECRET_KEY], !empty($payerCcProps[Entity\CloudCredentialsProperty::AWS_CERTIFICATE]) ? $payerCcProps[Entity\CloudCredentialsProperty::AWS_CERTIFICATE] : null, !empty($payerCcProps[Entity\CloudCredentialsProperty::AWS_PRIVATE_KEY]) ? $payerCcProps[Entity\CloudCredentialsProperty::AWS_PRIVATE_KEY] : null);
                             } else {
                                 $aws = $container->aws($expectingRegion, $ccProps[Entity\CloudCredentialsProperty::AWS_ACCESS_KEY], $ccProps[Entity\CloudCredentialsProperty::AWS_SECRET_KEY]);
                             }
                             $bucketObjects = $aws->s3->bucket->listObjects($ccProps[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_BUCKET]);
                             $ccProps[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_REGION] = $expectingRegion;
                         } else {
                             throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, $e->getMessage(), $e->getCode(), $e);
                         }
                     } else {
                         throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, $e->getMessage(), $e->getCode(), $e);
                     }
                 }
                 $objectName = (empty($ccProps[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_PAYER_ACCOUNT]) ? '' : "{$ccProps[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_PAYER_ACCOUNT]}-") . 'aws-billing-detailed-line-items-with-resources-and-tags';
                 $objectExists = false;
                 $bucketObjectName = null;
                 foreach ($bucketObjects as $bucketObject) {
                     /* @var $bucketObject ObjectData */
                     if (strpos($bucketObject->objectName, $objectName) !== false) {
                         $bucketObjectName = $bucketObject->objectName;
                         $ccProps[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_ENABLED] = 1;
                         $objectExists = true;
                         break;
                     }
                 }
                 if (!$objectExists) {
                     throw new ApiErrorException(404, ErrorMessage::ERR_OBJECT_NOT_FOUND, "Bucket with name '{$objectName}' does not exist");
                 }
                 $aws->s3->object->getMetadata($ccProps[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_BUCKET], $bucketObjectName);
             } catch (Exception $e) {
                 throw new ApiErrorException(400, ErrorMessage::ERR_INVALID_VALUE, sprintf("Cannot access billing bucket with name %s. Error: %s", $ccProps[Entity\CloudCredentialsProperty::AWS_DETAILED_BILLING_BUCKET], $e->getMessage()), $e->getCode(), $e);
             }
         }
     }
 }
Пример #17
0
 /**
  * @test
  */
 public function testGetReflectionClass()
 {
     $refl = Aws::getReflectionClass();
     $this->assertEquals(self::CLASS_AWS, $refl->getName(), 'Invalid reflection class.');
 }
Пример #18
0
 /**
  * Calls Amazon web service method.
  *
  * It ensures execution of the certain AWS action by transporting the request
  * and receiving response.
  *
  * @param     string    $action           REST METHOD (GET|PUT|DELETE|HEAD etc).
  * @param     array     $options          An options array. It's used to send headers for the HTTP request.
  *                                        extra options: _subdomain, _putData, _putFile
  * @param     string    $path    optional A relative path.
  * @return    ClientResponseInterface
  * @throws    ClientException
  */
 public function call($action, $options, $path = '/')
 {
     $httpRequest = $this->createRequest();
     $httpMethod = $action ?: 'GET';
     if (substr($path, 0, 1) !== '/') {
         $path = '/' . $path;
     }
     $this->lastApiCall = null;
     $eventObserver = $this->getAws()->getEventObserver();
     if ($eventObserver && $eventObserver->isSubscribed(EventType::EVENT_SEND_REQUEST)) {
         foreach (debug_backtrace() as $arr) {
             if (empty($arr['class']) || !preg_match("/\\\\Service\\\\Aws\\\\.+Api\$/", $arr['class']) || $arr['type'] !== '->') {
                 continue;
             }
             $this->lastApiCall = ucfirst($arr['function']);
             break;
         }
     }
     //Wipes out extra options from headers and moves them to separate array.
     //It also collects an x-amz headers.
     $extraOptions = array();
     $amzHeaders = array();
     foreach ($options as $key => $val) {
         if (substr($key, 0, 1) === '_') {
             $extraOptions[substr($key, 1)] = $val;
             unset($options[$key]);
         } elseif (preg_match('/^x-amz-/i', $key)) {
             //Saves amz headers which are used to sign the request
             $amzHeaders[strtolower($key)] = $val;
         }
     }
     if (!isset($options['Date'])) {
         $options['Date'] = gmdate('r');
     }
     if (!isset($options['Host'])) {
         $options['Host'] = (isset($extraOptions['subdomain']) ? $extraOptions['subdomain'] . '.' : '') . $this->url;
     }
     if (strpos($options['Host'], 'http') === 0) {
         $arr = parse_url($options['Host']);
         $scheme = $arr['scheme'];
         $options['Host'] = $arr['host'] . (isset($arr['port']) ? ':' . $arr['port'] : '');
         $path = (!empty($arr['path']) && $arr['path'] != '/' ? rtrim($arr['path'], '/') : '') . $path;
     } else {
         $scheme = 'https';
     }
     if ($httpMethod === 'PUT' || $httpMethod === 'POST') {
         if (array_key_exists('putData', $extraOptions)) {
             if ($httpMethod === 'PUT') {
                 $httpRequest->setPutData($extraOptions['putData']);
             } else {
                 $httpRequest->setBody($extraOptions['putData']);
             }
             if (!isset($options['Content-Length'])) {
                 $options['Content-Length'] = strlen($extraOptions['putData']);
             }
             if (!isset($options['Content-MD5']) && !empty($options['Content-Length'])) {
                 $options['Content-MD5'] = Aws::getMd5Base64Digest($extraOptions['putData']);
             }
         } elseif (array_key_exists('putFile', $extraOptions)) {
             if ($httpMethod === 'PUT') {
                 $httpRequest->setPutFile($extraOptions['putFile']);
             } else {
                 $httpRequest->setBody(file_get_contents($extraOptions['putFile']));
             }
             if (!isset($options['Content-Length'])) {
                 $options['Content-Length'] = filesize($extraOptions['putFile']);
             }
             if (!isset($options['Content-MD5']) && !empty($options['Content-Length'])) {
                 $options['Content-MD5'] = Aws::getMd5Base64DigestFile($extraOptions['putFile']);
             }
         }
     }
     //This also includes a mock objects which look like "Mock_S3QueryClient_d65a1dc1".
     if (preg_match('#(?<=[_\\\\])S3QueryClient(?=_|$)#', get_class($this))) {
         //S3 Client has a special Authorization string
         $canonicalizedAmzHeaders = '';
         if (!empty($amzHeaders)) {
             ksort($amzHeaders);
             foreach ($amzHeaders as $k => $v) {
                 $canonicalizedAmzHeaders .= $k . ':' . trim(preg_replace('/#( *[\\r\\n]+ *)+#/', ' ', $v)) . "\n";
             }
         }
         //Note that in case of multiple sub-resources, sub-resources must be lexicographically sorted
         //by sub-resource name and separated by '&'. e.g. ?acl&versionId=value.
         $t = explode('?', $path);
         if (!empty($t[1])) {
             $canonPath = $t[0] . '?';
             parse_str($t[1], $subresources);
             ksort($subresources);
             $allowed = $this->getAllowedSubResources();
             foreach ($subresources as $k => $v) {
                 if (in_array($k, $allowed)) {
                     $canonPath .= $k . ($v !== '' ? '=' . $v : '') . '&';
                 }
             }
             $canonPath = substr($canonPath, 0, -1);
         }
         $canonicalizedResource = (isset($extraOptions['subdomain']) ? '/' . strtolower($extraOptions['subdomain']) : '') . (isset($canonPath) ? $canonPath : $path);
         $stringToSign = $httpMethod . "\n" . (!empty($options['Content-MD5']) ? (string) $options['Content-MD5'] : '') . "\n" . (!empty($options['Content-Type']) ? (string) $options['Content-Type'] : '') . "\n" . (isset($amzHeaders['x-amz-date']) ? '' : $options['Date'] . "\n") . $canonicalizedAmzHeaders . $canonicalizedResource;
         $options['Authorization'] = "AWS " . $this->awsAccessKeyId . ":" . base64_encode(hash_hmac('sha1', $stringToSign, $this->secretAccessKey, 1));
     } else {
         $options['Authorization'] = "AWS " . $this->awsAccessKeyId . ":" . base64_encode(hash_hmac('sha1', $options['Date'], $this->secretAccessKey, 1));
     }
     $httpRequest->setUrl($scheme . '://' . $options['Host'] . $path);
     $httpRequest->setMethod(constant('HTTP_METH_' . $httpMethod));
     $httpRequest->addHeaders($options);
     $response = $this->tryCall($httpRequest);
     if ($this->getAws() && $this->getAws()->getDebug()) {
         echo "\n";
         echo $httpRequest->getRawRequestMessage() . "\n";
         echo $httpRequest->getRawResponseMessage() . "\n";
     }
     return $response;
 }