Skip to content
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
1 change: 1 addition & 0 deletions integration-tests/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ service:
env_prefix: 'PLATFORM_'
project_config_dir: '.platform'
console_url: 'https://console.cli-tests.example.com'
fixed_docs_url: 'https://docs.cli-tests.example.com/fixed/'

api:
# Placeholder URLs which can be replaced during tests.
Expand Down
101 changes: 101 additions & 0 deletions integration-tests/resources_sizing_disabled_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package tests

import (
"encoding/json"
"net/http"
"net/http/httptest"
"net/url"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/upsun/cli/pkg/mockapi"
)

// TestResourcesSizingDisabled checks the error shown when the project does not
// support flexible resources, which mentions Fixed only for Fixed orgs.
func TestResourcesSizingDisabled(t *testing.T) {
const (
fixedNote = "not available for Fixed organizations"
fixedURL = "https://docs.cli-tests.example.com/fixed/"
)

commands := [][]string{
{"resources:get", "-e", "main"},
{"resources:set", "-e", "main", "--size", "app:0.5"},
{"resources:size:list", "-e", "main"},
{"resources:build:get"},
{"resources:build:set", "--cpu", "1"},
}
cases := []struct {
name string
orgType string
wantFixed bool
}{
{"fixed org", "fixed", true},
{"flexible org", "flexible", false},
{"inaccessible org", "", false},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
authServer := mockapi.NewAuthServer(t)
defer authServer.Close()

apiHandler := mockapi.NewHandler(t)

myUserID := "my-user-id"
orgID := "org-id"
apiHandler.SetMyUser(&mockapi.User{ID: myUserID})
if c.orgType != "" {
apiHandler.SetOrgs([]*mockapi.Org{{
ID: orgID,
Type: c.orgType,
Name: "acme",
Label: "Acme",
Owner: myUserID,
Capabilities: []string{},
Links: mockapi.MakeHALLinks("self=/organizations/" + url.PathEscape(orgID)),
}})
}

projectID := mockapi.ProjectID()
apiHandler.SetProjects([]*mockapi.Project{{
ID: projectID,
Organization: orgID,
Links: mockapi.MakeHALLinks(
"self=/projects/"+projectID,
"environments=/projects/"+projectID+"/environments",
),
DefaultBranch: "main",
}})
apiHandler.SetEnvironments([]*mockapi.Environment{
makeEnv(projectID, "main", "production", "active", nil),
})
apiHandler.Get("/projects/"+projectID+"/settings", func(w http.ResponseWriter, _ *http.Request) {
_ = json.NewEncoder(w).Encode(map[string]any{"sizing_api_enabled": false})
})

apiServer := httptest.NewServer(apiHandler)
defer apiServer.Close()

f := newCommandFactory(t, apiServer.URL, authServer.URL)

for _, args := range commands {
t.Run(args[0], func(t *testing.T) {
_, stderr, err := f.RunCombinedOutput(append(args, "-p", projectID)...)
require.Error(t, err)
assert.Contains(t, stderr, "The flexible resources API is not enabled for the project")
if c.wantFixed {
assert.Contains(t, stderr, fixedNote)
assert.Contains(t, stderr, fixedURL)
} else {
assert.NotContains(t, stderr, fixedNote)
assert.NotContains(t, stderr, fixedURL)
}
})
}
})
}
}
2 changes: 2 additions & 0 deletions internal/config/platformsh-cli.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ service:

pricing_url: "https://upsun.com/pricing"

fixed_docs_url: "https://docs.upsun.com/anchors/fixed/"

activity_type_list_url: "https://docs.upsun.com/anchors/fixed/integrations/activity-scripts/type/"

runtime_operations_help_url: "https://docs.upsun.com/anchors/fixed/app/runtime-operations/"
Expand Down
2 changes: 2 additions & 0 deletions internal/config/upsun-cli.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ service:

pricing_url: "https://upsun.com/pricing"

fixed_docs_url: "https://docs.upsun.com/anchors/fixed/"

activity_type_list_url: "https://docs.upsun.com/anchors/integrations/activity-scripts/type/"

runtime_operations_help_url: "https://docs.upsun.com/anchors/app/runtime-operations/"
Expand Down
3 changes: 3 additions & 0 deletions legacy/config-defaults.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ service:
# Optional URL to help about managing resources.
resources_help_url: null

# Optional URL to docs for Fixed organizations.
fixed_docs_url: null

# Configuration relating to API calls.
# This can be overridden in the user config file.
api:
Expand Down
2 changes: 2 additions & 0 deletions legacy/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ service:

pricing_url: 'https://upsun.com/pricing'

