mirror of
https://github.com/containers/podman.git
synced 2025-05-17 23:26:08 +08:00

This requires updating all import paths throughout, and a matching buildah update to interoperate. I can't figure out the reason for go.mod tracking github.com/containers/image v3.0.2+incompatible // indirect ((go mod graph) lists it as a direct dependency of libpod, but (go list -json -m all) lists it as an indirect dependency), but at least looking at the vendor subdirectory, it doesn't seem to be actually used in the built binaries. Signed-off-by: Miloslav Trmač <mitr@redhat.com>
1003 lines
28 KiB
Go
1003 lines
28 KiB
Go
// +build remoteclient
|
|
|
|
package adapter
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"text/template"
|
|
"time"
|
|
|
|
"github.com/containers/buildah/imagebuildah"
|
|
"github.com/containers/buildah/pkg/formats"
|
|
"github.com/containers/image/v4/docker/reference"
|
|
"github.com/containers/image/v4/types"
|
|
"github.com/containers/libpod/cmd/podman/cliconfig"
|
|
"github.com/containers/libpod/cmd/podman/remoteclientconfig"
|
|
iopodman "github.com/containers/libpod/cmd/podman/varlink"
|
|
"github.com/containers/libpod/libpod"
|
|
"github.com/containers/libpod/libpod/define"
|
|
"github.com/containers/libpod/libpod/events"
|
|
"github.com/containers/libpod/libpod/image"
|
|
"github.com/containers/libpod/pkg/util"
|
|
"github.com/containers/libpod/utils"
|
|
"github.com/containers/storage/pkg/archive"
|
|
"github.com/opencontainers/go-digest"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/varlink/go/varlink"
|
|
v1 "k8s.io/api/core/v1"
|
|
)
|
|
|
|
// ImageRuntime is wrapper for image runtime
|
|
type RemoteImageRuntime struct{}
|
|
|
|
// RemoteRuntime describes a wrapper runtime struct
|
|
type RemoteRuntime struct {
|
|
Conn *varlink.Connection
|
|
Remote bool
|
|
cmd cliconfig.MainFlags
|
|
config io.Reader
|
|
}
|
|
|
|
// LocalRuntime describes a typical libpod runtime
|
|
type LocalRuntime struct {
|
|
*RemoteRuntime
|
|
}
|
|
|
|
// GetRuntimeNoStore returns a LocalRuntime struct with the actual runtime embedded in it
|
|
// The nostore is ignored
|
|
func GetRuntimeNoStore(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime, error) {
|
|
return GetRuntime(ctx, c)
|
|
}
|
|
|
|
// GetRuntime returns a LocalRuntime struct with the actual runtime embedded in it
|
|
func GetRuntime(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime, error) {
|
|
var (
|
|
customConfig bool
|
|
err error
|
|
f *os.File
|
|
)
|
|
runtime := RemoteRuntime{
|
|
Remote: true,
|
|
cmd: c.GlobalFlags,
|
|
}
|
|
configPath := remoteclientconfig.GetConfigFilePath()
|
|
// Check if the basedir for configPath exists and if not, create it.
|
|
if _, err := os.Stat(filepath.Dir(configPath)); os.IsNotExist(err) {
|
|
if mkdirErr := os.MkdirAll(filepath.Dir(configPath), 0750); mkdirErr != nil {
|
|
return nil, mkdirErr
|
|
}
|
|
}
|
|
if len(c.GlobalFlags.RemoteConfigFilePath) > 0 {
|
|
configPath = c.GlobalFlags.RemoteConfigFilePath
|
|
customConfig = true
|
|
}
|
|
|
|
f, err = os.Open(configPath)
|
|
if err != nil {
|
|
// If user does not explicitly provide a configuration file path and we cannot
|
|
// find a default, no error should occur.
|
|
if os.IsNotExist(err) && !customConfig {
|
|
logrus.Debugf("unable to load configuration file at %s", configPath)
|
|
runtime.config = nil
|
|
} else {
|
|
return nil, errors.Wrapf(err, "unable to load configuration file at %s", configPath)
|
|
}
|
|
} else {
|
|
// create the io reader for the remote client
|
|
runtime.config = bufio.NewReader(f)
|
|
}
|
|
conn, err := runtime.Connect()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
runtime.Conn = conn
|
|
return &LocalRuntime{
|
|
&runtime,
|
|
}, nil
|
|
}
|
|
|
|
// DeferredShutdown is a bogus wrapper for compaat with the libpod
|
|
// runtime and should only be run when a defer is being used
|
|
func (r RemoteRuntime) DeferredShutdown(force bool) {
|
|
if err := r.Shutdown(force); err != nil {
|
|
logrus.Error("unable to shutdown runtime")
|
|
}
|
|
}
|
|
|
|
// RuntimeConfig is a bogus wrapper for compat with the libpod runtime
|
|
type RuntimeConfig struct {
|
|
// CGroupManager is the CGroup Manager to use
|
|
// Valid values are "cgroupfs" and "systemd"
|
|
CgroupManager string
|
|
}
|
|
|
|
// Shutdown is a bogus wrapper for compat with the libpod runtime
|
|
func (r *RemoteRuntime) GetConfig() (*RuntimeConfig, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
// Shutdown is a bogus wrapper for compat with the libpod runtime
|
|
func (r RemoteRuntime) Shutdown(force bool) error {
|
|
return nil
|
|
}
|
|
|
|
// ContainerImage
|
|
type ContainerImage struct {
|
|
remoteImage
|
|
}
|
|
|
|
type remoteImage struct {
|
|
ID string
|
|
Labels map[string]string
|
|
RepoTags []string
|
|
RepoDigests []string
|
|
Parent string
|
|
Size int64
|
|
Created time.Time
|
|
InputName string
|
|
Names []string
|
|
Digest digest.Digest
|
|
isParent bool
|
|
Runtime *LocalRuntime
|
|
TopLayer string
|
|
ReadOnly bool
|
|
}
|
|
|
|
// Container ...
|
|
type Container struct {
|
|
remoteContainer
|
|
}
|
|
|
|
// remoteContainer ....
|
|
type remoteContainer struct {
|
|
Runtime *LocalRuntime
|
|
config *libpod.ContainerConfig
|
|
state *libpod.ContainerState
|
|
}
|
|
|
|
// Pod ...
|
|
type Pod struct {
|
|
remotepod
|
|
}
|
|
|
|
type remotepod struct {
|
|
config *libpod.PodConfig
|
|
state *libpod.PodInspectState
|
|
containers []libpod.PodContainerInfo
|
|
Runtime *LocalRuntime
|
|
}
|
|
|
|
type VolumeFilter func(*Volume) bool
|
|
|
|
// Volume is embed for libpod volumes
|
|
type Volume struct {
|
|
remoteVolume
|
|
}
|
|
|
|
type remoteVolume struct {
|
|
Runtime *LocalRuntime
|
|
config *libpod.VolumeConfig
|
|
}
|
|
|
|
// GetImages returns a slice of containerimages over a varlink connection
|
|
func (r *LocalRuntime) GetImages() ([]*ContainerImage, error) {
|
|
return r.getImages(false)
|
|
}
|
|
|
|
// GetRWImages returns a slice of read/write containerimages over a varlink connection
|
|
func (r *LocalRuntime) GetRWImages() ([]*ContainerImage, error) {
|
|
return r.getImages(true)
|
|
}
|
|
|
|
func (r *LocalRuntime) getImages(rwOnly bool) ([]*ContainerImage, error) {
|
|
var newImages []*ContainerImage
|
|
images, err := iopodman.ListImages().Call(r.Conn)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, i := range images {
|
|
if rwOnly && i.ReadOnly {
|
|
continue
|
|
}
|
|
name := i.Id
|
|
if len(i.RepoTags) > 1 {
|
|
name = i.RepoTags[0]
|
|
}
|
|
newImage, err := imageInListToContainerImage(i, name, r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
newImages = append(newImages, newImage)
|
|
}
|
|
return newImages, nil
|
|
}
|
|
|
|
func imageInListToContainerImage(i iopodman.Image, name string, runtime *LocalRuntime) (*ContainerImage, error) {
|
|
created, err := time.ParseInLocation(time.RFC3339, i.Created, time.UTC)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ri := remoteImage{
|
|
InputName: name,
|
|
ID: i.Id,
|
|
Digest: digest.Digest(i.Digest),
|
|
Labels: i.Labels,
|
|
RepoTags: i.RepoTags,
|
|
RepoDigests: i.RepoTags,
|
|
Parent: i.ParentId,
|
|
Size: i.Size,
|
|
Created: created,
|
|
Names: i.RepoTags,
|
|
isParent: i.IsParent,
|
|
Runtime: runtime,
|
|
TopLayer: i.TopLayer,
|
|
ReadOnly: i.ReadOnly,
|
|
}
|
|
return &ContainerImage{ri}, nil
|
|
}
|
|
|
|
// NewImageFromLocal returns a container image representation of a image over varlink
|
|
func (r *LocalRuntime) NewImageFromLocal(name string) (*ContainerImage, error) {
|
|
img, err := iopodman.GetImage().Call(r.Conn, name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return imageInListToContainerImage(img, name, r)
|
|
|
|
}
|
|
|
|
// LoadFromArchiveReference creates an image from a local archive
|
|
func (r *LocalRuntime) LoadFromArchiveReference(ctx context.Context, srcRef types.ImageReference, signaturePolicyPath string, writer io.Writer) ([]*ContainerImage, error) {
|
|
var iid string
|
|
reply, err := iopodman.PullImage().Send(r.Conn, varlink.More, srcRef.DockerReference().String())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for {
|
|
responses, flags, err := reply()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, line := range responses.Logs {
|
|
fmt.Print(line)
|
|
}
|
|
iid = responses.Id
|
|
if flags&varlink.Continues == 0 {
|
|
break
|
|
}
|
|
}
|
|
|
|
newImage, err := r.NewImageFromLocal(iid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return []*ContainerImage{newImage}, nil
|
|
}
|
|
|
|
// New calls into local storage to look for an image in local storage or to pull it
|
|
func (r *LocalRuntime) New(ctx context.Context, name, signaturePolicyPath, authfile string, writer io.Writer, dockeroptions *image.DockerRegistryOptions, signingoptions image.SigningOptions, label *string, pullType util.PullType) (*ContainerImage, error) {
|
|
var iid string
|
|
if label != nil {
|
|
return nil, errors.New("the remote client function does not support checking a remote image for a label")
|
|
}
|
|
reply, err := iopodman.PullImage().Send(r.Conn, varlink.More, name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for {
|
|
responses, flags, err := reply()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, line := range responses.Logs {
|
|
fmt.Print(line)
|
|
}
|
|
iid = responses.Id
|
|
if flags&varlink.Continues == 0 {
|
|
break
|
|
}
|
|
}
|
|
newImage, err := r.NewImageFromLocal(iid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return newImage, nil
|
|
}
|
|
|
|
// IsParent goes through the layers in the store and checks if i.TopLayer is
|
|
// the parent of any other layer in store. Double check that image with that
|
|
// layer exists as well.
|
|
func (ci *ContainerImage) IsParent(context.Context) (bool, error) {
|
|
return ci.remoteImage.isParent, nil
|
|
}
|
|
|
|
// ID returns the image ID as a string
|
|
func (ci *ContainerImage) ID() string {
|
|
return ci.remoteImage.ID
|
|
}
|
|
|
|
// Names returns a string array of names associated with the image
|
|
func (ci *ContainerImage) Names() []string {
|
|
return ci.remoteImage.Names
|
|
}
|
|
|
|
// Created returns the time the image was created
|
|
func (ci *ContainerImage) Created() time.Time {
|
|
return ci.remoteImage.Created
|
|
}
|
|
|
|
// IsReadOnly returns whether the image is ReadOnly
|
|
func (ci *ContainerImage) IsReadOnly() bool {
|
|
return ci.remoteImage.ReadOnly
|
|
}
|
|
|
|
// Size returns the size of the image
|
|
func (ci *ContainerImage) Size(ctx context.Context) (*uint64, error) {
|
|
usize := uint64(ci.remoteImage.Size)
|
|
return &usize, nil
|
|
}
|
|
|
|
// Digest returns the image's digest
|
|
func (ci *ContainerImage) Digest() digest.Digest {
|
|
return ci.remoteImage.Digest
|
|
}
|
|
|
|
// Labels returns a map of the image's labels
|
|
func (ci *ContainerImage) Labels(ctx context.Context) (map[string]string, error) {
|
|
return ci.remoteImage.Labels, nil
|
|
}
|
|
|
|
// Dangling returns a bool if the image is "dangling"
|
|
func (ci *ContainerImage) Dangling() bool {
|
|
return len(ci.Names()) == 0
|
|
}
|
|
|
|
// TopLayer returns an images top layer as a string
|
|
func (ci *ContainerImage) TopLayer() string {
|
|
return ci.remoteImage.TopLayer
|
|
}
|
|
|
|
// TagImage ...
|
|
func (ci *ContainerImage) TagImage(tag string) error {
|
|
_, err := iopodman.TagImage().Call(ci.Runtime.Conn, ci.ID(), tag)
|
|
return err
|
|
}
|
|
|
|
// RemoveImage calls varlink to remove an image
|
|
func (r *LocalRuntime) RemoveImage(ctx context.Context, img *ContainerImage, force bool) (string, error) {
|
|
return iopodman.RemoveImage().Call(r.Conn, img.InputName, force)
|
|
}
|
|
|
|
// History returns the history of an image and its layers
|
|
func (ci *ContainerImage) History(ctx context.Context) ([]*image.History, error) {
|
|
var imageHistories []*image.History
|
|
|
|
reply, err := iopodman.HistoryImage().Call(ci.Runtime.Conn, ci.InputName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, h := range reply {
|
|
created, err := time.ParseInLocation(time.RFC3339, h.Created, time.UTC)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ih := image.History{
|
|
ID: h.Id,
|
|
Created: &created,
|
|
CreatedBy: h.CreatedBy,
|
|
Size: h.Size,
|
|
Comment: h.Comment,
|
|
}
|
|
imageHistories = append(imageHistories, &ih)
|
|
}
|
|
return imageHistories, nil
|
|
}
|
|
|
|
// PruneImages is the wrapper call for a remote-client to prune images
|
|
func (r *LocalRuntime) PruneImages(ctx context.Context, all bool) ([]string, error) {
|
|
return iopodman.ImagesPrune().Call(r.Conn, all)
|
|
}
|
|
|
|
// Export is a wrapper to container export to a tarfile
|
|
func (r *LocalRuntime) Export(name string, path string) error {
|
|
tempPath, err := iopodman.ExportContainer().Call(r.Conn, name, "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return r.GetFileFromRemoteHost(tempPath, path, true)
|
|
}
|
|
|
|
func (r *LocalRuntime) GetFileFromRemoteHost(remoteFilePath, outputPath string, delete bool) error {
|
|
outputFile, err := os.Create(outputPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer outputFile.Close()
|
|
|
|
writer := bufio.NewWriter(outputFile)
|
|
defer writer.Flush()
|
|
|
|
reply, err := iopodman.ReceiveFile().Send(r.Conn, varlink.Upgrade, remoteFilePath, delete)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
length, _, err := reply()
|
|
if err != nil {
|
|
return errors.Wrap(err, "unable to get file length for transfer")
|
|
}
|
|
|
|
reader := r.Conn.Reader
|
|
if _, err := io.CopyN(writer, reader, length); err != nil {
|
|
return errors.Wrap(err, "file transer failed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Import implements the remote calls required to import a container image to the store
|
|
func (r *LocalRuntime) Import(ctx context.Context, source, reference string, changes []string, history string, quiet bool) (string, error) {
|
|
// First we send the file to the host
|
|
tempFile, err := r.SendFileOverVarlink(source)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return iopodman.ImportImage().Call(r.Conn, strings.TrimRight(tempFile, ":"), reference, history, changes, true)
|
|
}
|
|
|
|
func (r *LocalRuntime) Build(ctx context.Context, c *cliconfig.BuildValues, options imagebuildah.BuildOptions, dockerfiles []string) error {
|
|
buildOptions := iopodman.BuildOptions{
|
|
AddHosts: options.CommonBuildOpts.AddHost,
|
|
CgroupParent: options.CommonBuildOpts.CgroupParent,
|
|
CpuPeriod: int64(options.CommonBuildOpts.CPUPeriod),
|
|
CpuQuota: options.CommonBuildOpts.CPUQuota,
|
|
CpuShares: int64(options.CommonBuildOpts.CPUShares),
|
|
CpusetCpus: options.CommonBuildOpts.CPUSetMems,
|
|
CpusetMems: options.CommonBuildOpts.CPUSetMems,
|
|
Memory: options.CommonBuildOpts.Memory,
|
|
MemorySwap: options.CommonBuildOpts.MemorySwap,
|
|
ShmSize: options.CommonBuildOpts.ShmSize,
|
|
Ulimit: options.CommonBuildOpts.Ulimit,
|
|
Volume: options.CommonBuildOpts.Volumes,
|
|
}
|
|
|
|
buildinfo := iopodman.BuildInfo{
|
|
AdditionalTags: options.AdditionalTags,
|
|
Annotations: options.Annotations,
|
|
BuildArgs: options.Args,
|
|
BuildOptions: buildOptions,
|
|
CniConfigDir: options.CNIConfigDir,
|
|
CniPluginDir: options.CNIPluginPath,
|
|
Compression: string(options.Compression),
|
|
DefaultsMountFilePath: options.DefaultMountsFilePath,
|
|
Dockerfiles: dockerfiles,
|
|
// Err: string(options.Err),
|
|
ForceRmIntermediateCtrs: options.ForceRmIntermediateCtrs,
|
|
Iidfile: options.IIDFile,
|
|
Label: options.Labels,
|
|
Layers: options.Layers,
|
|
Nocache: options.NoCache,
|
|
// Out:
|
|
Output: options.Output,
|
|
OutputFormat: options.OutputFormat,
|
|
PullPolicy: options.PullPolicy.String(),
|
|
Quiet: options.Quiet,
|
|
RemoteIntermediateCtrs: options.RemoveIntermediateCtrs,
|
|
// ReportWriter:
|
|
RuntimeArgs: options.RuntimeArgs,
|
|
Squash: options.Squash,
|
|
}
|
|
// tar the file
|
|
outputFile, err := ioutil.TempFile("", "varlink_tar_send")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer outputFile.Close()
|
|
defer os.Remove(outputFile.Name())
|
|
|
|
// Create the tarball of the context dir to a tempfile
|
|
if err := utils.TarToFilesystem(options.ContextDirectory, outputFile); err != nil {
|
|
return err
|
|
}
|
|
// Send the context dir tarball over varlink.
|
|
tempFile, err := r.SendFileOverVarlink(outputFile.Name())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
buildinfo.ContextDir = tempFile
|
|
|
|
reply, err := iopodman.BuildImage().Send(r.Conn, varlink.More, buildinfo)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for {
|
|
responses, flags, err := reply()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, line := range responses.Logs {
|
|
fmt.Print(line)
|
|
}
|
|
if flags&varlink.Continues == 0 {
|
|
break
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
|
|
// SendFileOverVarlink sends a file over varlink in an upgraded connection
|
|
func (r *LocalRuntime) SendFileOverVarlink(source string) (string, error) {
|
|
fs, err := os.Open(source)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
fileInfo, err := fs.Stat()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
logrus.Debugf("sending %s over varlink connection", source)
|
|
reply, err := iopodman.SendFile().Send(r.Conn, varlink.Upgrade, "", int64(fileInfo.Size()))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
_, _, err = reply()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
reader := bufio.NewReader(fs)
|
|
_, err = reader.WriteTo(r.Conn.Writer)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
logrus.Debugf("file transfer complete for %s", source)
|
|
r.Conn.Writer.Flush()
|
|
|
|
// All was sent, wait for the ACK from the server
|
|
tempFile, err := r.Conn.Reader.ReadString(':')
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// r.Conn is kaput at this point due to the upgrade
|
|
if err := r.RemoteRuntime.RefreshConnection(); err != nil {
|
|
return "", err
|
|
|
|
}
|
|
|
|
return strings.Replace(tempFile, ":", "", -1), nil
|
|
}
|
|
|
|
// GetAllVolumes retrieves all the volumes
|
|
func (r *LocalRuntime) GetAllVolumes() ([]*libpod.Volume, error) {
|
|
return nil, define.ErrNotImplemented
|
|
}
|
|
|
|
// RemoveVolume removes a volumes
|
|
func (r *LocalRuntime) RemoveVolume(ctx context.Context, v *libpod.Volume, force, prune bool) error {
|
|
return define.ErrNotImplemented
|
|
}
|
|
|
|
// GetContainers retrieves all containers from the state
|
|
// Filters can be provided which will determine what containers are included in
|
|
// the output. Multiple filters are handled by ANDing their output, so only
|
|
// containers matching all filters are returned
|
|
func (r *LocalRuntime) GetContainers(filters ...libpod.ContainerFilter) ([]*libpod.Container, error) {
|
|
return nil, define.ErrNotImplemented
|
|
}
|
|
|
|
// RemoveContainer removes the given container
|
|
// If force is specified, the container will be stopped first
|
|
// Otherwise, RemoveContainer will return an error if the container is running
|
|
func (r *LocalRuntime) RemoveContainer(ctx context.Context, c *libpod.Container, force, volumes bool) error {
|
|
return define.ErrNotImplemented
|
|
}
|
|
|
|
// CreateVolume creates a volume over a varlink connection for the remote client
|
|
func (r *LocalRuntime) CreateVolume(ctx context.Context, c *cliconfig.VolumeCreateValues, labels, opts map[string]string) (string, error) {
|
|
cvOpts := iopodman.VolumeCreateOpts{
|
|
Options: opts,
|
|
Labels: labels,
|
|
}
|
|
if len(c.InputArgs) > 0 {
|
|
cvOpts.VolumeName = c.InputArgs[0]
|
|
}
|
|
|
|
if c.Flag("driver").Changed {
|
|
cvOpts.Driver = c.Driver
|
|
}
|
|
|
|
return iopodman.VolumeCreate().Call(r.Conn, cvOpts)
|
|
}
|
|
|
|
// RemoveVolumes removes volumes over a varlink connection for the remote client
|
|
func (r *LocalRuntime) RemoveVolumes(ctx context.Context, c *cliconfig.VolumeRmValues) ([]string, map[string]error, error) {
|
|
rmOpts := iopodman.VolumeRemoveOpts{
|
|
All: c.All,
|
|
Force: c.Force,
|
|
Volumes: c.InputArgs,
|
|
}
|
|
success, failures, err := iopodman.VolumeRemove().Call(r.Conn, rmOpts)
|
|
stringsToErrors := make(map[string]error)
|
|
for k, v := range failures {
|
|
stringsToErrors[k] = errors.New(v)
|
|
}
|
|
return success, stringsToErrors, err
|
|
}
|
|
|
|
func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestMIMEType, authfile, digestfile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions image.SigningOptions, dockerRegistryOptions *image.DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error {
|
|
|
|
reply, err := iopodman.PushImage().Send(r.Conn, varlink.More, srcName, destination, forceCompress, manifestMIMEType, signingOptions.RemoveSignatures, signingOptions.SignBy)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for {
|
|
responses, flags, err := reply()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, line := range responses.Logs {
|
|
fmt.Print(line)
|
|
}
|
|
if flags&varlink.Continues == 0 {
|
|
break
|
|
}
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// InspectVolumes returns a slice of volumes based on an arg list or --all
|
|
func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeInspectValues) ([]*Volume, error) {
|
|
reply, err := iopodman.GetVolumes().Call(r.Conn, c.InputArgs, c.All)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return varlinkVolumeToVolume(r, reply), nil
|
|
}
|
|
|
|
// Volumes returns a slice of adapter.volumes based on information about libpod
|
|
// volumes over a varlink connection
|
|
func (r *LocalRuntime) Volumes(ctx context.Context) ([]*Volume, error) {
|
|
reply, err := iopodman.GetVolumes().Call(r.Conn, []string{}, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return varlinkVolumeToVolume(r, reply), nil
|
|
}
|
|
|
|
func varlinkVolumeToVolume(r *LocalRuntime, volumes []iopodman.Volume) []*Volume {
|
|
var vols []*Volume
|
|
for _, v := range volumes {
|
|
volumeConfig := libpod.VolumeConfig{
|
|
Name: v.Name,
|
|
Labels: v.Labels,
|
|
MountPoint: v.MountPoint,
|
|
Driver: v.Driver,
|
|
Options: v.Options,
|
|
}
|
|
n := remoteVolume{
|
|
Runtime: r,
|
|
config: &volumeConfig,
|
|
}
|
|
newVol := Volume{
|
|
n,
|
|
}
|
|
vols = append(vols, &newVol)
|
|
}
|
|
return vols
|
|
}
|
|
|
|
// PruneVolumes removes all unused volumes from the remote system
|
|
func (r *LocalRuntime) PruneVolumes(ctx context.Context) ([]string, []error) {
|
|
var errs []error
|
|
prunedNames, prunedErrors, err := iopodman.VolumesPrune().Call(r.Conn)
|
|
if err != nil {
|
|
return []string{}, []error{err}
|
|
}
|
|
// We need to transform the string results of the error into actual error types
|
|
for _, e := range prunedErrors {
|
|
errs = append(errs, errors.New(e))
|
|
}
|
|
return prunedNames, errs
|
|
}
|
|
|
|
// SaveImage is a wrapper function for saving an image to the local filesystem
|
|
func (r *LocalRuntime) SaveImage(ctx context.Context, c *cliconfig.SaveValues) error {
|
|
source := c.InputArgs[0]
|
|
additionalTags := c.InputArgs[1:]
|
|
|
|
options := iopodman.ImageSaveOptions{
|
|
Name: source,
|
|
Format: c.Format,
|
|
Output: c.Output,
|
|
MoreTags: additionalTags,
|
|
Quiet: c.Quiet,
|
|
Compress: c.Compress,
|
|
}
|
|
reply, err := iopodman.ImageSave().Send(r.Conn, varlink.More, options)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var fetchfile string
|
|
for {
|
|
responses, flags, err := reply()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(responses.Id) > 0 {
|
|
fetchfile = responses.Id
|
|
}
|
|
for _, line := range responses.Logs {
|
|
fmt.Print(line)
|
|
}
|
|
if flags&varlink.Continues == 0 {
|
|
break
|
|
}
|
|
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
outputToDir := false
|
|
outfile := c.Output
|
|
var outputFile *os.File
|
|
// If the result is supposed to be a dir, then we need to put the tarfile
|
|
// from the host in a temporary file
|
|
if options.Format != "oci-archive" && options.Format != "docker-archive" {
|
|
outputToDir = true
|
|
outputFile, err = ioutil.TempFile("", "saveimage_tempfile")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
outfile = outputFile.Name()
|
|
defer outputFile.Close()
|
|
defer os.Remove(outputFile.Name())
|
|
}
|
|
// We now need to fetch the tarball result back to the more system
|
|
if err := r.GetFileFromRemoteHost(fetchfile, outfile, true); err != nil {
|
|
return err
|
|
}
|
|
|
|
// If the result is a tarball, we're done
|
|
// If it is a dir, we need to untar the temporary file into the dir
|
|
if outputToDir {
|
|
if err := utils.UntarToFileSystem(c.Output, outputFile, &archive.TarOptions{}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// LoadImage loads a container image from a remote client's filesystem
|
|
func (r *LocalRuntime) LoadImage(ctx context.Context, name string, cli *cliconfig.LoadValues) (string, error) {
|
|
var names string
|
|
remoteTempFile, err := r.SendFileOverVarlink(cli.Input)
|
|
if err != nil {
|
|
return "", nil
|
|
}
|
|
more := varlink.More
|
|
if cli.Quiet {
|
|
more = 0
|
|
}
|
|
reply, err := iopodman.LoadImage().Send(r.Conn, uint64(more), name, remoteTempFile, cli.Quiet, true)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
for {
|
|
responses, flags, err := reply()
|
|
if err != nil {
|
|
logrus.Error(err)
|
|
return "", err
|
|
}
|
|
for _, line := range responses.Logs {
|
|
fmt.Print(line)
|
|
}
|
|
names = responses.Id
|
|
if flags&varlink.Continues == 0 {
|
|
break
|
|
}
|
|
}
|
|
return names, nil
|
|
}
|
|
|
|
// IsImageNotFound checks if the error indicates that no image was found.
|
|
func IsImageNotFound(err error) bool {
|
|
if errors.Cause(err) == image.ErrNoSuchImage {
|
|
return true
|
|
}
|
|
switch err.(type) {
|
|
case *iopodman.ImageNotFound:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// HealthCheck executes a container's healthcheck over a varlink connection
|
|
func (r *LocalRuntime) HealthCheck(c *cliconfig.HealthCheckValues) (string, error) {
|
|
return iopodman.HealthCheckRun().Call(r.Conn, c.InputArgs[0])
|
|
}
|
|
|
|
// Events monitors libpod/podman events over a varlink connection
|
|
func (r *LocalRuntime) Events(c *cliconfig.EventValues) error {
|
|
var more uint64
|
|
if c.Stream {
|
|
more = uint64(varlink.More)
|
|
}
|
|
reply, err := iopodman.GetEvents().Send(r.Conn, more, c.Filter, c.Since, c.Until)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "unable to obtain events")
|
|
}
|
|
|
|
w := bufio.NewWriter(os.Stdout)
|
|
var tmpl *template.Template
|
|
if c.Format != formats.JSONString {
|
|
template, err := template.New("events").Parse(c.Format)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tmpl = template
|
|
}
|
|
|
|
for {
|
|
returnedEvent, flags, err := reply()
|
|
if err != nil {
|
|
// When the error handling is back into podman, we can flip this to a better way to check
|
|
// for problems. For now, this works.
|
|
return err
|
|
}
|
|
if returnedEvent.Time == "" && returnedEvent.Status == "" && returnedEvent.Type == "" {
|
|
// We got a blank event return, signals end of stream in certain cases
|
|
break
|
|
}
|
|
eTime, err := time.Parse(time.RFC3339Nano, returnedEvent.Time)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "unable to parse time of event %s", returnedEvent.Time)
|
|
}
|
|
eType, err := events.StringToType(returnedEvent.Type)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
eStatus, err := events.StringToStatus(returnedEvent.Status)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
event := events.Event{
|
|
ID: returnedEvent.Id,
|
|
Image: returnedEvent.Image,
|
|
Name: returnedEvent.Name,
|
|
Status: eStatus,
|
|
Time: eTime,
|
|
Type: eType,
|
|
}
|
|
if c.Format == formats.JSONString {
|
|
jsonStr, err := event.ToJSONString()
|
|
if err != nil {
|
|
return errors.Wrapf(err, "unable to format json")
|
|
}
|
|
if _, err := w.Write([]byte(jsonStr)); err != nil {
|
|
return err
|
|
}
|
|
} else if len(c.Format) > 0 {
|
|
if err := tmpl.Execute(w, event); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if _, err := w.Write([]byte(event.ToHumanReadable())); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if _, err := w.Write([]byte("\n")); err != nil {
|
|
return err
|
|
}
|
|
if err := w.Flush(); err != nil {
|
|
return err
|
|
}
|
|
if flags&varlink.Continues == 0 {
|
|
break
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Diff ...
|
|
func (r *LocalRuntime) Diff(c *cliconfig.DiffValues, to string) ([]archive.Change, error) {
|
|
var changes []archive.Change
|
|
reply, err := iopodman.Diff().Call(r.Conn, to)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, change := range reply {
|
|
changes = append(changes, archive.Change{Path: change.Path, Kind: stringToChangeType(change.ChangeType)})
|
|
}
|
|
return changes, nil
|
|
}
|
|
|
|
func stringToChangeType(change string) archive.ChangeType {
|
|
switch change {
|
|
case "A":
|
|
return archive.ChangeAdd
|
|
case "D":
|
|
return archive.ChangeDelete
|
|
default:
|
|
logrus.Errorf("'%s' is unknown archive type", change)
|
|
fallthrough
|
|
case "C":
|
|
return archive.ChangeModify
|
|
}
|
|
}
|
|
|
|
// GenerateKube creates kubernetes email from containers and pods
|
|
func (r *LocalRuntime) GenerateKube(c *cliconfig.GenerateKubeValues) (*v1.Pod, *v1.Service, error) {
|
|
var (
|
|
pod v1.Pod
|
|
service v1.Service
|
|
)
|
|
reply, err := iopodman.GenerateKube().Call(r.Conn, c.InputArgs[0], c.Service)
|
|
if err != nil {
|
|
return nil, nil, errors.Wrap(err, "unable to create kubernetes YAML")
|
|
}
|
|
if err := json.Unmarshal([]byte(reply.Pod), &pod); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
err = json.Unmarshal([]byte(reply.Service), &service)
|
|
return &pod, &service, err
|
|
}
|
|
|
|
// GetContainersByContext looks up containers based on the cli input of all, latest, or a list
|
|
func (r *LocalRuntime) GetContainersByContext(all bool, latest bool, namesOrIDs []string) ([]*Container, error) {
|
|
var containers []*Container
|
|
cids, err := iopodman.GetContainersByContext().Call(r.Conn, all, latest, namesOrIDs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, cid := range cids {
|
|
ctr, err := r.LookupContainer(cid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
containers = append(containers, ctr)
|
|
}
|
|
return containers, nil
|
|
}
|
|
|
|
// GetVersion returns version information from service
|
|
func (r *LocalRuntime) GetVersion() (define.Version, error) {
|
|
version, goVersion, gitCommit, built, osArch, apiVersion, err := iopodman.GetVersion().Call(r.Conn)
|
|
if err != nil {
|
|
return define.Version{}, errors.Wrapf(err, "Unable to obtain server version information")
|
|
}
|
|
|
|
var buildTime int64
|
|
if built != "" {
|
|
t, err := time.Parse(time.RFC3339, built)
|
|
if err != nil {
|
|
return define.Version{}, nil
|
|
}
|
|
buildTime = t.Unix()
|
|
}
|
|
|
|
return define.Version{
|
|
RemoteAPIVersion: apiVersion,
|
|
Version: version,
|
|
GoVersion: goVersion,
|
|
GitCommit: gitCommit,
|
|
Built: buildTime,
|
|
OsArch: osArch,
|
|
}, nil
|
|
}
|