github.com/rish1988/moby@v25.0.2+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 nw, err := n.backend.CreateNetwork(create) 217 if err != nil { 218 if _, ok := err.(libnetwork.ManagerRedirectError); !ok { 219 return err 220 } 221 id, err := n.cluster.CreateNetwork(create) 222 if err != nil { 223 return err 224 } 225 nw = &types.NetworkCreateResponse{ 226 ID: id, 227 } 228 } 229 230 return httputils.WriteJSON(w, http.StatusCreated, nw) 231 } 232 233 func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 234 if err := httputils.ParseForm(r); err != nil { 235 return err 236 } 237 238 var connect types.NetworkConnect 239 if err := httputils.ReadJSON(r, &connect); err != nil { 240 return err 241 } 242 243 // Unlike other operations, we does not check ambiguity of the name/ID here. 244 // The reason is that, In case of attachable network in swarm scope, the actual local network 245 // may not be available at the time. At the same time, inside daemon `ConnectContainerToNetwork` 246 // does the ambiguity check anyway. Therefore, passing the name to daemon would be enough. 247 return n.backend.ConnectContainerToNetwork(connect.Container, vars["id"], connect.EndpointConfig) 248 } 249 250 func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 251 if err := httputils.ParseForm(r); err != nil { 252 return err 253 } 254 255 var disconnect types.NetworkDisconnect 256 if err := httputils.ReadJSON(r, &disconnect); err != nil { 257 return err 258 } 259 260 return n.backend.DisconnectContainerFromNetwork(disconnect.Container, vars["id"], disconnect.Force) 261 } 262 263 func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 264 if err := httputils.ParseForm(r); err != nil { 265 return err 266 } 267 268 nw, err := n.findUniqueNetwork(vars["id"]) 269 if err != nil { 270 return err 271 } 272 if nw.Scope == "swarm" { 273 if err = n.cluster.RemoveNetwork(nw.ID); err != nil { 274 return err 275 } 276 } else { 277 if err := n.backend.DeleteNetwork(nw.ID); err != nil { 278 return err 279 } 280 } 281 w.WriteHeader(http.StatusNoContent) 282 return nil 283 } 284 285 func (n *networkRouter) postNetworksPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 286 if err := httputils.ParseForm(r); err != nil { 287 return err 288 } 289 290 pruneFilters, err := filters.FromJSON(r.Form.Get("filters")) 291 if err != nil { 292 return err 293 } 294 295 pruneReport, err := n.backend.NetworksPrune(ctx, pruneFilters) 296 if err != nil { 297 return err 298 } 299 return httputils.WriteJSON(w, http.StatusOK, pruneReport) 300 } 301 302 // findUniqueNetwork will search network across different scopes (both local and swarm). 303 // NOTE: This findUniqueNetwork is different from FindNetwork in the daemon. 304 // In case multiple networks have duplicate names, return error. 305 // First find based on full ID, return immediately once one is found. 306 // If a network appears both in swarm and local, assume it is in local first 307 // For full name and partial ID, save the result first, and process later 308 // in case multiple records was found based on the same term 309 // TODO (yongtang): should we wrap with version here for backward compatibility? 310 func (n *networkRouter) findUniqueNetwork(term string) (types.NetworkResource, error) { 311 listByFullName := map[string]types.NetworkResource{} 312 listByPartialID := map[string]types.NetworkResource{} 313 314 filter := filters.NewArgs(filters.Arg("idOrName", term)) 315 networks, _ := n.backend.GetNetworks(filter, backend.NetworkListConfig{Detailed: true}) 316 for _, nw := range networks { 317 if nw.ID == term { 318 return nw, nil 319 } 320 if nw.Name == term && !nw.Ingress { 321 // No need to check the ID collision here as we are still in 322 // local scope and the network ID is unique in this scope. 323 listByFullName[nw.ID] = nw 324 } 325 if strings.HasPrefix(nw.ID, term) { 326 // No need to check the ID collision here as we are still in 327 // local scope and the network ID is unique in this scope. 328 listByPartialID[nw.ID] = nw 329 } 330 } 331 332 networks, _ = n.cluster.GetNetworks(filter) 333 for _, nw := range networks { 334 if nw.ID == term { 335 return nw, nil 336 } 337 if nw.Name == term { 338 // Check the ID collision as we are in swarm scope here, and 339 // the map (of the listByFullName) may have already had a 340 // network with the same ID (from local scope previously) 341 if _, ok := listByFullName[nw.ID]; !ok { 342 listByFullName[nw.ID] = nw 343 } 344 } 345 if strings.HasPrefix(nw.ID, term) { 346 // Check the ID collision as we are in swarm scope here, and 347 // the map (of the listByPartialID) may have already had a 348 // network with the same ID (from local scope previously) 349 if _, ok := listByPartialID[nw.ID]; !ok { 350 listByPartialID[nw.ID] = nw 351 } 352 } 353 } 354 355 // Find based on full name, returns true only if no duplicates 356 if len(listByFullName) == 1 { 357 for _, v := range listByFullName { 358 return v, nil 359 } 360 } 361 if len(listByFullName) > 1 { 362 return types.NetworkResource{}, errdefs.InvalidParameter(errors.Errorf("network %s is ambiguous (%d matches found based on name)", term, len(listByFullName))) 363 } 364 365 // Find based on partial ID, returns true only if no duplicates 366 if len(listByPartialID) == 1 { 367 for _, v := range listByPartialID { 368 return v, nil 369 } 370 } 371 if len(listByPartialID) > 1 { 372 return types.NetworkResource{}, errdefs.InvalidParameter(errors.Errorf("network %s is ambiguous (%d matches found based on ID prefix)", term, len(listByPartialID))) 373 } 374 375 return types.NetworkResource{}, errdefs.NotFound(libnetwork.ErrNoSuchNetwork(term)) 376 }