Skip to content
Open
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
9 changes: 9 additions & 0 deletions libs/k8s-client/src/Model/Io/Keboola/Apps/V1/AppRunSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,13 @@ class AppRunSpec extends AbstractModel
* @var string|null
*/
public $imageVersion = null;

/**
* Mode records how the parent App was running when this run was created.
* "prod" (default) for normal runs; "dev" for dev-mode runs. Treat absent
* or unknown values as "prod" for compatibility with legacy AppRun CRDs.
*
* @var string|null
*/
public $mode = null;
}
31 changes: 31 additions & 0 deletions libs/k8s-client/src/Model/Io/Keboola/Apps/V2/AppDevSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace Keboola\K8sClient\Model\Io\Keboola\Apps\V2;

use KubernetesRuntime\AbstractModel;

/**
* AppDevSpec carries dev-mode-only knobs. Ignored when AppSpec.mode != "dev".
*/
class AppDevSpec extends AbstractModel
{
/**
* GitPollInterval is how often the in-pod git-watcher fetches and resets
* to origin/<branch>. ISO-8601 duration string (e.g. "1s", "30s", "5m").
* Default 1s, min 1s, max 5m (enforced at admission via CRD CEL).
*
* @var string|null
*/
public $gitPollInterval = null;

/**
* AutoRunSetupOnDepChange controls whether the in-pod git-watcher runs
* setup-dev.sh and restarts the app program when a tracked dependency
* file changes during a poll cycle. Default true.
*
* @var bool|null
*/
public $autoRunSetupOnDepChange = null;
}
16 changes: 16 additions & 0 deletions libs/k8s-client/src/Model/Io/Keboola/Apps/V2/AppSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,20 @@ class AppSpec extends AbstractModel
* @var AppFeatures|null
*/
public $features = null;

/**
* Mode selects how the app is run: "prod" (default) runs the app as-is;
* "dev" enables hot-reload via an in-pod git-watcher and the dev
* supervisord profile. Both Deployment and E2B backends honour Mode.
*
* @var string|null
*/
public $mode = null;

/**
* Dev carries dev-mode-only knobs. Ignored when mode != "dev".
*
* @var AppDevSpec|null
*/
public $dev = null;
}
3 changes: 3 additions & 0 deletions libs/k8s-client/tests/Model/V1/AppRunModelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ private static function getAppRunTestData(): array
'startupLogs' => "foo\nbar\n",
'runtimeSize' => 'small',
'configVersion' => '1',
'mode' => 'dev',
],
'status' => [
'syncedAt' => '2025-01-15T13:05:00Z',
Expand Down Expand Up @@ -77,6 +78,7 @@ public function testAppRunModelHydration(): void
self::assertSame("foo\nbar\n", $appRun->spec->startupLogs);
self::assertSame('small', $appRun->spec->runtimeSize);
self::assertSame('1', $appRun->spec->configVersion);
self::assertSame('dev', $appRun->spec->mode);

// PodRef
self::assertNotNull($appRun->spec->podRef);
Expand Down Expand Up @@ -118,5 +120,6 @@ public function testAppRunModelSerialization(): void
self::assertSame('Finished', $serialized['spec']['state']);
self::assertSame('small', $serialized['spec']['runtimeSize']);
self::assertSame('1', $serialized['spec']['configVersion']);
self::assertSame('dev', $serialized['spec']['mode']);
}
}
20 changes: 20 additions & 0 deletions libs/k8s-client/tests/Model/V2/AppModelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Keboola\K8sClient\Tests\Model\V2;

use Keboola\K8sClient\Model\Io\Keboola\Apps\V2\App;
use Keboola\K8sClient\Model\Io\Keboola\Apps\V2\AppDevSpec;
use Keboola\K8sClient\Model\Io\Keboola\Apps\V2\AppFeatures;
use Keboola\K8sClient\Model\Io\Keboola\Apps\V2\AppRuntime;
use Keboola\K8sClient\Model\Io\Keboola\Apps\V2\AppSpec;
Expand Down Expand Up @@ -63,6 +64,11 @@ private static function getAppTestData(): array
'type' => 'e2bSandbox',
],
],
'mode' => 'dev',
'dev' => [
'gitPollInterval' => '5s',
'autoRunSetupOnDepChange' => false,
],
'features' => [
'storageToken' => [
'description' => '[_internal][app] App 12345',
Expand Down Expand Up @@ -251,6 +257,13 @@ public function testStreamlitAppModelHydration(): void
self::assertInstanceOf(Backend::class, $app->spec->runtime->backend);
self::assertSame('e2bSandbox', $app->spec->runtime->backend->type);

// Mode + Dev
self::assertSame('dev', $app->spec->mode);
self::assertNotNull($app->spec->dev);
self::assertInstanceOf(AppDevSpec::class, $app->spec->dev);
self::assertSame('5s', $app->spec->dev->gitPollInterval);
self::assertFalse($app->spec->dev->autoRunSetupOnDepChange);

// Features
self::assertNotNull($app->spec->features);
self::assertInstanceOf(AppFeatures::class, $app->spec->features);
Expand Down Expand Up @@ -475,6 +488,13 @@ public function testAppModelSerialization(): void
self::assertSame('e2bSandbox', $serialized['spec']['runtime']['backend']['type']);
self::assertSame('small', $serialized['spec']['runtimeSize']);

// Verify mode + dev survive round-trip
self::assertArrayHasKey('mode', $serialized['spec']);
self::assertSame('dev', $serialized['spec']['mode']);
self::assertArrayHasKey('dev', $serialized['spec']);
self::assertSame('5s', $serialized['spec']['dev']['gitPollInterval']);
self::assertFalse($serialized['spec']['dev']['autoRunSetupOnDepChange']);

// Verify containerSpec is present and podSpec is not
self::assertArrayHasKey('containerSpec', $serialized['spec']);
self::assertArrayNotHasKey('podSpec', $serialized['spec']);
Expand Down