github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/adapter/runtime_remote.go (about) 1 // +build remoteclient 2 3 package adapter 4 5 import ( 6 "bufio" 7 "context" 8 "encoding/json" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "os" 13 "path/filepath" 14 "strings" 15 "text/template" 16 "time" 17 18 "github.com/containers/buildah/imagebuildah" 19 "github.com/containers/buildah/pkg/formats" 20 "github.com/containers/common/pkg/config" 21 "github.com/containers/image/v5/docker/reference" 22 "github.com/containers/image/v5/types" 23 "github.com/containers/libpod/cmd/podman/cliconfig" 24 "github.com/containers/libpod/cmd/podman/remoteclientconfig" 25 "github.com/containers/libpod/libpod" 26 "github.com/containers/libpod/libpod/define" 27 "github.com/containers/libpod/libpod/events" 28 "github.com/containers/libpod/libpod/image" 29 "github.com/containers/libpod/pkg/util" 30 iopodman "github.com/containers/libpod/pkg/varlink" 31 "github.com/containers/libpod/utils" 32 "github.com/containers/storage/pkg/archive" 33 "github.com/opencontainers/go-digest" 34 "github.com/pkg/errors" 35 "github.com/sirupsen/logrus" 36 "github.com/varlink/go/varlink" 37 v1 "k8s.io/api/core/v1" 38 ) 39 40 // ImageRuntime is wrapper for image runtime 41 type RemoteImageRuntime struct{} 42 43 // RemoteRuntime describes a wrapper runtime struct 44 type RemoteRuntime struct { 45 Conn *varlink.Connection 46 Remote bool 47 cmd cliconfig.MainFlags 48 config io.Reader 49 } 50 51 // LocalRuntime describes a typical libpod runtime 52 type LocalRuntime struct { 53 *RemoteRuntime 54 } 55 56 // GetRuntimeNoStore returns a LocalRuntime struct with the actual runtime embedded in it 57 // The nostore is ignored 58 func GetRuntimeNoStore(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime, error) { 59 return GetRuntime(ctx, c) 60 } 61 62 // GetRuntime returns a LocalRuntime struct with the actual runtime embedded in it 63 func GetRuntime(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime, error) { 64 var ( 65 customConfig bool 66 err error 67 f *os.File 68 ) 69 runtime := RemoteRuntime{ 70 Remote: true, 71 cmd: c.GlobalFlags, 72 } 73 configPath := remoteclientconfig.GetConfigFilePath() 74 // Check if the basedir for configPath exists and if not, create it. 75 if _, err := os.Stat(filepath.Dir(configPath)); os.IsNotExist(err) { 76 if mkdirErr := os.MkdirAll(filepath.Dir(configPath), 0750); mkdirErr != nil { 77 return nil, mkdirErr 78 } 79 } 80 if len(c.GlobalFlags.RemoteConfigFilePath) > 0 { 81 configPath = c.GlobalFlags.RemoteConfigFilePath 82 customConfig = true 83 } 84 85 f, err = os.Open(configPath) 86 if err != nil { 87 // If user does not explicitly provide a configuration file path and we cannot 88 // find a default, no error should occur. 89 if os.IsNotExist(err) && !customConfig { 90 logrus.Debugf("unable to load configuration file at %s", configPath) 91 runtime.config = nil 92 } else { 93 return nil, errors.Wrapf(err, "unable to load configuration file at %s", configPath) 94 } 95 } else { 96 // create the io reader for the remote client 97 runtime.config = bufio.NewReader(f) 98 } 99 conn, err := runtime.Connect() 100 if err != nil { 101 return nil, err 102 } 103 runtime.Conn = conn 104 return &LocalRuntime{ 105 &runtime, 106 }, nil 107 } 108 109 // DeferredShutdown is a bogus wrapper for compaat with the libpod 110 // runtime and should only be run when a defer is being used 111 func (r RemoteRuntime) DeferredShutdown(force bool) { 112 if err := r.Shutdown(force); err != nil { 113 logrus.Error("unable to shutdown runtime") 114 } 115 } 116 117 // Containers is a bogus wrapper for compat with the libpod runtime 118 type ContainersConfig struct { 119 // CGroupManager is the CGroup Manager to use 120 // Valid values are "cgroupfs" and "systemd" 121 CgroupManager string 122 } 123 124 // RuntimeConfig is a bogus wrapper for compat with the libpod runtime 125 type RuntimeConfig struct { 126 Containers ContainersConfig 127 } 128 129 // Shutdown is a bogus wrapper for compat with the libpod runtime 130 func (r *RemoteRuntime) GetConfig() (*config.Config, error) { 131 return nil, nil 132 } 133 134 // Shutdown is a bogus wrapper for compat with the libpod runtime 135 func (r RemoteRuntime) Shutdown(force bool) error { 136 return nil 137 } 138 139 // ContainerImage 140 type ContainerImage struct { 141 remoteImage 142 } 143 144 type remoteImage struct { 145 ID string 146 Labels map[string]string 147 RepoTags []string 148 RepoDigests []string 149 Parent string 150 Size int64 151 Created time.Time 152 InputName string 153 Names []string 154 Digest digest.Digest 155 Digests []digest.Digest 156 isParent bool 157 Runtime *LocalRuntime 158 TopLayer string 159 ReadOnly bool 160 NamesHistory []string 161 } 162 163 // Container ... 164 type Container struct { 165 remoteContainer 166 } 167 168 // remoteContainer .... 169 type remoteContainer struct { 170 Runtime *LocalRuntime 171 config *libpod.ContainerConfig 172 state *libpod.ContainerState 173 } 174 175 // Pod ... 176 type Pod struct { 177 remotepod 178 } 179 180 type remotepod struct { 181 config *libpod.PodConfig 182 state *libpod.PodInspectState 183 containers []libpod.PodContainerInfo // nolint: structcheck 184 Runtime *LocalRuntime 185 } 186 187 type VolumeFilter func(*Volume) bool 188 189 // Volume is embed for libpod volumes 190 type Volume struct { 191 remoteVolume 192 } 193 194 type remoteVolume struct { 195 Runtime *LocalRuntime 196 config *libpod.VolumeConfig 197 } 198 199 // GetImages returns a slice of containerimages over a varlink connection 200 func (r *LocalRuntime) GetImages() ([]*ContainerImage, error) { 201 return r.getImages(false) 202 } 203 204 // GetRWImages returns a slice of read/write containerimages over a varlink connection 205 func (r *LocalRuntime) GetRWImages() ([]*ContainerImage, error) { 206 return r.getImages(true) 207 } 208 209 func (r *LocalRuntime) GetFilteredImages(filters []string, rwOnly bool) ([]*ContainerImage, error) { 210 if len(filters) > 0 { 211 return nil, errors.Wrap(define.ErrNotImplemented, "filtering images is not supported on the remote client") 212 } 213 var newImages []*ContainerImage 214 images, err := iopodman.ListImages().Call(r.Conn) 215 if err != nil { 216 return nil, err 217 } 218 for _, i := range images { 219 if rwOnly && i.ReadOnly { 220 continue 221 } 222 name := i.Id 223 if len(i.RepoTags) > 1 { 224 name = i.RepoTags[0] 225 } 226 newImage, err := imageInListToContainerImage(i, name, r) 227 if err != nil { 228 return nil, err 229 } 230 newImages = append(newImages, newImage) 231 } 232 return newImages, nil 233 } 234 func (r *LocalRuntime) getImages(rwOnly bool) ([]*ContainerImage, error) { 235 var newImages []*ContainerImage 236 images, err := iopodman.ListImages().Call(r.Conn) 237 if err != nil { 238 return nil, err 239 } 240 for _, i := range images { 241 if rwOnly && i.ReadOnly { 242 continue 243 } 244 name := i.Id 245 if len(i.RepoTags) > 1 { 246 name = i.RepoTags[0] 247 } 248 newImage, err := imageInListToContainerImage(i, name, r) 249 if err != nil { 250 return nil, err 251 } 252 newImages = append(newImages, newImage) 253 } 254 return newImages, nil 255 } 256 257 func imageInListToContainerImage(i iopodman.Image, name string, runtime *LocalRuntime) (*ContainerImage, error) { 258 created, err := time.ParseInLocation(time.RFC3339, i.Created, time.UTC) 259 if err != nil { 260 return nil, err 261 } 262 var digests []digest.Digest 263 for _, d := range i.Digests { 264 digests = append(digests, digest.Digest(d)) 265 } 266 ri := remoteImage{ 267 InputName: name, 268 ID: i.Id, 269 Digest: digest.Digest(i.Digest), 270 Digests: digests, 271 Labels: i.Labels, 272 RepoTags: i.RepoTags, 273 RepoDigests: i.RepoTags, 274 Parent: i.ParentId, 275 Size: i.Size, 276 Created: created, 277 Names: i.RepoTags, 278 isParent: i.IsParent, 279 Runtime: runtime, 280 TopLayer: i.TopLayer, 281 ReadOnly: i.ReadOnly, 282 NamesHistory: i.History, 283 } 284 return &ContainerImage{ri}, nil 285 } 286 287 // NewImageFromLocal returns a container image representation of a image over varlink 288 func (r *LocalRuntime) NewImageFromLocal(name string) (*ContainerImage, error) { 289 img, err := iopodman.GetImage().Call(r.Conn, name) 290 if err != nil { 291 return nil, err 292 } 293 return imageInListToContainerImage(img, name, r) 294 295 } 296 297 // LoadFromArchiveReference creates an image from a local archive 298 func (r *LocalRuntime) LoadFromArchiveReference(ctx context.Context, srcRef types.ImageReference, signaturePolicyPath string, writer io.Writer) ([]*ContainerImage, error) { 299 var iid string 300 creds := iopodman.AuthConfig{} 301 reply, err := iopodman.PullImage().Send(r.Conn, varlink.More, srcRef.DockerReference().String(), creds) 302 if err != nil { 303 return nil, err 304 } 305 306 for { 307 responses, flags, err := reply() 308 if err != nil { 309 return nil, err 310 } 311 for _, line := range responses.Logs { 312 fmt.Print(line) 313 } 314 iid = responses.Id 315 if flags&varlink.Continues == 0 { 316 break 317 } 318 } 319 320 newImage, err := r.NewImageFromLocal(iid) 321 if err != nil { 322 return nil, err 323 } 324 return []*ContainerImage{newImage}, nil 325 } 326 327 // New calls into local storage to look for an image in local storage or to pull it 328 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) { 329 var iid string 330 if label != nil { 331 return nil, errors.New("the remote client function does not support checking a remote image for a label") 332 } 333 creds := iopodman.AuthConfig{} 334 if dockeroptions.DockerRegistryCreds != nil { 335 creds.Username = dockeroptions.DockerRegistryCreds.Username 336 creds.Password = dockeroptions.DockerRegistryCreds.Password 337 } 338 reply, err := iopodman.PullImage().Send(r.Conn, varlink.More, name, creds) 339 if err != nil { 340 return nil, err 341 } 342 for { 343 responses, flags, err := reply() 344 if err != nil { 345 return nil, err 346 } 347 for _, line := range responses.Logs { 348 fmt.Print(line) 349 } 350 iid = responses.Id 351 if flags&varlink.Continues == 0 { 352 break 353 } 354 } 355 newImage, err := r.NewImageFromLocal(iid) 356 if err != nil { 357 return nil, err 358 } 359 return newImage, nil 360 } 361 362 func (r *LocalRuntime) ImageTree(imageOrID string, whatRequires bool) (string, error) { 363 return iopodman.ImageTree().Call(r.Conn, imageOrID, whatRequires) 364 } 365 366 // IsParent goes through the layers in the store and checks if i.TopLayer is 367 // the parent of any other layer in store. Double check that image with that 368 // layer exists as well. 369 func (ci *ContainerImage) IsParent(context.Context) (bool, error) { 370 return ci.remoteImage.isParent, nil 371 } 372 373 // ID returns the image ID as a string 374 func (ci *ContainerImage) ID() string { 375 return ci.remoteImage.ID 376 } 377 378 // Names returns a string array of names associated with the image 379 func (ci *ContainerImage) Names() []string { 380 return ci.remoteImage.Names 381 } 382 383 // NamesHistory returns a string array of names previously associated with the image 384 func (ci *ContainerImage) NamesHistory() []string { 385 return ci.remoteImage.NamesHistory 386 } 387 388 // Created returns the time the image was created 389 func (ci *ContainerImage) Created() time.Time { 390 return ci.remoteImage.Created 391 } 392 393 // IsReadOnly returns whether the image is ReadOnly 394 func (ci *ContainerImage) IsReadOnly() bool { 395 return ci.remoteImage.ReadOnly 396 } 397 398 // Size returns the size of the image 399 func (ci *ContainerImage) Size(ctx context.Context) (*uint64, error) { 400 usize := uint64(ci.remoteImage.Size) 401 return &usize, nil 402 } 403 404 // Digest returns the image's digest 405 func (ci *ContainerImage) Digest() digest.Digest { 406 return ci.remoteImage.Digest 407 } 408 409 // Digests returns the image's digests 410 func (ci *ContainerImage) Digests() []digest.Digest { 411 return append([]digest.Digest{}, ci.remoteImage.Digests...) 412 } 413 414 // Labels returns a map of the image's labels 415 func (ci *ContainerImage) Labels(ctx context.Context) (map[string]string, error) { 416 return ci.remoteImage.Labels, nil 417 } 418 419 // Dangling returns a bool if the image is "dangling" 420 func (ci *ContainerImage) Dangling() bool { 421 return len(ci.Names()) == 0 422 } 423 424 // TopLayer returns an images top layer as a string 425 func (ci *ContainerImage) TopLayer() string { 426 return ci.remoteImage.TopLayer 427 } 428 429 // TagImage ... 430 func (ci *ContainerImage) TagImage(tag string) error { 431 _, err := iopodman.TagImage().Call(ci.Runtime.Conn, ci.ID(), tag) 432 return err 433 } 434 435 // UntagImage removes a single tag from an image 436 func (ci *ContainerImage) UntagImage(tag string) error { 437 _, err := iopodman.UntagImage().Call(ci.Runtime.Conn, ci.ID(), tag) 438 return err 439 } 440 441 // RemoveImage calls varlink to remove an image 442 func (r *LocalRuntime) RemoveImage(ctx context.Context, img *ContainerImage, force bool) (*image.ImageDeleteResponse, error) { 443 ir := image.ImageDeleteResponse{} 444 response, err := iopodman.RemoveImageWithResponse().Call(r.Conn, img.InputName, force) 445 if err != nil { 446 return nil, err 447 } 448 ir.Deleted = response.Deleted 449 ir.Untagged = append(ir.Untagged, response.Untagged...) 450 return &ir, nil 451 } 452 453 // History returns the history of an image and its layers 454 func (ci *ContainerImage) History(ctx context.Context) ([]*image.History, error) { 455 var imageHistories []*image.History 456 457 reply, err := iopodman.HistoryImage().Call(ci.Runtime.Conn, ci.InputName) 458 if err != nil { 459 return nil, err 460 } 461 for _, h := range reply { 462 created, err := time.ParseInLocation(time.RFC3339, h.Created, time.UTC) 463 if err != nil { 464 return nil, err 465 } 466 ih := image.History{ 467 ID: h.Id, 468 Created: &created, 469 CreatedBy: h.CreatedBy, 470 Size: h.Size, 471 Comment: h.Comment, 472 } 473 imageHistories = append(imageHistories, &ih) 474 } 475 return imageHistories, nil 476 } 477 478 // PruneImages is the wrapper call for a remote-client to prune images 479 func (r *LocalRuntime) PruneImages(ctx context.Context, all bool, filter []string) ([]string, error) { 480 return iopodman.ImagesPrune().Call(r.Conn, all, filter) 481 } 482 483 // Export is a wrapper to container export to a tarfile 484 func (r *LocalRuntime) Export(name string, path string) error { 485 tempPath, err := iopodman.ExportContainer().Call(r.Conn, name, "") 486 if err != nil { 487 return err 488 } 489 return r.GetFileFromRemoteHost(tempPath, path, true) 490 } 491 492 func (r *LocalRuntime) GetFileFromRemoteHost(remoteFilePath, outputPath string, delete bool) error { 493 outputFile, err := os.Create(outputPath) 494 if err != nil { 495 return err 496 } 497 defer outputFile.Close() 498 499 writer := bufio.NewWriter(outputFile) 500 defer writer.Flush() 501 502 reply, err := iopodman.ReceiveFile().Send(r.Conn, varlink.Upgrade, remoteFilePath, delete) 503 if err != nil { 504 return err 505 } 506 507 length, _, err := reply() 508 if err != nil { 509 return errors.Wrap(err, "unable to get file length for transfer") 510 } 511 512 reader := r.Conn.Reader 513 if _, err := io.CopyN(writer, reader, length); err != nil { 514 return errors.Wrap(err, "file transfer failed") 515 } 516 return nil 517 } 518 519 // Import implements the remote calls required to import a container image to the store 520 func (r *LocalRuntime) Import(ctx context.Context, source, reference string, changes []string, history string, quiet bool) (string, error) { 521 // First we send the file to the host 522 tempFile, err := r.SendFileOverVarlink(source) 523 if err != nil { 524 return "", err 525 } 526 return iopodman.ImportImage().Call(r.Conn, strings.TrimRight(tempFile, ":"), reference, history, changes, true) 527 } 528 529 func (r *LocalRuntime) Build(ctx context.Context, c *cliconfig.BuildValues, options imagebuildah.BuildOptions, dockerfiles []string) (string, reference.Canonical, error) { 530 buildOptions := iopodman.BuildOptions{ 531 AddHosts: options.CommonBuildOpts.AddHost, 532 CgroupParent: options.CommonBuildOpts.CgroupParent, 533 CpuPeriod: int64(options.CommonBuildOpts.CPUPeriod), 534 CpuQuota: options.CommonBuildOpts.CPUQuota, 535 CpuShares: int64(options.CommonBuildOpts.CPUShares), 536 CpusetCpus: options.CommonBuildOpts.CPUSetMems, 537 CpusetMems: options.CommonBuildOpts.CPUSetMems, 538 Memory: options.CommonBuildOpts.Memory, 539 MemorySwap: options.CommonBuildOpts.MemorySwap, 540 ShmSize: options.CommonBuildOpts.ShmSize, 541 Ulimit: options.CommonBuildOpts.Ulimit, 542 Volume: options.CommonBuildOpts.Volumes, 543 } 544 buildinfo := iopodman.BuildInfo{ 545 // Err: string(options.Err), 546 // Out: 547 // ReportWriter: 548 Architecture: options.Architecture, 549 AddCapabilities: options.AddCapabilities, 550 AdditionalTags: options.AdditionalTags, 551 Annotations: options.Annotations, 552 BuildArgs: options.Args, 553 BuildOptions: buildOptions, 554 CniConfigDir: options.CNIConfigDir, 555 CniPluginDir: options.CNIPluginPath, 556 Compression: string(options.Compression), 557 Devices: options.Devices, 558 DefaultsMountFilePath: options.DefaultMountsFilePath, 559 Dockerfiles: dockerfiles, 560 DropCapabilities: options.DropCapabilities, 561 ForceRmIntermediateCtrs: options.ForceRmIntermediateCtrs, 562 Iidfile: options.IIDFile, 563 Label: options.Labels, 564 Layers: options.Layers, 565 // NamespaceOptions: options.NamespaceOptions, 566 Nocache: options.NoCache, 567 Os: options.OS, 568 Output: options.Output, 569 OutputFormat: options.OutputFormat, 570 PullPolicy: options.PullPolicy.String(), 571 Quiet: options.Quiet, 572 RemoteIntermediateCtrs: options.RemoveIntermediateCtrs, 573 RuntimeArgs: options.RuntimeArgs, 574 SignBy: options.SignBy, 575 Squash: options.Squash, 576 Target: options.Target, 577 TransientMounts: options.TransientMounts, 578 } 579 // tar the file 580 outputFile, err := ioutil.TempFile("", "varlink_tar_send") 581 if err != nil { 582 return "", nil, err 583 } 584 defer outputFile.Close() 585 defer os.Remove(outputFile.Name()) 586 587 // Create the tarball of the context dir to a tempfile 588 if err := utils.TarToFilesystem(options.ContextDirectory, outputFile); err != nil { 589 return "", nil, err 590 } 591 // Send the context dir tarball over varlink. 592 tempFile, err := r.SendFileOverVarlink(outputFile.Name()) 593 if err != nil { 594 return "", nil, err 595 } 596 buildinfo.ContextDir = tempFile 597 598 reply, err := iopodman.BuildImage().Send(r.Conn, varlink.More, buildinfo) 599 if err != nil { 600 return "", nil, err 601 } 602 603 for { 604 responses, flags, err := reply() 605 if err != nil { 606 return "", nil, err 607 } 608 for _, line := range responses.Logs { 609 fmt.Print(line) 610 } 611 if flags&varlink.Continues == 0 { 612 break 613 } 614 } 615 return "", nil, err 616 } 617 618 // SendFileOverVarlink sends a file over varlink in an upgraded connection 619 func (r *LocalRuntime) SendFileOverVarlink(source string) (string, error) { 620 fs, err := os.Open(source) 621 if err != nil { 622 return "", err 623 } 624 625 fileInfo, err := fs.Stat() 626 if err != nil { 627 return "", err 628 } 629 logrus.Debugf("sending %s over varlink connection", source) 630 reply, err := iopodman.SendFile().Send(r.Conn, varlink.Upgrade, "", fileInfo.Size()) 631 if err != nil { 632 return "", err 633 } 634 _, _, err = reply() 635 if err != nil { 636 return "", err 637 } 638 639 reader := bufio.NewReader(fs) 640 _, err = reader.WriteTo(r.Conn.Writer) 641 if err != nil { 642 return "", err 643 } 644 logrus.Debugf("file transfer complete for %s", source) 645 r.Conn.Writer.Flush() 646 647 // All was sent, wait for the ACK from the server 648 tempFile, err := r.Conn.Reader.ReadString(':') 649 if err != nil { 650 return "", err 651 } 652 653 // r.Conn is kaput at this point due to the upgrade 654 if err := r.RemoteRuntime.RefreshConnection(); err != nil { 655 return "", err 656 657 } 658 659 return strings.Replace(tempFile, ":", "", -1), nil 660 } 661 662 // GetAllVolumes retrieves all the volumes 663 func (r *LocalRuntime) GetAllVolumes() ([]*libpod.Volume, error) { 664 return nil, define.ErrNotImplemented 665 } 666 667 // RemoveVolume removes a volumes 668 func (r *LocalRuntime) RemoveVolume(ctx context.Context, v *libpod.Volume, force, prune bool) error { 669 return define.ErrNotImplemented 670 } 671 672 // GetContainers retrieves all containers from the state 673 // Filters can be provided which will determine what containers are included in 674 // the output. Multiple filters are handled by ANDing their output, so only 675 // containers matching all filters are returned 676 func (r *LocalRuntime) GetContainers(filters ...libpod.ContainerFilter) ([]*libpod.Container, error) { 677 return nil, define.ErrNotImplemented 678 } 679 680 // RemoveContainer removes the given container 681 // If force is specified, the container will be stopped first 682 // Otherwise, RemoveContainer will return an error if the container is running 683 func (r *LocalRuntime) RemoveContainer(ctx context.Context, c *libpod.Container, force, volumes bool) error { 684 return define.ErrNotImplemented 685 } 686 687 // CreateVolume creates a volume over a varlink connection for the remote client 688 func (r *LocalRuntime) CreateVolume(ctx context.Context, c *cliconfig.VolumeCreateValues, labels, opts map[string]string) (string, error) { 689 cvOpts := iopodman.VolumeCreateOpts{ 690 Options: opts, 691 Labels: labels, 692 } 693 if len(c.InputArgs) > 0 { 694 cvOpts.VolumeName = c.InputArgs[0] 695 } 696 697 if c.Flag("driver").Changed { 698 cvOpts.Driver = c.Driver 699 } 700 701 return iopodman.VolumeCreate().Call(r.Conn, cvOpts) 702 } 703 704 // RemoveVolumes removes volumes over a varlink connection for the remote client 705 func (r *LocalRuntime) RemoveVolumes(ctx context.Context, c *cliconfig.VolumeRmValues) ([]string, map[string]error, error) { 706 rmOpts := iopodman.VolumeRemoveOpts{ 707 All: c.All, 708 Force: c.Force, 709 Volumes: c.InputArgs, 710 } 711 success, failures, err := iopodman.VolumeRemove().Call(r.Conn, rmOpts) 712 stringsToErrors := make(map[string]error) 713 for k, v := range failures { 714 stringsToErrors[k] = errors.New(v) 715 } 716 return success, stringsToErrors, err 717 } 718 719 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 { 720 721 reply, err := iopodman.PushImage().Send(r.Conn, varlink.More, srcName, destination, forceCompress, manifestMIMEType, signingOptions.RemoveSignatures, signingOptions.SignBy) 722 if err != nil { 723 return err 724 } 725 for { 726 responses, flags, err := reply() 727 if err != nil { 728 return err 729 } 730 for _, line := range responses.Logs { 731 fmt.Print(line) 732 } 733 if flags&varlink.Continues == 0 { 734 break 735 } 736 } 737 738 return err 739 } 740 741 // InspectVolumes returns a slice of volumes based on an arg list or --all 742 func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeInspectValues) ([]*libpod.InspectVolumeData, error) { 743 var ( 744 inspectData []*libpod.InspectVolumeData 745 volumes []string 746 ) 747 748 if c.All { 749 allVolumes, err := r.Volumes(ctx) 750 if err != nil { 751 return nil, err 752 } 753 for _, vol := range allVolumes { 754 volumes = append(volumes, vol.Name()) 755 } 756 } else { 757 volumes = append(volumes, c.InputArgs...) 758 } 759 760 for _, vol := range volumes { 761 jsonString, err := iopodman.InspectVolume().Call(r.Conn, vol) 762 if err != nil { 763 return nil, err 764 } 765 inspectJSON := new(libpod.InspectVolumeData) 766 if err := json.Unmarshal([]byte(jsonString), inspectJSON); err != nil { 767 return nil, errors.Wrapf(err, "error unmarshalling inspect JSON for volume %s", vol) 768 } 769 inspectData = append(inspectData, inspectJSON) 770 } 771 772 return inspectData, nil 773 } 774 775 // Volumes returns a slice of adapter.volumes based on information about libpod 776 // volumes over a varlink connection 777 func (r *LocalRuntime) Volumes(ctx context.Context) ([]*Volume, error) { 778 reply, err := iopodman.GetVolumes().Call(r.Conn, []string{}, true) 779 if err != nil { 780 return nil, err 781 } 782 return varlinkVolumeToVolume(r, reply), nil 783 } 784 785 func varlinkVolumeToVolume(r *LocalRuntime, volumes []iopodman.Volume) []*Volume { 786 var vols []*Volume 787 for _, v := range volumes { 788 volumeConfig := libpod.VolumeConfig{ 789 Name: v.Name, 790 Labels: v.Labels, 791 MountPoint: v.MountPoint, 792 Driver: v.Driver, 793 Options: v.Options, 794 } 795 n := remoteVolume{ 796 Runtime: r, 797 config: &volumeConfig, 798 } 799 newVol := Volume{ 800 n, 801 } 802 vols = append(vols, &newVol) 803 } 804 return vols 805 } 806 807 // PruneVolumes removes all unused volumes from the remote system 808 func (r *LocalRuntime) PruneVolumes(ctx context.Context) ([]string, []error) { 809 var errs []error 810 prunedNames, prunedErrors, err := iopodman.VolumesPrune().Call(r.Conn) 811 if err != nil { 812 return []string{}, []error{err} 813 } 814 // We need to transform the string results of the error into actual error types 815 for _, e := range prunedErrors { 816 errs = append(errs, errors.New(e)) 817 } 818 return prunedNames, errs 819 } 820 821 // SaveImage is a wrapper function for saving an image to the local filesystem 822 func (r *LocalRuntime) SaveImage(ctx context.Context, c *cliconfig.SaveValues) error { 823 source := c.InputArgs[0] 824 additionalTags := c.InputArgs[1:] 825 826 options := iopodman.ImageSaveOptions{ 827 Name: source, 828 Format: c.Format, 829 Output: c.Output, 830 MoreTags: additionalTags, 831 Quiet: c.Quiet, 832 Compress: c.Compress, 833 } 834 reply, err := iopodman.ImageSave().Send(r.Conn, varlink.More, options) 835 if err != nil { 836 return err 837 } 838 839 var fetchfile string 840 for { 841 responses, flags, err := reply() 842 if err != nil { 843 return err 844 } 845 if len(responses.Id) > 0 { 846 fetchfile = responses.Id 847 } 848 for _, line := range responses.Logs { 849 fmt.Print(line) 850 } 851 if flags&varlink.Continues == 0 { 852 break 853 } 854 855 } 856 if err != nil { // nolint: govet 857 return err 858 } 859 860 outputToDir := false 861 outfile := c.Output 862 var outputFile *os.File 863 // If the result is supposed to be a dir, then we need to put the tarfile 864 // from the host in a temporary file 865 if options.Format != "oci-archive" && options.Format != "docker-archive" { 866 outputToDir = true 867 outputFile, err = ioutil.TempFile("", "saveimage_tempfile") 868 if err != nil { 869 return err 870 } 871 outfile = outputFile.Name() 872 defer outputFile.Close() 873 defer os.Remove(outputFile.Name()) 874 } 875 // We now need to fetch the tarball result back to the more system 876 if err := r.GetFileFromRemoteHost(fetchfile, outfile, true); err != nil { 877 return err 878 } 879 880 // If the result is a tarball, we're done 881 // If it is a dir, we need to untar the temporary file into the dir 882 if outputToDir { 883 if err := utils.UntarToFileSystem(c.Output, outputFile, &archive.TarOptions{}); err != nil { 884 return err 885 } 886 } 887 return nil 888 } 889 890 // LoadImage loads a container image from a remote client's filesystem 891 func (r *LocalRuntime) LoadImage(ctx context.Context, name string, cli *cliconfig.LoadValues) (string, error) { 892 var names string 893 remoteTempFile, err := r.SendFileOverVarlink(cli.Input) 894 if err != nil { 895 return "", nil 896 } 897 more := varlink.More 898 if cli.Quiet { 899 more = 0 900 } 901 reply, err := iopodman.LoadImage().Send(r.Conn, uint64(more), name, remoteTempFile, cli.Quiet, true) 902 if err != nil { 903 return "", err 904 } 905 906 for { 907 responses, flags, err := reply() 908 if err != nil { 909 logrus.Error(err) 910 return "", err 911 } 912 for _, line := range responses.Logs { 913 fmt.Print(line) 914 } 915 names = responses.Id 916 if flags&varlink.Continues == 0 { 917 break 918 } 919 } 920 return names, nil 921 } 922 923 // IsImageNotFound checks if the error indicates that no image was found. 924 func IsImageNotFound(err error) bool { 925 if errors.Cause(err) == image.ErrNoSuchImage { 926 return true 927 } 928 switch err.(type) { // nolint: gocritic 929 case *iopodman.ImageNotFound: 930 return true 931 } 932 return false 933 } 934 935 // HealthCheck executes a container's healthcheck over a varlink connection 936 func (r *LocalRuntime) HealthCheck(c *cliconfig.HealthCheckValues) (string, error) { 937 return iopodman.HealthCheckRun().Call(r.Conn, c.InputArgs[0]) 938 } 939 940 // Events monitors libpod/podman events over a varlink connection 941 func (r *LocalRuntime) Events(c *cliconfig.EventValues) error { 942 var more uint64 943 if c.Stream { 944 more = uint64(varlink.More) 945 } 946 reply, err := iopodman.GetEvents().Send(r.Conn, more, c.Filter, c.Since, c.Until) 947 if err != nil { 948 return errors.Wrapf(err, "unable to obtain events") 949 } 950 951 w := bufio.NewWriter(os.Stdout) 952 var tmpl *template.Template 953 if c.Format != formats.JSONString { 954 template, err := template.New("events").Parse(c.Format) 955 if err != nil { 956 return err 957 } 958 tmpl = template 959 } 960 961 for { 962 returnedEvent, flags, err := reply() 963 if err != nil { 964 // When the error handling is back into podman, we can flip this to a better way to check 965 // for problems. For now, this works. 966 return err 967 } 968 if returnedEvent.Time == "" && returnedEvent.Status == "" && returnedEvent.Type == "" { 969 // We got a blank event return, signals end of stream in certain cases 970 break 971 } 972 eTime, err := time.Parse(time.RFC3339Nano, returnedEvent.Time) 973 if err != nil { 974 return errors.Wrapf(err, "unable to parse time of event %s", returnedEvent.Time) 975 } 976 eType, err := events.StringToType(returnedEvent.Type) 977 if err != nil { 978 return err 979 } 980 eStatus, err := events.StringToStatus(returnedEvent.Status) 981 if err != nil { 982 return err 983 } 984 event := events.Event{ 985 ID: returnedEvent.Id, 986 Image: returnedEvent.Image, 987 Name: returnedEvent.Name, 988 Status: eStatus, 989 Time: eTime, 990 Type: eType, 991 } 992 if c.Format == formats.JSONString { // nolint: gocritic 993 jsonStr, err := event.ToJSONString() 994 if err != nil { 995 return errors.Wrapf(err, "unable to format json") 996 } 997 if _, err := w.Write([]byte(jsonStr)); err != nil { 998 return err 999 } 1000 } else if len(c.Format) > 0 { 1001 if err := tmpl.Execute(w, event); err != nil { 1002 return err 1003 } 1004 } else { 1005 if _, err := w.Write([]byte(event.ToHumanReadable())); err != nil { 1006 return err 1007 } 1008 } 1009 1010 if _, err := w.Write([]byte("\n")); err != nil { 1011 return err 1012 } 1013 if err := w.Flush(); err != nil { 1014 return err 1015 } 1016 if flags&varlink.Continues == 0 { 1017 break 1018 } 1019 } 1020 return nil 1021 } 1022 1023 // Diff ... 1024 func (r *LocalRuntime) Diff(c *cliconfig.DiffValues, to string) ([]archive.Change, error) { 1025 var changes []archive.Change 1026 reply, err := iopodman.Diff().Call(r.Conn, to) 1027 if err != nil { 1028 return nil, err 1029 } 1030 for _, change := range reply { 1031 changes = append(changes, archive.Change{Path: change.Path, Kind: stringToChangeType(change.ChangeType)}) 1032 } 1033 return changes, nil 1034 } 1035 1036 func stringToChangeType(change string) archive.ChangeType { 1037 switch change { 1038 case "A": 1039 return archive.ChangeAdd 1040 case "D": 1041 return archive.ChangeDelete 1042 default: // nolint: gocritic,stylecheck 1043 logrus.Errorf("'%s' is unknown archive type", change) 1044 fallthrough 1045 case "C": 1046 return archive.ChangeModify 1047 } 1048 } 1049 1050 // GenerateKube creates kubernetes email from containers and pods 1051 func (r *LocalRuntime) GenerateKube(c *cliconfig.GenerateKubeValues) (*v1.Pod, *v1.Service, error) { 1052 var ( 1053 pod v1.Pod 1054 service v1.Service 1055 ) 1056 reply, err := iopodman.GenerateKube().Call(r.Conn, c.InputArgs[0], c.Service) 1057 if err != nil { 1058 return nil, nil, errors.Wrap(err, "unable to create kubernetes YAML") 1059 } 1060 if err := json.Unmarshal([]byte(reply.Pod), &pod); err != nil { 1061 return nil, nil, err 1062 } 1063 err = json.Unmarshal([]byte(reply.Service), &service) 1064 return &pod, &service, err 1065 } 1066 1067 // GetContainersByContext looks up containers based on the cli input of all, latest, or a list 1068 func (r *LocalRuntime) GetContainersByContext(all bool, latest bool, namesOrIDs []string) ([]*Container, error) { 1069 var containers []*Container 1070 cids, err := iopodman.GetContainersByContext().Call(r.Conn, all, latest, namesOrIDs) 1071 if err != nil { 1072 return nil, err 1073 } 1074 for _, cid := range cids { 1075 ctr, err := r.LookupContainer(cid) 1076 if err != nil { 1077 return nil, err 1078 } 1079 containers = append(containers, ctr) 1080 } 1081 return containers, nil 1082 } 1083 1084 // GetVersion returns version information from service 1085 func (r *LocalRuntime) GetVersion() (define.Version, error) { 1086 version, goVersion, gitCommit, built, osArch, apiVersion, err := iopodman.GetVersion().Call(r.Conn) 1087 if err != nil { 1088 return define.Version{}, errors.Wrapf(err, "Unable to obtain server version information") 1089 } 1090 1091 var buildTime int64 1092 if built != "" { 1093 t, err := time.Parse(time.RFC3339, built) 1094 if err != nil { 1095 return define.Version{}, nil 1096 } 1097 buildTime = t.Unix() 1098 } 1099 1100 return define.Version{ 1101 RemoteAPIVersion: apiVersion, 1102 Version: version, 1103 GoVersion: goVersion, 1104 GitCommit: gitCommit, 1105 Built: buildTime, 1106 OsArch: osArch, 1107 }, nil 1108 }