mirror of
https://github.com/containers/podman.git
synced 2025-07-15 03:02:52 +08:00
Merge pull request #17494 from ashley-cui/osapply
Introduce podman machine os apply
This commit is contained in:
9
Makefile
9
Makefile
@ -396,15 +396,6 @@ bin/rootlessport: $(SOURCES) go.mod go.sum
|
||||
.PHONY: rootlessport
|
||||
rootlessport: bin/rootlessport
|
||||
|
||||
.PHONY: podman-remote-experimental
|
||||
podman-remote-experimental: $(SRCBINDIR)/experimental/podman$(BINSFX)
|
||||
$(SRCBINDIR)/experimental/podman$(BINSFX): $(SOURCES) go.mod go.sum | $(SRCBINDIR)
|
||||
$(GOCMD) build \
|
||||
$(BUILDFLAGS) \
|
||||
$(GO_LDFLAGS) '$(LDFLAGS_PODMAN)' \
|
||||
-tags "${REMOTETAGS} experimental" \
|
||||
-o $@ ./cmd/podman
|
||||
|
||||
###
|
||||
### Secondary binary-build targets
|
||||
###
|
||||
|
@ -1,6 +1,5 @@
|
||||
//go:build (amd64 || arm64) && experimental
|
||||
//go:build amd64 || arm64
|
||||
// +build amd64 arm64
|
||||
// +build experimental
|
||||
|
||||
package machine
|
||||
|
||||
@ -13,8 +12,8 @@ import (
|
||||
var (
|
||||
OSCmd = &cobra.Command{
|
||||
Use: "os",
|
||||
Short: "Manage a virtual machine's os",
|
||||
Long: "Manage a virtual machine's operating system",
|
||||
Short: "Manage a Podman virtual machine's OS",
|
||||
Long: "Manage a Podman virtual machine's operating system",
|
||||
PersistentPreRunE: validate.NoOp,
|
||||
RunE: validate.SubCommandExists,
|
||||
}
|
||||
|
@ -1,38 +1,60 @@
|
||||
//go:build (amd64 || arm64) && experimental
|
||||
//go:build amd64 || arm64
|
||||
// +build amd64 arm64
|
||||
// +build experimental
|
||||
|
||||
package machineos
|
||||
package os
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/podman/v4/cmd/podman/common"
|
||||
"github.com/containers/podman/v4/cmd/podman/machine"
|
||||
"github.com/containers/podman/v4/cmd/podman/registry"
|
||||
"github.com/containers/podman/v4/cmd/podman/validate"
|
||||
"github.com/containers/podman/v4/pkg/machine/os"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
applyCmd = &cobra.Command{
|
||||
Use: "apply",
|
||||
Short: "Apply OCI image to existing VM",
|
||||
Long: "Apply custom layers from a containerized Fedora CoreOS image on top of an existing VM",
|
||||
Use: "apply [options] IMAGE [NAME]",
|
||||
Short: "Apply an OCI image to a Podman Machine's OS",
|
||||
Long: "Apply custom layers from a containerized Fedora CoreOS OCI image on top of an existing VM",
|
||||
PersistentPreRunE: validate.NoOp,
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
RunE: apply,
|
||||
ValidArgsFunction: common.AutocompleteImages,
|
||||
Example: `podman machine os apply myimage`,
|
||||
}
|
||||
)
|
||||
|
||||
var restart bool
|
||||
|
||||
func init() {
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Command: applyCmd,
|
||||
Parent: machine.OSCmd,
|
||||
})
|
||||
flags := applyCmd.Flags()
|
||||
|
||||
restartFlagName := "restart"
|
||||
flags.BoolVar(&restart, restartFlagName, false, "Restart VM to apply changes")
|
||||
}
|
||||
|
||||
func apply(cmd *cobra.Command, args []string) error {
|
||||
fmt.Println("Applying..")
|
||||
return nil
|
||||
vmName := ""
|
||||
if len(args) == 2 {
|
||||
vmName = args[1]
|
||||
}
|
||||
managerOpts := ManagerOpts{
|
||||
VMName: vmName,
|
||||
CLIArgs: args,
|
||||
Restart: restart,
|
||||
}
|
||||
osManager, err := NewOSManager(managerOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
applyOpts := os.ApplyOptions{
|
||||
Image: args[0],
|
||||
}
|
||||
return osManager.Apply(args[0], applyOpts)
|
||||
}
|
||||
|
91
cmd/podman/machine/os/manager.go
Normal file
91
cmd/podman/machine/os/manager.go
Normal file
@ -0,0 +1,91 @@
|
||||
//go:build amd64 || arm64
|
||||
// +build amd64 arm64
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
machineconfig "github.com/containers/common/pkg/machine"
|
||||
"github.com/containers/podman/v4/cmd/podman/machine"
|
||||
pkgMachine "github.com/containers/podman/v4/pkg/machine"
|
||||
pkgOS "github.com/containers/podman/v4/pkg/machine/os"
|
||||
)
|
||||
|
||||
type ManagerOpts struct {
|
||||
VMName string
|
||||
CLIArgs []string
|
||||
Restart bool
|
||||
}
|
||||
|
||||
// NewOSManager creates a new OSManager depending on the mode of the call
|
||||
func NewOSManager(opts ManagerOpts) (pkgOS.Manager, error) {
|
||||
// If a VM name is specified, then we know that we are not inside a
|
||||
// Podman VM, but rather outside of it.
|
||||
if machineconfig.IsPodmanMachine() && opts.VMName == "" {
|
||||
return guestOSManager()
|
||||
}
|
||||
return machineOSManager(opts)
|
||||
}
|
||||
|
||||
// guestOSManager returns an OSmanager for inside-VM operations
|
||||
func guestOSManager() (pkgOS.Manager, error) {
|
||||
dist := GetDistribution()
|
||||
switch {
|
||||
case dist.Name == "fedora" && dist.Variant == "coreos":
|
||||
return &pkgOS.OSTree{}, nil
|
||||
default:
|
||||
return nil, errors.New("unsupported OS")
|
||||
}
|
||||
}
|
||||
|
||||
// machineOSManager returns an os manager that manages outside the VM.
|
||||
func machineOSManager(opts ManagerOpts) (pkgOS.Manager, error) {
|
||||
vmName := opts.VMName
|
||||
if opts.VMName == "" {
|
||||
vmName = pkgMachine.DefaultMachineName
|
||||
}
|
||||
provider := machine.GetSystemDefaultProvider()
|
||||
vm, err := provider.LoadVMByName(vmName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pkgOS.MachineOS{
|
||||
VM: vm,
|
||||
Args: opts.CLIArgs,
|
||||
VMName: vmName,
|
||||
Restart: opts.Restart,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type Distribution struct {
|
||||
Name string
|
||||
Variant string
|
||||
}
|
||||
|
||||
// GetDistribution checks the OS distribution
|
||||
func GetDistribution() Distribution {
|
||||
dist := Distribution{
|
||||
Name: "unknown",
|
||||
Variant: "unknown",
|
||||
}
|
||||
f, err := os.Open("/etc/os-release")
|
||||
if err != nil {
|
||||
return dist
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
l := bufio.NewScanner(f)
|
||||
for l.Scan() {
|
||||
if strings.HasPrefix(l.Text(), "ID=") {
|
||||
dist.Name = strings.TrimPrefix(l.Text(), "ID=")
|
||||
}
|
||||
if strings.HasPrefix(l.Text(), "VARIANT_ID=") {
|
||||
dist.Variant = strings.Trim(strings.TrimPrefix(l.Text(), "VARIANT_ID="), "\"")
|
||||
}
|
||||
}
|
||||
return dist
|
||||
}
|
7
cmd/podman/machine/os/os_unsupported.go
Normal file
7
cmd/podman/machine/os/os_unsupported.go
Normal file
@ -0,0 +1,7 @@
|
||||
//go:build !amd64 && !arm64
|
||||
// +build !amd64,!arm64
|
||||
|
||||
package os
|
||||
|
||||
// init do not register _podman machine os_ command on unsupported platforms
|
||||
func init() {}
|
@ -11,6 +11,7 @@ import (
|
||||
_ "github.com/containers/podman/v4/cmd/podman/images"
|
||||
_ "github.com/containers/podman/v4/cmd/podman/kube"
|
||||
_ "github.com/containers/podman/v4/cmd/podman/machine"
|
||||
_ "github.com/containers/podman/v4/cmd/podman/machine/os"
|
||||
_ "github.com/containers/podman/v4/cmd/podman/manifest"
|
||||
_ "github.com/containers/podman/v4/cmd/podman/networks"
|
||||
_ "github.com/containers/podman/v4/cmd/podman/pods"
|
||||
|
@ -1,8 +0,0 @@
|
||||
//go:build experimental
|
||||
// +build experimental
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "github.com/containers/podman/v4/cmd/podman/machine/os"
|
||||
)
|
@ -4,7 +4,7 @@
|
||||
podman\-machine\-inspect - Inspect one or more virtual machines
|
||||
|
||||
## SYNOPSIS
|
||||
**podman machine inspect** [*options] *name* ...
|
||||
**podman machine inspect** [*options*] *name* ...
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
|
41
docs/source/markdown/podman-machine-os-apply.1.md
Normal file
41
docs/source/markdown/podman-machine-os-apply.1.md
Normal file
@ -0,0 +1,41 @@
|
||||
% podman-machine-os-apply 1
|
||||
|
||||
## NAME
|
||||
podman\-machine\-os\-apply - Apply an OCI image to a Podman Machine's OS
|
||||
|
||||
## SYNOPSIS
|
||||
**podman machine os apply** [*options*] *image* [vm]
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
Apply machine OS changes from an OCI image.
|
||||
|
||||
VM's that use OS's that use rpm-ostreee have the capability to rebase itself from the content of an OCI image.
|
||||
`podman machine image apply` takes an OCI image with container native ostree functionality and rebases itself on that image.
|
||||
|
||||
By default, Podman machines on Mac and Linux use an rpm-ostree based distrubition (Fedora CoreOS).
|
||||
|
||||
For more information, please see the [rpm-ostree docs](https://coreos.github.io/rpm-ostree/container/).
|
||||
|
||||
## OPTIONS
|
||||
|
||||
#### **--help**
|
||||
|
||||
Print usage statement.
|
||||
|
||||
#### **--restart**
|
||||
|
||||
Restart VM after applying changes.
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
```
|
||||
$ podman machine os apply quay.io/podman_next
|
||||
$ podman machine os apply quay.io/podman_next podman-machine-defualt
|
||||
```
|
||||
|
||||
## SEE ALSO
|
||||
**[podman(1)](podman.1.md)**, **[podman-machine(1)](podman-machine.1.md)**, **[podman-machine-os(1)](podman-machine-os.1.md)**
|
||||
|
||||
## HISTORY
|
||||
February 2023, Originally compiled by Ashley Cui <acui@redhat.com>
|
22
docs/source/markdown/podman-machine-os.1.md
Normal file
22
docs/source/markdown/podman-machine-os.1.md
Normal file
@ -0,0 +1,22 @@
|
||||
% podman-machine-os 1
|
||||
|
||||
## NAME
|
||||
podman\-machine\-os - Manage a Podman virtual machine's OS
|
||||
|
||||
## SYNOPSIS
|
||||
**podman machine os** *subcommand*
|
||||
|
||||
## DESCRIPTION
|
||||
`podman machine os` is a set of subcommands that manage a Podman virtual machine's operating system.
|
||||
|
||||
## SUBCOMMANDS
|
||||
|
||||
| Command | Man Page | Description |
|
||||
|---------|--------------------------------------------------------------|----------------------------------------------|
|
||||
| apply | [podman-machine-os-apply(1)](podman-machine-os-apply.1.md) | Apply an OCI image to a Podman Machine's OS |
|
||||
|
||||
## SEE ALSO
|
||||
**[podman(1)](podman.1.md)**, **[podman-machine(1)](podman-machine.1.md)**, **[podman-machine-os-apply(1)](podman-machine-os-apply.1.md)**
|
||||
|
||||
## HISTORY
|
||||
February 2023, Originally compiled by Ashley Cui <acui@redhat.com>
|
@ -22,20 +22,21 @@ environment variable while the machines are running can lead to unexpected behav
|
||||
|
||||
## SUBCOMMANDS
|
||||
|
||||
| Command | Man Page | Description |
|
||||
|---------|------------------------------------------------------|-----------------------------------|
|
||||
| info | [podman-machine-info(1)](podman-machine-info.1.md) | Display machine host info |
|
||||
| init | [podman-machine-init(1)](podman-machine-init.1.md) | Initialize a new virtual machine |
|
||||
| Command | Man Page | Description |
|
||||
|---------|-----------------------------------------------------------|--------------------------------------|
|
||||
| info | [podman-machine-info(1)](podman-machine-info.1.md) | Display machine host info |
|
||||
| init | [podman-machine-init(1)](podman-machine-init.1.md) | Initialize a new virtual machine |
|
||||
| inspect | [podman-machine-inspect(1)](podman-machine-inspect.1.md) | Inspect one or more virtual machines |
|
||||
| list | [podman-machine-list(1)](podman-machine-list.1.md) | List virtual machines |
|
||||
| rm | [podman-machine-rm(1)](podman-machine-rm.1.md) | Remove a virtual machine |
|
||||
| set | [podman-machine-set(1)](podman-machine-set.1.md) | Sets a virtual machine setting |
|
||||
| ssh | [podman-machine-ssh(1)](podman-machine-ssh.1.md) | SSH into a virtual machine |
|
||||
| start | [podman-machine-start(1)](podman-machine-start.1.md) | Start a virtual machine |
|
||||
| stop | [podman-machine-stop(1)](podman-machine-stop.1.md) | Stop a virtual machine |
|
||||
| list | [podman-machine-list(1)](podman-machine-list.1.md) | List virtual machines |
|
||||
| os | [podman-machine-os(1)](podman-machine-os.1.md) | Manage a Podman virtual machine's OS |
|
||||
| rm | [podman-machine-rm(1)](podman-machine-rm.1.md) | Remove a virtual machine |
|
||||
| set | [podman-machine-set(1)](podman-machine-set.1.md) | Sets a virtual machine setting |
|
||||
| ssh | [podman-machine-ssh(1)](podman-machine-ssh.1.md) | SSH into a virtual machine |
|
||||
| start | [podman-machine-start(1)](podman-machine-start.1.md) | Start a virtual machine |
|
||||
| stop | [podman-machine-stop(1)](podman-machine-stop.1.md) | Stop a virtual machine |
|
||||
|
||||
## SEE ALSO
|
||||
**[podman(1)](podman.1.md)**, **[podman-machine-info(1)](podman-machine-info.1.md)**, **[podman-machine-init(1)](podman-machine-init.1.md)**, **[podman-machine-list(1)](podman-machine-list.1.md)**, **[podman-machine-rm(1)](podman-machine-rm.1.md)**, **[podman-machine-ssh(1)](podman-machine-ssh.1.md)**, **[podman-machine-start(1)](podman-machine-start.1.md)**, **[podman-machine-stop(1)](podman-machine-stop.1.md)**, **[podman-machine-inspect(1)](podman-machine-inspect.1.md)**
|
||||
**[podman(1)](podman.1.md)**, **[podman-machine-info(1)](podman-machine-info.1.md)**, **[podman-machine-init(1)](podman-machine-init.1.md)**, **[podman-machine-list(1)](podman-machine-list.1.md)**, **[podman-machine-os(1)](podman-machine-os.1.md)**, **[podman-machine-rm(1)](podman-machine-rm.1.md)**, **[podman-machine-ssh(1)](podman-machine-ssh.1.md)**, **[podman-machine-start(1)](podman-machine-start.1.md)**, **[podman-machine-stop(1)](podman-machine-stop.1.md)**, **[podman-machine-inspect(1)](podman-machine-inspect.1.md)**
|
||||
|
||||
## HISTORY
|
||||
March 2021, Originally compiled by Ashley Cui <acui@redhat.com>
|
||||
|
27
pkg/machine/e2e/config_os_apply_test.go
Normal file
27
pkg/machine/e2e/config_os_apply_test.go
Normal file
@ -0,0 +1,27 @@
|
||||
package e2e_test
|
||||
|
||||
// type applyMachineOS struct {
|
||||
// restart bool
|
||||
|
||||
// cmd []string
|
||||
// }
|
||||
|
||||
// func (a *applyMachineOS) buildCmd(m *machineTestBuilder) []string {
|
||||
// cmd := []string{"machine", "os", "apply"}
|
||||
// if a.restart {
|
||||
// cmd = append(cmd, "--restart")
|
||||
// }
|
||||
|
||||
// a.cmd = cmd
|
||||
// return cmd
|
||||
// }
|
||||
|
||||
// func (a *applyMachineOS) withRestart() *applyMachineOS {
|
||||
// a.restart = true
|
||||
// return a
|
||||
// }
|
||||
|
||||
// func (a *applyMachineOS) args(cmd []string) *applyMachineOS {
|
||||
// a.cmd = cmd
|
||||
// return a
|
||||
// }
|
59
pkg/machine/e2e/os_test.go
Normal file
59
pkg/machine/e2e/os_test.go
Normal file
@ -0,0 +1,59 @@
|
||||
package e2e_test
|
||||
|
||||
// import (
|
||||
// . "github.com/onsi/ginkgo"
|
||||
// . "github.com/onsi/gomega"
|
||||
// . "github.com/onsi/gomega/gexec"
|
||||
// )
|
||||
|
||||
// var _ = Describe("podman machine os apply", func() {
|
||||
// var (
|
||||
// mb *machineTestBuilder
|
||||
// testDir string
|
||||
// )
|
||||
|
||||
// BeforeEach(func() {
|
||||
// testDir, mb = setup()
|
||||
// })
|
||||
// AfterEach(func() {
|
||||
// teardown(originalHomeDir, testDir, mb)
|
||||
// })
|
||||
|
||||
// It("apply machine", func() {
|
||||
// i := new(initMachine)
|
||||
// foo1, err := mb.setName("foo1").setCmd(i.withImagePath(mb.imagePath)).run()
|
||||
// Expect(err).ToNot(HaveOccurred())
|
||||
// Expect(foo1).To(Exit(0))
|
||||
|
||||
// apply := new(applyMachineOS)
|
||||
// applySession, err := mb.setName("foo1").setCmd(apply.args([]string{"quay.io/baude/podman_next"})).run()
|
||||
// Expect(err).ToNot(HaveOccurred())
|
||||
// Expect(applySession).To(Exit(0))
|
||||
// })
|
||||
|
||||
// It("apply machine from containers-storage", func() {
|
||||
// i := new(initMachine)
|
||||
// foo1, err := mb.setName("foo1").setCmd(i.withImagePath(mb.imagePath)).run()
|
||||
// Expect(err).ToNot(HaveOccurred())
|
||||
// Expect(foo1).To(Exit(0))
|
||||
|
||||
// ssh := sshMachine{}
|
||||
// sshSession, err := mb.setName("foo1").setCmd(ssh.withSSHComand([]string{"podman", "pull", "quay.io/baude/podman_next"})).run()
|
||||
// Expect(err).ToNot(HaveOccurred())
|
||||
// Expect(sshSession).To(Exit(0))
|
||||
|
||||
// apply := new(applyMachineOS)
|
||||
// applySession, err := mb.setName("foo1").setCmd(apply.args([]string{"quay.io/baude/podman_next"})).run()
|
||||
// Expect(err).ToNot(HaveOccurred())
|
||||
// Expect(applySession).To(Exit(0))
|
||||
// Expect(applySession.outputToString()).To(ContainSubstring("Pulling from: containers-storage"))
|
||||
// })
|
||||
|
||||
// It("apply machine not exist", func() {
|
||||
// apply := new(applyMachineOS)
|
||||
// applySession, err := mb.setName("foo1").setCmd(apply.args([]string{"quay.io/baude/podman_next", "notamachine"})).run()
|
||||
// Expect(err).ToNot(HaveOccurred())
|
||||
// Expect(applySession).To(Exit(125))
|
||||
// Expect(applySession.errorToString()).To(ContainSubstring("not exist"))
|
||||
// })
|
||||
// })
|
15
pkg/machine/os/config.go
Normal file
15
pkg/machine/os/config.go
Normal file
@ -0,0 +1,15 @@
|
||||
//go:build amd64 || arm64
|
||||
// +build amd64 arm64
|
||||
|
||||
package os
|
||||
|
||||
// Manager is the interface for operations on a Podman machine's OS
|
||||
type Manager interface {
|
||||
// Apply machine OS changes from an OCI image.
|
||||
Apply(image string, opts ApplyOptions) error
|
||||
}
|
||||
|
||||
// ApplyOptions are the options for applying an image into a Podman machine VM
|
||||
type ApplyOptions struct {
|
||||
Image string
|
||||
}
|
40
pkg/machine/os/machine_os.go
Normal file
40
pkg/machine/os/machine_os.go
Normal file
@ -0,0 +1,40 @@
|
||||
//go:build amd64 || arm64
|
||||
// +build amd64 arm64
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/podman/v4/pkg/machine"
|
||||
)
|
||||
|
||||
// MachineOS manages machine OS's from outside the machine.
|
||||
type MachineOS struct {
|
||||
Args []string
|
||||
VM machine.VM
|
||||
VMName string
|
||||
Restart bool
|
||||
}
|
||||
|
||||
// Apply applies the image by sshing into the machine and running apply from inside the VM.
|
||||
func (m *MachineOS) Apply(image string, opts ApplyOptions) error {
|
||||
sshOpts := machine.SSHOptions{
|
||||
Args: []string{"podman", "machine", "os", "apply", image},
|
||||
}
|
||||
|
||||
if err := m.VM.SSH(m.VMName, sshOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if m.Restart {
|
||||
if err := m.VM.Stop(m.VMName, machine.StopOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := m.VM.Start(m.VMName, machine.StartOptions{NoInfo: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Machine %q restarted successfully\n", m.VMName)
|
||||
}
|
||||
return nil
|
||||
}
|
142
pkg/machine/os/ostree.go
Normal file
142
pkg/machine/os/ostree.go
Normal file
@ -0,0 +1,142 @@
|
||||
//go:build amd64 || arm64
|
||||
// +build amd64 arm64
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/image/v5/transports/alltransports"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// OSTree deals with operations on ostree based os's
|
||||
type OSTree struct { //nolint:revive
|
||||
}
|
||||
|
||||
// Apply takes an OCI image and does an rpm-ostree rebase on the image
|
||||
// If no containers-transport is specified,
|
||||
// apply will first check if the image exists locally, then default to pulling.
|
||||
// Exec-ing out to rpm-ostree rebase requires sudo, so this means apply cannot
|
||||
// be called within podman's user namespace if run as rootless.
|
||||
// This means that we need to export images in containers-storage to oci-dirs
|
||||
// We also need to do this via an exec, because if we tried to use the ABI functions,
|
||||
// we would enter the user namespace, the rebase command would fail.
|
||||
// The pull portion of this function essentially is a work-around for two things:
|
||||
// 1. rpm-ostree requires you to specify the containers-transport when pulling.
|
||||
// The pull in podman allows the behavior of os apply to match other podman commands,
|
||||
// where you only pull if the image does not exist in storage already.
|
||||
// 2. This works around the root/rootless issue.
|
||||
// Podman machines are by default set up using a rootless connection.
|
||||
// rpm-ostree needs to be run as root. If a user wants to use an image in containers-storage,
|
||||
// rpm-ostree will look at the root storage, and not the user storage, which is unexpected behavior.
|
||||
// Exporting to an oci-dir works around this, without nagging the user to configure the machine in rootful mode.
|
||||
func (dist *OSTree) Apply(image string, opts ApplyOptions) error {
|
||||
imageWithTransport := image
|
||||
|
||||
transport := alltransports.TransportFromImageName(image)
|
||||
|
||||
switch {
|
||||
// no transport was specified
|
||||
case transport == nil:
|
||||
exists, err := execPodmanImageExists(image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if exists {
|
||||
fmt.Println("Pulling from", "containers-storage"+":", imageWithTransport)
|
||||
dir, err := os.MkdirTemp("", pathSafeString(imageWithTransport))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Chmod(dir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := os.RemoveAll(dir); err != nil {
|
||||
logrus.Errorf("failed to remove temporary pull file: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := execPodmanSave(dir, image); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
imageWithTransport = "oci:" + dir
|
||||
} else {
|
||||
// if image doesn't exist locally, assume that we want to pull and use docker transport
|
||||
imageWithTransport = "docker://" + image
|
||||
}
|
||||
// containers-transport specified
|
||||
case transport.Name() == "containers-storage":
|
||||
fmt.Println("Pulling from", image)
|
||||
dir, err := os.MkdirTemp("", pathSafeString(strings.TrimPrefix(image, "containers-storage"+":")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.Chmod(dir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := os.RemoveAll(dir); err != nil {
|
||||
logrus.Errorf("failed to remove temporary pull file: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := execPodmanSave(dir, image); err != nil {
|
||||
return err
|
||||
}
|
||||
imageWithTransport = "oci:" + dir
|
||||
}
|
||||
|
||||
ostreeCli := []string{"rpm-ostree", "--bypass-driver", "rebase", fmt.Sprintf("ostree-unverified-image:%s", imageWithTransport)}
|
||||
cmd := exec.Command("sudo", ostreeCli...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// pathSafeString creates a path-safe name for our tmpdirs
|
||||
func pathSafeString(str string) string {
|
||||
alphanumOnly := regexp.MustCompile(`[^a-zA-Z0-9]+`)
|
||||
|
||||
return alphanumOnly.ReplaceAllString(str, "")
|
||||
}
|
||||
|
||||
// execPodmanSave execs out to podman save
|
||||
func execPodmanSave(dir, image string) error {
|
||||
saveArgs := []string{"image", "save", "--format", "oci-dir", "-o", dir, image}
|
||||
|
||||
saveCmd := exec.Command("podman", saveArgs...)
|
||||
saveCmd.Stdout = os.Stdout
|
||||
saveCmd.Stderr = os.Stderr
|
||||
return saveCmd.Run()
|
||||
}
|
||||
|
||||
// execPodmanSave execs out to podman image exists
|
||||
func execPodmanImageExists(image string) (bool, error) {
|
||||
existsArgs := []string{"image", "exists", image}
|
||||
|
||||
existsCmd := exec.Command("podman", existsArgs...)
|
||||
|
||||
if err := existsCmd.Run(); err != nil {
|
||||
if exitError, ok := err.(*exec.ExitError); ok {
|
||||
switch exitCode := exitError.ExitCode(); exitCode {
|
||||
case 1:
|
||||
return false, nil
|
||||
default:
|
||||
return false, errors.New("unable to access local image store")
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
Reference in New Issue
Block a user