Skip to content

Commit

Permalink
[CORE-1576] Commands to set and unset primary domain. (#2011)
Browse files Browse the repository at this point in the history
* Commands to set and unset primary domain.

WIP: needs tests.

* Tests for setting/resetting primary domain and its commands.

* Rename command to remove primary domain to `domain:primary:reset` for consistency.

* Refactor set_primary_domain workflow maker out of a Command to Models\Environment class.

* CS fix

* Filter pantheonsite.io domains from interactive choice list.

While the platform supports setting these domains as primary, it seems
highly unlikely that a user would do this intentionally, and more likely
that if it were chosen from a multiple choice list, that it was just
fat-fingered. For better UX, they are therefore excluded.

Users that really mean to do this can do so by typing/passing the full
pantheonsite.io domain non-interactively to the domain:primary:set
command.

* Add primary domain to Domain model and domain:list command.

* UX review: Change command to domain:primary:unset, log completion.

* Changes that are clear from @TeslaDethray's requests.

* Rename domain:primary:set to domain:primary:add per consensus of product and technical leadership.

* Best guess at new model refactor request

* Fix failing tests in older minor versions of php 7.0 - 7.3.

It seems substr_compare issues a warning iff the first parameter is
the empty string in these earlier minor releases; manual states it
was fixed in 7.2.18. Since a) it's been fixed and b) empty strings
are not expected real-world input, I've simply removed that input
from the test coverage.

* Behat tests for add and remove primary domain

* Make behat.yml a bit more yaml-compliant (% not allowed as beginning of string)

* Restore Consolidation\\Filter\\Hooks\\FilterHooks to command list

* Changelog for 2.2.0

* Fix stated version in changelog
  • Loading branch information
mbaynton authored and TeslaDethray committed Sep 26, 2019
1 parent 15b9174 commit 6bddde1
Show file tree
Hide file tree
Showing 15 changed files with 656 additions and 3 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Change Log
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org)

