Merge pull request #11577 from rhatdan/tmpdir
Set default storage from containers.conf for temporary images
This commit is contained in:
		
						commit
						f9d8301c47
					
				|  | @ -174,7 +174,11 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { | |||
| 	} | ||||
| 	// Hard code TMPDIR functions to use /var/tmp, if user did not override
 | ||||
| 	if _, ok := os.LookupEnv("TMPDIR"); !ok { | ||||
| 		os.Setenv("TMPDIR", "/var/tmp") | ||||
| 		if tmpdir, err := cfg.ImageCopyTmpDir(); err != nil { | ||||
| 			logrus.Warnf("failed to retrieve default tmp dir: %s", err.Error()) | ||||
| 		} else { | ||||
| 			os.Setenv("TMPDIR", tmpdir) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	context := cmd.Root().LocalFlags().Lookup("context") | ||||
|  |  | |||
|  | @ -3,5 +3,6 @@ | |||
| x /tmp/podman-run-* | ||||
| x /tmp/containers-user-* | ||||
| x /tmp/run-*/libpod | ||||
| D! /var/lib/containers/storage/tmp 0700 root root | ||||
| D! /run/podman 0700 root root | ||||
| D! /var/lib/cni/networks | ||||
|  |  | |||
|  | @ -31,7 +31,7 @@ Run podman info with plain text response: | |||
| $ podman info | ||||
| host: | ||||
|   arch: amd64 | ||||
|   buildahVersion: 1.22.3 | ||||
|   buildahVersion: 1.23.0 | ||||
|   cgroupControllers: [] | ||||
|   cgroupManager: systemd | ||||
|   cgroupVersion: v2 | ||||
|  | @ -64,7 +64,7 @@ host: | |||
|   kernel: 5.13.13-200.fc34.x86_64 | ||||
|   linkmode: dynamic | ||||
|   logDriver: journald | ||||
|   memFree: 1351262208 | ||||
|   memFree: 1833385984 | ||||
|   memTotal: 16401895424 | ||||
|   ociRuntime: | ||||
|     name: crun | ||||
|  | @ -95,9 +95,9 @@ host: | |||
|       libslirp: 4.4.0 | ||||
|       SLIRP_CONFIG_VERSION_MAX: 3 | ||||
|       libseccomp: 2.5.0 | ||||
|   swapFree: 16818888704 | ||||
|   swapFree: 15687475200 | ||||
|   swapTotal: 16886259712 | ||||
|   uptime: 33h 57m 32.85s (Approximately 1.38 days) | ||||
|   uptime: 47h 15m 9.91s (Approximately 1.96 days) | ||||
| plugins: | ||||
|   log: | ||||
|   - k8s-file | ||||
|  | @ -109,24 +109,18 @@ plugins: | |||
|   volume: | ||||
|   - local | ||||
| registries: | ||||
|   localhost:5000: | ||||
|     Blocked: false | ||||
|     Insecure: true | ||||
|     Location: localhost:5000 | ||||
|     MirrorByDigestOnly: false | ||||
|     Mirrors: null | ||||
|     Prefix: localhost:5000 | ||||
|   search: | ||||
|   - registry.fedoraproject.org | ||||
|   - registry.access.redhat.com | ||||
|   - docker.io | ||||
|   - quay.io | ||||
| store: | ||||
|   configFile: /home/dwalsh/.config/containers/storage.conf | ||||
|   containerStore: | ||||
|     number: 2 | ||||
|     number: 9 | ||||
|     paused: 0 | ||||
|     running: 1 | ||||
|     stopped: 1 | ||||
|     stopped: 8 | ||||
|   graphDriverName: overlay | ||||
|   graphOptions: {} | ||||
|   graphRoot: /home/dwalsh/.local/share/containers/storage | ||||
|  | @ -135,26 +129,27 @@ store: | |||
|     Native Overlay Diff: "true" | ||||
|     Supports d_type: "true" | ||||
|     Using metacopy: "false" | ||||
|   imageCopyTmpDir: /home/dwalsh/.local/share/containers/storage/tmp | ||||
|   imageStore: | ||||
|     number: 37 | ||||
|     number: 5 | ||||
|   runRoot: /run/user/3267/containers | ||||
|   volumePath: /home/dwalsh/.local/share/containers/storage/volumes | ||||
| version: | ||||
|   APIVersion: 3.3.1 | ||||
|   Built: 1631137208 | ||||
|   BuiltTime: Wed Sep  8 17:40:08 2021 | ||||
|   GitCommit: ab272d1e9bf4daac224fb230e0c9b5c56c4cab4d-dirty | ||||
|   APIVersion: 4.0.0 | ||||
|   Built: 1631648722 | ||||
|   BuiltTime: Tue Sep 14 15:45:22 2021 | ||||
|   GitCommit: 23677f92dd83e96d2bc8f0acb611865fb8b1a56d | ||||
|   GoVersion: go1.16.6 | ||||
|   OsArch: linux/amd64 | ||||
|   Version: 3.3.1 | ||||
|   Version: 4.0.0 | ||||
| ``` | ||||
| Run podman info with JSON formatted response: | ||||
| ``` | ||||
| $ ./bin/podman info --format json | ||||
| $ podman info --format json | ||||
| { | ||||
|   "host": { | ||||
|     "arch": "amd64", | ||||
|     "buildahVersion": "1.22.3", | ||||
|     "buildahVersion": "1.23.0", | ||||
|     "cgroupManager": "systemd", | ||||
|     "cgroupVersion": "v2", | ||||
|     "cgroupControllers": [], | ||||
|  | @ -198,7 +193,7 @@ $ ./bin/podman info --format json | |||
|     }, | ||||
|     "kernel": "5.13.13-200.fc34.x86_64", | ||||
|     "logDriver": "journald", | ||||
|     "memFree": 1274040320, | ||||
|     "memFree": 1785753600, | ||||
|     "memTotal": 16401895424, | ||||
|     "ociRuntime": { | ||||
|       "name": "crun", | ||||
|  | @ -224,21 +219,22 @@ $ ./bin/podman info --format json | |||
|       "package": "slirp4netns-1.1.12-2.fc34.x86_64", | ||||
|       "version": "slirp4netns version 1.1.12\ncommit: 7a104a101aa3278a2152351a082a6df71f57c9a3\nlibslirp: 4.4.0\nSLIRP_CONFIG_VERSION_MAX: 3\nlibseccomp: 2.5.0" | ||||
|     }, | ||||
|     "swapFree": 16818888704, | ||||
|     "swapFree": 15687475200, | ||||
|     "swapTotal": 16886259712, | ||||
|     "uptime": "33h 59m 25.69s (Approximately 1.38 days)", | ||||
|     "uptime": "47h 17m 29.75s (Approximately 1.96 days)", | ||||
|     "linkmode": "dynamic" | ||||
|   }, | ||||
|   "store": { | ||||
|     "configFile": "/home/dwalsh/.config/containers/storage.conf", | ||||
|     "containerStore": { | ||||
|       "number": 2, | ||||
|       "number": 9, | ||||
|       "paused": 0, | ||||
|       "running": 1, | ||||
|       "stopped": 1 | ||||
|       "stopped": 8 | ||||
|     }, | ||||
|     "graphDriverName": "overlay", | ||||
|     "graphOptions": { | ||||
| 
 | ||||
|     }, | ||||
|     "graphRoot": "/home/dwalsh/.local/share/containers/storage", | ||||
|     "graphStatus": { | ||||
|  | @ -247,25 +243,19 @@ $ ./bin/podman info --format json | |||
|       "Supports d_type": "true", | ||||
|       "Using metacopy": "false" | ||||
|     }, | ||||
|     "imageCopyTmpDir": "/home/dwalsh/.local/share/containers/storage/tmp", | ||||
|     "imageStore": { | ||||
|       "number": 37 | ||||
|       "number": 5 | ||||
|     }, | ||||
|     "runRoot": "/run/user/3267/containers", | ||||
|     "volumePath": "/home/dwalsh/.local/share/containers/storage/volumes" | ||||
|   }, | ||||
|   "registries": { | ||||
|     "localhost:5000": { | ||||
|   "Prefix": "localhost:5000", | ||||
|   "Location": "localhost:5000", | ||||
|   "Insecure": true, | ||||
|   "Mirrors": null, | ||||
|   "Blocked": false, | ||||
|   "MirrorByDigestOnly": false | ||||
| }, | ||||
|     "search": [ | ||||
|   "registry.fedoraproject.org", | ||||
|   "registry.access.redhat.com", | ||||
|   "docker.io" | ||||
|   "docker.io", | ||||
|   "quay.io" | ||||
| ] | ||||
|   }, | ||||
|   "plugins": { | ||||
|  | @ -283,12 +273,12 @@ $ ./bin/podman info --format json | |||
|     ] | ||||
|   }, | ||||
|   "version": { | ||||
|     "APIVersion": "3.3.1", | ||||
|     "Version": "3.3.1", | ||||
|     "APIVersion": "4.0.0", | ||||
|     "Version": "4.0.0", | ||||
|     "GoVersion": "go1.16.6", | ||||
|     "GitCommit": "", | ||||
|     "BuiltTime": "Mon Aug 30 16:46:36 2021", | ||||
|     "Built": 1630356396, | ||||
|     "GitCommit": "23677f92dd83e96d2bc8f0acb611865fb8b1a56d", | ||||
|     "BuiltTime": "Tue Sep 14 15:45:22 2021", | ||||
|     "Built": 1631648722, | ||||
|     "OsArch": "linux/amd64" | ||||
|   } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										2
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										2
									
								
								go.mod
								
								
								
								
							|  | @ -12,7 +12,7 @@ require ( | |||
| 	github.com/containernetworking/cni v0.8.1 | ||||
| 	github.com/containernetworking/plugins v0.9.1 | ||||
| 	github.com/containers/buildah v1.23.0 | ||||
| 	github.com/containers/common v0.44.0 | ||||
| 	github.com/containers/common v0.44.1-0.20210914173811-fcaa2e0de285 | ||||
| 	github.com/containers/conmon v2.0.20+incompatible | ||||
| 	github.com/containers/image/v5 v5.16.0 | ||||
| 	github.com/containers/ocicrypt v1.1.2 | ||||
|  |  | |||
							
								
								
									
										3
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										3
									
								
								go.sum
								
								
								
								
							|  | @ -246,8 +246,9 @@ github.com/containernetworking/plugins v0.9.1 h1:FD1tADPls2EEi3flPc2OegIY1M9pUa9 | |||
| github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= | ||||
| github.com/containers/buildah v1.23.0 h1:qGIeSNOczUHzvnaaOS29HSMiYAjw6JgIXYksAyvqnLs= | ||||
| github.com/containers/buildah v1.23.0/go.mod h1:K0iMKgy/MffkkgELBXhSXwTy2HTT6hM0X8qruDR1FwU= | ||||
| github.com/containers/common v0.44.0 h1:YpjfOxmWrnVyxugYgiWV1Vo/Xg8JUfe32QZz3SAMfUk= | ||||
| github.com/containers/common v0.44.0/go.mod h1:7sdP4vmI5Bm6FPFxb3lvAh1Iktb6tiO1MzjUzhxdoGo= | ||||
| github.com/containers/common v0.44.1-0.20210914173811-fcaa2e0de285 h1:sXBzh8CcqR5cGGY9cM/AUIk58CJKHbyljVtFh8HYyLY= | ||||
| github.com/containers/common v0.44.1-0.20210914173811-fcaa2e0de285/go.mod h1:7sdP4vmI5Bm6FPFxb3lvAh1Iktb6tiO1MzjUzhxdoGo= | ||||
| github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg= | ||||
| github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= | ||||
| github.com/containers/image/v5 v5.16.0 h1:WQcNSzb7+ngS2cfynx0vUwhk+scpgiKlldVcsF8GPbI= | ||||
|  |  | |||
|  | @ -108,6 +108,7 @@ type StoreInfo struct { | |||
| 	GraphOptions    map[string]interface{} `json:"graphOptions"` | ||||
| 	GraphRoot       string                 `json:"graphRoot"` | ||||
| 	GraphStatus     map[string]string      `json:"graphStatus"` | ||||
| 	ImageCopyTmpDir string                 `json:"imageCopyTmpDir"` | ||||
| 	ImageStore      ImageStore             `json:"imageStore"` | ||||
| 	RunRoot         string                 `json:"runRoot"` | ||||
| 	VolumePath      string                 `json:"volumePath"` | ||||
|  |  | |||
|  | @ -288,6 +288,7 @@ func (r *Runtime) storeInfo() (*define.StoreInfo, error) { | |||
| 
 | ||||
| 	info := define.StoreInfo{ | ||||
| 		ImageStore:      imageInfo, | ||||
| 		ImageCopyTmpDir: os.Getenv("TMPDIR"), | ||||
| 		ContainerStore:  conInfo, | ||||
| 		GraphRoot:       r.store.GraphRoot(), | ||||
| 		RunRoot:         r.store.RunRoot(), | ||||
|  |  | |||
|  | @ -397,4 +397,51 @@ var _ = Describe("Podman run", func() { | |||
| 		Expect(session).Should(Exit(0)) | ||||
| 		Expect(session.OutputToString()).To(Equal(profile)) | ||||
| 	}) | ||||
| 
 | ||||
| 	It("podman info image_copy_tmp_dir", func() { | ||||
| 		session := podmanTest.Podman([]string{"info", "--format", "{{.Store.ImageCopyTmpDir}}"}) | ||||
| 		session.WaitWithDefaultTimeout() | ||||
| 		Expect(session).Should(Exit(0)) | ||||
| 		Expect(session.OutputToString()).To(Equal("/var/tmp")) | ||||
| 
 | ||||
| 		configPath := filepath.Join(podmanTest.TempDir, "containers.conf") | ||||
| 		os.Setenv("CONTAINERS_CONF", configPath) | ||||
| 
 | ||||
| 		containersConf := []byte(fmt.Sprintf("[engine]\nimage_copy_tmp_dir=\"/foobar\"")) | ||||
| 		err = ioutil.WriteFile(configPath, containersConf, os.ModePerm) | ||||
| 		Expect(err).To(BeNil()) | ||||
| 
 | ||||
| 		if IsRemote() { | ||||
| 			podmanTest.RestartRemoteService() | ||||
| 		} | ||||
| 
 | ||||
| 		session = podmanTest.Podman([]string{"info", "--format", "{{.Store.ImageCopyTmpDir}}"}) | ||||
| 		session.WaitWithDefaultTimeout() | ||||
| 		Expect(session).Should(Exit(0)) | ||||
| 		Expect(session.OutputToString()).To(Equal("/foobar")) | ||||
| 
 | ||||
| 		containersConf = []byte(fmt.Sprintf("[engine]\nimage_copy_tmp_dir=\"storage\"")) | ||||
| 		err = ioutil.WriteFile(configPath, containersConf, os.ModePerm) | ||||
| 		Expect(err).To(BeNil()) | ||||
| 		if IsRemote() { | ||||
| 			podmanTest.RestartRemoteService() | ||||
| 		} | ||||
| 
 | ||||
| 		session = podmanTest.Podman([]string{"info", "--format", "{{.Store.ImageCopyTmpDir}}"}) | ||||
| 		session.WaitWithDefaultTimeout() | ||||
| 		Expect(session).Should(Exit(0)) | ||||
| 		Expect(session.LineInOutputContains("containers/storage/tmp")).To(BeTrue()) | ||||
| 
 | ||||
| 		containersConf = []byte(fmt.Sprintf("[engine]\nimage_copy_tmp_dir=\"storage1\"")) | ||||
| 		err = ioutil.WriteFile(configPath, containersConf, os.ModePerm) | ||||
| 		Expect(err).To(BeNil()) | ||||
| 		if IsRemote() { | ||||
| 			podmanTest.RestartRemoteService() | ||||
| 		} | ||||
| 
 | ||||
| 		session = podmanTest.Podman([]string{"info", "--format", "{{.Store.ImageCopyTmpDir}}"}) | ||||
| 		session.WaitWithDefaultTimeout() | ||||
| 		Expect(session).Should(Exit(0)) | ||||
| 		Expect(session.ErrorToString()).To(ContainSubstring("invalid image_copy_tmp_dir")) | ||||
| 	}) | ||||
| }) | ||||
|  |  | |||
|  | @ -234,6 +234,9 @@ type EngineConfig struct { | |||
| 	// EventsLogger determines where events should be logged.
 | ||||
| 	EventsLogger string `toml:"events_logger,omitempty"` | ||||
| 
 | ||||
| 	// graphRoot internal stores the location of the graphroot
 | ||||
| 	graphRoot string | ||||
| 
 | ||||
| 	// HelperBinariesDir is a list of directories which are used to search for
 | ||||
| 	// helper binaries.
 | ||||
| 	HelperBinariesDir []string `toml:"helper_binaries_dir"` | ||||
|  | @ -384,6 +387,12 @@ type EngineConfig struct { | |||
| 	// before sending kill signal.
 | ||||
| 	StopTimeout uint `toml:"stop_timeout,omitempty"` | ||||
| 
 | ||||
| 	// ImageCopyTmpDir is the default location for storing temporary
 | ||||
| 	// container image content,  Can be overridden with the TMPDIR
 | ||||
| 	// environment variable.  If you specify "storage", then the
 | ||||
| 	// location of the container/storage tmp directory will be used.
 | ||||
| 	ImageCopyTmpDir string `toml:"image_copy_tmp_dir,omitempty"` | ||||
| 
 | ||||
| 	// TmpDir is the path to a temporary directory to store per-boot container
 | ||||
| 	// files. Must be stored in a tmpfs.
 | ||||
| 	TmpDir string `toml:"tmp_dir,omitempty"` | ||||
|  | @ -1148,3 +1157,22 @@ func (c *Config) FindHelperBinary(name string, searchPATH bool) (string, error) | |||
| 	} | ||||
| 	return "", errors.Errorf("could not find %q in one of %v", name, c.Engine.HelperBinariesDir) | ||||
| } | ||||
| 
 | ||||
| // ImageCopyTmpDir default directory to store tempory image files during copy
 | ||||
| func (c *Config) ImageCopyTmpDir() (string, error) { | ||||
| 	if path, found := os.LookupEnv("TMPDIR"); found { | ||||
| 		return path, nil | ||||
| 	} | ||||
| 	switch c.Engine.ImageCopyTmpDir { | ||||
| 	case "": | ||||
| 		return "", nil | ||||
| 	case "storage": | ||||
| 		return filepath.Join(c.Engine.graphRoot, "tmp"), nil | ||||
| 	default: | ||||
| 		if filepath.IsAbs(c.Engine.ImageCopyTmpDir) { | ||||
| 			return c.Engine.ImageCopyTmpDir, nil | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return "", errors.Errorf("invalid image_copy_tmp_dir value %q (relative paths are not accepted)", c.Engine.ImageCopyTmpDir) | ||||
| } | ||||
|  |  | |||
|  | @ -451,15 +451,20 @@ default_sysctls = [ | |||
| # List of the OCI runtimes that support --format=json.  When json is supported | ||||
| # engine will use it for reporting nicer errors. | ||||
| # | ||||
| #runtime_supports_json = ["crun", "runc", "kata", "runsc"] | ||||
| #runtime_supports_json = ["crun", "runc", "kata", "runsc", "krun"] | ||||
| 
 | ||||
| # List of the OCI runtimes that supports running containers with KVM Separation. | ||||
| # | ||||
| #runtime_supports_kvm = ["kata"] | ||||
| #runtime_supports_kvm = ["kata", "krun"] | ||||
| 
 | ||||
| # List of the OCI runtimes that supports running containers without cgroups. | ||||
| # | ||||
| #runtime_supports_nocgroups = ["crun"] | ||||
| #runtime_supports_nocgroups = ["crun", "krun"] | ||||
| 
 | ||||
| # Default location for storing temporary container image content.  Can be overridden with the TMPDIR environment | ||||
| # variable.  If you specify "storage", then the location of the | ||||
| # container/storage tmp directory will be used. | ||||
| # image_copy_tmp_dir="/var/tmp" | ||||
| 
 | ||||
| # Directory for persistent engine files (database, etc) | ||||
| # By default, this will be configured relative to where the containers/storage | ||||
|  | @ -498,7 +503,7 @@ default_sysctls = [ | |||
| # | ||||
| #volume_path = "/var/lib/containers/storage/volumes" | ||||
| 
 | ||||
| # Paths to look for a valid OCI runtime (crun, runc, kata, runsc, etc) | ||||
| # Paths to look for a valid OCI runtime (crun, runc, kata, runsc, krun, etc) | ||||
| [engine.runtimes] | ||||
| #crun = [ | ||||
| #  "/usr/bin/crun", | ||||
|  | @ -541,6 +546,11 @@ default_sysctls = [ | |||
| #  "/run/current-system/sw/bin/runsc", | ||||
| #] | ||||
| 
 | ||||
| #krun = [ | ||||
| #  "/usr/bin/krun", | ||||
| #  "/usr/local/bin/krun", | ||||
| #] | ||||
| 
 | ||||
| [engine.volume_plugins] | ||||
| #testplugin = "/run/podman/plugins/test.sock" | ||||
| 
 | ||||
|  |  | |||
|  | @ -244,6 +244,8 @@ func defaultConfigFromMemory() (*EngineConfig, error) { | |||
| 		logrus.Warnf("Storage configuration is unset - using hardcoded default graph root %q", _defaultGraphRoot) | ||||
| 		storeOpts.GraphRoot = _defaultGraphRoot | ||||
| 	} | ||||
| 	c.graphRoot = storeOpts.GraphRoot | ||||
| 	c.ImageCopyTmpDir = "/var/tmp" | ||||
| 	c.StaticDir = filepath.Join(storeOpts.GraphRoot, "libpod") | ||||
| 	c.VolumePath = filepath.Join(storeOpts.GraphRoot, "volumes") | ||||
| 
 | ||||
|  | @ -297,6 +299,10 @@ func defaultConfigFromMemory() (*EngineConfig, error) { | |||
| 			"/sbin/runsc", | ||||
| 			"/run/current-system/sw/bin/runsc", | ||||
| 		}, | ||||
| 		"krun": { | ||||
| 			"/usr/bin/krun", | ||||
| 			"/usr/local/bin/krun", | ||||
| 		}, | ||||
| 	} | ||||
| 	// Needs to be called after populating c.OCIRuntimes
 | ||||
| 	c.OCIRuntime = c.findRuntime() | ||||
|  | @ -320,9 +326,10 @@ func defaultConfigFromMemory() (*EngineConfig, error) { | |||
| 		"runc", | ||||
| 		"kata", | ||||
| 		"runsc", | ||||
| 		"krun", | ||||
| 	} | ||||
| 	c.RuntimeSupportsNoCgroups = []string{"crun"} | ||||
| 	c.RuntimeSupportsKVM = []string{"kata", "kata-runtime", "kata-qemu", "kata-fc"} | ||||
| 	c.RuntimeSupportsNoCgroups = []string{"crun", "krun"} | ||||
| 	c.RuntimeSupportsKVM = []string{"kata", "kata-runtime", "kata-qemu", "kata-fc", "krun"} | ||||
| 	c.InitPath = DefaultInitPath | ||||
| 	c.NoPivotRoot = false | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| package version | ||||
| 
 | ||||
| // Version is the version of the build.
 | ||||
| const Version = "0.44.0" | ||||
| const Version = "0.44.1-dev" | ||||
|  |  | |||
|  | @ -99,7 +99,7 @@ github.com/containers/buildah/pkg/rusage | |||
| github.com/containers/buildah/pkg/sshagent | ||||
| github.com/containers/buildah/pkg/util | ||||
| github.com/containers/buildah/util | ||||
| # github.com/containers/common v0.44.0 | ||||
| # github.com/containers/common v0.44.1-0.20210914173811-fcaa2e0de285 | ||||
| github.com/containers/common/libimage | ||||
| github.com/containers/common/libimage/manifests | ||||
| github.com/containers/common/pkg/apparmor | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue