diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/api/handlers/compat/networks.go | 301 | ||||
-rw-r--r-- | pkg/api/handlers/compat/swagger.go | 28 | ||||
-rw-r--r-- | pkg/api/server/register_networks.go | 90 | ||||
-rw-r--r-- | pkg/api/server/server.go | 8 | ||||
-rw-r--r-- | pkg/api/tags.yaml | 2 | ||||
-rw-r--r-- | pkg/domain/entities/generate.go | 6 | ||||
-rw-r--r-- | pkg/domain/infra/abi/generate.go | 6 | ||||
-rw-r--r-- | pkg/domain/infra/abi/system_varlink.go | 2 | ||||
-rw-r--r-- | pkg/network/network.go | 18 |
9 files changed, 456 insertions, 5 deletions
diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go new file mode 100644 index 000000000..ceeae30fb --- /dev/null +++ b/pkg/api/handlers/compat/networks.go @@ -0,0 +1,301 @@ +package compat + +import ( + "encoding/json" + "net" + "net/http" + "os" + "syscall" + "time" + + "github.com/containernetworking/cni/libcni" + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/api/handlers/utils" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/domain/infra/abi" + "github.com/containers/libpod/pkg/network" + "github.com/docker/docker/api/types" + dockerNetwork "github.com/docker/docker/api/types/network" + "github.com/gorilla/schema" + "github.com/pkg/errors" +) + +type CompatInspectNetwork struct { + types.NetworkResource +} + +func InspectNetwork(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + + // FYI scope and version are currently unused but are described by the API + // Leaving this for if/when we have to enable these + //query := struct { + // scope string + // verbose bool + //}{ + // // override any golang type defaults + //} + //decoder := r.Context().Value("decoder").(*schema.Decoder) + //if err := decoder.Decode(&query, r.URL.Query()); err != nil { + // utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String())) + // return + //} + config, err := runtime.GetConfig() + if err != nil { + utils.InternalServerError(w, err) + return + } + name := utils.GetName(r) + _, err = network.InspectNetwork(config, name) + if err != nil { + // TODO our network package does not distinguish between not finding a + // specific network vs not being able to read it + utils.InternalServerError(w, err) + return + } + report, err := getNetworkResourceByName(name, runtime) + if err != nil { + utils.InternalServerError(w, err) + return + } + utils.WriteResponse(w, http.StatusOK, report) +} + +func getNetworkResourceByName(name string, runtime *libpod.Runtime) (*types.NetworkResource, error) { + var ( + ipamConfigs []dockerNetwork.IPAMConfig + ) + config, err := runtime.GetConfig() + if err != nil { + return nil, err + } + containerEndpoints := map[string]types.EndpointResource{} + // Get the network path so we can get created time + networkConfigPath, err := network.GetCNIConfigPathByName(config, name) + if err != nil { + return nil, err + } + f, err := os.Stat(networkConfigPath) + if err != nil { + return nil, err + } + stat := f.Sys().(*syscall.Stat_t) + cons, err := runtime.GetAllContainers() + if err != nil { + return nil, err + } + conf, err := libcni.ConfListFromFile(networkConfigPath) + if err != nil { + return nil, err + } + + // No Bridge plugin means we bail + bridge, err := genericPluginsToBridge(conf.Plugins, network.DefaultNetworkDriver) + if err != nil { + return nil, err + } + for _, outer := range bridge.IPAM.Ranges { + for _, n := range outer { + ipamConfig := dockerNetwork.IPAMConfig{ + Subnet: n.Subnet, + Gateway: n.Gateway, + } + ipamConfigs = append(ipamConfigs, ipamConfig) + } + } + + for _, con := range cons { + data, err := con.Inspect(false) + if err != nil { + return nil, err + } + if netData, ok := data.NetworkSettings.Networks[name]; ok { + containerEndpoint := types.EndpointResource{ + Name: netData.NetworkID, + EndpointID: netData.EndpointID, + MacAddress: netData.MacAddress, + IPv4Address: netData.IPAddress, + IPv6Address: netData.GlobalIPv6Address, + } + containerEndpoints[con.ID()] = containerEndpoint + } + } + report := types.NetworkResource{ + Name: name, + ID: "", + Created: time.Unix(stat.Ctim.Sec, stat.Ctim.Nsec), + Scope: "", + Driver: network.DefaultNetworkDriver, + EnableIPv6: false, + IPAM: dockerNetwork.IPAM{ + Driver: "default", + Options: nil, + Config: ipamConfigs, + }, + Internal: false, + Attachable: false, + Ingress: false, + ConfigFrom: dockerNetwork.ConfigReference{}, + ConfigOnly: false, + Containers: containerEndpoints, + Options: nil, + Labels: nil, + Peers: nil, + Services: nil, + } + return &report, nil +} + +func genericPluginsToBridge(plugins []*libcni.NetworkConfig, pluginType string) (network.HostLocalBridge, error) { + var bridge network.HostLocalBridge + generic, err := findPluginByName(plugins, pluginType) + if err != nil { + return bridge, err + } + err = json.Unmarshal(generic, &bridge) + return bridge, err +} + +func findPluginByName(plugins []*libcni.NetworkConfig, pluginType string) ([]byte, error) { + for _, p := range plugins { + if pluginType == p.Network.Type { + return p.Bytes, nil + } + } + return nil, errors.New("unable to find bridge plugin") +} + +func ListNetworks(w http.ResponseWriter, r *http.Request) { + var ( + reports []*types.NetworkResource + ) + runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value("decoder").(*schema.Decoder) + query := struct { + Filters map[string][]string `schema:"filters"` + }{ + // override any golang type defaults + } + if err := decoder.Decode(&query, r.URL.Query()); err != nil { + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String())) + return + } + config, err := runtime.GetConfig() + if err != nil { + utils.InternalServerError(w, err) + return + } + // TODO remove when filters are implemented + if len(query.Filters) > 0 { + utils.InternalServerError(w, errors.New("filters for listing networks is not implemented")) + return + } + netNames, err := network.GetNetworkNamesFromFileSystem(config) + if err != nil { + utils.InternalServerError(w, err) + return + } + for _, name := range netNames { + report, err := getNetworkResourceByName(name, runtime) + if err != nil { + utils.InternalServerError(w, err) + } + reports = append(reports, report) + } + utils.WriteResponse(w, http.StatusOK, reports) +} + +func CreateNetwork(w http.ResponseWriter, r *http.Request) { + var ( + name string + networkCreate types.NetworkCreateRequest + ) + runtime := r.Context().Value("runtime").(*libpod.Runtime) + if err := json.NewDecoder(r.Body).Decode(&networkCreate); err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) + return + } + + if len(networkCreate.Name) > 0 { + name = networkCreate.Name + } + // At present I think we should just suport the bridge driver + // and allow demand to make us consider more + if networkCreate.Driver != network.DefaultNetworkDriver { + utils.InternalServerError(w, errors.New("network create only supports the bridge driver")) + return + } + ncOptions := entities.NetworkCreateOptions{ + Driver: network.DefaultNetworkDriver, + Internal: networkCreate.Internal, + } + if networkCreate.IPAM != nil && networkCreate.IPAM.Config != nil { + if len(networkCreate.IPAM.Config) > 1 { + utils.InternalServerError(w, errors.New("compat network create can only support one IPAM config")) + return + } + + if len(networkCreate.IPAM.Config[0].Subnet) > 0 { + _, subnet, err := net.ParseCIDR(networkCreate.IPAM.Config[0].Subnet) + if err != nil { + utils.InternalServerError(w, err) + return + } + ncOptions.Subnet = *subnet + } + if len(networkCreate.IPAM.Config[0].Gateway) > 0 { + ncOptions.Gateway = net.ParseIP(networkCreate.IPAM.Config[0].Gateway) + } + if len(networkCreate.IPAM.Config[0].IPRange) > 0 { + _, IPRange, err := net.ParseCIDR(networkCreate.IPAM.Config[0].IPRange) + if err != nil { + utils.InternalServerError(w, err) + return + } + ncOptions.Range = *IPRange + } + } + ce := abi.ContainerEngine{Libpod: runtime} + _, err := ce.NetworkCreate(r.Context(), name, ncOptions) + if err != nil { + utils.InternalServerError(w, err) + } + report := types.NetworkCreate{ + CheckDuplicate: networkCreate.CheckDuplicate, + Driver: networkCreate.Driver, + Scope: networkCreate.Scope, + EnableIPv6: networkCreate.EnableIPv6, + IPAM: networkCreate.IPAM, + Internal: networkCreate.Internal, + Attachable: networkCreate.Attachable, + Ingress: networkCreate.Ingress, + ConfigOnly: networkCreate.ConfigOnly, + ConfigFrom: networkCreate.ConfigFrom, + Options: networkCreate.Options, + Labels: networkCreate.Labels, + } + utils.WriteResponse(w, http.StatusOK, report) +} + +func RemoveNetwork(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + config, err := runtime.GetConfig() + if err != nil { + utils.InternalServerError(w, err) + return + } + name := utils.GetName(r) + exists, err := network.Exists(config, name) + if err != nil { + utils.InternalServerError(w, err) + return + } + if !exists { + utils.Error(w, "network not found", http.StatusNotFound, err) + return + } + if err := network.RemoveNetwork(config, name); err != nil { + utils.InternalServerError(w, err) + } + utils.WriteResponse(w, http.StatusNoContent, "") +} diff --git a/pkg/api/handlers/compat/swagger.go b/pkg/api/handlers/compat/swagger.go index ce83aa32f..dc94a7ebd 100644 --- a/pkg/api/handlers/compat/swagger.go +++ b/pkg/api/handlers/compat/swagger.go @@ -3,6 +3,7 @@ package compat import ( "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/storage/pkg/archive" + "github.com/docker/docker/api/types" ) // Create container @@ -35,3 +36,30 @@ type swagChangesResponse struct { Changes []archive.Change } } + +// Network inspect +// swagger:response CompatNetworkInspect +type swagCompatNetworkInspect struct { + // in:body + Body types.NetworkResource +} + +// Network list +// swagger:response CompatNetworkList +type swagCompatNetworkList struct { + // in:body + Body []types.NetworkResource +} + +// Network create +// swagger:model NetworkCreateRequest +type NetworkCreateRequest struct { + types.NetworkCreateRequest +} + +// Network create +// swagger:response CompatNetworkCreate +type swagCompatNetworkCreateResponse struct { + // in:body + Body struct{ types.NetworkCreate } +} diff --git a/pkg/api/server/register_networks.go b/pkg/api/server/register_networks.go index b1189c1f4..2c60b2b27 100644 --- a/pkg/api/server/register_networks.go +++ b/pkg/api/server/register_networks.go @@ -3,11 +3,96 @@ package server import ( "net/http" + "github.com/containers/libpod/pkg/api/handlers/compat" "github.com/containers/libpod/pkg/api/handlers/libpod" "github.com/gorilla/mux" ) func (s *APIServer) registerNetworkHandlers(r *mux.Router) error { + // swagger:operation DELETE /networks/{name} compat compatRemoveNetwork + // --- + // tags: + // - networks (compat) + // summary: Remove a network + // description: Remove a network + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: the name of the network + // produces: + // - application/json + // responses: + // 204: + // description: no error + // 404: + // $ref: "#/responses/NoSuchNetwork" + // 500: + // $ref: "#/responses/InternalError" + r.HandleFunc(VersionedPath("/networks/{name}"), s.APIHandler(compat.RemoveNetwork)).Methods(http.MethodDelete) + r.HandleFunc("/networks/{name}", s.APIHandler(compat.RemoveNetwork)).Methods(http.MethodDelete) + // swagger:operation GET /networks/{name}/json compat compatInspectNetwork + // --- + // tags: + // - networks (compat) + // summary: Inspect a network + // description: Display low level configuration network + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: the name of the network + // produces: + // - application/json + // responses: + // 200: + // $ref: "#/responses/CompatNetworkInspect" + // 404: + // $ref: "#/responses/NoSuchNetwork" + // 500: + // $ref: "#/responses/InternalError" + r.HandleFunc(VersionedPath("/networks/{name}/json"), s.APIHandler(compat.InspectNetwork)).Methods(http.MethodGet) + r.HandleFunc("/networks/{name}/json", s.APIHandler(compat.InspectNetwork)).Methods(http.MethodGet) + // swagger:operation GET /networks/json compat compatListNetwork + // --- + // tags: + // - networks (compat) + // summary: List networks + // description: Display summary of network configurations + // produces: + // - application/json + // responses: + // 200: + // $ref: "#/responses/CompatNetworkList" + // 500: + // $ref: "#/responses/InternalError" + r.HandleFunc(VersionedPath("/networks/json"), s.APIHandler(compat.ListNetworks)).Methods(http.MethodGet) + r.HandleFunc("/networks", s.APIHandler(compat.ListNetworks)).Methods(http.MethodGet) + // swagger:operation POST /networks/create compat compatCreateNetwork + // --- + // tags: + // - networks (compat) + // summary: Create network + // description: Create a network configuration + // produces: + // - application/json + // parameters: + // - in: body + // name: create + // description: attributes for creating a container + // schema: + // $ref: "#/definitions/NetworkCreateRequest" + // responses: + // 200: + // $ref: "#/responses/CompatNetworkCreate" + // 400: + // $ref: "#/responses/BadParamError" + // 500: + // $ref: "#/responses/InternalError" + r.HandleFunc(VersionedPath("/networks/create"), s.APIHandler(compat.CreateNetwork)).Methods(http.MethodPost) + r.HandleFunc("/networks/create", s.APIHandler(compat.CreateNetwork)).Methods(http.MethodPost) // swagger:operation DELETE /libpod/networks/{name} libpod libpodRemoveNetwork // --- // tags: @@ -33,6 +118,11 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchNetwork" // 500: // $ref: "#/responses/InternalError" + + /* + Libpod + */ + r.HandleFunc(VersionedPath("/libpod/networks/{name}"), s.APIHandler(libpod.RemoveNetwork)).Methods(http.MethodDelete) // swagger:operation GET /libpod/networks/{name}/json libpod libpodInspectNetwork // --- diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index d39528f45..9cbc66e87 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -92,6 +92,14 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li }, ) + router.MethodNotAllowedHandler = http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + // We can track user errors... + logrus.Infof("Failed Request: (%d:%s) for %s:'%s'", http.StatusMethodNotAllowed, http.StatusText(http.StatusMethodNotAllowed), r.Method, r.URL.String()) + http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + }, + ) + for _, fn := range []func(*mux.Router) error{ server.registerAuthHandlers, server.registerContainersHandlers, diff --git a/pkg/api/tags.yaml b/pkg/api/tags.yaml index 1ffb5e940..f86f8dbea 100644 --- a/pkg/api/tags.yaml +++ b/pkg/api/tags.yaml @@ -21,5 +21,7 @@ tags: description: Actions related to exec for the compatibility endpoints - name: images (compat) description: Actions related to images for the compatibility endpoints + - name: networks (compat) + description: Actions related to compatibility networks - name: system (compat) description: Actions related to Podman and compatibility engines diff --git a/pkg/domain/entities/generate.go b/pkg/domain/entities/generate.go index edd217615..68a42d897 100644 --- a/pkg/domain/entities/generate.go +++ b/pkg/domain/entities/generate.go @@ -14,6 +14,12 @@ type GenerateSystemdOptions struct { RestartPolicy string // StopTimeout - time when stopping the container. StopTimeout *uint + // ContainerPrefix - systemd unit name prefix for containers + ContainerPrefix string + // PodPrefix - systemd unit name prefix for pods + PodPrefix string + // Separator - systemd unit name seperator between name/id and prefix + Separator string } // GenerateSystemdReport diff --git a/pkg/domain/infra/abi/generate.go b/pkg/domain/infra/abi/generate.go index be5d452bd..abb5e2911 100644 --- a/pkg/domain/infra/abi/generate.go +++ b/pkg/domain/infra/abi/generate.go @@ -159,14 +159,14 @@ func (ic *ContainerEngine) generateSystemdgenContainerInfo(nameOrID string, pod func generateServiceName(ctr *libpod.Container, pod *libpod.Pod, options entities.GenerateSystemdOptions) (string, string) { var kind, name, ctrName string if pod == nil { - kind = "container" + kind = options.ContainerPrefix //defaults to container name = ctr.ID() if options.Name { name = ctr.Name() } ctrName = name } else { - kind = "pod" + kind = options.PodPrefix //defaults to pod name = pod.ID() ctrName = ctr.ID() if options.Name { @@ -174,7 +174,7 @@ func generateServiceName(ctr *libpod.Container, pod *libpod.Pod, options entitie ctrName = ctr.Name() } } - return ctrName, fmt.Sprintf("%s-%s", kind, name) + return ctrName, fmt.Sprintf("%s%s%s", kind, options.Separator, name) } func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) { diff --git a/pkg/domain/infra/abi/system_varlink.go b/pkg/domain/infra/abi/system_varlink.go index c0144babe..4dc766f52 100644 --- a/pkg/domain/infra/abi/system_varlink.go +++ b/pkg/domain/infra/abi/system_varlink.go @@ -39,7 +39,7 @@ func (ic *ContainerEngine) VarlinkService(_ context.Context, opts entities.Servi if err = service.Listen(opts.URI, opts.Timeout); err != nil { switch err.(type) { case varlink.ServiceTimeoutError: - logrus.Infof("varlink service expired (use --timeout to increase session time beyond %s ms, 0 means never timeout)", opts.Timeout.String()) + logrus.Infof("varlink service expired (use --time to increase session time beyond %s ms, 0 means never timeout)", opts.Timeout.String()) return nil default: return errors.Wrapf(err, "unable to start varlink service") diff --git a/pkg/network/network.go b/pkg/network/network.go index 5e9062019..526ee92d8 100644 --- a/pkg/network/network.go +++ b/pkg/network/network.go @@ -13,8 +13,11 @@ import ( "github.com/sirupsen/logrus" ) +// DefaultNetworkDriver is the default network type used +var DefaultNetworkDriver string = "bridge" + // SupportedNetworkDrivers describes the list of supported drivers -var SupportedNetworkDrivers = []string{"bridge"} +var SupportedNetworkDrivers = []string{DefaultNetworkDriver} // IsSupportedDriver checks if the user provided driver is supported func IsSupportedDriver(driver string) error { @@ -191,3 +194,16 @@ func InspectNetwork(config *config.Config, name string) (map[string]interface{}, err = json.Unmarshal(b, &rawList) return rawList, err } + +// Exists says whether a given network exists or not; it meant +// specifically for restful reponses so 404s can be used +func Exists(config *config.Config, name string) (bool, error) { + _, err := ReadRawCNIConfByName(config, name) + if err != nil { + if errors.Cause(err) == ErrNetworkNotFound { + return false, nil + } + return false, err + } + return true, nil +} |