Skip to content

Commit

Permalink
Merge pull request #18224 from flouthoc/split-store
Browse files Browse the repository at this point in the history
podman: add support for splitting imagestore using `--imagestore`
  • Loading branch information
openshift-merge-robot authored Jun 17, 2023
2 parents 6d311f7 + e5399aa commit 030213c
Show file tree
Hide file tree
Showing 16 changed files with 273 additions and 62 deletions.
4 changes: 4 additions & 0 deletions cmd/podman/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,10 @@ func rootFlags(cmd *cobra.Command, podmanConfig *entities.PodmanConfig) {
pFlags.StringVar(&podmanConfig.Runroot, runrootFlagName, "", "Path to the 'run directory' where all state information is stored")
_ = cmd.RegisterFlagCompletionFunc(runrootFlagName, completion.AutocompleteDefault)

imageStoreFlagName := "imagestore"
pFlags.StringVar(&podmanConfig.ImageStore, imageStoreFlagName, "", "Path to the `image store`, different from `graph root`, use this to split storing the image into a separate `image store`, see `man containers-storage.conf` for details")
_ = cmd.RegisterFlagCompletionFunc(imageStoreFlagName, completion.AutocompleteDefault)

pFlags.BoolVar(&podmanConfig.TransientStore, "transient-store", false, "Enable transient container storage")

runtimeFlagName := "runtime"
Expand Down
6 changes: 6 additions & 0 deletions docs/source/markdown/podman.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ Identity value resolution precedence:
- `containers.conf`
Remote connections use local containers.conf for default.

#### **--imagestore**=*path*

Path of the imagestore where images are stored. By default, the storage library stores all the images in the graphroot but if an imagestore is provided, then the storage library will store newly pulled images in the provided imagestore and keep using the graphroot for everything else. If the user is using the overlay driver, then the images which were already part of the graphroot will still be accessible.

This will override *imagestore* option in `containers-storage.conf(5)`, refer to `containers-storage.conf(5)` for more details.

#### **--log-level**=*level*

Log messages at and above specified level: debug, info, warn, error, fatal or panic (default: "warn")
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ require (
github.com/containers/libhvee v0.0.5
github.com/containers/ocicrypt v1.1.7
github.com/containers/psgo v1.8.0
github.com/containers/storage v1.46.2-0.20230613134951-e424b6649be3
github.com/containers/storage v1.46.2-0.20230616083707-cc0d208e5e1c
github.com/coreos/go-systemd/v22 v22.5.0
github.com/coreos/stream-metadata-go v0.4.2
github.com/crc-org/vfkit v0.0.5-0.20230602131541-3d57f09010c9
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,8 @@ github.com/containers/ocicrypt v1.1.7/go.mod h1:7CAhjcj2H8AYp5YvEie7oVSK2AhBY8Ns
github.com/containers/psgo v1.8.0 h1:2loGekmGAxM9ir5OsXWEfGwFxorMPYnc6gEDsGFQvhY=
github.com/containers/psgo v1.8.0/go.mod h1:T8ZxnX3Ur4RvnhxFJ7t8xJ1F48RhiZB4rSrOaR/qGHc=
github.com/containers/storage v1.43.0/go.mod h1:uZ147thiIFGdVTjMmIw19knttQnUCl3y9zjreHrg11s=
github.com/containers/storage v1.46.2-0.20230613134951-e424b6649be3 h1:nSCnnrCMocJDsNUU4EDPT8GkW7ToU43/QbXGRC+ciEs=
github.com/containers/storage v1.46.2-0.20230613134951-e424b6649be3/go.mod h1:pRp3lkRo2qodb/ltpnudoXggrviRmaCmU5a5GhTBae0=
github.com/containers/storage v1.46.2-0.20230616083707-cc0d208e5e1c h1:hJP+UF9OzDaThxavD5isFbAFxbvb25TdFtjohAhH/dc=
github.com/containers/storage v1.46.2-0.20230616083707-cc0d208e5e1c/go.mod h1:pRp3lkRo2qodb/ltpnudoXggrviRmaCmU5a5GhTBae0=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
Expand Down
12 changes: 12 additions & 0 deletions libpod/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,18 @@ func WithTransientStore(transientStore bool) RuntimeOption {
}
}

func WithImageStore(imageStore string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}

rt.storageConfig.ImageStore = imageStore

return nil
}
}

