diff options
-rw-r--r-- | .cirrus.yml | 2 | ||||
-rw-r--r-- | contrib/pkginstaller/.gitignore | 6 | ||||
-rw-r--r-- | contrib/pkginstaller/Distribution.in | 17 | ||||
-rw-r--r-- | contrib/pkginstaller/Makefile | 50 | ||||
-rw-r--r-- | contrib/pkginstaller/README.md | 22 | ||||
-rw-r--r-- | contrib/pkginstaller/Resources/banner.png | bin | 0 -> 50381 bytes | |||
-rw-r--r-- | contrib/pkginstaller/Resources/conclusion.html | 13 | ||||
-rwxr-xr-x | contrib/pkginstaller/package.sh | 60 | ||||
-rwxr-xr-x | contrib/pkginstaller/scripts/postinstall | 27 | ||||
-rwxr-xr-x | contrib/pkginstaller/scripts/preinstall | 5 | ||||
-rw-r--r-- | contrib/pkginstaller/welcome.html.in | 16 | ||||
-rw-r--r-- | docs/tutorials/socket_activation.md | 23 | ||||
-rw-r--r-- | pkg/domain/filters/containers.go | 9 | ||||
-rw-r--r-- | pkg/domain/filters/pods.go | 3 | ||||
-rw-r--r-- | pkg/domain/filters/volumes.go | 5 | ||||
-rw-r--r-- | pkg/domain/infra/abi/containers.go | 1 | ||||
-rw-r--r-- | pkg/util/filters.go | 33 | ||||
-rw-r--r-- | pkg/util/filters_test.go | 4 | ||||
-rw-r--r-- | test/e2e/prune_test.go | 18 |
19 files changed, 270 insertions, 44 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index c629bcf70..7a488216e 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -843,7 +843,7 @@ meta_task: container: cpu: 2 memory: 2 - image: quay.io/libpod/imgts:$IMAGE_SUFFIX + image: quay.io/libpod/imgts:latest env: # Space-separated list of images used by this repository state # Disabled ${PRIOR_FEDORA_CACHE_IMAGE_NAME} for Fedora 35 diff --git a/contrib/pkginstaller/.gitignore b/contrib/pkginstaller/.gitignore new file mode 100644 index 000000000..5e597ab07 --- /dev/null +++ b/contrib/pkginstaller/.gitignore @@ -0,0 +1,6 @@ +out +Distribution +welcome.html +tmp-download +.vscode +root diff --git a/contrib/pkginstaller/Distribution.in b/contrib/pkginstaller/Distribution.in new file mode 100644 index 000000000..0e0d3843a --- /dev/null +++ b/contrib/pkginstaller/Distribution.in @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<installer-script minSpecVersion="1.000000"> + <title>Podman __VERSION__</title> + <background mime-type="image/png" file="banner.png" scaling="proportional"/> + <welcome file="welcome.html" mime-type="text/html" /> + <conclusion file="conclusion.html" mime-type="text/html" /> + <license file="LICENSE.txt"/> + <options customize="never" hostArchitectures="x86_64,arm64" /> + <domains enable_localSystem="true" /> + <choices-outline> + <line choice="podman"/> + </choices-outline> + <choice id="podman" title="podman"> + <pkg-ref id="podman.pkg"/> + </choice> + <pkg-ref id="podman.pkg">podman.pkg</pkg-ref> +</installer-script> diff --git a/contrib/pkginstaller/Makefile b/contrib/pkginstaller/Makefile new file mode 100644 index 000000000..19c9b51aa --- /dev/null +++ b/contrib/pkginstaller/Makefile @@ -0,0 +1,50 @@ +SHELL := bash + +ARCH ?= aarch64 +PODMAN_VERSION ?= 4.1.0 +GVPROXY_VERSION ?= 0.4.0 +QEMU_VERSION ?= 7.0.0-2 +GVPROXY_RELEASE_URL ?= https://github.com/containers/gvisor-tap-vsock/releases/download/v$(GVPROXY_VERSION)/gvproxy-darwin +QEMU_RELEASE_URL ?= https://github.com/containers/podman-machine-qemu/releases/download/v$(QEMU_VERSION)/podman-machine-qemu-$(ARCH)-$(QEMU_VERSION).tar.xz +PACKAGE_DIR ?= out/packaging +TMP_DOWNLOAD ?= tmp-download +PACKAGE_ROOT ?= root + +default: pkginstaller + +get_gvproxy: + mkdir -p $(TMP_DOWNLOAD) + cd $(TMP_DOWNLOAD) && curl -sLo gvproxy $(GVPROXY_RELEASE_URL) + +get_qemu: + mkdir -p $(TMP_DOWNLOAD) + cd $(TMP_DOWNLOAD) && curl -sLO $(QEMU_RELEASE_URL) + +packagedir: package_root Distribution welcome.html + mkdir -p $(PACKAGE_DIR) + cp -r Resources $(PACKAGE_DIR)/ + cp welcome.html $(PACKAGE_DIR)/Resources/ + cp Distribution $(PACKAGE_DIR)/ + cp -r scripts $(PACKAGE_DIR)/ + cp -r $(PACKAGE_ROOT) $(PACKAGE_DIR)/ + cp package.sh $(PACKAGE_DIR)/ + cd $(PACKAGE_DIR) && pkgbuild --analyze --root ./root component.plist + echo -n $(PODMAN_VERSION) > $(PACKAGE_DIR)/VERSION + echo -n $(ARCH) > $(PACKAGE_DIR)/ARCH + cp ../../LICENSE $(PACKAGE_DIR)/Resources/LICENSE.txt + +package_root: get_gvproxy get_qemu + mkdir -p $(PACKAGE_ROOT)/podman/bin $(PACKAGE_ROOT)/podman/qemu + tar -C $(PACKAGE_ROOT)/podman/qemu -xf $(TMP_DOWNLOAD)/podman-machine-qemu-$(ARCH)-$(QEMU_VERSION).tar.xz + cp $(TMP_DOWNLOAD)/gvproxy $(PACKAGE_ROOT)/podman/bin/ + chmod a+x $(PACKAGE_ROOT)/podman/bin/* + +%: %.in + @sed -e 's/__VERSION__/'$(PODMAN_VERSION)'/g' $< >$@ + +pkginstaller: packagedir + cd $(PACKAGE_DIR) && ./package.sh .. + +.PHONY: clean +clean: + rm -rf $(TMP_DOWNLOAD) $(PACKAGE_ROOT) $(PACKAGE_DIR) Distribution welcome.html diff --git a/contrib/pkginstaller/README.md b/contrib/pkginstaller/README.md new file mode 100644 index 000000000..37c59ce04 --- /dev/null +++ b/contrib/pkginstaller/README.md @@ -0,0 +1,22 @@ +## How to build + +```sh +$ make ARCH=<amd64 | aarch64> NO_CODESIGN=1 pkginstaller + +# or to create signed pkg +$ make ARCH=<amd64 | aarch64> CODESIGN_IDENTITY=<ID> PRODUCTSIGN_IDENTITY=<ID> pkginstaller +``` + +The generated pkg will be written to `out/podman-macos-installer-*.pkg`. +Currently the pkg installs `podman`, `qemu`, `gvproxy` and `podman-mac-helper` to `/Applications/podman` + +The `qemu` build it uses is from [containers/podman-machine-qemu](https://github.com/containers/podman-machine-qemu) + +## Uninstalling + +```sh +$ sudo rm -rf /opt/podman +``` + +### Screenshot +<img width="626" alt="screenshot-macOS-pkg-podman" src="https://user-images.githubusercontent.com/8885742/157380992-2e3b1573-34a0-4aa0-bdc1-a85f4792a1d2.png"> diff --git a/contrib/pkginstaller/Resources/banner.png b/contrib/pkginstaller/Resources/banner.png Binary files differnew file mode 100644 index 000000000..7db751341 --- /dev/null +++ b/contrib/pkginstaller/Resources/banner.png diff --git a/contrib/pkginstaller/Resources/conclusion.html b/contrib/pkginstaller/Resources/conclusion.html new file mode 100644 index 000000000..c442e4ebf --- /dev/null +++ b/contrib/pkginstaller/Resources/conclusion.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"/> +</head> +<body> +<div align="left" style="font-family: Helvetica; padding-left: 10px;"> + <br/> + <p style="color: #020202; font-size: 12px;">Thanks for installing Podman!</p> + <p style="color: #020202; font-size: 12px;">You can now start using the 'podman' command. First run 'podman machine init'</b>.</p> +</div> +</body> +</html> diff --git a/contrib/pkginstaller/package.sh b/contrib/pkginstaller/package.sh new file mode 100755 index 000000000..b7b33954d --- /dev/null +++ b/contrib/pkginstaller/package.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +set -euxo pipefail + +BASEDIR=$(dirname "$0") +OUTPUT=$1 +CODESIGN_IDENTITY=${CODESIGN_IDENTITY:-mock} +PRODUCTSIGN_IDENTITY=${PRODUCTSIGN_IDENTITY:-mock} +NO_CODESIGN=${NO_CODESIGN:-0} +HELPER_BINARIES_DIR="/opt/podman/qemu/bin" + +binDir="${BASEDIR}/root/podman/bin" + +function build_podman() { + pushd "$1" + make podman-remote HELPER_BINARIES_DIR="${HELPER_BINARIES_DIR}" + make podman-mac-helper + cp bin/darwin/podman "contrib/pkginstaller/out/packaging/${binDir}/podman" + cp bin/darwin/podman-mac-helper "contrib/pkginstaller/out/packaging/${binDir}/podman-mac-helper" + popd +} + +function sign() { + if [ "${NO_CODESIGN}" -eq "1" ]; then + return + fi + local opts="" + entitlements="${BASEDIR}/$(basename "$1").entitlements" + if [ -f "${entitlements}" ]; then + opts="--entitlements ${entitlements}" + fi + codesign --deep --sign "${CODESIGN_IDENTITY}" --options runtime --force --timestamp "${opts}" "$1" +} + +version=$(cat "${BASEDIR}/VERSION") +arch=$(cat "${BASEDIR}/ARCH") + +build_podman "../../../../" +sign "${binDir}/podman" +sign "${binDir}/gvproxy" +sign "${binDir}/podman-mac-helper" + +pkgbuild --identifier com.redhat.podman --version "${version}" \ + --scripts "${BASEDIR}/scripts" \ + --root "${BASEDIR}/root" \ + --install-location /opt \ + --component-plist "${BASEDIR}/component.plist" \ + "${OUTPUT}/podman.pkg" + +productbuild --distribution "${BASEDIR}/Distribution" \ + --resources "${BASEDIR}/Resources" \ + --package-path "${OUTPUT}" \ + "${OUTPUT}/podman-unsigned.pkg" +rm "${OUTPUT}/podman.pkg" + +if [ ! "${NO_CODESIGN}" -eq "1" ]; then + productsign --timestamp --sign "${PRODUCTSIGN_IDENTITY}" "${OUTPUT}/podman-unsigned.pkg" "${OUTPUT}/podman-installer-macos-${arch}.pkg" +else + mv "${OUTPUT}/podman-unsigned.pkg" "${OUTPUT}/podman-installer-macos-${arch}.pkg" +fi diff --git a/contrib/pkginstaller/scripts/postinstall b/contrib/pkginstaller/scripts/postinstall new file mode 100755 index 000000000..db17eede8 --- /dev/null +++ b/contrib/pkginstaller/scripts/postinstall @@ -0,0 +1,27 @@ +#!/bin/bash + +set -e + +BZSH_PODMAN_PATH_EXP='PATH="/opt/podman/bin:$PATH"' +FISH_PODMAN_PATH_EXP='set PATH "/opt/podman/bin $PATH"' +BASHRC_PATH="$HOME/.bash_profile" +ZSHENV_PATH="$HOME/.zshenv" +ZSHRC_PATH="$HOME/.zshrc" +FSHCFG_PATH="$HOME/.config/fish/config.fish" + +# append /Applications/podman/bin to $PATH +if [ -f "$BASHRC_PATH" ]; then + grep -Fxq "$BZSH_PODMAN_PATH_EXP" "$BASHRC_PATH" || echo "$BZSH_PODMAN_PATH_EXP" >> "$BASHRC_PATH" +fi +if [ -f "$ZSHENV_PATH" ]; then + grep -Fxq "$BZSH_PODMAN_PATH_EXP" "$ZSHENV_PATH" || echo "$BZSH_PODMAN_PATH_EXP" >> "$ZSHENV_PATH" +fi +if [ -f "$ZSHRC_PATH" ]; then + grep -Fxq "$BZSH_PODMAN_PATH_EXP" "$ZSHRC_PATH" || echo "$BZSH_PODMAN_PATH_EXP" >> "$ZSHRC_PATH" +fi +if [ -f "$FSHCFG_PATH" ]; then + grep -Fxq "$FISH_PODMAN_PATH_EXP" "$FSHCFG_PATH" || echo "$FISH_PODMAN_PATH_EXP" >> "$FSHCFG_PATH" +fi + +ln -s /opt/podman/bin/podman-mac-helper /opt/podman/qemu/bin/podman-mac-helper +ln -s /opt/podman/bin/gvproxy /opt/podman/qemu/bin/gvproxy diff --git a/contrib/pkginstaller/scripts/preinstall b/contrib/pkginstaller/scripts/preinstall new file mode 100755 index 000000000..a381868fc --- /dev/null +++ b/contrib/pkginstaller/scripts/preinstall @@ -0,0 +1,5 @@ +#!/bin/bash + +set -e + +rm -rf /opt/podman diff --git a/contrib/pkginstaller/welcome.html.in b/contrib/pkginstaller/welcome.html.in new file mode 100644 index 000000000..b06198716 --- /dev/null +++ b/contrib/pkginstaller/welcome.html.in @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"/> +</head> +<body> +<div align="left" style="font-family: Helvetica; padding-left: 10px;"> + <br/> + <p style="color: #020202; font-size: 12px;">This will install <span style="color: #46b9d6; font-size: 12px;">Podman __VERSION__</span> + on your computer. You will be guided through the steps necessary to install this software.</p> + <br/> + <p style="color: #abb0b0; font-size: 12px;">Click <span style="color: #626666">“Continue"</span> to continue the + setup</p> +</div> +</body> +</html> diff --git a/docs/tutorials/socket_activation.md b/docs/tutorials/socket_activation.md index 9b4b02b81..f4ad5aefd 100644 --- a/docs/tutorials/socket_activation.md +++ b/docs/tutorials/socket_activation.md @@ -19,7 +19,7 @@ The architecture looks like this ``` mermaid stateDiagram-v2 - [*] --> systemd: client connects + [*] --> systemd: first client connects systemd --> podman: socket inherited via fork/exec ``` @@ -55,6 +55,9 @@ $ export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock $ docker-compose up ``` +When __docker-compose__ or any other client connects to the UNIX socket `$XDG_RUNTIME_DIR/podman/podman.sock`, +the service _podman.service_ is started. See its definition in the file _/usr/lib/systemd/user/podman.service_. + ## Socket activation of containers Since version 3.4.0 Podman supports socket activation of containers, i.e., passing @@ -65,7 +68,7 @@ as can be seen in the following diagram: ``` mermaid stateDiagram-v2 - [*] --> systemd: client connects + [*] --> systemd: first client connects systemd --> podman: socket inherited via fork/exec state "OCI runtime" as s2 podman --> conmon: socket inherited via double fork/exec @@ -207,6 +210,18 @@ container then runs with less privileges. When using rootless Podman, network traffic is normally passed through slirp4netns. This comes with a performance penalty. Fortunately, communication over the socket-activated socket does not pass through slirp4netns so it has the same performance characteristics as the normal network on the host. -Note, there is a delay when the first connection is made because the container needs to + +### Starting a socket-activated service + +There is a delay when the first connection is made because the container needs to start up. To minimize this delay, consider passing __--pull=never__ to `podman run` and instead -pull the container image beforehand. +pull the container image beforehand. Instead of waiting for the start of the service to be triggered by the +first client connecting to it, the service can also be explicitly started (`systemctl --user start echo.service`). + +### Stopping a socket-activated service + +Some services run a command (configured by the systemd directive __ExecStart__) that exits after some time of inactivity. +Depending on the restart configuration for the service +(systemd directive [__Restart__](https://www.freedesktop.org/software/systemd/man/systemd.service.html#Restart=)), +it may then be stopped. An example of this is _podman.service_ that stops after some time of inactivity. +The service will be started again when the next client connects to the socket. diff --git a/pkg/domain/filters/containers.go b/pkg/domain/filters/containers.go index f88a165e7..de62b6582 100644 --- a/pkg/domain/filters/containers.go +++ b/pkg/domain/filters/containers.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/containers/common/pkg/filters" cutil "github.com/containers/common/pkg/util" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" @@ -24,7 +25,7 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo case "label": // we have to match that all given labels exits on that container return func(c *libpod.Container) bool { - return util.MatchLabelFilters(filterValues, c.Labels()) + return filters.MatchLabelFilters(filterValues, c.Labels()) }, nil case "name": // we only have to match one name @@ -299,7 +300,11 @@ func GeneratePruneContainerFilterFuncs(filter string, filterValues []string, r * switch filter { case "label": return func(c *libpod.Container) bool { - return util.MatchLabelFilters(filterValues, c.Labels()) + return filters.MatchLabelFilters(filterValues, c.Labels()) + }, nil + case "label!": + return func(c *libpod.Container) bool { + return !filters.MatchLabelFilters(filterValues, c.Labels()) }, nil case "until": return prepareUntilFilterFunc(filterValues) diff --git a/pkg/domain/filters/pods.go b/pkg/domain/filters/pods.go index 78b97db64..7b0944292 100644 --- a/pkg/domain/filters/pods.go +++ b/pkg/domain/filters/pods.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" + "github.com/containers/common/pkg/filters" cutil "github.com/containers/common/pkg/util" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" @@ -115,7 +116,7 @@ func GeneratePodFilterFunc(filter string, filterValues []string, r *libpod.Runti case "label": return func(p *libpod.Pod) bool { labels := p.Labels() - return util.MatchLabelFilters(filterValues, labels) + return filters.MatchLabelFilters(filterValues, labels) }, nil case "until": return func(p *libpod.Pod) bool { diff --git a/pkg/domain/filters/volumes.go b/pkg/domain/filters/volumes.go index 7c5047225..9cec39fbb 100644 --- a/pkg/domain/filters/volumes.go +++ b/pkg/domain/filters/volumes.go @@ -6,6 +6,7 @@ import ( "regexp" "strings" + pruneFilters "github.com/containers/common/pkg/filters" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/pkg/util" ) @@ -36,7 +37,7 @@ func GenerateVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, error) { case "label": filter := val vf = append(vf, func(v *libpod.Volume) bool { - return util.MatchLabelFilters([]string{filter}, v.Labels()) + return pruneFilters.MatchLabelFilters([]string{filter}, v.Labels()) }) case "opt": filterArray := strings.SplitN(val, "=", 2) @@ -100,7 +101,7 @@ func GeneratePruneVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, erro switch filter { case "label": vf = append(vf, func(v *libpod.Volume) bool { - return util.MatchLabelFilters([]string{filterVal}, v.Labels()) + return pruneFilters.MatchLabelFilters([]string{filterVal}, v.Labels()) }) case "until": f, err := createUntilFilterVolumeFunction(filterVal) diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index dd7053a23..ab742fb35 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -260,6 +260,7 @@ func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities. if err != nil { return nil, err } + filterFuncs = append(filterFuncs, generatedFunc) } return ic.Libpod.PruneContainers(filterFuncs) diff --git a/pkg/util/filters.go b/pkg/util/filters.go index 08148806f..104b9c3c2 100644 --- a/pkg/util/filters.go +++ b/pkg/util/filters.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "net/http" - "path/filepath" "strings" "time" @@ -94,35 +93,3 @@ func PrepareFilters(r *http.Request) (*map[string][]string, error) { } return &filterMap, nil } - -func matchPattern(pattern string, value string) bool { - if strings.Contains(pattern, "*") { - filter := fmt.Sprintf("*%s*", pattern) - filter = strings.ReplaceAll(filter, string(filepath.Separator), "|") - newName := strings.ReplaceAll(value, string(filepath.Separator), "|") - match, _ := filepath.Match(filter, newName) - return match - } - return false -} - -// MatchLabelFilters matches labels and returns true if they are valid -func MatchLabelFilters(filterValues []string, labels map[string]string) bool { -outer: - for _, filterValue := range filterValues { - filterArray := strings.SplitN(filterValue, "=", 2) - filterKey := filterArray[0] - if len(filterArray) > 1 { - filterValue = filterArray[1] - } else { - filterValue = "" - } - for labelKey, labelValue := range labels { - if ((labelKey == filterKey) || matchPattern(filterKey, labelKey)) && (filterValue == "" || labelValue == filterValue) { - continue outer - } - } - return false - } - return true -} diff --git a/pkg/util/filters_test.go b/pkg/util/filters_test.go index 47259013e..8e45ea61c 100644 --- a/pkg/util/filters_test.go +++ b/pkg/util/filters_test.go @@ -2,6 +2,8 @@ package util import ( "testing" + + "github.com/containers/common/pkg/filters" ) func TestMatchLabelFilters(t *testing.T) { @@ -71,7 +73,7 @@ func TestMatchLabelFilters(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - if got := MatchLabelFilters(tt.args.filterValues, tt.args.labels); got != tt.want { + if got := filters.MatchLabelFilters(tt.args.filterValues, tt.args.labels); got != tt.want { t.Errorf("MatchLabelFilters() = %v, want %v", got, tt.want) } }) diff --git a/test/e2e/prune_test.go b/test/e2e/prune_test.go index 89cc65540..0b1d68aea 100644 --- a/test/e2e/prune_test.go +++ b/test/e2e/prune_test.go @@ -280,6 +280,24 @@ var _ = Describe("Podman prune", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.OutputToStringArray()).To(HaveLen(0)) + + // Create new network. + session = podmanTest.Podman([]string{"network", "create", "test1", "--label", "foo"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + // Remove all unused networks. + session = podmanTest.Podman([]string{"system", "prune", "-f", "--filter", "label!=foo"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(Equal("Total reclaimed space: 0B")) + + // Unused networks removed. + session = podmanTest.Podman([]string{"network", "ls", "-q", "--filter", "name=^test1$"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + // label should make sure we do not remove this network + Expect(session.OutputToStringArray()).To(HaveLen(1)) }) It("podman system prune - pod,container stopped", func() { |