Add bridge support, for the varlink connection

Read the $PODMAN_VARLINK_BRIDGE environment variable
(normally looks like: "ssh user@host varlink bridge")

Also respect $PODMAN_VARLINK_ADDRESS as an override,
if using a different podman socket than the default.

Signed-off-by: Anders F Björklund <anders.f.bjorklund@gmail.com>
This commit is contained in:
Anders F Björklund 2019-01-11 23:28:20 +01:00
parent f31fdb2219
commit c90e3e7fe5
11 changed files with 522 additions and 66 deletions

View file

@ -3,12 +3,32 @@
package adapter
import (
"os"
"github.com/sirupsen/logrus"
"github.com/varlink/go/varlink"
)
// DefaultAddress is the default address of the varlink socket
const DefaultAddress = "unix:/run/podman/io.podman"
// Connect provides a varlink connection
func (r RemoteRuntime) Connect() (*varlink.Connection, error) {
connection, err := varlink.NewConnection("unix:/run/podman/io.podman")
var err error
var connection *varlink.Connection
if bridge := os.Getenv("PODMAN_VARLINK_BRIDGE"); bridge != "" {
logrus.Infof("Connecting with varlink bridge")
logrus.Debugf("%s", bridge)
connection, err = varlink.NewBridge(bridge)
} else {
address := os.Getenv("PODMAN_VARLINK_ADDRESS")
if address == "" {
address = DefaultAddress
}
logrus.Infof("Connecting with varlink address")
logrus.Debugf("%s", address)
connection, err = varlink.NewConnection(address)
}
if err != nil {
return nil, err
}

View file

@ -90,7 +90,7 @@ k8s.io/api kubernetes-1.10.13-beta.0 https://github.com/kubernetes/api
k8s.io/apimachinery kubernetes-1.10.13-beta.0 https://github.com/kubernetes/apimachinery
k8s.io/client-go kubernetes-1.10.13-beta.0 https://github.com/kubernetes/client-go
github.com/mrunalp/fileutils 7d4729fb36185a7c1719923406c9d40e54fb93c7
github.com/varlink/go e9fdc57f40123518ac513eb3443e50625ad6b434
github.com/varlink/go 92687ab4eb68d99e43b1f5b93477ad76bb54f811
github.com/containers/buildah e7ca330f923701dba8859f5c014d0a9a3f7f0a49
# TODO: Gotty has not been updated since 2012. Can we find replacement?
github.com/Nvveen/Gotty cd527374f1e5bff4938207604a14f2e38a9cf512

View file

@ -1 +1,2 @@
/cmd/varlink-go-certification/orgvarlinkcertification/orgvarlinkcertification.go
/.idea

View file

@ -1,11 +1,13 @@
language: go
sudo: false
go:
- '1.9'
- 1.10.x
- '1.10'
- 1.11.x
install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
- go get github.com/TylerBrock/colorjson
- go get github.com/fatih/color
script:
- go generate ./...
- '"$HOME/gopath/bin/goveralls" -v -show -service=travis-ci -repotoken "$COVERALLS_TOKEN"'

295
vendor/github.com/varlink/go/cmd/varlink/main.go generated vendored Normal file
View file

@ -0,0 +1,295 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"github.com/TylerBrock/colorjson"
"github.com/fatih/color"
"github.com/varlink/go/varlink"
"os"
"strings"
)
var bold = color.New(color.Bold)
var errorBoldRed = bold.Sprint(color.New(color.FgRed).Sprint("Error:"))
var bridge string
func ErrPrintf(format string, a ...interface{}) {
fmt.Fprintf(os.Stderr, "%s ", errorBoldRed)
fmt.Fprintf(os.Stderr, format, a...)
}
func print_usage(set *flag.FlagSet, arg_help string) {
if set == nil {
fmt.Fprintf(os.Stderr, "Usage: %s [GLOBAL OPTIONS] COMMAND ...\n", os.Args[0])
} else {
fmt.Fprintf(os.Stderr, "Usage: %s [GLOBAL OPTIONS] %s [OPTIONS] %s\n", os.Args[0], set.Name(), arg_help)
}
fmt.Fprintln(os.Stderr, "\nGlobal Options:")
flag.PrintDefaults()
if set == nil {
fmt.Fprintln(os.Stderr, "\nCommands:")
fmt.Fprintln(os.Stderr, " info\tPrint information about a service")
fmt.Fprintln(os.Stderr, " help\tPrint interface description or service information")
fmt.Fprintln(os.Stderr, " call\tCall a method")
} else {
fmt.Fprintln(os.Stderr, "\nOptions:")
set.PrintDefaults()
}
os.Exit(1)
}
func varlink_call(args []string) {
var err error
var oneway bool
callFlags := flag.NewFlagSet("help", flag.ExitOnError)
callFlags.BoolVar(&oneway, "-oneway", false, "Use bridge for connection")
var help bool
callFlags.BoolVar(&help, "help", false, "Prints help information")
var usage = func() { print_usage(callFlags, "<[ADDRESS/]INTERFACE.METHOD> [ARGUMENTS]") }
callFlags.Usage = usage
_ = callFlags.Parse(args)
if help {
usage()
}
var con *varlink.Connection
var address string
var methodName string
if len(bridge) != 0 {
con, err = varlink.NewBridge(bridge)
if err != nil {
ErrPrintf("Cannot connect with bridge '%s': %v\n", bridge, err)
os.Exit(2)
}
address = "bridge:" + bridge
methodName = callFlags.Arg(0)
} else {
uri := callFlags.Arg(0)
if uri == "" {
usage()
}
li := strings.LastIndex(uri, "/")
if li == -1 {
ErrPrintf("Invalid address '%s'\n", uri)
os.Exit(2)
}
address = uri[:li]
methodName = uri[li+1:]
con, err = varlink.NewConnection(address)
if err != nil {
ErrPrintf("Cannot connect to '%s': %v\n", address, err)
os.Exit(2)
}
}
var parameters string
var params json.RawMessage
parameters = callFlags.Arg(1)
if parameters == "" {
params = nil
} else {
json.Unmarshal([]byte(parameters), &params)
}
var flags uint64
flags = 0
if oneway {
flags |= varlink.Oneway
}
recv, err := con.Send(methodName, params, flags)
var retval map[string]interface{}
// FIXME: Use cont
_, err = recv(&retval)
f := colorjson.NewFormatter()
f.Indent = 2
f.KeyColor = color.New(color.FgCyan)
f.StringColor = color.New(color.FgMagenta)
f.NumberColor = color.New(color.FgMagenta)
f.BoolColor = color.New(color.FgMagenta)
f.NullColor = color.New(color.FgMagenta)
if err != nil {
ErrPrintf("Error calling '%s': %v\n", methodName, err)
switch e := err.(type) {
case *varlink.Error:
println(e.Name)
errorRawParameters := e.Parameters.(*json.RawMessage)
if errorRawParameters == nil {
break
}
var param map[string]interface{}
_ = json.Unmarshal(*errorRawParameters, &param)
c, _ := f.Marshal(param)
ErrPrintf("%v\n", string(c))
}
os.Exit(2)
}
c, _ := f.Marshal(retval)
fmt.Println(string(c))
}
func varlink_help(args []string) {
var err error
helpFlags := flag.NewFlagSet("help", flag.ExitOnError)
var help bool
helpFlags.BoolVar(&help, "help", false, "Prints help information")
var usage = func() { print_usage(helpFlags, "<[ADDRESS/]INTERFACE>") }
helpFlags.Usage = usage
_ = helpFlags.Parse(args)
if help {
usage()
}
var con *varlink.Connection
var address string
var interfaceName string
if len(bridge) != 0 {
con, err = varlink.NewBridge(bridge)
if err != nil {
ErrPrintf("Cannot connect with bridge '%s': %v\n", bridge, err)
os.Exit(2)
}
address = "bridge:" + bridge
interfaceName = helpFlags.Arg(0)
} else {
uri := helpFlags.Arg(0)
if uri == "" && bridge == "" {
ErrPrintf("No ADDRESS or activation or bridge\n\n")
usage()
}
li := strings.LastIndex(uri, "/")
if li == -1 {
ErrPrintf("Invalid address '%s'\n", uri)
os.Exit(2)
}
address = uri[:li]
con, err = varlink.NewConnection(address)
if err != nil {
ErrPrintf("Cannot connect to '%s': %v\n", address, err)
os.Exit(2)
}
interfaceName = uri[li+1:]
}
description, err := con.GetInterfaceDescription(interfaceName)
if err != nil {
ErrPrintf("Cannot get interface description for '%s': %v\n", interfaceName, err)
os.Exit(2)
}
fmt.Println(description)
}
func varlink_info(args []string) {
var err error
infoFlags := flag.NewFlagSet("info", flag.ExitOnError)
var help bool
infoFlags.BoolVar(&help, "help", false, "Prints help information")
var usage = func() { print_usage(infoFlags, "[ADDRESS]") }
infoFlags.Usage = usage
_ = infoFlags.Parse(args)
if help {
usage()
}
var con *varlink.Connection
var address string
if len(bridge) != 0 {
con, err = varlink.NewBridge(bridge)
if err != nil {
ErrPrintf("Cannot connect with bridge '%s': %v\n", bridge, err)
os.Exit(2)
}
address = "bridge:" + bridge
} else {
address = infoFlags.Arg(0)
if address == "" && bridge == "" {
ErrPrintf("No ADDRESS or activation or bridge\n\n")
usage()
}
con, err = varlink.NewConnection(address)
if err != nil {
ErrPrintf("Cannot connect to '%s': %v\n", address, err)
os.Exit(2)
}
}
var vendor, product, version, url string
var interfaces []string
err = con.GetInfo(&vendor, &product, &version, &url, &interfaces)
if err != nil {
ErrPrintf("Cannot get info for '%s': %v\n", address, err)
os.Exit(2)
}
fmt.Printf("%s %s\n", bold.Sprint("Vendor:"), vendor)
fmt.Printf("%s %s\n", bold.Sprint("Product:"), product)
fmt.Printf("%s %s\n", bold.Sprint("Version:"), version)
fmt.Printf("%s %s\n", bold.Sprint("URL:"), url)
fmt.Printf("%s\n %s\n\n", bold.Sprint("Interfaces:"), strings.Join(interfaces[:], "\n "))
}
func main() {
var debug bool
var colorMode string
flag.CommandLine.Usage = func() { print_usage(nil, "") }
flag.BoolVar(&debug, "debug", false, "Enable debug output")
flag.StringVar(&bridge, "bridge", "", "Use bridge for connection")
flag.StringVar(&colorMode, "color", "auto", "colorize output [default: auto] [possible values: on, off, auto]")
flag.Parse()
if colorMode != "on" && (os.Getenv("TERM") == "" || colorMode == "off") {
color.NoColor = true // disables colorized output
}
switch flag.Arg(0) {
case "info":
varlink_info(flag.Args()[1:])
case "help":
varlink_help(flag.Args()[1:])
case "call":
varlink_call(flag.Args()[1:])
default:
print_usage(nil, "")
}
}

