docker: inject config files into containers via extra_configs

Add an `extra_configs` field (list of {target, content}) to the docker
node/template/appliance schemas. For each entry GNS3 writes `content` to a
file in the node working directory and bind-mounts it read-only at `target`
inside the container.

This lets a NOS appliance seed its startup config without rebuilding the
image: XRd points XR_FIRST_BOOT_CONFIG at an injected /firstboot.cfg, FRR at
/etc/frr/frr.conf, etc. The bind is a single-file mount applied at create
time, so it works for both the generic init.sh path and vendor nodes that
skip init.sh (console_type=docker_exec). Entries are only injected when
present, so ordinary nodes are unaffected.

The content can't go through `environment` (it is line-delimited, one var per
line), hence a dedicated field -- the same plumbing shape as extra_volumes.
This commit is contained in:
YueGuobin committed 2026-08-13 23:33:34 +08:00
1 parent 2bee34031c
commit 08e37a4509
7 files changed
+98 -3

No files matched your search

@@ -20,6 +20,7 @@ from enum import Enum
from typing import Annotated, List, Literal, Optional, Union
from uuid import UUID
from pydantic import AnyUrl, BaseModel, Discriminator, EmailStr, Field, Tag
from ..common import ExtraConfig
# ============================================================================
@@ -329,6 +330,7 @@ class Docker(BaseModel):
console_http_path: Optional[str] = Field(None, description='Path of the web interface')
extra_hosts: Optional[str] = Field(None, description='Hosts which will be written to /etc/hosts into container')
extra_volumes: Optional[List[str]] = Field(None, description='Additional directories to make persistent that are not included in the images VOLUME directive')
extra_configs: Optional[List[ExtraConfig]] = Field(None, description='Configuration files injected into the container (bind-mounted read-only)')
class Iou(BaseModel):
@@ -479,6 +481,9 @@ class DockerPropertiesV8(BaseModel):
extra_volumes: Optional[List[str]] = Field(
None, title='Additional directories to make persistent'
)
extra_configs: Optional[List[ExtraConfig]] = Field(
None, title='Configuration files injected into the container (bind-mounted read-only)'
)
class IouPropertiesV8(BaseModel):
@@ -16,7 +16,7 @@
from . import Category, TemplateBase
from ...common import ConsoleType, AuxType, CustomAdapter
from ...common import ConsoleType, AuxType, CustomAdapter, ExtraConfig
from pydantic import Field
from typing import Optional, List
@@ -49,6 +49,7 @@ class DockerTemplate(TemplateBase):
)
extra_hosts: Optional[str] = Field("", description="Docker extra hosts (added to /etc/hosts)")
extra_volumes: Optional[List] = Field([], description="Additional directories to make persistent")
extra_configs: Optional[List[ExtraConfig]] = Field(default_factory=list, description="Configuration files injected into the container (bind-mounted read-only)")
memory: Optional[int] = Field(0, ge=0, description="Maximum amount of memory the container can use in MB")
cpus: Optional[float] = Field(0, ge=0, description="Maximum amount of CPU resources the container can use")
custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters")