Initial varlink implementation

Signed-off-by: baude <bbaude@redhat.com>

Closes: #627
Approved by: mheon
This commit is contained in:
baude
2018-03-26 09:39:14 -05:00
committed by Atomic Bot
parent cf1d884ffa
commit 8493dba23c
45 changed files with 3434 additions and 32 deletions

1
.gitignore vendored
View File

@ -11,3 +11,4 @@
/test/checkseccomp/checkseccomp
/test/copyimg/copyimg
/build/
cmd/podman/ioprojectatomicpodman/ioprojectatomicpodman.go

View File

@ -79,4 +79,6 @@ RUN mkdir -p /etc/containers
COPY test/policy.json /etc/containers/policy.json
COPY test/redhat_sigstore.yaml /etc/containers/registries.d/registry.access.redhat.com.yaml
# Install varlink stuff
RUN pip3 install varlink
WORKDIR /go/src/github.com/projectatomic/libpod

View File

@ -15,7 +15,9 @@ MANDIR ?= ${PREFIX}/share/man
SHAREDIR_CONTAINERS ?= ${PREFIX}/share/containers
ETCDIR ?= ${DESTDIR}/etc
ETCDIR_LIBPOD ?= ${ETCDIR}/crio
SYSTEMDDIR ?= ${PREFIX}/lib/systemd/system
BUILDTAGS ?= seccomp $(shell hack/btrfs_tag.sh) $(shell hack/libdm_tag.sh) $(shell hack/btrfs_installed_tag.sh) $(shell hack/ostree_tag.sh) $(shell hack/selinux_tag.sh)
PYTHON ?= /usr/bin/python3
BASHINSTALLDIR=${PREFIX}/share/bash-completion/completions
OCIUMOUNTINSTALLDIR=$(PREFIX)/share/oci-umount/oci-umount.d
@ -65,7 +67,7 @@ ifeq ("$(wildcard $(GOPKGDIR))","")
endif
touch "$(GOPATH)/.gopathok"
lint: .gopathok
lint: .gopathok varlink_generate
@echo "checking lint"
@./.tool/lint
@ -101,6 +103,7 @@ endif
rm -f test/copyimg/copyimg
rm -f test/checkseccomp/checkseccomp
rm -fr build/
rm -f cmd/podman/ioprojectatomicpodman/ioprojectatomicpodman.go
libpodimage:
docker build -t ${LIBPOD_IMAGE} .
@ -126,19 +129,20 @@ shell: libpodimage
testunit: libpodimage
docker run -e STORAGE_OPTIONS="--storage-driver=vfs" -e TESTFLAGS -e TRAVIS -t --privileged --rm -v ${CURDIR}:/go/src/${PROJECT} ${LIBPOD_IMAGE} make localunit
localunit:
localunit: varlink_generate
$(GO) test -tags "$(BUILDTAGS)" -cover $(PACKAGES)
ginkgo:
ginkgo -v test/e2e/
localintegration: test-binaries
localintegration: varlink_generate test-binaries
ginkgo -v -cover -flakeAttempts 3 -progress -trace -noColor test/e2e/.
sh test/varlink/run_varlink_tests.sh
vagrant-check:
BOX=$(BOX) sh ./vagrant.sh
binaries: podman
binaries: varlink_generate podman
test-binaries: test/bin2img/bin2img test/copyimg/copyimg test/checkseccomp/checkseccomp
@ -163,7 +167,7 @@ changelog:
$(shell cat $(TMPFILE) >> changelog.txt)
$(shell rm $(TMPFILE))
install: .gopathok install.bin install.man install.cni
install: .gopathok install.bin install.man install.cni install.systemd
install.bin:
install ${SELINUXOPT} -D -m 755 bin/podman $(BINDIR)/podman
@ -189,6 +193,10 @@ install.docker: docker-docs
install ${SELINUXOPT} -d -m 755 $(MANDIR)/man1
install ${SELINUXOPT} -m 644 docs/docker*.1 -t $(MANDIR)/man1
install.systemd:
install ${SELINUXOPT} -m 644 contrib/varlink/io.projectatomic.podman.socket ${SYSTEMDDIR}/io.projectatomic.podman.socket
install ${SELINUXOPT} -m 644 contrib/varlink/io.projectatomic.podman.service ${SYSTEMDDIR}/io.projectatomic.podman.service
uninstall:
for i in $(filter %.1,$(MANPAGES)); do \
rm -f $(MANDIR)/man1/$$(basename $${i}); \
@ -229,6 +237,14 @@ install.tools: .install.gitvalidation .install.gometalinter .install.md2man
make all install; \
fi
.install.varlink: .gopathok
$(GO) get -u github.com/varlink/go/varlink
$(GO) get -u github.com/varlink/go/cmd/varlink-go-interface-generator
varlink_generate: .gopathok .install.varlink
rm -f cmd/podman/ioprojectatomicpodman/ioprojectatomicpodman.go
$(GO) generate ./cmd/podman/ioprojectatomicpodman/...
validate: gofmt .gitvalidation
.PHONY: \

View File

@ -79,6 +79,6 @@ func debugInfo(c *cli.Context) map[string]interface{} {
info["compiler"] = runtime.Compiler
info["go version"] = runtime.Version()
info["podman version"] = c.App.Version
info["git commit"] = gitCommit
info["git commit"] = libpod.GitCommit
return info
}

View File

@ -0,0 +1,3 @@
package ioprojectatomicpodman
//go:generate $GOPATH/bin/varlink-go-interface-generator io.projectatomic.podman.varlink

View File

@ -0,0 +1,65 @@
# Podman Service Interface
interface io.projectatomic.podman
type Version (
version: string,
go_version: string,
git_commit: string,
built: int,
os_arch: string
)
type NotImplemented (
comment: string
)
type StringResponse (
message: string
)
# System
method Ping() -> (ping: StringResponse)
method GetVersion() -> (version: Version)
# Containers
method ListContainers() -> (notimplemented: NotImplemented)
method CreateContainer() -> (notimplemented: NotImplemented)
method InspectContainer() -> (notimplemented: NotImplemented)
method ListContainerProcesses() -> (notimplemented: NotImplemented)
method GetContainerLogs() -> (notimplemented: NotImplemented)
method ListContainerChanges() -> (notimplemented: NotImplemented)
method ExportContainer() -> (notimplemented: NotImplemented)
method GetContainerStats() -> (notimplemented: NotImplemented)
method ResizeContainerTty() -> (notimplemented: NotImplemented)
method StartContainer() -> (notimplemented: NotImplemented)
method StopContainer() -> (notimplemented: NotImplemented)
method RestartContainer() -> (notimplemented: NotImplemented)
method KillContainer() -> (notimplemented: NotImplemented)
method UpdateContainer() -> (notimplemented: NotImplemented)
method RenameContainer() -> (notimplemented: NotImplemented)
method PauseContainer() -> (notimplemented: NotImplemented)
method UnpauseContainer() -> (notimplemented: NotImplemented)
method AttachToContainer() -> (notimplemented: NotImplemented)
method WaitContainer() -> (notimplemented: NotImplemented)
method RemoveContainer() -> (notimplemented: NotImplemented)
method DeleteStoppedContainers() -> (notimplemented: NotImplemented)
# Images
method ListImages() -> (notimplemented: NotImplemented)
method BuildImage() -> (notimplemented: NotImplemented)
method CreateImage() -> (notimplemented: NotImplemented)
method InspectImage() -> (notimplemented: NotImplemented)
method HistoryImage() -> (notimplemented: NotImplemented)
method PushImage() -> (notimplemented: NotImplemented)
method TagImage() -> (notimplemented: NotImplemented)
method RemoveImage() -> (notimplemented: NotImplemented)
method SearchImage() -> (notimplemented: NotImplemented)
method DeleteUnusedImages() -> (notimplemented: NotImplemented)
method CreateFromContainer() -> (notimplemented: NotImplemented)
method ImportImage() -> (notimplemented: NotImplemented)
method ExportImage() -> (notimplemented: NotImplemented)
method PullImage() -> (notimplemented: NotImplemented)
# Something failed
error ActionFailed (reason: string)

View File

@ -70,6 +70,7 @@ func main() {
topCommand,
umountCommand,
unpauseCommand,
varlinkCommand,
versionCommand,
waitCommand,
}

60
cmd/podman/varlink.go Normal file
View File

@ -0,0 +1,60 @@
package main
import (
"github.com/pkg/errors"
"github.com/projectatomic/libpod/cmd/podman/ioprojectatomicpodman"
"github.com/projectatomic/libpod/pkg/varlinkapi"
"github.com/projectatomic/libpod/version"
"github.com/urfave/cli"
"github.com/varlink/go/varlink"
)
var (
varlinkDescription = `
podman varlink
run varlink interface
`
varlinkFlags = []cli.Flag{}
varlinkCommand = cli.Command{
Name: "varlink",
Usage: "Run varlink interface",
Description: varlinkDescription,
Flags: varlinkFlags,
Action: varlinkCmd,
ArgsUsage: "VARLINK_URI",
}
)
func varlinkCmd(c *cli.Context) error {
args := c.Args()
if len(args) < 1 {
return errors.Errorf("you must provide a varlink URI")
}
var varlinkInterfaces = []*ioprojectatomicpodman.VarlinkInterface{varlinkapi.VarlinkLibpod}
// Register varlink service. The metadata can be retrieved with:
// $ varlink info [varlink address URI]
service, err := varlink.NewService(
"Atomic",
"podman",
version.Version,
"https://github.com/projectatomic/libpod",
)
if err != nil {
return errors.Wrapf(err, "unable to create new varlink service")
}
for _, i := range varlinkInterfaces {
if err := service.RegisterInterface(i); err != nil {
return errors.Errorf("unable to register varlink interface %v", i)
}
}
// Run the varlink server at the given address
if err = service.Listen(args[0], 0); err != nil {
return errors.Errorf("unable to start varlink service")
}
return nil
}

View File

