@@ -5,65 +5,42 @@ package network
5
5
6
6
import (
7
7
"context"
8
- "encoding/json"
9
8
"fmt"
10
- "os"
11
- "path/filepath"
12
9
"strings"
13
10
14
- "github.com/containerd/nerdctl/pkg/lockutil"
15
11
"github.com/containerd/nerdctl/pkg/netutil"
16
12
17
13
"github.com/runfinch/finch-daemon/api/types"
14
+ "github.com/runfinch/finch-daemon/internal/service/network/driver"
18
15
"github.com/runfinch/finch-daemon/pkg/errdefs"
19
16
"github.com/runfinch/finch-daemon/pkg/utility/maputility"
20
17
)
21
18
22
19
// Create implements the logic to turn a network create request to the back-end nerdctl create network calls.
23
20
func (s * service ) Create (ctx context.Context , request types.NetworkCreateRequest ) (types.NetworkCreateResponse , error ) {
24
- // enable_ip_masquerade, host_binding_ipv4, and bridge name network options are not supported by nerdctl.
25
- // So we must filter out any unsupported options which would prevent the network from being created and accept the defaults.
26
- bridge := ""
27
- filterUnsupportedOptions := func (original map [string ]string ) map [string ]string {
28
- options := map [string ]string {}
29
- for k , v := range original {
30
- switch k {
31
- case "com.docker.network.bridge.enable_ip_masquerade" :
32
- // must be true
33
- if v != "true" {
34
- s .logger .Warnf ("network option com.docker.network.bridge.enable_ip_masquerade is set to %s, but it must be true" , v )
35
- }
36
- case "com.docker.network.bridge.host_binding_ipv4" :
37
- // must be 0.0.0.0
38
- if v != "0.0.0.0" {
39
- s .logger .Warnf ("network option com.docker.network.bridge.host_binding_ipv4 is set to %s, but it must be 0.0.0.0" , v )
40
- }
41
- case "com.docker.network.bridge.enable_icc" :
42
- s .logger .Warnf ("network option com.docker.network.bridge.enable_icc is not currently supported in nerdctl" , v )
43
- case "com.docker.network.bridge.name" :
44
- bridge = v
45
- default :
46
- options [k ] = v
47
- }
21
+ var bridgeDriver driver.DriverHandler
22
+ var err error
23
+
24
+ createOptionsFrom := func (request types.NetworkCreateRequest ) (netutil.CreateOptions , error ) {
25
+ // Default to "bridge" driver if request does not specify a driver
26
+ networkDriver := request .Driver
27
+ if networkDriver == "" {
28
+ networkDriver = "bridge"
48
29
}
49
- return options
50
- }
51
30
52
- createOptionsFrom := func (r types.NetworkCreateRequest ) netutil.CreateOptions {
53
31
options := netutil.CreateOptions {
54
- Name : r .Name ,
55
- Driver : "bridge" ,
32
+ Name : request .Name ,
33
+ Driver : networkDriver ,
56
34
IPAMDriver : "default" ,
57
- IPAMOptions : r .IPAM .Options ,
58
- Options : filterUnsupportedOptions (r .Options ),
59
- Labels : maputility .Flatten (r .Labels , maputility .KeyEqualsValueFormat ),
60
- }
61
- if r .Driver != "" {
62
- options .Driver = r .Driver
35
+ IPAMOptions : request .IPAM .Options ,
36
+ Labels : maputility .Flatten (request .Labels , maputility .KeyEqualsValueFormat ),
37
+ IPv6 : request .EnableIPv6 ,
63
38
}
64
- if r .IPAM .Driver != "" {
65
- options .IPAMDriver = r .IPAM .Driver
39
+
40
+ if request .IPAM .Driver != "" {
41
+ options .IPAMDriver = request .IPAM .Driver
66
42
}
43
+
67
44
if len (request .IPAM .Config ) != 0 {
68
45
options .Subnets = []string {}
69
46
if subnet , ok := request .IPAM .Config [0 ]["Subnet" ]; ok {
@@ -76,7 +53,21 @@ func (s *service) Create(ctx context.Context, request types.NetworkCreateRequest
76
53
options .Gateway = gateway
77
54
}
78
55
}
79
- return options
56
+
57
+ // Handle driver-specific options
58
+ switch networkDriver {
59
+ case "bridge" :
60
+ bridgeDriver , err = driver .NewBridgeDriver (s .netClient , s .logger , options .IPv6 )
61
+ if err != nil {
62
+ return options , err
63
+ }
64
+ options , err = bridgeDriver .HandleCreateOptions (request , options )
65
+ return options , err
66
+ default :
67
+ options .Options = request .Options
68
+ }
69
+
70
+ return options , nil
80
71
}
81
72
82
73
if config , err := s .getNetwork (request .Name ); err == nil {
@@ -92,8 +83,21 @@ func (s *service) Create(ctx context.Context, request types.NetworkCreateRequest
92
83
return response , nil
93
84
}
94
85
95
- net , err := s .netClient .CreateNetwork (createOptionsFrom (request ))
96
- warning := ""
86
+ options , err := createOptionsFrom (request )
87
+ if err != nil {
88
+ return types.NetworkCreateResponse {}, err
89
+ }
90
+
91
+ // Ensure thread-safety for network operations using a per-network mutex.
92
+ // Operations on different network IDs can proceed concurrently.
93
+ netMu := s .ensureLock (request .Name )
94
+
95
+ netMu .Lock ()
96
+ defer netMu .Unlock ()
97
+ defer s .releaseLock (request .Name )
98
+
99
+ // Create network
100
+ net , err := s .netClient .CreateNetwork (options )
97
101
if err != nil && strings .Contains (err .Error (), "unsupported cni driver" ) {
98
102
return types.NetworkCreateResponse {}, errdefs .NewNotFound (errPluginNotFound )
99
103
} else if err != nil {
@@ -104,103 +108,33 @@ func (s *service) Create(ctx context.Context, request types.NetworkCreateRequest
104
108
return types.NetworkCreateResponse {}, errNetworkIDNotFound
105
109
}
106
110
107
- // Since nerdctl currently does not support custom bridge names,
108
- // we explicitly override bridge name in the conflist file for the network that was just created
109
- if bridge != "" {
110
- if err = s .setBridgeName (net , bridge ); err != nil {
111
- warning = fmt .Sprintf ("Failed to set network bridge name %s: %s" , bridge , err )
111
+ // Add cleanup func to remove the network if an error is encountered during post processing
112
+ cleanup := func (ctx context.Context , name string ) {
113
+ if cleanupErr := s .Remove (ctx , name ); cleanupErr != nil {
114
+ // ignore if the network does not exist
115
+ if ! errdefs .IsNotFound (cleanupErr ) {
116
+ s .logger .Errorf ("cleanup failed in defer %s: %v" , name , cleanupErr )
117
+ }
112
118
}
113
119
}
114
120
115
- return types.NetworkCreateResponse {
116
- ID : * net .NerdctlID ,
117
- Warning : warning ,
118
- }, nil
119
- }
120
-
121
- // setBridgeName will override the bridge name in an existing CNI config file for a network.
122
- func (s * service ) setBridgeName (net * netutil.NetworkConfig , bridge string ) error {
123
- return lockutil .WithDirLock (s .netClient .NetconfPath (), func () error {
124
- // first, make sure that the bridge name is not used by any of the existing bridge networks
125
- bridgeNet , err := s .getNetworkByBridgeName (bridge )
121
+ defer func () {
126
122
if err != nil {
127
- return err
128
- }
129
- if bridgeNet != nil {
130
- return fmt .Errorf ("bridge name %s already in use by network %s" , bridge , bridgeNet .Name )
123
+ cleanup (ctx , request .Name )
131
124
}
125
+ }()
132
126
133
- // load the CNI config file and set bridge name
134
- configFilename := s .getConfigPathForNetworkName (net .Name )
135
- configFile , err := os .Open (configFilename )
127
+ // Handle post network create actions
128
+ warning := ""
129
+ if bridgeDriver != nil {
130
+ warning , err = bridgeDriver .HandlePostCreate (net )
136
131
if err != nil {
137
- return err
138
- }
139
- defer configFile .Close ()
140
- var netJSON interface {}
141
- if err = json .NewDecoder (configFile ).Decode (& netJSON ); err != nil {
142
- return err
143
- }
144
- netMap , ok := netJSON .(map [string ]interface {})
145
- if ! ok {
146
- return fmt .Errorf ("network config file %s is not a valid map" , configFilename )
147
- }
148
- plugins , ok := netMap ["plugins" ]
149
- if ! ok {
150
- return fmt .Errorf ("could not find plugins in network config file %s" , configFilename )
151
- }
152
- pluginsMap , ok := plugins .([]interface {})
153
- if ! ok {
154
- return fmt .Errorf ("could not parse plugins in network config file %s" , configFilename )
155
- }
156
- for _ , plugin := range pluginsMap {
157
- pluginMap , ok := plugin .(map [string ]interface {})
158
- if ! ok {
159
- continue
160
- }
161
- if pluginMap ["type" ] == "bridge" {
162
- pluginMap ["bridge" ] = bridge
163
- data , err := json .MarshalIndent (netJSON , "" , " " )
164
- if err != nil {
165
- return err
166
- }
167
- return os .WriteFile (configFilename , data , 0o644 )
168
- }
169
- }
170
- return fmt .Errorf ("bridge plugin not found in network config file %s" , configFilename )
171
- })
172
- }
173
-
174
- // From https://github.com/containerd/nerdctl/blob/v1.5.0/pkg/netutil/netutil.go#L186-L188
175
- func (s * service ) getConfigPathForNetworkName (netName string ) string {
176
- return filepath .Join (s .netClient .NetconfPath (), "nerdctl-" + netName + ".conflist" )
177
- }
178
-
179
- type bridgePlugin struct {
180
- Type string `json:"type"`
181
- Bridge string `json:"bridge"`
182
- }
183
-
184
- func (s * service ) getNetworkByBridgeName (bridge string ) (* netutil.NetworkConfig , error ) {
185
- networks , err := s .netClient .FilterNetworks (func (* netutil.NetworkConfig ) bool {
186
- return true
187
- })
188
- if err != nil {
189
- return nil , err
190
- }
191
- for _ , network := range networks {
192
- for _ , plugin := range network .Plugins {
193
- if plugin .Network .Type != "bridge" {
194
- continue
195
- }
196
- var bridgeJSON bridgePlugin
197
- if err = json .Unmarshal (plugin .Bytes , & bridgeJSON ); err != nil {
198
- continue
199
- }
200
- if bridgeJSON .Bridge == bridge {
201
- return network , nil
202
- }
132
+ return types.NetworkCreateResponse {}, err
203
133
}
204
134
}
205
- return nil , nil
135
+
136
+ return types.NetworkCreateResponse {
137
+ ID : * net .NerdctlID ,
138
+ Warning : warning ,
139
+ }, nil
206
140
}
0 commit comments