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