Skip to content

Commit

Permalink
Split up util package into pkg/password, pkg/detach, pkg/version
Browse files Browse the repository at this point in the history
Fixes: #1654

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
  • Loading branch information
rhatdan committed Sep 19, 2023
1 parent d1d9d38 commit 54cdbd0
Show file tree
Hide file tree
Showing 12 changed files with 220 additions and 197 deletions.
10 changes: 5 additions & 5 deletions libnetwork/cni/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
"github.com/containernetworking/cni/libcni"
"github.com/containers/common/libnetwork/types"
"github.com/containers/common/pkg/config"
cutil "github.com/containers/common/pkg/util"
"github.com/containers/common/pkg/version"
"github.com/containers/storage/pkg/lockfile"
"github.com/containers/storage/pkg/unshare"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -302,8 +302,8 @@ func (n *cniNetwork) NetworkInfo() types.NetworkInfo {
path := ""
packageVersion := ""
for _, p := range n.cniPluginDirs {
ver := cutil.PackageVersion(p)
if ver != cutil.UnknownPackage {
ver := version.Package(p)
if ver != version.UnknownPackage {
path = p
packageVersion = ver
break
Expand All @@ -317,8 +317,8 @@ func (n *cniNetwork) NetworkInfo() types.NetworkInfo {
}

dnsPath := filepath.Join(path, "dnsname")
dnsPackage := cutil.PackageVersion(dnsPath)
dnsProgram, err := cutil.ProgramVersionDnsname(dnsPath)
dnsPackage := version.Package(dnsPath)
dnsProgram, err := version.ProgramDnsname(dnsPath)
if err != nil {
logrus.Infof("Failed to get the dnsname plugin version: %v", err)
}
Expand Down
10 changes: 5 additions & 5 deletions libnetwork/netavark/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"github.com/containers/common/libnetwork/internal/util"
"github.com/containers/common/libnetwork/types"
"github.com/containers/common/pkg/config"
cutil "github.com/containers/common/pkg/util"
"github.com/containers/common/pkg/version"
"github.com/containers/storage/pkg/lockfile"
"github.com/containers/storage/pkg/unshare"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -341,8 +341,8 @@ func (n *netavarkNetwork) DefaultInterfaceName() string {
// package version and program version.
func (n *netavarkNetwork) NetworkInfo() types.NetworkInfo {
path := n.netavarkBinary
packageVersion := cutil.PackageVersion(path)
programVersion, err := cutil.ProgramVersion(path)
packageVersion := version.Package(path)
programVersion, err := version.Program(path)
if err != nil {
logrus.Infof("Failed to get the netavark version: %v", err)
}
Expand All @@ -354,8 +354,8 @@ func (n *netavarkNetwork) NetworkInfo() types.NetworkInfo {
}

dnsPath := n.aardvarkBinary
dnsPackage := cutil.PackageVersion(dnsPath)
dnsProgram, err := cutil.ProgramVersion(dnsPath)
dnsPackage := version.Package(dnsPath)
dnsProgram, err := version.Program(dnsPath)
if err != nil {
logrus.Infof("Failed to get the aardvark version: %v", err)
}
Expand Down
2 changes: 1 addition & 1 deletion libnetwork/util/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func createFilterFuncs(key string, filterValues []string) (types.FilterFunc, err
case "id":
// matches part of one id
return func(net types.Network) bool {
return util.FilterID(net.ID, filterValues)
return filters.FilterID(net.ID, filterValues)
}, nil

// TODO: add dns enabled, internal filter
Expand Down
4 changes: 2 additions & 2 deletions pkg/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"path/filepath"
"strings"

"github.com/containers/common/pkg/util"
passwd "github.com/containers/common/pkg/password"
"github.com/containers/image/v5/docker"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/pkg/docker/config"
Expand Down Expand Up @@ -269,7 +269,7 @@ func getUserAndPass(opts *LoginOptions, password, userFromAuthFile string) (user
}
if password == "" {
fmt.Fprint(opts.Stdout, "Password: ")
pass, err := util.ReadPassword(int(os.Stdin.Fd()))
pass, err := passwd.Read(int(os.Stdin.Fd()))
if err != nil {
return "", "", fmt.Errorf("reading password: %w", err)
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/util/copy.go → pkg/detach/copy.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package util
package detach

import (
"errors"
Expand All @@ -9,8 +9,8 @@ import (
// the user.
var ErrDetach = errors.New("detached from container")

// CopyDetachable is similar to io.Copy but support a detach key sequence to break out.
func CopyDetachable(dst io.Writer, src io.Reader, keys []byte) (written int64, err error) {
// Copy is similar to io.Copy but support a detach key sequence to break out.
func Copy(dst io.Writer, src io.Reader, keys []byte) (written int64, err error) {
buf := make([]byte, 32*1024)
for {
nr, er := src.Read(buf)
Expand Down
21 changes: 21 additions & 0 deletions pkg/filters/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import (
"fmt"
"net/http"
"path/filepath"
"regexp"
"strings"
"time"

"github.com/containers/common/libnetwork/types"
"github.com/containers/common/pkg/timetype"
)

Expand Down Expand Up @@ -134,3 +136,22 @@ func matchPattern(pattern string, value string) bool {
}
return false
}

// FilterID is a function used to compare an id against a set of ids, if the
// input is hex we check if the prefix matches. Otherwise we assume it is a
// regex and try to match that.
// see https://github.com/containers/podman/issues/18471 for why we do this
func FilterID(id string, filters []string) bool {
for _, want := range filters {
isRegex := types.NotHexRegex.MatchString(want)
if isRegex {
match, err := regexp.MatchString(want, id)
if err == nil && match {
return true
}
} else if strings.HasPrefix(id, strings.ToLower(want)) {
return true
}
}
return false
}
57 changes: 57 additions & 0 deletions pkg/password/password_supported.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//go:build linux || darwin || freebsd
// +build linux darwin freebsd

package password

import (
"errors"
"os"
"os/signal"
"syscall"

terminal "golang.org/x/term"
)

var ErrInterrupt = errors.New("interrupted")

// Read reads a password from the terminal without echo.
func Read(fd int) ([]byte, error) {
// Store and restore the terminal status on interruptions to
// avoid that the terminal remains in the password state
// This is necessary as for https://github.com/golang/go/issues/31180

oldState, err := terminal.GetState(fd)
if err != nil {
return make([]byte, 0), err
}

type Buffer struct {
Buffer []byte
Error error
}
errorChannel := make(chan Buffer, 1)

// SIGINT and SIGTERM restore the terminal, otherwise the no-echo mode would remain intact
interruptChannel := make(chan os.Signal, 1)
signal.Notify(interruptChannel, syscall.SIGINT, syscall.SIGTERM)
defer func() {
signal.Stop(interruptChannel)
close(interruptChannel)
}()
go func() {
for range interruptChannel {
if oldState != nil {
_ = terminal.Restore(fd, oldState)
}
errorChannel <- Buffer{Buffer: make([]byte, 0), Error: ErrInterrupt}
}
}()

go func() {
buf, err := terminal.ReadPassword(fd)
errorChannel <- Buffer{Buffer: buf, Error: err}
}()

buf := <-errorChannel
return buf.Buffer, buf.Error
}
21 changes: 21 additions & 0 deletions pkg/password/password_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//go:build windows
// +build windows

package password

import (
terminal "golang.org/x/term"
)

// Read reads a password from the terminal.
func Read(fd int) ([]byte, error) {
oldState, err := terminal.GetState(fd)
if err != nil {
return make([]byte, 0), err
}
buf, err := terminal.ReadPassword(fd)
if oldState != nil {
_ = terminal.Restore(fd, oldState)
}
return buf, err
}
122 changes: 0 additions & 122 deletions pkg/util/util.go
Original file line number Diff line number Diff line change
@@ -1,119 +1,16 @@
package util

import (
"bytes"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"time"

"github.com/containers/common/libnetwork/types"
"github.com/fsnotify/fsnotify"
"github.com/sirupsen/logrus"
)

const (
UnknownPackage = "Unknown"
)

var ErrInterrupt = errors.New("interrupted")

// Note: This function is copied from containers/podman libpod/util.go
// Please see https://github.com/containers/common/pull/1460
func queryPackageVersion(cmdArg ...string) string {
output := UnknownPackage
if 1 < len(cmdArg) {
cmd := exec.Command(cmdArg[0], cmdArg[1:]...)
if outp, err := cmd.Output(); err == nil {
output = string(outp)
deb := false
if cmdArg[0] == "/usr/bin/dlocate" {
// can return multiple matches
l := strings.Split(output, "\n")
output = l[0]
deb = true
} else if cmdArg[0] == "/usr/bin/dpkg" {
deb = true
}
if deb {
r := strings.Split(output, ": ")
queryFormat := `${Package}_${Version}_${Architecture}`
cmd = exec.Command("/usr/bin/dpkg-query", "-f", queryFormat, "-W", r[0])
if outp, err := cmd.Output(); err == nil {
output = string(outp)
}
}
}
if cmdArg[0] == "/sbin/apk" {
prefix := cmdArg[len(cmdArg)-1] + " is owned by "
output = strings.Replace(output, prefix, "", 1)
}
}
return strings.Trim(output, "\n")
}

// Note: This function is copied from containers/podman libpod/util.go
// Please see https://github.com/containers/common/pull/1460
func PackageVersion(program string) string { // program is full path
_, err := os.Stat(program)
if err != nil {
return UnknownPackage
}
packagers := [][]string{
{"/usr/bin/rpm", "-q", "-f"},
{"/usr/bin/dlocate", "-F"}, // Debian, Ubuntu (quick)
{"/usr/bin/dpkg", "-S"}, // Debian, Ubuntu (slow)
{"/usr/bin/pacman", "-Qo"}, // Arch
{"/usr/bin/qfile", "-qv"}, // Gentoo (quick)
{"/usr/bin/equery", "b"}, // Gentoo (slow)
{"/sbin/apk", "info", "-W"}, // Alpine
{"/usr/local/sbin/pkg", "which", "-q"}, // FreeBSD
}

for _, cmd := range packagers {
cmd = append(cmd, program)
if out := queryPackageVersion(cmd...); out != UnknownPackage {
return out
}
}
return UnknownPackage
}

// Note: This function is copied from containers/podman libpod/util.go
// Please see https://github.com/containers/common/pull/1460
func ProgramVersion(program string) (string, error) {
return programVersion(program, false)
}

func ProgramVersionDnsname(program string) (string, error) {
return programVersion(program, true)
}

func programVersion(program string, dnsname bool) (string, error) {
cmd := exec.Command(program, "--version")
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr

err := cmd.Run()
if err != nil {
return "", fmt.Errorf("`%v --version` failed: %v %v (%v)", program, stderr.String(), stdout.String(), err)
}

output := strings.TrimSuffix(stdout.String(), "\n")
// dnsname --version returns the information to stderr
if dnsname {
output = strings.TrimSuffix(stderr.String(), "\n")
}

return output, nil
}

// StringInSlice determines if a string is in a string slice, returns bool
func StringInSlice(s string, sl []string) bool {
for _, i := range sl {
Expand All @@ -135,25 +32,6 @@ func StringMatchRegexSlice(s string, re []string) bool {
return false
}

// FilterID is a function used to compare an id against a set of ids, if the
// input is hex we check if the prefix matches. Otherwise we assume it is a
// regex and try to match that.
// see https://github.com/containers/podman/issues/18471 for why we do this
func FilterID(id string, filters []string) bool {
for _, want := range filters {
isRegex := types.NotHexRegex.MatchString(want)
if isRegex {
match, err := regexp.MatchString(want, id)
if err == nil && match {
return true
}
} else if strings.HasPrefix(id, strings.ToLower(want)) {
return true
}
}
return false
}

// WaitForFile waits until a file has been created or the given timeout has occurred
func WaitForFile(path string, chWait chan error, timeout time.Duration) (bool, error) {
var inotifyEvents chan fsnotify.Event
Expand Down
Loading

0 comments on commit 54cdbd0

Please sign in to comment.