github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/api/handlers/compat/networks.go (about) 1 package compat 2 3 import ( 4 "encoding/json" 5 "net" 6 "net/http" 7 "os" 8 "strings" 9 "syscall" 10 "time" 11 12 "github.com/containernetworking/cni/libcni" 13 "github.com/containers/podman/v2/libpod" 14 "github.com/containers/podman/v2/libpod/define" 15 "github.com/containers/podman/v2/libpod/network" 16 "github.com/containers/podman/v2/pkg/api/handlers/utils" 17 "github.com/containers/podman/v2/pkg/domain/entities" 18 "github.com/containers/podman/v2/pkg/domain/infra/abi" 19 "github.com/docker/docker/api/types" 20 dockerNetwork "github.com/docker/docker/api/types/network" 21 "github.com/gorilla/schema" 22 "github.com/pkg/errors" 23 "github.com/sirupsen/logrus" 24 ) 25 26 func InspectNetwork(w http.ResponseWriter, r *http.Request) { 27 runtime := r.Context().Value("runtime").(*libpod.Runtime) 28 29 // FYI scope and version are currently unused but are described by the API 30 // Leaving this for if/when we have to enable these 31 // query := struct { 32 // scope string 33 // verbose bool 34 // }{ 35 // // override any golang type defaults 36 // } 37 // decoder := r.Context().Value("decoder").(*schema.Decoder) 38 // if err := decoder.Decode(&query, r.URL.Query()); err != nil { 39 // utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 40 // return 41 // } 42 config, err := runtime.GetConfig() 43 if err != nil { 44 utils.InternalServerError(w, err) 45 return 46 } 47 name := utils.GetName(r) 48 _, err = network.InspectNetwork(config, name) 49 if err != nil { 50 utils.NetworkNotFound(w, name, err) 51 return 52 } 53 report, err := getNetworkResourceByName(name, runtime) 54 if err != nil { 55 utils.InternalServerError(w, err) 56 return 57 } 58 utils.WriteResponse(w, http.StatusOK, report) 59 } 60 61 func getNetworkResourceByName(name string, runtime *libpod.Runtime) (*types.NetworkResource, error) { 62 var ( 63 ipamConfigs []dockerNetwork.IPAMConfig 64 ) 65 config, err := runtime.GetConfig() 66 if err != nil { 67 return nil, err 68 } 69 containerEndpoints := map[string]types.EndpointResource{} 70 // Get the network path so we can get created time 71 networkConfigPath, err := network.GetCNIConfigPathByName(config, name) 72 if err != nil { 73 return nil, err 74 } 75 f, err := os.Stat(networkConfigPath) 76 if err != nil { 77 return nil, err 78 } 79 stat := f.Sys().(*syscall.Stat_t) 80 cons, err := runtime.GetAllContainers() 81 if err != nil { 82 return nil, err 83 } 84 conf, err := libcni.ConfListFromFile(networkConfigPath) 85 if err != nil { 86 return nil, err 87 } 88 89 // No Bridge plugin means we bail 90 bridge, err := genericPluginsToBridge(conf.Plugins, network.DefaultNetworkDriver) 91 if err != nil { 92 return nil, err 93 } 94 for _, outer := range bridge.IPAM.Ranges { 95 for _, n := range outer { 96 ipamConfig := dockerNetwork.IPAMConfig{ 97 Subnet: n.Subnet, 98 Gateway: n.Gateway, 99 } 100 ipamConfigs = append(ipamConfigs, ipamConfig) 101 } 102 } 103 104 for _, con := range cons { 105 data, err := con.Inspect(false) 106 if err != nil { 107 return nil, err 108 } 109 if netData, ok := data.NetworkSettings.Networks[name]; ok { 110 containerEndpoint := types.EndpointResource{ 111 Name: netData.NetworkID, 112 EndpointID: netData.EndpointID, 113 MacAddress: netData.MacAddress, 114 IPv4Address: netData.IPAddress, 115 IPv6Address: netData.GlobalIPv6Address, 116 } 117 containerEndpoints[con.ID()] = containerEndpoint 118 } 119 } 120 report := types.NetworkResource{ 121 Name: name, 122 ID: name, 123 Created: time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)), // nolint: unconvert 124 Scope: "", 125 Driver: network.DefaultNetworkDriver, 126 EnableIPv6: false, 127 IPAM: dockerNetwork.IPAM{ 128 Driver: "default", 129 Options: nil, 130 Config: ipamConfigs, 131 }, 132 Internal: false, 133 Attachable: false, 134 Ingress: false, 135 ConfigFrom: dockerNetwork.ConfigReference{}, 136 ConfigOnly: false, 137 Containers: containerEndpoints, 138 Options: nil, 139 Labels: nil, 140 Peers: nil, 141 Services: nil, 142 } 143 return &report, nil 144 } 145 146 func genericPluginsToBridge(plugins []*libcni.NetworkConfig, pluginType string) (network.HostLocalBridge, error) { 147 var bridge network.HostLocalBridge 148 generic, err := findPluginByName(plugins, pluginType) 149 if err != nil { 150 return bridge, err 151 } 152 err = json.Unmarshal(generic, &bridge) 153 return bridge, err 154 } 155 156 func findPluginByName(plugins []*libcni.NetworkConfig, pluginType string) ([]byte, error) { 157 for _, p := range plugins { 158 if pluginType == p.Network.Type { 159 return p.Bytes, nil 160 } 161 } 162 return nil, errors.New("unable to find bridge plugin") 163 } 164 165 func ListNetworks(w http.ResponseWriter, r *http.Request) { 166 runtime := r.Context().Value("runtime").(*libpod.Runtime) 167 decoder := r.Context().Value("decoder").(*schema.Decoder) 168 query := struct { 169 Filters map[string][]string `schema:"filters"` 170 }{ 171 // override any golang type defaults 172 } 173 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 174 utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 175 return 176 } 177 config, err := runtime.GetConfig() 178 if err != nil { 179 utils.InternalServerError(w, err) 180 return 181 } 182 183 filterNames, nameFilterExists := query.Filters["name"] 184 // TODO remove when filters are implemented 185 if (!nameFilterExists && len(query.Filters) > 0) || len(query.Filters) > 1 { 186 utils.InternalServerError(w, errors.New("only the name filter for listing networks is implemented")) 187 return 188 } 189 netNames, err := network.GetNetworkNamesFromFileSystem(config) 190 if err != nil { 191 utils.InternalServerError(w, err) 192 return 193 } 194 195 // filter by name 196 if nameFilterExists { 197 names := []string{} 198 for _, name := range netNames { 199 for _, filter := range filterNames { 200 if strings.Contains(name, filter) { 201 names = append(names, name) 202 break 203 } 204 } 205 } 206 netNames = names 207 } 208 209 reports := make([]*types.NetworkResource, 0, len(netNames)) 210 logrus.Errorf("netNames: %q", strings.Join(netNames, ", ")) 211 for _, name := range netNames { 212 report, err := getNetworkResourceByName(name, runtime) 213 if err != nil { 214 utils.InternalServerError(w, err) 215 return 216 } 217 reports = append(reports, report) 218 } 219 utils.WriteResponse(w, http.StatusOK, reports) 220 } 221 222 func CreateNetwork(w http.ResponseWriter, r *http.Request) { 223 var ( 224 name string 225 networkCreate types.NetworkCreateRequest 226 ) 227 runtime := r.Context().Value("runtime").(*libpod.Runtime) 228 if err := json.NewDecoder(r.Body).Decode(&networkCreate); err != nil { 229 utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) 230 return 231 } 232 233 if len(networkCreate.Name) > 0 { 234 name = networkCreate.Name 235 } 236 if len(networkCreate.Driver) < 1 { 237 networkCreate.Driver = network.DefaultNetworkDriver 238 } 239 // At present I think we should just support the bridge driver 240 // and allow demand to make us consider more 241 if networkCreate.Driver != network.DefaultNetworkDriver { 242 utils.InternalServerError(w, errors.New("network create only supports the bridge driver")) 243 return 244 } 245 ncOptions := entities.NetworkCreateOptions{ 246 Driver: network.DefaultNetworkDriver, 247 Internal: networkCreate.Internal, 248 } 249 if networkCreate.IPAM != nil && networkCreate.IPAM.Config != nil { 250 if len(networkCreate.IPAM.Config) > 1 { 251 utils.InternalServerError(w, errors.New("compat network create can only support one IPAM config")) 252 return 253 } 254 255 if len(networkCreate.IPAM.Config[0].Subnet) > 0 { 256 _, subnet, err := net.ParseCIDR(networkCreate.IPAM.Config[0].Subnet) 257 if err != nil { 258 utils.InternalServerError(w, err) 259 return 260 } 261 ncOptions.Subnet = *subnet 262 } 263 if len(networkCreate.IPAM.Config[0].Gateway) > 0 { 264 ncOptions.Gateway = net.ParseIP(networkCreate.IPAM.Config[0].Gateway) 265 } 266 if len(networkCreate.IPAM.Config[0].IPRange) > 0 { 267 _, IPRange, err := net.ParseCIDR(networkCreate.IPAM.Config[0].IPRange) 268 if err != nil { 269 utils.InternalServerError(w, err) 270 return 271 } 272 ncOptions.Range = *IPRange 273 } 274 } 275 ce := abi.ContainerEngine{Libpod: runtime} 276 if _, err := ce.NetworkCreate(r.Context(), name, ncOptions); err != nil { 277 utils.InternalServerError(w, err) 278 return 279 } 280 281 body := struct { 282 Id string 283 Warning []string 284 }{ 285 Id: name, 286 } 287 utils.WriteResponse(w, http.StatusCreated, body) 288 } 289 290 func RemoveNetwork(w http.ResponseWriter, r *http.Request) { 291 runtime := r.Context().Value("runtime").(*libpod.Runtime) 292 ic := abi.ContainerEngine{Libpod: runtime} 293 294 query := struct { 295 Force bool `schema:"force"` 296 }{ 297 // This is where you can override the golang default value for one of fields 298 } 299 300 decoder := r.Context().Value("decoder").(*schema.Decoder) 301 if err := decoder.Decode(&query, r.URL.Query()); err != nil { 302 utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) 303 return 304 } 305 306 options := entities.NetworkRmOptions{ 307 Force: query.Force, 308 } 309 310 name := utils.GetName(r) 311 reports, err := ic.NetworkRm(r.Context(), []string{name}, options) 312 if err != nil { 313 utils.Error(w, "remove Network failed", http.StatusInternalServerError, err) 314 return 315 } 316 if len(reports) == 0 { 317 utils.Error(w, "remove Network failed", http.StatusInternalServerError, errors.Errorf("internal error")) 318 return 319 } 320 report := reports[0] 321 if report.Err != nil { 322 if errors.Cause(report.Err) == define.ErrNoSuchNetwork { 323 utils.Error(w, "network not found", http.StatusNotFound, define.ErrNoSuchNetwork) 324 return 325 } 326 utils.InternalServerError(w, report.Err) 327 return 328 } 329 330 utils.WriteResponse(w, http.StatusNoContent, "") 331 } 332 333 // Connect adds a container to a network 334 func Connect(w http.ResponseWriter, r *http.Request) { 335 runtime := r.Context().Value("runtime").(*libpod.Runtime) 336 337 var ( 338 aliases []string 339 netConnect types.NetworkConnect 340 ) 341 if err := json.NewDecoder(r.Body).Decode(&netConnect); err != nil { 342 utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) 343 return 344 } 345 name := utils.GetName(r) 346 if netConnect.EndpointConfig != nil { 347 if netConnect.EndpointConfig.Aliases != nil { 348 aliases = netConnect.EndpointConfig.Aliases 349 } 350 } 351 err := runtime.ConnectContainerToNetwork(netConnect.Container, name, aliases) 352 if err != nil { 353 if errors.Cause(err) == define.ErrNoSuchCtr { 354 utils.ContainerNotFound(w, netConnect.Container, err) 355 return 356 } 357 if errors.Cause(err) == define.ErrNoSuchNetwork { 358 utils.Error(w, "network not found", http.StatusNotFound, err) 359 return 360 } 361 utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) 362 return 363 } 364 utils.WriteResponse(w, http.StatusOK, "OK") 365 } 366 367 // Disconnect removes a container from a network 368 func Disconnect(w http.ResponseWriter, r *http.Request) { 369 runtime := r.Context().Value("runtime").(*libpod.Runtime) 370 371 var netDisconnect types.NetworkDisconnect 372 if err := json.NewDecoder(r.Body).Decode(&netDisconnect); err != nil { 373 utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) 374 return 375 } 376 377 name := utils.GetName(r) 378 err := runtime.DisconnectContainerFromNetwork(netDisconnect.Container, name, netDisconnect.Force) 379 if err != nil { 380 if errors.Cause(err) == define.ErrNoSuchCtr { 381 utils.Error(w, "container not found", http.StatusNotFound, err) 382 return 383 } 384 if errors.Cause(err) == define.ErrNoSuchNetwork { 385 utils.Error(w, "network not found", http.StatusNotFound, err) 386 return 387 } 388 utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) 389 return 390 } 391 utils.WriteResponse(w, http.StatusOK, "OK") 392 }