Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix support for "aws/aws-sdk-php:^3.0" #1814

Merged
merged 1 commit into from
Sep 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions UPGRADE-3.x.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
UPGRADE 3.x
===========

UPGRADE FROM 3.x to 3.x
=======================

### Configuration node "sonata_media.filesystem.s3.sdk_version"

The configuration node "sonata_media.filesystem.s3.sdk_version" is deprecated. The
version of aws/aws-sdk-php is automatically inferred from the installed package.

### Configuration node "sonata_media.cdn.cloudfront"

The configuration nodes "sonata_media.cdn.cloudfront.region" and "sonata_media.cdn.cloudfront.version"
are required when aws/aws-sdk-php 3.x is installed.

UPGRADE FROM 3.25 to 3.26
=========================

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"symfony/monolog-bundle": "<2.4"
},
"require-dev": {
"aws/aws-sdk-php": "^2.8",
"aws/aws-sdk-php": "^2.8 || ^3.0",
"friendsofsymfony/rest-bundle": "^2.6 || ^3.0",
"jackalope/jackalope-doctrine-dbal": "^1.1",
"liip/imagine-bundle": "^1.9 || ^2.0",
Expand Down
3 changes: 3 additions & 0 deletions docs/reference/advanced_configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ Full configuration options:
distribution_id:
key:
secret:
region:
version:

fallback:
master: sonata.media.cdn.panther
Expand All @@ -103,6 +105,7 @@ Full configuration options:
secretKey:
create: false
region: s3.amazonaws.com # change if not using US Standard region
version: 2006-03-01 # change according the API version you are using
storage: standard # can be one of: standard or reduced
acl: public # can be one of: public, private, open, auth_read, owner_read, owner_full_control
encryption: aes256 # can be aes256 or not set
Expand Down
25 changes: 14 additions & 11 deletions docs/reference/amazon_s3.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Amazon S3
=========

In order to use Amazon S3, you will need to require the following library:
In order to use Amazon S3, you will need to require the following package:

.. code-block:: bash

Expand All @@ -10,7 +10,7 @@ In order to use Amazon S3, you will need to require the following library:
Configuration
-------------

This is a sample config file to enable amazon S3 as a filesystem & provider:
This is a sample configuration to enable amazon S3 as a filesystem and provider:

.. code-block:: yaml

Expand All @@ -27,15 +27,18 @@ This is a sample config file to enable amazon S3 as a filesystem & provider:

filesystem:
s3:
bucket: '%s3_bucket_name%'
accessKey: '%s3_access_key%'
secretKey: '%s3_secret_key%'
region: '%s3_region%'
version: '%s3_version%' # latest by default (cf. https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/guide_configuration.html#cfg-version)
endpoint: '%s3_endpoint%' # null by default (cf. https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/guide_configuration.html#endpoint)
sdk_version: '%s3_sdk_version%' # 2 by default
bucket: '%s3_bucket_name%'
accessKey: '%s3_access_key%'
secretKey: '%s3_secret_key%'
region: '%s3_region%'
version: '%s3_version%' # defaults to "latest" (cf. https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/guide_configuration.html#cfg-version)
endpoint: '%s3_endpoint%' # defaults to null (cf. https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/guide_configuration.html#endpoint)

.. note::

This bundle is currently using KNP Gaufrette as S3 adapter and the default SDK used is version 2.
Changes have been made in the bundle to allow you to use version 3, update `sdk_version` parameter for this.
This bundle is currently using KNP Gaufrette as S3 adapter.

.. note::

Using "latest" for "sonata_media.filesystem.s3.version" in a production environment is not recommended
because pulling in a new minor version of the SDK that includes an API update could break your production application.
101 changes: 87 additions & 14 deletions src/CDN/CloudFront.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

use Aws\CloudFront\CloudFrontClient;
use Aws\CloudFront\Exception\CloudFrontException;
use Aws\Sdk;

/**
* From http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html.
Expand Down Expand Up @@ -77,17 +78,55 @@ class CloudFront implements CDNInterface
protected $client;

/**
* @var string
*/
private $region;

/**
* @var string
*/
private $version;

/**
* @todo: Make mandatory argument 5 and 6 when support for aws/aws-sdk-php < 3.0 is dropped.
wbloszyk marked this conversation as resolved.
Show resolved Hide resolved
*
* @param string $path
* @param string $key
* @param string $secret
* @param string $distributionId
*/
public function __construct($path, $key, $secret, $distributionId)
{
public function __construct(
$path,
$key,
$secret,
$distributionId,
?string $region = null,
?string $version = null
) {
$this->path = $path;
$this->key = $key;
$this->secret = $secret;
$this->distributionId = $distributionId;

// @todo: Remove the following conditional block when support for aws/aws-sdk-php < 3.0 is dropped.
if (class_exists(Sdk::class)) {
if (null === $region) {
throw new \TypeError(sprintf(
'Argument 5 for "%s()" is required and can not be null when aws/aws-sdk-php >= 3.0 is installed.',
__METHOD__
));
}

if (null === $version) {
throw new \TypeError(sprintf(
'Argument 6 for "%s()" is required and can not be null when aws/aws-sdk-php >= 3.0 is installed.',
__METHOD__
));
}
}

$this->region = $region;
$this->version = $region;
}

public function getPath($relativePath, $isFlushable = false)
Expand Down Expand Up @@ -121,15 +160,29 @@ public function flushPaths(array $paths)
return '/'.ltrim($path, '/');
}, $paths);

