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
3 changes: 3 additions & 0 deletions CubeAPI/src/cubemaster/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1840,6 +1840,9 @@ pub struct CreateTemplateFromImageReq {
skip_serializing_if = "Option::is_none"
)]
pub cube_network_config: Option<CreateTemplateCubeNetworkConfig>,
/// Whether the template build sandbox should include ivshmem.
#[serde(skip_serializing_if = "Option::is_none")]
pub enable_ivshmem: Option<bool>,
/// Whether CubeMaster bakes the CubeEgress root CA into the template rootfs.
#[serde(skip_serializing_if = "Option::is_none")]
pub with_cube_ca: Option<bool>,
Expand Down
3 changes: 3 additions & 0 deletions CubeAPI/src/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,9 @@ pub struct CreateTemplateRequest {
/// Denied outbound CIDRs for CubeVS egress policy.
#[serde(rename = "denyOut", default)]
pub deny_out: Option<Vec<String>>,
/// Enable ivshmem when building the template snapshot.
#[serde(rename = "enableIvshmem", default)]
pub enable_ivshmem: Option<bool>,
/// Whether CubeMaster bakes the CubeEgress root CA into the template rootfs.
/// Omitted or null defaults to true on CubeMaster.
#[serde(rename = "with_cube_ca", default)]
Expand Down
2 changes: 2 additions & 0 deletions CubeAPI/src/services/templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ impl TemplateService {
distribution_scope: non_empty_vec(body.nodes),
container_overrides,
cube_network_config,
enable_ivshmem: body.enable_ivshmem,
with_cube_ca: body.with_cube_ca,
};

Expand Down Expand Up @@ -533,6 +534,7 @@ mod tests {
dns: Some(vec!["8.8.8.8".to_string(), "1.1.1.1".to_string()]),
allow_out: Some(vec!["172.67.0.0/16".to_string()]),
deny_out: Some(vec!["10.0.0.0/8".to_string()]),
enable_ivshmem: Some(true),
with_cube_ca: Some(false),
}
}
Expand Down
10 changes: 10 additions & 0 deletions CubeMaster/cmd/cubemastercli/commands/cubebox/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,14 @@ func mergeCreateFromImageCubeNetworkConfigFlags(c *cli.Context, existing *types.
return mergeCubeNetworkConfigValues(existing, hasAllowInternetAccess, allowInternetAccess, allowOut, denyOut), nil
}

func applyCreateFromImageIvshmemFlag(c *cli.Context, req *types.CreateTemplateFromImageReq) {
if !c.IsSet("enable-ivshmem") {
return
}
enableIvshmem := c.Bool("enable-ivshmem")
req.EnableIvshmem = &enableIvshmem
}

