Skip to content

Commit 8ebc17c

Browse files
committed
FOUR-29068 Add NAYRA_REST_API_HOST support to ScriptDockerNayraTrait
When NAYRA_REST_API_HOST is set in .env, the trait uses that URL directly instead of Docker discovery, enabling Nayra script execution where automatic discovery fails (macOS/Docker Desktop, port conflicts, external Nayra services). Problem (without this change): - ScriptDockerNayraTrait uses Docker discovery (docker inspect, hostname -i) to build http://{ip}:{port}/run_script - On macOS/Docker Desktop: IPv6 from hostname -i produces invalid URLs; container IP may be unreachable from host - Port 8080 conflict: nginx or other service responds instead of Nayra - External Nayra: no way to configure a fixed URL Solution: - Add getNayraBaseUrl(): returns NAYRA_REST_API_HOST if set, else getNayraInstanceUrl() - Update handleNayraDocker() to use getNayraBaseUrl() - Update ensureNayraServerIsRunning(): when NAYRA_REST_API_HOST is set and connection fails, throw ScriptException instead of bringUpNayra() Configuration (.env): NAYRA_REST_API_HOST=http://localhost:8081 Backward compatible: if not set, original Docker discovery flow is used. Affected file: ProcessMaker/Models/ScriptDockerNayraTrait.php
1 parent 4b66ff3 commit 8ebc17c

2 files changed

Lines changed: 193 additions & 7 deletions

File tree

ProcessMaker/Models/ScriptDockerNayraTrait.php

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,9 @@ public function handleNayraDocker(string $code, array $data, array $config, $tim
4545
'timeout' => $timeout,
4646
];
4747
$body = json_encode($params);
48-
$servers = self::getNayraAddresses();
49-
if (!$servers) {
50-
$this->bringUpNayra();
51-
}
52-
$baseUrl = $this->getNayraInstanceUrl();
53-
$url = $baseUrl . '/run_script';
48+
49+
$baseUrl = $this->getNayraBaseUrl();
50+
$url = rtrim($baseUrl, '/') . '/run_script';
5451
$this->ensureNayraServerIsRunning($baseUrl);
5552

5653
$ch = curl_init($url);
@@ -78,9 +75,28 @@ public function handleNayraDocker(string $code, array $data, array $config, $tim
7875
return $result;
7976
}
8077

81-
private function getNayraInstanceUrl()
78+
/**
79+
* Get the base URL for the Nayra service.
80+
* Uses NAYRA_REST_API_HOST if set, otherwise Docker discovery.
81+
*/
82+
private function getNayraBaseUrl(): string
83+
{
84+
$restApiHost = config('app.nayra_rest_api_host');
85+
if (!empty($restApiHost)) {
86+
return rtrim($restApiHost, '/');
87+
}
88+
89+
return $this->getNayraInstanceUrl();
90+
}
91+
92+
private function getNayraInstanceUrl(): string
8293
{
8394
$servers = self::getNayraAddresses();
95+
if (!$servers) {
96+
$this->bringUpNayra();
97+
$servers = self::getNayraAddresses();
98+
}
99+
84100
return $this->schema . '://' . $servers[0] . ':' . $this->getNayraPort();
85101
}
86102

