Thomas Mooney <tmooney@genome.wustl.edu>
Chen Chen <aflyhorse@gmail.com>
Veritas Genetics, Inc. <*@veritasgenetics.com>
-Curii Corporation <*@curii.com>
\ No newline at end of file
+Curii Corporation, Inc. <*@curii.com>
## Development
-[![Build Status](https://ci.curoverse.com/buildStatus/icon?job=run-tests)](https://ci.curoverse.com/job/run-tests/)
+[![Build Status](https://ci.arvados.org/buildStatus/icon?job=run-tests)](https://ci.arvados.org/job/run-tests/)
[![Go Report Card](https://goreportcard.com/badge/github.com/arvados/arvados)](https://goreportcard.com/report/github.com/arvados/arvados)
The Arvados public bug tracker is located at https://dev.arvados.org/projects/arvados/issues
case "$TARGET" in
centos*)
- fpm_depends+=(git)
+ fpm_depends+=(git bison make automake gcc gcc-c++)
;;
debian* | ubuntu*)
- fpm_depends+=(git g++)
+ fpm_depends+=(git g++ bison zlib1g-dev make)
;;
esac
# "https://zzzzz.example.com/login") as an authorized redirect
# URL.
#
- # Requires EnableBetaController14287. ProviderAppID must be
+ # Incompatible with ForceLegacyAPI14. ProviderAppID must be
# blank.
GoogleClientID: ""
GoogleClientSecret: ""
<p>An administrator must activate your account before you can get
any further.</p>
- # Use experimental controller code (see https://dev.arvados.org/issues/14287)
- EnableBetaController14287: false
+ # Bypass new (Arvados 1.5) API implementations, and hand off
+ # requests directly to Rails instead. This can provide a temporary
+ # workaround for clients that are incompatible with the new API
+ # implementation. Note that it also disables some new federation
+ # features and will be removed in a future release.
+ ForceLegacyAPI14: false
"Containers.SupportedDockerImageFormats": true,
"Containers.SupportedDockerImageFormats.*": true,
"Containers.UsePreemptibleInstances": true,
- "EnableBetaController14287": false,
+ "ForceLegacyAPI14": false,
"Git": false,
"InstanceTypes": true,
"InstanceTypes.*": true,
# "https://zzzzz.example.com/login") as an authorized redirect
# URL.
#
- # Requires EnableBetaController14287. ProviderAppID must be
+ # Incompatible with ForceLegacyAPI14. ProviderAppID must be
# blank.
GoogleClientID: ""
GoogleClientSecret: ""
<p>An administrator must activate your account before you can get
any further.</p>
- # Use experimental controller code (see https://dev.arvados.org/issues/14287)
- EnableBetaController14287: false
+ # Bypass new (Arvados 1.5) API implementations, and hand off
+ # requests directly to Rails instead. This can provide a temporary
+ # workaround for clients that are incompatible with the new API
+ # implementation. Note that it also disables some new federation
+ # features and will be removed in a future release.
+ ForceLegacyAPI14: false
`)
"prefs": true,
"username": true,
+ "etag": false,
"full_name": false,
"identity_url": false,
"is_invited": false,
"owner_uuid": false,
"uuid": false,
+ "writable_by": false,
}
func (conn *Conn) UserList(ctx context.Context, options arvados.ListOptions) (arvados.UserList, error) {
c.Assert(s.remoteMock.Start(), check.IsNil)
cluster := &arvados.Cluster{
- ClusterID: "zhome",
- PostgreSQL: integrationTestCluster().PostgreSQL,
- EnableBetaController14287: enableBetaController14287,
+ ClusterID: "zhome",
+ PostgreSQL: integrationTestCluster().PostgreSQL,
+ ForceLegacyAPI14: forceLegacyAPI14,
}
cluster.TLS.Insecure = true
cluster.API.MaxItemsPerResponse = 1000
rtr := router.New(federation.New(h.Cluster))
mux.Handle("/arvados/v1/config", rtr)
- if h.Cluster.EnableBetaController14287 {
+ if !h.Cluster.ForceLegacyAPI14 {
mux.Handle("/arvados/v1/collections", rtr)
mux.Handle("/arvados/v1/collections/", rtr)
mux.Handle("/arvados/v1/users", rtr)
check "gopkg.in/check.v1"
)
-var enableBetaController14287 bool
+var forceLegacyAPI14 bool
// Gocheck boilerplate
func Test(t *testing.T) {
- for _, enableBetaController14287 = range []bool{false, true} {
+ for _, forceLegacyAPI14 = range []bool{false, true} {
check.TestingT(t)
}
}
s.ctx, s.cancel = context.WithCancel(context.Background())
s.ctx = ctxlog.Context(s.ctx, ctxlog.New(os.Stderr, "json", "debug"))
s.cluster = &arvados.Cluster{
- ClusterID: "zzzzz",
- PostgreSQL: integrationTestCluster().PostgreSQL,
-
- EnableBetaController14287: enableBetaController14287,
+ ClusterID: "zzzzz",
+ PostgreSQL: integrationTestCluster().PostgreSQL,
+ ForceLegacyAPI14: forceLegacyAPI14,
}
s.cluster.TLS.Insecure = true
arvadostest.SetServiceURL(&s.cluster.Services.RailsAPI, "https://"+os.Getenv("ARVADOS_TEST_API_HOST"))
}
var boolParams = map[string]bool{
- "distinct": true,
- "ensure_unique_name": true,
- "include_trash": true,
- "include_old_versions": true,
+ "distinct": true,
+ "ensure_unique_name": true,
+ "include_trash": true,
+ "include_old_versions": true,
+ "redirect_to_new_user": true,
+ "send_notification_email": true,
}
func stringToBool(s string) bool {
selected[attr] = v
}
}
- // Preserve "kind" even if not requested
- if v, ok := orig["kind"]; ok {
- selected["kind"] = v
+ // Some keys are always preserved, even if not requested
+ for _, k := range []string{"etag", "kind", "writable_by"} {
+ if v, ok := orig[k]; ok {
+ selected[k] = v
+ }
}
return selected
}
},
} {
rtr.addRoute(route.endpoint, route.defaultOpts, route.exec)
- if route.endpoint.Method == "PATCH" {
- // Accept PUT as a synonym for PATCH.
- endpointPUT := route.endpoint
- endpointPUT.Method = "PUT"
- rtr.addRoute(endpointPUT, route.defaultOpts, route.exec)
- }
}
rtr.mux.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
httpserver.Errors(w, []string{"API endpoint not found"}, http.StatusNotFound)
})
}
+var altMethod = map[string]string{
+ "PATCH": "PUT", // Accept PUT as a synonym for PATCH
+ "GET": "HEAD", // Accept HEAD at any GET route
+}
+
func (rtr *router) addRoute(endpoint arvados.APIEndpoint, defaultOpts func() interface{}, exec routableFunc) {
- rtr.mux.Methods(endpoint.Method).Path("/" + endpoint.Path).HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ methods := []string{endpoint.Method}
+ if alt, ok := altMethod[endpoint.Method]; ok {
+ methods = append(methods, alt)
+ }
+ rtr.mux.Methods(methods...).Path("/" + endpoint.Path).HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
logger := ctxlog.FromContext(req.Context())
params, err := rtr.loadRequestParams(req, endpoint.AttrsKey)
if err != nil {
c.Check(jresp["uuid"], check.IsNil)
}
+func (s *RouterIntegrationSuite) TestWritableBy(c *check.C) {
+ _, rr, jresp := doRequest(c, s.rtr, arvadostest.ActiveTokenV2, "GET", `/arvados/v1/users/`+arvadostest.ActiveUserUUID, nil, nil)
+ c.Check(rr.Code, check.Equals, http.StatusOK)
+ c.Check(jresp["writable_by"], check.DeepEquals, []interface{}{"zzzzz-tpzed-000000000000000", "zzzzz-tpzed-xurymjxw79nv3jz", "zzzzz-j7d0g-48foin4vonvc2at"})
+}
+
func (s *RouterIntegrationSuite) TestFullTimestampsInResponse(c *check.C) {
uuid := arvadostest.CollectionReplicationDesired2Confirmed2UUID
token := arvadostest.ActiveTokenV2
c.Check(rr.Code, check.Equals, http.StatusOK)
c.Check(resp["kind"], check.Equals, "arvados#container")
+ c.Check(resp["etag"], check.FitsTypeOf, "")
+ c.Check(resp["etag"], check.Not(check.Equals), "")
c.Check(resp["uuid"], check.HasLen, 27)
c.Check(resp["command"], check.HasLen, 2)
c.Check(resp["mounts"], check.IsNil)
}
}
+func (s *RouterIntegrationSuite) TestHEAD(c *check.C) {
+ _, rr, _ := doRequest(c, s.rtr, arvadostest.ActiveTokenV2, "HEAD", "/arvados/v1/containers/"+arvadostest.QueuedContainerUUID, nil, nil)
+ c.Check(rr.Code, check.Equals, http.StatusOK)
+}
+
func (s *RouterIntegrationSuite) TestRouteNotFound(c *check.C) {
token := arvadostest.ActiveTokenV2
req := (&testReq{
return resp, err
}
func (conn *Conn) UserMerge(ctx context.Context, options arvados.UserMergeOptions) (arvados.User, error) {
- ep := arvados.EndpointUserUpdateUUID
+ ep := arvados.EndpointUserMerge
var resp arvados.User
err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
return resp, err
}
func (conn *Conn) UserActivate(ctx context.Context, options arvados.UserActivateOptions) (arvados.User, error) {
- ep := arvados.EndpointUserUpdateUUID
+ ep := arvados.EndpointUserActivate
var resp arvados.User
err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
return resp, err
}
func (conn *Conn) UserSetup(ctx context.Context, options arvados.UserSetupOptions) (map[string]interface{}, error) {
- ep := arvados.EndpointUserUpdateUUID
+ ep := arvados.EndpointUserSetup
var resp map[string]interface{}
err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
return resp, err
}
func (conn *Conn) UserUnsetup(ctx context.Context, options arvados.GetOptions) (arvados.User, error) {
- ep := arvados.EndpointUserUpdateUUID
+ ep := arvados.EndpointUserUnsetup
var resp arvados.User
err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
return resp, err
log := ctxlog.TestLogger(c)
handler := &Handler{Cluster: &arvados.Cluster{
- ClusterID: "zzzzz",
- PostgreSQL: integrationTestCluster().PostgreSQL,
-
- EnableBetaController14287: enableBetaController14287,
+ ClusterID: "zzzzz",
+ PostgreSQL: integrationTestCluster().PostgreSQL,
+ ForceLegacyAPI14: forceLegacyAPI14,
}}
handler.Cluster.TLS.Insecure = true
arvadostest.SetServiceURL(&handler.Cluster.Services.RailsAPI, "https://"+os.Getenv("ARVADOS_TEST_API_HOST"))
EndpointUserGetSystem = APIEndpoint{"GET", "arvados/v1/users/system", ""}
EndpointUserList = APIEndpoint{"GET", "arvados/v1/users", ""}
EndpointUserMerge = APIEndpoint{"POST", "arvados/v1/users/merge", ""}
- EndpointUserSetup = APIEndpoint{"POST", "arvados/v1/users/setup", ""}
+ EndpointUserSetup = APIEndpoint{"POST", "arvados/v1/users/setup", "user"}
EndpointUserSystem = APIEndpoint{"GET", "arvados/v1/users/system", ""}
EndpointUserUnsetup = APIEndpoint{"POST", "arvados/v1/users/{uuid}/unsetup", ""}
EndpointUserUpdate = APIEndpoint{"PATCH", "arvados/v1/users/{uuid}", "user"}
}
type UserSetupOptions struct {
- UUID string `json:"uuid"`
- Email string `json:"email"`
- OpenIDPrefix string `json:"openid_prefix"`
- RepoName string `json:"repo_name"`
- VMUUID string `json:"vm_uuid"`
- SendNotificationEmail bool `json:"send_notification_email"`
+ UUID string `json:"uuid,omitempty"`
+ Email string `json:"email,omitempty"`
+ OpenIDPrefix string `json:"openid_prefix,omitempty"`
+ RepoName string `json:"repo_name,omitempty"`
+ VMUUID string `json:"vm_uuid,omitempty"`
+ SendNotificationEmail bool `json:"send_notification_email,omitempty"`
Attrs map[string]interface{} `json:"attrs"`
}
type UserMergeOptions struct {
- NewUserUUID string `json:"new_user_uuid,omitempty"`
- OldUserUUID string `json:"old_user_uuid,omitempty"`
- NewUserToken string `json:"new_user_token,omitempty"`
+ NewUserUUID string `json:"new_user_uuid,omitempty"`
+ OldUserUUID string `json:"old_user_uuid,omitempty"`
+ NewOwnerUUID string `json:"new_owner_uuid,omitempty"`
+ NewUserToken string `json:"new_user_token,omitempty"`
+ RedirectToNewUser bool `json:"redirect_to_new_user"`
}
type UserBatchUpdateOptions struct {
DeleteAt *time.Time `json:"delete_at"`
IsTrashed bool `json:"is_trashed"`
Properties map[string]interface{} `json:"properties"`
+ WritableBy []string `json:"writable_by,omitempty"`
}
func (c Collection) resourceName() string {
InactivePageHTML string
}
- EnableBetaController14287 bool
+ ForceLegacyAPI14 bool
}
type Volume struct {
// Container is an arvados#container resource.
type Container struct {
UUID string `json:"uuid"`
+ Etag string `json:"etag"`
CreatedAt time.Time `json:"created_at"`
ModifiedByClientUUID string `json:"modified_by_client_uuid"`
ModifiedByUserUUID string `json:"modified_by_user_uuid"`
go func() {
defer close(done)
defer close(errs)
- locked := map[*filenode]bool{}
locator, _, err := dn.fs.PutB(block)
dn.fs.throttle().Release()
{
- if !sync {
- dn.Lock()
- defer dn.Unlock()
- for _, name := range dn.sortedNames() {
- if fn, ok := dn.inodes[name].(*filenode); ok {
- fn.Lock()
- defer fn.Unlock()
- locked[fn] = true
- }
- }
- }
defer func() {
for _, seg := range segs {
if seg.flushing == done {
}
for idx, ref := range refs {
if !sync {
+ ref.fn.Lock()
// In async mode, fn's lock was
// released while we were waiting for
// PutB(); lots of things might have
// file segments have
// rearranged or changed in
// some way
+ ref.fn.Unlock()
continue
} else if seg, ok := ref.fn.segments[ref.idx].(*memSegment); !ok || seg != segs[idx] {
// segment has been replaced
+ ref.fn.Unlock()
continue
} else if seg.flushing != done {
// seg.buf has been replaced
- continue
- } else if !locked[ref.fn] {
- // file was renamed, moved, or
- // deleted since we called
- // PutB
+ ref.fn.Unlock()
continue
}
}
// lock, writing different segments from the
// same file.
atomic.AddInt64(&ref.fn.memsize, -int64(len(data)))
+ if !sync {
+ ref.fn.Unlock()
+ }
}
}()
if sync {
// User is an arvados#user record
type User struct {
UUID string `json:"uuid"`
+ Etag string `json:"etag"`
IsActive bool `json:"is_active"`
IsAdmin bool `json:"is_admin"`
Username string `json:"username"`
ModifiedByUserUUID string `json:"modified_by_user_uuid"`
ModifiedByClientUUID string `json:"modified_by_client_uuid"`
Prefs map[string]interface{} `json:"prefs"`
+ WritableBy []string `json:"writable_by,omitempty"`
}
// UserList is an arvados#userList resource.
config = {
"Clusters": {
"zzzzz": {
- "EnableBetaController14287": ('14287' in os.environ.get('ARVADOS_EXPERIMENTAL', '')),
"ManagementToken": "e687950a23c3a9bceec28c6223a06c79",
"SystemRootToken": auth_token('system_user'),
"API": {
resp, err := (&http.Client{}).Do(req)
c.Assert(err, Equals, nil)
c.Check(resp.Header.Get("Via"), Equals, "HTTP/1.1 keepproxy")
+ c.Assert(resp.StatusCode, Equals, http.StatusOK)
locator, err := ioutil.ReadAll(resp.Body)
c.Assert(err, Equals, nil)
resp.Body.Close()
s.add_runtime_dependency 'arvados', '~> 1.3.0', '>= 1.3.0'
# arvados-google-api-client 0.8.7.2 is incompatible with faraday 0.16.2
s.add_dependency('faraday', '< 0.16')
+ # arvados-google-api-client (and thus arvados) gems
+ # depend on signet, but signet 0.12 is incompatible with ruby 2.3.
+ s.add_dependency('signet', '< 0.12')
s.homepage =
'https://arvados.org'
end