@ -2,41 +2,30 @@ package main
import (
"fmt"
"runtime"
"strconv"
"time"
"github.com/pkg/errors"
"github.com/projectatomic/libpod/libpod"
"github.com/urfave/cli"
)
// Overwritten at build time
var (
// gitCommit is the commit that the binary is being built from.
// It will be populated by the Makefile.
gitCommit string
// buildInfo is the time at which the binary was built
// It will be populated by the Makefile.
buildInfo string
)
// versionCmd gets and prints version info for version command
func versionCmd(c *cli.Context) error {
fmt.Println("Version: ", c.App.Version)
fmt.Println("Go Version: ", runtime.Version())
if gitCommit != "" {
fmt.Println("Git Commit: ", gitCommit)
output, err := libpod.GetVersion()
if err != nil {
errors.Wrapf(err, "unable to determine version")
}
if buildInfo != "" {
// Converts unix time from string to int64
buildTime, err := strconv.ParseInt(buildInfo, 10, 64)
if err != nil {
return err
}
// Prints out the build time in readable format
fmt.Println("Built: ", time.Unix(buildTime, 0).Format(time.ANSIC))
fmt.Println("Version: ", output.Version)
fmt.Println("Go Version: ", output.GoVersion)
if output.GitCommit != "" {
fmt.Println("Git Commit: ", output.GitCommit)
}
// Prints out the build time in readable format
if libpod.BuildInfo != "" {
fmt.Println("Built: ", time.Unix(output.Built, 0).Format(time.ANSIC))
}
fmt.Println("OS/Arch: ", runtime.GOOS+"/"+runtime.GOARCH)
fmt.Println("OS/Arch: ", output.OsArch)
return nil
}

View File

@ -42,5 +42,6 @@
| [podman-top(1)](/docs/podman-top.1.md) | Display the running processes of a container |[![...](/docs/play.png)](https://asciinema.org/a/5WCCi1LXwSuRbvaO9cBUYf3fk)|
| [podman-umount(1)](/docs/podman-umount.1.md) | Unmount a working container's root filesystem |[![...](/docs/play.png)](https://asciinema.org/a/MZPTWD5CVs3dMREkBxQBY9C5z)|
| [podman-unpause(1)](/docs/podman-unpause.1.md) | Unpause one or more running containers |[![...](/docs/play.png)](https://asciinema.org/a/141292)|
| [podman-varlink(1)](/docs/podman-varlink.1.md) | Run the varlink backend ||
| [podman-version(1)](/docs/podman-version.1.md) | Display the version information |[![...](/docs/play.png)](https://asciinema.org/a/mfrn61pjZT9Fc8L4NbfdSqfgu)|
| [podman-wait(1)](/docs/podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes |[![...](/docs/play.png)](https://asciinema.org/a/QNPGKdjWuPgI96GcfkycQtah0)|

View File

@ -1514,6 +1514,14 @@ _podman_unpause() {
_complete_ "$options_with_args" "$boolean_options"
}
_podman_varlink() {
local options_with_args="
--help -h
"
local boolean_options=""
_complete_ "$options_with_args" "$boolean_options"
}
_podman_wait() {
local options_with_args=""
local boolean_options="
@ -1633,6 +1641,7 @@ _podman_podman() {
umount
unmount
unpause
varlink
version
wait
"

View File

@ -0,0 +1,12 @@
[Unit]
Description=Pod Manager
Requires=io.projectatomic.podman.socket
After=io.projectatomic.podman.socket
[Service]
Type=simple
ExecStart=/usr/bin/podman --varlink=unix:/run/io.projectatomic.podman
[Install]
WantedBy=multi-user.target
Also=io.projectatomic.podman.socket

View File

@ -0,0 +1,8 @@
[Unit]
Description=Pod Manager Socket
[Socket]
ListenStream=/run/io.projectatomic.podman
[Install]
WantedBy=sockets.target

38
docs/podman-varlink.1.md Normal file
View File

@ -0,0 +1,38 @@
% podman(1) podman-varlink - Waits on a container
% Brent Baude
# podman-varlink "1" "April 2018" "podman"
## NAME
podman varlink - Runs the varlink backend interface
## SYNOPSIS
**podman varlink**
[**--help**|**-h**]
VARLINK_URI
## DESCRIPTION
Starts the varlink service that allows varlink clients to interact with podman.
<!--
More will go here as the docs and api firm up.
-->
**podman [GLOBAL OPTIONS] varlink **
## GLOBAL OPTIONS
**--help, -h**
Print usage statement
## EXAMPLES
podman varlink unix:/run/io.projectatomic.podman
<!--
TODO: More examples with TCP can be added when that works
as well.
-->
## SEE ALSO
podman(1)
## HISTORY
April 2018, Originally compiled by Brent Baude<bbaude@redhat.com>

View File

@ -10,7 +10,8 @@ find_files() {
-wholename '*/vendor/*' \
\) -prune \
\) -name '*.go' \
-not \( -wholename './_output/*' \)
-not \( -wholename './_output/*' \) \
-not \( -wholename './cmd/podman/ioprojectatomicpodman/ioprojectatomicpodman.go' \)
}
FIX=0
GOFMT="gofmt -s"

50
libpod/version.go Normal file
View File

@ -0,0 +1,50 @@
package libpod
import (
"runtime"
"strconv"
"github.com/projectatomic/libpod/cmd/podman/ioprojectatomicpodman"
podmanVersion "github.com/projectatomic/libpod/version"
)
// Overwritten at build time
var (
// GitCommit is the commit that the binary is being built from.
// It will be populated by the Makefile.
GitCommit string
// BuildInfo is the time at which the binary was built
// It will be populated by the Makefile.
BuildInfo string
)
//Version is an output struct for varlink
type Version struct {
ioprojectatomicpodman.VarlinkInterface
Version string
GoVersion string
GitCommit string
Built int64
OsArch string
}
// GetVersion returns a VersionOutput struct for varlink and podman
func GetVersion() (Version, error) {
var err error
var buildTime int64
if BuildInfo != "" {
// Converts unix time from string to int64
buildTime, err = strconv.ParseInt(BuildInfo, 10, 64)
if err != nil {
return Version{}, err
}
}
return Version{
Version: podmanVersion.Version,
GoVersion: runtime.Version(),
GitCommit: GitCommit,
Built: buildTime,
OsArch: runtime.GOOS + "/" + runtime.GOARCH,
}, nil
}

14
pkg/varlinkapi/config.go Normal file
View File

@ -0,0 +1,14 @@
package varlinkapi
import "github.com/projectatomic/libpod/cmd/podman/ioprojectatomicpodman"
// LibpodAPI is the basic varlink struct for libpod
type LibpodAPI struct {
ioprojectatomicpodman.VarlinkInterface
}
var (
lp = LibpodAPI{}
// VarlinkLibpod instantiation
VarlinkLibpod = ioprojectatomicpodman.VarlinkNew(&lp)
)

View File

@ -0,0 +1,111 @@
package varlinkapi
import (
"github.com/projectatomic/libpod/cmd/podman/ioprojectatomicpodman"
)
// ListContainers ...
func (i *LibpodAPI) ListContainers(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("ListContainers")
}
// CreateContainer ...
func (i *LibpodAPI) CreateContainer(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("CreateContainer")
}
// InspectContainer ...
func (i *LibpodAPI) InspectContainer(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("InspectContainer")
}
// ListContainerProcesses ...
func (i *LibpodAPI) ListContainerProcesses(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("ListContainerProcesses")
}
// GetContainerLogs ...
func (i *LibpodAPI) GetContainerLogs(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("GetContainerLogs")
}
// ListContainerChanges ...
func (i *LibpodAPI) ListContainerChanges(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("ListContianerChanges")
}
// ExportContainer ...
func (i *LibpodAPI) ExportContainer(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("ExportContainer")
}
// GetContainerStats ...
func (i *LibpodAPI) GetContainerStats(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("GetContainerStates")
}
// ResizeContainerTty ...
func (i *LibpodAPI) ResizeContainerTty(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("ResizeContainerTty")
}
// StartContainer ...
func (i *LibpodAPI) StartContainer(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("StartContainer")
}
// StopContainer ...
func (i *LibpodAPI) StopContainer(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("StopContainer")
}
// RestartContainer ...
func (i *LibpodAPI) RestartContainer(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("RestartContainer")
}
// KillContainer ...
func (i *LibpodAPI) KillContainer(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("KillContainer")
}
// UpdateContainer ...
func (i *LibpodAPI) UpdateContainer(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("UpdateContainer")
}
// RenameContainer ...
func (i *LibpodAPI) RenameContainer(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("RenameContainer")
}
// PauseContainer ...
func (i *LibpodAPI) PauseContainer(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("PauseContainer")
}
// UnpauseContainer ...
func (i *LibpodAPI) UnpauseContainer(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("UnpauseContainer")
}
// AttachToContainer ...
// TODO: DO we also want a different one for websocket?
func (i *LibpodAPI) AttachToContainer(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("AttachToContainer")
}
// WaitContainer ...
func (i *LibpodAPI) WaitContainer(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("WaitContainer")
}
// RemoveContainer ...
func (i *LibpodAPI) RemoveContainer(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("RemoveContainer")
}
// DeleteStoppedContainers ...
func (i *LibpodAPI) DeleteStoppedContainers(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("DeleteContainer")
}

75
pkg/varlinkapi/images.go Normal file
View File

@ -0,0 +1,75 @@
package varlinkapi
import (
"github.com/projectatomic/libpod/cmd/podman/ioprojectatomicpodman"
)
// ListImages ...
func (i *LibpodAPI) ListImages(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("ListImages")
}
// BuildImage ...
func (i *LibpodAPI) BuildImage(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("BuildImage")
}
// CreateImage ...
func (i *LibpodAPI) CreateImage(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("CreateImage")
}
// InspectImage ...
func (i *LibpodAPI) InspectImage(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("InspectImage")
}
// HistoryImage ...
func (i *LibpodAPI) HistoryImage(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("HistoryImage")
}
// PushImage ...
func (i *LibpodAPI) PushImage(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("PushImage")
}
// TagImage ...
func (i *LibpodAPI) TagImage(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("TagImage")
}
// RemoveImage ...
func (i *LibpodAPI) RemoveImage(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("RemoveImage")
}
// SearchImage ...
func (i *LibpodAPI) SearchImage(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("SearchImage")
}
// DeleteUnusedImages ...
func (i *LibpodAPI) DeleteUnusedImages(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("DeleteUnusedImages")
}
// CreateFromContainer ...
func (i *LibpodAPI) CreateFromContainer(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("CreateFromContainer")
}
// ImportImage ...
func (i *LibpodAPI) ImportImage(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("ImportImage")
}
// ExportImage ...
func (i *LibpodAPI) ExportImage(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("ExportImage")
}
// PullImage ...
func (i *LibpodAPI) PullImage(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyMethodNotImplemented("PullImage")
}

30
pkg/varlinkapi/system.go Normal file
View File

@ -0,0 +1,30 @@
package varlinkapi
import (
"github.com/projectatomic/libpod/cmd/podman/ioprojectatomicpodman"
"github.com/projectatomic/libpod/libpod"
)
// GetVersion ...
func (i *LibpodAPI) GetVersion(call ioprojectatomicpodman.VarlinkCall) error {
versionInfo, err := libpod.GetVersion()
if err != nil {
return err
}
return call.ReplyGetVersion(ioprojectatomicpodman.Version{
Version: versionInfo.Version,
Go_version: versionInfo.GoVersion,
Git_commit: versionInfo.GitCommit,
Built: versionInfo.Built,
Os_arch: versionInfo.OsArch,
})
}
// Ping returns a simple string "OK" response for clients to make sure
// the service is working.
func (i *LibpodAPI) Ping(call ioprojectatomicpodman.VarlinkCall) error {
return call.ReplyPing(ioprojectatomicpodman.StringResponse{
Message: "OK",
})
}

View File

@ -0,0 +1,37 @@
#!/bin/bash
set -x
if [ ! -n "${PYTHON+ }" ]; then
if hash python3 > /dev/null 2>&1 /dev/null; then
PYTHON=$(hash -t python3)
elif type python3 > /dev/null 2>&1; then
PYTHON=$(type python3 | awk '{print $3}')
elif hash python2 > /dev/null 2>&1; then
PYTHON=$(hash -t python2)
elif type python2 > /dev/null 2>&1; then
PYTHON=$(type python2 | awk '{print $3}')
else
PYTHON='/usr/bin/python'
fi
fi
# Create temporary directory for storage
TMPSTORAGE=`mktemp -d`
# Need a location to store the podman socket
mkdir /run/podman
# Run podman in background without systemd for test purposes
bin/podman --storage-driver=vfs --root=${TMPSTORAGE}/crio --runroot=${TMPSTORAGE}/crio-run varlink unix:/run/podman/io.projectatomic.podman&
# Record podman's pid to be killed later
PODMAN_PID=`echo $!`
# Run tests
${PYTHON} -m unittest discover -s test/varlink/
# Kill podman
kill -9 ${PODMAN_PID}
# Clean up
rm -fr ${TMPSTORAGE}

View File

@ -0,0 +1,99 @@
import unittest
from varlink import (Client, VarlinkError)
address = "unix:/run/podman/io.projectatomic.podman"
client = Client(address=address)
def runErrorTest(tfunc):
try:
tfunc()
except VarlinkError as e:
return e.error() == "org.varlink.service.MethodNotImplemented"
return False
class ContainersAPI(unittest.TestCase):
def test_ListContainers(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.ListContainers))
def test_CreateContainer(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.CreateContainer))
def test_InspecContainer(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.InspectContainer))
def test_ListContainerProcesses(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.ListContainerProcesses))
def test_GetContainerLogs(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.GetContainerLogs))
def test_ListContainerChanges(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.ListContainerChanges))
def test_ExportContainer(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.ExportContainer))
def test_GetContainerStats(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.GetContainerStats))
def test_ResizeContainerTty(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.ResizeContainerTty))
def test_StartContainer(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.StartContainer))
def test_RestartContainer(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.RestartContainer))
def test_KillContainer(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.KillContainer))
def test_UpdateContainer(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.UpdateContainer))
def test_RenameContainer(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.RenameContainer))
def test_PauseContainer(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.PauseContainer))
def test_UnpauseContainer(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.UnpauseContainer))
def test_AttachToContainer(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.AttachToContainer))
def test_WaitContainer(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.WaitContainer))
def test_RemoveContainer(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.RemoveContainer))
def test_DeleteContainer(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.DeleteContainer))
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,71 @@
import unittest
from varlink import (Client, VarlinkError)
address = "unix:/run/podman/io.projectatomic.podman"
client = Client(address=address)
def runErrorTest(tfunc):
try:
tfunc()
except VarlinkError as e:
return e.error() == "org.varlink.service.MethodNotImplemented"
return False
class ImagesAPI(unittest.TestCase):
def test_ListImages(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.ListImages))
def test_BuildImage(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.BuildImage))
def test_CreateImage(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.CreateImage))
def test_InspectImage(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.InspectImage))
def test_HistoryImage(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.HistoryImage))
def test_PushImage(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.PushImage))
def test_TagImage(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.TagImage))
def test_RemoveImage(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.TagImage))
def test_SearchImage(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.SearchImage))
def test_DeleteUnusedImages(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.DeleteUnusedImages))
def test_CreateFromContainer(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.CreateFromContainer))
def test_ImportImage(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.ImportImage))
def test_ExportImage(self):
podman = client.open("io.projectatomic.podman")
self.assertTrue(runErrorTest(podman.ExportImage))
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,21 @@
import unittest
from varlink import (Client, VarlinkError)
address = "unix:/run/podman/io.projectatomic.podman"
client = Client(address=address)
class SystemAPI(unittest.TestCase):
def test_ping(self):
podman = client.open("io.projectatomic.podman")
response = podman.Ping()
self.assertEqual("OK", response["ping"]["message"])
def test_GetVersion(self):
podman = client.open("io.projectatomic.podman")
response = podman.GetVersion()
for k in ["version", "go_version", "built", "os_arch"]:
self.assertTrue(k in response["version"].keys())
if __name__ == '__main__':
unittest.main()

