[ARVADOS] created: 1.3.0-2717-gb694b711c
Git user
git at public.arvados.org
Tue Jun 23 14:10:10 UTC 2020
at b694b711c69fdfd46b6732a6d6c67e37951dab39 (commit)
commit b694b711c69fdfd46b6732a6d6c67e37951dab39
Author: Tom Clegg <tom at tomclegg.ca>
Date: Tue Jun 23 10:00:45 2020 -0400
15348: Add pam_sm_setcred stub.
Otherwise, auth fails: "PAM unable to resolve symbol: pam_sm_setcred"
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/lib/pam/pam_arvados.go b/lib/pam/pam_arvados.go
index 48e0f5048..e9070599c 100644
--- a/lib/pam/pam_arvados.go
+++ b/lib/pam/pam_arvados.go
@@ -55,6 +55,11 @@ func init() {
}
}
+//export pam_sm_setcred
+func pam_sm_setcred(pamh *C.pam_handle_t, flags, cArgc C.int, cArgv **C.char) C.int {
+ return C.PAM_IGNORE
+}
+
//export pam_sm_authenticate
func pam_sm_authenticate(pamh *C.pam_handle_t, flags, cArgc C.int, cArgv **C.char) C.int {
runtime.GOMAXPROCS(1)
commit 7c9a94f70a895d5293ebdb5953518e417d173191
Author: Tom Clegg <tom at tomclegg.ca>
Date: Tue Jun 23 00:55:47 2020 -0400
15348: Add debug flag & installation note.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/lib/pam/docker_test.go b/lib/pam/docker_test.go
index 455d26441..196a45244 100644
--- a/lib/pam/docker_test.go
+++ b/lib/pam/docker_test.go
@@ -119,6 +119,7 @@ func (s *DockerSuite) runTestClient(c *check.C, args ...string) (stdout, stderr
func (s *DockerSuite) TestSuccess(c *check.C) {
stdout, stderr, err := s.runTestClient(c, "try", "active", arvadostest.ActiveTokenV2)
c.Check(err, check.IsNil)
+ c.Logf("%s", stderr.String())
c.Check(stdout.String(), check.Equals, "")
c.Check(stderr.String(), check.Matches, `(?ms).*authentication succeeded.*`)
}
@@ -136,6 +137,7 @@ func (s *DockerSuite) TestFailure(c *check.C) {
} {
c.Logf("trial: %s", trial.label)
stdout, stderr, err := s.runTestClient(c, "try", trial.username, trial.token)
+ c.Logf("%s", stderr.String())
c.Check(err, check.NotNil)
c.Check(stdout.String(), check.Equals, "")
c.Check(stderr.String(), check.Matches, `(?ms).*authentication failed.*`)
diff --git a/lib/pam/pam_arvados.go b/lib/pam/pam_arvados.go
index ddca355b8..48e0f5048 100644
--- a/lib/pam/pam_arvados.go
+++ b/lib/pam/pam_arvados.go
@@ -2,6 +2,23 @@
//
// SPDX-License-Identifier: Apache-2.0
+// To enable, add an entry in /etc/pam.d/common-auth where pam_unix.so
+// would normally be. Examples:
+//
+// auth [success=1 default=ignore] /usr/lib/pam_arvados.so zzzzz.arvadosapi.com vmhostname.example
+// auth [success=1 default=ignore] /usr/lib/pam_arvados.so zzzzz.arvadosapi.com vmhostname.example insecure debug
+//
+// Replace zzzzz.arvadosapi.com with your controller host or
+// host:port.
+//
+// Replace vmhostname.example with the VM's name as it appears in the
+// Arvados virtual_machine object.
+//
+// Use "insecure" if your API server certificate does not pass name
+// verification.
+//
+// Use "debug" to enable debug log messages.
+
package main
import (
@@ -65,7 +82,7 @@ func pam_sm_authenticate(pamh *C.pam_handle_t, flags, cArgc C.int, cArgv **C.cha
return C.PAM_SUCCESS
}
-func authenticate(logger logrus.FieldLogger, username, token string, argv []string) error {
+func authenticate(logger *logrus.Logger, username, token string, argv []string) error {
hostname := ""
apiHost := ""
insecure := false
@@ -76,6 +93,8 @@ func authenticate(logger logrus.FieldLogger, username, token string, argv []stri
hostname = arg
} else if arg == "insecure" {
insecure = true
+ } else if arg == "debug" {
+ logger.SetLevel(logrus.DebugLevel)
} else {
logger.Warnf("unkown option: %s\n", arg)
}
@@ -117,7 +136,7 @@ func authenticate(logger logrus.FieldLogger, username, token string, argv []stri
}
var links arvados.LinkList
err = arv.RequestAndDecodeContext(ctx, &links, "GET", "arvados/v1/links", nil, arvados.ListOptions{
- Limit: 10000,
+ Limit: 1,
Filters: []arvados.Filter{
{"link_class", "=", "permission"},
{"name", "=", "can_login"},
commit b578113d9c7f0d20d8d6f091b0dfc37e9b788ae8
Author: Tom Clegg <tom at tomclegg.ca>
Date: Tue Jun 23 00:40:14 2020 -0400
15348: Add libpam-arvados-experimental package.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/build/run-build-packages-one-target.sh b/build/run-build-packages-one-target.sh
index 1a845d200..f96fb7589 100755
--- a/build/run-build-packages-one-target.sh
+++ b/build/run-build-packages-one-target.sh
@@ -208,6 +208,8 @@ if test -z "$packages" ; then
keepstore
keep-web
libarvados-perl
+ libpam-arvados
+ libpam-arvados-experimental
python-arvados-fuse
python-arvados-python-client
python-arvados-cwl-runner"
diff --git a/build/run-build-packages.sh b/build/run-build-packages.sh
index 3ba1dcc05..b5b7f2555 100755
--- a/build/run-build-packages.sh
+++ b/build/run-build-packages.sh
@@ -318,6 +318,8 @@ package_go_binary tools/keep-rsync keep-rsync \
"Copy all data from one set of Keep servers to another"
package_go_binary tools/keep-exercise keep-exercise \
"Performance testing tool for Arvados Keep"
+package_go_so lib/pam pam_arvados.so libpam-arvados-experimental \
+ "Arvados PAM authentication module (experimental)"
# The Python SDK - Should be built first because it's needed by others
fpm_build_virtualenv "arvados-python-client" "sdk/python"
diff --git a/build/run-library.sh b/build/run-library.sh
index b75b6cca7..ca909d3dc 100755
--- a/build/run-library.sh
+++ b/build/run-library.sh
@@ -146,7 +146,7 @@ calculate_go_package_version() {
__returnvar="$version"
}
-# Usage: package_go_binary services/foo arvados-foo "Compute foo to arbitrary precision"
+# Usage: package_go_binary services/foo arvados-foo "Compute foo to arbitrary precision" [apache-2.0.txt]
package_go_binary() {
local src_path="$1"; shift
local prog="$1"; shift
@@ -188,6 +188,30 @@ package_go_binary() {
fpm_build "$GOPATH/bin/${basename}=/usr/bin/${prog}" "${prog}" dir "${go_package_version}" "--url=https://arvados.org" "--license=GNU Affero General Public License, version 3.0" "--description=${description}" "${switches[@]}"
}
+# Usage: package_go_so lib/foo arvados_foo.so arvados-foo "Arvados foo library"
+package_go_so() {
+ local src_path="$1"; shift
+ local sofile="$1"; shift
+ local pkg="$1"; shift
+ local description="$1"; shift
+
+ debug_echo "package_go_so $src_path as $pkg"
+
+ calculate_go_package_version go_package_version $src_path
+ cd $WORKSPACE/packages/$TARGET
+ test_package_presence $pkg $go_package_version go || return 1
+ cd $WORKSPACE/$src_path
+ go build -buildmode=c-shared -o ${GOPATH}/bin/${sofile}
+ cd $WORKSPACE/packages/$TARGET
+ local -a fpmargs=(
+ "--url=https://arvados.org"
+ "--license=Apache License, Version 2.0"
+ "--description=${description}"
+ "$WORKSPACE/apache-2.0.txt=/usr/share/doc/$pkg/apache-2.0.txt"
+ )
+ fpm_build "$GOPATH/bin/${sofile}=/usr/lib/${sofile}" "${pkg}" dir "${go_package_version}" "${fpmargs[@]}"
+}
+
default_iteration() {
if [[ -n "$ARVADOS_BUILDING_VERSION" ]]; then
echo "$ARVADOS_BUILDING_ITERATION"
commit a35ec27b40ce3ca0797cdcd8e0a79b2b8896af47
Author: Tom Clegg <tom at tomclegg.ca>
Date: Mon Jun 22 16:58:14 2020 -0400
15348: Add Go-based PAM module.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>
diff --git a/lib/pam/.gitignore b/lib/pam/.gitignore
new file mode 100644
index 000000000..8d44d630d
--- /dev/null
+++ b/lib/pam/.gitignore
@@ -0,0 +1,2 @@
+pam_arvados.h
+pam_arvados.so
diff --git a/lib/pam/docker_test.go b/lib/pam/docker_test.go
new file mode 100644
index 000000000..455d26441
--- /dev/null
+++ b/lib/pam/docker_test.go
@@ -0,0 +1,143 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package main
+
+import (
+ "bytes"
+ "crypto/tls"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "net/http/httputil"
+ "net/url"
+ "os"
+ "os/exec"
+ "strings"
+ "testing"
+
+ "git.arvados.org/arvados.git/sdk/go/arvadostest"
+ "gopkg.in/check.v1"
+)
+
+type DockerSuite struct {
+ tmpdir string
+ hostip string
+ proxyln net.Listener
+ proxysrv *http.Server
+}
+
+var _ = check.Suite(&DockerSuite{})
+
+func Test(t *testing.T) { check.TestingT(t) }
+
+func (s *DockerSuite) SetUpSuite(c *check.C) {
+ if testing.Short() {
+ c.Skip("skipping docker tests in short mode")
+ } else if _, err := exec.Command("docker", "info").CombinedOutput(); err != nil {
+ c.Skip("skipping docker tests because docker is not available")
+ }
+
+ s.tmpdir = c.MkDir()
+
+ // The integration-testing controller listens on the loopback
+ // interface, so it won't be reachable directly from the
+ // docker container -- so here we run a proxy on 0.0.0.0 for
+ // the duration of the test.
+ hostips, err := exec.Command("hostname", "-I").Output()
+ c.Assert(err, check.IsNil)
+ s.hostip = strings.Split(strings.Trim(string(hostips), "\n"), " ")[0]
+ ln, err := net.Listen("tcp", s.hostip+":0")
+ c.Assert(err, check.IsNil)
+ s.proxyln = ln
+ proxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: "https", Host: os.Getenv("ARVADOS_API_HOST")})
+ proxy.Transport = &http.Transport{
+ TLSClientConfig: &tls.Config{
+ InsecureSkipVerify: true,
+ },
+ }
+ s.proxysrv = &http.Server{Handler: proxy}
+ go s.proxysrv.ServeTLS(ln, "../../services/api/tmp/self-signed.pem", "../../services/api/tmp/self-signed.key")
+ proxyhost := ln.Addr().String()
+
+ // Build a pam module to install & configure in the docker
+ // container.
+ cmd := exec.Command("go", "build", "-buildmode=c-shared", "-o", s.tmpdir+"/pam_arvados.so")
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ err = cmd.Run()
+ c.Assert(err, check.IsNil)
+
+ // Write a PAM config file that uses our proxy as
+ // ARVADOS_API_HOST.
+ confdata := fmt.Sprintf(`Name: Arvados authentication
+Default: yes
+Priority: 256
+Auth-Type: Primary
+Auth:
+ [success=end default=ignore] /usr/lib/security/pam_arvados.so %s testvm2.shell insecure
+Auth-Initial:
+ [success=end default=ignore] /usr/lib/security/pam_arvados.so %s testvm2.shell insecure
+`, proxyhost, proxyhost)
+ err = ioutil.WriteFile(s.tmpdir+"/conffile", []byte(confdata), 0755)
+ c.Assert(err, check.IsNil)
+
+ // Build the testclient program that will (from inside the
+ // docker container) configure the system to use the above PAM
+ // config, and then try authentication.
+ cmd = exec.Command("go", "build", "-o", s.tmpdir+"/testclient", "./testclient.go")
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ err = cmd.Run()
+ c.Assert(err, check.IsNil)
+}
+
+func (s *DockerSuite) TearDownSuite(c *check.C) {
+ s.proxysrv.Close()
+ s.proxyln.Close()
+}
+
+func (s *DockerSuite) runTestClient(c *check.C, args ...string) (stdout, stderr *bytes.Buffer, err error) {
+ cmd := exec.Command("docker", append([]string{
+ "run", "--rm",
+ "--add-host", "zzzzz.arvadosapi.com:" + s.hostip,
+ "-v", s.tmpdir + "/pam_arvados.so:/usr/lib/security/pam_arvados.so:ro",
+ "-v", s.tmpdir + "/conffile:/usr/share/pam-configs/arvados:ro",
+ "-v", s.tmpdir + "/testclient:/testclient:ro",
+ "debian:buster",
+ "/testclient"}, args...)...)
+ stdout = &bytes.Buffer{}
+ stderr = &bytes.Buffer{}
+ cmd.Stdout = stdout
+ cmd.Stderr = stderr
+ err = cmd.Run()
+ return
+}
+
+func (s *DockerSuite) TestSuccess(c *check.C) {
+ stdout, stderr, err := s.runTestClient(c, "try", "active", arvadostest.ActiveTokenV2)
+ c.Check(err, check.IsNil)
+ c.Check(stdout.String(), check.Equals, "")
+ c.Check(stderr.String(), check.Matches, `(?ms).*authentication succeeded.*`)
+}
+
+func (s *DockerSuite) TestFailure(c *check.C) {
+ for _, trial := range []struct {
+ label string
+ username string
+ token string
+ }{
+ {"bad token", "active", arvadostest.ActiveTokenV2 + "badtoken"},
+ {"empty token", "active", ""},
+ {"empty username", "", arvadostest.ActiveTokenV2},
+ {"wrong username", "wrongusername", arvadostest.ActiveTokenV2},
+ } {
+ c.Logf("trial: %s", trial.label)
+ stdout, stderr, err := s.runTestClient(c, "try", trial.username, trial.token)
+ c.Check(err, check.NotNil)
+ c.Check(stdout.String(), check.Equals, "")
+ c.Check(stderr.String(), check.Matches, `(?ms).*authentication failed.*`)
+ }
+}
diff --git a/lib/pam/pam_arvados.go b/lib/pam/pam_arvados.go
new file mode 100644
index 000000000..ddca355b8
--- /dev/null
+++ b/lib/pam/pam_arvados.go
@@ -0,0 +1,148 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package main
+
+import (
+ "io/ioutil"
+ "log/syslog"
+
+ "context"
+ "errors"
+ "fmt"
+ "runtime"
+ "syscall"
+ "time"
+
+ "git.arvados.org/arvados.git/sdk/go/arvados"
+ "github.com/sirupsen/logrus"
+ lSyslog "github.com/sirupsen/logrus/hooks/syslog"
+ "golang.org/x/sys/unix"
+)
+
+/*
+#cgo LDFLAGS: -lpam -fPIC
+#include <security/pam_ext.h>
+char *stringindex(char** a, int i);
+const char *get_user(pam_handle_t *pamh);
+const char *get_authtoken(pam_handle_t *pamh);
+*/
+import "C"
+
+func main() {}
+
+func init() {
+ if err := unix.Prctl(syscall.PR_SET_DUMPABLE, 0, 0, 0, 0); err != nil {
+ newLogger(false).WithError(err).Warn("unable to disable ptrace")
+ }
+}
+
+//export pam_sm_authenticate
+func pam_sm_authenticate(pamh *C.pam_handle_t, flags, cArgc C.int, cArgv **C.char) C.int {
+ runtime.GOMAXPROCS(1)
+ logger := newLogger(flags&C.PAM_SILENT == 0)
+ cUsername := C.get_user(pamh)
+ if cUsername == nil {
+ return C.PAM_USER_UNKNOWN
+ }
+
+ cToken := C.get_authtoken(pamh)
+ if cToken == nil {
+ return C.PAM_AUTH_ERR
+ }
+
+ argv := make([]string, cArgc)
+ for i := 0; i < int(cArgc); i++ {
+ argv[i] = C.GoString(C.stringindex(cArgv, C.int(i)))
+ }
+
+ err := authenticate(logger, C.GoString(cUsername), C.GoString(cToken), argv)
+ if err != nil {
+ logger.WithError(err).Error("authentication failed")
+ return C.PAM_AUTH_ERR
+ }
+ return C.PAM_SUCCESS
+}
+
+func authenticate(logger logrus.FieldLogger, username, token string, argv []string) error {
+ hostname := ""
+ apiHost := ""
+ insecure := false
+ for idx, arg := range argv {
+ if idx == 0 {
+ apiHost = arg
+ } else if idx == 1 {
+ hostname = arg
+ } else if arg == "insecure" {
+ insecure = true
+ } else {
+ logger.Warnf("unkown option: %s\n", arg)
+ }
+ }
+ logger.Debugf("username=%q arvados_api_host=%q hostname=%q insecure=%t", username, apiHost, hostname, insecure)
+ if apiHost == "" || hostname == "" {
+ logger.Warnf("cannot authenticate: config error: arvados_api_host and hostname must be non-empty")
+ return errors.New("config error")
+ }
+ arv := &arvados.Client{
+ Scheme: "https",
+ APIHost: apiHost,
+ AuthToken: token,
+ Insecure: insecure,
+ }
+ ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Minute))
+ defer cancel()
+ var vms arvados.VirtualMachineList
+ err := arv.RequestAndDecodeContext(ctx, &vms, "GET", "arvados/v1/virtual_machines", nil, arvados.ListOptions{
+ Limit: 2,
+ Filters: []arvados.Filter{
+ {"hostname", "=", hostname},
+ },
+ })
+ if err != nil {
+ return err
+ }
+ if len(vms.Items) == 0 {
+ return fmt.Errorf("no results for hostname %q", hostname)
+ } else if len(vms.Items) > 1 {
+ return fmt.Errorf("multiple results for hostname %q", hostname)
+ } else if vms.Items[0].Hostname != hostname {
+ return fmt.Errorf("looked up hostname %q but controller returned record with hostname %q", hostname, vms.Items[0].Hostname)
+ }
+ var user arvados.User
+ err = arv.RequestAndDecodeContext(ctx, &user, "GET", "arvados/v1/users/current", nil, nil)
+ if err != nil {
+ return err
+ }
+ var links arvados.LinkList
+ err = arv.RequestAndDecodeContext(ctx, &links, "GET", "arvados/v1/links", nil, arvados.ListOptions{
+ Limit: 10000,
+ Filters: []arvados.Filter{
+ {"link_class", "=", "permission"},
+ {"name", "=", "can_login"},
+ {"tail_uuid", "=", user.UUID},
+ {"head_uuid", "=", vms.Items[0].UUID},
+ {"properties.username", "=", username},
+ },
+ })
+ if err != nil {
+ return err
+ }
+ if len(links.Items) < 1 || links.Items[0].Properties["username"] != username {
+ return errors.New("permission denied")
+ }
+ logger.Debugf("permission granted based on link with UUID %s", links.Items[0].UUID)
+ return nil
+}
+
+func newLogger(stderr bool) *logrus.Logger {
+ logger := logrus.New()
+ if !stderr {
+ logger.Out = ioutil.Discard
+ }
+ if hook, err := lSyslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_AUTH|syslog.LOG_INFO, "pam_arvados"); err != nil {
+ logger.Hooks.Add(hook)
+ }
+ return logger
+}
diff --git a/lib/pam/pam_c.go b/lib/pam/pam_c.go
new file mode 100644
index 000000000..4bf975b22
--- /dev/null
+++ b/lib/pam/pam_c.go
@@ -0,0 +1,24 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package main
+
+/*
+#cgo LDFLAGS: -lpam -fPIC
+#include <security/pam_ext.h>
+char *stringindex(char** a, int i) { return a[i]; }
+const char *get_user(pam_handle_t *pamh) {
+ const char *user;
+ if (pam_get_item(pamh, PAM_USER, (const void**)&user) != PAM_SUCCESS)
+ return NULL;
+ return user;
+}
+const char *get_authtoken(pam_handle_t *pamh) {
+ const char *token;
+ if (pam_get_authtok(pamh, PAM_AUTHTOK, &token, NULL) != PAM_SUCCESS)
+ return NULL;
+ return token;
+}
+*/
+import "C"
diff --git a/lib/pam/testclient.go b/lib/pam/testclient.go
new file mode 100644
index 000000000..3e92cac44
--- /dev/null
+++ b/lib/pam/testclient.go
@@ -0,0 +1,83 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+// +build never
+
+// This file is compiled by docker_test.go to build a test client.
+// It's not part of the pam module itself.
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+
+ "github.com/msteinert/pam"
+ "github.com/sirupsen/logrus"
+)
+
+func main() {
+ if len(os.Args) != 4 || os.Args[1] != "try" {
+ logrus.Print("usage: testclient try 'username' 'password'")
+ os.Exit(1)
+ }
+ username := os.Args[2]
+ password := os.Args[3]
+
+ // Configure PAM to use arvados token auth by default.
+ cmd := exec.Command("pam-auth-update", "--force", "arvados", "--remove", "unix")
+ cmd.Env = append([]string{"DEBIAN_FRONTEND=noninteractive"}, os.Environ()...)
+ cmd.Stdin = nil
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ err := cmd.Run()
+ if err != nil {
+ logrus.WithError(err).Error("pam-auth-update failed")
+ os.Exit(1)
+ }
+
+ // Check that pam-auth-update actually added arvados config.
+ cmd = exec.Command("grep", "-Hn", "arvados", "/etc/pam.d/common-auth")
+ cmd.Stdout = os.Stderr
+ cmd.Stderr = os.Stderr
+ err = cmd.Run()
+ if err != nil {
+ panic(err)
+ }
+
+ logrus.Debugf("starting pam: username=%q password=%q", username, password)
+
+ sentPassword := false
+ errorMessage := ""
+ tx, err := pam.StartFunc("default", username, func(style pam.Style, message string) (string, error) {
+ logrus.Debugf("pam conversation: style=%v message=%q", style, message)
+ switch style {
+ case pam.ErrorMsg:
+ logrus.WithField("Message", message).Info("pam.ErrorMsg")
+ errorMessage = message
+ return "", nil
+ case pam.TextInfo:
+ logrus.WithField("Message", message).Info("pam.TextInfo")
+ errorMessage = message
+ return "", nil
+ case pam.PromptEchoOn, pam.PromptEchoOff:
+ sentPassword = true
+ return password, nil
+ default:
+ return "", fmt.Errorf("unrecognized message style %d", style)
+ }
+ })
+ if err != nil {
+ logrus.WithError(err).Print("StartFunc failed")
+ os.Exit(1)
+ }
+ err = tx.Authenticate(pam.DisallowNullAuthtok)
+ if err != nil {
+ err = fmt.Errorf("PAM: %s (message = %q)", err, errorMessage)
+ logrus.WithError(err).Print("authentication failed")
+ os.Exit(1)
+ }
+ logrus.Print("authentication succeeded")
+}
diff --git a/sdk/go/arvados/link.go b/sdk/go/arvados/link.go
index fbd699f30..fdddfc537 100644
--- a/sdk/go/arvados/link.go
+++ b/sdk/go/arvados/link.go
@@ -6,14 +6,15 @@ package arvados
// Link is an arvados#link record
type Link struct {
- UUID string `json:"uuid,omiempty"`
- OwnerUUID string `json:"owner_uuid"`
- Name string `json:"name"`
- LinkClass string `json:"link_class"`
- HeadUUID string `json:"head_uuid"`
- HeadKind string `json:"head_kind"`
- TailUUID string `json:"tail_uuid"`
- TailKind string `json:"tail_kind"`
+ UUID string `json:"uuid,omiempty"`
+ OwnerUUID string `json:"owner_uuid"`
+ Name string `json:"name"`
+ LinkClass string `json:"link_class"`
+ HeadUUID string `json:"head_uuid"`
+ HeadKind string `json:"head_kind"`
+ TailUUID string `json:"tail_uuid"`
+ TailKind string `json:"tail_kind"`
+ Properties map[string]interface{} `json:"properties"`
}
// UserList is an arvados#userList resource.
diff --git a/sdk/go/arvados/virtual_machine.go b/sdk/go/arvados/virtual_machine.go
new file mode 100644
index 000000000..1506ede20
--- /dev/null
+++ b/sdk/go/arvados/virtual_machine.go
@@ -0,0 +1,25 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package arvados
+
+import "time"
+
+// VirtualMachine is an arvados#virtualMachine resource.
+type VirtualMachine struct {
+ UUID string `json:"uuid"`
+ OwnerUUID string `json:"owner_uuid"`
+ Hostname string `json:"hostname"`
+ CreatedAt *time.Time `json:"created_at"`
+ ModifiedAt *time.Time `json:"modified_at"`
+ ModifiedByUserUUID string `json:"modified_by_user_uuid"`
+}
+
+// VirtualMachineList is an arvados#virtualMachineList resource.
+type VirtualMachineList struct {
+ Items []VirtualMachine `json:"items"`
+ ItemsAvailable int `json:"items_available"`
+ Offset int `json:"offset"`
+ Limit int `json:"limit"`
+}
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list