Skip to content
33 changes: 33 additions & 0 deletions cmd/workload-manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"fmt"
"os"
"os/signal"
"strings"
"syscall"
"time"

Expand Down Expand Up @@ -58,6 +59,13 @@ func main() {
tlsCert = flag.String("tls-cert", "", "Path to TLS certificate file")
tlsKey = flag.String("tls-key", "", "Path to TLS key file")
enableAuth = flag.Bool("enable-auth", false, "Enable Authentication")
routerSelector = flag.String("router-selector", "app=agentcube-router",
"Pod label selector identifying the router pod, used for the mandatory "+
"router→sandbox ingress rule when NetworkPolicy.Mode=Restricted. "+
"Format: comma-separated key=value pairs (e.g. app=agentcube-router,tier=proxy)")
routerNamespace = flag.String("router-namespace", "",
"Namespace the router runs in. Used to build a cross-namespace NamespaceSelector "+
"for the router→sandbox ingress rule. Defaults to the workloadmanager's own namespace.")
)

// Initialize klog flags
Expand Down Expand Up @@ -95,6 +103,11 @@ func main() {
os.Exit(1)
}

sel := parseSelector(*routerSelector)
if len(sel) == 0 {
klog.Warningf("--router-selector %q parsed to an empty selector; falling back to default %v", *routerSelector, workloadmanager.DefaultRouterSelector)
}