// WithSignaturePolicy specifies the path of a file which decides how trust is
// managed for images we've pulled.
// If this is not specified, the system default configuration will be used
Expand Down
1 change: 1 addition & 0 deletions pkg/domain/entities/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type PodmanConfig struct {
URI string // URI to RESTful API Service

Runroot string
ImageStore string
StorageDriver string
StorageOpts []string
SSHMode string
Expand Down
4 changes: 4 additions & 0 deletions pkg/domain/infra/runtime_libpod.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo
storageSet = true
storageOpts.RunRoot = cfg.Runroot
}
if fs.Changed("imagestore") {
storageOpts.ImageStore = cfg.ImageStore
options = append(options, libpod.WithImageStore(cfg.ImageStore))
}
if len(storageOpts.RunRoot) > 50 {
return nil, errors.New("the specified runroot is longer than 50 characters")
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/specgenutil/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,9 @@ func CreateExitCommandArgs(storageConfig storageTypes.StoreOptions, config *conf
"--db-backend", config.Engine.DBBackend,
fmt.Sprintf("--transient-store=%t", storageConfig.TransientStore),
}
if storageConfig.ImageStore != "" {
command = append(command, []string{"--imagestore", storageConfig.ImageStore}...)
}
if config.Engine.OCIRuntime != "" {
command = append(command, []string{"--runtime", config.Engine.OCIRuntime}...)
}
Expand Down
67 changes: 67 additions & 0 deletions test/e2e/pull_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,73 @@ var _ = Describe("Podman pull", func() {
Expect(session).Should(Exit(0))
})

It("podman pull and run on split imagestore", func() {
SkipIfRemote("podman-remote does not support setting external imagestore")
imgName := "splitstoretest"

// Make alpine write-able
session := podmanTest.Podman([]string{"build", "--pull=never", "--tag", imgName, "build/basicalpine"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))

tmpDir := filepath.Join(podmanTest.TempDir, "splitstore")
outfile := filepath.Join(podmanTest.TempDir, "image.tar")

save := podmanTest.Podman([]string{"save", "-o", outfile, "--format", "oci-archive", imgName})
save.WaitWithDefaultTimeout()
Expect(save).Should(Exit(0))

rmi := podmanTest.Podman([]string{"rmi", imgName})
rmi.WaitWithDefaultTimeout()
Expect(rmi).Should(Exit(0))

// load to splitstore
result := podmanTest.Podman([]string{"load", "--imagestore", tmpDir, "-q", "-i", outfile})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))

// tag busybox to busybox-test in graphroot since we can delete readonly busybox
session = podmanTest.Podman([]string{"tag", "quay.io/libpod/busybox:latest", "busybox-test"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))

session = podmanTest.Podman([]string{"images", "--imagestore", tmpDir})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
Expect(session.OutputToString()).To(ContainSubstring(imgName))
Expect(session.OutputToString()).To(ContainSubstring("busybox-test"))

// Test deleting image in graphroot even when `--imagestore` is set
session = podmanTest.Podman([]string{"rmi", "--imagestore", tmpDir, "busybox-test"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))

// Images without --imagestore should not contain alpine
session = podmanTest.Podman([]string{"images"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
Expect(session.OutputToString()).To(Not(ContainSubstring(imgName)))

// Set `imagestore` in `storage.conf` and container should run.
configPath := filepath.Join(podmanTest.TempDir, ".config", "containers", "storage.conf")
os.Setenv("CONTAINERS_STORAGE_CONF", configPath)
defer func() {
os.Unsetenv("CONTAINERS_STORAGE_CONF")
}()

err = os.MkdirAll(filepath.Dir(configPath), os.ModePerm)
Expect(err).ToNot(HaveOccurred())
storageConf := []byte(fmt.Sprintf("[storage]\nimagestore=\"%s\"", tmpDir))
err = os.WriteFile(configPath, storageConf, os.ModePerm)
Expect(err).ToNot(HaveOccurred())

session = podmanTest.Podman([]string{"run", "--name", "test", "--rm",
imgName, "echo", "helloworld"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
Expect(session.OutputToString()).To(ContainSubstring("helloworld"))
})

It("podman pull by digest", func() {
session := podmanTest.Podman([]string{"pull", "quay.io/libpod/testdigest_v2s2@sha256:755f4d90b3716e2bf57060d249e2cd61c9ac089b1233465c5c2cb2d7ee550fdb"})
session.WaitWithDefaultTimeout()
Expand Down
4 changes: 2 additions & 2 deletions vendor/github.com/containers/storage/.cirrus.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 030213c

Please sign in to comment.