blob: eabcfd315de063d95c5a2eea0114344b551092fe [file] [log] [blame]
Lorenz Brunc607bf62025-07-22 20:25:26 +02001// Copyright The Monogon Project Authors.
2// SPDX-License-Identifier: Apache-2.0
3
4// Package cni implements an adapter between the go-cni interface and
5// the Monogon gRPC Workload Attachment interface. As we do not intend to
6// actually implement a CNI-compliant plugin it makes more sense to just cut
7// out as much unnecessary logic and take over at the containerd API boundary.
8package cni
9
10import (
11 "context"
12 "fmt"
13 "net"
14
15 "google.golang.org/grpc"
16 "google.golang.org/grpc/credentials/insecure"
17
18 wlapi "source.monogon.dev/metropolis/node/core/network/workloads/spec"
19)
20
21func New(_ ...Opt) (CNI, error) {
22 conn, err := grpc.NewClient("unix:/ephemeral/workloadnet.sock", grpc.WithTransportCredentials(insecure.NewCredentials()))
23 if err != nil {
24 panic(err)
25 }
26 wlClient := wlapi.NewWorkloadNetworkingClient(conn)
27 return &adapter{
28 client: wlClient,
29 }, nil
30}
31
32type NamespaceOpts func(n *Namespace) error
33
34// Namespace differs significantly from upstream as we do not have the actual
35// underlying CNI interface and thus we do not need to transform the data into
36// JSON keys.
37type Namespace struct {
38 labels map[string]string
39 annotations map[string]string
40 portMapping []PortMapping
41 bandwidth BandWidth
42 dns DNS
43 cgroupPath string
44}
45
46func WithLabels(labels map[string]string) NamespaceOpts {
47 return func(n *Namespace) error {
48 n.labels = labels
49 return nil
50 }
51}
52
53func WithCapability(name string, capability interface{}) NamespaceOpts {
54 return func(n *Namespace) error {
55 if name == "io.kubernetes.cri.pod-annotations" {
56 n.annotations = capability.(map[string]string)
57 }
58 return nil
59 }
60}
61
62func WithCapabilityPortMap(portMapping []PortMapping) NamespaceOpts {
63 return func(c *Namespace) error {
64 c.portMapping = portMapping
65 return nil
66 }
67}
68
69func WithCapabilityBandWidth(bandWidth BandWidth) NamespaceOpts {
70 return func(c *Namespace) error {
71 c.bandwidth = bandWidth
72 return nil
73 }
74}
75
76func WithCapabilityDNS(dns DNS) NamespaceOpts {
77 return func(c *Namespace) error {
78 c.dns = dns
79 return nil
80 }
81}
82
83func WithCapabilityCgroupPath(cgroupPath string) NamespaceOpts {
84 return func(c *Namespace) error {
85 c.cgroupPath = cgroupPath
86 return nil
87 }
88}
89
90type adapter struct {
91 client wlapi.WorkloadNetworkingClient
92}
93
94func (s *adapter) Setup(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*Result, error) {
95 var n Namespace
96 for _, opt := range opts {
97 opt(&n)
98 }
99 res, err := s.client.Attach(ctx, &wlapi.AttachRequest{
100 WorkloadId: n.labels["K8S_POD_UID"],
101 Netns: &wlapi.NetNSAttachment{
102 NetnsPath: path,
103 IfName: "eth0",
104 },
105 })
106 if err != nil {
107 return nil, fmt.Errorf("while requesting workload network attachment: %w", err)
108 }
109 // Provide IP to containerd/CRI, rest is ignored anyways.
110 var ipConfigs []*IPConfig
111 for _, ip := range res.Ip {
112 ipConfigs = append(ipConfigs, &IPConfig{IP: net.IP(ip)})
113 }
114 return &Result{
115 Interfaces: map[string]*Config{
116 "eth0": {
117 IPConfigs: ipConfigs,
118 },
119 },
120 }, nil
121}
122
123func (s *adapter) SetupSerially(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*Result, error) {
124 // We do not support multiple plugins, the distinction between serial or
125 // parallel does not exist. Just forward the call.
126 return s.Setup(ctx, id, path, opts...)
127}
128
129func (s *adapter) Remove(ctx context.Context, id string, path string, opts ...NamespaceOpts) error {
130 var n Namespace
131 for _, opt := range opts {
132 opt(&n)
133 }
134
135 _, err := s.client.Detach(ctx, &wlapi.DetachRequest{
136 WorkloadId: n.labels["K8S_POD_UID"],
137 Netns: &wlapi.NetNSAttachment{
138 NetnsPath: path,
139 IfName: "eth0",
140 },
141 })
142 return err
143}
144
145func (s *adapter) Check(ctx context.Context, id string, path string, opts ...NamespaceOpts) error {
146 return nil
147}
148
149func (s *adapter) Load(opts ...Opt) error {
150 // Stub, we do not actually have any CNI config.
151 return nil
152}
153
154func (s *adapter) Status() error {
155 _, err := s.client.Status(context.Background(), &wlapi.StatusRequest{})
156 return err
157}
158
159func (s *adapter) GetConfig() *ConfigResult {
160 return &ConfigResult{}
161}