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