$invalidationBatch = [
'Paths' => [
'Quantity' => \count($normalizedPaths),
'Items' => $normalizedPaths,
],
'CallerReference' => $this->getCallerReference($normalizedPaths),
];

$arguments = [
'DistributionId' => $this->distributionId,
];

// @todo: Remove the following check and the `else` block when support for aws/aws-sdk-php < 3.0 is dropped.
wbloszyk marked this conversation as resolved.
Show resolved Hide resolved
if (class_exists(Sdk::class)) {
// AWS v3.x.
$arguments += ['InvalidationBatch' => $invalidationBatch];
} else {
// AWS v2.x.
$arguments += $invalidationBatch;
}

try {
$result = $this->getClient()->createInvalidation([
'DistributionId' => $this->distributionId,
'Paths' => [
'Quantity' => \count($normalizedPaths),
'Items' => $normalizedPaths,
],
'CallerReference' => $this->getCallerReference($normalizedPaths),
]);
$result = $this->getClient()->createInvalidation($arguments);

if (!\in_array($status = $result->get('Status'), ['Completed', 'InProgress'], true)) {
throw new \RuntimeException('Unable to flush : '.$status);
Expand Down Expand Up @@ -198,13 +251,33 @@ protected function getCallerReference(array $paths)
return md5(implode(',', $paths));
}

/**
* @todo: Inject client through DI.
*/
private function getClient(): CloudFrontClient
{
if (!$this->client) {
$this->client = CloudFrontClient::factory([
'key' => $this->key,
'secret' => $this->secret,
]);
if (null !== $this->region) {
$config['region'] = $this->region;
}

if (null !== $this->version) {
$config['version'] = $this->version;
}

$credentials = [
'key' => $this->region,
'secret' => $this->version,
];

// @todo: Remove the following check and the `else` block when support for aws/aws-sdk-php < 3.0 is dropped.
if (class_exists(Sdk::class)) {
// AWS v3.x.
$this->client = new CloudFrontClient($config + ['credentials' => $credentials]);
} else {
// AWS v2.x.
$this->client = CloudFrontClient::factory($config + $credentials);
}
}

return $this->client;
Expand Down
111 changes: 98 additions & 13 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@

namespace Sonata\MediaBundle\DependencyInjection;

use Aws\Sdk;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

Expand Down Expand Up @@ -154,17 +156,7 @@ private function addCdnSection(ArrayNodeDefinition $node): void
->end()
->end()

->arrayNode('cloudfront')
->children()
->scalarNode('path')
->info('e.g. http://xxxxxxxxxxxxxx.cloudfront.net/uploads/media')
->isRequired()
->end()
->scalarNode('distribution_id')->isRequired()->end()
->scalarNode('key')->isRequired()->end()
->scalarNode('secret')->isRequired()->end()
->end()
->end()
->append($this->getCloudFrontCdnSection())

->arrayNode('fallback')
->children()
Expand All @@ -178,6 +170,49 @@ private function addCdnSection(ArrayNodeDefinition $node): void
;
}

/**
* @todo: Remove this method when support for aws/aws-sdk-php < 3.0 is dropped
* and move the corresponding logic to `addCdnSection()`.
*/
private function getCloudFrontCdnSection(): NodeDefinition
{
$treeBuilder = new TreeBuilder('cloudfront');

if (class_exists(Sdk::class)) {
// aws/aws-sdk-php >= 3.0
$node = $treeBuilder->getRootNode()
->children()
->scalarNode('path')
->info('e.g. http://xxxxxxxxxxxxxx.cloudfront.net/uploads/media')
->isRequired()
->end()
->scalarNode('distribution_id')->isRequired()->end()
->scalarNode('key')->isRequired()->end()
->scalarNode('secret')->isRequired()->end()
->scalarNode('region')->isRequired()->end()
->scalarNode('version')->isRequired()->end()
->end()
;
} else {
// aws/aws-sdk-php < 3.0
$node = $treeBuilder->getRootNode()
->children()
->scalarNode('path')
->info('e.g. http://xxxxxxxxxxxxxx.cloudfront.net/uploads/media')
->isRequired()
->end()
->scalarNode('distribution_id')->isRequired()->end()
->scalarNode('key')->isRequired()->end()
->scalarNode('secret')->isRequired()->end()
->scalarNode('region')->end()
->scalarNode('version')->end()
->end()
;
}

return $node;
}

private function addFilesystemSection(ArrayNodeDefinition $node): void
{
$node
Expand Down Expand Up @@ -236,10 +271,39 @@ private function addFilesystemSection(ArrayNodeDefinition $node): void
->end()
->scalarNode('region')->defaultValue('s3.amazonaws.com')->end()
->scalarNode('endpoint')->defaultNull()->end()
->scalarNode('version')->defaultValue('latest')->end()
->scalarNode('version')
->info(
'Using "latest" in a production application is not recommended because pulling in a new minor version of the SDK'
.' that includes an API update could break your production application.'
.' See https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/guide_configuration.html#cfg-version.'
)
->defaultValue('latest')
->end()
->enumNode('sdk_version')
->setDeprecated(...$this->getBackwardCompatibleArgumentsForSetDeprecated(
'The node "%node%" is deprecated and will be removed in version 4.0'
.' since the version for aws/aws-sdk-php is inferred automatically.',
'3.x'
))
->beforeNormalization()
->ifString()
->then(static function (string $v): int {
return (int) $v;
})
->end()
->validate()
->ifTrue(static function (int $v): bool {
return 2 === $v && class_exists(Sdk::class);
})
->thenInvalid('Can not use %s for "sdk_version" since the installed version of aws/aws-sdk-php is not 2.x.')
->end()
->validate()
->ifTrue(static function (int $v): bool {
return 3 === $v && !class_exists(Sdk::class);
})
->thenInvalid('Can not use %s for "sdk_version" since the installed version of aws/aws-sdk-php is not 3.x.')
->end()
->values([2, 3])
->defaultValue(2)
->end()
->arrayNode('meta')
->useAttributeAsKey('name')
Expand Down Expand Up @@ -509,4 +573,25 @@ private function addAdapterSection(ArrayNodeDefinition $node): void
->end()
;
}

/**
* Returns the correct deprecation arguments as an array for `setDeprecated()`.
*
* symfony/config 5.1 introduces a deprecation notice when calling
* `setDeprecation()` with less than 3 arguments and the `getDeprecation()` method was
* introduced at the same time. By checking if `getDeprecation()` exists,
* we can determine the correct parameter count to use when calling `setDeprecated()`.
*/
private function getBackwardCompatibleArgumentsForSetDeprecated(string $message, string $version): array
{
if (method_exists(BaseNode::class, 'getDeprecation')) {
return [
'sonata-project/media-bundle',
$version,
$message,
];
}

return [$message];
}
}
Loading