9
varlink_client.py Normal file
View File

@ -0,0 +1,9 @@
from varlink import (Client, VarlinkError)
import json
address = "unix:/run/podman/io.projectatomic.podman"
with Client(address=address) as client:
podman = client.open('io.projectatomic.podman')
response = podman.GetVersion()
print(json.dumps(response, indent=4, separators=(',', ': ')))

View File

@ -87,3 +87,4 @@ k8s.io/client-go 7cd1d3291b7d9b1e2d54d4b69eb65995eaf8888e https://github.com/kub
k8s.io/kube-openapi 275e2ce91dec4c05a4094a7b1daee5560b555ac9 https://github.com/kubernetes/kube-openapi
k8s.io/utils 258e2a2fa64568210fbd6267cf1d8fd87c3cb86e https://github.com/kubernetes/utils
github.com/mrunalp/fileutils master
github.com/varlink/go master https://github.com/varlink/go

0
vendor/github.com/varlink/go/.gitignore generated vendored Normal file
View File

13
vendor/github.com/varlink/go/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,13 @@
language: go
sudo: false
go:
- '1.9'
- 1.10.x
install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
script:
- '"$HOME/gopath/bin/goveralls" -show -service=travis-ci -repotoken "$COVERALLS_TOKEN"'
env:
global:
- secure: bjxOSgBfB+YooxNTkIDHAD+/X6g56qBWoYpB1JinuS5kmt3vSjfRSuXui71sGuha7jO2FOJja8HcpjOv3UP+qmmej9276o5VWrjS1AwnI95hSQQ4JHm293Z1QeojjRaxmoKrgn7i82Hn4qNdVLQA142s+SIdqOxtN6LDs7i0Yb4IuXoiMQHbd6kAAL95o9IUFPpYAdsXoQ6xnx+TXNiSwPPeh4m5CNKuTtmGTuMGaj8tXxttFKJhZcRzvOpDuh7luc9PSVnQgYmKE/3S9ehzGV8Lk4T8eC7587DY1GdYQKt1egJSE72L+PVnmoalWROaAGHZvYWsSAeNi1UIvcFwGbXBRpq7kz3DVfIULM8V67UAaF3dGYDN3Ae825mDjN5JDfml17AoEjMjI0LlBImZLX2EWIEN225JIREHdpG9seJkaN1ClcpvEIeYuThF2MiivP1EE8/w8S80yoO5nW76Py/th16OuaEiP9LdLsbXimObUPsS9Sr8qquf/PiVqRMMpVW88oOEG5HVn4Ra5B/xVC6nPEF88tE6p9+7RSz4rOWih8QmW+6SX6eo0BI9di4L779f/WfUrddN0JLIvEnRFZZ+pVF/oo+N2INNeIMsZBvG3FVo+Zxzo6SExXnSSpuf1bp140ZdinUMACq6BqK+9gj1C9vNRmqQJaEefrqutws=

201
vendor/github.com/varlink/go/LICENSE generated vendored Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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
http://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.

3
vendor/github.com/varlink/go/Makefile generated vendored Normal file
View File

@ -0,0 +1,3 @@
all:
go test -v ./...
.PHONY: all

7
vendor/github.com/varlink/go/README.md generated vendored Normal file
View File