@@ -106,6 +122,9 @@ private function ensureNayraServerIsRunning(string $url)
106122
{
107123
$header = @get_headers($url);
108124
if (!$header) {
125+
if (!empty(config('app.nayra_rest_api_host'))) {
126+
throw new ScriptException('Could not connect to the nayra container');
127+
}
109128
$this->bringUpNayra(true);
110129
}
111130
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Unit\ProcessMaker\Models;
6+
7+
use Illuminate\Support\Facades\Config;
8+
use ProcessMaker\Exception\ScriptException;
9+
use ProcessMaker\Models\ScriptExecutor;
10+
use ProcessMaker\ScriptRunners\Base;
11+
use Tests\TestCase;
12+
13+
/**
14+
* Tests for ScriptDockerNayraTrait - getNayraBaseUrl, ensureNayraServerIsRunning,
15+
* getNayraAddresses, setNayraAddresses, clearNayraAddresses, and handleNayraDocker.
16+
*/
17+
class ScriptDockerNayraTraitTest extends TestCase
18+
{
19+
/**
20+
* Create a Nayra runner for testing (uses ScriptDockerNayraTrait via Base).
21+
*/
22+
private function createNayraRunner(): Base
23+
{
24+
$scriptExecutor = ScriptExecutor::factory()->create([
25+
'language' => Base::NAYRA_LANG,
26+
]);
27+
28+
return new class($scriptExecutor) extends Base {
29+
public function config($code, array $dockerConfig): array
30+
{
31+
return [];
32+
}
33+
};
34+
}
35+
36+
protected function tearDown(): void
37+
{
38+
Base::clearNayraAddresses();
39+
parent::tearDown();
40+
}
41+
42+
public function testGetNayraAddressesReturnsNullWhenNotSet(): void
43+
{
44+
Base::clearNayraAddresses();
45+
$this->assertNull(Base::getNayraAddresses());
46+
}
47+
48+
public function testSetAndGetNayraAddresses(): void
49+
{
50+
$addresses = ['192.168.1.100', '192.168.1.101'];
51+
Base::setNayraAddresses($addresses);
52+
$this->assertEquals($addresses, Base::getNayraAddresses());
53+
}
54+
55+
public function testClearNayraAddresses(): void
56+
{
57+
Base::setNayraAddresses(['192.168.1.100']);
58+
Base::clearNayraAddresses();
59+
$this->assertNull(Base::getNayraAddresses());
60+
}
61+
62+
public function testGetNayraBaseUrlReturnsNayraRestApiHostWhenConfigured(): void
63+
{
64+
Config::set('app.nayra_rest_api_host', 'http://nayra.example.com:9000');
65+
$runner = $this->createNayraRunner();
66+
67+
// getNayraBaseUrl is private - test via handleNayraDocker which uses it.
68+
// When NAYRA_REST_API_HOST is set and server is unreachable, ensureNayraServerIsRunning
69+
// throws immediately (does not try bringUpNayra).
70+
$this->expectException(ScriptException::class);
71+
$this->expectExceptionMessage('Could not connect to the nayra container');
72+
73+
$runner->handleNayraDocker(
74+
'<?php return ["ok"];',
75+
[],
76+
[],
77+
60,
78+
['API_TOKEN=test']
79+
);
80+
}
81+
82+
public function testGetNayraBaseUrlTrimsTrailingSlashFromRestApiHost(): void
83+
{
84+
Config::set('app.nayra_rest_api_host', 'http://nayra.example.com/');
85+
$runner = $this->createNayraRunner();
86+
87+
// URL used should be http://nayra.example.com/run_script (no double slash)
88+
// We verify by checking the exception - if URL were wrong we might get different error.
89+
// The key: NAYRA_REST_API_HOST with trailing slash is trimmed.
90+
$this->expectException(ScriptException::class);
91+
$this->expectExceptionMessage('Could not connect to the nayra container');
92+
93+
$runner->handleNayraDocker(
94+
'<?php return ["ok"];',
95+
[],
96+
[],
97+
60,
98+
['API_TOKEN=test']
99+
);
100+
}
101+
102+
public function testEnsureNayraServerIsRunningThrowsImmediatelyWhenRestApiHostSetAndUnreachable(): void
103+
{
104+
Config::set('app.nayra_rest_api_host', 'http://this-domain-does-not-exist-12345.invalid');
105+
$runner = $this->createNayraRunner();
106+
107+
$this->expectException(ScriptException::class);
108+
$this->expectExceptionMessage('Could not connect to the nayra container');
109+
110+
$runner->handleNayraDocker(
111+
'<?php return ["ok"];',
112+
[],
113+
[],
114+
60,
115+
['API_TOKEN=test']
116+
);
117+
}
118+
119+
public function testHandleNayraDockerUsesRestApiHostUrl(): void
120+
{
121+
Config::set('app.nayra_rest_api_host', 'http://unreachable-host.invalid');
122+
$runner = $this->createNayraRunner();
123+
124+
// Verifies that NAYRA_REST_API_HOST is used (ensureNayraServerIsRunning throws
125+
// immediately instead of trying Docker bringUpNayra)
126+
$this->expectException(ScriptException::class);
127+
$this->expectExceptionMessage('Could not connect to the nayra container');
128+
129+
$runner->handleNayraDocker(
130+
'<?php return ["ok"];',
131+
[],
132+
[],
133+
60,
134+
[]
135+
);
136+
}
137+
138+
public function testHandleNayraDockerParsesEnvironmentVariables(): void
139+
{
140+
Config::set('app.nayra_rest_api_host', 'http://unreachable.invalid');
141+
$runner = $this->createNayraRunner();
142+
143+
// Just verify it reaches ensureNayraServerIsRunning (env parsing happens before)
144+
$this->expectException(ScriptException::class);
145+
$this->expectExceptionMessage('Could not connect to the nayra container');
146+
147+
$runner->handleNayraDocker(
148+
'<?php return ["ok"];',
149+
['key' => 'value'],
150+
['config' => 'data'],
151+
120,
152+
['API_TOKEN=secret', 'HOST_URL=http://localhost']
153+
);
154+
}
155+
156+
public function testCachedAddressesPersistAcrossCalls(): void
157+
{
158+
Base::clearNayraAddresses();
159+
$this->assertNull(Base::getNayraAddresses());
160+
161+
Base::setNayraAddresses(['10.0.0.5']);
162+
$this->assertEquals(['10.0.0.5'], Base::getNayraAddresses());
163+
164+
Base::setNayraAddresses(['192.168.1.1', '192.168.1.2']);
165+
$this->assertEquals(['192.168.1.1', '192.168.1.2'], Base::getNayraAddresses());
166+
}
167+
}

0 commit comments

Comments
 (0)