diff options
Diffstat (limited to 'pkg')
41 files changed, 1163 insertions, 299 deletions
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index 239e41af4..cea4bd0f6 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "encoding/json" "fmt" + "io" "net/http" "strconv" "strings" @@ -295,7 +296,9 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) { }() w.WriteHeader(http.StatusOK) - var builder strings.Builder + + var frame strings.Builder + header := make([]byte, 8) for ok := true; ok; ok = query.Follow { for line := range logChannel { if _, found := r.URL.Query()["until"]; found { @@ -304,10 +307,8 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) { } } - // Reset variables we're ready to loop again - builder.Reset() - header := [8]byte{} - + // Reset buffer we're ready to loop again + frame.Reset() switch line.Device { case "stdout": if !query.Stdout { @@ -327,17 +328,17 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) { } if query.Timestamps { - builder.WriteString(line.Time.Format(time.RFC3339)) - builder.WriteRune(' ') + frame.WriteString(line.Time.Format(time.RFC3339)) + frame.WriteString(" ") } - builder.WriteString(line.Msg) - // Build header and output entry - binary.BigEndian.PutUint32(header[4:], uint32(len(header)+builder.Len())) - if _, err := w.Write(header[:]); err != nil { + frame.WriteString(line.Msg) + + binary.BigEndian.PutUint32(header[4:], uint32(frame.Len())) + if _, err := w.Write(header[0:8]); err != nil { log.Errorf("unable to write log output header: %q", err) } - if _, err := fmt.Fprint(w, builder.String()); err != nil { - log.Errorf("unable to write builder string: %q", err) + if _, err := io.WriteString(w, frame.String()); err != nil { + log.Errorf("unable to write frame string: %q", err) } if flusher, ok := w.(http.Flusher); ok { flusher.Flush() diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index e208e6ddc..e9d8fd719 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -226,6 +226,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { id, _, err := runtime.Build(r.Context(), buildOptions, query.Dockerfile) if err != nil { utils.InternalServerError(w, err) + return } // Find image ID that was built... 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/handlers/libpod/healthcheck.go b/pkg/api/handlers/libpod/healthcheck.go index 6eb2ab0e3..0ca3574b7 100644 --- a/pkg/api/handlers/libpod/healthcheck.go +++ b/pkg/api/handlers/libpod/healthcheck.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/api/handlers/utils" ) @@ -12,32 +13,27 @@ func RunHealthCheck(w http.ResponseWriter, r *http.Request) { name := utils.GetName(r) status, err := runtime.HealthCheck(name) if err != nil { - if status == libpod.HealthCheckContainerNotFound { + if status == define.HealthCheckContainerNotFound { utils.ContainerNotFound(w, name, err) return } - if status == libpod.HealthCheckNotDefined { + if status == define.HealthCheckNotDefined { utils.Error(w, "no healthcheck defined", http.StatusConflict, err) return } - if status == libpod.HealthCheckContainerStopped { + if status == define.HealthCheckContainerStopped { utils.Error(w, "container not running", http.StatusConflict, err) return } utils.InternalServerError(w, err) return } - ctr, err := runtime.LookupContainer(name) - if err != nil { - utils.InternalServerError(w, err) - return + hcStatus := define.HealthCheckUnhealthy + if status == define.HealthCheckSuccess { + hcStatus = define.HealthCheckHealthy } - - hcLog, err := ctr.GetHealthCheckLog() - if err != nil { - utils.InternalServerError(w, err) - return + report := define.HealthCheckResults{ + Status: hcStatus, } - - utils.WriteResponse(w, http.StatusOK, hcLog) + utils.WriteResponse(w, http.StatusOK, report) } diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go index d87ed7eba..93ca367f7 100644 --- a/pkg/api/handlers/libpod/manifests.go +++ b/pkg/api/handlers/libpod/manifests.go @@ -151,6 +151,7 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) { } sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false) opts := manifests.PushOptions{ + Store: runtime.GetStore(), ImageListSelection: copy2.CopySpecificImages, SystemContext: sc, } diff --git a/pkg/api/handlers/libpod/system.go b/pkg/api/handlers/libpod/system.go index 81ed37b4a..f575546c9 100644 --- a/pkg/api/handlers/libpod/system.go +++ b/pkg/api/handlers/libpod/system.go @@ -71,16 +71,6 @@ func SystemPrune(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, systemPruneReport) } -// SystemReset Resets podman storage back to default state -func SystemReset(w http.ResponseWriter, r *http.Request) { - err := r.Context().Value("runtime").(*libpod.Runtime).Reset(r.Context()) - if err != nil { - utils.InternalServerError(w, err) - return - } - utils.WriteResponse(w, http.StatusOK, nil) -} - func DiskUsage(w http.ResponseWriter, r *http.Request) { // Options are only used by the CLI options := entities.SystemDfOptions{} diff --git a/pkg/api/handlers/utils/images.go b/pkg/api/handlers/utils/images.go index 1c67de9db..7fb31a177 100644 --- a/pkg/api/handlers/utils/images.go +++ b/pkg/api/handlers/utils/images.go @@ -62,7 +62,6 @@ func GetImages(w http.ResponseWriter, r *http.Request) ([]*image.Image, error) { }{ // This is where you can override the golang default value for one of fields } - // TODO I think all is implemented with a filter? if err := decoder.Decode(&query, r.URL.Query()); err != nil { return nil, err @@ -71,6 +70,10 @@ func GetImages(w http.ResponseWriter, r *http.Request) ([]*image.Image, error) { if _, found := r.URL.Query()["digests"]; found && query.Digests { UnSupportedParameter("digests") } + var ( + images []*image.Image + err error + ) if len(query.Filters) > 0 { for k, v := range query.Filters { @@ -78,11 +81,33 @@ func GetImages(w http.ResponseWriter, r *http.Request) ([]*image.Image, error) { filters = append(filters, fmt.Sprintf("%s=%s", k, val)) } } - return runtime.ImageRuntime().GetImagesWithFilters(filters) + images, err = runtime.ImageRuntime().GetImagesWithFilters(filters) + if err != nil { + return images, err + } } else { - return runtime.ImageRuntime().GetImages() + images, err = runtime.ImageRuntime().GetImages() + if err != nil { + return images, err + } } - + if query.All { + return images, nil + } + var returnImages []*image.Image + for _, img := range images { + if len(img.Names()) == 0 { + parent, err := img.IsParent(r.Context()) + if err != nil { + return nil, err + } + if parent { + continue + } + } + returnImages = append(returnImages, img) + } + return returnImages, nil } func GetImage(r *http.Request, name string) (*image.Image, error) { diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index 01854b9c4..c885dc81a 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -1188,5 +1188,214 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/images/{name}/changes"), s.APIHandler(compat.Changes)).Methods(http.MethodGet) + // swagger:operation POST /libpod/build libpod libpodBuildImage + // --- + // tags: + // - images + // summary: Create image + // description: Build an image from the given Dockerfile(s) + // parameters: + // - in: query + // name: dockerfile + // type: string + // default: Dockerfile + // description: | + // Path within the build context to the `Dockerfile`. + // This is ignored if remote is specified and points to an external `Dockerfile`. + // - in: query + // name: t + // type: string + // default: latest + // description: A name and optional tag to apply to the image in the `name:tag` format. + // - in: query + // name: extrahosts + // type: string + // default: + // description: | + // TBD Extra hosts to add to /etc/hosts + // (As of version 1.xx) + // - in: query + // name: remote + // type: string + // default: + // description: | + // A Git repository URI or HTTP/HTTPS context URI. + // If the URI points to a single text file, the file’s contents are placed + // into a file called Dockerfile and the image is built from that file. If + // the URI points to a tarball, the file is downloaded by the daemon and the + // contents therein used as the context for the build. If the URI points to a + // tarball and the dockerfile parameter is also specified, there must be a file + // with the corresponding path inside the tarball. + // (As of version 1.xx) + // - in: query + // name: q + // type: boolean + // default: false + // description: | + // Suppress verbose build output + // - in: query + // name: nocache + // type: boolean + // default: false + // description: | + // Do not use the cache when building the image + // (As of version 1.xx) + // - in: query + // name: cachefrom + // type: string + // default: + // description: | + // JSON array of images used to build cache resolution + // (As of version 1.xx) + // - in: query + // name: pull + // type: boolean + // default: false + // description: | + // Attempt to pull the image even if an older image exists locally + // (As of version 1.xx) + // - in: query + // name: rm + // type: boolean + // default: true + // description: | + // Remove intermediate containers after a successful build + // (As of version 1.xx) + // - in: query + // name: forcerm + // type: boolean + // default: false + // description: | + // Always remove intermediate containers, even upon failure + // (As of version 1.xx) + // - in: query + // name: memory + // type: integer + // description: | + // Memory is the upper limit (in bytes) on how much memory running containers can use + // (As of version 1.xx) + // - in: query + // name: memswap + // type: integer + // description: | + // MemorySwap limits the amount of memory and swap together + // (As of version 1.xx) + // - in: query + // name: cpushares + // type: integer + // description: | + // CPUShares (relative weight + // (As of version 1.xx) + // - in: query + // name: cpusetcpus + // type: string + // description: | + // CPUSetCPUs in which to allow execution (0-3, 0,1) + // (As of version 1.xx) + // - in: query + // name: cpuperiod + // type: integer + // description: | + // CPUPeriod limits the CPU CFS (Completely Fair Scheduler) period + // (As of version 1.xx) + // - in: query + // name: cpuquota + // type: integer + // description: | + // CPUQuota limits the CPU CFS (Completely Fair Scheduler) quota + // (As of version 1.xx) + // - in: query + // name: buildargs + // type: string + // default: + // description: | + // JSON map of string pairs denoting build-time variables. + // For example, the build argument `Foo` with the value of `bar` would be encoded in JSON as `["Foo":"bar"]`. + // + // For example, buildargs={"Foo":"bar"}. + // + // Note(s): + // * This should not be used to pass secrets. + // * The value of buildargs should be URI component encoded before being passed to the API. + // + // (As of version 1.xx) + // - in: query + // name: shmsize + // type: integer + // default: 67108864 + // description: | + // ShmSize is the "size" value to use when mounting an shmfs on the container's /dev/shm directory. + // Default is 64MB + // (As of version 1.xx) + // - in: query + // name: squash + // type: boolean + // default: false + // description: | + // Silently ignored. + // Squash the resulting images layers into a single layer + // (As of version 1.xx) + // - in: query + // name: labels + // type: string + // default: + // description: | + // JSON map of key, value pairs to set as labels on the new image + // (As of version 1.xx) + // - in: query + // name: networkmode + // type: string + // default: bridge + // description: | + // Sets the networking mode for the run commands during build. + // Supported standard values are: + // * `bridge` limited to containers within a single host, port mapping required for external access + // * `host` no isolation between host and containers on this network + // * `none` disable all networking for this container + // * container:<nameOrID> share networking with given container + // ---All other values are assumed to be a custom network's name + // (As of version 1.xx) + // - in: query + // name: platform + // type: string + // default: + // description: | + // Platform format os[/arch[/variant]] + // (As of version 1.xx) + // - in: query + // name: target + // type: string + // default: + // description: | + // Target build stage + // (As of version 1.xx) + // - in: query + // name: outputs + // type: string + // default: + // description: | + // output configuration TBD + // (As of version 1.xx) + // produces: + // - application/json + // responses: + // 200: + // description: OK (As of version 1.xx) + // schema: + // type: object + // required: + // - stream + // properties: + // stream: + // type: string + // description: output from build process + // example: | + // (build details...) + // Successfully built 8ba084515c724cbf90d447a63600c0a6 + // 400: + // $ref: "#/responses/BadParamError" + // 500: + // $ref: "#/responses/InternalError" + r.Handle(VersionedPath("/libpod/build"), s.APIHandler(compat.BuildImage)).Methods(http.MethodPost) return nil } 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/register_system.go b/pkg/api/server/register_system.go index 8a942a888..118ad2d08 100644 --- a/pkg/api/server/register_system.go +++ b/pkg/api/server/register_system.go @@ -27,20 +27,6 @@ func (s *APIServer) registerSystemHandlers(r *mux.Router) error { // 500: // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/system/prune"), s.APIHandler(libpod.SystemPrune)).Methods(http.MethodPost) - // swagger:operation POST /libpod/system/reset libpod resetSystem - // --- - // tags: - // - system - // summary: Reset podman storage - // description: All containers will be stopped and removed, and all images, volumes and container content will be removed. - // produces: - // - application/json - // responses: - // 200: - // description: no error - // 500: - // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/system/reset"), s.APIHandler(libpod.SystemReset)).Methods(http.MethodPost) // swagger:operation GET /libpod/system/df libpod df // --- // tags: 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/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index 74f6ded45..39a077f36 100644 --- a/pkg/bindings/containers/containers.go +++ b/pkg/bindings/containers/containers.go @@ -11,19 +11,19 @@ import ( "os/signal" "strconv" "strings" - "syscall" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/bindings" "github.com/containers/libpod/pkg/domain/entities" + sig "github.com/containers/libpod/pkg/signal" "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh/terminal" ) var ( - ErrLostSync = errors.New("lost synchronization with attach multiplexed result") + ErrLostSync = errors.New("lost synchronization with multiplexed stream") ) // List obtains a list of containers in local storage. All parameters to this method are optional. @@ -397,13 +397,13 @@ func Attach(ctx context.Context, nameOrId string, detachKeys *string, logs, stre }() winChange := make(chan os.Signal, 1) - signal.Notify(winChange, syscall.SIGWINCH) + signal.Notify(winChange, sig.SIGWINCH) winCtx, winCancel := context.WithCancel(ctx) defer winCancel() go func() { // Prime the pump, we need one reset to ensure everything is ready - winChange <- syscall.SIGWINCH + winChange <- sig.SIGWINCH for { select { case <-winCtx.Done(): @@ -485,7 +485,7 @@ func Attach(ctx context.Context, nameOrId string, detachKeys *string, logs, stre return err } case fd == 3: - return fmt.Errorf("error from daemon in stream: %s", frame) + return errors.New("error from service in stream: " + string(frame)) default: return fmt.Errorf("unrecognized input header: %d", fd) } @@ -507,7 +507,7 @@ func DemuxHeader(r io.Reader, buffer []byte) (fd, sz int, err error) { fd = int(buffer[0]) if fd < 0 || fd > 3 { - err = ErrLostSync + err = errors.Wrapf(ErrLostSync, fmt.Sprintf(`channel "%d" found, 0-3 supported`, fd)) return } @@ -528,7 +528,6 @@ func DemuxFrame(r io.Reader, buffer []byte, length int) (frame []byte, err error err = io.ErrUnexpectedEOF return } - return buffer[0:length], nil } diff --git a/pkg/bindings/containers/logs.go b/pkg/bindings/containers/logs.go index b7ecb3c7e..20c8b4292 100644 --- a/pkg/bindings/containers/logs.go +++ b/pkg/bindings/containers/logs.go @@ -1,8 +1,9 @@ package containers import ( + "bytes" "context" - "encoding/binary" + "fmt" "io" "net/http" "net/url" @@ -49,68 +50,34 @@ func Logs(ctx context.Context, nameOrID string, opts LogOptions, stdoutChan, std if err != nil { return err } + defer response.Body.Close() - // read 8 bytes - // first byte determines stderr=2|stdout=1 - // bytes 4-7 len(msg) in uint32 + buffer := make([]byte, 1024) for { - stream, msgSize, err := readHeader(response.Body) + fd, l, err := DemuxHeader(response.Body, buffer) if err != nil { - // In case the server side closes up shop because !follow - if err == io.EOF { - break + if errors.Is(err, io.EOF) { + return nil } - return errors.Wrap(err, "unable to read log header") + return err } - msg, err := readMsg(response.Body, msgSize) + frame, err := DemuxFrame(response.Body, buffer, l) if err != nil { - return errors.Wrap(err, "unable to read log message") + return err } - if stream == 1 { - stdoutChan <- msg - } else { - stderrChan <- msg - } - } - return nil -} + frame = bytes.Replace(frame[0:l], []byte{13}, []byte{10}, -1) -func readMsg(r io.Reader, msgSize int) (string, error) { - var msg []byte - size := msgSize - for { - b := make([]byte, size) - _, err := r.Read(b) - if err != nil { - return "", err - } - msg = append(msg, b...) - if len(msg) == msgSize { - break - } - size = msgSize - len(msg) - } - return string(msg), nil -} - -func readHeader(r io.Reader) (byte, int, error) { - var ( - header []byte - size = 8 - ) - for { - b := make([]byte, size) - _, err := r.Read(b) - if err != nil { - return 0, 0, err - } - header = append(header, b...) - if len(header) == 8 { - break + switch fd { + case 0: + stdoutChan <- string(frame) + case 1: + stdoutChan <- string(frame) + case 2: + stderrChan <- string(frame) + case 3: + return errors.New("error from service in stream: " + string(frame)) + default: + return fmt.Errorf("unrecognized input header: %d", fd) } - size = 8 - len(header) } - stream := header[0] - msgSize := int(binary.BigEndian.Uint32(header[4:]) - 8) - return stream, msgSize, nil } diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index 69b9e9bbf..f9c02d199 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -1,6 +1,7 @@ package images import ( + "bytes" "context" "fmt" "io" @@ -8,10 +9,13 @@ import ( "net/url" "strconv" + "github.com/containers/buildah" "github.com/containers/image/v5/types" "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/bindings" "github.com/containers/libpod/pkg/domain/entities" + "github.com/docker/go-units" + jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" ) @@ -209,7 +213,108 @@ func Untag(ctx context.Context, nameOrID, tag, repo string) error { return response.Process(nil) } -func Build(nameOrId string) {} +// Build creates an image using a containerfile reference +func Build(ctx context.Context, containerFiles []string, options entities.BuildOptions, tarfile io.Reader) (*entities.BuildReport, error) { + var ( + platform string + report entities.BuildReport + ) + conn, err := bindings.GetClient(ctx) + if err != nil { + return nil, err + } + params := url.Values{} + params.Set("dockerfile", containerFiles[0]) + if t := options.Output; len(t) > 0 { + params.Set("t", t) + } + // TODO Remote, Quiet + if options.NoCache { + params.Set("nocache", "1") + } + // TODO cachefrom + if options.PullPolicy == buildah.PullAlways { + params.Set("pull", "1") + } + if options.RemoveIntermediateCtrs { + params.Set("rm", "1") + } + if options.ForceRmIntermediateCtrs { + params.Set("forcerm", "1") + } + if mem := options.CommonBuildOpts.Memory; mem > 0 { + params.Set("memory", strconv.Itoa(int(mem))) + } + if memSwap := options.CommonBuildOpts.MemorySwap; memSwap > 0 { + params.Set("memswap", strconv.Itoa(int(memSwap))) + } + if cpuShares := options.CommonBuildOpts.CPUShares; cpuShares > 0 { + params.Set("cpushares", strconv.Itoa(int(cpuShares))) + } + if cpuSetCpus := options.CommonBuildOpts.CPUSetCPUs; len(cpuSetCpus) > 0 { + params.Set("cpusetcpues", cpuSetCpus) + } + if cpuPeriod := options.CommonBuildOpts.CPUPeriod; cpuPeriod > 0 { + params.Set("cpuperiod", strconv.Itoa(int(cpuPeriod))) + } + if cpuQuota := options.CommonBuildOpts.CPUQuota; cpuQuota > 0 { + params.Set("cpuquota", strconv.Itoa(int(cpuQuota))) + } + if buildArgs := options.Args; len(buildArgs) > 0 { + bArgs, err := jsoniter.MarshalToString(buildArgs) + if err != nil { + return nil, err + } + params.Set("buildargs", bArgs) + } + if shmSize := options.CommonBuildOpts.ShmSize; len(shmSize) > 0 { + shmBytes, err := units.RAMInBytes(shmSize) + if err != nil { + return nil, err + } + params.Set("shmsize", strconv.Itoa(int(shmBytes))) + } + if options.Squash { + params.Set("squash", "1") + } + if labels := options.Labels; len(labels) > 0 { + l, err := jsoniter.MarshalToString(labels) + if err != nil { + return nil, err + } + params.Set("labels", l) + } + + // TODO network? + if OS := options.OS; len(OS) > 0 { + platform += OS + } + if arch := options.Architecture; len(arch) > 0 { + platform += "/" + arch + } + if len(platform) > 0 { + params.Set("platform", platform) + } + // TODO outputs? + + response, err := conn.DoRequest(tarfile, http.MethodPost, "/build", params) + if err != nil { + return nil, err + } + var streamReponse []byte + bb := bytes.NewBuffer(streamReponse) + if _, err = io.Copy(bb, response.Body); err != nil { + return nil, err + } + var s struct { + Stream string `json:"stream"` + } + if err := jsoniter.UnmarshalFromString(bb.String(), &s); err != nil { + return nil, err + } + fmt.Print(s.Stream) + return &report, nil +} // Imports adds the given image to the local image store. This can be done by file and the given reader // or via the url parameter. Additional metadata can be associated with the image by using the changes and diff --git a/pkg/bindings/manifests/manifests.go b/pkg/bindings/manifests/manifests.go index 3e0ef0325..f5ee31d93 100644 --- a/pkg/bindings/manifests/manifests.go +++ b/pkg/bindings/manifests/manifests.go @@ -112,17 +112,17 @@ func Push(ctx context.Context, name string, destination *string, all *bool) (str params := url.Values{} params.Set("image", name) if destination != nil { - dest = name + dest = *destination } params.Set("destination", dest) if all != nil { params.Set("all", strconv.FormatBool(*all)) } - response, err := conn.DoRequest(nil, http.MethodPost, "/manifests/%s/push", params, name) + _, err = conn.DoRequest(nil, http.MethodPost, "/manifests/%s/push", params, name) if err != nil { return "", err } - return idr.ID, response.Process(&idr) + return idr.ID, err } // There is NO annotate endpoint. this binding could never work diff --git a/pkg/bindings/system/system.go b/pkg/bindings/system/system.go index e567e7a86..5348d0cfb 100644 --- a/pkg/bindings/system/system.go +++ b/pkg/bindings/system/system.go @@ -112,29 +112,16 @@ func Version(ctx context.Context) (*entities.SystemVersionReport, error) { f, _ := strconv.ParseFloat(component.APIVersion, 64) b, _ := time.Parse(time.RFC3339, component.BuildTime) report.Server = &define.Version{ - RemoteAPIVersion: int64(f), - Version: component.Version.Version, - GoVersion: component.GoVersion, - GitCommit: component.GitCommit, - Built: b.Unix(), - OsArch: fmt.Sprintf("%s/%s", component.Os, component.Arch), + APIVersion: int64(f), + Version: component.Version.Version, + GoVersion: component.GoVersion, + GitCommit: component.GitCommit, + Built: b.Unix(), + OsArch: fmt.Sprintf("%s/%s", component.Os, component.Arch), } return &report, err } -// Reset removes all unused system data. -func Reset(ctx context.Context) error { - conn, err := bindings.GetClient(ctx) - if err != nil { - return err - } - response, err := conn.DoRequest(nil, http.MethodPost, "/system/reset", nil) - if err != nil { - return err - } - return response.Process(response) -} - // DiskUsage returns information about image, container, and volume disk // consumption func DiskUsage(ctx context.Context) (*entities.SystemDfReport, error) { diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go index f725d1cf2..3b94b10eb 100644 --- a/pkg/bindings/test/containers_test.go +++ b/pkg/bindings/test/containers_test.go @@ -378,9 +378,9 @@ var _ = Describe("Podman containers ", func() { containers.Logs(bt.conn, r.ID, opts, stdoutChan, nil) }() o := <-stdoutChan - o = strings.ReplaceAll(o, "\r", "") + o = strings.TrimSpace(o) _, err = time.Parse(time.RFC1123Z, o) - Expect(err).To(BeNil()) + Expect(err).ShouldNot(HaveOccurred()) }) It("podman top", func() { diff --git a/pkg/bindings/test/system_test.go b/pkg/bindings/test/system_test.go index fb2df258b..27ab2f555 100644 --- a/pkg/bindings/test/system_test.go +++ b/pkg/bindings/test/system_test.go @@ -5,7 +5,6 @@ import ( "github.com/containers/libpod/pkg/bindings" "github.com/containers/libpod/pkg/bindings/containers" - "github.com/containers/libpod/pkg/bindings/images" "github.com/containers/libpod/pkg/bindings/pods" "github.com/containers/libpod/pkg/bindings/system" "github.com/containers/libpod/pkg/bindings/volumes" @@ -150,45 +149,4 @@ var _ = Describe("Podman system", func() { // Volume should be pruned now as flag set true Expect(len(systemPruneResponse.VolumePruneReport)).To(Equal(1)) }) - - It("podman system reset", func() { - // Adding an unused volume should work - _, err := volumes.Create(bt.conn, entities.VolumeCreateOptions{}) - Expect(err).To(BeNil()) - - vols, err := volumes.List(bt.conn, nil) - Expect(err).To(BeNil()) - Expect(len(vols)).To(Equal(1)) - - // Start a pod and leave it running - _, err = pods.Start(bt.conn, newpod) - Expect(err).To(BeNil()) - - imageSummary, err := images.List(bt.conn, nil, nil) - Expect(err).To(BeNil()) - // Since in the begin context images are created - Expect(len(imageSummary)).To(Equal(3)) - - err = system.Reset(bt.conn) - Expect(err).To(BeNil()) - - // re-establish connection - s = bt.startAPIService() - time.Sleep(1 * time.Second) - - // No pods - podSummary, err := pods.List(bt.conn, nil) - Expect(err).To(BeNil()) - Expect(len(podSummary)).To(Equal(0)) - - // No images - imageSummary, err = images.List(bt.conn, bindings.PTrue, nil) - Expect(err).To(BeNil()) - Expect(len(imageSummary)).To(Equal(0)) - - // no volumes - vols, err = volumes.List(bt.conn, nil) - Expect(err).To(BeNil()) - Expect(len(vols)).To(BeZero()) - }) }) diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 3cc4b6db1..8d85a9b23 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -242,7 +242,6 @@ type ExecOptions struct { Latest bool PreserveFDs uint Privileged bool - Streams define.AttachStreams Tty bool User string WorkDir string @@ -311,6 +310,7 @@ type ContainerRunReport struct { // cleanup command type ContainerCleanupOptions struct { All bool + Exec string Latest bool Remove bool RemoveImage bool diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index e77f0758b..3d5161745 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -19,7 +19,8 @@ type ContainerEngine interface { ContainerCp(ctx context.Context, source, dest string, options ContainerCpOptions) (*ContainerCpReport, error) ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error) ContainerDiff(ctx context.Context, nameOrId string, options DiffOptions) (*DiffReport, error) - ContainerExec(ctx context.Context, nameOrId string, options ExecOptions) (int, error) + ContainerExec(ctx context.Context, nameOrId string, options ExecOptions, streams define.AttachStreams) (int, error) + ContainerExecDetached(ctx context.Context, nameOrID string, options ExecOptions) (string, error) ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, error) ContainerExport(ctx context.Context, nameOrId string, options ContainerExportOptions) error ContainerInit(ctx context.Context, namesOrIds []string, options ContainerInitOptions) ([]*ContainerInitReport, error) diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index cce3001eb..0f909ab37 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -1,7 +1,6 @@ package entities import ( - "net/url" "time" "github.com/containers/image/v5/manifest" @@ -221,15 +220,13 @@ type ImageSearchReport struct { // Image List Options type ImageListOptions struct { - All bool `json:"all" schema:"all"` - Filter []string `json:"Filter,omitempty"` - Filters url.Values `json:"filters" schema:"filters"` + All bool `json:"all" schema:"all"` + Filter []string `json:"Filter,omitempty"` } type ImagePruneOptions struct { - All bool `json:"all" schema:"all"` - Filter []string `json:"filter" schema:"filter"` - Filters url.Values `json:"filters" schema:"filters"` + All bool `json:"all" schema:"all"` + Filter []string `json:"filter" schema:"filter"` } type ImagePruneReport struct { diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 035efe575..b4e38ca23 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -536,7 +536,22 @@ func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string, return nil } -func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions) (int, error) { +func makeExecConfig(options entities.ExecOptions) *libpod.ExecConfig { + execConfig := new(libpod.ExecConfig) + execConfig.Command = options.Cmd + execConfig.Terminal = options.Tty + execConfig.Privileged = options.Privileged + execConfig.Environment = options.Envs + execConfig.User = options.User + execConfig.WorkDir = options.WorkDir + execConfig.DetachKeys = &options.DetachKeys + execConfig.PreserveFDs = options.PreserveFDs + execConfig.AttachStdin = options.Interactive + + return execConfig +} + +func checkExecPreserveFDs(options entities.ExecOptions) (int, error) { ec := define.ExecErrorCodeGeneric if options.PreserveFDs > 0 { entries, err := ioutil.ReadDir("/proc/self/fd") @@ -559,15 +574,66 @@ func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, o } } } + return ec, nil +} + +func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions, streams define.AttachStreams) (int, error) { + ec, err := checkExecPreserveFDs(options) + if err != nil { + return ec, err + } ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrId}, ic.Libpod) if err != nil { return ec, err } ctr := ctrs[0] - ec, err = terminal.ExecAttachCtr(ctx, ctr, options.Tty, options.Privileged, options.Envs, options.Cmd, options.User, options.WorkDir, &options.Streams, options.PreserveFDs, options.DetachKeys) + + execConfig := makeExecConfig(options) + + ec, err = terminal.ExecAttachCtr(ctx, ctr, execConfig, &streams) return define.TranslateExecErrorToExitCode(ec, err), err } +func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrId string, options entities.ExecOptions) (string, error) { + _, err := checkExecPreserveFDs(options) + if err != nil { + return "", err + } + ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrId}, ic.Libpod) + if err != nil { + return "", err + } + ctr := ctrs[0] + + execConfig := makeExecConfig(options) + + // Make an exit command + storageConfig := ic.Libpod.StorageConfig() + runtimeConfig, err := ic.Libpod.GetConfig() + if err != nil { + return "", errors.Wrapf(err, "error retrieving Libpod configuration to build exec exit command") + } + podmanPath, err := os.Executable() + if err != nil { + return "", errors.Wrapf(err, "error retrieving executable to build exec exit command") + } + // TODO: Add some ability to toggle syslog + exitCommandArgs := generate.CreateExitCommandArgs(storageConfig, runtimeConfig, podmanPath, false, true, true) + execConfig.ExitCommand = exitCommandArgs + + // Create and start the exec session + id, err := ctr.ExecCreate(execConfig) + if err != nil { + return "", err + } + + // TODO: we should try and retrieve exit code if this fails. + if err := ctr.ExecStart(id); err != nil { + return "", err + } + return id, nil +} + func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) { var reports []*entities.ContainerStartReport var exitCode = define.ExecErrorCodeGeneric @@ -836,6 +902,20 @@ func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []st for _, ctr := range ctrs { var err error report := entities.ContainerCleanupReport{Id: ctr.ID()} + + if options.Exec != "" { + if options.Remove { + if err := ctr.ExecRemove(options.Exec, false); err != nil { + return nil, err + } + } else { + if err := ctr.ExecCleanup(options.Exec); err != nil { + return nil, err + } + } + return []*entities.ContainerCleanupReport{}, nil + } + if options.Remove { err = ic.Libpod.RemoveContainer(ctx, ctr, false, true) if err != nil { diff --git a/pkg/domain/infra/abi/healthcheck.go b/pkg/domain/infra/abi/healthcheck.go index 351bf4f7e..4e925ef56 100644 --- a/pkg/domain/infra/abi/healthcheck.go +++ b/pkg/domain/infra/abi/healthcheck.go @@ -3,7 +3,6 @@ package abi import ( "context" - "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/domain/entities" ) @@ -13,9 +12,9 @@ func (ic *ContainerEngine) HealthCheckRun(ctx context.Context, nameOrId string, if err != nil { return nil, err } - hcStatus := "unhealthy" - if status == libpod.HealthCheckSuccess { - hcStatus = "healthy" + hcStatus := define.HealthCheckUnhealthy + if status == define.HealthCheckSuccess { + hcStatus = define.HealthCheckHealthy } report := define.HealthCheckResults{ Status: hcStatus, diff --git a/pkg/domain/infra/abi/images_list.go b/pkg/domain/infra/abi/images_list.go index c559e250c..3034e36ec 100644 --- a/pkg/domain/infra/abi/images_list.go +++ b/pkg/domain/infra/abi/images_list.go @@ -13,14 +13,7 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) err error ) - // TODO: Future work support for domain.Filters - // filters := utils.ToLibpodFilters(opts.Filters) - - if len(opts.Filter) > 0 { - images, err = ir.Libpod.ImageRuntime().GetImagesWithFilters(opts.Filter) - } else { - images, err = ir.Libpod.ImageRuntime().GetImages() - } + images, err = ir.Libpod.ImageRuntime().GetImagesWithFilters(opts.Filter) if err != nil { return nil, err } @@ -40,9 +33,18 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) } } } else { - repoTags, _ = img.RepoTags() - if len(repoTags) == 0 { - continue + repoTags, err = img.RepoTags() + if err != nil { + return nil, err + } + if len(img.Names()) == 0 { + parent, err := img.IsParent(ctx) + if err != nil { + return nil, err + } + if parent { + continue + } } } diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index af2ec5f7b..52dfaba7d 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -16,56 +16,18 @@ import ( "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/util" - iopodman "github.com/containers/libpod/pkg/varlink" - iopodmanAPI "github.com/containers/libpod/pkg/varlinkapi" "github.com/containers/libpod/utils" - "github.com/containers/libpod/version" "github.com/docker/distribution/reference" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/pflag" - "github.com/varlink/go/varlink" ) func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) { return ic.Libpod.Info() } -func (ic *ContainerEngine) VarlinkService(_ context.Context, opts entities.ServiceOptions) error { - var varlinkInterfaces = []*iopodman.VarlinkInterface{ - iopodmanAPI.New(opts.Command, ic.Libpod), - } - - service, err := varlink.NewService( - "Atomic", - "podman", - version.Version, - "https://github.com/containers/libpod", - ) - if err != nil { - return errors.Wrapf(err, "unable to create new varlink service") - } - - for _, i := range varlinkInterfaces { - if err := service.RegisterInterface(i); err != nil { - return errors.Errorf("unable to register varlink interface %v", i) - } - } - - // Run the varlink server at the given address - 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()) - return nil - default: - return errors.Wrapf(err, "unable to start varlink service") - } - } - return nil -} - func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) error { // do it only after podman has already re-execed and running with uid==0. if os.Geteuid() == 0 { diff --git a/pkg/domain/infra/abi/system_novalink.go b/pkg/domain/infra/abi/system_novalink.go new file mode 100644 index 000000000..a71b0170a --- /dev/null +++ b/pkg/domain/infra/abi/system_novalink.go @@ -0,0 +1,14 @@ +// +build !varlink + +package abi + +import ( + "context" + + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" +) + +func (ic *ContainerEngine) VarlinkService(_ context.Context, opts entities.ServiceOptions) error { + return errors.Errorf("varlink is not supported") +} diff --git a/pkg/domain/infra/abi/system_varlink.go b/pkg/domain/infra/abi/system_varlink.go new file mode 100644 index 000000000..4dc766f52 --- /dev/null +++ b/pkg/domain/infra/abi/system_varlink.go @@ -0,0 +1,49 @@ +// +build varlink + +package abi + +import ( + "context" + + "github.com/containers/libpod/pkg/domain/entities" + iopodman "github.com/containers/libpod/pkg/varlink" + iopodmanAPI "github.com/containers/libpod/pkg/varlinkapi" + "github.com/containers/libpod/version" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/varlink/go/varlink" +) + +func (ic *ContainerEngine) VarlinkService(_ context.Context, opts entities.ServiceOptions) error { + var varlinkInterfaces = []*iopodman.VarlinkInterface{ + iopodmanAPI.New(opts.Command, ic.Libpod), + } + + service, err := varlink.NewService( + "Atomic", + "podman", + version.Version, + "https://github.com/containers/libpod", + ) + if err != nil { + return errors.Wrapf(err, "unable to create new varlink service") + } + + for _, i := range varlinkInterfaces { + if err := service.RegisterInterface(i); err != nil { + return errors.Errorf("unable to register varlink interface %v", i) + } + } + + // Run the varlink server at the given address + if err = service.Listen(opts.URI, opts.Timeout); err != nil { + switch err.(type) { + case varlink.ServiceTimeoutError: + 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") + } + } + return nil +} diff --git a/pkg/domain/infra/abi/terminal/terminal_linux.go b/pkg/domain/infra/abi/terminal/terminal_linux.go index 15701342f..8d9cdde03 100644 --- a/pkg/domain/infra/abi/terminal/terminal_linux.go +++ b/pkg/domain/infra/abi/terminal/terminal_linux.go @@ -15,13 +15,13 @@ import ( ) // ExecAttachCtr execs and attaches to a container -func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *define.AttachStreams, preserveFDs uint, detachKeys string) (int, error) { +func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, execConfig *libpod.ExecConfig, streams *define.AttachStreams) (int, error) { resize := make(chan remotecommand.TerminalSize) haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd())) // Check if we are attached to a terminal. If we are, generate resize // events, and set the terminal to raw mode - if haveTerminal && tty { + if haveTerminal && execConfig.Terminal { cancel, oldTermState, err := handleTerminalAttach(ctx, resize) if err != nil { return -1, err @@ -34,16 +34,6 @@ func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged b }() } - execConfig := new(libpod.ExecConfig) - execConfig.Command = cmd - execConfig.Terminal = tty - execConfig.Privileged = privileged - execConfig.Environment = env - execConfig.User = user - execConfig.WorkDir = workDir - execConfig.DetachKeys = &detachKeys - execConfig.PreserveFDs = preserveFDs - return ctr.Exec(execConfig, streams, resize) } diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 828bfae5b..beba55c2b 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -4,6 +4,9 @@ import ( "context" "io" "os" + "strconv" + "strings" + "time" "github.com/containers/common/pkg/config" "github.com/containers/image/v5/docker/reference" @@ -86,10 +89,25 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin } for _, c := range ctrs { report := entities.StopReport{Id: c.ID} - report.Err = containers.Stop(ic.ClientCxt, c.ID, &options.Timeout) - // TODO we need to associate errors returned by http with common - // define.errors so that we can equity tests. this will allow output - // to be the same as the native client + if err = containers.Stop(ic.ClientCxt, c.ID, &options.Timeout); err != nil { + // These first two are considered non-fatal under the right conditions + if errors.Cause(err).Error() == define.ErrCtrStopped.Error() { + logrus.Debugf("Container %s is already stopped", c.ID) + reports = append(reports, &report) + continue + } else if options.All && errors.Cause(err).Error() == define.ErrCtrStateInvalid.Error() { + logrus.Debugf("Container %s is not running, could not stop", c.ID) + reports = append(reports, &report) + continue + } + + // TODO we need to associate errors returned by http with common + // define.errors so that we can equity tests. this will allow output + // to be the same as the native client + report.Err = err + reports = append(reports, &report) + continue + } reports = append(reports, &report) } return reports, nil @@ -320,19 +338,50 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG return &entities.ContainerCreateReport{Id: response.ID}, nil } -func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []string, options entities.ContainerLogsOptions) error { - // The endpoint is not ready yet and requires some more work. - return errors.New("not implemented yet") +func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIds []string, options entities.ContainerLogsOptions) error { + since := options.Since.Format(time.RFC3339) + tail := strconv.FormatInt(options.Tail, 10) + stdout := options.Writer != nil + opts := containers.LogOptions{ + Follow: &options.Follow, + Since: &since, + Stderr: &stdout, + Stdout: &stdout, + Tail: &tail, + Timestamps: &options.Timestamps, + Until: nil, + } + + var err error + outCh := make(chan string) + ctx, cancel := context.WithCancel(context.Background()) + go func() { + err = containers.Logs(ic.ClientCxt, nameOrIds[0], opts, outCh, outCh) + cancel() + }() + + for { + select { + case <-ctx.Done(): + return err + case line := <-outCh: + _, _ = io.WriteString(options.Writer, line) + } + } } func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string, options entities.AttachOptions) error { return containers.Attach(ic.ClientCxt, nameOrId, &options.DetachKeys, nil, bindings.PTrue, options.Stdin, options.Stdout, options.Stderr, nil) } -func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions) (int, error) { +func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions, streams define.AttachStreams) (int, error) { return 125, errors.New("not implemented") } +func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID string, options entities.ExecOptions) (string, error) { + return "", errors.New("not implemented") +} + func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, input, output, errput *os.File) error { //nolint attachErr := make(chan error) attachReady := make(chan bool) @@ -352,13 +401,26 @@ func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, input, func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) { var reports []*entities.ContainerStartReport for _, name := range namesOrIds { - report := entities.ContainerStartReport{Id: name} + report := entities.ContainerStartReport{ + Id: name, + RawInput: name, + ExitCode: 125, + } if options.Attach { report.Err = startAndAttach(ic, name, &options.DetachKeys, options.Stdin, options.Stdout, options.Stderr) + if report.Err == nil { + exitCode, err := containers.Wait(ic.ClientCxt, name, nil) + if err == nil { + report.ExitCode = int(exitCode) + } + } else { + report.ExitCode = define.ExitCode(report.Err) + } reports = append(reports, &report) return reports, nil } report.Err = containers.Start(ic.ClientCxt, name, &options.DetachKeys) + report.ExitCode = define.ExitCode(report.Err) reports = append(reports, &report) } return reports, nil @@ -380,11 +442,18 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta // Attach if !opts.Detach { err = startAndAttach(ic, con.ID, &opts.DetachKeys, opts.InputStream, opts.OutputStream, opts.ErrorStream) - + if err == nil { + exitCode, err := containers.Wait(ic.ClientCxt, con.ID, nil) + if err == nil { + report.ExitCode = int(exitCode) + } + } } else { err = containers.Start(ic.ClientCxt, con.ID, nil) } - report.ExitCode = define.ExitCode(err) + if err != nil { + report.ExitCode = define.ExitCode(err) + } return &report, err } @@ -405,6 +474,11 @@ func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []strin } for _, ctr := range ctrs { err := containers.ContainerInit(ic.ClientCxt, ctr.ID) + // When using all, it is NOT considered an error if a container + // has already been init'd. + if err != nil && options.All && strings.Contains(errors.Cause(err).Error(), define.ErrCtrStateInvalid.Error()) { + err = nil + } reports = append(reports, &entities.ContainerInitReport{ Err: err, Id: ctr.ID, diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 4d00d331b..c300e74d0 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -4,6 +4,8 @@ import ( "context" "io/ioutil" "os" + "path" + "strings" "github.com/containers/common/pkg/config" "github.com/containers/image/v5/docker/reference" @@ -12,6 +14,7 @@ import ( "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/domain/utils" utils2 "github.com/containers/libpod/utils" + "github.com/containers/storage/pkg/archive" "github.com/pkg/errors" ) @@ -25,8 +28,13 @@ func (ir *ImageEngine) Remove(ctx context.Context, imagesArg []string, opts enti } func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) { - images, err := images.List(ir.ClientCxt, &opts.All, opts.Filters) + filters := make(map[string][]string, len(opts.Filter)) + for _, filter := range opts.Filter { + f := strings.Split(filter, "=") + filters[f[0]] = f[1:] + } + images, err := images.List(ir.ClientCxt, &opts.All, filters) if err != nil { return nil, err } @@ -61,7 +69,13 @@ func (ir *ImageEngine) History(ctx context.Context, nameOrId string, opts entiti } func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) { - results, err := images.Prune(ir.ClientCxt, &opts.All, opts.Filters) + filters := make(map[string][]string, len(opts.Filter)) + for _, filter := range opts.Filter { + f := strings.Split(filter, "=") + filters[f[0]] = f[1:] + } + + results, err := images.Prune(ir.ClientCxt, &opts.All, filters) if err != nil { return nil, err } @@ -253,7 +267,14 @@ func (ir *ImageEngine) Config(_ context.Context) (*config.Config, error) { } func (ir *ImageEngine) Build(ctx context.Context, containerFiles []string, opts entities.BuildOptions) (*entities.BuildReport, error) { - return nil, errors.New("not implemented yet") + if len(containerFiles) > 1 { + return nil, errors.New("something") + } + tarfile, err := archive.Tar(path.Base(containerFiles[0]), 0) + if err != nil { + return nil, err + } + return images.Build(ir.ClientCxt, containerFiles, opts, tarfile) } func (ir *ImageEngine) Tree(ctx context.Context, nameOrId string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) { diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go index 829af31f6..109e6c1d7 100644 --- a/pkg/domain/infra/tunnel/system.go +++ b/pkg/domain/infra/tunnel/system.go @@ -27,11 +27,6 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys return system.Prune(ic.ClientCxt, &options.All, &options.Volume) } -// Reset removes all storage -func (ic *SystemEngine) Reset(ctx context.Context) error { - return system.Reset(ic.ClientCxt) -} - func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.SystemDfOptions) (*entities.SystemDfReport, error) { return system.DiskUsage(ic.ClientCxt) } diff --git a/pkg/network/netconflist.go b/pkg/network/netconflist.go index 34ff00024..4271d3f54 100644 --- a/pkg/network/netconflist.go +++ b/pkg/network/netconflist.go @@ -21,10 +21,11 @@ func NewNcList(name, version string) NcList { // NewHostLocalBridge creates a new LocalBridge for host-local func NewHostLocalBridge(name string, isGateWay, isDefaultGW, ipMasq bool, ipamConf IPAMHostLocalConf) *HostLocalBridge { hostLocalBridge := HostLocalBridge{ - PluginType: "bridge", - BrName: name, - IPMasq: ipMasq, - IPAM: ipamConf, + PluginType: "bridge", + BrName: name, + IPMasq: ipMasq, + HairpinMode: true, + IPAM: ipamConf, } if isGateWay { hostLocalBridge.IsGW = true 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 +} diff --git a/pkg/specgen/generate/config_linux_nocgo.go b/pkg/specgen/generate/config_linux_nocgo.go index fc8ed206d..81d1c7011 100644 --- a/pkg/specgen/generate/config_linux_nocgo.go +++ b/pkg/specgen/generate/config_linux_nocgo.go @@ -5,10 +5,11 @@ package generate import ( "errors" + "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/specgen" spec "github.com/opencontainers/runtime-spec/specs-go" ) -func (s *specgen.SpecGenerator) getSeccompConfig(configSpec *spec.Spec) (*spec.LinuxSeccomp, error) { +func getSeccompConfig(s *specgen.SpecGenerator, configSpec *spec.Spec, img *image.Image) (*spec.LinuxSeccomp, error) { return nil, errors.New("not implemented") } diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index f3aaf96bf..ffd7fd4dd 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -111,7 +111,8 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener if err != nil { return nil, err } - options = append(options, createExitCommandOption(s, rt.StorageConfig(), rtc, podmanPath)) + // TODO: Enable syslog support - we'll need to put this in SpecGen. + options = append(options, libpod.WithExitCommand(CreateExitCommandArgs(rt.StorageConfig(), rtc, podmanPath, false, s.Remove, false))) runtimeSpec, err := SpecGenToOCI(ctx, s, rt, rtc, newImage, finalMounts) if err != nil { @@ -228,7 +229,7 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen. return options, nil } -func createExitCommandOption(s *specgen.SpecGenerator, storageConfig storage.StoreOptions, config *config.Config, podmanPath string) libpod.CtrCreateOption { +func CreateExitCommandArgs(storageConfig storage.StoreOptions, config *config.Config, podmanPath string, syslog, rm bool, exec bool) []string { // We need a cleanup process for containers in the current model. // But we can't assume that the caller is Podman - it could be another // user of the API. @@ -255,14 +256,18 @@ func createExitCommandOption(s *specgen.SpecGenerator, storageConfig storage.Sto command = append(command, []string{"--events-backend", config.Engine.EventsLogger}...) } - // TODO Mheon wants to leave this for now - //if s.sys { - // command = append(command, "--syslog", "true") - //} + if syslog { + command = append(command, "--syslog", "true") + } command = append(command, []string{"container", "cleanup"}...) - if s.Remove { + if rm { command = append(command, "--rm") } - return libpod.WithExitCommand(command) + + if exec { + command = append(command, "--exec") + } + + return command } diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go index 11dee1986..da1f8e8fc 100644 --- a/pkg/specgen/namespaces.go +++ b/pkg/specgen/namespaces.go @@ -40,6 +40,9 @@ const ( KeepID NamespaceMode = "keep-id" // KeepId indicates to automatically create a user namespace Auto NamespaceMode = "auto" + // DefaultKernelNamespaces is a comma-separated list of default kernel + // namespaces. + DefaultKernelNamespaces = "cgroup,ipc,net,uts" ) // Namespace describes the namespace diff --git a/pkg/util/mountOpts_linux.go b/pkg/util/mountOpts_linux.go index 3eac4dd25..bc7c675f3 100644 --- a/pkg/util/mountOpts_linux.go +++ b/pkg/util/mountOpts_linux.go @@ -7,7 +7,7 @@ import ( ) func getDefaultMountOptions(path string) (defaultMountOptions, error) { - opts := defaultMountOptions{true, true, true} + opts := defaultMountOptions{false, true, true} if path == "" { return opts, nil } diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go index 258cb8652..291353cad 100644 --- a/pkg/varlinkapi/containers.go +++ b/pkg/varlinkapi/containers.go @@ -901,12 +901,12 @@ func (i *VarlinkAPI) ExecContainer(call iopodman.VarlinkCall, opts iopodman.Exec // HealthCheckRun executes defined container's healthcheck command and returns the container's health status. func (i *VarlinkAPI) HealthCheckRun(call iopodman.VarlinkCall, nameOrID string) error { hcStatus, err := i.Runtime.HealthCheck(nameOrID) - if err != nil && hcStatus != libpod.HealthCheckFailure { + if err != nil && hcStatus != define.HealthCheckFailure { return call.ReplyErrorOccurred(err.Error()) } - status := libpod.HealthCheckUnhealthy - if hcStatus == libpod.HealthCheckSuccess { - status = libpod.HealthCheckHealthy + status := define.HealthCheckUnhealthy + if hcStatus == define.HealthCheckSuccess { + status = define.HealthCheckHealthy } return call.ReplyHealthCheckRun(status) } diff --git a/pkg/varlinkapi/system.go b/pkg/varlinkapi/system.go index 82efe9b5d..308f02274 100644 --- a/pkg/varlinkapi/system.go +++ b/pkg/varlinkapi/system.go @@ -28,7 +28,7 @@ func (i *VarlinkAPI) GetVersion(call iopodman.VarlinkCall) error { versionInfo.GitCommit, time.Unix(versionInfo.Built, 0).Format(time.RFC3339), versionInfo.OsArch, - versionInfo.RemoteAPIVersion, + versionInfo.APIVersion, ) } |