Skip to content

Commit 1dccbde

Browse files
committed
Cleanup annotations: move to separate pkg
Signed-off-by: apostasie <spam_blackhole@farcloser.world>
1 parent fbead99 commit 1dccbde

File tree

11 files changed

+642
-459
lines changed

11 files changed

+642
-459
lines changed

pkg/annotations/annotations.go

Lines changed: 339 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,350 @@
1414
limitations under the License.
1515
*/
1616

17-
// Package annotations defines OCI annotations
17+
// Package annotations provides a high-level interface to retrieve and manipulate containers OCI annotations.
18+
// Any parsing, encoding or transformation of the struct into concrete containers annotations must be handled here,
19+
// and the containers underlying annotations map should never be accessed directly by consuming code, to ensure
20+
// we can evolve the storage format in a single place and consuming code to be fully isolated from the details of
21+
// how it is being handled.
22+
// Container annotations generally should be used to store state information or other container properties that
23+
// we may want to inspect at different stages of the container lifecycle.
1824
package annotations
1925

20-
const (
21-
// Prefix is the common prefix of nerdctl annotations
22-
Prefix = "nerdctl/"
26+
import (
27+
"context"
28+
"encoding/json"
29+
"fmt"
30+
"strconv"
31+
"strings"
2332

24-
// Bypass4netns is the flag for acceleration with bypass4netns
25-
// Boolean value which can be parsed with strconv.ParseBool() is required.
26-
// (like "nerdctl/bypass4netns=true" or "nerdctl/bypass4netns=false")
27-
Bypass4netns = Prefix + "bypass4netns"
33+
"github.com/opencontainers/runtime-spec/specs-go"
2834

29-
// Bypass4netnsIgnoreSubnets is a JSON of []string that is appended to
30-
// the `bypass4netns --ignore` list.
31-
Bypass4netnsIgnoreSubnets = Bypass4netns + "-ignore-subnets"
35+
containerd "github.com/containerd/containerd/v2/client"
36+
"github.com/containerd/go-cni"
3237

33-
// Bypass4netnsIgnoreBind disables acceleration for bind.
34-
// Boolean value which can be parsed with strconv.ParseBool() is required.
35-
Bypass4netnsIgnoreBind = Bypass4netns + "-ignore-bind"
38+
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
39+
"github.com/containerd/nerdctl/v2/pkg/mountutil"
40+
"github.com/containerd/nerdctl/v2/pkg/platformutil"
3641
)
3742

38-
var ShellCompletions = []string{
39-
Bypass4netns + "=true",
40-
Bypass4netns + "=false",
41-
Bypass4netnsIgnoreSubnets + "=",
42-
Bypass4netnsIgnoreBind + "=true",
43-
Bypass4netnsIgnoreBind + "=false",
43+
func New(ctx context.Context, con containerd.Container) (*Annotations, error) {
44+
an := &Annotations{}
45+
err := an.Unmarshall(ctx, con)
46+
return an, err
47+
}
48+
49+
func NewFromState(state *specs.State) (*Annotations, error) {
50+
an := &Annotations{}
51+
if state == nil || state.Annotations == nil {
52+
return nil, fmt.Errorf("invalid state")
53+
}
54+
err := an.UnmarshallFromMap(state.Annotations)
55+
return an, err
56+
}
57+
58+
type Log struct {
59+
Driver string
60+
Opts map[string]string
61+
Address string
62+
}
63+
64+
type Annotations struct {
65+
AnonVolumes []string
66+
Bypass4netns bool
67+
Bypass4netnsIgnoreBind bool
68+
Bypass4netnsIgnoreSubnets []string
69+
CidFile string
70+
DeviceMapping []dockercompat.DeviceMapping
71+
DNSResolvConfOptions []string
72+
DNSSearchDomains []string
73+
DNSServers []string
74+
DomainName string
75+
ExtraHosts map[string]string
76+
// GroupAdd []string
77+
HostName string
78+
IPC string
79+
LogConfig *Log
80+
LogURI string
81+
MountPoints []*mountutil.Processed
82+
Name string
83+
Namespace string
84+
NetworkNamespace string
85+
Networks []string
86+
Platform string
87+
StateDir string
88+
PidContainer string
89+
PidFile string
90+
Ports []cni.PortMapping
91+
Rm bool
92+
User string
93+
94+
// FIXME: these should be replaced by a richer Network label allowing per network ip and mac
95+
IP6Address string
96+
IPAddress string
97+
MACAddress string
98+
}
99+
100+
func (an *Annotations) UnmarshallFromMap(source map[string]string) error {
101+
hostConfig := &dockercompat.HostConfig{}
102+
dnsConfig := &dockercompat.DNSSettings{}
103+
for k, v := range source {
104+
switch k {
105+
case AnonymousVolumes:
106+
_ = json.Unmarshal([]byte(v), &an.AnonVolumes)
107+
case Bypass4netns:
108+
an.Bypass4netns, _ = strconv.ParseBool(v)
109+
case Bypass4netnsIgnoreBind:
110+
an.Bypass4netnsIgnoreBind, _ = strconv.ParseBool(v)
111+
case Bypass4netnsIgnoreSubnets:
112+
_ = json.Unmarshal([]byte(v), &an.Bypass4netnsIgnoreSubnets)
113+
case DNSSetting:
114+
_ = json.Unmarshal([]byte(v), dnsConfig)
115+
an.DNSServers = dnsConfig.DNSServers
116+
an.DNSSearchDomains = dnsConfig.DNSSearchDomains
117+
an.DNSResolvConfOptions = dnsConfig.DNSResolvConfOptions
118+
case Domainname:
119+
an.DomainName = v
120+
case ExtraHosts:
121+
hosts := []string{}
122+
_ = json.Unmarshal([]byte(v), &hosts)
123+
if an.ExtraHosts == nil {
124+
an.ExtraHosts = map[string]string{}
125+
}
126+
for _, host := range hosts {
127+
if v := strings.SplitN(host, ":", 2); len(v) == 2 {
128+
an.ExtraHosts[v[0]] = v[1]
129+
}
130+
}
131+
132+
case HostConfig:
133+
_ = json.Unmarshal([]byte(v), hostConfig)
134+
an.CidFile = hostConfig.ContainerIDFile
135+
// = hostConfig.CgroupnsMode
136+
case Hostname:
137+
an.HostName = v
138+
case IP6Address:
139+
an.IP6Address = v
140+
case IPAddress:
141+
an.IPAddress = v
142+
case IPC:
143+
an.IPC = v
144+
case LogConfig:
145+
_ = json.Unmarshal([]byte(v), &an.LogConfig)
146+
case LogURI:
147+
an.LogURI = v
148+
case MACAddress:
149+
an.MACAddress = v
150+
case Mounts:
151+
_ = json.Unmarshal([]byte(v), &an.MountPoints)
152+
case Name:
153+
an.Name = v
154+
case Namespace:
155+
an.Namespace = v
156+
case NetworkNamespace:
157+
an.NetworkNamespace = v
158+
case Networks:
159+
_ = json.Unmarshal([]byte(v), &an.Networks)
160+
case PIDContainer:
161+
an.PidContainer = v
162+
case PIDFile:
163+
an.PidFile = v
164+
case Platform:
165+
an.Platform = v
166+
case Ports:
167+
_ = json.Unmarshal([]byte(v), &an.Ports)
168+
case ContainerAutoRemove:
169+
an.Rm, _ = strconv.ParseBool(v)
170+
case StateDir:
171+
an.StateDir = v
172+
// FIXME: add missing, including NetworkNamespace
173+
case User:
174+
an.User = v
175+
default:
176+
}
177+
}
178+
179+
return nil
180+
}
181+
182+
func (an *Annotations) Unmarshall(ctx context.Context, con containerd.Container) error {
183+
conSpecs, err := con.Spec(ctx)
184+
if err != nil || conSpecs.Annotations == nil {
185+
return err
186+
}
187+
188+
return an.UnmarshallFromMap(conSpecs.Annotations)
189+
}
190+
191+
func (an *Annotations) Marshall() (map[string]string, error) {
192+
annot := make(map[string]string)
193+
194+
var (
195+
err error
196+
hostConfig dockercompat.HostConfigLabel
197+
dnsSettings dockercompat.DNSSettings
198+
)
199+
200+
if len(an.AnonVolumes) > 0 {
201+
anonVolumeJSON, err := json.Marshal(an.AnonVolumes)
202+
if err != nil {
203+
return nil, err
204+
}
205+
annot[AnonymousVolumes] = string(anonVolumeJSON)
206+
}
207+
208+
if an.CidFile != "" {
209+
hostConfig.CidFile = an.CidFile
210+
}
211+
212+
if len(an.DeviceMapping) > 0 {
213+
hostConfig.Devices = append(hostConfig.Devices, an.DeviceMapping...)
214+
}
215+
216+
if len(an.DNSResolvConfOptions) > 0 {
217+
dnsSettings.DNSResolvConfOptions = an.DNSResolvConfOptions
218+
}
219+
220+
if len(an.DNSServers) > 0 {
221+
dnsSettings.DNSServers = an.DNSServers
222+
}
223+
224+
if len(an.DNSSearchDomains) > 0 {
225+
dnsSettings.DNSSearchDomains = an.DNSSearchDomains
226+
}
227+
228+
dnsSettingsJSON, err := json.Marshal(dnsSettings)
229+
if err != nil {
230+
return nil, err
231+
}
232+
annot[DNSSetting] = string(dnsSettingsJSON)
233+
234+
annot[Domainname] = an.DomainName
235+
236+
//if len(an.GroupAdd) > 0 {
237+
//}
238+
239+
hosts := []string{}
240+
for k, v := range an.ExtraHosts {
241+
hosts = append(hosts, fmt.Sprintf("%s:%s", k, v))
242+
}
243+
extraHostsJSON, err := json.Marshal(hosts)
244+
if err != nil {
245+
return nil, err
246+
}
247+
annot[ExtraHosts] = string(extraHostsJSON)
248+
249+
hostConfigJSON, err := json.Marshal(hostConfig)
250+
if err != nil {
251+
return nil, err
252+
}
253+
annot[HostConfig] = string(hostConfigJSON)
254+
255+
annot[Hostname] = an.HostName
256+
257+
if an.IP6Address != "" {
258+
annot[IP6Address] = an.IP6Address
259+
}
260+
261+
if an.IPAddress != "" {
262+
annot[IPAddress] = an.IPAddress
263+
}
264+
265+
if an.IPC != "" {
266+
annot[IPC] = an.IPC
267+
}
268+
269+
if an.LogURI != "" {
270+
annot[LogURI] = an.LogURI
271+
272+
if an.LogConfig != nil {
273+
logConfigJSON, err := json.Marshal(an.LogConfig)
274+
if err != nil {
275+
return nil, err
276+
}
277+
278+
annot[LogConfig] = string(logConfigJSON)
279+
}
280+
}
281+
282+
if an.MACAddress != "" {
283+
annot[MACAddress] = an.MACAddress
284+
}
285+
286+
if len(an.MountPoints) > 0 {
287+
mounts := dockercompatMounts(an.MountPoints)
288+
mountPointsJSON, err := json.Marshal(mounts)
289+
if err != nil {
290+
return nil, err
291+
}
292+
annot[Mounts] = string(mountPointsJSON)
293+
}
294+
295+
annot[Name] = an.Name
296+
annot[Namespace] = an.Namespace
297+
298+
networksJSON, err := json.Marshal(an.Networks)
299+
if err != nil {
300+
return nil, err
301+
}
302+
annot[Networks] = string(networksJSON)
303+
304+
annot[Platform], err = platformutil.NormalizeString(an.Platform)
305+
if err != nil {
306+
return nil, err
307+
}
308+
309+
if an.PidFile != "" {
310+
annot[PIDFile] = an.PidFile
311+
}
312+
313+
if len(an.Ports) > 0 {
314+
portsJSON, err := json.Marshal(an.Ports)
315+
if err != nil {
316+
return nil, err
317+
}
318+
319+
annot[Ports] = string(portsJSON)
320+
}
321+
322+
if an.PidContainer != "" {
323+
annot[PIDContainer] = an.PidContainer
324+
}
325+
326+
annot[ContainerAutoRemove] = fmt.Sprintf("%t", an.Rm)
327+
328+
annot[StateDir] = an.StateDir
329+
330+
if an.User != "" {
331+
annot[User] = an.User
332+
}
333+
334+
return annot, nil
335+
}
336+
337+
func dockercompatMounts(mountPoints []*mountutil.Processed) []dockercompat.MountPoint {
338+
result := make([]dockercompat.MountPoint, len(mountPoints))
339+
for i := range mountPoints {
340+
mp := mountPoints[i]
341+
result[i] = dockercompat.MountPoint{
342+
Type: mp.Type,
343+
Name: mp.Name,
344+
Source: mp.Mount.Source,
345+
Destination: mp.Mount.Destination,
346+
Driver: "",
347+
Mode: mp.Mode,
348+
}
349+
result[i].RW, result[i].Propagation = dockercompat.ParseMountProperties(strings.Split(mp.Mode, ","))
350+
351+
// it's an anonymous volume
352+
if mp.AnonymousVolume != "" {
353+
result[i].Name = mp.AnonymousVolume
354+
}
355+
356+
// volume only support local driver
357+
if mp.Type == "volume" {
358+
result[i].Driver = "local"
359+
}
360+
}
361+
362+
return result
44363
}

0 commit comments

Comments
 (0)