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