github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/api/server/router/network/network_routes.go (about) 1 package network // import "github.com/docker/docker/api/server/router/network" 2 3 import ( 4 "context" 5 "net/http" 6 "strconv" 7 "strings" 8 9 "github.com/docker/docker/api/server/httputils" 10 "github.com/docker/docker/api/types" 11 "github.com/docker/docker/api/types/filters" 12 "github.com/docker/docker/api/types/network" 13 "github.com/docker/docker/api/types/versions" 14 "github.com/docker/docker/errdefs" 15 "github.com/docker/docker/libnetwork" 16 netconst "github.com/docker/docker/libnetwork/datastore" 17 "github.com/pkg/errors" 18 ) 19 20 func (n *networkRouter) getNetworksList(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 21 if err := httputils.ParseForm(r); err != nil { 22 return err 23 } 24 25 filter, err := filters.FromJSON(r.Form.Get("filters")) 26 if err != nil { 27 return err 28 } 29 30 if err := network.ValidateFilters(filter); err != nil { 31 return err 32 } 33 34 var list []types.NetworkResource 35 nr, err := n.cluster.GetNetworks(filter) 36 if err == nil { 37 list = nr 38 } 39 40 // Combine the network list returned by Docker daemon if it is not already 41 // returned by the cluster manager 42 localNetworks, err := n.backend.GetNetworks(filter, types.NetworkListConfig{Detailed: versions.LessThan(httputils.VersionFromContext(ctx), "1.28")}) 43 if err != nil { 44 return err 45 } 46 47 var idx map[string]bool 48 if len(list) > 0 { 49 idx = make(map[string]bool, len(list)) 50 for _, n := range list { 51 idx[n.ID] = true 52 } 53 } 54 for _, n := range localNetworks { 55 if idx[n.ID] { 56 continue 57 } 58 list = append(list, n) 59 } 60 61 if list == nil { 62 list = []types.NetworkResource{} 63 } 64 65 return httputils.WriteJSON(w, http.StatusOK, list) 66 } 67 68 type invalidRequestError struct { 69 cause error 70 } 71 72 func (e invalidRequestError) Error() string { 73 return e.cause.Error() 74 } 75 76 func (e invalidRequestError) InvalidParameter() {} 77 78 type ambigousResultsError string 79 80 func (e ambigousResultsError) Error() string { 81 return "network " + string(e) + " is ambiguous" 82 } 83 84 func (ambigousResultsError) InvalidParameter() {} 85 86 func nameConflict(name string) error { 87 return errdefs.Conflict(libnetwork.NetworkNameError(name)) 88 } 89 90 func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 91 if err := httputils.ParseForm(r); err != nil { 92 return err 93 } 94 95 term := vars["id"] 96 var ( 97 verbose bool 98 err error 99 ) 100 if v := r.URL.Query().Get("verbose"); v != "" { 101 if verbose, err = strconv.ParseBool(v); err != nil { 102 return errors.Wrapf(invalidRequestError{err}, "invalid value for verbose: %s", v) 103 } 104 } 105 scope := r.URL.Query().Get("scope") 106 107 // In case multiple networks have duplicate names, return error. 108 // TODO (yongtang): should we wrap with version here for backward compatibility? 109 110 // First find based on full ID, return immediately once one is found. 111 // If a network appears both in swarm and local, assume it is in local first 112 113 // For full name and partial ID, save the result first, and process later 114 // in case multiple records was found based on the same term 115 listByFullName := map[string]types.NetworkResource{} 116 listByPartialID := map[string]types.NetworkResource{} 117 118 // TODO(@cpuguy83): All this logic for figuring out which network to return does not belong here 119 // Instead there should be a backend function to just get one network. 120 filter := filters.NewArgs(filters.Arg("idOrName", term)) 121 if scope != "" { 122 filter.Add("scope", scope) 123 } 124 nw, _ := n.backend.GetNetworks(filter, types.NetworkListConfig{Detailed: true, Verbose: verbose}) 125 for _, network := range nw { 126 if network.ID == term { 127 return httputils.WriteJSON(w, http.StatusOK, network) 128 } 129 if network.Name == term { 130 // No need to check the ID collision here as we are still in 131 // local scope and the network ID is unique in this scope. 132 listByFullName[network.ID] = network 133 } 134 if strings.HasPrefix(network.ID, term) { 135 // No need to check the ID collision here as we are still in 136 // local scope and the network ID is unique in this scope. 137 listByPartialID[network.ID] = network 138 } 139 } 140 141 nwk, err := n.cluster.GetNetwork(term) 142 if err == nil { 143 // If the get network is passed with a specific network ID / partial network ID 144 // or if the get network was passed with a network name and scope as swarm 145 // return the network. Skipped using isMatchingScope because it is true if the scope 146 // is not set which would be case if the client API v1.30 147 if strings.HasPrefix(nwk.ID, term) || (netconst.SwarmScope == scope) { 148 // If we have a previous match "backend", return it, we need verbose when enabled 149 // ex: overlay/partial_ID or name/swarm_scope 150 if nwv, ok := listByPartialID[nwk.ID]; ok { 151 nwk = nwv 152 } else if nwv, ok := listByFullName[nwk.ID]; ok { 153 nwk = nwv 154 } 155 return httputils.WriteJSON(w, http.StatusOK, nwk) 156 } 157 } 158 159 nr, _ := n.cluster.GetNetworks(filter) 160 for _, network := range nr { 161 if network.ID == term { 162 return httputils.WriteJSON(w, http.StatusOK, network) 163 } 164 if network.Name == term { 165 // Check the ID collision as we are in swarm scope here, and 166 // the map (of the listByFullName) may have already had a 167 // network with the same ID (from local scope previously) 168 if _, ok := listByFullName[network.ID]; !ok { 169 listByFullName[network.ID] = network 170 } 171 } 172 if strings.HasPrefix(network.ID, term) { 173 // Check the ID collision as we are in swarm scope here, and 174 // the map (of the listByPartialID) may have already had a 175 // network with the same ID (from local scope previously) 176 if _, ok := listByPartialID[network.ID]; !ok { 177 listByPartialID[network.ID] = network 178 } 179 } 180 } 181 182 // Find based on full name, returns true only if no duplicates 183 if len(listByFullName) == 1 { 184 for _, v := range listByFullName { 185 return httputils.WriteJSON(w, http.StatusOK, v) 186 } 187 } 188 if len(listByFullName) > 1 { 189 return errors.Wrapf(ambigousResultsError(term), "%d matches found based on name", len(listByFullName)) 190 } 191 192 // Find based on partial ID, returns true only if no duplicates 193 if len(listByPartialID) == 1 { 194 for _, v := range listByPartialID { 195 return httputils.WriteJSON(w, http.StatusOK, v) 196 } 197 } 198 if len(listByPartialID) > 1 { 199 return errors.Wrapf(ambigousResultsError(term), "%d matches found based on ID prefix", len(listByPartialID)) 200 } 201 202 return libnetwork.ErrNoSuchNetwork(term) 203 } 204 205 func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 206 if err := httputils.ParseForm(r); err != nil { 207 return err 208 } 209 210 var create types.NetworkCreateRequest 211 if err := httputils.ReadJSON(r, &create); err != nil { 212 return err 213 } 214 215 if nws, err := n.cluster.GetNetworksByName(create.Name); err == nil && len(nws) > 0 { 216 return nameConflict(create.Name) 217 } 218 219 nw, err := n.backend.CreateNetwork(create) 220 if err != nil { 221 var warning string 222 if _, ok := err.(libnetwork.NetworkNameError); ok { 223 // check if user defined CheckDuplicate, if set true, return err 224 // otherwise prepare a warning message 225 if create.CheckDuplicate { 226 return nameConflict(create.Name) 227 } 228 warning = libnetwork.NetworkNameError(create.Name).Error() 229 } 230 231 if _, ok := err.(libnetwork.ManagerRedirectError); !ok { 232 return err 233 } 234 id, err := n.cluster.CreateNetwork(create) 235 if err != nil { 236 return err 237 } 238 nw = &types.NetworkCreateResponse{ 239 ID: id, 240 Warning: warning, 241 } 242 } 243 244 return httputils.WriteJSON(w, http.StatusCreated, nw) 245 } 246 247 func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 248 if err := httputils.ParseForm(r); err != nil { 249 return err 250 } 251 252 var connect types.NetworkConnect 253 if err := httputils.ReadJSON(r, &connect); err != nil { 254 return err 255 } 256 257 // Unlike other operations, we does not check ambiguity of the name/ID here. 258 // The reason is that, In case of attachable network in swarm scope, the actual local network 259 // may not be available at the time. At the same time, inside daemon `ConnectContainerToNetwork` 260 // does the ambiguity check anyway. Therefore, passing the name to daemon would be enough. 261 return n.backend.ConnectContainerToNetwork(connect.Container, vars["id"], connect.EndpointConfig) 262 } 263 264 func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 265 if err := httputils.ParseForm(r); err != nil { 266 return err 267 } 268 269 var disconnect types.NetworkDisconnect 270 if err := httputils.ReadJSON(r, &disconnect); err != nil { 271 return err 272 } 273 274 return n.backend.DisconnectContainerFromNetwork(disconnect.Container, vars["id"], disconnect.Force) 275 } 276 277 func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 278 if err := httputils.ParseForm(r); err != nil { 279 return err 280 } 281 282 nw, err := n.findUniqueNetwork(vars["id"]) 283 if err != nil { 284 return err 285 } 286 if nw.Scope == "swarm" { 287 if err = n.cluster.RemoveNetwork(nw.ID); err != nil { 288 return err 289 } 290 } else { 291 if err := n.backend.DeleteNetwork(nw.ID); err != nil { 292 return err 293 } 294 } 295 w.WriteHeader(http.StatusNoContent) 296 return nil 297 } 298 299 func (n *networkRouter) postNetworksPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 300 if err := httputils.ParseForm(r); err != nil { 301 return err 302 } 303 304 pruneFilters, err := filters.FromJSON(r.Form.Get("filters")) 305 if err != nil { 306 return err 307 } 308 309 pruneReport, err := n.backend.NetworksPrune(ctx, pruneFilters) 310 if err != nil { 311 return err 312 } 313 return httputils.WriteJSON(w, http.StatusOK, pruneReport) 314 } 315 316 // findUniqueNetwork will search network across different scopes (both local and swarm). 317 // NOTE: This findUniqueNetwork is different from FindNetwork in the daemon. 318 // In case multiple networks have duplicate names, return error. 319 // First find based on full ID, return immediately once one is found. 320 // If a network appears both in swarm and local, assume it is in local first 321 // For full name and partial ID, save the result first, and process later 322 // in case multiple records was found based on the same term 323 // TODO (yongtang): should we wrap with version here for backward compatibility? 324 func (n *networkRouter) findUniqueNetwork(term string) (types.NetworkResource, error) { 325 listByFullName := map[string]types.NetworkResource{} 326 listByPartialID := map[string]types.NetworkResource{} 327 328 filter := filters.NewArgs(filters.Arg("idOrName", term)) 329 nw, _ := n.backend.GetNetworks(filter, types.NetworkListConfig{Detailed: true}) 330 for _, network := range nw { 331 if network.ID == term { 332 return network, nil 333 } 334 if network.Name == term && !network.Ingress { 335 // No need to check the ID collision here as we are still in 336 // local scope and the network ID is unique in this scope. 337 listByFullName[network.ID] = network 338 } 339 if strings.HasPrefix(network.ID, term) { 340 // No need to check the ID collision here as we are still in 341 // local scope and the network ID is unique in this scope. 342 listByPartialID[network.ID] = network 343 } 344 } 345 346 nr, _ := n.cluster.GetNetworks(filter) 347 for _, network := range nr { 348 if network.ID == term { 349 return network, nil 350 } 351 if network.Name == term { 352 // Check the ID collision as we are in swarm scope here, and 353 // the map (of the listByFullName) may have already had a 354 // network with the same ID (from local scope previously) 355 if _, ok := listByFullName[network.ID]; !ok { 356 listByFullName[network.ID] = network 357 } 358 } 359 if strings.HasPrefix(network.ID, term) { 360 // Check the ID collision as we are in swarm scope here, and 361 // the map (of the listByPartialID) may have already had a 362 // network with the same ID (from local scope previously) 363 if _, ok := listByPartialID[network.ID]; !ok { 364 listByPartialID[network.ID] = network 365 } 366 } 367 } 368 369 // Find based on full name, returns true only if no duplicates 370 if len(listByFullName) == 1 { 371 for _, v := range listByFullName { 372 return v, nil 373 } 374 } 375 if len(listByFullName) > 1 { 376 return types.NetworkResource{}, errdefs.InvalidParameter(errors.Errorf("network %s is ambiguous (%d matches found based on name)", term, len(listByFullName))) 377 } 378 379 // Find based on partial ID, returns true only if no duplicates 380 if len(listByPartialID) == 1 { 381 for _, v := range listByPartialID { 382 return v, nil 383 } 384 } 385 if len(listByPartialID) > 1 { 386 return types.NetworkResource{}, errdefs.InvalidParameter(errors.Errorf("network %s is ambiguous (%d matches found based on ID prefix)", term, len(listByPartialID))) 387 } 388 389 return types.NetworkResource{}, errdefs.NotFound(libnetwork.ErrNoSuchNetwork(term)) 390 }