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
2 changes: 1 addition & 1 deletion docs/runware_serverless_apps.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ runware serverless apps [flags]
* [runware serverless apps stop](runware_serverless_apps_stop.md) - Stop a serverless application
* [runware serverless apps tasks](runware_serverless_apps_tasks.md) - List and inspect application tasks
* [runware serverless apps usage](runware_serverless_apps_usage.md) - Show usage for a serverless application
* [runware serverless apps versions](runware_serverless_apps_versions.md) - Inspect application versions
* [runware serverless apps versions](runware_serverless_apps_versions.md) - Manage application versions
* [runware serverless apps workers](runware_serverless_apps_workers.md) - List workers for a serverless application

8 changes: 8 additions & 0 deletions docs/runware_serverless_apps_invoke.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ to poll until the task is completed or failed.
expires, the command polls the returned task id; it never treats expiry as
a failure and never resubmits.

A client-generated task id is sent with every invoke. Omit --task-id to
generate one. Resubmitting the same id returns the task it already names
instead of starting a second run.

```
runware serverless apps invoke <appId> <endpointPath> [flags]
```
Expand All @@ -32,6 +36,9 @@ runware serverless apps invoke <appId> <endpointPath> [flags]

# async invoke and poll
runware serverless apps invoke my-app infer --wait -f payload.json

# retry a lost response without starting a second task
runware serverless apps invoke my-app infer --task-id 7c9e6679-7425-40de-944b-e07fc1f90ae7 -f payload.json
```

### Options
Expand All @@ -41,6 +48,7 @@ runware serverless apps invoke <appId> <endpointPath> [flags]
-h, --help help for invoke
--poll-interval duration Polling interval when waiting for a task (default 2s)
--sync Use sync invocation and wait for a terminal task
--task-id string Client task id (UUID); generated if omitted
--wait Poll until the task is completed or failed
```

Expand Down
5 changes: 3 additions & 2 deletions docs/runware_serverless_apps_versions.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
## runware serverless apps versions

Inspect application versions
Manage application versions

### Synopsis

List and inspect immutable versions of a serverless application.
List, inspect, and activate immutable versions of a serverless application.

```
runware serverless apps versions [flags]
Expand All @@ -28,6 +28,7 @@ runware serverless apps versions [flags]
### SEE ALSO

* [runware serverless apps](runware_serverless_apps.md) - Manage deployed serverless applications
* [runware serverless apps versions activate](runware_serverless_apps_versions_activate.md) - Activate a ready application version
* [runware serverless apps versions list](runware_serverless_apps_versions_list.md) - List versions of a serverless application
* [runware serverless apps versions show](runware_serverless_apps_versions_show.md) - Show a version of a serverless application

50 changes: 50 additions & 0 deletions docs/runware_serverless_apps_versions_activate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
## runware serverless apps versions activate

Activate a ready application version

### Synopsis

Activate a ready version by number, including rollback to an older version.

The server accepts the deploy and returns immediately with the updated app.
Worker rollout is asynchronous; this command does not wait until workers are
healthy. Re-activating the currently active version is permitted and re-applies
it. On a stopped app the version is recorded and applied on resume.

A missing app is 404. A missing version, a version that is not ready, or an
app that is deleting is 409.

```
runware serverless apps versions activate <appId> <versionNumber> [flags]
```

### Examples

```
# list versions, then activate one
runware serverless apps versions list my-app
runware serverless apps versions activate my-app 2

