diff options
33 files changed, 309 insertions, 85 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 70f65436e..461a39e4b 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -489,7 +489,7 @@ special_testing_cgroupv2_task: TEST_REMOTE_CLIENT: true TEST_REMOTE_CLIENT: false - timeout_in: 20m + timeout_in: 120m networking_script: '${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/networking.sh' setup_environment_script: '$SCRIPT_BASE/setup_environment.sh |& ${TIMESTAMP}' @@ -165,8 +165,13 @@ podman-remote: .gopathok $(PODMAN_VARLINK_DEPENDENCIES) ## Build with podman on $(GO_BUILD) $(BUILDFLAGS) -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "$(BUILDTAGS) remoteclient" -o bin/$@ $(PROJECT)/cmd/podman .PHONY: podman.msi -podman.msi: podman-remote-windows ## Will always rebuild exe as there is no podman-remote-windows.exe target to verify timestamp - wixl -D VERSION=$(RELEASE_NUMBER) -o bin/podman-v$(RELEASE_NUMBER).msi contrib/msi/podman.wxs +podman.msi: podman-remote podman-remote-windows docs ## Will always rebuild exe as there is no podman-remote-windows.exe target to verify timestamp + rm -rf bin/windows + mkdir -p bin/windows + docs/podman-remote.sh windows bin/windows docs + find bin/windows -print \ + |wixl-heat --var var.ManSourceDir --component-group ManFiles --directory-ref INSTALLDIR --prefix bin/windows/ >bin/windows/pages.wsx + wixl -D VERSION=$(RELEASE_NUMBER) -D ManSourceDir=bin/windows -o podman-v$(RELEASE_NUMBER).msi contrib/msi/podman.wxs bin/windows/pages.wsx podman-remote-%: .gopathok $(PODMAN_VARLINK_DEPENDENCIES) ## Build podman for a specific GOOS $(eval BINSFX := $(shell test "$*" != "windows" || echo ".exe")) @@ -316,8 +321,9 @@ $(MANPAGES): %: %.md .gopathok docs: $(MANPAGES) ## Generate documentation -install-podman-remote-docs: docs - @(cd docs; ./podman-remote.sh ./remote) +install-podman-remote-docs: podman-remote docs + rm -rf docs/remote + docs/podman-remote.sh darwin docs/remote docs man-page-check: ./hack/man-page-checker @@ -102,10 +102,10 @@ includes tables showing Docker commands and their Podman equivalent commands. **[Tutorials](docs/tutorials)** Tutorials on using Podman. -**[Remote Client](remote_client.md)** +**[Remote Client](https://github.com/containers/libpod/blob/master/docs/tutorials/remote_client.md)** A brief how-to on using the Podman remote-client. -**[Basic Setup and Use of Podman in a Rootless environment](https://github.com/containers/libpod/blob/master/docs/tutorials/rootless_tutorial.md) +**[Basic Setup and Use of Podman in a Rootless environment](https://github.com/containers/libpod/blob/master/docs/tutorials/rootless_tutorial.md)** A tutorial showing the setup and configuration necessary to run Rootless Podman. **[Release Notes](RELEASE_NOTES.md)** diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index e0ce202cc..b8796f9b3 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -41,6 +41,7 @@ type MainFlags struct { VarlinkAddress string ConnectionName string RemoteConfigFilePath string + Port int } type AttachValues struct { diff --git a/cmd/podman/main.go b/cmd/podman/main.go index b83ccd9a5..992dbe1d5 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -111,6 +111,11 @@ func before(cmd *cobra.Command, args []string) error { return err } + // check that global opts input is valid + if err := checkInput(); err != nil { + return err + } + // Set log level; if not log-level is provided, default to error logLevel := MainGlobalOpts.LogLevel if logLevel == "" { diff --git a/cmd/podman/main_local.go b/cmd/podman/main_local.go index 08d7ccaf4..917096e17 100644 --- a/cmd/podman/main_local.go +++ b/cmd/podman/main_local.go @@ -267,3 +267,8 @@ func setUMask() { // Be sure we can create directories with 0755 mode. syscall.Umask(0022) } + +// checkInput can be used to verify any of the globalopt values +func checkInput() error { + return nil +} diff --git a/cmd/podman/main_remote.go b/cmd/podman/main_remote.go index a005e925c..f617422e6 100644 --- a/cmd/podman/main_remote.go +++ b/cmd/podman/main_remote.go @@ -3,6 +3,7 @@ package main import ( + "github.com/pkg/errors" "os/user" "github.com/spf13/cobra" @@ -18,6 +19,7 @@ func init() { rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.ConnectionName, "connection", "", "remote connection name") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.RemoteConfigFilePath, "remote-config-path", "", "alternate path for configuration file") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.RemoteUserName, "username", username, "username on the remote host") + rootCmd.PersistentFlags().IntVar(&MainGlobalOpts.Port, "port", 22, "port on remote host") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.RemoteHost, "remote-host", "", "remote host") // TODO maybe we allow the altering of this for bridge connections? // rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.VarlinkAddress, "varlink-address", adapter.DefaultAddress, "address of the varlink socket") @@ -42,3 +44,11 @@ func setRLimits() error { } func setUMask() {} + +// checkInput can be used to verify any of the globalopt values +func checkInput() error { + if MainGlobalOpts.Port < 0 || MainGlobalOpts.Port > 65536 { + return errors.Errorf("remote port must be between 0 and 65536") + } + return nil +} diff --git a/cmd/podman/remoteclientconfig/config.go b/cmd/podman/remoteclientconfig/config.go index 01f293ec3..13880a868 100644 --- a/cmd/podman/remoteclientconfig/config.go +++ b/cmd/podman/remoteclientconfig/config.go @@ -12,6 +12,7 @@ type RemoteConnection struct { Destination string `toml:"destination"` Username string `toml:"username"` IsDefault bool `toml:"default"` + Port int `toml:"port"` } // GetConfigFilePath is a simple helper to export the configuration file's diff --git a/cmd/podman/remoteclientconfig/configfile_test.go b/cmd/podman/remoteclientconfig/configfile_test.go index 66e0a4693..ea2224ea7 100644 --- a/cmd/podman/remoteclientconfig/configfile_test.go +++ b/cmd/podman/remoteclientconfig/configfile_test.go @@ -13,11 +13,13 @@ var goodConfig = ` [connections.homer] destination = "192.168.1.1" username = "myuser" +port = 22 default = true [connections.bart] destination = "foobar.com" username = "root" +port = 22 ` var noDest = ` [connections] @@ -26,9 +28,11 @@ var noDest = ` destination = "192.168.1.1" username = "myuser" default = true +port = 22 [connections.bart] username = "root" +port = 22 ` var noUser = ` @@ -36,6 +40,7 @@ var noUser = ` [connections.homer] destination = "192.168.1.1" +port = 22 ` func makeGoodResult() *RemoteConfig { @@ -44,10 +49,12 @@ func makeGoodResult() *RemoteConfig { Destination: "192.168.1.1", Username: "myuser", IsDefault: true, + Port: 22, } goodConnections["bart"] = RemoteConnection{ Destination: "foobar.com", Username: "root", + Port: 22, } var goodResult = RemoteConfig{ Connections: goodConnections, @@ -59,6 +66,7 @@ func makeNoUserResult() *RemoteConfig { var goodConnections = make(map[string]RemoteConnection) goodConnections["homer"] = RemoteConnection{ Destination: "192.168.1.1", + Port: 22, } var goodResult = RemoteConfig{ Connections: goodConnections, @@ -135,7 +143,7 @@ func TestRemoteConfig_GetDefault(t *testing.T) { wantErr bool }{ // A good toml should return the connection that is marked isDefault - {"good", fields{Connections: makeGoodResult().Connections}, &RemoteConnection{"192.168.1.1", "myuser", true}, false}, + {"good", fields{Connections: makeGoodResult().Connections}, &RemoteConnection{"192.168.1.1", "myuser", true, 22}, false}, // If nothing is marked as isDefault and there is more than one connection, error should occur {"nodefault", fields{Connections: noDefault}, nil, true}, // if nothing is marked as isDefault but there is only one connection, the one connection is considered the default @@ -175,9 +183,9 @@ func TestRemoteConfig_GetRemoteConnection(t *testing.T) { wantErr bool }{ // Good connection - {"goodhomer", fields{Connections: makeGoodResult().Connections}, args{name: "homer"}, &RemoteConnection{"192.168.1.1", "myuser", true}, false}, + {"goodhomer", fields{Connections: makeGoodResult().Connections}, args{name: "homer"}, &RemoteConnection{"192.168.1.1", "myuser", true, 22}, false}, // Good connection - {"goodbart", fields{Connections: makeGoodResult().Connections}, args{name: "bart"}, &RemoteConnection{"foobar.com", "root", false}, false}, + {"goodbart", fields{Connections: makeGoodResult().Connections}, args{name: "bart"}, &RemoteConnection{"foobar.com", "root", false, 22}, false}, // Getting an unknown connection should result in error {"noexist", fields{Connections: makeGoodResult().Connections}, args{name: "foobar"}, nil, true}, // Getting a connection when there are none should result in an error diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh index cd8b2ef61..f26eec87f 100644 --- a/contrib/cirrus/lib.sh +++ b/contrib/cirrus/lib.sh @@ -345,6 +345,21 @@ install_test_configs(){ remove_packaged_podman_files(){ echo "Removing packaged podman files to prevent conflicts with source build and testing." req_env_var OS_RELEASE_ID + + # If any binaries are resident they could cause unexpected pollution + for unit in io.podman.service io.podman.socket + do + for state in enabled active + do + if systemctl --quiet is-$state $unit + then + echo "Warning: $unit found $state prior to packaged-file removal" + systemctl --quiet disable $unit || true + systemctl --quiet stop $unit || true + fi + done + done + if [[ "$OS_RELEASE_ID" =~ "ubuntu" ]] then LISTING_CMD="sudo -E dpkg-query -L podman" @@ -359,6 +374,9 @@ remove_packaged_podman_files(){ if [[ -d "$fullpath" ]] || [[ $(basename "$fullpath") == "conmon" ]] ; then continue; fi ooe.sh sudo rm -vf "$fullpath" done + + # Be super extra sure and careful vs performant and completely safe + sync && echo 3 > /proc/sys/vm/drop_caches } systemd_banish(){ diff --git a/contrib/msi/podman.bat b/contrib/msi/podman.bat index 091c1c4c4..e0c7e1137 100644 --- a/contrib/msi/podman.bat +++ b/contrib/msi/podman.bat @@ -3,6 +3,14 @@ setlocal enableextensions title Podman +if "%1" EQU "" ( + goto run_help +) + +if "%1" EQU "/?" ( + goto run_help +) + :: If remote-host is given on command line -- use it setlocal enabledelayedexpansion for %%a in (%*) do ( @@ -39,5 +47,22 @@ set /p user="Please enter the remote user name: " :run_podman endlocal podman-remote-windows.exe %* +goto end + +:run_help +set run=start "Podman Help" /D "%~dp0" /B + +if not "%3" == "" ( + %run% "podman-%2-%3.html + goto end +) + +if not "%2" == "" ( + %run% "podman-%2.html + goto end +) + +%run% "%podman-remote.html" +goto end :End diff --git a/contrib/msi/podman.wxs b/contrib/msi/podman.wxs index 77c6e2815..ec62a93c5 100644 --- a/contrib/msi/podman.wxs +++ b/contrib/msi/podman.wxs @@ -5,6 +5,10 @@ <?error VERSION must be defined via command line argument?> <?endif?> + <?ifndef var.ManSourceDir?> + <?define ManSourceDir = "bin/windows" ?> + <?endif?> + <Product Name="Podman $(var.VERSION)" Id="*" UpgradeCode="696BAB5D-CA1F-4B05-B123-320F245B8D6D" Version="$(var.VERSION)" Language="1033" Manufacturer="Red Hat Inc."> <Package Id="*" Keywords="Installer" Description="Red Hat's Podman $(var.VERSION) Installer" Comments="Apache 2.0 License" Manufacturer="Red Hat Inc." InstallScope="perMachine" InstallerVersion="100" Compressed="yes"/> @@ -34,6 +38,7 @@ <Feature Id="Complete" Level="1"> <ComponentRef Id="INSTALLDIR_Component"/> <ComponentRef Id="MainExecutable"/> + <ComponentGroupRef Id="ManFiles"/> </Feature> <Icon Id="podman.ico" SourceFile="contrib/msi/podman-logo.ico"/> diff --git a/docs/links-to-html.lua b/docs/links-to-html.lua new file mode 100644 index 000000000..74072a9e4 --- /dev/null +++ b/docs/links-to-html.lua @@ -0,0 +1,5 @@ +# links-to-html.lua +function Link(el) + el.target = string.gsub(el.target, "%.1.md", ".html") + return el +end diff --git a/docs/podman-remote.1.md b/docs/podman-remote.1.md index 84042a842..04010abaf 100644 --- a/docs/podman-remote.1.md +++ b/docs/podman-remote.1.md @@ -35,6 +35,10 @@ Print usage statement Log messages above specified level: debug, info, warn, error (default), fatal or panic +**--port**=*integer* + +Use an alternative port for the ssh connections. The default port is 22 + **--remote-config-path**=*path* Alternate path for configuration file diff --git a/docs/podman-remote.conf.5.md b/docs/podman-remote.conf.5.md index 3e1cffb02..3c8a1a801 100644 --- a/docs/podman-remote.conf.5.md +++ b/docs/podman-remote.conf.5.md @@ -22,6 +22,9 @@ of the user's remote connections. Denotes whether the connection is the default connection for the user. The default connection is used when the user does not specify a destination or connection name to `podman`. +**port** = int + Use an alternative port for the ssh connections. The default port is 22. + ## EXAMPLE @@ -37,6 +40,7 @@ is designated as the default connection. [connections.host2] destination = "192.168.122.133" username = "fedora" + port = 2222 ``` ## FILES diff --git a/docs/podman-remote.sh b/docs/podman-remote.sh index db3bb6d50..2f8e76d1b 100755 --- a/docs/podman-remote.sh +++ b/docs/podman-remote.sh @@ -1,11 +1,100 @@ -#!/bin/sh +#!/bin/bash -e +# Assemble remote man pages for darwin or windows from markdown files -BREWDIR=$1 -mkdir -p $BREWDIR -docs() { -[ -z $1 ] || type="-$1" -for i in $(podman-remote $1 --help | sed -n '/^Available Commands:/,/^Flags:/p'| sed -e '1d;$d' -e '/^$/d' | awk '{print $1}'); do install podman$type-$i.1 $BREWDIR 2>/dev/null || install links/podman$type-$i.1 $BREWDIR; done +PLATFORM=$1 ## windows or darwin +TARGET=$2 ## where to output files +SOURCES=${@:3} ## directories to find markdown files + +PODMAN=${PODMAN:-bin/podman-remote} ## location overridden for testing + +function usage() { + echo >&2 "$0 PLATFORM TARGET SOURCES..." + echo >&2 "PLATFORM: Is either darwin or windows." + echo >&2 "TARGET: Is the directory where files will be staged." + echo >&2 "SOURCES: Are the directories to source markdown files." +} + +function fail() { + echo >&2 -e "$@\n" + usage + exit 1 +} + +case $PLATFORM in +'darwin') + EXT=1 + PUBLISHER=darwin_fn + ;; +'windows') + EXT=1.md + PUBLISHER=windows_fn + ;; +'-help') + usage + exit 0 + ;; +*) fail '"darwin" and "windows" are currently the only supported platforms.' ;; +esac + +if [[ -z $TARGET ]]; then + fail 'TARGET directory is required' +fi + +if [[ -z $SOURCES ]]; then + fail 'At least one SOURCE directory is required' +fi + +if [[ ! -x $PODMAN ]]; then + fail "$PODMAN does not exist" +fi + +## darwin_fn copies the markdown page or link to flattened directory +function darwin_fn() { + local markdown=$1 + local file=$(basename $markdown) + local dir=$(dirname $markdown) + + if [[ -f $dir/links/$file ]]; then + markdown=$dir/links/$file + fi + install $markdown $TARGET +} + +## windows_fn converts the markdown page or link to HTML +function windows_fn() { + local markdown=$1 + local file=$(basename $markdown) + local dir=$(dirname $markdown) + + if [[ ! -f $markdown ]]; then + local link=$(sed -e 's?.so man1/\(.*\)?\1?' <$dir/links/${file%.md}) + markdown=$dir/$link.md + fi + pandoc --ascii --lua-filter=$dir/links-to-html.lua -o $TARGET/${file%.$EXT}.html $markdown +} + +## pub_pages finds and publishes the remote manual pages +function pub_pages() { + local source=$1 + local publisher=$2 + for f in $(ls $source/podman-remote*$EXT); do + $publisher $f + done + + for c in "container" "image" "pod" "volume" ""; do + local cmd=${c:+-$c} + for s in $($PODMAN $c --help | sed -n '/^Available Commands:/,/^Flags:/p' | sed -e '1d;$d' -e '/^$/d' | awk '{print $1}'); do + $publisher $source/podman$cmd-$s.$EXT + done + done } -docs -for cmd in 'container image pod volume'; do docs $cmd; done +## walk the SOURCES for markdown sources +mkdir -p $TARGET +for s in $SOURCES; do + if [[ -d $s ]]; then + pub_pages $s $PUBLISHER + else + echo >&2 "Warning: $s does not exist" + fi +done diff --git a/docs/tutorials/README.md b/docs/tutorials/README.md index 925cfb970..c340d683f 100644 --- a/docs/tutorials/README.md +++ b/docs/tutorials/README.md @@ -8,6 +8,14 @@ Learn how to setup Podman and perform some basic commands with the utility. -**[Basic Setup and Use of Podman in a Rootless environment.](https://github.com/containers/libpod/blob/master/docs/tutorials/rootless_tutorial.md). +**[Basic Setup and Use of Podman in a Rootless environment.](https://github.com/containers/libpod/blob/master/docs/tutorials/rootless_tutorial.md).** The steps required to setup rootless Podman are enumerated. + +**[Setup on OS X](https://github.com/containers/libpod/blob/master/doc/tutorials/mac_client.md)** + +Special setup for running the Podman remote client on a Mac and connecting to Podman running on a Linux VM are documented + +**[Remote Client](https://github.com/containers/libpod/blob/master/doc/tutorials/remote_client.md)** + +A brief how-to on using the Podman remote-client. diff --git a/mac_client.md b/docs/tutorials/mac_client.md index bf08e8cc1..bf08e8cc1 100644 --- a/mac_client.md +++ b/docs/tutorials/mac_client.md diff --git a/docs/tutorials/podman_tutorial.md b/docs/tutorials/podman_tutorial.md index d2f8e08fa..559d25d6a 100644 --- a/docs/tutorials/podman_tutorial.md +++ b/docs/tutorials/podman_tutorial.md @@ -5,6 +5,9 @@ Podman is a utility provided as part of the libpod library. It can be used to c containers. The following tutorial will teach you how to set up Podman and perform some basic commands with Podman. +If you are running on a Mac, you should instead follow the [Mac tutorial](https://github.com/containers/libpod/blob/master/mac_client.md) +to set up the remote Podman client. + **NOTE**: the code samples are intended to be run as a non-root user, and use `sudo` where root escalation is required. diff --git a/remote_client.md b/docs/tutorials/remote_client.md index be5c918c8..197ff3d26 100644 --- a/remote_client.md +++ b/docs/tutorials/remote_client.md @@ -19,8 +19,8 @@ system as a user with privileges to the varlink socket (more on this later). ## Building the remote client At this time, the remote-client is not being packaged for any distribution. It must be built from -source. To set up your build environment, see [Installation notes](install.md) and follow the -section [Building from scratch](install.md#building-from-scratch). Once you can successfully +source. To set up your build environment, see [Installation notes](https://github.com/containers/libpod/blob/master/install.md) and follow the +section [Building from scratch](https://github.com/containers/libpod/blob/master/install.md#building-from-scratch). Once you can successfully build the regular Podman binary, you can now build the remote-client. ``` $ make podman-remote @@ -17,7 +17,7 @@ require ( github.com/containers/conmon v0.3.0 // indirect github.com/containers/image v3.0.2+incompatible github.com/containers/psgo v1.3.1 - github.com/containers/storage v1.13.2 + github.com/containers/storage v1.13.3 github.com/coreos/bbolt v1.3.3 // indirect github.com/coreos/etcd v3.3.13+incompatible // indirect github.com/coreos/go-iptables v0.4.2 // indirect @@ -108,6 +108,8 @@ github.com/containers/storage v1.13.1 h1:rjVirLS9fCGkUFlLDZEoGDDUugtIf46DufWvJu0 github.com/containers/storage v1.13.1/go.mod h1:6D8nK2sU9V7nEmAraINRs88ZEscM5C5DK+8Npp27GeA= github.com/containers/storage v1.13.2 h1:UXZ0Ckmk6+6+4vj2M2ywruVtH97pnRoAhTG8ctd+yQI= github.com/containers/storage v1.13.2/go.mod h1:6D8nK2sU9V7nEmAraINRs88ZEscM5C5DK+8Npp27GeA= +github.com/containers/storage v1.13.3 h1:9EzTXZXG/8SGD9MnkSCe/jLq3QldcE1QlgW7vePEsjw= +github.com/containers/storage v1.13.3/go.mod h1:6D8nK2sU9V7nEmAraINRs88ZEscM5C5DK+8Npp27GeA= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= diff --git a/libpod/runtime.go b/libpod/runtime.go index 80b58654e..e2b9667be 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -247,7 +247,7 @@ type RuntimeConfig struct { // EventsLogger determines where events should be logged EventsLogger string `toml:"events_logger"` // EventsLogFilePath is where the events log is stored. - EventsLogFilePath string `toml:"-events_logfile_path"` + EventsLogFilePath string `toml:"events_logfile_path"` //DetachKeys is the sequence of keys used to detach a container DetachKeys string `toml:"detach_keys"` diff --git a/pkg/adapter/client.go b/pkg/adapter/client.go index da6ff5fd0..1805c758d 100644 --- a/pkg/adapter/client.go +++ b/pkg/adapter/client.go @@ -35,7 +35,7 @@ func (r RemoteRuntime) RemoteEndpoint() (remoteEndpoint *Endpoint, err error) { if len(r.cmd.RemoteUserName) < 1 { return nil, errors.New("you must provide a username when providing a remote host name") } - rc := remoteclientconfig.RemoteConnection{r.cmd.RemoteHost, r.cmd.RemoteUserName, false} + rc := remoteclientconfig.RemoteConnection{r.cmd.RemoteHost, r.cmd.RemoteUserName, false, r.cmd.Port} remoteEndpoint, err = newBridgeConnection("", &rc, r.cmd.LogLevel) // if the user has a config file with connections in it } else if len(remoteConfigConnections.Connections) > 0 { diff --git a/pkg/adapter/client_unix.go b/pkg/adapter/client_unix.go index 4781acd06..a7bc7c1c0 100644 --- a/pkg/adapter/client_unix.go +++ b/pkg/adapter/client_unix.go @@ -10,7 +10,11 @@ import ( ) func formatDefaultBridge(remoteConn *remoteclientconfig.RemoteConnection, logLevel string) string { + port := remoteConn.Port + if port == 0 { + port = 22 + } return fmt.Sprintf( - `ssh -T %s@%s -- /usr/bin/varlink -A \'/usr/bin/podman --log-level=%s varlink \\\$VARLINK_ADDRESS\' bridge`, - remoteConn.Username, remoteConn.Destination, logLevel) + `ssh -p %d -T %s@%s -- /usr/bin/varlink -A \'/usr/bin/podman --log-level=%s varlink \\\$VARLINK_ADDRESS\' bridge`, + port, remoteConn.Username, remoteConn.Destination, logLevel) } diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go index cc091dcee..3d59d70d8 100644 --- a/pkg/spec/storage.go +++ b/pkg/spec/storage.go @@ -220,6 +220,7 @@ func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount, // volumes, and return a list of them. // Conflicts are resolved simply - the last container specified wins. // Container names may be suffixed by mount options after a colon. +// TODO: We should clean these paths if possible func (config *CreateConfig) getVolumesFrom(runtime *libpod.Runtime) (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) { // Both of these are maps of mount destination to mount type. // We ensure that each destination is only mounted to once in this way. @@ -465,7 +466,7 @@ func getBindMount(args []string) (spec.Mount, error) { if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { return newMount, err } - newMount.Destination = kv[1] + newMount.Destination = filepath.Clean(kv[1]) setDest = true case "relabel": if setRelabel { @@ -559,7 +560,7 @@ func getTmpfsMount(args []string) (spec.Mount, error) { if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { return newMount, err } - newMount.Destination = kv[1] + newMount.Destination = filepath.Clean(kv[1]) setDest = true default: return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0]) @@ -623,7 +624,7 @@ func getNamedVolume(args []string) (*libpod.ContainerNamedVolume, error) { if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { return nil, err } - newVolume.Dest = kv[1] + newVolume.Dest = filepath.Clean(kv[1]) setDest = true default: return nil, errors.Wrapf(util.ErrBadMntOption, kv[0]) @@ -678,10 +679,12 @@ func (config *CreateConfig) getVolumeMounts() (map[string]spec.Mount, map[string return nil, nil, err } + cleanDest := filepath.Clean(dest) + if strings.HasPrefix(src, "/") || strings.HasPrefix(src, ".") { // This is not a named volume newMount := spec.Mount{ - Destination: dest, + Destination: cleanDest, Type: string(TypeBind), Source: src, Options: options, @@ -694,7 +697,7 @@ func (config *CreateConfig) getVolumeMounts() (map[string]spec.Mount, map[string // This is a named volume newNamedVol := new(libpod.ContainerNamedVolume) newNamedVol.Name = src - newNamedVol.Dest = dest + newNamedVol.Dest = cleanDest newNamedVol.Options = options if _, ok := volumes[newNamedVol.Dest]; ok { @@ -719,10 +722,11 @@ func (config *CreateConfig) getImageVolumes() (map[string]spec.Mount, map[string } for vol := range config.BuiltinImgVolumes { + cleanDest := filepath.Clean(vol) if config.ImageVolumeType == "tmpfs" { // Tmpfs image volumes are handled as mounts mount := spec.Mount{ - Destination: vol, + Destination: cleanDest, Source: TypeTmpfs, Type: TypeTmpfs, Options: []string{"rprivate", "rw", "nodev"}, @@ -732,7 +736,7 @@ func (config *CreateConfig) getImageVolumes() (map[string]spec.Mount, map[string namedVolume := new(libpod.ContainerNamedVolume) namedVolume.Name = stringid.GenerateNonCryptoID() namedVolume.Options = []string{"rprivate", "rw", "nodev"} - namedVolume.Dest = vol + namedVolume.Dest = cleanDest volumes[vol] = namedVolume } } @@ -760,7 +764,7 @@ func (config *CreateConfig) getTmpfsMounts() (map[string]spec.Mount, error) { } mount := spec.Mount{ - Destination: destPath, + Destination: filepath.Clean(destPath), Type: string(TypeTmpfs), Options: options, Source: string(TypeTmpfs), diff --git a/rootless.md b/rootless.md index 960430d54..53463dccc 100644 --- a/rootless.md +++ b/rootless.md @@ -14,6 +14,9 @@ can easily fail * No cgroup V1 Support * cgroup V1 does not safely support cgroup delegation. * However, cgroup V2 provides cgroup delegation and is available on Fedora starting with version 29 and other Linux distributions. +* Some systemd's unit configuration options do not work in the rootless container + * systemd fails to apply several options and failures are silently ignored (e.g. CPUShares, MemoryLimit). + * Use of certain options will cause service startup failures (e.g. PrivateNetwork). * Can not share container images with CRI-O or other users * Difficult to use additional stores for sharing content * Does not work on NFS or parallel filesystem homedirs (e.g. [GPFS](https://www.ibm.com/support/knowledgecenter/en/SSFKCN/gpfs_welcome.html)) diff --git a/vendor/github.com/containers/storage/Makefile b/vendor/github.com/containers/storage/Makefile index bb1de007b..fa0fddaeb 100644 --- a/vendor/github.com/containers/storage/Makefile +++ b/vendor/github.com/containers/storage/Makefile @@ -1,4 +1,5 @@ export GO111MODULE=off +export GOPROXY=https://proxy.golang.org .PHONY: \ all \ diff --git a/vendor/github.com/containers/storage/VERSION b/vendor/github.com/containers/storage/VERSION index 065f9ec4c..01b756823 100644 --- a/vendor/github.com/containers/storage/VERSION +++ b/vendor/github.com/containers/storage/VERSION @@ -1 +1 @@ -1.13.3-dev +1.13.3 diff --git a/vendor/github.com/containers/storage/images.go b/vendor/github.com/containers/storage/images.go index b1df9b765..5d6a2e48d 100644 --- a/vendor/github.com/containers/storage/images.go +++ b/vendor/github.com/containers/storage/images.go @@ -372,7 +372,7 @@ func (r *imageStore) ClearFlag(id string, flag string) error { } image, ok := r.lookup(id) if !ok { - return ErrImageUnknown + return errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) } delete(image.Flags, flag) return r.Save() @@ -384,7 +384,7 @@ func (r *imageStore) SetFlag(id string, flag string, value interface{}) error { } image, ok := r.lookup(id) if !ok { - return ErrImageUnknown + return errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) } if image.Flags == nil { image.Flags = make(map[string]interface{}) @@ -456,14 +456,14 @@ func (r *imageStore) addMappedTopLayer(id, layer string) error { image.MappedTopLayers = append(image.MappedTopLayers, layer) return r.Save() } - return ErrImageUnknown + return errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) } func (r *imageStore) Metadata(id string) (string, error) { if image, ok := r.lookup(id); ok { return image.Metadata, nil } - return "", ErrImageUnknown + return "", errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) } func (r *imageStore) SetMetadata(id, metadata string) error { @@ -474,7 +474,7 @@ func (r *imageStore) SetMetadata(id, metadata string) error { image.Metadata = metadata return r.Save() } - return ErrImageUnknown + return errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) } func (r *imageStore) removeName(image *Image, name string) { @@ -499,7 +499,7 @@ func (r *imageStore) SetNames(id string, names []string) error { image.Names = names return r.Save() } - return ErrImageUnknown + return errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) } func (r *imageStore) Delete(id string) error { @@ -508,7 +508,7 @@ func (r *imageStore) Delete(id string) error { } image, ok := r.lookup(id) if !ok { - return ErrImageUnknown + return errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) } id = image.ID toDeleteIndex := -1 @@ -551,14 +551,14 @@ func (r *imageStore) Get(id string) (*Image, error) { if image, ok := r.lookup(id); ok { return copyImage(image), nil } - return nil, ErrImageUnknown + return nil, errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) } func (r *imageStore) Lookup(name string) (id string, err error) { if image, ok := r.lookup(name); ok { return image.ID, nil } - return "", ErrImageUnknown + return "", errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) } func (r *imageStore) Exists(id string) bool { @@ -570,7 +570,7 @@ func (r *imageStore) ByDigest(d digest.Digest) ([]*Image, error) { if images, ok := r.bydigest[d]; ok { return copyImageSlice(images), nil } - return nil, ErrImageUnknown + return nil, errors.Wrapf(ErrImageUnknown, "error locating image with digest %q", d) } func (r *imageStore) BigData(id, key string) ([]byte, error) { @@ -579,7 +579,7 @@ func (r *imageStore) BigData(id, key string) ([]byte, error) { } image, ok := r.lookup(id) if !ok { - return nil, ErrImageUnknown + return nil, errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) } return ioutil.ReadFile(r.datapath(image.ID, key)) } @@ -590,7 +590,7 @@ func (r *imageStore) BigDataSize(id, key string) (int64, error) { } image, ok := r.lookup(id) if !ok { - return -1, ErrImageUnknown + return -1, errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) } if image.BigDataSizes == nil { image.BigDataSizes = make(map[string]int64) @@ -610,7 +610,7 @@ func (r *imageStore) BigDataDigest(id, key string) (digest.Digest, error) { } image, ok := r.lookup(id) if !ok { - return "", ErrImageUnknown + return "", errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) } if image.BigDataDigests == nil { image.BigDataDigests = make(map[string]digest.Digest) @@ -624,7 +624,7 @@ func (r *imageStore) BigDataDigest(id, key string) (digest.Digest, error) { func (r *imageStore) BigDataNames(id string) ([]string, error) { image, ok := r.lookup(id) if !ok { - return nil, ErrImageUnknown + return nil, errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) } return copyStringSlice(image.BigDataNames), nil } @@ -649,7 +649,7 @@ func (r *imageStore) SetBigData(id, key string, data []byte, digestManifest func } image, ok := r.lookup(id) if !ok { - return ErrImageUnknown + return errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) } err := os.MkdirAll(r.datadir(image.ID), 0700) if err != nil { diff --git a/vendor/github.com/containers/storage/layers.go b/vendor/github.com/containers/storage/layers.go index d746ba061..ac905b0b4 100644 --- a/vendor/github.com/containers/storage/layers.go +++ b/vendor/github.com/containers/storage/layers.go @@ -363,7 +363,7 @@ func (r *layerStore) Load() error { } if cleanup, ok := layer.Flags[incompleteFlag]; ok { if b, ok := cleanup.(bool); ok && b { - err = r.Delete(layer.ID) + err = r.deleteInternal(layer.ID) if err != nil { break } @@ -372,7 +372,7 @@ func (r *layerStore) Load() error { } } if shouldSave { - return r.Save() + return r.saveLayers() } } return err @@ -416,6 +416,16 @@ func (r *layerStore) loadMounts() error { } func (r *layerStore) Save() error { + r.mountsLockfile.Lock() + defer r.mountsLockfile.Unlock() + defer r.mountsLockfile.Touch() + if err := r.saveLayers(); err != nil { + return err + } + return r.saveMounts() +} + +func (r *layerStore) saveLayers() error { if !r.IsReadWrite() { return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to modify the layer store at %q", r.layerspath()) } @@ -431,13 +441,7 @@ func (r *layerStore) Save() error { return err } defer r.Touch() - if err := ioutils.AtomicWriteFile(rpath, jldata, 0600); err != nil { - return err - } - r.mountsLockfile.Lock() - defer r.mountsLockfile.Unlock() - defer r.mountsLockfile.Touch() - return r.saveMounts() + return ioutils.AtomicWriteFile(rpath, jldata, 0600) } func (r *layerStore) saveMounts() error { @@ -954,7 +958,7 @@ func (r *layerStore) tspath(id string) string { return filepath.Join(r.layerdir, id+tarSplitSuffix) } -func (r *layerStore) Delete(id string) error { +func (r *layerStore) deleteInternal(id string) error { if !r.IsReadWrite() { return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to delete layers at %q", r.layerspath()) } @@ -963,23 +967,7 @@ func (r *layerStore) Delete(id string) error { return ErrLayerUnknown } id = layer.ID - // The layer may already have been explicitly unmounted, but if not, we - // should try to clean that up before we start deleting anything at the - // driver level. - mountCount, err := r.Mounted(id) - if err != nil { - return errors.Wrapf(err, "error checking if layer %q is still mounted", id) - } - for mountCount > 0 { - if _, err := r.Unmount(id, false); err != nil { - return err - } - mountCount, err = r.Mounted(id) - if err != nil { - return errors.Wrapf(err, "error checking if layer %q is still mounted", id) - } - } - err = r.driver.Remove(id) + err := r.driver.Remove(id) if err == nil { os.Remove(r.tspath(id)) delete(r.byid, id) @@ -1015,11 +1003,36 @@ func (r *layerStore) Delete(id string) error { label.ReleaseLabel(mountLabel) } } - if err = r.Save(); err != nil { + } + return err +} + +func (r *layerStore) Delete(id string) error { + layer, ok := r.lookup(id) + if !ok { + return ErrLayerUnknown + } + id = layer.ID + // The layer may already have been explicitly unmounted, but if not, we + // should try to clean that up before we start deleting anything at the + // driver level. + mountCount, err := r.Mounted(id) + if err != nil { + return errors.Wrapf(err, "error checking if layer %q is still mounted", id) + } + for mountCount > 0 { + if _, err := r.Unmount(id, false); err != nil { return err } + mountCount, err = r.Mounted(id) + if err != nil { + return errors.Wrapf(err, "error checking if layer %q is still mounted", id) + } } - return err + if err := r.deleteInternal(id); err != nil { + return err + } + return r.Save() } func (r *layerStore) Lookup(name string) (id string, err error) { diff --git a/vendor/github.com/containers/storage/store.go b/vendor/github.com/containers/storage/store.go index 74275482b..dd3405212 100644 --- a/vendor/github.com/containers/storage/store.go +++ b/vendor/github.com/containers/storage/store.go @@ -1202,7 +1202,7 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat } } if cimage == nil { - return nil, ErrImageUnknown + return nil, errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) } imageID = cimage.ID @@ -1437,7 +1437,7 @@ func (s *store) ListImageBigData(id string) ([]string, error) { return bigDataNames, err } } - return nil, ErrImageUnknown + return nil, errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) } func (s *store) ImageBigDataSize(id, key string) (int64, error) { @@ -1516,7 +1516,7 @@ func (s *store) ImageBigData(id, key string) ([]byte, error) { return data, nil } } - return nil, ErrImageUnknown + return nil, errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) } func (s *store) SetImageBigData(id, key string, data []byte, digestManifest func([]byte) (digest.Digest, error)) error { @@ -2891,7 +2891,7 @@ func (s *store) Image(id string) (*Image, error) { return image, nil } } - return nil, ErrImageUnknown + return nil, errors.Wrapf(ErrImageUnknown, "error locating image with ID %q", id) } func (s *store) ImagesByTopLayer(id string) ([]*Image, error) { @@ -2953,7 +2953,7 @@ func (s *store) ImagesByDigest(d digest.Digest) ([]*Image, error) { } } imageList, err := store.ByDigest(d) - if err != nil && err != ErrImageUnknown { + if err != nil && errors.Cause(err) != ErrImageUnknown { return nil, err } images = append(images, imageList...) diff --git a/vendor/modules.txt b/vendor/modules.txt index 1de9fede0..3a27eea12 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -109,7 +109,7 @@ github.com/containers/psgo/internal/dev github.com/containers/psgo/internal/proc github.com/containers/psgo/internal/process github.com/containers/psgo/internal/host -# github.com/containers/storage v1.13.2 +# github.com/containers/storage v1.13.3 github.com/containers/storage github.com/containers/storage/pkg/archive github.com/containers/storage/pkg/chrootarchive |