59
vendor/github.com/varlink/go/varlink/bridge.go generated vendored Normal file
View file

@ -0,0 +1,59 @@
// +build !windows
package varlink
import (
"bufio"
"io"
"log"
"net"
"os/exec"
)
type PipeCon struct {
net.Conn
reader *io.ReadCloser
writer *io.WriteCloser
}
func (p PipeCon) Close() error {
err1 := (*p.reader).Close()
err2 := (*p.writer).Close()
if err1 != nil {
return err1
}
if err2 != nil {
return err2
}
return nil
}
// NewConnection returns a new connection to the given address.
func NewBridge(bridge string) (*Connection, error) {
//var err error
c := Connection{}
cmd := exec.Command("sh", "-c", bridge)
r, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
w, err := cmd.StdinPipe()
if err != nil {
return nil, err
}
c.conn = PipeCon{nil, &r, &w}
c.address = ""
c.reader = bufio.NewReader(r)
c.writer = bufio.NewWriter(w)
go func() {
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}()
return &c, nil
}

57
vendor/github.com/varlink/go/varlink/bridge_windows.go generated vendored Normal file
View file

@ -0,0 +1,57 @@
package varlink
import (
"bufio"
"io"
"log"
"net"
"os/exec"
)
type PipeCon struct {
net.Conn
reader *io.ReadCloser
writer *io.WriteCloser
}
func (p PipeCon) Close() error {
err1 := (*p.reader).Close()
err2 := (*p.writer).Close()
if err1 != nil {
return err1
}
if err2 != nil {
return err2
}
return nil
}
// NewConnection returns a new connection to the given address.
func NewBridge(bridge string) (*Connection, error) {
//var err error
c := Connection{}
cmd := exec.Command("cmd", "/C", bridge)
r, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
w, err := cmd.StdinPipe()
if err != nil {
return nil, err
}
c.conn = PipeCon{nil, &r, &w}
c.address = ""
c.reader = bufio.NewReader(r)
c.writer = bufio.NewWriter(w)
go func() {
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}()
return &c, nil
}