# roll back to an older ready version
runware serverless apps versions activate my-app 1
```

### Options

```
-h, --help help for activate
```

### Options inherited from parent commands

```
--debug Show full debug output
-F, --format string CLI output format: table, json, yaml (default "table")
--transport string Transport protocol: ws (WebSocket) or http (REST) (default "ws")
-v, --verbose Show request/response details
```

### SEE ALSO

* [runware serverless apps versions](runware_serverless_apps_versions.md) - Manage application versions

2 changes: 1 addition & 1 deletion docs/runware_serverless_apps_versions_list.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,5 @@ runware serverless apps versions list <appId> [flags]

### SEE ALSO

* [runware serverless apps versions](runware_serverless_apps_versions.md) - Inspect application versions
* [runware serverless apps versions](runware_serverless_apps_versions.md) - Manage application versions

2 changes: 1 addition & 1 deletion docs/runware_serverless_apps_versions_show.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ runware serverless apps versions show <appId> <versionNumber> [flags]

### SEE ALSO

* [runware serverless apps versions](runware_serverless_apps_versions.md) - Inspect application versions
* [runware serverless apps versions](runware_serverless_apps_versions.md) - Manage application versions

35 changes: 26 additions & 9 deletions docs/runware_serverless_deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,27 @@ Deploy a new serverless application

### Synopsis

Create a new serverless application from a Python entry file.
Create a new serverless application from Python code or a container source.

The whole source directory is zipped and submitted as the application source, so
the entry file can import its own modules and read its own data files. That
directory is the working directory unless --src-dir says otherwise.
A code deploy takes a Python entry file. The whole source directory is zipped
and submitted as the application source, so the entry file can import its own
modules and read its own data files. That directory is the working directory
unless --src-dir says otherwise.

The entry file must live inside the source directory. A relative path is resolved
inside it; an absolute path is taken as given.

A container deploy takes --container pointing at a directory whose root contains
Dockerfile and container.yaml (plus any build-context files the Dockerfile
copies). The directory is zipped and uploaded as source type container. Runware
builds a hosted wrapper image from that archive; the version records a buildId,
not a customer image reference. Invalid container.yaml is rejected on create
(400 if it cannot be parsed, 422 if it breaks a rule). The app stays
initializing until that first build rolls out.

--container cannot be combined with an entry file, --src-dir, --base-image, or
--requirement.

Exclude what the app does not need with a .runwareignore file at the root of the
source directory; it takes gitignore syntax. A .gitignore is NOT consulted --
what a project keeps out of version control is a different question from what it
Expand All @@ -31,10 +43,11 @@ download is copied into every checkpoint and fetched again on every cold start.
A volume keeps it out of both.

Worker settings are supplied via flags (a local project config via 'runware
serverless init' is planned). Endpoints are derived server-side from the SDK.
serverless init' is planned). Endpoints are derived server-side from the SDK
(code) or from container.yaml (container).

```
runware serverless deploy <file> [flags]
runware serverless deploy [file] [flags]
```

### Examples
Expand All @@ -61,12 +74,16 @@ runware serverless deploy <file> [flags]
runware serverless deploy ./app.py --id my-app --name "My App" \
--max-workers 2 --idle-ttl 120 --gpu-type h100 \
--base-image python:3.11-slim --requirement torch

# deploy a container source (Dockerfile + container.yaml at the directory root)
runware serverless deploy --id my-app --gpu-type h100 --container ./wrapper
```

### Options

```
--base-image string Builder base image (default "python:3.11-slim")
--base-image string Builder base image (code deploys only) (default "python:3.11-slim")
--container string Directory whose root contains Dockerfile and container.yaml
--env stringArray Environment variable as KEY=VALUE (repeatable)
--env-file stringArray File of KEY=VALUE lines to read environment variables from (repeatable)
--gpu-type string GPU type ID (see 'serverless gpus')
Expand All @@ -77,9 +94,9 @@ runware serverless deploy <file> [flags]
--max-workers int32 Maximum number of workers (default 1)
--min-workers int32 Minimum number of workers
--name string Display name (defaults to --id)
--requirement stringArray Additional pip package to install (repeatable)
--requirement stringArray Additional pip package to install (repeatable; code deploys only)
--scaling-delay int32 Scaling delay in seconds (default 10)
--src-dir string Directory to package as the application source (default: the working directory)
--src-dir string Directory to package as the application source (default: the working directory; code deploys only)
--volume stringArray Absolute path inside the app backed by persistent node-local storage (repeatable)
```

Expand Down
62 changes: 57 additions & 5 deletions internal/api/serverless/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ const defaultTimeout = 30 * time.Second
const createAppTimeout = 5 * time.Minute

// invokeSyncTimeout bounds startSyncTask. It must exceed the platform wait
// window so a 504/202 with taskId is received; a client-side timeout would
// lose the id and force a resubmit (a second billable run).
// window so a 202 with the accepted task is received; a client-side timeout
// would lose the response and force a resubmit (a second billable run unless
// the same client task id is reused).
const invokeSyncTimeout = 5 * time.Minute

