Resurrect auto-port reassignment, but for all providers

- Updates common to pull in new locked edit

[NO NEW TESTS NEEDED]

Signed-off-by: Jason T. Greene <jason.greene@redhat.com>
This commit is contained in:
Jason T. Greene
2024-03-03 17:20:52 -06:00
parent ef7727238a
commit 6272abbbb8
20 changed files with 312 additions and 303 deletions

2
go.mod
View File

@ -11,7 +11,7 @@ require (
github.com/checkpoint-restore/go-criu/v7 v7.0.0
github.com/containernetworking/plugins v1.4.0
github.com/containers/buildah v1.34.1-0.20240229193131-f5d7689ef4cd
github.com/containers/common v0.57.1-0.20240229165734-cec09922602e
github.com/containers/common v0.57.1-0.20240304165751-a0d555c70d52
github.com/containers/conmon v2.0.20+incompatible
github.com/containers/gvisor-tap-vsock v0.7.3
github.com/containers/image/v5 v5.29.3-0.20240229213915-cdc68020a24f

4
go.sum
View File

@ -76,8 +76,8 @@ github.com/containernetworking/plugins v1.4.0 h1:+w22VPYgk7nQHw7KT92lsRmuToHvb7w
github.com/containernetworking/plugins v1.4.0/go.mod h1:UYhcOyjefnrQvKvmmyEKsUA+M9Nfn7tqULPpH0Pkcj0=
github.com/containers/buildah v1.34.1-0.20240229193131-f5d7689ef4cd h1:4cHNzaywyyJsCAtwUKMZm8r/wqm/WuNC70GfnI3kh18=
github.com/containers/buildah v1.34.1-0.20240229193131-f5d7689ef4cd/go.mod h1:3fn5edBIPpIOPJYdnxBdTF7bjnBHhqZwYK5a6ApNdyk=
github.com/containers/common v0.57.1-0.20240229165734-cec09922602e h1:TPgCd6bWFyliJxCXEiCI1LnbB3kBUkpx1dw51ngDjWI=
github.com/containers/common v0.57.1-0.20240229165734-cec09922602e/go.mod h1:8irlyBcVooYx0F+YmoY7PQPAIgdJvCj17bvL7PqeaxI=
github.com/containers/common v0.57.1-0.20240304165751-a0d555c70d52 h1:+rq1qOOEv/2Sa1A9Tmv7yKuOzea8W2n6kFUH+bon61Y=
github.com/containers/common v0.57.1-0.20240304165751-a0d555c70d52/go.mod h1:h92alKzSekxVC+VDaX4gt7RJpXvJKF79a9TnULZ5ZDc=
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/gvisor-tap-vsock v0.7.3 h1:yORnf15sP+sLFhxLNLgmB5/lOhldn9dRMHx/tmYtSOQ=

View File

@ -313,6 +313,11 @@ func (a AppleHVStubber) StopHostNetworking(_ *vmconfigs.MachineConfig, _ define.
return nil
}
func (a AppleHVStubber) UpdateSSHPort(mc *vmconfigs.MachineConfig, port int) error {
// managed by gvproxy on this backend, so nothing to do
return nil
}
func (a AppleHVStubber) VMType() define.VMType {
return define.AppleHvVirt
}

View File

@ -1,3 +1,5 @@
//go:build amd64 || arm64
package connection
import (
@ -14,19 +16,8 @@ func AddSSHConnectionsToPodmanSocket(uid, port int, identityPath, name, remoteUs
fmt.Println("An ignition path was provided. No SSH connection was added to Podman")
return nil
}
uri := makeSSHURL(LocalhostIP, fmt.Sprintf("/run/user/%d/podman/podman.sock", uid), strconv.Itoa(port), remoteUsername)
uriRoot := makeSSHURL(LocalhostIP, "/run/podman/podman.sock", strconv.Itoa(port), "root")
cons := []connection{
{
name: name,
uri: uri,
},
{
name: name + "-root",
uri: uriRoot,
},
}
cons := createConnections(name, uid, port, remoteUsername)
// The first connection defined when connections is empty will become the default
// regardless of IsDefault, so order according to rootful
@ -36,3 +27,19 @@ func AddSSHConnectionsToPodmanSocket(uid, port int, identityPath, name, remoteUs
return addConnection(cons, identityPath, opts.IsDefault)
}
func createConnections(name string, uid, port int, remoteUsername string) []connection {
uri := makeSSHURL(LocalhostIP, fmt.Sprintf("/run/user/%d/podman/podman.sock", uid), strconv.Itoa(port), remoteUsername)
uriRoot := makeSSHURL(LocalhostIP, "/run/podman/podman.sock", strconv.Itoa(port), "root")
return []connection{
{
name: name,
uri: uri,
},
{
name: name + "-root",
uri: uriRoot,
},
}
}

View File

@ -55,14 +55,17 @@ func addConnection(cons []connection, identity string, isDefault bool) error {
})
}
func ChangeConnectionURI(name string, uri fmt.Stringer) error {
func UpdateConnectionPairPort(name string, port, uid int, remoteUsername string, identityPath string) error {
cons := createConnections(name, uid, port, remoteUsername)
return config.EditConnectionConfig(func(cfg *config.ConnectionsFile) error {
dst, ok := cfg.Connection.Connections[name]
if !ok {
return errors.New("connection not found")
for _, con := range cons {
dst := config.Destination{
IsMachine: true,
URI: con.uri.String(),
Identity: identityPath,
}
cfg.Connection.Connections[name] = dst
}
dst.URI = uri.String()
cfg.Connection.Connections[name] = dst
return nil
})

View File

@ -446,6 +446,11 @@ func (h HyperVStubber) PostStartNetworking(mc *vmconfigs.MachineConfig, noInfo b
return err
}
func (h HyperVStubber) UpdateSSHPort(mc *vmconfigs.MachineConfig, port int) error {
// managed by gvproxy on this backend, so nothing to do
return nil
}
func (h HyperVStubber) GetDisk(userInputPath string, dirs *define.MachineDirs, mc *vmconfigs.MachineConfig) error {
return diskpull.GetDisk(userInputPath, dirs, mc.ImagePath, h.VMType(), mc.Name)
}

View File

@ -1,3 +1,5 @@
//go:build amd64 || arm64
package ignition
import (

View File

@ -1,4 +1,4 @@
//go:build !darwin
//go:build linux || freebsd
package qemu

View File

@ -1,4 +1,4 @@
//go:build windows && amd64
//go:build tempoff
package qemu

View File

@ -1,4 +1,4 @@
//go:build !darwin
//go:build linux || freebsd
package qemu
@ -352,6 +352,11 @@ func (q *QEMUStubber) PostStartNetworking(mc *vmconfigs.MachineConfig, noInfo bo
return nil
}
func (q *QEMUStubber) UpdateSSHPort(mc *vmconfigs.MachineConfig, port int) error {
// managed by gvproxy on this backend, so nothing to do
return nil
}
func (q *QEMUStubber) GetDisk(userInputPath string, dirs *define.MachineDirs, mc *vmconfigs.MachineConfig) error {
return diskpull.GetDisk(userInputPath, dirs, mc.ImagePath, q.VMType(), mc.Name)
}

View File

@ -11,8 +11,10 @@ import (
"github.com/containers/common/pkg/config"
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
"github.com/containers/podman/v5/pkg/machine"
"github.com/containers/podman/v5/pkg/machine/connection"
"github.com/containers/podman/v5/pkg/machine/define"
"github.com/containers/podman/v5/pkg/machine/env"
"github.com/containers/podman/v5/pkg/machine/ports"
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
"github.com/sirupsen/logrus"
)
@ -91,6 +93,14 @@ func startHostForwarder(mc *vmconfigs.MachineConfig, provider vmconfigs.VMProvid
}
func startNetworking(mc *vmconfigs.MachineConfig, provider vmconfigs.VMProvider) (string, machine.APIForwardingState, error) {
// Check if SSH port is in use, and reassign if necessary
if !ports.IsLocalPortAvailable(mc.SSH.Port) {
logrus.Warnf("detected port conflict on machine ssh port [%d], reassigning", mc.SSH.Port)
if err := reassignSSHPort(mc, provider); err != nil {
return "", 0, err
}
}
// Provider has its own networking code path (e.g. WSL)
if provider.UseProviderNetworkSetup() {
return "", 0, provider.StartNetworking(mc, nil)
@ -153,6 +163,53 @@ func conductVMReadinessCheck(mc *vmconfigs.MachineConfig, maxBackoffs int, backo
return
}
func reassignSSHPort(mc *vmconfigs.MachineConfig, provider vmconfigs.VMProvider) error {
newPort, err := ports.AllocateMachinePort()
if err != nil {
return err
}
success := false
defer func() {
if !success {
if err := ports.ReleaseMachinePort(newPort); err != nil {
logrus.Warnf("could not release port allocation as part of failure rollback (%d): %s", newPort, err.Error())
}
}
}()
// Write a transient invalid port, to force a retry on failure
oldPort := mc.SSH.Port
mc.SSH.Port = 0
if err := mc.Write(); err != nil {
return err
}
if err := ports.ReleaseMachinePort(oldPort); err != nil {
logrus.Warnf("could not release current ssh port allocation (%d): %s", oldPort, err.Error())
}
// Update the backend's settings if relevant (e.g. WSL)
if err := provider.UpdateSSHPort(mc, newPort); err != nil {
return err
}
mc.SSH.Port = newPort
if err := connection.UpdateConnectionPairPort(mc.Name, newPort, mc.HostUser.UID, mc.SSH.RemoteUsername, mc.SSH.IdentityPath); err != nil {
return fmt.Errorf("could not update remote connection configuration: %w", err)
}
// Write updated port back
if err := mc.Write(); err != nil {
return err
}
// inform defer routine not to release the port
success = true
return nil
}
func isListening(port int) bool {
// Check if we can dial it
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", "127.0.0.1", port), 10*time.Millisecond)

View File

@ -96,6 +96,7 @@ type VMProvider interface { //nolint:interfacebloat
UserModeNetworkEnabled(mc *MachineConfig) bool
UseProviderNetworkSetup() bool
RequireExclusiveActive() bool
UpdateSSHPort(mc *MachineConfig, port int) error
}
// HostUser describes the host user

View File

@ -15,7 +15,7 @@ import (
"github.com/containers/podman/v5/pkg/machine/connection"
"github.com/containers/podman/v5/pkg/machine/define"
"github.com/containers/podman/v5/pkg/machine/lock"
"github.com/containers/podman/v5/utils"
"github.com/containers/podman/v5/pkg/machine/ports"
"github.com/containers/storage/pkg/ioutils"
"github.com/sirupsen/logrus"
)
@ -78,8 +78,7 @@ func NewMachineConfig(opts define.InitOptions, dirs *define.MachineDirs, sshIden
}
mc.Resources = mrc
// TODO WSL had a locking port mechanism, we should consider this.
sshPort, err := utils.GetRandomPort()
sshPort, err := ports.AllocateMachinePort()
if err != nil {
return nil, err
}
@ -204,6 +203,11 @@ func (mc *MachineConfig) Remove(saveIgnition, saveImage bool) ([]string, func()
if err := mc.configPath.Delete(); err != nil {
errs = append(errs, err)
}
if err := ports.ReleaseMachinePort(mc.SSH.Port); err != nil {
errs = append(errs, err)
}
return errorhandling.JoinErrors(errs)
}

View File

@ -11,7 +11,6 @@ import (
"strings"
"github.com/containers/podman/v5/pkg/machine/ocipull"
"github.com/containers/podman/v5/pkg/machine/ports"
"github.com/containers/podman/v5/pkg/machine/shim/diskpull"
"github.com/containers/podman/v5/pkg/machine/stdpull"
"github.com/containers/podman/v5/pkg/machine/wsl/wutil"
@ -111,7 +110,7 @@ func (w WSLStubber) Remove(mc *vmconfigs.MachineConfig) ([]string, func() error,
if err := runCmdPassThrough(wutil.FindWSL(), "--unregister", machine.ToDist(mc.Name)); err != nil {
logrus.Error(err)
}
return ports.ReleaseMachinePort(mc.SSH.Port)
return nil
}
return []string{}, wslRemoveFunc, nil
@ -205,15 +204,6 @@ func (w WSLStubber) PostStartNetworking(mc *vmconfigs.MachineConfig, noInfo bool
func (w WSLStubber) StartVM(mc *vmconfigs.MachineConfig) (func() error, func() error, error) {
dist := machine.ToDist(mc.Name)
// TODO The original code checked to see if the SSH port was actually open and re-assigned if it was
// we could consider this but it should be higher up the stack
// if !machine.IsLocalPortAvailable(v.Port) {
// logrus.Warnf("SSH port conflict detected, reassigning a new port")
// if err := v.reassignSshPort(); err != nil {
// return err
// }
// }
err := wslInvoke(dist, "/root/bootstrap")
if err != nil {
err = fmt.Errorf("the WSL bootstrap script failed: %w", err)
@ -279,6 +269,16 @@ func (w WSLStubber) StopHostNetworking(mc *vmconfigs.MachineConfig, vmType defin
return stopUserModeNetworking(mc)
}
func (w WSLStubber) UpdateSSHPort(mc *vmconfigs.MachineConfig, port int) error {
dist := machine.ToDist(mc.Name)
if err := wslInvoke(dist, "sh", "-c", fmt.Sprintf(changePort, port)); err != nil {
return fmt.Errorf("could not change SSH port for guest OS: %w", err)
}
return nil
}
func (w WSLStubber) VMType() define.VMType {
return define.WSLVirt
}

View File

@ -13,6 +13,9 @@ import (
"path/filepath"
"strconv"
"strings"
"sync"
"syscall"
"time"
"github.com/containers/storage/pkg/unshare"
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
@ -22,6 +25,7 @@ import (
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/sirupsen/logrus"
"golang.org/x/exp/maps"
"golang.org/x/sys/unix"
)
var (
@ -30,6 +34,10 @@ var (
// ErrCgroupV1Rootless means the cgroup v1 were attempted to be used in rootless environment
ErrCgroupV1Rootless = errors.New("no support for CGroups V1 in rootless environments")
ErrStatCgroup = errors.New("no cgroup available for gathering user statistics")
isUnifiedOnce sync.Once
isUnified bool
isUnifiedErr error
)
// CgroupControl controls a cgroup hierarchy
@ -731,3 +739,139 @@ func SystemCPUUsage() (uint64, error) {
}
return total, nil
}
// IsCgroup2UnifiedMode returns whether we are running in cgroup 2 cgroup2 mode.
func IsCgroup2UnifiedMode() (bool, error) {
isUnifiedOnce.Do(func() {
var st syscall.Statfs_t
if err := syscall.Statfs("/sys/fs/cgroup", &st); err != nil {
isUnified, isUnifiedErr = false, err
} else {
isUnified, isUnifiedErr = st.Type == unix.CGROUP2_SUPER_MAGIC, nil
}
})
return isUnified, isUnifiedErr
}
// UserConnection returns an user connection to D-BUS
func UserConnection(uid int) (*systemdDbus.Conn, error) {
return systemdDbus.NewConnection(func() (*dbus.Conn, error) {
return dbusAuthConnection(uid, dbus.SessionBusPrivateNoAutoStartup)
})
}
// UserOwnsCurrentSystemdCgroup checks whether the current EUID owns the
// current cgroup.
func UserOwnsCurrentSystemdCgroup() (bool, error) {
uid := os.Geteuid()
cgroup2, err := IsCgroup2UnifiedMode()
if err != nil {
return false, err
}
f, err := os.Open("/proc/self/cgroup")
if err != nil {
return false, err
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
parts := strings.SplitN(line, ":", 3)
if len(parts) < 3 {
continue
}
var cgroupPath string
if cgroup2 {
cgroupPath = filepath.Join(cgroupRoot, parts[2])
} else {
if parts[1] != "name=systemd" {
continue
}
cgroupPath = filepath.Join(cgroupRoot, "systemd", parts[2])
}
st, err := os.Stat(cgroupPath)
if err != nil {
return false, err
}
s := st.Sys()
if s == nil {
return false, fmt.Errorf("stat cgroup path %s", cgroupPath)
}
if int(s.(*syscall.Stat_t).Uid) != uid {
return false, nil
}
}
if err := scanner.Err(); err != nil {
return false, fmt.Errorf("parsing file /proc/self/cgroup: %w", err)
}
return true, nil
}
// rmDirRecursively delete recursively a cgroup directory.
// It differs from os.RemoveAll as it doesn't attempt to unlink files.
// On cgroupfs we are allowed only to rmdir empty directories.
func rmDirRecursively(path string) error {
killProcesses := func(signal syscall.Signal) {
if signal == unix.SIGKILL {
if err := os.WriteFile(filepath.Join(path, "cgroup.kill"), []byte("1"), 0o600); err == nil {
return
}
}
// kill all the processes that are still part of the cgroup
if procs, err := os.ReadFile(filepath.Join(path, "cgroup.procs")); err == nil {
for _, pidS := range strings.Split(string(procs), "\n") {
if pid, err := strconv.Atoi(pidS); err == nil {
_ = unix.Kill(pid, signal)
}
}
}
}
if err := os.Remove(path); err == nil || errors.Is(err, os.ErrNotExist) {
return nil
}
entries, err := os.ReadDir(path)
if err != nil {
return err
}
for _, i := range entries {
if i.IsDir() {
if err := rmDirRecursively(filepath.Join(path, i.Name())); err != nil {
return err
}
}
}
attempts := 0
for {
err := os.Remove(path)
if err == nil || errors.Is(err, os.ErrNotExist) {
return nil
}
if errors.Is(err, unix.EBUSY) {
// send a SIGTERM after 3 second
if attempts == 300 {
killProcesses(unix.SIGTERM)
}
// send SIGKILL after 8 seconds
if attempts == 800 {
killProcesses(unix.SIGKILL)
}
// give up after 10 seconds
if attempts < 1000 {
time.Sleep(time.Millisecond * 10)
attempts++
continue
}
}
return fmt.Errorf("remove %s: %w", path, err)
}
}

View File

@ -1,162 +0,0 @@
//go:build linux
package cgroups
import (
"bufio"
"errors"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"syscall"
"time"
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
"github.com/godbus/dbus/v5"
"golang.org/x/sys/unix"
)
var (
isUnifiedOnce sync.Once
isUnified bool
isUnifiedErr error
)
// IsCgroup2UnifiedMode returns whether we are running in cgroup 2 cgroup2 mode.
func IsCgroup2UnifiedMode() (bool, error) {
isUnifiedOnce.Do(func() {
var st syscall.Statfs_t
if err := syscall.Statfs("/sys/fs/cgroup", &st); err != nil {
isUnified, isUnifiedErr = false, err
} else {
isUnified, isUnifiedErr = st.Type == unix.CGROUP2_SUPER_MAGIC, nil
}
})
return isUnified, isUnifiedErr
}
// UserConnection returns an user connection to D-BUS
func UserConnection(uid int) (*systemdDbus.Conn, error) {
return systemdDbus.NewConnection(func() (*dbus.Conn, error) {
return dbusAuthConnection(uid, dbus.SessionBusPrivateNoAutoStartup)
})
}
// UserOwnsCurrentSystemdCgroup checks whether the current EUID owns the
// current cgroup.
func UserOwnsCurrentSystemdCgroup() (bool, error) {
uid := os.Geteuid()
cgroup2, err := IsCgroup2UnifiedMode()
if err != nil {
return false, err
}
f, err := os.Open("/proc/self/cgroup")
if err != nil {
return false, err
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
parts := strings.SplitN(line, ":", 3)
if len(parts) < 3 {
continue
}
var cgroupPath string
if cgroup2 {
cgroupPath = filepath.Join(cgroupRoot, parts[2])
} else {
if parts[1] != "name=systemd" {
continue
}
cgroupPath = filepath.Join(cgroupRoot, "systemd", parts[2])
}
st, err := os.Stat(cgroupPath)
if err != nil {
return false, err
}
s := st.Sys()
if s == nil {
return false, fmt.Errorf("stat cgroup path %s", cgroupPath)
}
if int(s.(*syscall.Stat_t).Uid) != uid {
return false, nil
}
}
if err := scanner.Err(); err != nil {
return false, fmt.Errorf("parsing file /proc/self/cgroup: %w", err)
}
return true, nil
}
// rmDirRecursively delete recursively a cgroup directory.
// It differs from os.RemoveAll as it doesn't attempt to unlink files.
// On cgroupfs we are allowed only to rmdir empty directories.
func rmDirRecursively(path string) error {
killProcesses := func(signal syscall.Signal) {
if signal == unix.SIGKILL {
if err := os.WriteFile(filepath.Join(path, "cgroup.kill"), []byte("1"), 0o600); err == nil {
return
}
}
// kill all the processes that are still part of the cgroup
if procs, err := os.ReadFile(filepath.Join(path, "cgroup.procs")); err == nil {
for _, pidS := range strings.Split(string(procs), "\n") {
if pid, err := strconv.Atoi(pidS); err == nil {
_ = unix.Kill(pid, signal)
}
}
}
}
if err := os.Remove(path); err == nil || errors.Is(err, os.ErrNotExist) {
return nil
}
entries, err := os.ReadDir(path)
if err != nil {
return err
}
for _, i := range entries {
if i.IsDir() {
if err := rmDirRecursively(filepath.Join(path, i.Name())); err != nil {
return err
}
}
}
attempts := 0
for {
err := os.Remove(path)
if err == nil || errors.Is(err, os.ErrNotExist) {
return nil
}
if errors.Is(err, unix.EBUSY) {
// send a SIGTERM after 3 second
if attempts == 300 {
killProcesses(unix.SIGTERM)
}
// send SIGKILL after 8 seconds
if attempts == 800 {
killProcesses(unix.SIGKILL)
}
// give up after 10 seconds
if attempts < 1000 {
time.Sleep(time.Millisecond * 10)
attempts++
continue
}
}
return fmt.Errorf("remove %s: %w", path, err)
}
}

View File

@ -3,10 +3,7 @@
package cgroups
import (
"fmt"
"os"
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
)
// IsCgroup2UnifiedMode returns whether we are running in cgroup 2 cgroup2 mode.
@ -23,8 +20,3 @@ func UserOwnsCurrentSystemdCgroup() (bool, error) {
func rmDirRecursively(path string) error {
return os.RemoveAll(path)
}
// UserConnection returns an user connection to D-BUS
func UserConnection(uid int) (*systemdDbus.Conn, error) {
return nil, fmt.Errorf("systemd d-bus is not supported on this platform")
}

View File

@ -1,80 +0,0 @@
//go:build !linux
package cgroups
import (
"context"
"fmt"
"path/filepath"
"strings"
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
"github.com/godbus/dbus/v5"
)
func systemdCreate(path string, c *systemdDbus.Conn) error {
slice, name := filepath.Split(path)
slice = strings.TrimSuffix(slice, "/")
var lastError error
for i := 0; i < 2; i++ {
properties := []systemdDbus.Property{
systemdDbus.PropDescription(fmt.Sprintf("cgroup %s", name)),
systemdDbus.PropWants(slice),
}
pMap := map[string]bool{
"DefaultDependencies": false,
"MemoryAccounting": true,
"CPUAccounting": true,
"BlockIOAccounting": true,
}
if i == 0 {
pMap["Delegate"] = true
}
for k, v := range pMap {
p := systemdDbus.Property{
Name: k,
Value: dbus.MakeVariant(v),
}
properties = append(properties, p)
}
ch := make(chan string)
_, err := c.StartTransientUnitContext(context.TODO(), name, "replace", properties, ch)
if err != nil {
lastError = err
continue
}
<-ch
return nil
}
return lastError
}
/*
systemdDestroyConn is copied from containerd/cgroups/systemd.go file, that
has the following license:
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
func systemdDestroyConn(path string, c *systemdDbus.Conn) error {
name := filepath.Base(path)
ch := make(chan string)
_, err := c.StopUnitContext(context.TODO(), name, "replace", ch)
if err != nil {
return err
}
<-ch
return nil
}

View File

@ -9,6 +9,7 @@ import (
"path/filepath"
"github.com/containers/storage/pkg/ioutils"
"github.com/containers/storage/pkg/lockfile"
)
const connectionsFile = "podman-connections.json"
@ -64,28 +65,24 @@ type Farm struct {
ReadWrite bool
}
func readConnectionConf() (*ConnectionsFile, string, error) {
path, err := connectionsConfigFile()
if err != nil {
return nil, "", err
}
func readConnectionConf(path string) (*ConnectionsFile, error) {
conf := new(ConnectionsFile)
f, err := os.Open(path)
if err != nil {
// return empty config if file does not exists
if errors.Is(err, fs.ErrNotExist) {
return conf, path, nil
return conf, nil
}
return nil, "", err
return nil, err
}
defer f.Close()
err = json.NewDecoder(f).Decode(conf)
if err != nil {
return nil, "", fmt.Errorf("parse %q: %w", path, err)
return nil, fmt.Errorf("parse %q: %w", path, err)
}
return conf, path, nil
return conf, nil
}
func writeConnectionConf(path string, conf *ConnectionsFile) error {
@ -113,7 +110,20 @@ func writeConnectionConf(path string, conf *ConnectionsFile) error {
// The function will read and write the file automatically and the
// callback function just needs to modify the cfg as needed.
func EditConnectionConfig(callback func(cfg *ConnectionsFile) error) error {
conf, path, err := readConnectionConf()
path, err := connectionsConfigFile()
if err != nil {
return err
}
lockPath := path + ".lock"
lock, err := lockfile.GetLockFile(lockPath)
if err != nil {
return fmt.Errorf("obtain lock file: %w", err)
}
lock.Lock()
defer lock.Unlock()
conf, err := readConnectionConf(path)
if err != nil {
return fmt.Errorf("read connections file: %w", err)
}
@ -139,7 +149,11 @@ func makeConnection(name string, dst Destination, def, readWrite bool) *Connecti
// GetConnection return the connection for the given name or if def is set to true then return the default connection.
func (c *Config) GetConnection(name string, def bool) (*Connection, error) {
conConf, _, err := readConnectionConf()
path, err := connectionsConfigFile()
if err != nil {
return nil, err
}
conConf, err := readConnectionConf(path)
if err != nil {
return nil, err
}
@ -167,7 +181,11 @@ func (c *Config) GetConnection(name string, def bool) (*Connection, error) {
// GetAllConnections return all configured connections
func (c *Config) GetAllConnections() ([]Connection, error) {
conConf, _, err := readConnectionConf()
path, err := connectionsConfigFile()
if err != nil {
return nil, err
}
conConf, err := readConnectionConf(path)
if err != nil {
return nil, err
}
@ -222,7 +240,11 @@ func (c *Config) GetDefaultFarmConnections() (string, []Connection, error) {
// if def is true it will use the default farm instead of the name.
// Returns the name of the farm and the connections for it.
func (c *Config) getFarmConnections(name string, def bool) (string, []Connection, error) {
conConf, _, err := readConnectionConf()
path, err := connectionsConfigFile()
if err != nil {
return "", nil, err
}
conConf, err := readConnectionConf(path)
if err != nil {
return "", nil, err
}
@ -259,7 +281,11 @@ func makeFarm(name string, cons []string, def, readWrite bool) Farm {
// GetAllFarms returns all configured farms
func (c *Config) GetAllFarms() ([]Farm, error) {
conConf, _, err := readConnectionConf()
path, err := connectionsConfigFile()
if err != nil {
return nil, err
}
conConf, err := readConnectionConf(path)
if err != nil {
return nil, err
}

2
vendor/modules.txt vendored
View File

@ -171,7 +171,7 @@ github.com/containers/buildah/pkg/sshagent
github.com/containers/buildah/pkg/util
github.com/containers/buildah/pkg/volumes
github.com/containers/buildah/util
# github.com/containers/common v0.57.1-0.20240229165734-cec09922602e
# github.com/containers/common v0.57.1-0.20240304165751-a0d555c70d52
## explicit; go 1.20
github.com/containers/common/internal
github.com/containers/common/internal/attributedstring