1
0
mirror of https://github.com/containers/podman.git synced 2026-02-05 15:45:08 +01:00

Merge pull request #27999 from baude/applytransports

add bootc transports to os-apply
This commit is contained in:
Paul Holzinger
2026-02-03 14:53:18 +01:00
committed by GitHub
11 changed files with 336 additions and 54 deletions

View File

@@ -504,12 +504,12 @@ func simplePathJoinUnix(p1, p2 string) string {
return p1 + "/" + p2
}
// validCurrentCmdLine validates the current cmd line
// ValidCurrentCmdLine validates the current cmd line
// It utilizes the Args function from the cmd struct
// In most cases the Args function validates the args length but it
// is also used to verify that --latest is not given with an argument.
// This function helps to makes sure we only complete valid arguments.
func validCurrentCmdLine(cmd *cobra.Command, args []string, toComplete string) bool {
func ValidCurrentCmdLine(cmd *cobra.Command, args []string, toComplete string) bool {
if cmd.Args == nil {
// Without an Args function we cannot check so assume it's correct
return true
@@ -592,14 +592,14 @@ func getBoolCompletion(_ string) ([]string, cobra.ShellCompDirective) {
/* Autocomplete Functions for cobra ValidArgsFunction */
func AutocompleteArtifacts(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getArtifacts(cmd, toComplete)
}
func AutocompleteArtifactAdd(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
if len(args) == 0 {
@@ -611,7 +611,7 @@ func AutocompleteArtifactAdd(cmd *cobra.Command, args []string, toComplete strin
// AutocompleteContainers - Autocomplete all container names.
func AutocompleteContainers(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getContainers(cmd, toComplete, completeDefault)
@@ -619,7 +619,7 @@ func AutocompleteContainers(cmd *cobra.Command, args []string, toComplete string
// AutocompleteContainersCreated - Autocomplete only created container names.
func AutocompleteContainersCreated(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getContainers(cmd, toComplete, completeDefault, "created")
@@ -627,7 +627,7 @@ func AutocompleteContainersCreated(cmd *cobra.Command, args []string, toComplete
// AutocompleteContainersExited - Autocomplete only exited container names.
func AutocompleteContainersExited(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getContainers(cmd, toComplete, completeDefault, "exited")
@@ -635,7 +635,7 @@ func AutocompleteContainersExited(cmd *cobra.Command, args []string, toComplete
// AutocompleteContainersPaused - Autocomplete only paused container names.
func AutocompleteContainersPaused(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getContainers(cmd, toComplete, completeDefault, "paused")
@@ -643,7 +643,7 @@ func AutocompleteContainersPaused(cmd *cobra.Command, args []string, toComplete
// AutocompleteContainersRunning - Autocomplete only running container names.
func AutocompleteContainersRunning(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getContainers(cmd, toComplete, completeDefault, "running")
@@ -651,7 +651,7 @@ func AutocompleteContainersRunning(cmd *cobra.Command, args []string, toComplete
// AutocompleteContainersStartable - Autocomplete only created and exited container names.
func AutocompleteContainersStartable(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getContainers(cmd, toComplete, completeDefault, "created", "exited")
@@ -659,7 +659,7 @@ func AutocompleteContainersStartable(cmd *cobra.Command, args []string, toComple
// AutocompletePods - Autocomplete all pod names.
func AutocompletePods(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getPods(cmd, toComplete, completeDefault)
@@ -668,7 +668,7 @@ func AutocompletePods(cmd *cobra.Command, args []string, toComplete string) ([]s
// AutocompletePodsRunning - Autocomplete only running pod names.
// It considers degraded as running.
func AutocompletePodsRunning(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getPods(cmd, toComplete, completeDefault, "running", "degraded")
@@ -678,7 +678,7 @@ func AutocompletePodsRunning(cmd *cobra.Command, args []string, toComplete strin
// When a pod has a few containers paused, that ends up in degraded state
// So autocomplete degraded pod names as well
func AutoCompletePodsPause(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getPods(cmd, toComplete, completeDefault, "paused", "degraded")
@@ -686,7 +686,7 @@ func AutoCompletePodsPause(cmd *cobra.Command, args []string, toComplete string)
// AutocompleteForKube - Autocomplete all Podman objects supported by kube generate.
func AutocompleteForKube(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
containers, _ := getContainers(cmd, toComplete, completeDefault)
@@ -704,7 +704,7 @@ func AutocompleteForGenerate(cmd *cobra.Command, args []string, toComplete strin
// AutocompleteContainersAndPods - Autocomplete container names and pod names.
func AutocompleteContainersAndPods(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
containers, _ := getContainers(cmd, toComplete, completeDefault)
@@ -714,7 +714,7 @@ func AutocompleteContainersAndPods(cmd *cobra.Command, args []string, toComplete
// AutocompleteContainersAndImages - Autocomplete container names and pod names.
func AutocompleteContainersAndImages(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
containers, _ := getContainers(cmd, toComplete, completeDefault)
@@ -724,7 +724,7 @@ func AutocompleteContainersAndImages(cmd *cobra.Command, args []string, toComple
// AutocompleteVolumes - Autocomplete volumes.
func AutocompleteVolumes(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getVolumes(cmd, toComplete)
@@ -732,7 +732,7 @@ func AutocompleteVolumes(cmd *cobra.Command, args []string, toComplete string) (
// AutocompleteSecrets - Autocomplete secrets.
func AutocompleteSecrets(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getSecrets(cmd, toComplete, completeDefault)
@@ -747,7 +747,7 @@ func AutocompleteSecretCreate(_ *cobra.Command, args []string, _ string) ([]stri
// AutocompleteImages - Autocomplete images.
func AutocompleteImages(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getImages(cmd, toComplete)
@@ -755,7 +755,7 @@ func AutocompleteImages(cmd *cobra.Command, args []string, toComplete string) ([
// AutocompleteQuadlets - Autocomplete quadlets.
func AutocompleteQuadlets(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getQuadlets(cmd, toComplete)
@@ -763,7 +763,7 @@ func AutocompleteQuadlets(cmd *cobra.Command, args []string, toComplete string)
// AutocompleteManifestListAndMember - Autocomplete names of manifest lists and digests of items in them.
func AutocompleteManifestListAndMember(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
if len(args) == 0 {
@@ -787,7 +787,7 @@ func AutocompletePodExitPolicy(_ *cobra.Command, _ []string, _ string) ([]string
// AutocompleteCreateRun - Autocomplete only the fist argument as image and then do file completion.
func AutocompleteCreateRun(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
if len(args) < 1 {
@@ -829,7 +829,7 @@ func AutocompleteCreateRun(cmd *cobra.Command, args []string, toComplete string)
// AutocompleteRegistries - Autocomplete registries.
func AutocompleteRegistries(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getRegistries()
@@ -837,7 +837,7 @@ func AutocompleteRegistries(cmd *cobra.Command, args []string, toComplete string
// AutocompleteNetworks - Autocomplete networks.
func AutocompleteNetworks(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return getNetworks(cmd, toComplete, completeDefault)
@@ -860,7 +860,7 @@ func AutocompleteDefaultOneArg(_ *cobra.Command, args []string, _ string) ([]str
// AutocompleteCommitCommand - Autocomplete podman commit command args.
func AutocompleteCommitCommand(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
if len(args) == 0 {
@@ -875,7 +875,7 @@ func AutocompleteCommitCommand(cmd *cobra.Command, args []string, toComplete str
// AutocompleteCpCommand - Autocomplete podman cp command args.
func AutocompleteCpCommand(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
if len(args) < 2 {
@@ -921,7 +921,7 @@ func AutocompleteCpCommand(cmd *cobra.Command, args []string, toComplete string)
// AutocompleteExecCommand - Autocomplete podman exec command args.
func AutocompleteExecCommand(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
if len(args) == 0 {
@@ -932,7 +932,7 @@ func AutocompleteExecCommand(cmd *cobra.Command, args []string, toComplete strin
// AutocompleteRunlabelCommand - Autocomplete podman container runlabel command args.
func AutocompleteRunlabelCommand(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
if len(args) == 0 {
@@ -949,7 +949,7 @@ func AutocompleteRunlabelCommand(cmd *cobra.Command, args []string, toComplete s
// AutocompleteContainerOneArg - Autocomplete containers as fist arg.
func AutocompleteContainerOneArg(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
if len(args) == 0 {
@@ -991,7 +991,7 @@ func AutocompleteTopCmd(cmd *cobra.Command, args []string, toComplete string) ([
// AutocompleteInspect - Autocomplete podman inspect.
func AutocompleteInspect(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
containers, _ := getContainers(cmd, toComplete, completeDefault)
@@ -1010,7 +1010,7 @@ func AutocompleteInspect(cmd *cobra.Command, args []string, toComplete string) (
}
func AutoCompleteFarms(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
farms, err := podmanConfig.ContainersConfDefaultsRO.GetAllFarms()
@@ -1029,7 +1029,7 @@ func AutoCompleteFarms(cmd *cobra.Command, args []string, toComplete string) ([]
// AutocompleteSystemConnections - Autocomplete system connections.
func AutocompleteSystemConnections(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
@@ -1050,7 +1050,7 @@ func AutocompleteSystemConnections(cmd *cobra.Command, args []string, toComplete
// AutocompleteScp returns a list of connections, images, or both, depending on the amount of arguments
func AutocompleteScp(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
switch len(args) {
@@ -1957,7 +1957,7 @@ func AutocompleteCompressionFormat(_ *cobra.Command, _ []string, _ string) ([]st
// AutocompleteClone - Autocomplete container and image names
func AutocompleteClone(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
switch len(args) {
@@ -1977,7 +1977,7 @@ func AutocompleteClone(cmd *cobra.Command, args []string, toComplete string) ([]
// AutocompleteSSH - Autocomplete ssh modes
func AutocompleteSSH(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if !validCurrentCmdLine(cmd, args, toComplete) {
if !ValidCurrentCmdLine(cmd, args, toComplete) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return []string{string(ssh.GolangMode), string(ssh.NativeMode)}, cobra.ShellCompDirectiveNoFileComp

View File

@@ -23,7 +23,7 @@ var (
PersistentPreRunE: machinePreRunE,
RunE: inspect,
Example: `podman machine inspect myvm`,
ValidArgsFunction: autocompleteMachine,
ValidArgsFunction: AutocompleteMachine,
}
inspectFlag = inspectFlagType{}
)

View File

@@ -12,6 +12,7 @@ import (
"sync"
"time"
"github.com/containers/podman/v6/cmd/podman/common"
"github.com/containers/podman/v6/cmd/podman/registry"
"github.com/containers/podman/v6/cmd/podman/validate"
"github.com/containers/podman/v6/libpod/events"
@@ -93,9 +94,9 @@ func autocompleteMachineCp(_ *cobra.Command, args []string, toComplete string) (
return nil, cobra.ShellCompDirectiveNoFileComp
}
// autocompleteMachine - Autocomplete machines.
func autocompleteMachine(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
// AutocompleteMachine - Autocomplete machines.
func AutocompleteMachine(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if common.ValidCurrentCmdLine(cmd, args, toComplete) {
return getMachines(toComplete)
}
return nil, cobra.ShellCompDirectiveNoFileComp

View File

@@ -12,13 +12,24 @@ import (
)
var applyCmd = &cobra.Command{
Use: "apply [options] IMAGE [NAME]",
Use: "apply [options] URI|IMAGE [MACHINE]",
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,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
switch len(args) {
case 0:
images, _ := common.AutocompleteImages(cmd, args, toComplete)
// We also accept an URI so ignore ShellCompDirectiveNoFileComp and use the default one instead to get file paths completed by the shell.
return images, cobra.ShellCompDirectiveDefault
case 1:
return machine.AutocompleteMachine(cmd, args, toComplete)
default:
return nil, cobra.ShellCompDirectiveNoFileComp
}
},
Example: `podman machine os apply myimage`,
}

View File

@@ -18,7 +18,7 @@ var rmCmd = &cobra.Command{
RunE: rm,
Args: cobra.MaximumNArgs(1),
Example: `podman machine rm podman-machine-default`,
ValidArgsFunction: autocompleteMachine,
ValidArgsFunction: AutocompleteMachine,
}
var destroyOptions machine.RemoveOptions

View File

@@ -21,7 +21,7 @@ var (
RunE: start,
Args: cobra.MaximumNArgs(1),
Example: `podman machine start podman-machine-default`,
ValidArgsFunction: autocompleteMachine,
ValidArgsFunction: AutocompleteMachine,
}
startOpts = machine.StartOptions{}
setDefaultSystemConn bool

View File

@@ -19,7 +19,7 @@ var stopCmd = &cobra.Command{
RunE: stop,
Args: cobra.MaximumNArgs(1),
Example: `podman machine stop podman-machine-default`,
ValidArgsFunction: autocompleteMachine,
ValidArgsFunction: AutocompleteMachine,
}
func init() {

View File

@@ -4,7 +4,7 @@
podman\-machine\-os\-apply - Apply an OCI image to a Podman Machine's OS
## SYNOPSIS
**podman machine os apply** [*options*] *image* [vm]
**podman machine os apply** [*options*] *uri* [vm]
## DESCRIPTION
@@ -19,7 +19,16 @@ customized distribution and cannot be updated with this command.
Note: WSL-based machines are upgradable by using the `podman machine ssh <machine_name>` command followed by `sudo dnf update`. This can, however, result in unexpected results in
Podman client and server version differences.
The applying of the OCI image is done by a command called `bootc`.
The applying of the OCI image is done by a command called `bootc` and specifically `bootc switch`. By default, this command
takes an OCI registry image reference like `quay.io/custom/machine-os:latest`. However, `bootc` also
understands references with different transports. At present, Podman will support the following transports:
* containers-storage
* oci
* oci-archive
* registry
Examples for these transports in URI form are provided below.
Podman machine images are stored as OCI images at `quay.io/podman/machine-os`. When applying an image using this
command, the fully qualified OCI reference name must be used including tag where the tag is the
@@ -47,15 +56,37 @@ bootable OCI image.
Note: This may result in having a newer Podman version inside the machine
than the client. Unexpected results may occur.
Update the default Podman machine to the most recent Podman 6.1 bootable
OCI image.
Apply a new custom operating system from an OCI bootable image on quay.
```
$ podman machine os apply quay.io/podman/machine-os:6.1
$ podman machine os apply quay.io/custom/machine-os:latest
```
Update the specified Podman machine to latest Podman 6.1 bootable OCI image.
Apply a new custom operating system to a specific machine from an OCI bootable image on quay.
```
$ podman machine os apply quay.io/podman/machine-os:6.1 mymachine
$ podman machine os apply quay.io/custom/machine-os:latest mymachine
```
Apply a new custom operating system that was pulled or built on your existing machine by the unprivileged user.
Note the use of brackets around the path because this command is run by the root user.
```
$ podman machine os apply containers-storage:[/home/core/.local/share/containers/storage]localhost/mycustomimage:latest
```
Apply a new custom operating system that was pulled or built on your existing machine by the privileged user. Note that
because a privileged user built or pulled the image, `bootc` will resolve that users storage and the path
is not needed.
```
$ podman machine os apply containers-storage:localhost/mycustomimage:latest
```
Apply a new custom operating system from an OCI archive in tar form.
```
$ podman machine os apply oci-archive:/tmp/oci-image.tar
```
Apply a new custom operating system from an OCI formatted directory.
```
$ podman machine os apply oci:/tmp/oci-image/
```
## SEE ALSO

View File

@@ -8,6 +8,7 @@ import (
"fmt"
"os"
"os/exec"
"strings"
"github.com/blang/semver/v4"
"github.com/opencontainers/go-digest"
@@ -16,6 +17,7 @@ import (
"go.podman.io/image/v5/docker/reference"
"go.podman.io/image/v5/image"
"go.podman.io/image/v5/manifest"
"go.podman.io/image/v5/transports/alltransports"
"go.podman.io/image/v5/types"
)
@@ -27,7 +29,17 @@ type OSTree struct{}
// is pulled from an OCI registry. We simply pass the user
// input to bootc without any manipulation.
func (dist *OSTree) Apply(image string, _ ApplyOptions) error {
cli := []string{"bootc", "switch", image}
t, pathOrImageRef, err := parseApplyInput(image)
if err != nil {
return err
}
cli := []string{"bootc", "switch"}
// registry is the default transport and therefore not
// necessary
if t != "registry" {
cli = append(cli, "--transport", t)
}
cli = append(cli, pathOrImageRef)
cmd := exec.Command("sudo", cli...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
@@ -185,3 +197,79 @@ func printJSON(out UpgradeOutput) error {
fmt.Println(string(b))
return nil
}
// parseApplyInput takes well known OCI references and splits them up. this
// function should only be used to deal with bootc transports. podman takes
// the entire reference as an oci reference but bootc requires we use
// --transport if the default transport is not used. also, bootc's default
// transport is "registry" which mimics docker in the way it is handled.
func parseApplyInput(arg string) (string, string, error) {
var (
containersStorageTransport = "containers-storage"
dockerTransport = "docker"
ociArchiveTransport = "oci-archive"
ociTransport = "oci"
registryTransport = "registry"
)
// The order of this parsing matters. Be careful when you edit.
// containers-storage:/home/user:localhost/fedora-bootc:latest
afterCS, hasCS := strings.CutPrefix(arg, containersStorageTransport+":")
if hasCS {
return containersStorageTransport, afterCS, nil
}
imgRef, err := alltransports.ParseImageName(arg)
if err == nil {
transportName := imgRef.Transport().Name()
if imgRef.DockerReference() != nil {
// bootc docs do not show the docker transport, but the
// code apparently does handle it and because docker is
// a well-known transport, we do as well, but we change
// it to registry for convenience.
if transportName == dockerTransport {
transportName = registryTransport
}
// docker://quay.io/fedora/fedora-bootc:40
return transportName, imgRef.DockerReference().String(), nil
}
imagePath := imgRef.StringWithinTransport()
if transportName == ociTransport { //nolint:staticcheck
// oci:/tmp/oci-image
imagePath, _, _ = strings.Cut(imagePath, ":")
} else if transportName == ociArchiveTransport {
// oci-archive:/tmp/myimage.tar
imagePath = strings.TrimSuffix(imagePath, ":")
}
return transportName, imagePath, nil
}
// quay.io/fedora/fedora-bootc:40
if _, err := reference.ParseNamed(arg); err == nil {
return registryTransport, arg, nil
}
// registry://quay.io/fedora/fedora-bootc:40
afterReg, hasRegistry := strings.CutPrefix(arg, registryTransport+"://")
if hasRegistry {
return registryTransport, afterReg, nil
}
// oci-archive:/var/tmp/fedora-bootc.tar
// oci-archive:/mnt/usb/images/myapp.tar
afterOCIArchive, hasOCIArchive := strings.CutPrefix(arg, ociArchiveTransport+":")
if hasOCIArchive {
return ociArchiveTransport, afterOCIArchive, nil
}
// oci:/tmp/oci-image
// oci:/var/mnt/usb/bootc-image
afterOCI, hasOCI := strings.CutPrefix(arg, ociTransport+":")
if hasOCI {
return ociTransport, afterOCI, nil
}
return "", "", fmt.Errorf("unknown transport %q given", arg)
}

View File

@@ -89,3 +89,151 @@ func Test_compareMajorMinor(t *testing.T) {
})
}
}
func Test_parseApplyInput(t *testing.T) {
type args struct {
arg string
}
tests := []struct {
name string
args args
want string
want1 string
wantErr bool
}{
{
name: "bare registry reference with tag",
args: args{arg: "quay.io/fedora/fedora-bootc:40"},
want: "registry",
want1: "quay.io/fedora/fedora-bootc:40",
wantErr: false,
},
{
name: "docker transport with tag",
args: args{arg: "docker://quay.io/fedora/fedora-bootc:40"},
want: "registry",
want1: "quay.io/fedora/fedora-bootc:40",
wantErr: false,
},
{
name: "docker transport with digest",
args: args{arg: "docker://quay.io/podman/stable@sha256:9cca0703342e24806a9f64e08c053dca7f2cd90f10529af8ea872afb0a0c77d4"},
want: "registry",
want1: "quay.io/podman/stable@sha256:9cca0703342e24806a9f64e08c053dca7f2cd90f10529af8ea872afb0a0c77d4",
wantErr: false,
},
{
name: "registry transport with tag",
args: args{arg: "registry://quay.io/fedora/fedora-bootc:40"},
want: "registry",
want1: "quay.io/fedora/fedora-bootc:40",
wantErr: false,
},
{
name: "registry transport with digest",
args: args{arg: "registry://quay.io/podman/stable@sha256:9cca0703342e24806a9f64e08c053dca7f2cd90f10529af8ea872afb0a0c77d4"},
want: "registry",
want1: "quay.io/podman/stable@sha256:9cca0703342e24806a9f64e08c053dca7f2cd90f10529af8ea872afb0a0c77d4",
wantErr: false,
},
{
name: "registry transport with port",
args: args{arg: "registry://localhost:5000/myapp:latest"},
want: "registry",
want1: "localhost:5000/myapp:latest",
wantErr: false,
},
{
name: "oci transport var path",
args: args{arg: "oci:/var/mnt/usb/bootc-image"},
want: "oci",
want1: "/var/mnt/usb/bootc-image",
wantErr: false,
},
{
name: "oci transport opt path",
args: args{arg: "oci:/opt/images/mylayout"},
want: "oci",
want1: "/opt/images/mylayout",
wantErr: false,
},
{
name: "oci transport tmp path",
args: args{arg: "oci:/tmp/oci-image"},
want: "oci",
want1: "/tmp/oci-image",
wantErr: false,
},
{
name: "oci transport with reference",
args: args{arg: "oci:/path/to/oci-dir:myref"},
want: "oci",
want1: "/path/to/oci-dir:myref",
wantErr: false,
},
{
name: "oci-archive transport tmp tar",
args: args{arg: "oci-archive:/tmp/myimage.tar"},
want: "oci-archive",
want1: "/tmp/myimage.tar",
wantErr: false,
},
{
name: "oci-archive transport mnt path",
args: args{arg: "oci-archive:/mnt/usb/images/myapp.tar"},
want: "oci-archive",
want1: "/mnt/usb/images/myapp.tar",
wantErr: false,
},
{
name: "oci-archive transport with reference",
args: args{arg: "oci-archive:/home/user/archives/image.tar:myref"},
want: "oci-archive",
want1: "/home/user/archives/image.tar:myref",
wantErr: false,
},
{
name: "containers-storage transport with tag",
args: args{arg: "containers-storage:quay.io/podman/machine-os:6.0"},
want: "containers-storage",
want1: "quay.io/podman/machine-os:6.0",
wantErr: false,
},
{
name: "containers-storage transport with graph root",
args: args{arg: "containers-storage:[/home/core/.local/share/containers/storage]quay.io/podman/machine-os:6.0"},
want: "containers-storage",
want1: "[/home/core/.local/share/containers/storage]quay.io/podman/machine-os:6.0",
wantErr: false,
},
{
name: "good reference bad transport",
args: args{"foobar://quay.io/podman/machine-os:6.0"},
want: "",
want1: "",
wantErr: true,
},
{
name: "similar name but incorrect transport",
args: args{"oci-dir:/foo/bar/bar.tar"},
want: "",
want1: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, got1, err := parseApplyInput(tt.args.arg)
if (err != nil) != tt.wantErr {
t.Errorf("parseApplyInput() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("parseApplyInput() got = '%v', want %v", got, tt.want)
}
if got1 != tt.want1 {
t.Errorf("parseApplyInput() got1 = '%v', want %v", got1, tt.want1)
}
})
}
}

View File

@@ -196,8 +196,11 @@ function check_shell_completion() {
_check_completion_end NoSpace
else
_check_completion_end Default
# machine os apply is special and offers images and normal shell completion
if [[ "$cmd" != "apply" ]]; then
_check_no_suggestions
fi
fi
;;
*)