## MASTER
### Added
- Added `primary` field to the output of `domain:list`. (#2011)
- Added `domain:primary:add` command to set a domain as primary, causing traffic to redirect to it. (#2011)
- Added `domain:primary:remove` command to remove a domain's primary designation. (#2011)

## 2.1.0 - 2019-09-03
### Added
- Added `--filter` option to `backup:list` command (#1992)
Expand Down
1 change: 1 addition & 0 deletions src/Commands/Domain/ListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class ListCommand extends TerminusCommand implements SiteAwareInterface
* @field-labels
* id: Domain/ID
* type: Type
* primary: Is Primary
* deletable: Is Deletable
* status: status
* @return RowsOfFields
Expand Down
98 changes: 98 additions & 0 deletions src/Commands/Domain/Primary/AddCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php


namespace Pantheon\Terminus\Commands\Domain\Primary;

use Consolidation\AnnotatedCommand\AnnotationData;
use Pantheon\Terminus\Commands\TerminusCommand;
use Pantheon\Terminus\Commands\WorkflowProcessingTrait;
use Pantheon\Terminus\Models\Environment;
use Pantheon\Terminus\Models\Site;
use Pantheon\Terminus\Site\SiteAwareInterface;
use Pantheon\Terminus\Site\SiteAwareTrait;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
* Class AddCommand
* @package Pantheon\Terminus\Commands\Domain\Primary
*/
class AddCommand extends TerminusCommand implements SiteAwareInterface
{
use SiteAwareTrait;
use WorkflowProcessingTrait;

const PLATFORM_DOMAIN = '.pantheonsite.io';

/**
* Sets a domain associated to the environment as primary, causing all traffic to redirect to it.
*
* @authorize
*
* @command domain:primary:add
*
* @param string $site_env Site & environment in the format `site-name.env`
* @param string $domain A domain that has been associated to your site. Optional when running interactively.
*
* @usage domain:primary:add <site_env>
*/
public function add($site_env, $domain)
{
/**
* @var $site Site
* @var $env Environment
*/
list($site, $env) = $this->getSiteEnv($site_env);

// The primary domain is set via a workflow so as to use workflow logging to track changes & update policy docs.
$workflow = $env->getPrimaryDomainModel()->setPrimaryDomain($domain);
$this->processWorkflow($workflow);
$this->log()->notice(
'Set {domain} as primary for {site}.{env}',
['domain' => $domain, 'site' => $site->get('name'), 'env' => $env->id,]
);
}

/**
* Prompt the user for the domain, if it was not specified.
*
* n.b. This hook is not called in --no-interaction mode.
*
* @param InputInterface $input
* @param OutputInterface $output
* @param AnnotationData $annotationData
*
* @hook interact domain:primary:add
*/
public function interact(InputInterface $input, OutputInterface $output, AnnotationData $annotationData)
{
$domain = $input->getArgument('domain');
if (empty($domain)) {
/**
* @var $site Site
* @var $env Environment
*/
list($site, $env) = $this->getSiteEnv($input->getArgument('site_env'));
$domains = $this->filterPlatformDomains($env->getDomains()->ids());
sort($domains);

if (!empty($domains)) {
$domain = $this->io()->choice('Select the primary domain for this site', $domains);
$input->setArgument('domain', $domain);
}
}
}

/**
* Filters strings ending in the platform domain from an array.
*
* @param $domains
* @return array
*/
public function filterPlatformDomains($domains)
{
return array_filter($domains, function ($domain) {
return substr_compare($domain, self::PLATFORM_DOMAIN, -strlen(self::PLATFORM_DOMAIN)) !== 0;
});
}
}
43 changes: 43 additions & 0 deletions src/Commands/Domain/Primary/RemoveCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php


namespace Pantheon\Terminus\Commands\Domain\Primary;

use Pantheon\Terminus\Commands\TerminusCommand;
use Pantheon\Terminus\Commands\WorkflowProcessingTrait;
use Pantheon\Terminus\Models\Environment;
use Pantheon\Terminus\Models\Site;
use Pantheon\Terminus\Site\SiteAwareInterface;
use Pantheon\Terminus\Site\SiteAwareTrait;

class RemoveCommand extends TerminusCommand implements SiteAwareInterface
{
use SiteAwareTrait;
use WorkflowProcessingTrait;

/**
* Removes the primary designation from the primary domain in the site and environment.
*
* @authorize
*
* @command domain:primary:remove
* @aliases domain:primary:rm
*
* @param string $site_env Site & environment in the format `site-name.env`
*/
public function remove($site_env)
{
/**
* @var $site Site
* @var $env Environment
*/
list($site, $env) = $this->getSiteEnv($site_env);

$workflow = $env->getPrimaryDomainModel()->removePrimaryDomain();
$this->processWorkflow($workflow);
$this->log()->notice(
'Primary domain has been removed from {site}.{env}',
['site' => $site->get('name'), 'env' => $env->id,]
);
}
}
1 change: 1 addition & 0 deletions src/Models/Domain.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public function serialize()
'status' => in_array($this->get('status'), ['ok', 'okay',]) ? 'OK' : $this->get('status'),
'status_message' => $this->get('status_message'),
'deletable' => (boolean)$this->get('deletable'),
'primary' => (boolean)$this->get('primary'),
];
}
}
8 changes: 8 additions & 0 deletions src/Models/Environment.php
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,14 @@ public function getDomains()
return $this->domains;
}

/**
* @return PrimaryDomain
*/
public function getPrimaryDomainModel()
{
return $this->getContainer()->get(PrimaryDomain::class, [$this]);
}

/**
* Gets the Drush version of this environment
*
Expand Down
54 changes: 54 additions & 0 deletions src/Models/PrimaryDomain.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php


namespace Pantheon\Terminus\Models;

use Pantheon\Terminus\Friends\EnvironmentInterface;
use Pantheon\Terminus\Friends\EnvironmentTrait;

class PrimaryDomain implements EnvironmentInterface
{
use EnvironmentTrait;

public function __construct(Environment $environment)
{
$this->setEnvironment($environment);
}

/**
* Builds a Workflow to set the primary domain for this environment.
*
* @param string $domain A domain name attached to this environment.
*
* @return Workflow
*/
public function setPrimaryDomain($domain)
{
return $this->workflowFactory($domain);
}

/**
* Builds a workflow to remove the primary domain from this environment.
*
* @return Workflow
*/
public function removePrimaryDomain()
{
return $this->workflowFactory(null);
}