// GpuType is the public catalogue entry for a supported GPU type.
Expand All @@ -42,6 +43,9 @@ type App = gen.App
// AppCreate is the request body for createApp.
type AppCreate = gen.AppCreate

// AppSourceType selects the version creation path (`code` or `container`).
type AppSourceType = gen.AppSourceType

// AppSourceUpsert selects the initial version creation path.
type AppSourceUpsert = gen.AppSourceUpsert

Expand All @@ -51,6 +55,9 @@ type CodeSourceUpsert = gen.CodeSourceUpsert
// CodebaseSource is the zipped customer code payload.
type CodebaseSource = gen.CodebaseSource

// ContainerSource is a Dockerfile + container.yaml archive identified by sourceId.
type ContainerSource = gen.ContainerSource

// AppVolume is a persistent node-local directory mounted into the application.
type AppVolume = gen.AppVolume

Expand All @@ -66,6 +73,9 @@ type AppUpdate = gen.AppUpdate
// WorkerConfigPatch is a partial worker configuration for updateApp.
type WorkerConfigPatch = gen.WorkerConfigPatch

// DeployRequest is the request body for DeployVersion.
type DeployRequest = gen.DeployRequest

// ListAppsParams are optional filters for ListApps.
type ListAppsParams = gen.ListAppsParams

Expand Down Expand Up @@ -94,7 +104,8 @@ type Task = gen.Task
type TaskStatus = gen.TaskStatus

// TaskPayload is the JSON object forwarded to an endpoint handler.
type TaskPayload = gen.TaskPayload
// It is the TaskInvocation.payload member, not the request body itself.
type TaskPayload = map[string]interface{}

// ListTasksParams are optional filters for ListTasks.
type ListTasksParams = gen.ListTasksParams
Expand Down Expand Up @@ -135,8 +146,12 @@ type Page[T any] struct {
NextCursor *string `json:"nextCursor,omitempty"`
}

// AppSourceTypeCode is appSource.type = "code".
const AppSourceTypeCode = gen.Code
const (
// AppSourceTypeCode is appSource.type = "code".
AppSourceTypeCode = gen.Code
// AppSourceTypeContainer is appSource.type = "container".
AppSourceTypeContainer = gen.Container
)

func pageOf[T any](data *[]T, nextCursor *string) Page[T] {
if data == nil || *data == nil {
Expand Down Expand Up @@ -447,6 +462,31 @@ func (c *Client) DeleteApp(ctx context.Context, appID string) (*App, error) {
})
}

// DeployVersion activates a ready version by number. Worker rollout is
// asynchronous; the 202 App reflects the persisted intent (activeVersionId
// and status), not healthy workers. An older ready number is a rollback.
func (c *Client) DeployVersion(ctx context.Context, appID string, versionNumber int32) (*App, error) {
if c.apiKey == "" {
return nil, transport.ErrNoAPIKey
}

resp, err := c.inner.DeployVersionWithResponse(ctx, appID, DeployRequest{
VersionNumber: versionNumber,
})
if err != nil {
return nil, fmt.Errorf("deploy version: %w", err)
}

c.logResponse(ctx, resp.HTTPResponse, resp.Body)

return acceptedApp("deploy version", resp.StatusCode(), resp.JSON202, resp.Body, lifecycleProblems{
Unauthorized: resp.ApplicationproblemJSON401,
Forbidden: resp.ApplicationproblemJSON403,
NotFound: resp.ApplicationproblemJSON404,
Conflict: resp.ApplicationproblemJSON409,
})
}

// lifecycleProblems are typed RFC 9457 bodies bound by the generated client.
type lifecycleProblems struct {
Unauthorized *gen.ProblemDetails
Expand Down Expand Up @@ -669,3 +709,15 @@ func NewCodeAppSource(src CodeSourceUpsert) (AppSourceUpsert, error) {
Source: source,
}, nil
}

// NewContainerAppSource builds an appSource for a container-based create.
func NewContainerAppSource(src ContainerSource) (AppSourceUpsert, error) {
var source gen.AppSourceUpsert_Source
if err := source.FromContainerSource(src); err != nil {
return AppSourceUpsert{}, err
}
return AppSourceUpsert{
Type: AppSourceTypeContainer,
Source: source,
}, nil
}
Loading