func parseCreateFromImageExtraNetworkFlags(c *cli.Context) (*createFromImageExtraNetworkFlags, error) {
extraArgs := make([]string, 0, c.NArg())
for i := 0; i < c.NArg(); i++ {
Expand Down Expand Up @@ -763,6 +771,7 @@ var TemplateCreateFromImageCommand = cli.Command{
cli.StringSliceFlag{Name: "deny-out-cidr", Usage: "append a denied egress CIDR to cube_network_config; repeat the flag to specify multiple CIDRs"},
cli.StringFlag{Name: "registry-username", Usage: "registry username"},
cli.StringFlag{Name: "registry-password", Usage: "registry password"},
cli.BoolFlag{Name: "enable-ivshmem", Usage: "boot the template build sandbox with ivshmem enabled"},

cli.StringSliceFlag{Name: "cmd", Usage: "override container ENTRYPOINT (command); repeat for multiple elements, e.g. --cmd /bin/sh --cmd -c"},
cli.StringSliceFlag{Name: "arg", Usage: "override container CMD (args); repeat for multiple elements"},
Expand Down Expand Up @@ -816,6 +825,7 @@ var TemplateCreateFromImageCommand = cli.Command{
// SDKs) can still rely on `nil = server-side default`.
withCubeCA := c.BoolT("with-cube-ca")
req.WithCubeCA = &withCubeCA
applyCreateFromImageIvshmemFlag(c, req)
req.CubeNetworkConfig, err = mergeCreateFromImageCubeNetworkConfigFlags(c, req.CubeNetworkConfig)
if err != nil {
return err
Expand Down
14 changes: 14 additions & 0 deletions CubeMaster/cmd/cubemastercli/commands/cubebox/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,20 @@ func TestCreateFromImageCommandParsesNodeScope(t *testing.T) {
}
}

func TestApplyCreateFromImageIvshmemFlag(t *testing.T) {
withoutFlag := &types.CreateTemplateFromImageReq{}
applyCreateFromImageIvshmemFlag(newCreateFromImageContext(t, nil), withoutFlag)
if withoutFlag.EnableIvshmem != nil {
t.Fatalf("EnableIvshmem=%v, want nil when flag is not set", *withoutFlag.EnableIvshmem)
}

withFlag := &types.CreateTemplateFromImageReq{}
applyCreateFromImageIvshmemFlag(newCreateFromImageContext(t, []string{"--enable-ivshmem"}), withFlag)
if withFlag.EnableIvshmem == nil || !*withFlag.EnableIvshmem {
t.Fatalf("EnableIvshmem=%v, want true", withFlag.EnableIvshmem)
}
}

func TestMergeCreateFromImageCubeNetworkConfigFlagsRejectsUnexpectedArgs(t *testing.T) {
ctx := newCreateFromImageContext(t, []string{
"--allow-internet-access", "false",
Expand Down
1 change: 1 addition & 0 deletions CubeMaster/pkg/base/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ const (
// CubeAnnotationCreateTimeEnvVars stores the serialized create-time env map
// that CubeMaster passes to cubelet for envd initialization.
CubeAnnotationCreateTimeEnvVars = "cube.master.internal.create_time_env_vars"
CubeAnnotationEnableIvshmem = "cube.master.enable_ivshmem"

CubeAnnotationsVirtiofsCache = "cube.master.virtiofs.cache"

Expand Down
5 changes: 5 additions & 0 deletions CubeMaster/pkg/service/sandbox/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,11 @@ type CreateTemplateFromImageReq struct {
// false → skip the bake entirely
// See design/cube-egress-ca-bake.md.
WithCubeCA *bool `json:"with_cube_ca,omitempty"`

// EnableIvshmem controls whether the template build sandbox should boot
// with ivshmem enabled so the captured snapshot already contains the
// device topology.
EnableIvshmem *bool `json:"enable_ivshmem,omitempty"`
}

type RedoTemplateFromImageReq struct {
Expand Down
3 changes: 3 additions & 0 deletions CubeMaster/pkg/templatecenter/request_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ func normalizeTemplateImageRequest(req *types.CreateTemplateFromImageReq) (*type
if cloned.NetworkType == "" {
cloned.NetworkType = cubeboxv1.NetworkType_tap.String()
}
if cloned.EnableIvshmem != nil && !*cloned.EnableIvshmem {
cloned.EnableIvshmem = nil
}
if err := validateTemplateCubeNetworkConfig(cloned.CubeNetworkConfig); err != nil {
return nil, err
}
Expand Down
67 changes: 67 additions & 0 deletions CubeMaster/pkg/templatecenter/template_image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,22 @@ func TestNormalizeTemplateImageRequestIgnoresProvidedTemplateID(t *testing.T) {
}
}

func TestNormalizeTemplateImageRequestDropsDisabledIvshmemFlag(t *testing.T) {
disabled := false
req, err := normalizeTemplateImageRequest(&types.CreateTemplateFromImageReq{
Request: &types.Request{RequestID: "req-1"},
SourceImageRef: "docker.io/library/nginx:latest",
WritableLayerSize: "20Gi",
EnableIvshmem: &disabled,
})
if err != nil {
t.Fatalf("normalizeTemplateImageRequest failed: %v", err)
}
if req.EnableIvshmem != nil {
t.Fatal("EnableIvshmem should be canonicalized to nil when false")
}
}

func TestNormalizeTemplateImageRequestNormalizesExposedPorts(t *testing.T) {

req, err := normalizeTemplateImageRequest(&types.CreateTemplateFromImageReq{
Expand Down Expand Up @@ -663,6 +679,57 @@ func TestGenerateTemplateCreateRequestClonesCubeNetworkRules(t *testing.T) {
}
}

func TestGenerateTemplateCreateRequestAddsIvshmemAnnotation(t *testing.T) {
enabled := true
req := &types.CreateTemplateFromImageReq{
Request: &types.Request{RequestID: "req-1"},
SourceImageRef: "docker.io/library/nginx:latest",
TemplateID: "template-1",
WritableLayerSize: "20Gi",
InstanceType: cubeboxv1.InstanceType_cubebox.String(),
NetworkType: cubeboxv1.NetworkType_tap.String(),
EnableIvshmem: &enabled,
}
artifact := &models.RootfsArtifact{
ArtifactID: "artifact-1",
TemplateSpecFingerprint: "fingerprint-1",
Ext4SHA256: "sha256-1",
Ext4SizeBytes: 1024,
DownloadToken: "token-1",
}
got, err := generateTemplateCreateRequest(req, artifact, image.DockerImageConfig{}, "http://master.example")
if err != nil {
t.Fatalf("generateTemplateCreateRequest failed: %v", err)
}
if got.Annotations[constants.CubeAnnotationEnableIvshmem] != "true" {
t.Fatalf("expected ivshmem annotation to be set, got %q", got.Annotations[constants.CubeAnnotationEnableIvshmem])
}
}

func TestBuildTemplateSpecFingerprintIgnoresIvshmemFlag(t *testing.T) {
enabled := true
reqA := &types.CreateTemplateFromImageReq{
Request: &types.Request{RequestID: "req-a"},
SourceImageRef: "docker.io/library/nginx:latest",
WritableLayerSize: "20Gi",
InstanceType: cubeboxv1.InstanceType_cubebox.String(),
NetworkType: cubeboxv1.NetworkType_tap.String(),
}
reqB := &types.CreateTemplateFromImageReq{
Request: &types.Request{RequestID: "req-b"},
SourceImageRef: reqA.SourceImageRef,
WritableLayerSize: reqA.WritableLayerSize,
InstanceType: reqA.InstanceType,
NetworkType: reqA.NetworkType,
EnableIvshmem: &enabled,
}
gotA := buildTemplateSpecFingerprint(reqA, "sha256:source")
gotB := buildTemplateSpecFingerprint(reqB, "sha256:source")
if gotA != gotB {
t.Fatalf("ivshmem should not affect rootfs artifact fingerprint: %q vs %q", gotA, gotB)
}
}

func TestMarshalTemplateImageJobRequestIgnoresRequestIDAndPassword(t *testing.T) {
reqA := &types.CreateTemplateFromImageReq{
Request: &types.Request{RequestID: "req-a"},
Expand Down
3 changes: 3 additions & 0 deletions CubeMaster/pkg/templatecenter/template_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ func generateTemplateCreateRequest(req *types.CreateTemplateFromImageReq, artifa
if len(req.ExposedPorts) > 0 {
annotations[constants.AnnotationsExposedPort] = formatExposedPortsAnnotation(req.ExposedPorts)
}
if req.EnableIvshmem != nil && *req.EnableIvshmem {
annotations[constants.CubeAnnotationEnableIvshmem] = "true"
}
rootVolume := &types.Volume{
Name: rootfsWritableVolumeName,
VolumeSource: &types.VolumeSource{
Expand Down
Loading
Loading