@@ -25,7 +25,6 @@ import (
2525 "strings"
2626
2727 oci "github.com/opencontainers/runtime-spec/specs-go"
28- ocigen "github.com/opencontainers/runtime-tools/generate"
2928 cdi "tags.cncf.io/container-device-interface/specs-go"
3029)
3130
@@ -80,9 +79,11 @@ func (e *ContainerEdits) Apply(spec *oci.Spec) error {
8079 return nil
8180 }
8281
83- specgen := ocigen .NewFromSpec (spec )
8482 if len (e .Env ) > 0 {
85- specgen .AddMultipleProcessEnv (e .Env )
83+ if spec .Process == nil {
84+ spec .Process = & oci.Process {}
85+ }
86+ addMultipleProcessEnv (spec .Process , e .Env )
8687 }
8788
8889 for _ , d := range e .DeviceNodes {
@@ -104,8 +105,11 @@ func (e *ContainerEdits) Apply(spec *oci.Spec) error {
104105 }
105106 }
106107
107- specgen .RemoveDevice (dev .Path )
108- specgen .AddDevice (dev )
108+ if spec .Linux == nil {
109+ spec .Linux = & oci.Linux {}
110+ }
111+ removeDevice (spec , dev .Path )
112+ spec .Linux .Devices = append (spec .Linux .Devices , dev )
109113
110114 if dev .Type == "b" || dev .Type == "c" {
111115 access := d .Permissions
@@ -115,80 +119,134 @@ func (e *ContainerEdits) Apply(spec *oci.Spec) error {
115119 case NoPermissions :
116120 access = ""
117121 }
118- specgen .AddLinuxResourcesDevice (true , dev .Type , & dev .Major , & dev .Minor , access )
122+ if spec .Linux .Resources == nil {
123+ spec .Linux .Resources = & oci.LinuxResources {}
124+ }
125+ spec .Linux .Resources .Devices = append (spec .Linux .Resources .Devices , oci.LinuxDeviceCgroup {
126+ Allow : true ,
127+ Type : dev .Type ,
128+ Major : & dev .Major ,
129+ Minor : & dev .Minor ,
130+ Access : access ,
131+ })
119132 }
120133 }
121134
122- if len (e .NetDevices ) > 0 {
123- // specgen is currently missing functionality to set Linux NetDevices,
124- // so we use a locally rolled function for now.
125- for _ , dev := range e .NetDevices {
126- specgenAddLinuxNetDevice (& specgen , dev .HostInterfaceName , (& LinuxNetDevice {dev }).toOCI ())
127- }
135+ for _ , dev := range e .NetDevices {
136+ ensureLinuxNetDevices (spec )
137+ spec .Linux .NetDevices [dev .HostInterfaceName ] = * (& LinuxNetDevice {dev }).toOCI ()
128138 }
129139
130140 if len (e .Mounts ) > 0 {
131141 for _ , m := range e .Mounts {
132142 mnt := & Mount {m }
133143
134- specgen . RemoveMount ( m .ContainerPath )
144+ removeMount ( spec , m .ContainerPath )
135145
136146 if ! specHasUserNamespace (spec ) {
137- specgen . AddMount ( mnt .toOCI ())
147+ spec . Mounts = append ( spec . Mounts , mnt .toOCI ())
138148 } else {
139- specgen . AddMount ( mnt .toOCI (withIDMapForBindMount ()))
149+ spec . Mounts = append ( spec . Mounts , mnt .toOCI (withIDMapForBindMount ()))
140150 }
141151 }
142- sortMounts ( & specgen )
152+ sort . Stable ( orderedMounts ( spec . Mounts ) )
143153 }
144154
145155 for _ , h := range e .Hooks {
146156 ociHook := (& Hook {h }).toOCI ()
157+ ensureOCIHooks (spec )
147158 switch h .HookName {
148159 case PrestartHook :
149- specgen . AddPreStartHook ( ociHook )
160+ spec . Hooks . Prestart = append ( spec . Hooks . Prestart , ociHook ) //nolint:staticcheck
150161 case PoststartHook :
151- specgen . AddPostStartHook ( ociHook )
162+ spec . Hooks . Poststart = append ( spec . Hooks . Poststart , ociHook )
152163 case PoststopHook :
153- specgen .AddPostStopHook (ociHook )
154- // TODO: Maybe runtime-tools/generate should be updated with these...
164+ spec .Hooks .Poststop = append (spec .Hooks .Poststop , ociHook )
155165 case CreateRuntimeHook :
156- ensureOCIHooks (spec )
157166 spec .Hooks .CreateRuntime = append (spec .Hooks .CreateRuntime , ociHook )
158167 case CreateContainerHook :
159- ensureOCIHooks (spec )
160168 spec .Hooks .CreateContainer = append (spec .Hooks .CreateContainer , ociHook )
161169 case StartContainerHook :
162- ensureOCIHooks (spec )
163170 spec .Hooks .StartContainer = append (spec .Hooks .StartContainer , ociHook )
164171 default :
165172 return fmt .Errorf ("unknown hook name %q" , h .HookName )
166173 }
167174 }
168175
169176 if e .IntelRdt != nil {
170- // The specgen is missing functionality to set all parameters so we
171- // just piggy-back on it to initialize all structs and the copy over.
172- specgen . SetLinuxIntelRdtClosID ( e . IntelRdt . ClosID )
177+ if spec . Linux == nil {
178+ spec . Linux = & oci. Linux {}
179+ }
173180 spec .Linux .IntelRdt = (& IntelRdt {e .IntelRdt }).toOCI ()
174181 }
175182
176183 for _ , additionalGID := range e .AdditionalGIDs {
177184 if additionalGID == 0 {
178185 continue
179186 }
180- specgen .AddProcessAdditionalGid (additionalGID )
187+ if spec .Process == nil {
188+ spec .Process = & oci.Process {}
189+ }
190+ addProcessAdditionalGid (spec , additionalGID )
181191 }
182192
183193 return nil
184194}
185195
186- func specgenAddLinuxNetDevice (specgen * ocigen.Generator , hostIf string , netDev * oci.LinuxNetDevice ) {
187- if specgen == nil || netDev == nil {
196+ // addMultipleProcessEnv adds or replaces environment variables on the process,
197+ // deduplicating by key.
198+ func addMultipleProcessEnv (process * oci.Process , envs []string ) {
199+ for _ , env := range envs {
200+ parts := strings .SplitN (env , "=" , 2 )
201+ if len (parts ) < 2 {
202+ continue
203+ }
204+ prefix := parts [0 ] + "="
205+ replaced := false
206+ for i , e := range process .Env {
207+ if strings .HasPrefix (e , prefix ) {
208+ process .Env [i ] = env
209+ replaced = true
210+ break
211+ }
212+ }
213+ if ! replaced {
214+ process .Env = append (process .Env , env )
215+ }
216+ }
217+ }
218+
219+ // removeDevice removes the device at path from spec.Linux.Devices.
220+ func removeDevice (spec * oci.Spec , path string ) {
221+ if spec .Linux == nil {
188222 return
189223 }
190- ensureLinuxNetDevices (specgen .Config )
191- specgen .Config .Linux .NetDevices [hostIf ] = * netDev
224+ for i , d := range spec .Linux .Devices {
225+ if d .Path == path {
226+ spec .Linux .Devices = append (spec .Linux .Devices [:i ], spec .Linux .Devices [i + 1 :]... )
227+ return
228+ }
229+ }
230+ }
231+
232+ // removeMount removes the mount with the given destination from spec.Mounts.
233+ func removeMount (spec * oci.Spec , dest string ) {
234+ for i , m := range spec .Mounts {
235+ if m .Destination == dest {
236+ spec .Mounts = append (spec .Mounts [:i ], spec .Mounts [i + 1 :]... )
237+ return
238+ }
239+ }
240+ }
241+
242+ // addProcessAdditionalGid appends gid to spec.Process.User.AdditionalGids if not already present.
243+ func addProcessAdditionalGid (spec * oci.Spec , gid uint32 ) {
244+ for _ , g := range spec .Process .User .AdditionalGids {
245+ if g == gid {
246+ return
247+ }
248+ }
249+ spec .Process .User .AdditionalGids = append (spec .Process .User .AdditionalGids , gid )
192250}
193251
194252// Ensure OCI Spec Linux NetDevices map is not nil.
@@ -444,14 +502,6 @@ func ensureOCIHooks(spec *oci.Spec) {
444502 }
445503}
446504
447- // sortMounts sorts the mounts in the given OCI Spec.
448- func sortMounts (specgen * ocigen.Generator ) {
449- mounts := specgen .Mounts ()
450- specgen .ClearMounts ()
451- sort .Stable (orderedMounts (mounts ))
452- specgen .Config .Mounts = mounts
453- }
454-
455505// orderedMounts defines how to sort an OCI Spec Mount slice.
456506// This is the almost the same implementation sa used by CRI-O and Docker,
457507// with a minor tweak for stable sorting order (easier to test):
0 commit comments