Initial varlink implementation
Signed-off-by: baude <bbaude@redhat.com> Closes: #627 Approved by: mheon
This commit is contained in:
parent
cf1d884ffa
commit
8493dba23c
|
@ -11,3 +11,4 @@
|
||||||
/test/checkseccomp/checkseccomp
|
/test/checkseccomp/checkseccomp
|
||||||
/test/copyimg/copyimg
|
/test/copyimg/copyimg
|
||||||
/build/
|
/build/
|
||||||
|
cmd/podman/ioprojectatomicpodman/ioprojectatomicpodman.go
|
||||||
|
|
|
@ -79,4 +79,6 @@ RUN mkdir -p /etc/containers
|
||||||
COPY test/policy.json /etc/containers/policy.json
|
COPY test/policy.json /etc/containers/policy.json
|
||||||
COPY test/redhat_sigstore.yaml /etc/containers/registries.d/registry.access.redhat.com.yaml
|
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
|
WORKDIR /go/src/github.com/projectatomic/libpod
|
||||||
|
|
26
Makefile
26
Makefile
|
@ -15,7 +15,9 @@ MANDIR ?= ${PREFIX}/share/man
|
||||||
SHAREDIR_CONTAINERS ?= ${PREFIX}/share/containers
|
SHAREDIR_CONTAINERS ?= ${PREFIX}/share/containers
|
||||||
ETCDIR ?= ${DESTDIR}/etc
|
ETCDIR ?= ${DESTDIR}/etc
|
||||||
ETCDIR_LIBPOD ?= ${ETCDIR}/crio
|
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)
|
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
|
BASHINSTALLDIR=${PREFIX}/share/bash-completion/completions
|
||||||
OCIUMOUNTINSTALLDIR=$(PREFIX)/share/oci-umount/oci-umount.d
|
OCIUMOUNTINSTALLDIR=$(PREFIX)/share/oci-umount/oci-umount.d
|
||||||
|
@ -65,7 +67,7 @@ ifeq ("$(wildcard $(GOPKGDIR))","")
|
||||||
endif
|
endif
|
||||||
touch "$(GOPATH)/.gopathok"
|
touch "$(GOPATH)/.gopathok"
|
||||||
|
|
||||||
lint: .gopathok
|
lint: .gopathok varlink_generate
|
||||||
@echo "checking lint"
|
@echo "checking lint"
|
||||||
@./.tool/lint
|
@./.tool/lint
|
||||||
|
|
||||||
|
@ -101,6 +103,7 @@ endif
|
||||||
rm -f test/copyimg/copyimg
|
rm -f test/copyimg/copyimg
|
||||||
rm -f test/checkseccomp/checkseccomp
|
rm -f test/checkseccomp/checkseccomp
|
||||||
rm -fr build/
|
rm -fr build/
|
||||||
|
rm -f cmd/podman/ioprojectatomicpodman/ioprojectatomicpodman.go
|
||||||
|
|
||||||
libpodimage:
|
libpodimage:
|
||||||
docker build -t ${LIBPOD_IMAGE} .
|
docker build -t ${LIBPOD_IMAGE} .
|
||||||
|
@ -126,19 +129,20 @@ shell: libpodimage
|
||||||
testunit: 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
|
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)
|
$(GO) test -tags "$(BUILDTAGS)" -cover $(PACKAGES)
|
||||||
|
|
||||||
ginkgo:
|
ginkgo:
|
||||||
ginkgo -v test/e2e/
|
ginkgo -v test/e2e/
|
||||||
|
|
||||||
localintegration: test-binaries
|
localintegration: varlink_generate test-binaries
|
||||||
ginkgo -v -cover -flakeAttempts 3 -progress -trace -noColor test/e2e/.
|
ginkgo -v -cover -flakeAttempts 3 -progress -trace -noColor test/e2e/.
|
||||||
|
sh test/varlink/run_varlink_tests.sh
|
||||||
|
|
||||||
vagrant-check:
|
vagrant-check:
|
||||||
BOX=$(BOX) sh ./vagrant.sh
|
BOX=$(BOX) sh ./vagrant.sh
|
||||||
|
|
||||||
binaries: podman
|
binaries: varlink_generate podman
|
||||||
|
|
||||||
test-binaries: test/bin2img/bin2img test/copyimg/copyimg test/checkseccomp/checkseccomp
|
test-binaries: test/bin2img/bin2img test/copyimg/copyimg test/checkseccomp/checkseccomp
|
||||||
|
|
||||||
|
@ -163,7 +167,7 @@ changelog:
|
||||||
$(shell cat $(TMPFILE) >> changelog.txt)
|
$(shell cat $(TMPFILE) >> changelog.txt)
|
||||||
$(shell rm $(TMPFILE))
|
$(shell rm $(TMPFILE))
|
||||||
|
|
||||||
install: .gopathok install.bin install.man install.cni
|
install: .gopathok install.bin install.man install.cni install.systemd
|
||||||
|
|
||||||
install.bin:
|
install.bin:
|
||||||
install ${SELINUXOPT} -D -m 755 bin/podman $(BINDIR)/podman
|
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} -d -m 755 $(MANDIR)/man1
|
||||||
install ${SELINUXOPT} -m 644 docs/docker*.1 -t $(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:
|
uninstall:
|
||||||
for i in $(filter %.1,$(MANPAGES)); do \
|
for i in $(filter %.1,$(MANPAGES)); do \
|
||||||
rm -f $(MANDIR)/man1/$$(basename $${i}); \
|
rm -f $(MANDIR)/man1/$$(basename $${i}); \
|
||||||
|
@ -229,6 +237,14 @@ install.tools: .install.gitvalidation .install.gometalinter .install.md2man
|
||||||
make all install; \
|
make all install; \
|
||||||
fi
|
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
|
validate: gofmt .gitvalidation
|
||||||
|
|
||||||
.PHONY: \
|
.PHONY: \
|
||||||
|
|
|
@ -79,6 +79,6 @@ func debugInfo(c *cli.Context) map[string]interface{} {
|
||||||
info["compiler"] = runtime.Compiler
|
info["compiler"] = runtime.Compiler
|
||||||
info["go version"] = runtime.Version()
|
info["go version"] = runtime.Version()
|
||||||
info["podman version"] = c.App.Version
|
info["podman version"] = c.App.Version
|
||||||
info["git commit"] = gitCommit
|
info["git commit"] = libpod.GitCommit
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
package ioprojectatomicpodman
|
||||||
|
|
||||||
|
//go:generate $GOPATH/bin/varlink-go-interface-generator io.projectatomic.podman.varlink
|
|
@ -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)
|
|
@ -70,6 +70,7 @@ func main() {
|
||||||
topCommand,
|
topCommand,
|
||||||
umountCommand,
|
umountCommand,
|
||||||
unpauseCommand,
|
unpauseCommand,
|
||||||
|
varlinkCommand,
|
||||||
versionCommand,
|
versionCommand,
|
||||||
waitCommand,
|
waitCommand,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -2,41 +2,30 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/projectatomic/libpod/libpod"
|
||||||
"github.com/urfave/cli"
|
"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
|
// versionCmd gets and prints version info for version command
|
||||||
func versionCmd(c *cli.Context) error {
|
func versionCmd(c *cli.Context) error {
|
||||||
fmt.Println("Version: ", c.App.Version)
|
output, err := libpod.GetVersion()
|
||||||
fmt.Println("Go Version: ", runtime.Version())
|
if err != nil {
|
||||||
if gitCommit != "" {
|
errors.Wrapf(err, "unable to determine version")
|
||||||
fmt.Println("Git Commit: ", gitCommit)
|
|
||||||
}
|
}
|
||||||
if buildInfo != "" {
|
fmt.Println("Version: ", output.Version)
|
||||||
// Converts unix time from string to int64
|
fmt.Println("Go Version: ", output.GoVersion)
|
||||||
buildTime, err := strconv.ParseInt(buildInfo, 10, 64)
|
if output.GitCommit != "" {
|
||||||
if err != nil {
|
fmt.Println("Git Commit: ", output.GitCommit)
|
||||||
return err
|
}
|
||||||
}
|
// Prints out the build time in readable format
|
||||||
// Prints out the build time in readable format
|
if libpod.BuildInfo != "" {
|
||||||
fmt.Println("Built: ", time.Unix(buildTime, 0).Format(time.ANSIC))
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,5 +42,6 @@
|
||||||
| [podman-top(1)](/docs/podman-top.1.md) | Display the running processes of a container |[](https://asciinema.org/a/5WCCi1LXwSuRbvaO9cBUYf3fk)|
|
| [podman-top(1)](/docs/podman-top.1.md) | Display the running processes of a container |[](https://asciinema.org/a/5WCCi1LXwSuRbvaO9cBUYf3fk)|
|
||||||
| [podman-umount(1)](/docs/podman-umount.1.md) | Unmount a working container's root filesystem |[](https://asciinema.org/a/MZPTWD5CVs3dMREkBxQBY9C5z)|
|
| [podman-umount(1)](/docs/podman-umount.1.md) | Unmount a working container's root filesystem |[](https://asciinema.org/a/MZPTWD5CVs3dMREkBxQBY9C5z)|
|
||||||
| [podman-unpause(1)](/docs/podman-unpause.1.md) | Unpause one or more running containers |[](https://asciinema.org/a/141292)|
|
| [podman-unpause(1)](/docs/podman-unpause.1.md) | Unpause one or more running containers |[](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 |[](https://asciinema.org/a/mfrn61pjZT9Fc8L4NbfdSqfgu)|
|
| [podman-version(1)](/docs/podman-version.1.md) | Display the version information |[](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 |[](https://asciinema.org/a/QNPGKdjWuPgI96GcfkycQtah0)|
|
| [podman-wait(1)](/docs/podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes |[](https://asciinema.org/a/QNPGKdjWuPgI96GcfkycQtah0)|
|
||||||
|
|
|
@ -1514,6 +1514,14 @@ _podman_unpause() {
|
||||||
_complete_ "$options_with_args" "$boolean_options"
|
_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() {
|
_podman_wait() {
|
||||||
local options_with_args=""
|
local options_with_args=""
|
||||||
local boolean_options="
|
local boolean_options="
|
||||||
|
@ -1633,6 +1641,7 @@ _podman_podman() {
|
||||||
umount
|
umount
|
||||||
unmount
|
unmount
|
||||||
unpause
|
unpause
|
||||||
|
varlink
|
||||||
version
|
version
|
||||||
wait
|
wait
|
||||||
"
|
"
|
||||||
|
|
|
@ -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
|
|
@ -0,0 +1,8 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Pod Manager Socket
|
||||||
|
|
||||||
|
[Socket]
|
||||||
|
ListenStream=/run/io.projectatomic.podman
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=sockets.target
|
|
@ -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>
|
|
@ -10,7 +10,8 @@ find_files() {
|
||||||
-wholename '*/vendor/*' \
|
-wholename '*/vendor/*' \
|
||||||
\) -prune \
|
\) -prune \
|
||||||
\) -name '*.go' \
|
\) -name '*.go' \
|
||||||
-not \( -wholename './_output/*' \)
|
-not \( -wholename './_output/*' \) \
|
||||||
|
-not \( -wholename './cmd/podman/ioprojectatomicpodman/ioprojectatomicpodman.go' \)
|
||||||
}
|
}
|
||||||
FIX=0
|
FIX=0
|
||||||
GOFMT="gofmt -s"
|
GOFMT="gofmt -s"
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
)
|
|
@ -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")
|
||||||
|
}
|
|
@ -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")
|
||||||
|
}
|
|
@ -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",
|
||||||
|
})
|
||||||
|
}
|
|
@ -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}
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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=(',', ': ')))
|
|
@ -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/kube-openapi 275e2ce91dec4c05a4094a7b1daee5560b555ac9 https://github.com/kubernetes/kube-openapi
|
||||||
k8s.io/utils 258e2a2fa64568210fbd6267cf1d8fd87c3cb86e https://github.com/kubernetes/utils
|
k8s.io/utils 258e2a2fa64568210fbd6267cf1d8fd87c3cb86e https://github.com/kubernetes/utils
|
||||||
github.com/mrunalp/fileutils master
|
github.com/mrunalp/fileutils master
|
||||||
|
github.com/varlink/go master https://github.com/varlink/go
|
||||||
|
|
|
@ -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=
|
|
@ -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.
|
|
@ -0,0 +1,3 @@
|
||||||
|
all:
|
||||||
|
go test -v ./...
|
||||||
|
.PHONY: all
|
|
@ -0,0 +1,7 @@
|
||||||
|
[](https://travis-ci.org/varlink/go)
|
||||||
|
[](https://goreportcard.com/report/github.com/varlink/go)
|
||||||
|
[](http://godoc.org/github.com/varlink/go/varlink)
|
||||||
|
[](https://coveralls.io/github/varlink/go?branch=master)
|
||||||
|
[](https://github.com/varlink/go/varlink/releases/latest)
|
||||||
|
|
||||||
|
# go/varlink
|
85
vendor/github.com/varlink/go/cmd/varlink-go-interface-generator/generator_test.go
generated
vendored
Normal file
85
vendor/github.com/varlink/go/cmd/varlink-go-interface-generator/generator_test.go
generated
vendored
Normal 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
|
||||||
|
}
|
294
vendor/github.com/varlink/go/cmd/varlink-go-interface-generator/main.go
generated
vendored
Normal file
294
vendor/github.com/varlink/go/cmd/varlink-go-interface-generator/main.go
generated
vendored
Normal 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])
|
||||||
|
}
|
142
vendor/github.com/varlink/go/cmd/varlink-go-type-generator/main.go
generated
vendored
Normal file
142
vendor/github.com/varlink/go/cmd/varlink-go-type-generator/main.go
generated
vendored
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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,
|
||||||
|
})
|
||||||
|
}
|
|
@ -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, ¶meters, 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
|
||||||
|
}
|
|
@ -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
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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() -> ()
|
||||||
|
`)
|
||||||
|
}
|
|
@ -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{}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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())
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue