Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OCI entrypoint configuration #1845

Merged
merged 5 commits into from
Mar 27, 2025
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
18 changes: 18 additions & 0 deletions cmd/incusd/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"sync"
"time"

"github.com/kballard/go-shellquote"
ociSpecs "github.com/opencontainers/runtime-spec/specs-go"

internalInstance "github.com/lxc/incus/v6/internal/instance"
Expand Down Expand Up @@ -245,6 +246,23 @@ func instanceCreateFromImage(ctx context.Context, s *state.State, r *http.Reques
}
}

// Set the entrypoint configuration options.
if len(config.Process.Args) > 0 && args.Config["oci.entrypoint"] == "" {
args.Config["oci.entrypoint"] = shellquote.Join(config.Process.Args...)
}

if config.Process.Cwd != "" && args.Config["oci.cwd"] == "" {
args.Config["oci.cwd"] = config.Process.Cwd
}

if args.Config["oci.uid"] == "" {
args.Config["oci.uid"] = fmt.Sprintf("%d", config.Process.User.UID)
}

if args.Config["oci.gid"] == "" {
args.Config["oci.gid"] = fmt.Sprintf("%d", config.Process.User.GID)
}

err = inst.Update(args, false)
if err != nil {
return err
Expand Down
11 changes: 11 additions & 0 deletions doc/api-extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2764,3 +2764,14 @@ Adds a new `usb` value for `io.bus` on `disk` devices.
## `storage_driver_linstor`

This adds a LINSTOR storage driver.

## `instance_oci_entrypoint`

This introduces a set of new configuration options on the container to configure the OCI entry point:

* `oci.entrypoint`
* `oci.cwd`
* `oci.uid`
* `oci.gid`

Those are initialized at creation time using the values from the OCI image.
34 changes: 34 additions & 0 deletions doc/config_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,40 @@ The specified version expression is used to set `libnvidia-container NVIDIA_REQU
```

<!-- config group instance-nvidia end -->
<!-- config group instance-oci start -->
```{config:option} oci.cwd instance-oci
:condition: "OCI container"
:liveupdate: "no"
:shortdesc: "OCI container working directory"
:type: "string"
Override the working directory of an OCI container.
```

```{config:option} oci.entrypoint instance-oci
:condition: "OCI container"
:liveupdate: "no"
:shortdesc: "OCI container entrypoint"
:type: "string"
Override the entrypoint of an OCI container.
```

```{config:option} oci.gid instance-oci
:condition: "OCI container"
:liveupdate: "no"
:shortdesc: "OCI container GID"
:type: "string"
Override the GID of the process run in an OCI container.
```

```{config:option} oci.uid instance-oci
:condition: "OCI container"
:liveupdate: "no"
:shortdesc: "OCI container UID"
:type: "string"
Override the UID of the process run in an OCI container.
```

<!-- config group instance-oci end -->
<!-- config group instance-raw start -->
```{config:option} raw.apparmor instance-raw
:liveupdate: "yes"
Expand Down
36 changes: 36 additions & 0 deletions internal/instance/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,42 @@ var InstanceConfigKeysContainer = map[string]func(value string) error{
// shortdesc: Required driver version
"nvidia.require.driver": validate.IsAny,

// gendoc:generate(entity=instance, group=oci, key=oci.entrypoint)
// Override the entrypoint of an OCI container.
// ---
// type: string
// liveupdate: no
// condition: OCI container
// shortdesc: OCI container entrypoint
"oci.entrypoint": validate.IsAny,

// gendoc:generate(entity=instance, group=oci, key=oci.cwd)
// Override the working directory of an OCI container.
// ---
// type: string
// liveupdate: no
// condition: OCI container
// shortdesc: OCI container working directory
"oci.cwd": validate.Optional(validate.IsAbsFilePath),

// gendoc:generate(entity=instance, group=oci, key=oci.gid)
// Override the GID of the process run in an OCI container.
// ---
// type: string
// liveupdate: no
// condition: OCI container
// shortdesc: OCI container GID
"oci.gid": validate.Optional(validate.IsUint32),

// gendoc:generate(entity=instance, group=oci, key=oci.uid)
// Override the UID of the process run in an OCI container.
// ---
// type: string
// liveupdate: no
// condition: OCI container
// shortdesc: OCI container UID
"oci.uid": validate.Optional(validate.IsUint32),

// Caller is responsible for full validation of any raw.* value.

// gendoc:generate(entity=instance, group=raw, key=raw.lxc)
Expand Down
56 changes: 44 additions & 12 deletions internal/server/instance/drivers/driver_lxc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2341,33 +2341,65 @@ func (d *lxc) startCommon() (string, []func() error, error) {
}

// Configure the entry point.
if len(config.Process.Args) > 0 && slices.Contains([]string{"/init", "/sbin/init", "/s6-init"}, config.Process.Args[0]) {
entrypoint := config.Process.Args
if d.expandedConfig["oci.entrypoint"] != "" {
entrypoint, err = shellquote.Split(d.expandedConfig["oci.entrypoint"])
if err != nil {
return "", nil, err
}
}

if len(entrypoint) > 0 && slices.Contains([]string{"/init", "/sbin/init", "/s6-init"}, entrypoint[0]) {
// For regular init systems, call them directly as PID1.
err = lxcSetConfigItem(cc, "lxc.init.cmd", shellquote.Join(config.Process.Args...))
err = lxcSetConfigItem(cc, "lxc.init.cmd", shellquote.Join(entrypoint...))
if err != nil {
return "", nil, err
}
} else {
// For anything else, run them under our own PID1.
err = lxcSetConfigItem(cc, "lxc.execute.cmd", shellquote.Join(config.Process.Args...))
err = lxcSetConfigItem(cc, "lxc.execute.cmd", shellquote.Join(entrypoint...))
if err != nil {
return "", nil, err
}
}

err = lxcSetConfigItem(cc, "lxc.init.cwd", config.Process.Cwd)
if err != nil {
return "", nil, err
// Configure the cwd.
if d.expandedConfig["oci.cwd"] != "" {
err = lxcSetConfigItem(cc, "lxc.init.cwd", d.expandedConfig["oci.cwd"])
if err != nil {
return "", nil, err
}
} else {
err = lxcSetConfigItem(cc, "lxc.init.cwd", config.Process.Cwd)
if err != nil {
return "", nil, err
}
}

err = lxcSetConfigItem(cc, "lxc.init.uid", fmt.Sprintf("%d", config.Process.User.UID))
if err != nil {
return "", nil, err
// Configure the UID
if d.expandedConfig["oci.uid"] != "" {
err = lxcSetConfigItem(cc, "lxc.init.uid", d.expandedConfig["oci.uid"])
if err != nil {
return "", nil, err
}
} else {
err = lxcSetConfigItem(cc, "lxc.init.uid", fmt.Sprintf("%d", config.Process.User.UID))
if err != nil {
return "", nil, err
}
}

err = lxcSetConfigItem(cc, "lxc.init.gid", fmt.Sprintf("%d", config.Process.User.GID))
if err != nil {
return "", nil, err
// Configure the GID
if d.expandedConfig["oci.gid"] != "" {
err = lxcSetConfigItem(cc, "lxc.init.gid", d.expandedConfig["oci.gid"])
if err != nil {
return "", nil, err
}
} else {
err = lxcSetConfigItem(cc, "lxc.init.gid", fmt.Sprintf("%d", config.Process.User.GID))
if err != nil {
return "", nil, err
}
}

// Get all mounts so far.
Expand Down
40 changes: 40 additions & 0 deletions internal/server/metadata/configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,46 @@
}
]
},
"oci": {
"keys": [
{
"oci.cwd": {
"condition": "OCI container",
"liveupdate": "no",
"longdesc": "Override the working directory of an OCI container.",
"shortdesc": "OCI container working directory",
"type": "string"
}
},
{
"oci.entrypoint": {
"condition": "OCI container",
"liveupdate": "no",
"longdesc": "Override the entrypoint of an OCI container.",
"shortdesc": "OCI container entrypoint",
"type": "string"
}
},
{
"oci.gid": {
"condition": "OCI container",
"liveupdate": "no",
"longdesc": "Override the GID of the process run in an OCI container.",
"shortdesc": "OCI container GID",
"type": "string"
}
},
{
"oci.uid": {
"condition": "OCI container",
"liveupdate": "no",
"longdesc": "Override the UID of the process run in an OCI container.",
"shortdesc": "OCI container UID",
"type": "string"
}
}
]
},
"raw": {
"keys": [
{
Expand Down
1 change: 1 addition & 0 deletions internal/version/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,7 @@ var APIExtensions = []string{
"network_io_bus",
"disk_io_bus_usb",
"storage_driver_linstor",
"instance_oci_entrypoint",
}

// APIExtensionsCount returns the number of available API extensions.
Expand Down