@ -0,0 +1,7 @@
[![Build Status](https://travis-ci.org/varlink/go.svg?branch=master)](https://travis-ci.org/varlink/go)
[![Go Report Card](https://goreportcard.com/badge/github.com/varlink/go)](https://goreportcard.com/report/github.com/varlink/go)
[![Go Doc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](http://godoc.org/github.com/varlink/go/varlink)
[![Coverage Status](https://coveralls.io/repos/github/varlink/go/badge.svg?branch=master)](https://coveralls.io/github/varlink/go?branch=master)
[![Release](https://img.shields.io/github/release/golang-standards/project-layout.svg?style=flat-square)](https://github.com/varlink/go/varlink/releases/latest)
# go/varlink

View File

@ -0,0 +1,85 @@
package main
import (
"strings"
"testing"
)
func expect(t *testing.T, expected string, returned string) {
if strings.Compare(returned, expected) != 0 {
t.Fatalf("Expected(%d): `%s`\nGot(%d): `%s`\n",
len(expected), expected,
len(returned), returned)
}
}
func TestIDLParser(t *testing.T) {
pkgname, b, err := generateTemplate(`
# Interface to jump a spacecraft to another point in space. The
# FTL Drive is the propulsion system to achieve faster-than-light
# travel through space. A ship making a properly calculated
# jump can arrive safely in planetary orbit, or alongside other
# ships or spaceborne objects.
interface org.example.ftl
# The current state of the FTL drive and the amount of fuel
# available to jump.
type DriveCondition (
state: (idle, spooling, busy),
booster: bool,
active_engines: [](id: int, state: bool),
tylium_level: int
)
# Speed, trajectory and jump duration is calculated prior to
# activating the FTL drive.
type DriveConfiguration (
speed: int,
trajectory: int,
duration: int
)
# The galactic coordinates use the Sun as the origin. Galactic
# longitude is measured with primary direction from the Sun to
# the center of the galaxy in the galactic plane, while the
# galactic latitude measures the angle of the object above the
# galactic plane.
type Coordinate (
longitude: float,
latitude: float,
distance: int
)
# Monitor the drive. The method will reply with an update whenever
# the drive's state changes
method Monitor() -> (condition: DriveCondition)
# Calculate the drive's jump parameters from the current
# position to the target position in the galaxy
method CalculateConfiguration(
current: Coordinate,
target: Coordinate
) -> (configuration: DriveConfiguration)
# Jump to the calculated point in space
method Jump(configuration: DriveConfiguration) -> ()
# There is not enough tylium to jump with the given parameters
error NotEnoughEnergy ()
# The supplied parameters are outside the supported range
error ParameterOutOfRange (field: string)
# some more coverage
method Foo(interface: string) -> (ret: (go: string, switch: bool, more: (t:bool, f:bool)))
`)
if err != nil {
t.Fatalf("Error parsing %v", err)
}
expect(t, "orgexampleftl", pkgname)
if len(b) <= 0 {
t.Fatal("No generated go source")
}
// FIXME: compare b.String() against expected output
}

View File

@ -0,0 +1,294 @@
package main
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path"
"strings"
"github.com/varlink/go/varlink/idl"
)
var goKeywords = map[string]struct{}{
"break": {},
"case": {},
"chan": {},
"const": {},
"continue": {},
"default": {},
"defer": {},
"else": {},
"fallthrough": {},
"for": {},
"func": {},
"go": {},
"goto": {},
"if": {},
"import": {},
"interface": {},
"map": {},
"package": {},
"range": {},
"return": {},
"select": {},
"struct": {},
"switch": {},
"type": {},
"var": {},
}
func sanitizeGoName(name string) string {
if _, ok := goKeywords[name]; !ok {
return name
}
return name + "_"
}
func writeType(b *bytes.Buffer, t *idl.Type, json bool, ident int) {
switch t.Kind {
case idl.TypeBool:
b.WriteString("bool")
case idl.TypeInt:
b.WriteString("int64")
case idl.TypeFloat:
b.WriteString("float64")
case idl.TypeString, idl.TypeEnum:
b.WriteString("string")
case idl.TypeArray:
b.WriteString("[]")
writeType(b, t.ElementType, json, ident)
case idl.TypeMaybe:
b.WriteString("*")
writeType(b, t.ElementType, json, ident)
case idl.TypeAlias:
b.WriteString(t.Alias)
case idl.TypeStruct:
b.WriteString("struct{\n")
for _, field := range t.Fields {
for i := 0; i < ident+1; i++ {
b.WriteString("\t")
}
b.WriteString(strings.Title(field.Name) + " ")
writeType(b, field.Type, json, ident+1)
if json {
b.WriteString(" `json:\"" + field.Name + "\"`")
}
b.WriteString("\n")
}
for i := 0; i < ident; i++ {
b.WriteString("\t")
}
b.WriteString("}")
}
}
func generateTemplate(description string) (string, []byte, error) {
description = strings.TrimRight(description, "\n")
midl, err := idl.New(description)
if err != nil {
return "", nil, err
}
pkgname := strings.Replace(midl.Name, ".", "", -1)
var b bytes.Buffer
b.WriteString("// Generated with github.com/varlink/go/cmd/varlink-go-interface-generator\n")
b.WriteString("package " + pkgname + "\n\n")
b.WriteString(`import "github.com/varlink/go/varlink"` + "\n\n")
// Type declarations
for _, a := range midl.Aliases {
b.WriteString("type " + a.Name + " ")
writeType(&b, a.Type, true, 0)
b.WriteString("\n\n")
}
// Local interface with all methods
b.WriteString("type " + pkgname + "Interface interface {\n")
for _, m := range midl.Methods {
b.WriteString("\t" + m.Name + "(c VarlinkCall")
for _, field := range m.In.Fields {
b.WriteString(", " + strings.Title(field.Name) + " ")
writeType(&b, field.Type, false, 1)
}
b.WriteString(") error\n")
}
b.WriteString("}\n\n")
// Local object with all methods
b.WriteString("type VarlinkCall struct{ varlink.Call }\n\n")
// Reply methods for all varlink errors
for _, e := range midl.Errors {
b.WriteString("func (c *VarlinkCall) Reply" + e.Name + "(")
for i, field := range e.Type.Fields {
if i > 0 {
b.WriteString(", ")
}
b.WriteString(sanitizeGoName(field.Name) + " ")
writeType(&b, field.Type, false, 1)
}
b.WriteString(") error {\n")
if len(e.Type.Fields) > 0 {
b.WriteString("\tvar out ")
writeType(&b, e.Type, true, 1)
b.WriteString("\n")
for _, field := range e.Type.Fields {
switch field.Type.Kind {
case idl.TypeStruct, idl.TypeArray:
b.WriteString("\tout." + strings.Title(field.Name) + " = ")
writeType(&b, field.Type, true, 1)
b.WriteString("(" + sanitizeGoName(field.Name) + ")\n")
default:
b.WriteString("\tout." + strings.Title(field.Name) + " = " + sanitizeGoName(field.Name) + "\n")
}
}
b.WriteString("\treturn c.ReplyError(\"" + midl.Name + "." + e.Name + "\", &out)\n")
} else {
b.WriteString("\treturn c.ReplyError(\"" + midl.Name + "." + e.Name + "\", nil)\n")
}
b.WriteString("}\n\n")
}
// Reply methods for all varlink methods
for _, m := range midl.Methods {
b.WriteString("func (c *VarlinkCall) Reply" + m.Name + "(")
for i, field := range m.Out.Fields {
if i > 0 {
b.WriteString(", ")
}
b.WriteString(sanitizeGoName(field.Name) + " ")
writeType(&b, field.Type, false, 1)
}
b.WriteString(") error {\n")
if len(m.Out.Fields) > 0 {
b.WriteString("\tvar out ")
writeType(&b, m.Out, true, 1)
b.WriteString("\n")
for _, field := range m.Out.Fields {
switch field.Type.Kind {
case idl.TypeStruct, idl.TypeArray:
b.WriteString("\tout." + strings.Title(field.Name) + " = ")
writeType(&b, field.Type, true, 1)
b.WriteString("(" + sanitizeGoName(field.Name) + ")\n")
default:
b.WriteString("\tout." + strings.Title(field.Name) + " = " + sanitizeGoName(field.Name) + "\n")
}
}
b.WriteString("\treturn c.Reply(&out)\n")
} else {
b.WriteString("\treturn c.Reply(nil)\n")
}
b.WriteString("}\n\n")
}
// Dummy methods for all varlink methods
for _, m := range midl.Methods {
b.WriteString("func (s *VarlinkInterface) " + m.Name + "(c VarlinkCall")
for _, field := range m.In.Fields {
b.WriteString(", " + sanitizeGoName(field.Name) + " ")
writeType(&b, field.Type, false, 1)
}
b.WriteString(") error {\n" +
"\treturn c.ReplyMethodNotImplemented(\"" + m.Name + "\")\n" +
"}\n\n")
}
// Method call dispatcher
b.WriteString("func (s *VarlinkInterface) VarlinkDispatch(call varlink.Call, methodname string) error {\n" +
"\tswitch methodname {\n")
for _, m := range midl.Methods {
b.WriteString("\tcase \"" + m.Name + "\":\n")
if len(m.In.Fields) > 0 {
b.WriteString("\t\tvar in ")
writeType(&b, m.In, true, 2)
b.WriteString("\n")
b.WriteString("\t\terr := call.GetParameters(&in)\n" +
"\t\tif err != nil {\n" +
"\t\t\treturn call.ReplyInvalidParameter(\"parameters\")\n" +
"\t\t}\n")
b.WriteString("\t\treturn s." + pkgname + "Interface." + m.Name + "(VarlinkCall{call}")
if len(m.In.Fields) > 0 {
for _, field := range m.In.Fields {
switch field.Type.Kind {
case idl.TypeStruct, idl.TypeArray:
b.WriteString(", ")
writeType(&b, field.Type, false, 2)
b.WriteString("(in." + strings.Title(field.Name) + ")")
default:
b.WriteString(", in." + strings.Title(field.Name))
}
}
}
b.WriteString(")\n")
} else {
b.WriteString("\t\treturn s." + pkgname + "Interface." + m.Name + "(VarlinkCall{call})\n")
}
b.WriteString("\n")
}
b.WriteString("\tdefault:\n" +
"\t\treturn call.ReplyMethodNotFound(methodname)\n" +
"\t}\n" +
"}\n")
// Varlink interface name
b.WriteString("func (s *VarlinkInterface) VarlinkGetName() string {\n" +
"\treturn `" + midl.Name + "`\n" + "}\n\n")
// Varlink interface description
b.WriteString("func (s *VarlinkInterface) VarlinkGetDescription() string {\n" +
"\treturn `" + midl.Description + "\n`\n}\n\n")
b.WriteString("type VarlinkInterface struct {\n" +
"\t" + pkgname + "Interface\n" +
"}\n\n")
b.WriteString("func VarlinkNew(m " + pkgname + "Interface) *VarlinkInterface {\n" +
"\treturn &VarlinkInterface{m}\n" +
"}\n")
return pkgname, b.Bytes(), nil
}
func generateFile(varlinkFile string) {
file, err := ioutil.ReadFile(varlinkFile)
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading file '%s': %s\n", varlinkFile, err)
os.Exit(1)
}
pkgname, b, err := generateTemplate(string(file))
if err != nil {
fmt.Fprintf(os.Stderr, "Error parsing file '%s': %s\n", varlinkFile, err)
os.Exit(1)
}
filename := path.Dir(varlinkFile) + "/" + pkgname + ".go"
err = ioutil.WriteFile(filename, b, 0660)
if err != nil {
fmt.Fprintf(os.Stderr, "Error writing file '%s': %s\n", filename, err)
os.Exit(1)
}
}
func main() {
if len(os.Args) != 2 {
fmt.Printf("Usage: %s <file>\n", os.Args[0])
os.Exit(1)
}
generateFile(os.Args[1])
}

View File

@ -0,0 +1,142 @@
package main
import (
"fmt"
"go/ast"
"go/importer"
"go/parser"
"go/token"
"go/types"
"log"
"os"
)
func GoToVarlinkType(t types.Type) string {
switch u := t.(type) {
case *types.Basic:
if u.Info()&types.IsBoolean != 0 {
return "bool"
}
if u.Info()&types.IsInteger != 0 {
return "int"
}
if u.Info()&types.IsFloat != 0 {
return "float"
}
if u.Info()&types.IsString != 0 {
return "string"
}
return fmt.Sprintf("<<<%s>>>", t.String())
case *types.Named:
return u.Obj().Name()
case *types.Map:
return fmt.Sprintf("<<<%s>>>", u.String())
case *types.Interface:
return fmt.Sprintf("<<<%s>>>", u.String())
case *types.Pointer:
return fmt.Sprintf("?%s", GoToVarlinkType(u.Elem()))
case *types.Array:
return fmt.Sprintf("[]%s", GoToVarlinkType(u.Elem()))
case *types.Slice:
return fmt.Sprintf("[]%s", GoToVarlinkType(u.Elem()))
default:
return fmt.Sprintf("<<<%T %s>>>", t, u)
}
return t.String()
}
func PrintDefsUses(name string, fset *token.FileSet, files []*ast.File) error {
conf := types.Config{
Importer: importer.Default(),
FakeImportC: true,
}
info := &types.Info{
Defs: make(map[*ast.Ident]types.Object),
}
_, err := conf.Check(name, fset, files, info)
if err != nil {
return err // type error
}
seen := map[string]interface{}{}
for id, obj := range info.Defs {
if obj == nil {
continue
}
if _, ok := seen[id.Name]; ok {
continue
}
/*
if !obj.Exported() || obj.Pkg().Name() != name {
continue
}
*/
switch f := obj.Type().Underlying().(type) {
case *types.Struct:
if f.NumFields() > 0 {
fmt.Printf("type %s (\n", id.Name)
fmt.Printf("\t%s: %s",
f.Field(0).Name(), GoToVarlinkType(f.Field(0).Type()))
for i := 1; i < f.NumFields(); i++ {
fmt.Printf(",\n\t%s: %s",
f.Field(i).Name(), GoToVarlinkType(f.Field(i).Type()))
}
fmt.Printf("\n)\n\n")
}
}
seen[id.Name] = nil
}
return nil
}
func main() {
path := os.Args[1]
fs := token.NewFileSet()
if stat, err := os.Stat(path); err == nil && stat.IsDir() {
pkgs, err := parser.ParseDir(fs, path, nil, 0)
if err != nil {
fmt.Printf("parsing dir '%s': %s", path, err)
}
for name, pkg := range pkgs {
log.Println("Found package:", name)
fset := make([]*ast.File, len(pkg.Files), len(pkg.Files))
idx := 0
for _, value := range pkg.Files {
fset[idx] = value
idx++
}
if err := PrintDefsUses(name, fs, fset); err != nil {
log.Print(err) // type error
}
}
} else {
fset, err := parser.ParseFile(fs, path, nil, 0)
if err != nil {
fmt.Printf("parsing file '%s': %s", path, err)
}
name := fset.Name.String()
if err := PrintDefsUses(name, fs, []*ast.File{fset}); err != nil {
log.Print(err) // type error
}
}
}

View File

@ -0,0 +1,44 @@
%global goipath github.com/varlink/go
Version: 0
%gometa
Name: %{goname}
Release: 1%{?dist}
Summary: Go bindings for varlink
License: ASL 2.0
URL: %{gourl}
Source0: %{gosource}
%description
Native Go bindings for the varlink protocol.
%package devel
Summary: %{summary}
BuildArch: noarch
%description devel
%{summary}
This package contains library source intended for
building other packages which use import path with
%{gobaseipath} prefix.
%prep
%forgesetup
%build
%gobuildroot
%install
gofiles=$(find . %{gofindfilter} -print)
%goinstall $gofiles
%check
%files devel -f devel.file-list
%license LICENSE
%doc README.md
%changelog
* Tue Mar 20 2018 <info@varlink.org> 0-1
- Version 0

86
vendor/github.com/varlink/go/varlink/call.go generated vendored Normal file
View File

@ -0,0 +1,86 @@
package varlink
import (
"bufio"
"encoding/json"
"fmt"
"strings"
)
// Call is a method call retrieved by a Service. The connection from the
// client can be terminated by returning an error from the call instead
// of sending a reply or error reply.
type Call struct {
writer *bufio.Writer
in *serviceCall
Continues bool
}
// WantsMore indicates if the calling client accepts more than one reply to this method call.
func (c *Call) WantsMore() bool {
return c.in.More
}
// IsOneShot indicate that the calling client does not expect a reply.
func (c *Call) IsOneShot() bool {
return c.in.OneShot
}
// GetParameters retrieves the method call parameters.
func (c *Call) GetParameters(p interface{}) error {
if c.in.Parameters == nil {
return fmt.Errorf("empty parameters")
}
return json.Unmarshal(*c.in.Parameters, p)
}
func (c *Call) sendMessage(r *serviceReply) error {
if c.in.OneShot {
return nil
}
b, e := json.Marshal(r)
if e != nil {
return e
}
b = append(b, 0)
_, e = c.writer.Write(b)
if e != nil {
return e
}
return c.writer.Flush()
}
// Reply sends a reply to this method call.
func (c *Call) Reply(parameters interface{}) error {
if !c.Continues {
return c.sendMessage(&serviceReply{
Parameters: parameters,
})
}
if !c.in.More {
return fmt.Errorf("call did not set more, it does not expect continues")
}
return c.sendMessage(&serviceReply{
Continues: true,
Parameters: parameters,
})
}
// ReplyError sends an error reply to this method call.
func (c *Call) ReplyError(name string, parameters interface{}) error {
r := strings.LastIndex(name, ".")
if r <= 0 {
return fmt.Errorf("invalid error name")
}
if name[:r] == "org.varlink.service" {
return fmt.Errorf("refused to send org.varlink.service errors")
}
return c.sendMessage(&serviceReply{
Error: name,
Parameters: parameters,
})
}

197
vendor/github.com/varlink/go/varlink/connection.go generated vendored Normal file
View File

@ -0,0 +1,197 @@
package varlink
import (
"bufio"
"encoding/json"
"net"
"strings"
)
// Error is a varlink error returned from a method call.
type Error struct {
Name string
Parameters interface{}
}
// Error returns the fully-qualified varlink error name.
func (e *Error) Error() string {
return e.Name
}
// Connection is a connection from a client to a service.
type Connection struct {
address string
conn net.Conn
reader *bufio.Reader
writer *bufio.Writer
}
// Send sends a method call.
func (c *Connection) Send(method string, parameters interface{}, more bool) error {
type call struct {
Method string `json:"method"`
Parameters interface{} `json:"parameters,omitempty"`
More bool `json:"more,omitempty"`
OneShot bool `json:"oneshot,omitempty"`
}
m := call{
Method: method,
Parameters: parameters,
More: more,
}
b, err := json.Marshal(m)
if err != nil {
return err
}
b = append(b, 0)
_, err = c.writer.Write(b)
if err != nil {
return err
}
return c.writer.Flush()
}
// Receive receives a method reply.
func (c *Connection) Receive(parameters interface{}, continues *bool, oneshot *bool) error {
type reply struct {
Parameters *json.RawMessage `json:"parameters"`
Continues bool `json:"continues"`
Oneshot bool `json:"oneshot"`
Error string `json:"error"`
}
out, err := c.reader.ReadBytes('\x00')
if err != nil {
return err
}
var m reply
err = json.Unmarshal(out[:len(out)-1], &m)
if err != nil {
return err
}
if m.Error != "" {
return &Error{
Name: m.Error,
Parameters: m.Parameters,
}
}
if continues != nil {
*continues = m.Continues
}
if oneshot != nil {
*oneshot = m.Oneshot
}
if parameters != nil && m.Parameters != nil {
return json.Unmarshal(*m.Parameters, parameters)
}
return nil
}
// Call sends a method call and returns the result of the call.
func (c *Connection) Call(method string, parameters interface{}, result interface{}) error {
err := c.Send(method, &parameters, false)
if err != nil {
return err
}
return c.Receive(result, nil, nil)
}
// GetInterfaceDescription requests the interface description string from the service.
func (c *Connection) GetInterfaceDescription(name string) (string, error) {
type request struct {
Interface string `json:"interface"`
}
type reply struct {
Description string `json:"description"`
}
var r reply
err := c.Call("org.varlink.service.GetInterfaceDescription", request{Interface: name}, &r)
if err != nil {
return "", err
}
return r.Description, nil
}
// GetInfo requests information about the service.
func (c *Connection) GetInfo(vendor *string, product *string, version *string, url *string, interfaces *[]string) error {
type reply struct {
Vendor string `json:"vendor"`
Product string `json:"product"`
Version string `json:"version"`
URL string `json:"url"`
Interfaces []string `json:"interfaces"`
}
var r reply
err := c.Call("org.varlink.service.GetInfo", nil, &r)
if err != nil {
return err
}
if vendor != nil {
*vendor = r.Vendor
}
if product != nil {
*product = r.Product
}
if version != nil {
*version = r.Version
}
if url != nil {
*url = r.URL
}
if interfaces != nil {
*interfaces = r.Interfaces
}
return nil
}
// Close terminates the connection.
func (c *Connection) Close() error {
return c.conn.Close()
}
// NewConnection returns a new connection to the given address.
func NewConnection(address string) (*Connection, error) {
var err error
words := strings.SplitN(address, ":", 2)
protocol := words[0]
addr := words[1]
// Ignore parameters after ';'
words = strings.SplitN(addr, ";", 2)
if words != nil {
addr = words[0]
}
switch protocol {
case "unix":
break
case "tcp":
break
}
c := Connection{}
c.conn, err = net.Dial(protocol, addr)
if err != nil {
return nil, err
}
c.address = address
c.reader = bufio.NewReader(c.conn)
c.writer = bufio.NewWriter(c.conn)
return &c, nil
}

63
vendor/github.com/varlink/go/varlink/doc.go generated vendored Normal file
View File

@ -0,0 +1,63 @@
/*
Package varlink provides varlink client and server implementations. See http://varlink.org
for more information about varlink.
Example varlink interface definition in a org.example.this.varlink file:
interface org.example.this
method Ping(in: string) -> (out: string)
Generated Go module in a orgexamplethis/orgexamplethis.go file. The generated module
provides reply methods for all methods specified in the varlink interface description.
The stub implementations return a MethodNotImplemented error; the service implementation
using this module will override the methods with its own implementation.
// Generated with github.com/varlink/go/cmd/varlink-go-interface-generator
package orgexamplethis
import "github.com/varlink/go/varlink"
type orgexamplethisInterface interface {
Ping(c VarlinkCall, in string) error
}
type VarlinkCall struct{ varlink.Call }
func (c *VarlinkCall) ReplyPing(out string) error {
var out struct {
Out string `json:"out,omitempty"`
}
out.Out = out
return c.Reply(&out)
}
func (s *VarlinkInterface) Ping(c VarlinkCall, in string) error {
return c.ReplyMethodNotImplemented("Ping")
}
[...]
Service implementing the interface and its method:
import ("orgexamplethis")
type Data struct {
orgexamplethis.VarlinkInterface
data string
}
data := Data{data: "test"}
func (d *Data) Ping(call orgexamplethis.VarlinkCall, ping string) error {
return call.ReplyPing(ping)
}
service, _ = varlink.NewService(
"Example",
"This",
"1",
"https://example.org/this",
)
service.RegisterInterface(orgexamplethis.VarlinkNew(&data))
err := service.Listen("unix:/run/org.example.this", 0)
*/
package varlink

144
vendor/github.com/varlink/go/varlink/external_test.go generated vendored Normal file
View File

@ -0,0 +1,144 @@
package varlink_test
// test with no internal access
import (
"github.com/varlink/go/varlink"
"os"
"testing"
"time"
)
type VarlinkInterface struct{}
func (s *VarlinkInterface) VarlinkDispatch(call varlink.Call, methodname string) error {
return call.ReplyMethodNotImplemented(methodname)
}
func (s *VarlinkInterface) VarlinkGetName() string {
return `org.example.test`
}
func (s *VarlinkInterface) VarlinkGetDescription() string {
return "#"
}
type VarlinkInterface2 struct{}
func (s *VarlinkInterface2) VarlinkDispatch(call varlink.Call, methodname string) error {
return call.ReplyMethodNotImplemented(methodname)
}
func (s *VarlinkInterface2) VarlinkGetName() string {
return `org.example.test2`
}
func (s *VarlinkInterface2) VarlinkGetDescription() string {
return "#"
}
func TestRegisterService(t *testing.T) {
newTestInterface := new(VarlinkInterface)
service, err := varlink.NewService(
"Varlink",
"Varlink Test",
"1",
"https://github.com/varlink/go/varlink",
)
if err != nil {
t.Fatalf("NewService(): %v", err)
}
if err := service.RegisterInterface(newTestInterface); err != nil {
t.Fatalf("Couldn't register service: %v", err)
}
if err := service.RegisterInterface(newTestInterface); err == nil {
t.Fatal("Could register service twice")
}
defer func() { service.Shutdown() }()
servererror := make(chan error)
go func() {
servererror <- service.Listen("unix:@varlinkexternal_TestRegisterService", 0)
}()
time.Sleep(time.Second / 5)
n := new(VarlinkInterface2)
if err := service.RegisterInterface(n); err == nil {
t.Fatal("Could register service while running")
}
time.Sleep(time.Second / 5)
service.Shutdown()
if err := <-servererror; err != nil {
t.Fatalf("service.Listen(): %v", err)
}
}
func TestUnix(t *testing.T) {
newTestInterface := new(VarlinkInterface)
service, err := varlink.NewService(
"Varlink",
"Varlink Test",
"1",
"https://github.com/varlink/go/varlink",
)
if err != nil {
t.Fatalf("NewService(): %v", err)
}
if err := service.RegisterInterface(newTestInterface); err != nil {
t.Fatalf("RegisterInterface(): %v", err)
}
servererror := make(chan error)
go func() {
servererror <- service.Listen("unix:varlinkexternal_TestUnix", 0)
}()
time.Sleep(time.Second / 5)
service.Shutdown()
if err := <-servererror; err != nil {
t.Fatalf("service.Listen(): %v", err)
}
}
func TestListenFDSNotInt(t *testing.T) {
newTestInterface := new(VarlinkInterface)
service, err := varlink.NewService(
"Varlink",
"Varlink Test",
"1",
"https://github.com/varlink/go/varlink",
)
if err != nil {
t.Fatalf("NewService(): %v", err)
}
if err := service.RegisterInterface(newTestInterface); err != nil {
t.Fatalf("Couldn't register service: %v", err)
}
os.Setenv("LISTEN_FDS", "foo")
servererror := make(chan error)
go func() {
servererror <- service.Listen("unix:varlinkexternal_TestListenFDSNotInt", 0)
}()
time.Sleep(time.Second / 5)
service.Shutdown()
err = <-servererror
if err != nil {
t.Fatalf("service.Run(): %v", err)
}
}

465
vendor/github.com/varlink/go/varlink/idl/idl.go generated vendored Normal file
View File

@ -0,0 +1,465 @@
// Package idl provides a varlink interface description parser.
package idl
import (
"bytes"
"fmt"
"regexp"
)
// Valid TypeKind values.
const (
TypeBool = iota
TypeInt
TypeFloat
TypeString
TypeArray
TypeMaybe
TypeStruct
TypeEnum
TypeAlias
)
// TypeKind specifies the type of an Type.
type TypeKind uint
// Type represents a varlink type. Types are method input and output parameters,
// error output parameters, or custom defined types in the interface description.
type Type struct {
Kind TypeKind
ElementType *Type
Alias string
Fields []TypeField
}
// TypeField is a named member of a TypeStruct.
type TypeField struct {
Name string
Type *Type
}
// Alias represents a named Type in the interface description.
type Alias struct {
Name string
Doc string
Type *Type
}
// Method represents a method defined in the interface description.
type Method struct {
Name string
Doc string
In *Type
Out *Type
}
// Error represents an error defined in the interface description.
type Error struct {
Name string
Type *Type
}
// IDL represents a parsed varlink interface description with types, methods, errors and
// documentation.
type IDL struct {
Name string
Doc string
Description string
Members []interface{}
Aliases map[string]*Alias
Methods map[string]*Method
Errors map[string]*Error
}
type parser struct {
input string
position int
lineStart int
lastComment bytes.Buffer
}
func (p *parser) next() int {
r := -1
if p.position < len(p.input) {
r = int(p.input[p.position])
}
p.position++
return r
}
func (p *parser) backup() {
p.position--
}
func (p *parser) advance() bool {
for {
char := p.next()
if char == '\n' {
p.lineStart = p.position
p.lastComment.Reset()
} else if char == ' ' || char == '\t' {
// ignore
} else if char == '#' {
p.next()
start := p.position
for {
c := p.next()
if c < 0 || c == '\n' {
p.backup()
break
}
}
if p.lastComment.Len() > 0 {
p.lastComment.WriteByte('\n')
}
p.lastComment.WriteString(p.input[start:p.position])
p.next()
} else {
p.backup()
break
}
}
return p.position < len(p.input)
}
func (p *parser) advanceOnLine() {
for {
char := p.next()
if char != ' ' {
p.backup()
return
}
}
}
func (p *parser) readKeyword() string {
start := p.position
for {
char := p.next()
if char < 'a' || char > 'z' {
p.backup()
break
}
}
return p.input[start:p.position]
}
func (p *parser) readInterfaceName() string {
start := p.position
dnrx := regexp.MustCompile(`^[a-z]+(\.[a-z0-9]+([-][a-z0-9]+)*)+`)
name := dnrx.FindString(p.input[start:])
if name != "" {
if len(name) > 255 {
return ""
}
p.position += len(name)
return name
}
xdnrx := regexp.MustCompile(`^xn--[a-z0-9]+(\.[a-z0-9]+([-][a-z0-9]+)*)+`)
name = xdnrx.FindString(p.input[start:])
if name != "" {
if len(name) > 255 {
return ""
}
p.position += len(name)
return name
}
return ""
}
func (p *parser) readFieldName() string {
start := p.position
char := p.next()
if char < 'a' || char > 'z' {
p.backup()
return ""
}
for {
char := p.next()
if (char < 'A' || char > 'Z') && (char < 'a' || char > 'z') && (char < '0' || char > '9') && char != '_' {
p.backup()
break
}
}
return p.input[start:p.position]
}
func (p *parser) readTypeName() string {
start := p.position
for {
char := p.next()
if (char < 'A' || char > 'Z') && (char < 'a' || char > 'z') && (char < '0' || char > '9') {
p.backup()
break
}
}
return p.input[start:p.position]
}
func (p *parser) readStructType() *Type {
if p.next() != '(' {
p.backup()
return nil
}
t := &Type{Kind: TypeStruct}
t.Fields = make([]TypeField, 0)
char := p.next()
if char != ')' {
p.backup()
for {
field := TypeField{}
p.advance()
field.Name = p.readFieldName()
if field.Name == "" {
return nil
}
p.advance()
// Enums have no types, they are just a list of names
if p.next() == ':' {
if t.Kind == TypeEnum {
return nil
}
p.advance()
field.Type = p.readType()
if field.Type == nil {
return nil
}
} else {
t.Kind = TypeEnum
p.backup()
}
t.Fields = append(t.Fields, field)
p.advance()
char = p.next()
if char != ',' {
break
}
}
if char != ')' {
return nil
}
}
return t
}
func (p *parser) readType() *Type {
var t *Type
switch p.next() {
case '?':
e := p.readType()
if e == nil {
return nil
}
t = &Type{Kind: TypeMaybe, ElementType: e}
case '[':
if p.next() != ']' {
return nil
}
e := p.readType()
if e == nil {
return nil
}
t = &Type{Kind: TypeArray, ElementType: e}
default:
p.backup()
if keyword := p.readKeyword(); keyword != "" {
switch keyword {
case "bool":
t = &Type{Kind: TypeBool}
case "int":
t = &Type{Kind: TypeInt}
case "float":
t = &Type{Kind: TypeFloat}
case "string":
t = &Type{Kind: TypeString}
}
} else if name := p.readTypeName(); name != "" {
t = &Type{Kind: TypeAlias, Alias: name}
} else if t = p.readStructType(); t == nil {
return nil
}
}
return t
}
func (p *parser) readAlias(idl *IDL) (*Alias, error) {
a := &Alias{}
p.advance()
a.Doc = p.lastComment.String()
a.Name = p.readTypeName()
if a.Name == "" {
return nil, fmt.Errorf("missing type name")
}
p.advance()
a.Type = p.readType()
if a.Type == nil {
return nil, fmt.Errorf("missing type declaration")
}
return a, nil
}
func (p *parser) readMethod(idl *IDL) (*Method, error) {
m := &Method{}
p.advance()
m.Doc = p.lastComment.String()
m.Name = p.readTypeName()
if m.Name == "" {
return nil, fmt.Errorf("missing method type")
}
p.advance()
m.In = p.readType()
if m.In == nil {
return nil, fmt.Errorf("missing method input")
}
p.advance()
one := p.next()
two := p.next()
if (one != '-') || two != '>' {
return nil, fmt.Errorf("missing method '->' operator")
}
p.advance()
m.Out = p.readType()
if m.Out == nil {
return nil, fmt.Errorf("missing method output")
}
return m, nil
}
func (p *parser) readError(idl *IDL) (*Error, error) {
e := &Error{}
p.advance()
e.Name = p.readTypeName()
if e.Name == "" {
return nil, fmt.Errorf("missing error name")
}
p.advanceOnLine()
e.Type = p.readType()
return e, nil
}
func (p *parser) readIDL() (*IDL, error) {
if keyword := p.readKeyword(); keyword != "interface" {
return nil, fmt.Errorf("missing interface keyword")
}
idl := &IDL{
Members: make([]interface{}, 0),
Aliases: make(map[string]*Alias),
Methods: make(map[string]*Method),
Errors: make(map[string]*Error),
}
p.advance()
idl.Doc = p.lastComment.String()
idl.Name = p.readInterfaceName()
if idl.Name == "" {
return nil, fmt.Errorf("interface name")
}
for {
if !p.advance() {
break
}
switch keyword := p.readKeyword(); keyword {
case "type":
a, err := p.readAlias(idl)
if err != nil {
return nil, err
}
idl.Members = append(idl.Members, a)
idl.Aliases[a.Name] = a
case "method":
m, err := p.readMethod(idl)
if err != nil {
return nil, err
}
idl.Members = append(idl.Members, m)
if _, ok := idl.Methods[m.Name]; ok {
return nil, fmt.Errorf("method `%s` already defined", m.Name)
}
idl.Methods[m.Name] = m
case "error":
e, err := p.readError(idl)
if err != nil {
return nil, err
}
idl.Members = append(idl.Members, e)
idl.Errors[e.Name] = e
default:
return nil, fmt.Errorf("unknown keyword '%s'", keyword)
}
}
return idl, nil
}
// New parses a varlink interface description.
func New(description string) (*IDL, error) {
p := &parser{input: description}
p.advance()
idl, err := p.readIDL()
if err != nil {
return nil, err
}
if len(idl.Methods) == 0 {
return nil, fmt.Errorf("no methods defined")
}
idl.Description = description
return idl, nil
}

127
vendor/github.com/varlink/go/varlink/idl/idl_test.go generated vendored Normal file
View File

@ -0,0 +1,127 @@
package idl
import (
"fmt"
"runtime"
"testing"
)
/*
func expect(t *testing.T, expected string, returned string) {
if strings.Compare(returned, expected) != 0 {
t.Fatalf("Expected(%d): `%s`\nGot(%d): `%s`\n",
len(expected), expected,
len(returned), returned)
}
}
*/
func testParse(t *testing.T, pass bool, description string) {
_, _, line, _ := runtime.Caller(1)
t.Run(fmt.Sprintf("Line-%d", line), func(t *testing.T) {
midl, err := New(description)
if pass {
if err != nil {
t.Fatalf("generateTemplate(`%s`): %v", description, err)
}
if len(midl.Name) <= 0 {
t.Fatalf("generateTemplate(`%s`): returned no pkgname", description)
}
}
if !pass && (err == nil) {
t.Fatalf("generateTemplate(`%s`): did not fail", description)
}
})
}
func TestOneMethod(t *testing.T) {
testParse(t, true, "interface foo.bar\nmethod Foo()->()")
}
func TestOneMethodNoType(t *testing.T) {
testParse(t, false, "interface foo.bar\nmethod Foo()->(b:)")
}
func TestDomainNames(t *testing.T) {
testParse(t, true, "interface org.varlink.service\nmethod F()->()")
testParse(t, true, "interface com.example.0example\nmethod F()->()")
testParse(t, true, "interface com.example.example-dash\nmethod F()->()")
testParse(t, true, "interface xn--lgbbat1ad8j.example.algeria\nmethod F()->()")
testParse(t, false, "interface com.-example.leadinghyphen\nmethod F()->()")
testParse(t, false, "interface com.example-.danglinghyphen-\nmethod F()->()")
testParse(t, false, "interface Com.example.uppercase-toplevel\nmethod F()->()")
testParse(t, false, "interface Co9.example.number-toplevel\nmethod F()->()")
testParse(t, false, "interface 1om.example.number-toplevel\nmethod F()->()")
testParse(t, false, "interface com.Example\nmethod F()->()")
var name string
for i := 0; i < 255; i++ {
name += "a"
}
testParse(t, false, "interface com.example.toolong"+name+"\nmethod F()->()")
testParse(t, false, "interface xn--example.toolong"+name+"\nmethod F()->()")
}
func TestNoMethod(t *testing.T) {
testParse(t, false, `
interface org.varlink.service
type Interface (name: string, types: []Type, methods: []Method)
type Property (key: string, value: string)
`)
}
func TestTypeNoArgs(t *testing.T) {
testParse(t, true, "interface foo.bar\n type I ()\nmethod F()->()")
}
func TestTypeOneArg(t *testing.T) {
testParse(t, true, "interface foo.bar\n type I (b:bool)\nmethod F()->()")
}
func TestTypeOneArray(t *testing.T) {
testParse(t, true, "interface foo.bar\n type I (b:[]bool)\nmethod F()->()")
testParse(t, false, "interface foo.bar\n type I (b:bool[ ])\nmethod F()->()")
testParse(t, false, "interface foo.bar\n type I (b:bool[1])\nmethod F()->()")
testParse(t, false, "interface foo.bar\n type I (b:bool[ 1 ])\nmethod F()->()")
testParse(t, false, "interface foo.bar\n type I (b:bool[ 1 1 ])\nmethod F()->()")
}
func TestFieldnames(t *testing.T) {
testParse(t, false, "interface foo.bar\n type I (Test:[]bool)\nmethod F()->()")
testParse(t, false, "interface foo.bar\n type I (_test:[]bool)\nmethod F()->()")
testParse(t, false, "interface foo.bar\n type I (Äest:[]bool)\nmethod F()->()")
}
func TestNestedStructs(t *testing.T) {
testParse(t, true, "interface foo.bar\n type I ( b: [](foo: bool, bar: bool, baz: int) )\nmethod F()->()")
}
func TestEnum(t *testing.T) {
testParse(t, true, "interface foo.bar\n type I (b:(foo, bar, baz))\nmethod F()->()")
testParse(t, false, "interface foo.bar\n type I (foo, bar, baz : bool)\nmethod F()->()")
}
func TestIncomplete(t *testing.T) {
testParse(t, false, "interfacef foo.bar\nmethod F()->()")
testParse(t, false, "interface foo.bar\nmethod F()->()\ntype I (b: bool")
testParse(t, false, "interface foo.bar\nmethod F()->(")
testParse(t, false, "interface foo.bar\nmethod F(")
testParse(t, false, "interface foo.bar\nmethod ()->()")
testParse(t, false, "interface foo.bar\nmethod F->()\n")
testParse(t, false, "interface foo.bar\nmethod F()->\n")
testParse(t, false, "interface foo.bar\nmethod F()>()\n")
testParse(t, false, "interface foo.bar\nmethod F()->()\ntype (b: bool)")
testParse(t, false, "interface foo.bar\nmethod F()->()\nerror (b: bool)")
testParse(t, false, "interface foo.bar\nmethod F()->()\n dfghdrg")
}
func TestDuplicate(t *testing.T) {
testParse(t, false, `
interface foo.example
type Device()
type Device()
type T()
type T()
method F() -> ()
method F() -> ()
`)
}

View File

@ -0,0 +1,133 @@
package varlink
func doReplyError(c *Call, name string, parameters interface{}) error {
return c.sendMessage(&serviceReply{
Error: name,
Parameters: parameters,
})
}
// ReplyInterfaceNotFound sends a org.varlink.service errror reply to this method call
func (c *Call) ReplyInterfaceNotFound(interfaceA string) error {
var out struct {
Interface string `json:"interface,omitempty"`
}
out.Interface = interfaceA
return doReplyError(c, "org.varlink.service.InterfaceNotFound", &out)
}
// ReplyMethodNotFound sends a org.varlink.service errror reply to this method call
func (c *Call) ReplyMethodNotFound(method string) error {
var out struct {
Method string `json:"method,omitempty"`
}
out.Method = method
return doReplyError(c, "org.varlink.service.MethodNotFound", &out)
}
// ReplyMethodNotImplemented sends a org.varlink.service errror reply to this method call
func (c *Call) ReplyMethodNotImplemented(method string) error {
var out struct {
Method string `json:"method,omitempty"`
}
out.Method = method
return doReplyError(c, "org.varlink.service.MethodNotImplemented", &out)
}
// ReplyInvalidParameter sends a org.varlink.service errror reply to this method call
func (c *Call) ReplyInvalidParameter(parameter string) error {
var out struct {
Parameter string `json:"parameter,omitempty"`
}
out.Parameter = parameter
return doReplyError(c, "org.varlink.service.InvalidParameter", &out)
}
func (c *Call) replyGetInfo(vendor string, product string, version string, url string, interfaces []string) error {
var out struct {
Vendor string `json:"vendor,omitempty"`
Product string `json:"product,omitempty"`
Version string `json:"version,omitempty"`
URL string `json:"url,omitempty"`
Interfaces []string `json:"interfaces,omitempty"`
}
out.Vendor = vendor
out.Product = product
out.Version = version
out.URL = url
out.Interfaces = interfaces
return c.Reply(&out)
}
func (c *Call) replyGetInterfaceDescription(description string) error {
var out struct {
Description string `json:"description,omitempty"`
}
out.Description = description
return c.Reply(&out)
}
func (s *Service) orgvarlinkserviceDispatch(c Call, methodname string) error {
switch methodname {
case "GetInfo":
return s.getInfo(c)
case "GetInterfaceDescription":
var in struct {
Interface string `json:"interface"`
}
err := c.GetParameters(&in)
if err != nil {
return c.ReplyInvalidParameter("parameters")
}
return s.getInterfaceDescription(c, in.Interface)
default:
return c.ReplyMethodNotFound(methodname)
}
}
func (s *orgvarlinkserviceInterface) VarlinkDispatch(call Call, methodname string) error {
return nil
}
func (s *orgvarlinkserviceInterface) VarlinkGetName() string {
return `org.varlink.service`
}
func (s *orgvarlinkserviceInterface) VarlinkGetDescription() string {
return `# The Varlink Service Interface is provided by every varlink service. It
# describes the service and the interfaces it implements.
interface org.varlink.service
# Get a list of all the interfaces a service provides and information
# about the implementation.
method GetInfo() -> (
vendor: string,
product: string,
version: string,
url: string,
interfaces: []string
)
# Get the description of an interface that is implemented by this service.
method GetInterfaceDescription(interface: string) -> (description: string)
# The requested interface was not found.
error InterfaceNotFound (interface: string)
# The requested method was not found
error MethodNotFound (method: string)
# The interface defines the requested method, but the service does not
# implement it.
error MethodNotImplemented (method: string)
# One of the passed parameters is invalid.
error InvalidParameter (parameter: string)`
}
type orgvarlinkserviceInterface struct{}
func orgvarlinkserviceNew() *orgvarlinkserviceInterface {
return &orgvarlinkserviceInterface{}
}

92
vendor/github.com/varlink/go/varlink/resolver.go generated vendored Normal file
View File

@ -0,0 +1,92 @@
package varlink
// ResolverAddress is the well-known address of the varlink interface resolver,
// it translates varlink interface names to varlink service addresses.
const ResolverAddress = "unix:/run/org.varlink.resolver"
// Resolver resolves varlink interface names to varlink addresses
type Resolver struct {
address string
conn *Connection
}
// Resolve resolves a varlink interface name to a varlink address.
func (r *Resolver) Resolve(iface string) (string, error) {
type request struct {
Interface string `json:"interface"`
}
type reply struct {
Address string `json:"address"`
}
/* don't ask the resolver for itself */
if iface == "org.varlink.resolver" {
return r.address, nil
}
var rep reply
err := r.conn.Call("org.varlink.resolver.Resolve", &request{Interface: iface}, &rep)
if err != nil {
return "", err
}
return rep.Address, nil
}
// GetInfo requests information about the resolver.
func (r *Resolver) GetInfo(vendor *string, product *string, version *string, url *string, interfaces *[]string) error {
type reply struct {
Vendor string
Product string
Version string
URL string
Interfaces []string
}
var rep reply
err := r.conn.Call("org.varlink.resolver.GetInfo", nil, &rep)
if err != nil {
return err
}
if vendor != nil {
*vendor = rep.Vendor
}
if product != nil {
*product = rep.Product
}
if version != nil {
*version = rep.Version
}
if url != nil {
*url = rep.URL
}
if interfaces != nil {
*interfaces = rep.Interfaces
}
return nil
}
// Close terminates the resolver.
func (r *Resolver) Close() error {
return r.conn.Close()
}
// NewResolver returns a new resolver connected to the given address.
func NewResolver(address string) (*Resolver, error) {
if address == "" {
address = ResolverAddress
}
c, err := NewConnection(address)
if err != nil {
return nil, err
}
r := Resolver{
address: address,
conn: c,
}
return &r, nil
}

350
vendor/github.com/varlink/go/varlink/service.go generated vendored Normal file
View File

@ -0,0 +1,350 @@
package varlink
import (
"bufio"
"encoding/json"
"fmt"
"net"
"os"
"strconv"
"strings"
"sync"
"syscall"
"time"
)
type dispatcher interface {
VarlinkDispatch(c Call, methodname string) error
VarlinkGetName() string
VarlinkGetDescription() string
}
type serviceCall struct {
Method string `json:"method"`
Parameters *json.RawMessage `json:"parameters,omitempty"`
More bool `json:"more,omitempty"`
OneShot bool `json:"oneshot,omitempty"`
}
type serviceReply struct {
Parameters interface{} `json:"parameters,omitempty"`
Continues bool `json:"continues,omitempty"`
Error string `json:"error,omitempty"`
}
// Service represents an active varlink service. In addition to the registered custom varlink Interfaces, every service
// implements the org.varlink.service interface which allows clients to retrieve information about the
// running service.
type Service struct {
vendor string
product string
version string
url string
interfaces map[string]dispatcher
names []string
descriptions map[string]string
running bool
listener net.Listener
conncounter int64
mutex sync.Mutex
protocol string
address string
}
func (s *Service) getInfo(c Call) error {
return c.replyGetInfo(s.vendor, s.product, s.version, s.url, s.names)
}
func (s *Service) getInterfaceDescription(c Call, name string) error {
if name == "" {
return c.ReplyInvalidParameter("interface")
}
description, ok := s.descriptions[name]
if !ok {
return c.ReplyInvalidParameter("interface")
}
return c.replyGetInterfaceDescription(description)
}
func (s *Service) handleMessage(writer *bufio.Writer, request []byte) error {
var in serviceCall
err := json.Unmarshal(request, &in)
if err != nil {
return err
}
c := Call{
writer: writer,
in: &in,
}
r := strings.LastIndex(in.Method, ".")
if r <= 0 {
return c.ReplyInvalidParameter("method")
}
interfacename := in.Method[:r]
methodname := in.Method[r+1:]
if interfacename == "org.varlink.service" {
return s.orgvarlinkserviceDispatch(c, methodname)
}
// Find the interface and method in our service
iface, ok := s.interfaces[interfacename]
if !ok {
return c.ReplyInterfaceNotFound(interfacename)
}
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
}
os.Unsetenv("LISTEN_PID")
os.Unsetenv("LISTEN_FDS")
os.Unsetenv("LISTEN_FDNAMES")
return listener
}
// Shutdown shuts down the listener of a running service.
func (s *Service) Shutdown() {
s.running = false
s.mutex.Lock()
if s.listener != nil {
s.listener.Close()
}
s.mutex.Unlock()
}
func (s *Service) handleConnection(conn net.Conn, wg *sync.WaitGroup) {
defer func() { s.mutex.Lock(); s.conncounter--; s.mutex.Unlock(); wg.Done() }()
reader := bufio.NewReader(conn)
writer := bufio.NewWriter(conn)
for {
request, err := reader.ReadBytes('\x00')
if err != nil {
break
}
err = s.handleMessage(writer, request[:len(request)-1])
if err != nil {
// FIXME: report error
//fmt.Fprintf(os.Stderr, "handleMessage: %v", err)
break
}
}
conn.Close()
}
func (s *Service) teardown() {
s.mutex.Lock()
s.listener = nil
s.running = false
s.protocol = ""
s.address = ""
s.mutex.Unlock()
}
func (s *Service) parseAddress(address string) error {
words := strings.SplitN(address, ":", 2)
s.protocol = words[0]
s.address = words[1]
// Ignore parameters after ';'
words = strings.SplitN(s.address, ";", 2)
if words != nil {
s.address = words[0]
}
switch s.protocol {
case "unix":
break
case "tcp":
break
default:
return fmt.Errorf("Unknown protocol")
}
return nil
}
func getListener(protocol string, address string) (net.Listener, error) {
l := activationListener()
if l == nil {
if protocol == "unix" && address[0] != '@' {
os.Remove(address)
}
var err error
l, err = net.Listen(protocol, address)
if err != nil {
return nil, err
}
if protocol == "unix" && address[0] != '@' {
l.(*net.UnixListener).SetUnlinkOnClose(true)
}
}
return l, nil
}
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 {
return err
}
case "tcp":
if err := s.listener.(*net.TCPListener).SetDeadline(time.Now().Add(timeout)); err != nil {
return err
}
}
return nil
}
// Listen starts a Service.
func (s *Service) Listen(address string, timeout time.Duration) error {
var wg sync.WaitGroup
defer func() { s.teardown(); wg.Wait() }()
s.mutex.Lock()
if s.running {
s.mutex.Unlock()
return fmt.Errorf("Listen(): already running")
}
s.mutex.Unlock()
s.parseAddress(address)
l, err := getListener(s.protocol, s.address)
if err != nil {
return err
}
s.mutex.Lock()
s.listener = l
s.running = true
s.mutex.Unlock()
for s.running {
if timeout != 0 {
if err := s.refreshTimeout(timeout); err != nil {
return err
}
}
conn, err := l.Accept()
if err != nil {
if err.(net.Error).Timeout() {
s.mutex.Lock()
if s.conncounter == 0 {
s.mutex.Unlock()
return nil
}
s.mutex.Unlock()
continue
}
if !s.running {
return nil
}
return err
}
s.mutex.Lock()
s.conncounter++
s.mutex.Unlock()
wg.Add(1)
go s.handleConnection(conn, &wg)
}
return nil
}
// RegisterInterface registers a varlink.Interface containing struct to the Service
func (s *Service) RegisterInterface(iface dispatcher) error {
name := iface.VarlinkGetName()
if _, ok := s.interfaces[name]; ok {
return fmt.Errorf("interface '%s' already registered", name)
}
if s.running {
return fmt.Errorf("service is already running")
}
s.interfaces[name] = iface
s.descriptions[name] = iface.VarlinkGetDescription()
s.names = append(s.names, name)
return nil
}
// NewService creates a new Service which implements the list of given varlink interfaces.
func NewService(vendor string, product string, version string, url string) (*Service, error) {
s := Service{
vendor: vendor,
product: product,
version: version,
url: url,
interfaces: make(map[string]dispatcher),
descriptions: make(map[string]string),
}
err := s.RegisterInterface(orgvarlinkserviceNew())
return &s, err
}

232
vendor/github.com/varlink/go/varlink/varlink_test.go generated vendored Normal file
View File

@ -0,0 +1,232 @@
package varlink
// tests with access to internals
import (
"bufio"
"bytes"
"fmt"
"strings"
"testing"
)
func expect(t *testing.T, expected string, returned string) {
if strings.Compare(returned, expected) != 0 {
t.Fatalf("Expected(%d): `%s`\nGot(%d): `%s`\n",
len(expected), expected,
len(returned), strings.Replace(returned, "\000", "`+\"\\000\"+`", -1))
}
}
func TestService(t *testing.T) {
service, _ := NewService(
"Varlink",
"Varlink Test",
"1",
"https://github.com/varlink/go/varlink",
)
t.Run("ZeroMessage", func(t *testing.T) {
var b bytes.Buffer
w := bufio.NewWriter(&b)
if err := service.handleMessage(w, []byte{0}); err == nil {
t.Fatal("HandleMessage returned non-error")
}
})
t.Run("InvalidJson", func(t *testing.T) {
var b bytes.Buffer
w := bufio.NewWriter(&b)
msg := []byte(`{"method":"foo.GetInterfaceDescription" fdgdfg}`)
if err := service.handleMessage(w, msg); err == nil {
t.Fatal("HandleMessage returned no error on invalid json")
}
})
t.Run("WrongInterface", func(t *testing.T) {
var b bytes.Buffer
w := bufio.NewWriter(&b)
msg := []byte(`{"method":"foo.GetInterfaceDescription"}`)
if err := service.handleMessage(w, msg); err != nil {
t.Fatal("HandleMessage returned error on wrong interface")
}
expect(t, `{"parameters":{"interface":"foo"},"error":"org.varlink.service.InterfaceNotFound"}`+"\000",
b.String())
})
t.Run("InvalidMethod", func(t *testing.T) {
var b bytes.Buffer
w := bufio.NewWriter(&b)
msg := []byte(`{"method":"InvalidMethod"}`)
if err := service.handleMessage(w, msg); err != nil {
t.Fatal("HandleMessage returned error on invalid method")
}
expect(t, `{"parameters":{"parameter":"method"},"error":"org.varlink.service.InvalidParameter"}`+"\000",
b.String())
})
t.Run("WrongMethod", func(t *testing.T) {
var b bytes.Buffer
w := bufio.NewWriter(&b)
msg := []byte(`{"method":"org.varlink.service.WrongMethod"}`)
if err := service.handleMessage(w, msg); err != nil {
t.Fatal("HandleMessage returned error on wrong method")
}
expect(t, `{"parameters":{"method":"WrongMethod"},"error":"org.varlink.service.MethodNotFound"}`+"\000",
b.String())
})
t.Run("GetInterfaceDescriptionNullParameters", func(t *testing.T) {
var b bytes.Buffer
w := bufio.NewWriter(&b)
msg := []byte(`{"method":"org.varlink.service.GetInterfaceDescription","parameters": null}`)
if err := service.handleMessage(w, msg); err != nil {
t.Fatalf("HandleMessage returned error: %v", err)
}
expect(t, `{"parameters":{"parameter":"parameters"},"error":"org.varlink.service.InvalidParameter"}`+"\000",
b.String())
})
t.Run("GetInterfaceDescriptionNoInterface", func(t *testing.T) {
var b bytes.Buffer
w := bufio.NewWriter(&b)
msg := []byte(`{"method":"org.varlink.service.GetInterfaceDescription","parameters":{}}`)
if err := service.handleMessage(w, msg); err != nil {
t.Fatalf("HandleMessage returned error: %v", err)
}
expect(t, `{"parameters":{"parameter":"interface"},"error":"org.varlink.service.InvalidParameter"}`+"\000",
b.String())
})
t.Run("GetInterfaceDescriptionWrongInterface", func(t *testing.T) {
var b bytes.Buffer
w := bufio.NewWriter(&b)
msg := []byte(`{"method":"org.varlink.service.GetInterfaceDescription","parameters":{"interface":"foo"}}`)
if err := service.handleMessage(w, msg); err != nil {
t.Fatalf("HandleMessage returned error: %v", err)
}
expect(t, `{"parameters":{"parameter":"interface"},"error":"org.varlink.service.InvalidParameter"}`+"\000",
b.String())
})
t.Run("GetInterfaceDescription", func(t *testing.T) {
var b bytes.Buffer
w := bufio.NewWriter(&b)
msg := []byte(`{"method":"org.varlink.service.GetInterfaceDescription","parameters":{"interface":"org.varlink.service"}}`)
if err := service.handleMessage(w, msg); err != nil {
t.Fatalf("HandleMessage returned error: %v", err)
}
expect(t, `{"parameters":{"description":"# The Varlink Service Interface is provided by every varlink service. It\n# describes the service and the interfaces it implements.\ninterface org.varlink.service\n\n# Get a list of all the interfaces a service provides and information\n# about the implementation.\nmethod GetInfo() -\u003e (\n vendor: string,\n product: string,\n version: string,\n url: string,\n interfaces: []string\n)\n\n# Get the description of an interface that is implemented by this service.\nmethod GetInterfaceDescription(interface: string) -\u003e (description: string)\n\n# The requested interface was not found.\nerror InterfaceNotFound (interface: string)\n\n# The requested method was not found\nerror MethodNotFound (method: string)\n\n# The interface defines the requested method, but the service does not\n# implement it.\nerror MethodNotImplemented (method: string)\n\n# One of the passed parameters is invalid.\nerror InvalidParameter (parameter: string)"}}`+"\000",
b.String())
})
t.Run("GetInfo", func(t *testing.T) {
var b bytes.Buffer
w := bufio.NewWriter(&b)
msg := []byte(`{"method":"org.varlink.service.GetInfo"}`)
if err := service.handleMessage(w, msg); err != nil {
t.Fatalf("HandleMessage returned error: %v", err)
}
expect(t, `{"parameters":{"vendor":"Varlink","product":"Varlink Test","version":"1","url":"https://github.com/varlink/go/varlink","interfaces":["org.varlink.service"]}}`+"\000",
b.String())
})
}
type VarlinkInterface struct{}
func (s *VarlinkInterface) VarlinkDispatch(call Call, methodname string) error {
switch methodname {
case "Ping":
if !call.WantsMore() {
return fmt.Errorf("More flag not passed")
}
if call.IsOneShot() {
return fmt.Errorf("OneShot flag set")
}
call.Continues = true
if err := call.Reply(nil); err != nil {
return err
}
if err := call.Reply(nil); err != nil {
return err
}
call.Continues = false
if err := call.Reply(nil); err != nil {
return err
}
return nil
case "PingError":
return call.ReplyError("org.example.test.PingError", nil)
}
call.Continues = true
if err := call.Reply(nil); err == nil {
return fmt.Errorf("call.Reply did not fail for Continues/More mismatch")
}
call.Continues = false
if err := call.ReplyError("WrongName", nil); err == nil {
return fmt.Errorf("call.ReplyError accepted invalid error name")
}
if err := call.ReplyError("org.varlink.service.MethodNotImplemented", nil); err == nil {
return fmt.Errorf("call.ReplyError accepted org.varlink.service error")
}
return call.ReplyMethodNotImplemented(methodname)
}
func (s *VarlinkInterface) VarlinkGetName() string {
return `org.example.test`
}
func (s *VarlinkInterface) VarlinkGetDescription() string {
return "#"
}
func TestMoreService(t *testing.T) {
newTestInterface := new(VarlinkInterface)
service, _ := NewService(
"Varlink",
"Varlink Test",
"1",
"https://github.com/varlink/go/varlink",
)
if err := service.RegisterInterface(newTestInterface); err != nil {
t.Fatalf("Couldn't register service: %v", err)
}
t.Run("MethodNotImplemented", func(t *testing.T) {
var b bytes.Buffer
w := bufio.NewWriter(&b)
msg := []byte(`{"method":"org.example.test.Pingf"}`)
if err := service.handleMessage(w, msg); err != nil {
t.Fatalf("HandleMessage returned error: %v", err)
}
expect(t, `{"parameters":{"method":"Pingf"},"error":"org.varlink.service.MethodNotImplemented"}`+"\000",
b.String())
})
t.Run("PingError", func(t *testing.T) {
var b bytes.Buffer
w := bufio.NewWriter(&b)
msg := []byte(`{"method":"org.example.test.PingError", "more" : true}`)
if err := service.handleMessage(w, msg); err != nil {
t.Fatalf("HandleMessage returned error: %v", err)
}
expect(t, `{"error":"org.example.test.PingError"}`+"\000",
b.String())
})
t.Run("MoreTest", func(t *testing.T) {
var b bytes.Buffer
w := bufio.NewWriter(&b)
msg := []byte(`{"method":"org.example.test.Ping", "more" : true}`)
if err := service.handleMessage(w, msg); err != nil {
t.Fatalf("HandleMessage returned error: %v", err)
}
expect(t, `{"continues":true}`+"\000"+`{"continues":true}`+"\000"+`{}`+"\000",
b.String())
})
}