fixed_docs_url: 'https://docs.upsun.com/anchors/fixed/'

activity_type_list_url: 'https://docs.upsun.com/anchors/fixed/integrations/activity-scripts/type/'

runtime_operations_help_url: 'https://docs.upsun.com/anchors/fixed/app/runtime-operations/'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Platformsh\Cli\Selector\Selector;
use Platformsh\Cli\Service\Api;
use Platformsh\Cli\Service\Config;
use Platformsh\Cli\Service\ResourcesUtil;
use Platformsh\Cli\Command\CommandBase;
use Platformsh\Cli\Service\Table;
use Symfony\Component\Console\Attribute\AsCommand;
Expand All @@ -23,7 +24,7 @@ class BuildResourcesGetCommand extends CommandBase
'memory' => 'Memory (MB)',
];

public function __construct(private readonly Api $api, private readonly Config $config, private readonly Selector $selector, private readonly Table $table)
public function __construct(private readonly Api $api, private readonly Config $config, private readonly ResourcesUtil $resourcesUtil, private readonly Selector $selector, private readonly Table $table)
{
parent::__construct();
}
Expand All @@ -42,7 +43,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
{
$selection = $this->selector->getSelection($input);
if (!$this->api->supportsSizingApi($selection->getProject())) {
$this->stdErr->writeln(sprintf('The flexible resources API is not enabled for the project %s.', $this->api->getProjectLabel($selection->getProject(), 'comment')));
$this->resourcesUtil->writeSizingApiDisabledError($selection->getProject());
return 1;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
{
$selection = $this->selector->getSelection($input);
if (!$this->api->supportsSizingApi($selection->getProject())) {
$this->stdErr->writeln(sprintf('The flexible resources API is not enabled for the project %s.', $this->api->getProjectLabel($selection->getProject(), 'comment')));
$this->resourcesUtil->writeSizingApiDisabledError($selection->getProject());
return 1;
}

Expand Down
2 changes: 1 addition & 1 deletion legacy/src/Command/Resources/ResourcesGetCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
{
$selection = $this->selector->getSelection($input);
if (!$this->api->supportsSizingApi($selection->getProject())) {
$this->stdErr->writeln(sprintf('The flexible resources API is not enabled for the project %s.', $this->api->getProjectLabel($selection->getProject(), 'comment')));
$this->resourcesUtil->writeSizingApiDisabledError($selection->getProject());
return 1;
}

Expand Down
2 changes: 1 addition & 1 deletion legacy/src/Command/Resources/ResourcesSetCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
{
$selection = $this->selector->getSelection($input);
if (!$this->api->supportsSizingApi($selection->getProject())) {
$this->stdErr->writeln(sprintf('The flexible resources API is not enabled for the project %s.', $this->api->getProjectLabel($selection->getProject(), 'comment')));
$this->resourcesUtil->writeSizingApiDisabledError($selection->getProject());
return 1;
}

Expand Down
2 changes: 1 addition & 1 deletion legacy/src/Command/Resources/ResourcesSizeListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
{
$selection = $this->selector->getSelection($input);
if (!$this->api->supportsSizingApi($selection->getProject())) {
$this->stdErr->writeln(sprintf('The flexible resources API is not enabled for the project %s.', $this->api->getProjectLabel($selection->getProject(), 'comment')));
$this->resourcesUtil->writeSizingApiDisabledError($selection->getProject());
return 1;
}

Expand Down
29 changes: 29 additions & 0 deletions legacy/src/Service/ResourcesUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,35 @@ public function featureEnabled(): bool
return $this->config->getBool('api.sizing');
}

/**
* Prints an error explaining that the project does not support flexible resources.
*/
public function writeSizingApiDisabledError(Project $project): void
{
$this->stdErr->writeln(sprintf('The flexible resources API is not enabled for the project %s.', $this->api->getProjectLabel($project, 'comment')));
if ($this->config->has('service.fixed_docs_url') && $this->isFixedProject($project)) {
$this->stdErr->writeln('Flexible resources are not available for Fixed organizations. See: <info>' . $this->config->getStr('service.fixed_docs_url') . '</info>');
}
}

/**
* Checks if a project belongs to a Fixed organization, returning false if unknown.
*/
private function isFixedProject(Project $project): bool
{
$orgId = $project->getProperty('organization', false, false);
if (!$orgId || !$this->config->getBool('api.organizations')) {
return false;
}
try {
$organization = $this->api->getOrganizationById($orgId);
} catch (\Exception) {
// The user may not have access to the organization.
return false;
}
return $organization && $organization->getProperty('type', false) === 'fixed';
}

/**
* Lists services in a deployment.
*
Expand Down
Loading