mirror of https://github.com/containers/podman.git
Merge pull request #5611 from baude/v2podcreate
podmanv2 pod create using podspecgen
This commit is contained in:
commit
4233250c43
|
@ -0,0 +1,2 @@
|
|||
all:
|
||||
GO111MODULE=off go build -tags 'ABISupport'
|
|
@ -0,0 +1,108 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/containers/libpod/pkg/rootless"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func getDefaultNetwork() string {
|
||||
if rootless.IsRootless() {
|
||||
return "slirp4netns"
|
||||
}
|
||||
return "bridge"
|
||||
}
|
||||
|
||||
func GetNetFlags() *pflag.FlagSet {
|
||||
netFlags := pflag.FlagSet{}
|
||||
netFlags.StringSlice(
|
||||
"add-host", []string{},
|
||||
"Add a custom host-to-IP mapping (host:ip) (default [])",
|
||||
)
|
||||
netFlags.StringSlice(
|
||||
"dns", []string{},
|
||||
"Set custom DNS servers",
|
||||
)
|
||||
netFlags.StringSlice(
|
||||
"dns-opt", []string{},
|
||||
"Set custom DNS options",
|
||||
)
|
||||
netFlags.StringSlice(
|
||||
"dns-search", []string{},
|
||||
"Set custom DNS search domains",
|
||||
)
|
||||
netFlags.String(
|
||||
"ip", "",
|
||||
"Specify a static IPv4 address for the container",
|
||||
)
|
||||
netFlags.String(
|
||||
"mac-address", "",
|
||||
"Container MAC address (e.g. 92:d0:c6:0a:29:33)",
|
||||
)
|
||||
netFlags.String(
|
||||
"network", getDefaultNetwork(),
|
||||
"Connect a container to a network",
|
||||
)
|
||||
netFlags.StringSliceP(
|
||||
"publish", "p", []string{},
|
||||
"Publish a container's port, or a range of ports, to the host (default [])",
|
||||
)
|
||||
netFlags.Bool(
|
||||
"no-hosts", false,
|
||||
"Do not create /etc/hosts within the container, instead use the version from the image",
|
||||
)
|
||||
return &netFlags
|
||||
}
|
||||
|
||||
func NetFlagsToNetOptions(cmd *cobra.Command) (*entities.NetOptions, error) {
|
||||
var (
|
||||
err error
|
||||
)
|
||||
opts := entities.NetOptions{}
|
||||
opts.AddHosts, err = cmd.Flags().GetStringSlice("add-host")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
servers, err := cmd.Flags().GetStringSlice("dns")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, d := range servers {
|
||||
if d == "none" {
|
||||
opts.DNSHost = true
|
||||
break
|
||||
}
|
||||
opts.DNSServers = append(opts.DNSServers, net.ParseIP(d))
|
||||
}
|
||||
opts.DNSSearch, err = cmd.Flags().GetStringSlice("dns-search")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m, err := cmd.Flags().GetString("mac-address")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(m) > 0 {
|
||||
mac, err := net.ParseMAC(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts.StaticMAC = &mac
|
||||
}
|
||||
inputPorts, err := cmd.Flags().GetStringSlice("publish")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(inputPorts) > 0 {
|
||||
opts.PublishPorts, err = createPortBindings(inputPorts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
opts.NoHosts, err = cmd.Flags().GetBool("no-hosts")
|
||||
return &opts, err
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package common
|
||||
|
||||
var DefaultKernelNamespaces = "cgroup,ipc,net,uts"
|
|
@ -0,0 +1,43 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/cri-o/ocicni/pkg/ocicni"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// createPortBindings iterates ports mappings and exposed ports into a format CNI understands
|
||||
func createPortBindings(ports []string) ([]ocicni.PortMapping, error) {
|
||||
// TODO wants someone to rewrite this code in the future
|
||||
var portBindings []ocicni.PortMapping
|
||||
// The conversion from []string to natBindings is temporary while mheon reworks the port
|
||||
// deduplication code. Eventually that step will not be required.
|
||||
_, natBindings, err := nat.ParsePortSpecs(ports)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for containerPb, hostPb := range natBindings {
|
||||
var pm ocicni.PortMapping
|
||||
pm.ContainerPort = int32(containerPb.Int())
|
||||
for _, i := range hostPb {
|
||||
var hostPort int
|
||||
var err error
|
||||
pm.HostIP = i.HostIP
|
||||
if i.HostPort == "" {
|
||||
hostPort = containerPb.Int()
|
||||
} else {
|
||||
hostPort, err = strconv.Atoi(i.HostPort)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to convert host port to integer")
|
||||
}
|
||||
}
|
||||
|
||||
pm.HostPort = int32(hostPort)
|
||||
pm.Protocol = containerPb.Proto()
|
||||
portBindings = append(portBindings, pm)
|
||||
}
|
||||
}
|
||||
return portBindings, nil
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
package pods
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/libpod/cmd/podmanV2/common"
|
||||
"github.com/containers/libpod/cmd/podmanV2/parse"
|
||||
"github.com/containers/libpod/cmd/podmanV2/registry"
|
||||
"github.com/containers/libpod/libpod/define"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/containers/libpod/pkg/errorhandling"
|
||||
"github.com/containers/libpod/pkg/specgen"
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
podCreateDescription = `After creating the pod, the pod ID is printed to stdout.
|
||||
|
||||
You can then start it at any time with the podman pod start <pod_id> command. The pod will be created with the initial state 'created'.`
|
||||
|
||||
createCommand = &cobra.Command{
|
||||
Use: "create",
|
||||
Args: cobra.NoArgs,
|
||||
Short: "Create a new empty pod",
|
||||
Long: podCreateDescription,
|
||||
RunE: create,
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
createOptions entities.PodCreateOptions
|
||||
labels, labelFile []string
|
||||
podIDFile string
|
||||
share string
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||
Command: createCommand,
|
||||
Parent: podCmd,
|
||||
})
|
||||
flags := createCommand.Flags()
|
||||
flags.SetInterspersed(false)
|
||||
flags.AddFlagSet(common.GetNetFlags())
|
||||
flags.StringVar(&createOptions.CGroupParent, "cgroup-parent", "", "Set parent cgroup for the pod")
|
||||
flags.BoolVar(&createOptions.Infra, "infra", true, "Create an infra container associated with the pod to share namespaces with")
|
||||
flags.StringVar(&createOptions.InfraImage, "infra-image", define.DefaultInfraImage, "The image of the infra container to associate with the pod")
|
||||
flags.StringVar(&createOptions.InfraCommand, "infra-command", define.DefaultInfraCommand, "The command to run on the infra container when the pod is started")
|
||||
flags.StringSliceVar(&labelFile, "label-file", []string{}, "Read in a line delimited file of labels")
|
||||
flags.StringSliceVarP(&labels, "label", "l", []string{}, "Set metadata on pod (default [])")
|
||||
flags.StringVarP(&createOptions.Name, "name", "n", "", "Assign a name to the pod")
|
||||
flags.StringVarP(&createOptions.Hostname, "hostname", "", "", "Set a hostname to the pod")
|
||||
flags.StringVar(&podIDFile, "pod-id-file", "", "Write the pod ID to the file")
|
||||
flags.StringVar(&share, "share", common.DefaultKernelNamespaces, "A comma delimited list of kernel namespaces the pod will share")
|
||||
}
|
||||
|
||||
func create(cmd *cobra.Command, args []string) error {
|
||||
var (
|
||||
err error
|
||||
podIdFile *os.File
|
||||
)
|
||||
createOptions.Labels, err = parse.GetAllLabels(labelFile, labels)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to process labels")
|
||||
}
|
||||
|
||||
if !createOptions.Infra && cmd.Flag("share").Changed && share != "none" && share != "" {
|
||||
return errors.Errorf("You cannot share kernel namespaces on the pod level without an infra container")
|
||||
}
|
||||
createOptions.Share = strings.Split(share, ",")
|
||||
if cmd.Flag("pod-id-file").Changed {
|
||||
podIdFile, err = util.OpenExclusiveFile(podIDFile)
|
||||
if err != nil && os.IsExist(err) {
|
||||
return errors.Errorf("pod id file exists. Ensure another pod is not using it or delete %s", podIDFile)
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Errorf("error opening pod-id-file %s", podIDFile)
|
||||
}
|
||||
defer errorhandling.CloseQuiet(podIdFile)
|
||||
defer errorhandling.SyncQuiet(podIdFile)
|
||||
}
|
||||
|
||||
createOptions.Net, err = common.NetFlagsToNetOptions(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
netInput, err := cmd.Flags().GetString("network")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n := specgen.Namespace{}
|
||||
switch netInput {
|
||||
case "bridge":
|
||||
n.NSMode = specgen.Bridge
|
||||
case "host":
|
||||
n.NSMode = specgen.Host
|
||||
case "slip4netns":
|
||||
n.NSMode = specgen.Slirp
|
||||
default:
|
||||
if strings.HasPrefix(netInput, "container:") { //nolint
|
||||
split := strings.Split(netInput, ":")
|
||||
if len(split) != 2 {
|
||||
return errors.Errorf("invalid network paramater: %q", netInput)
|
||||
}
|
||||
n.NSMode = specgen.FromContainer
|
||||
n.Value = split[1]
|
||||
} else if strings.HasPrefix(netInput, "ns:") {
|
||||
return errors.New("the ns: network option is not supported for pods")
|
||||
} else {
|
||||
n.NSMode = specgen.Bridge
|
||||
createOptions.Net.CNINetworks = strings.Split(netInput, ",")
|
||||
}
|
||||
}
|
||||
if len(createOptions.Net.PublishPorts) > 0 {
|
||||
if !createOptions.Infra {
|
||||
return errors.Errorf("you must have an infra container to publish port bindings to the host")
|
||||
}
|
||||
}
|
||||
|
||||
response, err := registry.ContainerEngine().PodCreate(context.Background(), createOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(response.Id)
|
||||
return nil
|
||||
}
|
|
@ -5,7 +5,6 @@ import (
|
|||
|
||||
"github.com/containers/libpod/libpod/define"
|
||||
"github.com/containers/libpod/libpod/events"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -130,10 +129,8 @@ func (r *Runtime) GetAllVolumes() ([]*Volume, error) {
|
|||
}
|
||||
|
||||
// PruneVolumes removes unused volumes from the system
|
||||
func (r *Runtime) PruneVolumes(ctx context.Context) ([]*entities.VolumePruneReport, error) {
|
||||
var (
|
||||
reports []*entities.VolumePruneReport
|
||||
)
|
||||
func (r *Runtime) PruneVolumes(ctx context.Context) (map[string]error, error) {
|
||||
reports := make(map[string]error)
|
||||
vols, err := r.GetAllVolumes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -142,12 +139,12 @@ func (r *Runtime) PruneVolumes(ctx context.Context) ([]*entities.VolumePruneRepo
|
|||
for _, vol := range vols {
|
||||
if err := r.RemoveVolume(ctx, vol, false); err != nil {
|
||||
if errors.Cause(err) != define.ErrVolumeBeingUsed && errors.Cause(err) != define.ErrVolumeRemoved {
|
||||
reports = append(reports, &entities.VolumePruneReport{Id: vol.Name(), Err: err})
|
||||
reports[vol.Name()] = err
|
||||
}
|
||||
continue
|
||||
}
|
||||
vol.newVolumeEvent(events.Prune)
|
||||
reports = append(reports, &entities.VolumePruneReport{Id: vol.Name()})
|
||||
reports[vol.Name()] = nil
|
||||
}
|
||||
return reports, nil
|
||||
}
|
||||
|
|
|
@ -356,11 +356,11 @@ func (r *LocalRuntime) PruneVolumes(ctx context.Context) ([]string, []error) {
|
|||
errs = append(errs, err)
|
||||
return vids, errs
|
||||
}
|
||||
for _, r := range reports {
|
||||
if r.Err == nil {
|
||||
vids = append(vids, r.Id)
|
||||
for k, v := range reports {
|
||||
if v == nil {
|
||||
vids = append(vids, k)
|
||||
} else {
|
||||
errs = append(errs, r.Err)
|
||||
errs = append(errs, v)
|
||||
}
|
||||
}
|
||||
return vids, errs
|
||||
|
|
|
@ -4,15 +4,13 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/shared"
|
||||
"github.com/containers/libpod/cmd/podman/shared/parse"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/define"
|
||||
"github.com/containers/libpod/pkg/api/handlers"
|
||||
"github.com/containers/libpod/pkg/api/handlers/utils"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/containers/libpod/pkg/specgen"
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/gorilla/schema"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -21,76 +19,14 @@ import (
|
|||
func PodCreate(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
runtime = r.Context().Value("runtime").(*libpod.Runtime)
|
||||
options []libpod.PodCreateOption
|
||||
err error
|
||||
)
|
||||
labels := make(map[string]string)
|
||||
input := handlers.PodCreateConfig{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
|
||||
var psg specgen.PodSpecGenerator
|
||||
if err := json.NewDecoder(r.Body).Decode(&psg); err != nil {
|
||||
utils.Error(w, "Failed to decode specgen", http.StatusInternalServerError, errors.Wrap(err, "failed to decode specgen"))
|
||||
return
|
||||
}
|
||||
if len(input.InfraCommand) > 0 || len(input.InfraImage) > 0 {
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError,
|
||||
errors.New("infra-command and infra-image are not implemented yet"))
|
||||
return
|
||||
}
|
||||
// TODO long term we should break the following out of adapter and into libpod proper
|
||||
// so that the cli and api can share the creation of a pod with the same options
|
||||
if len(input.CGroupParent) > 0 {
|
||||
options = append(options, libpod.WithPodCgroupParent(input.CGroupParent))
|
||||
}
|
||||
|
||||
if len(input.Labels) > 0 {
|
||||
labels, err = parse.GetAllLabels([]string{}, input.Labels)
|
||||
if err != nil {
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(labels) != 0 {
|
||||
options = append(options, libpod.WithPodLabels(labels))
|
||||
}
|
||||
|
||||
if len(input.Name) > 0 {
|
||||
options = append(options, libpod.WithPodName(input.Name))
|
||||
}
|
||||
|
||||
if len(input.Hostname) > 0 {
|
||||
options = append(options, libpod.WithPodHostname(input.Hostname))
|
||||
}
|
||||
|
||||
if input.Infra {
|
||||
// TODO infra-image and infra-command are not supported in the libpod API yet. Will fix
|
||||
// when implemented in libpod
|
||||
options = append(options, libpod.WithInfraContainer())
|
||||
sharedNamespaces := shared.DefaultKernelNamespaces
|
||||
if len(input.Share) > 0 {
|
||||
sharedNamespaces = input.Share
|
||||
}
|
||||
nsOptions, err := shared.GetNamespaceOptions(strings.Split(sharedNamespaces, ","))
|
||||
if err != nil {
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
options = append(options, nsOptions...)
|
||||
}
|
||||
|
||||
if len(input.Publish) > 0 {
|
||||
portBindings, err := shared.CreatePortBindings(input.Publish)
|
||||
if err != nil {
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
options = append(options, libpod.WithInfraContainerPorts(portBindings))
|
||||
|
||||
}
|
||||
// always have containers use pod cgroups
|
||||
// User Opt out is not yet supported
|
||||
options = append(options, libpod.WithPodCgroups())
|
||||
|
||||
pod, err := runtime.NewPod(r.Context(), options...)
|
||||
pod, err := psg.MakePod(runtime)
|
||||
if err != nil {
|
||||
http_code := http.StatusInternalServerError
|
||||
if errors.Cause(err) == define.ErrPodExists {
|
||||
|
|
|
@ -149,13 +149,20 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) {
|
|||
func PruneVolumes(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
runtime = r.Context().Value("runtime").(*libpod.Runtime)
|
||||
reports []*entities.VolumePruneReport
|
||||
)
|
||||
pruned, err := runtime.PruneVolumes(r.Context())
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
utils.WriteResponse(w, http.StatusOK, pruned)
|
||||
for k, v := range pruned {
|
||||
reports = append(reports, &entities.VolumePruneReport{
|
||||
Err: v,
|
||||
Id: k,
|
||||
})
|
||||
}
|
||||
utils.WriteResponse(w, http.StatusOK, reports)
|
||||
}
|
||||
|
||||
func RemoveVolume(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -133,19 +133,6 @@ type ContainerTopOKBody struct {
|
|||
dockerContainer.ContainerTopOKBody
|
||||
}
|
||||
|
||||
// swagger:model PodCreateConfig
|
||||
type PodCreateConfig struct {
|
||||
Name string `json:"name"`
|
||||
CGroupParent string `json:"cgroup-parent"`
|
||||
Hostname string `json:"hostname"`
|
||||
Infra bool `json:"infra"`
|
||||
InfraCommand string `json:"infra-command"`
|
||||
InfraImage string `json:"infra-image"`
|
||||
Labels []string `json:"labels"`
|
||||
Publish []string `json:"publish"`
|
||||
Share string `json:"share"`
|
||||
}
|
||||
|
||||
type ErrorModel struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
|
|||
// description: attributes for creating a pod
|
||||
// schema:
|
||||
// type: object
|
||||
// $ref: "#/definitions/PodCreateConfig"
|
||||
// $ref: "#/definitions/PodSpecGenerator"
|
||||
// responses:
|
||||
// 200:
|
||||
// $ref: "#/definitions/IdResponse"
|
||||
|
|
|
@ -5,15 +5,33 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/pkg/bindings"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/containers/libpod/pkg/specgen"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
func CreatePod() error {
|
||||
// TODO
|
||||
return bindings.ErrNotImplemented
|
||||
func CreatePodFromSpec(ctx context.Context, s *specgen.PodSpecGenerator) (*entities.PodCreateReport, error) {
|
||||
var (
|
||||
pcr entities.PodCreateReport
|
||||
)
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
specgenString, err := jsoniter.MarshalToString(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stringReader := strings.NewReader(specgenString)
|
||||
response, err := conn.DoRequest(stringReader, http.MethodPost, "/pods/create", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pcr, response.Process(&pcr)
|
||||
}
|
||||
|
||||
// Exists is a lightweight method to determine if a pod exists in local storage
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/containers/libpod/libpod/define"
|
||||
"github.com/containers/libpod/pkg/bindings"
|
||||
"github.com/containers/libpod/pkg/bindings/pods"
|
||||
"github.com/containers/libpod/pkg/specgen"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/onsi/gomega/gexec"
|
||||
|
@ -307,4 +308,15 @@ var _ = Describe("Podman pods", func() {
|
|||
Expect(err).To(BeNil())
|
||||
Expect(len(podSummary)).To(Equal(0))
|
||||
})
|
||||
|
||||
It("simple create pod", func() {
|
||||
ps := specgen.PodSpecGenerator{}
|
||||
ps.Name = "foobar"
|
||||
_, err := pods.CreatePodFromSpec(bt.conn, &ps)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
exists, err := pods.Exists(bt.conn, "foobar")
|
||||
Expect(err).To(BeNil())
|
||||
Expect(exists).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
|
|
@ -15,7 +15,7 @@ type ContainerEngine interface {
|
|||
ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error)
|
||||
ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error)
|
||||
ContainerTop(ctx context.Context, options TopOptions) (*StringSliceReport, error)
|
||||
|
||||
PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error)
|
||||
PodExists(ctx context.Context, nameOrId string) (*BoolReport, error)
|
||||
PodKill(ctx context.Context, namesOrIds []string, options PodKillOptions) ([]*PodKillReport, error)
|
||||
PodPause(ctx context.Context, namesOrIds []string, options PodPauseOptions) ([]*PodPauseReport, error)
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
package entities
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/containers/libpod/pkg/specgen"
|
||||
)
|
||||
|
||||
type PodKillOptions struct {
|
||||
All bool
|
||||
|
@ -92,3 +96,48 @@ type PodRmReport struct {
|
|||
Err error
|
||||
Id string
|
||||
}
|
||||
|
||||
type PodCreateOptions struct {
|
||||
CGroupParent string
|
||||
Hostname string
|
||||
Infra bool
|
||||
InfraImage string
|
||||
InfraCommand string
|
||||
Labels map[string]string
|
||||
Name string
|
||||
Net *NetOptions
|
||||
Share []string
|
||||
}
|
||||
|
||||
type PodCreateReport struct {
|
||||
Id string
|
||||
}
|
||||
|
||||
func (p PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) {
|
||||
// Basic Config
|
||||
s.Name = p.Name
|
||||
s.Hostname = p.Hostname
|
||||
s.Labels = p.Labels
|
||||
s.NoInfra = !p.Infra
|
||||
s.InfraCommand = []string{p.InfraCommand}
|
||||
s.InfraImage = p.InfraImage
|
||||
s.SharedNamespaces = p.Share
|
||||
|
||||
// Networking config
|
||||
s.NetNS = p.Net.Network
|
||||
s.StaticIP = p.Net.StaticIP
|
||||
s.StaticMAC = p.Net.StaticMAC
|
||||
s.PortMappings = p.Net.PublishPorts
|
||||
s.CNINetworks = p.Net.CNINetworks
|
||||
if p.Net.DNSHost {
|
||||
s.NoManageResolvConf = true
|
||||
}
|
||||
s.DNSServer = p.Net.DNSServers
|
||||
s.DNSSearch = p.Net.DNSSearch
|
||||
s.DNSOption = p.Net.DNSOptions
|
||||
s.NoManageHosts = p.Net.NoHosts
|
||||
s.HostAdd = p.Net.AddHosts
|
||||
|
||||
// Cgroup
|
||||
s.CgroupParent = p.CGroupParent
|
||||
}
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
package entities
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/containers/libpod/pkg/specgen"
|
||||
"github.com/cri-o/ocicni/pkg/ocicni"
|
||||
)
|
||||
|
||||
type Container struct {
|
||||
IdOrNamed
|
||||
}
|
||||
|
@ -15,3 +22,23 @@ type Report struct {
|
|||
|
||||
type PodDeleteReport struct{ Report }
|
||||
type PodPruneOptions struct{}
|
||||
|
||||
type PodPruneReport struct{ Report }
|
||||
type VolumeDeleteOptions struct{}
|
||||
type VolumeDeleteReport struct{ Report }
|
||||
|
||||
// NetOptions reflect the shared network options between
|
||||
// pods and containers
|
||||
type NetOptions struct {
|
||||
AddHosts []string
|
||||
CNINetworks []string
|
||||
DNSHost bool
|
||||
DNSOptions []string
|
||||
DNSSearch []string
|
||||
DNSServers []net.IP
|
||||
Network specgen.Namespace
|
||||
NoHosts bool
|
||||
PublishPorts []ocicni.PortMapping
|
||||
StaticIP *net.IP
|
||||
StaticMAC *net.HardwareAddr
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/containers/libpod/libpod/define"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/containers/libpod/pkg/signal"
|
||||
"github.com/containers/libpod/pkg/specgen"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
@ -239,3 +240,13 @@ func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, optio
|
|||
}
|
||||
return reports, nil
|
||||
}
|
||||
|
||||
func (ic *ContainerEngine) PodCreate(ctx context.Context, opts entities.PodCreateOptions) (*entities.PodCreateReport, error) {
|
||||
podSpec := specgen.NewPodSpecGenerator()
|
||||
opts.ToPodSpecGen(podSpec)
|
||||
pod, err := podSpec.MakePod(ic.Libpod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &entities.PodCreateReport{Id: pod.ID()}, nil
|
||||
}
|
||||
|
|
|
@ -113,7 +113,20 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
|
|||
}
|
||||
|
||||
func (ic *ContainerEngine) VolumePrune(ctx context.Context, opts entities.VolumePruneOptions) ([]*entities.VolumePruneReport, error) {
|
||||
return ic.Libpod.PruneVolumes(ctx)
|
||||
var (
|
||||
reports []*entities.VolumePruneReport
|
||||
)
|
||||
pruned, err := ic.Libpod.PruneVolumes(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range pruned {
|
||||
reports = append(reports, &entities.VolumePruneReport{
|
||||
Err: v,
|
||||
Id: k,
|
||||
})
|
||||
}
|
||||
return reports, nil
|
||||
}
|
||||
|
||||
func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeListOptions) ([]*entities.VolumeListReport, error) {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/containers/libpod/pkg/bindings/pods"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/containers/libpod/pkg/specgen"
|
||||
)
|
||||
|
||||
func (ic *ContainerEngine) PodExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) {
|
||||
|
@ -170,3 +171,9 @@ func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, optio
|
|||
}
|
||||
return reports, nil
|
||||
}
|
||||
|
||||
func (ic *ContainerEngine) PodCreate(ctx context.Context, opts entities.PodCreateOptions) (*entities.PodCreateReport, error) {
|
||||
podSpec := specgen.NewPodSpecGenerator()
|
||||
opts.ToPodSpecGen(podSpec)
|
||||
return pods.CreatePodFromSpec(ic.ClientCxt, podSpec)
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
|
||||
// MakeContainer creates a container based on the SpecGenerator
|
||||
func (s *SpecGenerator) MakeContainer(rt *libpod.Runtime) (*libpod.Container, error) {
|
||||
if err := s.validate(rt); err != nil {
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, errors.Wrap(err, "invalid config provided")
|
||||
}
|
||||
rtc, err := rt.GetConfig()
|
|
@ -4,8 +4,6 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/containers/libpod/pkg/rootless"
|
||||
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
@ -25,7 +23,7 @@ func exclusiveOptions(opt1, opt2 string) error {
|
|||
|
||||
// Validate verifies that the given SpecGenerator is valid and satisfies required
|
||||
// input for creating a container.
|
||||
func (s *SpecGenerator) validate(rt *libpod.Runtime) error {
|
||||
func (s *SpecGenerator) validate() error {
|
||||
|
||||
//
|
||||
// ContainerBasicConfig
|
|
@ -0,0 +1,83 @@
|
|||
package specgen
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/shared"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (p *PodSpecGenerator) MakePod(rt *libpod.Runtime) (*libpod.Pod, error) {
|
||||
if err := p.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options, err := p.createPodOptions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rt.NewPod(context.Background(), options...)
|
||||
}
|
||||
|
||||
func (p *PodSpecGenerator) createPodOptions() ([]libpod.PodCreateOption, error) {
|
||||
var (
|
||||
options []libpod.PodCreateOption
|
||||
)
|
||||
if !p.NoInfra {
|
||||
options = append(options, libpod.WithInfraContainer())
|
||||
nsOptions, err := shared.GetNamespaceOptions(p.SharedNamespaces)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options = append(options, nsOptions...)
|
||||
}
|
||||
if len(p.CgroupParent) > 0 {
|
||||
options = append(options, libpod.WithPodCgroupParent(p.CgroupParent))
|
||||
}
|
||||
if len(p.Labels) > 0 {
|
||||
options = append(options, libpod.WithPodLabels(p.Labels))
|
||||
}
|
||||
if len(p.Name) > 0 {
|
||||
options = append(options, libpod.WithPodName(p.Name))
|
||||
}
|
||||
if len(p.Hostname) > 0 {
|
||||
options = append(options, libpod.WithPodHostname(p.Hostname))
|
||||
}
|
||||
if len(p.HostAdd) > 0 {
|
||||
options = append(options, libpod.WithPodHosts(p.HostAdd))
|
||||
}
|
||||
if len(p.DNSOption) > 0 {
|
||||
options = append(options, libpod.WithPodDNSOption(p.DNSOption))
|
||||
}
|
||||
if len(p.DNSSearch) > 0 {
|
||||
options = append(options, libpod.WithPodDNSSearch(p.DNSSearch))
|
||||
}
|
||||
if p.StaticIP != nil {
|
||||
options = append(options, libpod.WithPodStaticIP(*p.StaticIP))
|
||||
}
|
||||
if p.StaticMAC != nil {
|
||||
options = append(options, libpod.WithPodStaticMAC(*p.StaticMAC))
|
||||
}
|
||||
if p.NoManageResolvConf {
|
||||
options = append(options, libpod.WithPodUseImageResolvConf())
|
||||
}
|
||||
switch p.NetNS.NSMode {
|
||||
case Bridge:
|
||||
logrus.Debugf("Pod using default network mode")
|
||||
case Host:
|
||||
logrus.Debugf("Pod will use host networking")
|
||||
options = append(options, libpod.WithPodHostNetwork())
|
||||
default:
|
||||
logrus.Debugf("Pod joining CNI networks: %v", p.CNINetworks)
|
||||
options = append(options, libpod.WithPodNetworks(p.CNINetworks))
|
||||
}
|
||||
|
||||
if p.NoManageHosts {
|
||||
options = append(options, libpod.WithPodUseImageHosts())
|
||||
}
|
||||
if len(p.PortMappings) > 0 {
|
||||
options = append(options, libpod.WithInfraContainerPorts(p.PortMappings))
|
||||
}
|
||||
options = append(options, libpod.WithPodCgroups())
|
||||
return options, nil
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package specgen
|
||||
|
||||
import (
|
||||
"github.com/containers/libpod/libpod/define"
|
||||
"github.com/containers/libpod/pkg/rootless"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidPodSpecConfig describes an error given when the podspecgenerator is invalid
|
||||
ErrInvalidPodSpecConfig error = errors.New("invalid pod spec")
|
||||
)
|
||||
|
||||
func exclusivePodOptions(opt1, opt2 string) error {
|
||||
return errors.Wrapf(ErrInvalidPodSpecConfig, "%s and %s are mutually exclusive pod options", opt1, opt2)
|
||||
}
|
||||
|
||||
func (p *PodSpecGenerator) validate() error {
|
||||
// PodBasicConfig
|
||||
if p.NoInfra {
|
||||
if len(p.InfraCommand) > 0 {
|
||||
return exclusivePodOptions("NoInfra", "InfraCommand")
|
||||
}
|
||||
if len(p.InfraImage) > 0 {
|
||||
return exclusivePodOptions("NoInfra", "InfraImage")
|
||||
}
|
||||
if len(p.SharedNamespaces) > 0 {
|
||||
return exclusivePodOptions("NoInfo", "SharedNamespaces")
|
||||
}
|
||||
}
|
||||
|
||||
// PodNetworkConfig
|
||||
if err := p.NetNS.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
if p.NoInfra {
|
||||
if p.NetNS.NSMode == NoNetwork {
|
||||
return errors.New("NoInfra and a none network cannot be used toegther")
|
||||
}
|
||||
if p.StaticIP != nil {
|
||||
return exclusivePodOptions("NoInfra", "StaticIP")
|
||||
}
|
||||
if p.StaticMAC != nil {
|
||||
return exclusivePodOptions("NoInfra", "StaticMAC")
|
||||
}
|
||||
if len(p.DNSOption) > 0 {
|
||||
return exclusivePodOptions("NoInfra", "DNSOption")
|
||||
}
|
||||
if len(p.DNSSearch) > 0 {
|
||||
return exclusivePodOptions("NoInfo", "DNSSearch")
|
||||
}
|
||||
if len(p.DNSServer) > 0 {
|
||||
return exclusivePodOptions("NoInfra", "DNSServer")
|
||||
}
|
||||
if len(p.HostAdd) > 0 {
|
||||
return exclusivePodOptions("NoInfra", "HostAdd")
|
||||
}
|
||||
if p.NoManageResolvConf {
|
||||
return exclusivePodOptions("NoInfra", "NoManageResolvConf")
|
||||
}
|
||||
}
|
||||
if p.NetNS.NSMode != Bridge {
|
||||
if len(p.PortMappings) > 0 {
|
||||
return errors.New("PortMappings can only be used with Bridge mode networking")
|
||||
}
|
||||
if len(p.CNINetworks) > 0 {
|
||||
return errors.New("CNINetworks can only be used with Bridge mode networking")
|
||||
}
|
||||
}
|
||||
if p.NoManageResolvConf {
|
||||
if len(p.DNSServer) > 0 {
|
||||
return exclusivePodOptions("NoManageResolvConf", "DNSServer")
|
||||
}
|
||||
if len(p.DNSSearch) > 0 {
|
||||
return exclusivePodOptions("NoManageResolvConf", "DNSSearch")
|
||||
}
|
||||
if len(p.DNSOption) > 0 {
|
||||
return exclusivePodOptions("NoManageResolvConf", "DNSOption")
|
||||
}
|
||||
}
|
||||
if p.NoManageHosts && len(p.HostAdd) > 0 {
|
||||
return exclusivePodOptions("NoManageHosts", "HostAdd")
|
||||
}
|
||||
|
||||
if err := p.NetNS.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set Defaults
|
||||
if p.NetNS.Value == "" {
|
||||
if rootless.IsRootless() {
|
||||
p.NetNS.NSMode = Slirp
|
||||
} else {
|
||||
p.NetNS.NSMode = Bridge
|
||||
}
|
||||
}
|
||||
if len(p.InfraImage) < 1 {
|
||||
p.InfraImage = define.DefaultInfraImage
|
||||
}
|
||||
if len(p.InfraCommand) < 1 {
|
||||
p.InfraCommand = []string{define.DefaultInfraCommand}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -138,3 +138,16 @@ type PodCgroupConfig struct {
|
|||
// Optional.
|
||||
CgroupParent string `json:"cgroup_parent,omitempty"`
|
||||
}
|
||||
|
||||
// PodSpecGenerator describes options to create a pod
|
||||
// swagger:model PodSpecGenerator
|
||||
type PodSpecGenerator struct {
|
||||
PodBasicConfig
|
||||
PodNetworkConfig
|
||||
PodCgroupConfig
|
||||
}
|
||||
|
||||
// NewPodSpecGenerator creates a new pod spec
|
||||
func NewPodSpecGenerator() *PodSpecGenerator {
|
||||
return &PodSpecGenerator{}
|
||||
}
|
|
@ -394,18 +394,18 @@ type SpecGenerator struct {
|
|||
|
||||
// NewSpecGenerator returns a SpecGenerator struct given one of two mandatory inputs
|
||||
func NewSpecGenerator(image string) *SpecGenerator {
|
||||
net := ContainerNetworkConfig{
|
||||
networkConfig := ContainerNetworkConfig{
|
||||
NetNS: Namespace{
|
||||
NSMode: Bridge,
|
||||
},
|
||||
}
|
||||
csc := ContainerStorageConfig{Image: image}
|
||||
if rootless.IsRootless() {
|
||||
net.NetNS.NSMode = Slirp
|
||||
networkConfig.NetNS.NSMode = Slirp
|
||||
}
|
||||
return &SpecGenerator{
|
||||
ContainerStorageConfig: csc,
|
||||
ContainerNetworkConfig: net,
|
||||
ContainerNetworkConfig: networkConfig,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"encoding/json"
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/shared"
|
||||
"github.com/containers/libpod/cmd/podman/varlink"
|
||||
iopodman "github.com/containers/libpod/cmd/podman/varlink"
|
||||
"github.com/containers/libpod/libpod"
|
||||
)
|
||||
|
||||
|
@ -113,11 +113,11 @@ func (i *LibpodAPI) VolumesPrune(call iopodman.VarlinkCall) error {
|
|||
if err != nil {
|
||||
return call.ReplyVolumesPrune([]string{}, []string{err.Error()})
|
||||
}
|
||||
for _, i := range responses {
|
||||
if i.Err == nil {
|
||||
prunedNames = append(prunedNames, i.Id)
|
||||
for k, v := range responses {
|
||||
if v == nil {
|
||||
prunedNames = append(prunedNames, k)
|
||||
} else {
|
||||
prunedErrors = append(prunedErrors, i.Err.Error())
|
||||
prunedErrors = append(prunedErrors, v.Error())
|
||||
}
|
||||
}
|
||||
return call.ReplyVolumesPrune(prunedNames, prunedErrors)
|
||||
|
|
|
@ -12,11 +12,11 @@ t GET libpod/pods/notfoo/exists 404
|
|||
t GET libpod/pods/foo/json 200 \
|
||||
.Config.name=foo \
|
||||
.Config.id=$pod_id \
|
||||
.Containers=null
|
||||
.Containers\|length=1
|
||||
t GET libpod/pods/json 200 \
|
||||
.[0].Name=foo \
|
||||
.[0].Id=$pod_id \
|
||||
.[0].Containers=null
|
||||
.[0].Containers\|length=1
|
||||
|
||||
# Cannot create a dup pod with the same name
|
||||
t POST libpod/pods/create name=foo 409 .cause="pod already exists"
|
||||
|
@ -34,7 +34,7 @@ fi
|
|||
t POST libpod/pods/foo/unpause '' 200
|
||||
t POST libpod/pods/foo/unpause '' 200 # (2nd time)
|
||||
t POST libpod/pods/foo/stop '' 304
|
||||
t POST libpod/pods/foo/restart '' 500 .cause="no such container"
|
||||
t POST libpod/pods/foo/restart '' 200
|
||||
|
||||
t POST libpod/pods/bar/restart '' 404
|
||||
|
||||
|
|
Loading…
Reference in New Issue