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

chore: add proxy support #67

Merged
merged 2 commits into from
Nov 18, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion examples/example.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ function printBanner(string $message, LoggerInterface $logger): void
$logger->info("Getting value for key: $KEY\n");
$response = $client->get($CACHE_NAME, $KEY);
if ($response->asHit()) {
$logger->info("SUCCESS: - Get key: " . $KEY . " value: " . $response->asHit()->value() . " cache: " . $CACHE_NAME . "\n");
$logger->info("SUCCESS: - Get key: " . $KEY . " value: " . $response->asHit()->valueString() . " cache: " . $CACHE_NAME . "\n");
} elseif ($response->asMiss()) {
$logger->info("Get operation was a MISS\n");
} elseif ($response->asError()) {
Expand Down
110 changes: 110 additions & 0 deletions examples/proxy-example.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php
declare(strict_types=1);

require "vendor/autoload.php";

use Momento\Auth\EnvMomentoTokenProvider;
use Momento\Auth\EnvMomentoTokenProxyProvider;
use Momento\Cache\SimpleCacheClient;
use Momento\Config\Configurations\Laptop;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Psr\Log\LoggerInterface;

$CACHE_NAME = getenv("CACHE_NAME");
if (!$CACHE_NAME) {
print "Error: Environment variable CACHE_NAME was not found.\n";
exit;
}
$ITEM_DEFAULT_TTL_SECONDS = 60;
$KEY = "MyKey";
$VALUE = "MyValue";

// Setup
$authProvider = new EnvMomentoTokenProxyProvider(
"MOMENTO_AUTH_TOKEN",
"momento-control:4443",
"momento-cache:4444"
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is so much other code in this example that this bit gets lost in the noise. tbh I think if we want to have a proxy example it should probably just have the auth provider instantiation, with more comments about what it's doing and why, and then like one call to "listCaches" or something. and we should remove the rest of the stuff that's duplicated from the other examples so that their attention is drawn to the important thing.


// Use your favorite PSR-3 logger.
$logger = new Logger("example");
$streamHandler = new StreamHandler("php://stderr");
$formatter = new LineFormatter("%message%\n");
$streamHandler->setFormatter($formatter);
$logger->pushHandler($streamHandler);

// Or use the built-in minimal logger, equivalent to the above Monolog configuration.
//$logger = \Momento\Utilities\LoggingHelper::getMinimalLogger();
// Or discard all log messages.
//$logger = \Momento\Utilities\LoggingHelper::getNullLogger();

$configuration = Laptop::latest($logger);
$client = new SimpleCacheClient($configuration, $authProvider, $ITEM_DEFAULT_TTL_SECONDS);

function printBanner(string $message, LoggerInterface $logger): void
{
$line = "******************************************************************";
$logger->info($line);
$logger->info($message);
$logger->info($line);
}

printBanner("* Momento Example Start *", $logger);

// Ensure test cache exists
$response = $client->createCache($CACHE_NAME);
if ($response->asSuccess()) {
$logger->info("Created cache " . $CACHE_NAME . "\n");
} elseif ($response->asError()) {
$logger->info("Error creating cache: " . $response->asError()->message() . "\n");
exit;
} elseif ($response->asAlreadyExists()) {
$logger->info("Cache " . $CACHE_NAME . " already exists.\n");
}

// List cache
$response = $client->listCaches();
if ($response->asSuccess()) {
while (true) {
$logger->info("SUCCESS: List caches: \n");
foreach ($response->asSuccess()->caches() as $cache) {
$cacheName = $cache->name();
$logger->info("$cacheName\n");
}
$nextToken = $response->asSuccess()->nextToken();
if (!$nextToken) {
break;
}
$response = $client->listCaches($nextToken);
}
$logger->info("\n");
} elseif ($response->asError()) {
$logger->info("Error listing cache: " . $response->asError()->message() . "\n");
exit;
}

// Set
$logger->info("Setting key: $KEY to value: $VALUE\n");
$response = $client->set($CACHE_NAME, $KEY, $VALUE);
if ($response->asSuccess()) {
$logger->info("SUCCESS: - Set key: " . $KEY . " value: " . $VALUE . " cache: " . $CACHE_NAME . "\n");
} elseif ($response->asError()) {
$logger->info("Error setting key: " . $response->asError()->message() . "\n");
exit;
}

// Get
$logger->info("Getting value for key: $KEY\n");
$response = $client->get($CACHE_NAME, $KEY);
if ($response->asHit()) {
$logger->info("SUCCESS: - Get key: " . $KEY . " value: " . $response->asHit()->valueString() . " cache: " . $CACHE_NAME . "\n");
} elseif ($response->asMiss()) {
$logger->info("Get operation was a MISS\n");
} elseif ($response->asError()) {
$logger->info("Error getting cache: " . $response->asError()->message() . "\n");
exit;
}

printBanner("* Momento Example End *", $logger);
11 changes: 10 additions & 1 deletion src/Auth/EnvMomentoTokenProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

namespace Momento\Auth;

use Momento\Auth\AuthUtils;
use Momento\Cache\Errors\InvalidArgumentError;
use function \Momento\Utilities\isNullOrEmpty;

Expand Down Expand Up @@ -40,4 +39,14 @@ public function getControlEndpoint(): string
{
return $this->controlEndpoint;
}

public function getControlProxyEndpoint(): string|null
{
return null;
}

public function getCacheProxyEndpoint(): string|null
{
return null;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left you some input in slack about some other ideas for this interface.

}
60 changes: 60 additions & 0 deletions src/Auth/EnvMomentoTokenProxyProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);

namespace Momento\Auth;

use Momento\Cache\Errors\InvalidArgumentError;
use function Momento\Utilities\isNullOrEmpty;

class EnvMomentoTokenProxyProvider implements ICredentialProvider
{

private string $authToken;
private string $controlProxyEndpoint;
private string $cacheProxyEndpoint;
private string $controlEndpoint;
private string $cacheEndpoint;

public function __construct(
string $envVariableName,
string $controlProxyEndpoint,
string $cacheProxyEndpoint
)
{
$this->controlProxyEndpoint = $controlProxyEndpoint;
$this->cacheProxyEndpoint = $cacheProxyEndpoint;
$authToken = getenv($envVariableName);
if ($authToken === false || isNullOrEmpty($authToken)) {
throw new InvalidArgumentError("Environment variable $envVariableName is empty or null.");
}
$payload = AuthUtils::parseAuthToken($authToken);
$this->authToken = $authToken;
$this->controlEndpoint = $payload->cp;
$this->cacheEndpoint = $payload->c;
}

public function getAuthToken(): string
{
return $this->authToken;
}

public function getControlProxyEndpoint(): string|null
{
return $this->controlProxyEndpoint;
}

public function getCacheProxyEndpoint(): string|null
{
return $this->cacheProxyEndpoint;
}

public function getControlEndpoint(): string
{
return $this->controlEndpoint;
}

public function getCacheEndpoint(): string
{
return $this->cacheEndpoint;
}
}
6 changes: 5 additions & 1 deletion src/Auth/ICredentialProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,9 @@ public function getAuthToken(): string;

public function getControlEndpoint(): string;

public function getCacheEndpoint() : string;
public function getCacheEndpoint(): string;

public function getControlProxyEndpoint(): string|null;

public function getCacheProxyEndpoint(): string|null;
}
7 changes: 2 additions & 5 deletions src/Cache/SimpleCacheClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,10 @@ public function __construct(
{
$this->configuration = $configuration;
$this->setLogger($this->configuration->getLogger());
$this->controlClient = new _ScsControlClient(
$this->logger, $authProvider->getAuthToken(), $authProvider->getControlEndpoint()
);
$this->controlClient = new _ScsControlClient($this->logger, $authProvider);
$this->dataClient = new _ScsDataClient(
$this->configuration,
$authProvider->getAuthToken(),
$authProvider->getCacheEndpoint(),
$authProvider,
$defaultTtlSeconds
);
}
Expand Down
25 changes: 16 additions & 9 deletions src/Cache/_ControlGrpcManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,31 @@
use Grpc\Channel;
use Grpc\ChannelCredentials;
use Control_client\ScsControlClient;
use Grpc\Interceptor;
use Momento\Auth\ICredentialProvider;
use Momento\Cache\Interceptors\AgentInterceptor;
use Momento\Cache\Interceptors\AuthorizationInterceptor;


class _ControlGrpcManager
{

public ScsControlClient $client;

public function __construct(string $authToken, string $endpoint)
public function __construct(ICredentialProvider $authProvider)
{
$options = [
"update_metadata" => function ($metadata) use ($authToken) {
$metadata["authorization"] = [$authToken];
$metadata["agent"] = ["php:0.1"];
return $metadata;
}
$endpoint = $authProvider->getControlProxyEndpoint() ?? $authProvider->getControlEndpoint();
$channelArgs = ["credentials" => ChannelCredentials::createSsl()];
if ($authProvider->getControlProxyEndpoint()) {
$channelArgs["grpc.ssl_target_name_override"] = $authProvider->getControlEndpoint();
}
$channel = new Channel($endpoint, $channelArgs);
$interceptors = [
new AuthorizationInterceptor($authProvider->getAuthToken()),
new AgentInterceptor(),
];

$channel = new Channel($endpoint, ["credentials" => ChannelCredentials::createSsl()]);
$channel = Interceptor::intercept($channel, $interceptors);
$options = [];
$this->client = new ScsControlClient($endpoint, $options, $channel);
}

Expand Down
14 changes: 10 additions & 4 deletions src/Cache/_DataGrpcManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Grpc\Channel;
use Grpc\ChannelCredentials;
use Grpc\Interceptor;
use Momento\Auth\ICredentialProvider;
use Momento\Cache\Interceptors\AgentInterceptor;
use Momento\Cache\Interceptors\AuthorizationInterceptor;

Expand All @@ -15,15 +16,20 @@ class _DataGrpcManager

public ScsClient $client;

public function __construct(string $authToken, string $endpoint)
public function __construct(ICredentialProvider $authProvider)
{
$options = [];
$channel = new Channel($endpoint, ["credentials" => ChannelCredentials::createSsl()]);
$endpoint = $authProvider->getCacheProxyEndpoint() ?? $authProvider->getCacheEndpoint();
$channelArgs = ["credentials" => ChannelCredentials::createSsl()];
if ($authProvider->getCacheProxyEndpoint()) {
$channelArgs["grpc.ssl_target_name_override"] = $authProvider->getCacheEndpoint();
}
$channel = new Channel($endpoint, $channelArgs);
$interceptors = [
new AuthorizationInterceptor($authToken),
new AuthorizationInterceptor($authProvider->getAuthToken()),
new AgentInterceptor(),
];
$channel = Interceptor::intercept($channel, $interceptors);
$options = [];
$this->client = new ScsClient($endpoint, $options, $channel);
}
}
5 changes: 3 additions & 2 deletions src/Cache/_ScsControlClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Control_client\_DeleteCacheRequest;
use Control_client\_ListCachesRequest;
use Grpc\UnaryCall;
use Momento\Auth\ICredentialProvider;
use Momento\Cache\CacheOperationTypes\CreateCacheResponse;
use Momento\Cache\CacheOperationTypes\CreateCacheResponseAlreadyExists;
use Momento\Cache\CacheOperationTypes\CreateCacheResponseError;
Expand All @@ -32,9 +33,9 @@ class _ScsControlClient implements LoggerAwareInterface
private _ControlGrpcManager $grpcManager;
private LoggerInterface $logger;

public function __construct(LoggerInterface $logger, string $authToken, string $endpoint)
public function __construct(LoggerInterface $logger, ICredentialProvider $authProvider)
{
$this->grpcManager = new _ControlGrpcManager($authToken, $endpoint);
$this->grpcManager = new _ControlGrpcManager($authProvider);
$this->setLogger($logger);
}

Expand Down
5 changes: 3 additions & 2 deletions src/Cache/_ScsDataClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use Cache_client\ECacheResult;
use Exception;
use Grpc\UnaryCall;
use Momento\Auth\ICredentialProvider;
use Momento\Cache\CacheOperationTypes\CacheDeleteResponse;
use Momento\Cache\CacheOperationTypes\CacheDeleteResponseError;
use Momento\Cache\CacheOperationTypes\CacheDeleteResponseSuccess;
Expand Down Expand Up @@ -134,7 +135,7 @@ class _ScsDataClient implements LoggerAwareInterface
private LoggerInterface $logger;
private int $timeout;

public function __construct(IConfiguration $configuration, string $authToken, string $endpoint, int $defaultTtlSeconds)
public function __construct(IConfiguration $configuration, ICredentialProvider $authProvider, int $defaultTtlSeconds)
{
validateTtl($defaultTtlSeconds);
$this->defaultTtlSeconds = $defaultTtlSeconds;
Expand All @@ -145,7 +146,7 @@ public function __construct(IConfiguration $configuration, string $authToken, st
validateOperationTimeout($operationTimeoutMs);
$this->deadline_milliseconds = $operationTimeoutMs ?? self::$DEFAULT_DEADLINE_MILLISECONDS;
$this->timeout = $this->deadline_milliseconds * self::$TIMEOUT_MULTIPLIER;
$this->grpcManager = new _DataGrpcManager($authToken, $endpoint);
$this->grpcManager = new _DataGrpcManager($authProvider);
$this->setLogger($configuration->getLogger());
}

Expand Down