View file

@ -3,6 +3,7 @@ package varlink
import (
"bufio"
"encoding/json"
"fmt"
"net"
"strings"
)
@ -18,6 +19,7 @@ const (
// Error is a varlink error returned from a method call.
type Error struct {
error
Name string
Parameters interface{}
}
@ -189,6 +191,11 @@ func NewConnection(address string) (*Connection, error) {
var err error
words := strings.SplitN(address, ":", 2)
if len(words) != 2 {
return nil, fmt.Errorf("Protocol missing")
}
protocol := words[0]
addr := words[1]

View file

@ -6,10 +6,8 @@ import (
"fmt"
"net"
"os"
"strconv"
"strings"
"sync"
"syscall"
"time"
)
@ -110,58 +108,6 @@ func (s *Service) handleMessage(writer *bufio.Writer, request []byte) error {
return iface.VarlinkDispatch(c, methodname)
}
func activationListener() net.Listener {
pid, err := strconv.Atoi(os.Getenv("LISTEN_PID"))
if err != nil || pid != os.Getpid() {
return nil
}
nfds, err := strconv.Atoi(os.Getenv("LISTEN_FDS"))
if err != nil || nfds < 1 {
return nil
}
fd := -1
// If more than one file descriptor is passed, find the
// "varlink" tag. The first file descriptor is always 3.
if nfds > 1 {
fdnames, set := os.LookupEnv("LISTEN_FDNAMES")
if !set {
return nil
}
names := strings.Split(fdnames, ":")
if len(names) != nfds {
return nil
}
for i, name := range names {
if name == "varlink" {
fd = 3 + i
break
}
}
if fd < 0 {
return nil
}
} else {
fd = 3
}
syscall.CloseOnExec(fd)
file := os.NewFile(uintptr(fd), "varlink")
listener, err := net.FileListener(file)
if err != nil {
return nil
}
return listener
}
// Shutdown shuts down the listener of a running service.
func (s *Service) Shutdown() {
s.running = false
@ -253,18 +199,17 @@ func getListener(protocol string, address string) (net.Listener, error) {
}
func (s *Service) refreshTimeout(timeout time.Duration) error {
switch s.protocol {
case "unix":
if err := s.listener.(*net.UnixListener).SetDeadline(time.Now().Add(timeout)); err != nil {
switch l := s.listener.(type) {
case *net.UnixListener:
if err:= l.SetDeadline(time.Now().Add(timeout)); err != nil {
return err
}
case *net.TCPListener:
if err:= l.SetDeadline(time.Now().Add(timeout)); err != nil {
return err
}
case "tcp":
if err := s.listener.(*net.TCPListener).SetDeadline(time.Now().Add(timeout)); err != nil {
return err
}
}
return nil
}

View file

@ -0,0 +1,63 @@
// +build !windows
package varlink
import (
"net"
"os"
"strconv"
"strings"
"syscall"
)
func activationListener() net.Listener {
pid, err := strconv.Atoi(os.Getenv("LISTEN_PID"))
if err != nil || pid != os.Getpid() {
return nil
}
nfds, err := strconv.Atoi(os.Getenv("LISTEN_FDS"))
if err != nil || nfds < 1 {
return nil
}
fd := -1
// If more than one file descriptor is passed, find the
// "varlink" tag. The first file descriptor is always 3.
if nfds > 1 {
fdnames, set := os.LookupEnv("LISTEN_FDNAMES")
if !set {
return nil
}
names := strings.Split(fdnames, ":")
if len(names) != nfds {
return nil
}
for i, name := range names {
if name == "varlink" {
fd = 3 + i
break
}
}
if fd < 0 {
return nil
}
} else {
fd = 3
}
syscall.CloseOnExec(fd)
file := os.NewFile(uintptr(fd), "varlink")
listener, err := net.FileListener(file)
if err != nil {
return nil
}
return listener
}

View file

@ -0,0 +1,7 @@
package varlink
import "net"
func activationListener() net.Listener {
return nil
}