/**
* @param string $domain
* @return Workflow
*/
protected function workflowFactory($domain)
{
return $this->getEnvironment()->getWorkflows()->create(
'set_primary_domain',
[
'environment' => $this->getEnvironment()->id,
'params' => ['primary_domain' => $domain]
]
);
}
}
3 changes: 3 additions & 0 deletions src/Terminus.php
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ private function addBuiltInCommandsAndHooks()
'Pantheon\\Terminus\\Commands\\Domain\\DNSCommand',
'Pantheon\\Terminus\\Commands\\Domain\\ListCommand',
'Pantheon\\Terminus\\Commands\\Domain\\LookupCommand',
'Pantheon\\Terminus\\Commands\\Domain\\Primary\\AddCommand',
'Pantheon\\Terminus\\Commands\\Domain\\Primary\\RemoveCommand',
'Pantheon\\Terminus\\Commands\\Domain\\RemoveCommand',
'Pantheon\\Terminus\\Commands\\Env\\ClearCacheCommand',
'Pantheon\\Terminus\\Commands\\Env\\CloneContentCommand',
Expand Down Expand Up @@ -367,6 +369,7 @@ private function configureModulesAndCollections($container)
$container->add(\Pantheon\Terminus\Models\OrganizationUserMembership::class);
$container->add(\Pantheon\Terminus\Models\PaymentMethod::class);
$container->add(\Pantheon\Terminus\Models\Plan::class);
$container->add(\Pantheon\Terminus\Models\PrimaryDomain::class);
$container->add(\Pantheon\Terminus\Models\Profile::class);
$container->add(\Pantheon\Terminus\Models\Redis::class);
$container->add(\Pantheon\Terminus\Models\SSHKey::class);
Expand Down
4 changes: 2 additions & 2 deletions tests/config/behat.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
default:
autoload: [ %paths.base%/../features/bootstrap ]
autoload: [ '%paths.base%/../features/bootstrap' ]
suites:
default:
paths: [ %paths.base%/../features ]
paths: [ '%paths.base%/../features' ]
contexts:
- Pantheon\Terminus\FeatureTests\FeatureContext:
parameters:
Expand Down
28 changes: 28 additions & 0 deletions tests/features/domain-primary.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
Feature: Adding a primary domain to an environment
In order to redirect all visitors to my preferred domain
As a user
I need to be able to manage primary domains attached to my site's environments

Background: I am authenticated and have a site named [[test_site_name]]
Given I am authenticated
And a site named "[[test_site_name]]"

@vcr domain-primary-add.yml
Scenario: Adding a primary domain to an environment
When I run "terminus domain:primary:add [[test_site_name]].live testdomain.com"
Then I should get "."
And I should get "."
Then I should get:
"""
Set testdomain.com as primary for [[test_site_name]].live
"""

@vcr domain-primary-add.yml
Scenario: Removing a primary domain from an environment
When I run "terminus domain:primary:remove [[test_site_name]].live"
Then I should get "."
And I should get "."
Then I should get:
"""
Primary domain has been removed from [[test_site_name]].live
"""
223 changes: 223 additions & 0 deletions tests/fixtures/domain-primary-add.yml

Large diffs are not rendered by default.

54 changes: 54 additions & 0 deletions tests/unit_tests/Commands/Domain/Primary/AddCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php


namespace Pantheon\Terminus\UnitTests\Commands\Domain\Primary;

use Pantheon\Terminus\Commands\Domain\Primary\AddCommand;
use Pantheon\Terminus\UnitTests\Commands\Domain\Primary\PrimaryDomainCommandsTestBase;

/**
* Class AddCommandTest
* Test suite class for Pantheon\Terminus\Commands\Domain\Primary\AddCommand
* @package Pantheon\Terminus\UnitTests\Commands\Domain\Primary
*/
class AddCommandTest extends PrimaryDomainCommandsTestBase
{
protected function getSystemUnderTest()
{
return new AddCommand();
}

public function testAdd()
{
$site_name = 'site_name';
$domain = 'some.domain';
$this->prepareTestSetReset(
$domain,
'Set {domain} as primary for {site}.{env}',
['domain' => $domain, 'site' => $this->site->get('name'), 'env' => $this->environment->id]
);

$out = $this->command->add("$site_name.{$this->environment->id}", $domain);
$this->assertNull($out);
}

public function testFilterPlatformDomains()
{
$sys_under_test = new AddCommand();
$this->assertEquals(
array_values([
'x',
'something.com',
'averylong.domain.ofsomeosrt.tld',
]),
array_values($sys_under_test->filterPlatformDomains(
[
'x',
'something.com',
'dev-mikes-testsite.pantheonsite.io',
'averylong.domain.ofsomeosrt.tld',
]
))
);
}
}
Loading

0 comments on commit 6bddde1

Please sign in to comment.