// Create API server configuration
config := &workloadmanager.Config{
Port: *port,
Expand All @@ -103,6 +116,8 @@ func main() {
TLSCert: *tlsCert,
TLSKey: *tlsKey,
EnableAuth: *enableAuth,
RouterSelector: sel,
RouterNamespace: *routerNamespace,
}

// Create and initialize API server
Expand Down Expand Up @@ -166,6 +181,24 @@ func main() {
klog.Info("Server stopped")
}

// parseSelector parses a comma-separated "key=value" label selector string into a map.
// Malformed pairs are silently skipped; callers should validate the resulting map is non-empty.
func parseSelector(s string) map[string]string {
m := make(map[string]string)
for _, pair := range strings.Split(s, ",") {
pair = strings.TrimSpace(pair)
if pair == "" {
continue
}
k, v, ok := strings.Cut(pair, "=")
if !ok || strings.TrimSpace(k) == "" {
continue
}
m[strings.TrimSpace(k)] = strings.TrimSpace(v)
}
return m
Comment thread
Sanchit2662 marked this conversation as resolved.
}

func setupControllers(mgr ctrl.Manager, sandboxReconciler *workloadmanager.SandboxReconciler, codeInterpreterReconciler *workloadmanager.CodeInterpreterReconciler) error {
// Setup Sandbox controller
if err := ctrl.NewControllerManagedBy(mgr).
Expand Down
78 changes: 78 additions & 0 deletions cmd/workload-manager/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
Copyright The Volcano Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestParseSelector(t *testing.T) {
tests := []struct {
name string
in string
want map[string]string
}{
{
name: "empty string",
in: "",
want: map[string]string{},
},
{
name: "single pair",
in: "app=agentcube-router",
want: map[string]string{"app": "agentcube-router"},
},
{
name: "multiple pairs",
in: "app=agentcube-router,tier=proxy",
want: map[string]string{"app": "agentcube-router", "tier": "proxy"},
},
{
name: "whitespace around pairs and keys is trimmed",
in: " app = agentcube-router , tier = proxy ",
want: map[string]string{"app": "agentcube-router", "tier": "proxy"},
},
{
name: "malformed pairs (no '=') are dropped",
in: "app=agentcube-router,bogus,tier=proxy",
want: map[string]string{"app": "agentcube-router", "tier": "proxy"},
},
{
name: "empty key is dropped",
in: "=value,app=agentcube-router",
want: map[string]string{"app": "agentcube-router"},
},
{
name: "empty value is preserved (k8s allows it)",
in: "role=",
want: map[string]string{"role": ""},
},
{
name: "trailing comma ignored",
in: "app=agentcube-router,",
want: map[string]string{"app": "agentcube-router"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := parseSelector(tt.in)
assert.Equal(t, tt.want, got)
})
}
}
67 changes: 67 additions & 0 deletions example/code-interpreter/code-interpreter-network-policy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Example: CodeInterpreter with NetworkPolicy enabled
#
# Set networkPolicy to a non-nil value to enforce isolation. A deny-all baseline
# is created with automatic allow rules for router ingress and DNS egress.
# Leave networkPolicy unset (nil) to disable enforcement entirely.
#
# Two examples are shown:
# 1. Default enforcement — router ingress and DNS egress only
# 2. Extra egress — allow outbound to a specific CIDR/port

---
# 1. Default enforcement — simplest secure setup
apiVersion: runtime.agentcube.volcano.sh/v1alpha1
kind: CodeInterpreter
metadata:
name: interpreter-restricted
namespace: default
spec:
ports:
- pathPrefix: "/"
port: 8080
protocol: "HTTP"
template:
image: ghcr.io/volcano-sh/picod:latest
imagePullPolicy: IfNotPresent
resources:
limits:
cpu: "500m"
memory: "512Mi"
requests:
cpu: "100m"
memory: "128Mi"
networkPolicy: {}
sessionTimeout: "15m"
maxSessionDuration: "8h"

---
# 2. Extra egress — allow outbound to 10.0.0.0/8 on port 443
apiVersion: runtime.agentcube.volcano.sh/v1alpha1
kind: CodeInterpreter
metadata:
name: interpreter-extra-egress
namespace: default
spec:
ports:
- pathPrefix: "/"
port: 8080
protocol: "HTTP"
template:
image: ghcr.io/volcano-sh/picod:latest
imagePullPolicy: IfNotPresent
resources:
limits:
cpu: "500m"
memory: "512Mi"
requests:
cpu: "100m"
memory: "128Mi"
networkPolicy:
egress:
- cidrs:
- "10.0.0.0/8"
ports:
- protocol: TCP
port: 443
Comment on lines +60 to +65
sessionTimeout: "15m"
maxSessionDuration: "8h"
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,93 @@ spec:
type: string
description: Labels to apply to the sandbox Pod.
type: object
networkPolicy:
description: |-
NetworkPolicy defines the network isolation policy for sandbox pods created from
this template. Defaults to Mode=None (no policy created).
properties:
egress:
description: Egress lists additional egress allow rules beyond
the default DNS rule.
items:
description: SandboxNetworkPolicyRule describes a single
ingress or egress allow rule for a sandbox pod.
properties:
cidrs:
description: |-
CIDRs is the list of CIDR blocks for this rule (source for ingress, destination for egress).
Each entry must be valid CIDR notation (e.g. "10.0.0.0/8" or "2001:db8::/32").
items:
pattern: ^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$|^[0-9a-fA-F:]+\/\d{1,3}$
type: string
type: array
x-kubernetes-list-type: atomic
ports:
description: Ports restricts the rule to these ports.
An empty list matches all ports.
items:
description: SandboxNetworkPolicyPort describes a
port for a sandbox network policy rule.
properties:
port:
anyOf:
- type: integer
- type: string
description: Port is the destination port number
or named port.
x-kubernetes-int-or-string: true
protocol:
description: Protocol is TCP or UDP. Defaults
to TCP.
type: string
required:
- port
type: object
type: array
type: object
type: array
ingress:
description: |-
Ingress lists additional ingress allow rules.
Router ingress is always permitted regardless of this field.
items:
description: SandboxNetworkPolicyRule describes a single
ingress or egress allow rule for a sandbox pod.
properties:
cidrs:
description: |-
CIDRs is the list of CIDR blocks for this rule (source for ingress, destination for egress).
Each entry must be valid CIDR notation (e.g. "10.0.0.0/8" or "2001:db8::/32").
items:
pattern: ^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$|^[0-9a-fA-F:]+\/\d{1,3}$
type: string
type: array
x-kubernetes-list-type: atomic
ports:
description: Ports restricts the rule to these ports.
An empty list matches all ports.
items:
description: SandboxNetworkPolicyPort describes a
port for a sandbox network policy rule.
properties:
port:
anyOf:
- type: integer
- type: string
description: Port is the destination port number
or named port.
x-kubernetes-int-or-string: true
protocol:
description: Protocol is TCP or UDP. Defaults
to TCP.
type: string
required:
- port
type: object
type: array
type: object
type: array
type: object
spec:
description: Spec is the Pod's spec
properties:
Expand Down
Loading
Loading