Merge branch '13973-child-priority' refs #13973
authorPeter Amstutz <pamstutz@veritasgenetics.com>
Fri, 24 Aug 2018 19:33:56 +0000 (15:33 -0400)
committerPeter Amstutz <pamstutz@veritasgenetics.com>
Fri, 24 Aug 2018 19:33:56 +0000 (15:33 -0400)
Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <pamstutz@veritasgenetics.com>

51 files changed:
build/build.list
build/package-build-dockerfiles/Makefile
build/package-build-dockerfiles/centos7/Dockerfile
build/package-build-dockerfiles/debian8/Dockerfile
build/package-build-dockerfiles/debian9/Dockerfile
build/package-build-dockerfiles/ubuntu1404/Dockerfile
build/package-build-dockerfiles/ubuntu1604/Dockerfile
build/package-build-dockerfiles/ubuntu1804/Dockerfile [new file with mode: 0644]
build/package-test-dockerfiles/centos7/Dockerfile
build/package-test-dockerfiles/debian8/Dockerfile
build/package-test-dockerfiles/ubuntu1404/Dockerfile
build/package-test-dockerfiles/ubuntu1604/Dockerfile
build/package-test-dockerfiles/ubuntu1804/Dockerfile [new file with mode: 0644]
build/package-test-dockerfiles/ubuntu1804/etc-apt-preferences.d-arvados [new file with mode: 0644]
build/package-testing/deb-common-test-packages.sh
build/package-testing/test-packages-ubuntu1804.sh [new symlink]
build/run-build-packages-sso.sh
build/run-build-packages.sh
build/run-library.sh
build/run-tests.sh
doc/api/methods/groups.html.textile.liquid
sdk/cwl/setup.py
sdk/cwl/tests/test_container.py
sdk/cwl/tests/test_job.py
sdk/cwl/tests/test_submit.py
sdk/go/arvadosclient/arvadosclient.go
sdk/go/dispatch/dispatch.go
sdk/python/arvados/commands/run.py
sdk/python/setup.py
services/api/app/controllers/application_controller.rb
services/api/app/controllers/arvados/v1/groups_controller.rb
services/api/app/models/container_request.rb
services/api/config/routes.rb
services/api/db/migrate/20180820130357_add_pdh_and_trash_index_to_collections.rb [new file with mode: 0644]
services/api/db/migrate/20180820135808_drop_pdh_index_from_collections.rb [new file with mode: 0644]
services/api/db/structure.sql
services/api/test/fixtures/api_client_authorizations.yml
services/api/test/fixtures/groups.yml
services/api/test/functional/arvados/v1/container_requests_controller_test.rb
services/api/test/functional/arvados/v1/groups_controller_test.rb
services/api/test/unit/container_request_test.rb
services/api/test/unit/container_test.rb
services/crunch-dispatch-local/crunch-dispatch-local.go
services/crunch-dispatch-local/crunch-dispatch-local_test.go
services/crunch-dispatch-slurm/crunch-dispatch-slurm.go
services/crunch-dispatch-slurm/crunch-dispatch-slurm_test.go
services/crunch-dispatch-slurm/squeue.go
services/crunch-dispatch-slurm/squeue_test.go
services/fuse/arvados_fuse/fusedir.py
services/keep-web/handler.go
services/keep-web/handler_test.go

index 5196e9c64d47a70601e1c816ce81abc7b1ced7ef..7f644f158dfba5f724e0cd995cbbabdabdba6b2d 100644 (file)
@@ -4,35 +4,35 @@
 
 #distribution(s)|name|version|iteration|type|architecture|extra fpm arguments
 debian8,debian9,centos7|python-gflags|2.0|2|python|all
-debian8,debian9,ubuntu1404,ubuntu1604,centos7|google-api-python-client|1.6.2|2|python|all
+debian8,debian9,ubuntu1404,ubuntu1604,ubuntu1804,centos7|google-api-python-client|1.6.2|2|python|all
 debian8,debian9,ubuntu1404,centos7|oauth2client|1.5.2|2|python|all
 debian8,debian9,ubuntu1404,centos7|pyasn1|0.1.7|2|python|all
 debian8,debian9,ubuntu1404,centos7|pyasn1-modules|0.0.5|2|python|all
-debian8,debian9,ubuntu1404,ubuntu1604,centos7|rsa|3.4.2|2|python|all
-debian8,debian9,ubuntu1404,ubuntu1604,centos7|uritemplate|3.0.0|2|python|all
-debian8,debian9,ubuntu1404,ubuntu1604,centos7|httplib2|0.9.2|3|python|all
-debian8,debian9,centos7|ws4py|0.3.5|2|python|all
+debian8,debian9,ubuntu1404,ubuntu1604,ubuntu1804,centos7|rsa|3.4.2|2|python|all
+debian8,debian9,ubuntu1404,ubuntu1604,ubuntu1804,centos7|uritemplate|3.0.0|2|python|all
+debian8,debian9,ubuntu1404,ubuntu1604,ubuntu1804,centos7|httplib2|0.9.2|3|python|all
+debian8,debian9,centos7,ubuntu1404,ubuntu1604|ws4py|0.4.2|2|python|all
 debian8,debian9,centos7|pykka|1.2.1|2|python|all
 debian8,debian9,ubuntu1404,centos7|six|1.10.0|2|python|all
-debian8,debian9,ubuntu1404,ubuntu1604,centos7|ciso8601|1.0.6|3|python|amd64
+debian8,debian9,ubuntu1404,ubuntu1604,ubuntu1804,centos7|ciso8601|1.0.6|3|python|amd64
 debian8,debian9,centos7|pycrypto|2.6.1|3|python|amd64
-debian8,debian9,ubuntu1404,ubuntu1604|backports.ssl_match_hostname|3.5.0.1|2|python|all
-debian8,debian9,ubuntu1404,ubuntu1604,centos7|llfuse|1.2|3|python|amd64
+debian8,debian9,ubuntu1404,ubuntu1604,ubuntu1804|backports.ssl_match_hostname|3.5.0.1|2|python|all
+debian8,debian9,ubuntu1404,ubuntu1604,ubuntu1804,centos7|llfuse|1.2|3|python|amd64
 debian8,debian9,ubuntu1404,centos7|pycurl|7.19.5.3|3|python|amd64
-debian8,debian9,ubuntu1404,ubuntu1604,centos7|pyyaml|3.12|2|python|amd64
-debian8,debian9,ubuntu1404,ubuntu1604,centos7|rdflib|4.2.2|2|python|all
+debian8,debian9,ubuntu1404,ubuntu1604,ubuntu1804,centos7|pyyaml|3.12|2|python|amd64
+debian8,debian9,ubuntu1404,ubuntu1604,ubuntu1804,centos7|rdflib|4.2.2|2|python|all
 debian8,debian9,ubuntu1404,centos7|shellescape|3.4.1|2|python|all
-debian8,debian9,ubuntu1404,ubuntu1604,centos7|mistune|0.7.3|2|python|all
-debian8,debian9,ubuntu1404,ubuntu1604,centos7|typing|3.6.4|2|python|all
-debian8,debian9,ubuntu1404,ubuntu1604,centos7|avro|1.8.1|2|python|all
+debian8,debian9,ubuntu1404,ubuntu1604,ubuntu1804,centos7|mistune|0.7.3|2|python|all
+debian8,debian9,ubuntu1404,ubuntu1604,ubuntu1804,centos7|typing|3.6.4|2|python|all
+debian8,debian9,ubuntu1404,ubuntu1604,ubuntu1804,centos7|avro|1.8.1|2|python|all
 debian8,debian9,ubuntu1404,centos7|ruamel.ordereddict|0.4.9|2|python|amd64
-debian8,debian9,ubuntu1404,ubuntu1604,centos7|cachecontrol|0.11.7|2|python|all
-debian8,debian9,ubuntu1404,ubuntu1604,centos7|pathlib2|2.3.2|2|python|all
-debian8,debian9,ubuntu1404,ubuntu1604,centos7|scandir|1.7|2|python|all
-debian8,debian9,ubuntu1404,ubuntu1604,centos7|docker-py|1.7.2|2|python3|all
+debian8,debian9,ubuntu1404,ubuntu1604,ubuntu1804,centos7|cachecontrol|0.11.7|2|python|all
+debian8,debian9,ubuntu1404,ubuntu1604,ubuntu1804,centos7|pathlib2|2.3.2|2|python|all
+debian8,debian9,ubuntu1404,ubuntu1604,ubuntu1804,centos7|scandir|1.7|2|python|all
+debian8,debian9,ubuntu1404,ubuntu1604,ubuntu1804,centos7|docker-py|1.7.2|2|python3|all
 debian8,debian9,centos7|six|1.10.0|2|python3|all
 debian8,debian9,ubuntu1404,centos7|requests|2.12.4|2|python3|all
-debian8,debian9,ubuntu1404,ubuntu1604,centos7|websocket-client|0.37.0|2|python3|all
+debian8,debian9,ubuntu1404,ubuntu1604,ubuntu1804,centos7|websocket-client|0.37.0|2|python3|all
 ubuntu1404|requests|2.4.3|2|python|all
 centos7|contextlib2|0.5.4|2|python|all
 centos7|isodate|0.5.4|2|python|all
@@ -42,9 +42,9 @@ centos7|pyparsing|2.1.10|2|python|all
 centos7|keepalive|0.5|2|python|all
 centos7|networkx|1.11|0|python|all
 centos7|psutil|5.0.1|0|python|all
-debian8,debian9,ubuntu1404,ubuntu1604,centos7|lockfile|0.12.2|2|python|all|--epoch 1
-debian8,debian9,ubuntu1404,ubuntu1604,centos7|subprocess32|3.5.1|2|python|all
-all|ruamel.yaml|0.14.12|2|python|amd64|--python-setup-py-arguments --single-version-externally-managed
+debian8,debian9,ubuntu1404,ubuntu1604,ubuntu1804,centos7|lockfile|0.12.2|2|python|all|--epoch 1
+debian8,debian9,ubuntu1404,ubuntu1604,ubuntu1804,centos7|subprocess32|3.5.1|2|python|all
+centos7,debian8,debian9,ubuntu1404,ubuntu1604|ruamel.yaml|0.15.34|1|python|amd64|--python-setup-py-arguments --single-version-externally-managed
 all|cwltest|1.0.20180518074130|4|python|all|--depends 'python-futures >= 3.0.5' --depends 'python-subprocess32 >= 3.5.0'
 all|junit-xml|1.8|3|python|all
 all|rdflib-jsonld|0.4.0|2|python|all
index ab1ade14deababdcc76abd11d02a99968ac0dac1..9dc536bf1eb919a7d334d3d18061db3dfd8c90d7 100644 (file)
@@ -2,7 +2,7 @@
 #
 # SPDX-License-Identifier: AGPL-3.0
 
-all: centos7/generated debian8/generated debian9/generated ubuntu1204/generated ubuntu1404/generated ubuntu1604/generated
+all: centos7/generated debian8/generated debian9/generated ubuntu1404/generated ubuntu1604/generated ubuntu1804/generated
 
 centos7/generated: common-generated-all
        test -d centos7/generated || mkdir centos7/generated
@@ -16,10 +16,6 @@ debian9/generated: common-generated-all
        test -d debian9/generated || mkdir debian9/generated
        cp -rlt debian9/generated common-generated/*
 
-ubuntu1204/generated: common-generated-all
-       test -d ubuntu1204/generated || mkdir ubuntu1204/generated
-       cp -rlt ubuntu1204/generated common-generated/*
-
 ubuntu1404/generated: common-generated-all
        test -d ubuntu1404/generated || mkdir ubuntu1404/generated
        cp -rlt ubuntu1404/generated common-generated/*
@@ -28,6 +24,10 @@ ubuntu1604/generated: common-generated-all
        test -d ubuntu1604/generated || mkdir ubuntu1604/generated
        cp -rlt ubuntu1604/generated common-generated/*
 
+ubuntu1804/generated: common-generated-all
+       test -d ubuntu1804/generated || mkdir ubuntu1804/generated
+       cp -rlt ubuntu1804/generated common-generated/*
+
 GOTARBALL=go1.10.1.linux-amd64.tar.gz
 NODETARBALL=node-v6.11.2-linux-x64.tar.xz
 
index 3a8b03f190b420a69b673780a46d434c7dad8da1..216c5cbbad079bfc4ef7a16d5734579b8578995b 100644 (file)
@@ -9,12 +9,12 @@ MAINTAINER Ward Vandewege <ward@curoverse.com>
 RUN yum -q -y install make automake gcc gcc-c++ libyaml-devel patch readline-devel zlib-devel libffi-devel openssl-devel bzip2 libtool bison sqlite-devel rpm-build git perl-ExtUtils-MakeMaker libattr-devel nss-devel libcurl-devel which tar unzip scl-utils centos-release-scl postgresql-devel python-devel python-setuptools fuse-devel xz-libs git
 
 # Install RVM
-RUN gpg --keyserver pool.sks-keyservers.net --recv-keys D39DC0E3 && \
+RUN gpg --keyserver ha.pool.sks-keyservers.net --recv-keys D39DC0E3 && \
     curl -L https://get.rvm.io | bash -s stable && \
     /usr/local/rvm/bin/rvm install 2.3 && \
     /usr/local/rvm/bin/rvm alias create default ruby-2.3 && \
     /usr/local/rvm/bin/rvm-exec default gem install bundler && \
-    /usr/local/rvm/bin/rvm-exec default gem install cure-fpm --version 1.6.0b
+    /usr/local/rvm/bin/rvm-exec default gem install fpm --version 1.10.2
 
 # Install golang binary
 ADD generated/go1.10.1.linux-amd64.tar.gz /usr/local/
index 54267d708e2cc2ce34c603bf5048cf816c31de86..fb1209027bdf75438790d8736b6ce710ef656b67 100644 (file)
@@ -11,12 +11,12 @@ ENV DEBIAN_FRONTEND noninteractive
 RUN /usr/bin/apt-get update && /usr/bin/apt-get install -q -y python2.7-dev python3 python-setuptools python3-setuptools libcurl4-gnutls-dev curl git procps libattr1-dev libfuse-dev libgnutls28-dev libpq-dev python-pip unzip
 
 # Install RVM
-RUN gpg --keyserver pool.sks-keyservers.net --recv-keys D39DC0E3 && \
+RUN gpg --keyserver ha.pool.sks-keyservers.net --recv-keys D39DC0E3 && \
     curl -L https://get.rvm.io | bash -s stable && \
     /usr/local/rvm/bin/rvm install 2.3 && \
     /usr/local/rvm/bin/rvm alias create default ruby-2.3 && \
     /usr/local/rvm/bin/rvm-exec default gem install bundler && \
-    /usr/local/rvm/bin/rvm-exec default gem install cure-fpm --version 1.6.0b
+    /usr/local/rvm/bin/rvm-exec default gem install fpm --version 1.10.2
 
 # Install golang binary
 ADD generated/go1.10.1.linux-amd64.tar.gz /usr/local/
index 9ade5fa27232f6613fd07199a6c8a3d9f54565ca..e2da5ad2119f09f91d558ca327baad7dda5e3cf7 100644 (file)
@@ -18,7 +18,7 @@ RUN gpg --import /tmp/D39DC0E3.asc && \
     /usr/local/rvm/bin/rvm install 2.3 && \
     /usr/local/rvm/bin/rvm alias create default ruby-2.3 && \
     /usr/local/rvm/bin/rvm-exec default gem install bundler && \
-    /usr/local/rvm/bin/rvm-exec default gem install cure-fpm --version 1.6.0b
+    /usr/local/rvm/bin/rvm-exec default gem install fpm --version 1.10.2
 
 # Install golang binary
 ADD generated/go1.10.1.linux-amd64.tar.gz /usr/local/
index 4ff47ff315bee92d127814348f621a54f64e789a..b7d4081ffb98e8aa2be1d241d30938d7eaf21820 100644 (file)
@@ -11,12 +11,12 @@ ENV DEBIAN_FRONTEND noninteractive
 RUN /usr/bin/apt-get update && /usr/bin/apt-get install -q -y python2.7-dev python3 python-setuptools python3-setuptools libcurl4-gnutls-dev curl git libattr1-dev libfuse-dev libpq-dev python-pip unzip 
 
 # Install RVM
-RUN gpg --keyserver pool.sks-keyservers.net --recv-keys D39DC0E3 && \
+RUN gpg --keyserver ha.pool.sks-keyservers.net --recv-keys D39DC0E3 && \
     curl -L https://get.rvm.io | bash -s stable && \
     /usr/local/rvm/bin/rvm install 2.3 && \
     /usr/local/rvm/bin/rvm alias create default ruby-2.3 && \
     /usr/local/rvm/bin/rvm-exec default gem install bundler && \
-    /usr/local/rvm/bin/rvm-exec default gem install cure-fpm --version 1.6.0b
+    /usr/local/rvm/bin/rvm-exec default gem install fpm --version 1.10.2
 
 # Install golang binary
 ADD generated/go1.10.1.linux-amd64.tar.gz /usr/local/
index 7e5701f871cb987dc581fe843b1b2f3c4a2d3b7c..6220652e56e15faa8c917c8838b024e12bbe740c 100644 (file)
@@ -11,12 +11,12 @@ ENV DEBIAN_FRONTEND noninteractive
 RUN /usr/bin/apt-get update && /usr/bin/apt-get install -q -y python2.7-dev python3 python-setuptools python3-setuptools libcurl4-gnutls-dev libgnutls-dev curl git libattr1-dev libfuse-dev libpq-dev python-pip unzip tzdata
 
 # Install RVM
-RUN gpg --keyserver pool.sks-keyservers.net --recv-keys D39DC0E3 && \
+RUN gpg --keyserver ha.pool.sks-keyservers.net --recv-keys D39DC0E3 && \
     curl -L https://get.rvm.io | bash -s stable && \
     /usr/local/rvm/bin/rvm install 2.3 && \
     /usr/local/rvm/bin/rvm alias create default ruby-2.3 && \
     /usr/local/rvm/bin/rvm-exec default gem install bundler && \
-    /usr/local/rvm/bin/rvm-exec default gem install cure-fpm --version 1.6.0b
+    /usr/local/rvm/bin/rvm-exec default gem install fpm --version 1.10.2
 
 # Install golang binary
 ADD generated/go1.10.1.linux-amd64.tar.gz /usr/local/
diff --git a/build/package-build-dockerfiles/ubuntu1804/Dockerfile b/build/package-build-dockerfiles/ubuntu1804/Dockerfile
new file mode 100644 (file)
index 0000000..68df124
--- /dev/null
@@ -0,0 +1,35 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+FROM ubuntu:bionic
+MAINTAINER Ward Vandewege <ward@curoverse.com>
+
+ENV DEBIAN_FRONTEND noninteractive
+
+# Install dependencies.
+RUN /usr/bin/apt-get update && /usr/bin/apt-get install -q -y python2.7-dev python3 python-setuptools python3-pip libcurl4-gnutls-dev libgnutls28-dev curl git libattr1-dev libfuse-dev libpq-dev python-pip unzip tzdata
+
+# Install RVM
+RUN gpg --keyserver ha.pool.sks-keyservers.net --recv-keys D39DC0E3 && \
+    curl -L https://get.rvm.io | bash -s stable && \
+    /usr/local/rvm/bin/rvm install 2.3 && \
+    /usr/local/rvm/bin/rvm alias create default ruby-2.3 && \
+    /usr/local/rvm/bin/rvm-exec default gem install bundler && \
+    /usr/local/rvm/bin/rvm-exec default gem install fpm --version 1.10.2
+
+# Install golang binary
+ADD generated/go1.10.1.linux-amd64.tar.gz /usr/local/
+RUN ln -s /usr/local/go/bin/go /usr/local/bin/
+
+# Install nodejs and npm
+ADD generated/node-v6.11.2-linux-x64.tar.xz /usr/local/
+RUN ln -s /usr/local/node-v6.11.2-linux-x64/bin/* /usr/local/bin/
+
+# Old versions of setuptools cannot build a schema-salad package.
+RUN pip install --upgrade setuptools
+
+RUN git clone --depth 1 git://git.curoverse.com/arvados.git /tmp/arvados && cd /tmp/arvados/services/api && /usr/local/rvm/bin/rvm-exec default bundle && cd /tmp/arvados/apps/workbench && /usr/local/rvm/bin/rvm-exec default bundle && rm -rf /tmp/arvados
+
+ENV WORKSPACE /arvados
+CMD ["/usr/local/rvm/bin/rvm-exec", "default", "bash", "/jenkins/run-build-packages.sh", "--target", "ubuntu1804"]
index fd2f9e3d8cdbafced2990875f56ce72a2719672f..fa959a1eb8cc2e7aa39d89338ae203c5fe599933 100644 (file)
@@ -9,7 +9,7 @@ RUN yum -q -y install scl-utils centos-release-scl which tar
 
 # Install RVM
 RUN touch /var/lib/rpm/* && \
-    gpg --keyserver pool.sks-keyservers.net --recv-keys D39DC0E3 && \
+    gpg --keyserver ha.pool.sks-keyservers.net --recv-keys D39DC0E3 && \
     curl -L https://get.rvm.io | bash -s stable && \
     /usr/local/rvm/bin/rvm install 2.3 && \
     /usr/local/rvm/bin/rvm alias create default ruby-2.3 && \
index dcf581a1e036876cb85676e305b78eb399381448..c40ed820790a8672e63b9f01e19ba4ea86c3313f 100644 (file)
@@ -10,7 +10,7 @@ ENV DEBIAN_FRONTEND noninteractive
 # Install RVM
 RUN apt-get update && \
     apt-get -y install --no-install-recommends curl ca-certificates && \
-    gpg --keyserver pool.sks-keyservers.net --recv-keys D39DC0E3 && \
+    gpg --keyserver ha.pool.sks-keyservers.net --recv-keys D39DC0E3 && \
     curl -L https://get.rvm.io | bash -s stable && \
     /usr/local/rvm/bin/rvm install 2.3 && \
     /usr/local/rvm/bin/rvm alias create default ruby-2.3
index a1bc48443ea4eb48e4939d04deb8952c67220882..6d929e84948e3eb3d91f974b5b292b18b22fea62 100644 (file)
@@ -10,7 +10,7 @@ ENV DEBIAN_FRONTEND noninteractive
 # Install dependencies and RVM
 RUN apt-get update && \
     apt-get -y install --no-install-recommends curl ca-certificates python2.7-dev python3 python-setuptools python3-setuptools libcurl4-gnutls-dev curl git libattr1-dev libfuse-dev libpq-dev python-pip unzip binutils build-essential ca-certificates  && \
-    gpg --keyserver pool.sks-keyservers.net --recv-keys D39DC0E3 && \
+    gpg --keyserver ha.pool.sks-keyservers.net --recv-keys D39DC0E3 && \
     curl -L https://get.rvm.io | bash -s stable && \
     /usr/local/rvm/bin/rvm install 2.3 && \
     /usr/local/rvm/bin/rvm alias create default ruby-2.3
index 25d49dc5019112cc7d9cc6b251904780471faa79..54b1f401cf00cd2a3b6248676de51aee99915b29 100644 (file)
@@ -10,7 +10,7 @@ ENV DEBIAN_FRONTEND noninteractive
 # Install RVM
 RUN apt-get update && \
     apt-get -y install --no-install-recommends curl ca-certificates && \
-    gpg --keyserver pool.sks-keyservers.net --recv-keys D39DC0E3 && \
+    gpg --keyserver ha.pool.sks-keyservers.net --recv-keys D39DC0E3 && \
     curl -L https://get.rvm.io | bash -s stable && \
     /usr/local/rvm/bin/rvm install 2.3 && \
     /usr/local/rvm/bin/rvm alias create default ruby-2.3
diff --git a/build/package-test-dockerfiles/ubuntu1804/Dockerfile b/build/package-test-dockerfiles/ubuntu1804/Dockerfile
new file mode 100644 (file)
index 0000000..506abac
--- /dev/null
@@ -0,0 +1,26 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+FROM ubuntu:bionic
+MAINTAINER Ward Vandewege <wvandewege@veritasgenetics.com>
+
+ENV DEBIAN_FRONTEND noninteractive
+
+# Install RVM
+RUN apt-get update && \
+    apt-get -y install --no-install-recommends curl ca-certificates gnupg2 && \
+    gpg --keyserver ha.pool.sks-keyservers.net --recv-keys D39DC0E3 && \
+    curl -L https://get.rvm.io | bash -s stable && \
+    /usr/local/rvm/bin/rvm install 2.3 && \
+    /usr/local/rvm/bin/rvm alias create default ruby-2.3
+
+# udev daemon can't start in a container, so don't try.
+RUN mkdir -p /etc/udev/disabled
+
+RUN echo "deb [trusted=yes] file:///arvados/packages/ubuntu1804/ /" >>/etc/apt/sources.list
+
+# Add preferences file for the Arvados packages. This pins Arvados
+# packages at priority 501, so that older python dependency versions
+# are preferred in those cases where we need them
+ADD etc-apt-preferences.d-arvados /etc/apt/preferences.d/arvados
diff --git a/build/package-test-dockerfiles/ubuntu1804/etc-apt-preferences.d-arvados b/build/package-test-dockerfiles/ubuntu1804/etc-apt-preferences.d-arvados
new file mode 100644 (file)
index 0000000..9e24695
--- /dev/null
@@ -0,0 +1,3 @@
+Package: *
+Pin: release o=Arvados
+Pin-Priority: 501
index 900b091959107ace13d606f0a433d5d90c56f0d9..b5325224ee170bbc6170babfa8c2b95665d2057a 100755 (executable)
@@ -27,7 +27,7 @@ cd /tmp/opts
 
 export ARV_PACKAGES_DIR="/arvados/packages/$target"
 
-if [[ -f $(ls -t "$ARV_PACKAGES_DIR/$1"_*.deb | head -n1) ]] ; then
+if [[ -f $(ls -t "$ARV_PACKAGES_DIR/$1"_*.deb 2>/dev/null | head -n1) ]] ; then
     debpkg=$(ls -t "$ARV_PACKAGES_DIR/$1"_*.deb | head -n1)
 else
     debpkg=$(ls -t "$ARV_PACKAGES_DIR/processed/$1"_*.deb | head -n1)
diff --git a/build/package-testing/test-packages-ubuntu1804.sh b/build/package-testing/test-packages-ubuntu1804.sh
new file mode 120000 (symlink)
index 0000000..54ce94c
--- /dev/null
@@ -0,0 +1 @@
+deb-common-test-packages.sh
\ No newline at end of file
index bd3975841bf40cbb674dce29b43960ac1a08a524..d6a21178f8cfe3d224a08f955ca35eeb996d0dff 100755 (executable)
@@ -71,22 +71,13 @@ if [[ "$DEBUG" != 0 ]]; then
 fi
 
 case "$TARGET" in
-    debian8)
+    debian*)
         FORMAT=deb
         ;;
-    debian9)
+    ubuntu*)
         FORMAT=deb
         ;;
-    ubuntu1204)
-        FORMAT=deb
-        ;;
-    ubuntu1404)
-        FORMAT=deb
-        ;;
-    ubuntu1604)
-        FORMAT=deb
-        ;;
-    centos7)
+    centos*)
         FORMAT=rpm
         ;;
     *)
index caebac013d4db721af25655a3551371c35275782..9ffa20fe6492443feb5490ec4ce06309374b4d06 100755 (executable)
@@ -104,19 +104,13 @@ PYTHON3_INSTALL_LIB=lib/python$PYTHON3_VERSION/dist-packages
 ## End Debian Python defaults.
 
 case "$TARGET" in
-    debian8)
+    debian*)
         FORMAT=deb
         ;;
-    debian9)
+    ubuntu*)
         FORMAT=deb
         ;;
-    ubuntu1404)
-        FORMAT=deb
-        ;;
-    ubuntu1604)
-        FORMAT=deb
-        ;;
-    centos7)
+    centos*)
         FORMAT=rpm
         PYTHON2_PACKAGE=$(rpm -qf "$(which python$PYTHON2_VERSION)" --queryformat '%{NAME}\n')
         PYTHON2_PKG_PREFIX=$PYTHON2_PACKAGE
@@ -153,8 +147,13 @@ if [[ "$?" != 0 ]]; then
   exit 1
 fi
 
-EASY_INSTALL2=$(find_easy_install -$PYTHON2_VERSION "")
-EASY_INSTALL3=$(find_easy_install -$PYTHON3_VERSION 3)
+PYTHON2_FPM_INSTALLER=(--python-easyinstall "$(find_python_program easy_install-$PYTHON2_VERSION easy_install)")
+install3=$(find_python_program easy_install-$PYTHON3_VERSION easy_install3 pip-$PYTHON3_VERSION pip3)
+if [[ $install3 =~ easy_ ]]; then
+    PYTHON3_FPM_INSTALLER=(--python-easyinstall "$install3")
+else
+    PYTHON3_FPM_INSTALLER=(--python-pip "$install3")
+fi
 
 RUN_BUILD_PACKAGES_PATH="`dirname \"$0\"`"
 RUN_BUILD_PACKAGES_PATH="`( cd \"$RUN_BUILD_PACKAGES_PATH\" && pwd )`"  # absolutized and normalized
index 6ee57a4be5597114bbb0521d562883c3a1850399..8ba14949d3c0847acaaa8c2fe3671a513c7668de 100755 (executable)
@@ -20,17 +20,19 @@ debug_echo () {
     echo "$@" >"$STDOUT_IF_DEBUG"
 }
 
-find_easy_install() {
-    for version_suffix in "$@"; do
-        if "easy_install$version_suffix" --version >/dev/null 2>&1; then
-            echo "easy_install$version_suffix"
+find_python_program() {
+    prog="$1"
+    shift
+    for prog in "$@"; do
+        if "$prog" --version >/dev/null 2>&1; then
+            echo "$prog"
             return 0
         fi
     done
     cat >&2 <<EOF
 $helpmessage
 
-Error: easy_install$1 (from Python setuptools module) not found
+Error: $prog (from Python setuptools module) not found
 
 EOF
     exit 1
@@ -264,7 +266,7 @@ test_package_presence() {
     # Get the list of packages from the repos
 
     if [[ "$FORMAT" == "deb" ]]; then
-      debian_distros="jessie precise stretch trusty wheezy xenial"
+      debian_distros="jessie precise stretch trusty wheezy xenial bionic"
 
       for D in ${debian_distros}; do
         if [ ${pkgname:0:3} = "lib" ]; then
@@ -391,7 +393,7 @@ fpm_build () {
           # Make sure we build with that for consistency.
           python=python2.7
           set -- "$@" --python-bin python2.7 \
-              --python-easyinstall "$EASY_INSTALL2" \
+              "${PYTHON_FPM_INSTALLER[@]}" \
               --python-package-name-prefix "$PYTHON2_PKG_PREFIX" \
               --prefix "$PYTHON2_PREFIX" \
               --python-install-lib "$PYTHON2_INSTALL_LIB" \
@@ -407,7 +409,7 @@ fpm_build () {
           PACKAGE_TYPE=python
           python=python3
           set -- "$@" --python-bin python3 \
-              --python-easyinstall "$EASY_INSTALL3" \
+              "${PYTHON3_FPM_INSTALLER[@]}" \
               --python-package-name-prefix "$PYTHON3_PKG_PREFIX" \
               --prefix "$PYTHON3_PREFIX" \
               --python-install-lib "$PYTHON3_INSTALL_LIB" \
index e669e326c512b8d29694c05106ad26d05507a4fa..4ddbf89c1d7ccb286fcfe887fb941734bffbb519 100755 (executable)
@@ -270,6 +270,7 @@ rotate_logfile() {
 
 declare -a failures
 declare -A skip
+declare -A only
 declare -A testargs
 skip[apps/workbench_profile]=1
 # nodemanager_integration tests are not reliable, see #12061.
@@ -288,7 +289,7 @@ do
             skip[$1]=1; shift
             ;;
         --only)
-            only="$1"; skip[$1]=""; shift
+            only[$1]=1; skip[$1]=""; shift
             ;;
         --short)
             short=1
@@ -331,14 +332,19 @@ done
 # required when testing it. Skip that step if it is not needed.
 NEED_SDK_R=true
 
-if [[ ! -z "${only}" && "${only}" != "sdk/R" ]]; then
+if [[ ${#only[@]} -ne 0 ]] &&
+   [[ -z "${only['sdk/R']}" && -z "${only['doc']}" ]]; then
   NEED_SDK_R=false
 fi
 
-if [[ ! -z "${skip}" && "${skip}" == "sdk/R" ]]; then
+if [[ ${skip["sdk/R"]} == 1 && ${skip["doc"]} == 1 ]]; then
   NEED_SDK_R=false
 fi
 
+if [[ $NEED_SDK_R == false ]]; then
+       echo "R SDK not needed, it will not be installed."
+fi
+
 start_services() {
     echo 'Starting API, keepproxy, keep-web, ws, arv-git-httpd, and nginx ssl proxy...'
     if [[ ! -d "$WORKSPACE/services/api/log" ]]; then
@@ -658,9 +664,9 @@ do_test() {
             ;;
     esac
     if [[ -z "${skip[$suite]}" && -z "${skip[$1]}" && \
-              (-z "${only}" || "${only}" == "${suite}" || \
-                   "${only}" == "${1}") ||
-                  "${only}" == "${2}" ]]; then
+              (${#only[@]} -eq 0 || ${only[$suite]} -eq 1 || \
+                   ${only[$1]} -eq 1) ||
+                  ${only[$2]} -eq 1 ]]; then
         retry do_test_once ${@}
     else
         title "Skipping ${1} tests"
index e87bc51ad4a590b4102fd4f1047c9b878de466a2..c50366d4de6c4a14b5e1203283301a2938d54623 100644 (file)
@@ -125,3 +125,24 @@ table(table table-bordered table-condensed).
 |_. Argument |_. Type |_. Description |_. Location |_. Example |
 {background:#ccffcc}.|uuid|string|The UUID of the Group to untrash.|path||
 |ensure_unique_name|boolean (default false)|Rename project uniquely if untrashing it would fail with a unique name conflict.|query||
+
+h3. shared
+
+This endpoint returns the toplevel set of groups to which access is granted through a chain of one or more permission links rather than through direct ownership by the current user account.  This is useful for clients which wish to browse the list of projects the user has permission to read which are not part of the "home" project tree.
+
+When called with "include=owner_uuid" this also returns (in the "included" field) the objects that own those projects (users or non-project groups).
+
+Specifically, the logic is:
+
+<pre>
+select groups that are readable by current user AND
+    (the owner_uuid is a user (but not the current user) OR
+     the owner_uuid is not readable by the current user OR
+     the owner_uuid is a group but group_class is not a project)
+</pre>
+
+In addition to the "include" parameter this endpoint also supports the same parameters as the "list method.":{{site.baseurl}}/api/methods.html#index
+
+table(table table-bordered table-condensed).
+|_. Argument |_. Type |_. Description |_. Location |_. Example |
+|include|string|If provided with the value "owner_uuid", this will return owner objects in the "included" field of the response.|query|?include=owner_uuid|
index 3b4dfbe99111972e638ae19ac40813d065f6d2d4..2b7b31b9f3f4b4070cbd14d986ffe87259989200 100644 (file)
@@ -36,7 +36,9 @@ setup(name='arvados-cwl-runner',
           'cwltool==1.0.20180806194258',
           'schema-salad==2.7.20180719125426',
           'typing >= 3.6.4',
-          'ruamel.yaml >=0.13.11, <0.15',
+          # Need to limit ruamel.yaml version to 0.15.26 because of bug
+          # https://bitbucket.org/ruamel/yaml/issues/227/regression-parsing-flow-mapping
+          'ruamel.yaml >=0.13.11, <= 0.15.26',
           'arvados-python-client>=1.1.4.20180607143841',
           'setuptools',
           'ciso8601 >=1.0.6, <2.0.0',
index 3f8a32816ddccdad01c78eedfdce1ed0b2be5e64..69f3ae046e31ca3e02aa52be7967e43da2394d00 100644 (file)
@@ -5,6 +5,7 @@
 import arvados_cwl
 import arvados_cwl.context
 from arvados_cwl.arvdocker import arv_docker_clear_cache
+import arvados.config
 import logging
 import mock
 import unittest
@@ -21,6 +22,32 @@ if not os.getenv('ARVADOS_DEBUG'):
     logging.getLogger('arvados.cwl-runner').setLevel(logging.WARN)
     logging.getLogger('arvados.arv-run').setLevel(logging.WARN)
 
+class CollectionMock(object):
+    def __init__(self, vwdmock, *args, **kwargs):
+        self.vwdmock = vwdmock
+        self.count = 0
+
+    def open(self, *args, **kwargs):
+        self.count += 1
+        return self.vwdmock.open(*args, **kwargs)
+
+    def copy(self, *args, **kwargs):
+        self.count += 1
+        self.vwdmock.copy(*args, **kwargs)
+
+    def save_new(self, *args, **kwargs):
+        pass
+
+    def __len__(self):
+        return self.count
+
+    def portable_data_hash(self):
+        if self.count == 0:
+            return arvados.config.EMPTY_BLOCK_LOCATOR
+        else:
+            return "99999999999999999999999999999996+99"
+
+
 class TestContainer(unittest.TestCase):
 
     def helper(self, runner, enable_reuse=True):
@@ -231,8 +258,7 @@ class TestContainer(unittest.TestCase):
         runner.fs_access.get_collection.side_effect = get_collection_mock
 
         vwdmock = mock.MagicMock()
-        collection_mock.return_value = vwdmock
-        vwdmock.portable_data_hash.return_value = "99999999999999999999999999999996+99"
+        collection_mock.side_effect = lambda *args, **kwargs: CollectionMock(vwdmock, *args, **kwargs)
 
         tool = cmap({
             "inputs": [],
index 4473b88ca0d785dbb2eaff961bf64fd21c25c280..20efe1513981585b3c699f73d0dbba6994f7c682 100644 (file)
@@ -19,6 +19,7 @@ from schema_salad.ref_resolver import Loader
 from schema_salad.sourceline import cmap
 from .mock_discovery import get_rootDesc
 from .matcher import JsonDiffMatcher, StripYAMLComments
+from .test_container import CollectionMock
 
 if not os.getenv('ARVADOS_DEBUG'):
     logging.getLogger('arvados.cwl-runner').setLevel(logging.WARN)
@@ -388,7 +389,8 @@ class TestWorkflow(unittest.TestCase):
         tool, metadata = loadingContext.loader.resolve_ref("tests/wf/scatter2.cwl")
         metadata["cwlVersion"] = tool["cwlVersion"]
 
-        mockcollection().portable_data_hash.return_value = "99999999999999999999999999999999+118"
+        mockc = mock.MagicMock()
+        mockcollection.side_effect = lambda *args, **kwargs: CollectionMock(mockc, *args, **kwargs)
         mockcollectionreader().find.return_value = arvados.arvfile.ArvadosFile(mock.MagicMock(), "token.txt")
 
         arvtool = arvados_cwl.ArvadosWorkflow(runner, tool, loadingContext)
@@ -412,8 +414,8 @@ class TestWorkflow(unittest.TestCase):
                         'HOME': '$(task.outdir)',
                         'TMPDIR': '$(task.tmpdir)'},
                                'task.vwd': {
-                                   'workflow.cwl': '$(task.keep)/99999999999999999999999999999999+118/workflow.cwl',
-                                   'cwl.input.yml': '$(task.keep)/99999999999999999999999999999999+118/cwl.input.yml'
+                                   'workflow.cwl': '$(task.keep)/99999999999999999999999999999996+99/workflow.cwl',
+                                   'cwl.input.yml': '$(task.keep)/99999999999999999999999999999996+99/cwl.input.yml'
                                },
                     'command': [u'cwltool', u'--no-container', u'--move-outputs', u'--preserve-entire-environment', u'workflow.cwl#main', u'cwl.input.yml'],
                     'task.stdout': 'cwl.output.json'}]},
@@ -430,8 +432,8 @@ class TestWorkflow(unittest.TestCase):
                      ['docker_image_locator', 'in docker', 'arvados/jobs']],
             find_or_create=True)
 
-        mockcollection().open().__enter__().write.assert_has_calls([mock.call(subwf)])
-        mockcollection().open().__enter__().write.assert_has_calls([mock.call(
+        mockc.open().__enter__().write.assert_has_calls([mock.call(subwf)])
+        mockc.open().__enter__().write.assert_has_calls([mock.call(
 '''{
   "fileblub": {
     "basename": "token.txt",
@@ -469,7 +471,7 @@ class TestWorkflow(unittest.TestCase):
         tool, metadata = loadingContext.loader.resolve_ref("tests/wf/echo-wf.cwl")
         metadata["cwlVersion"] = tool["cwlVersion"]
 
-        mockcollection().portable_data_hash.return_value = "99999999999999999999999999999999+118"
+        mockcollection.side_effect = lambda *args, **kwargs: CollectionMock(mock.MagicMock(), *args, **kwargs)
 
         arvtool = arvados_cwl.ArvadosWorkflow(runner, tool, loadingContext)
         arvtool.formatgraph = None
@@ -491,8 +493,8 @@ class TestWorkflow(unittest.TestCase):
                         'HOME': '$(task.outdir)',
                         'TMPDIR': '$(task.tmpdir)'},
                                'task.vwd': {
-                                   'workflow.cwl': '$(task.keep)/99999999999999999999999999999999+118/workflow.cwl',
-                                   'cwl.input.yml': '$(task.keep)/99999999999999999999999999999999+118/cwl.input.yml'
+                                   'workflow.cwl': '$(task.keep)/99999999999999999999999999999996+99/workflow.cwl',
+                                   'cwl.input.yml': '$(task.keep)/99999999999999999999999999999996+99/cwl.input.yml'
                                },
                     'command': [u'cwltool', u'--no-container', u'--move-outputs', u'--preserve-entire-environment', u'workflow.cwl#main', u'cwl.input.yml'],
                     'task.stdout': 'cwl.output.json'}]},
index d980db575dd8d6e3db1ac3dbb0f7709cb14a894e..8875b7d954d916e332175b5dbdd67103833719b1 100644 (file)
@@ -289,14 +289,14 @@ class TestSubmit(unittest.TestCase):
                 'manifest_text':
                 '. 5bcc9fe8f8d5992e6cf418dc7ce4dbb3+16 0:16:blub.txt\n',
                 'replication_desired': None,
-                'name': 'submit_tool.cwl dependencies',
-            }), ensure_unique_name=True),
+                'name': 'submit_tool.cwl dependencies (5d373e7629203ce39e7c22af98a0f881+52)',
+            }), ensure_unique_name=False),
             mock.call(body=JsonDiffMatcher({
                 'manifest_text':
                 '. 979af1245a12a1fed634d4222473bfdc+16 0:16:blorp.txt\n',
                 'replication_desired': None,
-                'name': 'submit_wf.cwl input',
-            }), ensure_unique_name=True),
+                'name': 'submit_wf.cwl input (169f39d466a5438ac4a90e779bf750c7+53)',
+            }), ensure_unique_name=False),
             mock.call(body=JsonDiffMatcher({
                 'manifest_text':
                 '. 61df2ed9ee3eb7dd9b799e5ca35305fa+1217 0:1217:workflow.cwl\n',
@@ -485,14 +485,14 @@ class TestSubmit(unittest.TestCase):
                 'manifest_text':
                 '. 5bcc9fe8f8d5992e6cf418dc7ce4dbb3+16 0:16:blub.txt\n',
                 'replication_desired': None,
-                'name': 'submit_tool.cwl dependencies',
-            }), ensure_unique_name=True),
+                'name': 'submit_tool.cwl dependencies (5d373e7629203ce39e7c22af98a0f881+52)',
+            }), ensure_unique_name=False),
             mock.call(body=JsonDiffMatcher({
                 'manifest_text':
                 '. 979af1245a12a1fed634d4222473bfdc+16 0:16:blorp.txt\n',
                 'replication_desired': None,
-                'name': 'submit_wf.cwl input',
-            }), ensure_unique_name=True)])
+                'name': 'submit_wf.cwl input (169f39d466a5438ac4a90e779bf750c7+53)',
+            }), ensure_unique_name=False)])
 
         expect_container = copy.deepcopy(stubs.expect_container_spec)
         stubs.api.container_requests().create.assert_called_with(
index 91da5a3fd62ce6eb099e4ce0c0e206a1220268ae..e3a9f4ae8d892ea03f379e7dad3ded4a6dc951fc 100644 (file)
@@ -173,8 +173,11 @@ func New(c *arvados.Client) (*ArvadosClient, error) {
                ApiServer:   c.APIHost,
                ApiToken:    c.AuthToken,
                ApiInsecure: c.Insecure,
-               Client: &http.Client{Transport: &http.Transport{
-                       TLSClientConfig: MakeTLSConfig(c.Insecure)}},
+               Client: &http.Client{
+                       Timeout: 5 * time.Minute,
+                       Transport: &http.Transport{
+                               TLSClientConfig: MakeTLSConfig(c.Insecure)},
+               },
                External:          false,
                Retries:           2,
                KeepServiceURIs:   c.KeepServiceURIs,
index 4e25ba4f0603699569402d619127dd4b9fd99fb1..c8fb5aeb37e97eb9a43846301e2a07ea63e28ae7 100644 (file)
@@ -9,12 +9,12 @@ package dispatch
 import (
        "context"
        "fmt"
-       "log"
        "sync"
        "time"
 
        "git.curoverse.com/arvados.git/sdk/go/arvados"
        "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
+       "github.com/Sirupsen/logrus"
 )
 
 const (
@@ -25,10 +25,18 @@ const (
        Cancelled = arvados.ContainerStateCancelled
 )
 
+type Logger interface {
+       Printf(string, ...interface{})
+       Warnf(string, ...interface{})
+       Debugf(string, ...interface{})
+}
+
 // Dispatcher struct
 type Dispatcher struct {
        Arv *arvadosclient.ArvadosClient
 
+       Logger Logger
+
        // Batch size for container queries
        BatchSize int64
 
@@ -65,6 +73,10 @@ type DispatchFunc func(*Dispatcher, arvados.Container, <-chan arvados.Container)
 // dispatcher's token. When a new one appears, Run calls RunContainer
 // in a new goroutine.
 func (d *Dispatcher) Run(ctx context.Context) error {
+       if d.Logger == nil {
+               d.Logger = logrus.StandardLogger()
+       }
+
        err := d.Arv.Call("GET", "api_client_authorizations", "", "current", nil, &d.auth)
        if err != nil {
                return fmt.Errorf("error getting my token UUID: %v", err)
@@ -142,7 +154,7 @@ func (d *Dispatcher) Run(ctx context.Context) error {
                // Containers that I know about that didn't show up in any
                // query should be let go.
                for uuid, tracker := range todo {
-                       log.Printf("Container %q not returned by any query, stopping tracking.", uuid)
+                       d.Logger.Printf("Container %q not returned by any query, stopping tracking.", uuid)
                        tracker.close()
                }
 
@@ -152,7 +164,10 @@ func (d *Dispatcher) Run(ctx context.Context) error {
 // Start a runner in a new goroutine, and send the initial container
 // record to its updates channel.
 func (d *Dispatcher) start(c arvados.Container) *runTracker {
-       tracker := &runTracker{updates: make(chan arvados.Container, 1)}
+       tracker := &runTracker{
+               updates: make(chan arvados.Container, 1),
+               logger:  d.Logger,
+       }
        tracker.updates <- c
        go func() {
                d.RunContainer(d, c, tracker.updates)
@@ -174,7 +189,7 @@ func (d *Dispatcher) checkForUpdates(filters [][]interface{}, todo map[string]*r
                "order":   []string{"priority desc"}}
        err := d.Arv.List("containers", params, &countList)
        if err != nil {
-               log.Printf("error getting count of containers: %q", err)
+               d.Logger.Warnf("error getting count of containers: %q", err)
                return false
        }
        itemsAvailable := countList.ItemsAvailable
@@ -195,7 +210,7 @@ func (d *Dispatcher) checkForUpdates(filters [][]interface{}, todo map[string]*r
 
                err := d.Arv.List("containers", params, &list)
                if err != nil {
-                       log.Printf("Error getting list of containers: %q", err)
+                       d.Logger.Warnf("error getting list of containers: %q", err)
                        return false
                }
                d.checkListForUpdates(list.Items, todo)
@@ -218,7 +233,7 @@ func (d *Dispatcher) checkListForUpdates(containers []arvados.Container, todo ma
                delete(todo, c.UUID)
 
                if c.LockedByUUID != "" && c.LockedByUUID != d.auth.UUID {
-                       log.Printf("debug: ignoring %s locked by %s", c.UUID, c.LockedByUUID)
+                       d.Logger.Debugf("ignoring %s locked by %s", c.UUID, c.LockedByUUID)
                } else if alreadyTracking {
                        switch c.State {
                        case Queued:
@@ -236,7 +251,7 @@ func (d *Dispatcher) checkListForUpdates(containers []arvados.Container, todo ma
                                }
                                err := d.lock(c.UUID)
                                if err != nil {
-                                       log.Printf("debug: error locking container %s: %s", c.UUID, err)
+                                       d.Logger.Warnf("error locking container %s: %s", c.UUID, err)
                                        break
                                }
                                c.State = Locked
@@ -260,7 +275,7 @@ func (d *Dispatcher) UpdateState(uuid string, state arvados.ContainerState) erro
                        "container": arvadosclient.Dict{"state": state},
                }, nil)
        if err != nil {
-               log.Printf("Error updating container %s to state %q: %s", uuid, state, err)
+               d.Logger.Warnf("error updating container %s to state %q: %s", uuid, state, err)
        }
        return err
 }
@@ -315,6 +330,7 @@ func (d *Dispatcher) TrackContainer(uuid string) error {
 type runTracker struct {
        closing bool
        updates chan arvados.Container
+       logger  Logger
 }
 
 func (tracker *runTracker) close() {
@@ -330,7 +346,7 @@ func (tracker *runTracker) update(c arvados.Container) {
        }
        select {
        case <-tracker.updates:
-               log.Printf("debug: runner is handling updates slowly, discarded previous update for %s", c.UUID)
+               tracker.logger.Debugf("runner is handling updates slowly, discarded previous update for %s", c.UUID)
        default:
        }
        tracker.updates <- c
index c4748fa995759ef0cc934b699a14523f8a3181f8..96f5bdd44a12ae42c25fbe64f68b342cb0356fcf 100644 (file)
@@ -1,6 +1,19 @@
 # Copyright (C) The Arvados Authors. All rights reserved.
+# Copyright (C) 2018 Genome Research Ltd.
 #
 # SPDX-License-Identifier: Apache-2.0
+#
+# 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.
 
 from __future__ import print_function
 from __future__ import absolute_import
@@ -22,6 +35,7 @@ import sys
 import errno
 import arvados.commands._util as arv_cmd
 import arvados.collection
+import arvados.config as config
 
 from arvados._version import __version__
 
@@ -207,22 +221,48 @@ def uploadfiles(files, api, dry_run=False, num_retries=0,
                     for src in iterfiles:
                         write_file(collection, pathprefix, os.path.join(root, src), not packed)
 
-        filters=[["portable_data_hash", "=", collection.portable_data_hash()]]
-        if name:
-            filters.append(["name", "like", name+"%"])
-        if project:
-            filters.append(["owner_uuid", "=", project])
-
-        exists = api.collections().list(filters=filters, limit=1).execute(num_retries=num_retries)
-
-        if exists["items"]:
-            item = exists["items"][0]
-            pdh = item["portable_data_hash"]
-            logger.info("Using collection %s (%s)", pdh, item["uuid"])
-        elif len(collection) > 0:
-            collection.save_new(name=name, owner_uuid=project, ensure_unique_name=True)
+        pdh = None
+        if len(collection) > 0:
+            # non-empty collection
+            filters = [["portable_data_hash", "=", collection.portable_data_hash()]]
+            name_pdh = "%s (%s)" % (name, collection.portable_data_hash())
+            if name:
+                filters.append(["name", "=", name_pdh])
+            if project:
+                filters.append(["owner_uuid", "=", project])
+
+            # do the list / create in a loop with up to 2 tries as we are using `ensure_unique_name=False`
+            # and there is a potential race with other workflows that may have created the collection
+            # between when we list it and find it does not exist and when we attempt to create it.
+            tries = 2
+            while pdh is None and tries > 0:
+                exists = api.collections().list(filters=filters, limit=1).execute(num_retries=num_retries)
+
+                if exists["items"]:
+                    item = exists["items"][0]
+                    pdh = item["portable_data_hash"]
+                    logger.info("Using collection %s (%s)", pdh, item["uuid"])
+                else:
+                    try:
+                        collection.save_new(name=name_pdh, owner_uuid=project, ensure_unique_name=False)
+                        pdh = collection.portable_data_hash()
+                        logger.info("Uploaded to %s (%s)", pdh, collection.manifest_locator())
+                    except arvados.errors.ApiError as ae:
+                        tries -= 1
+            if pdh is None:
+                # Something weird going on here, probably a collection
+                # with a conflicting name but wrong PDH.  We won't
+                # able to reuse it but we still need to save our
+                # collection, so so save it with unique name.
+                logger.info("Name conflict on '%s', existing collection has an unexpected portable data hash", name_pdh)
+                collection.save_new(name=name_pdh, owner_uuid=project, ensure_unique_name=True)
+                pdh = collection.portable_data_hash()
+                logger.info("Uploaded to %s (%s)", pdh, collection.manifest_locator())
+        else:
+            # empty collection
             pdh = collection.portable_data_hash()
-            logger.info("Uploaded to %s (%s)", pdh, collection.manifest_locator())
+            assert (pdh == config.EMPTY_BLOCK_LOCATOR), "Empty collection portable_data_hash did not have expected locator, was %s" % pdh
+            logger.info("Using empty collection %s", pdh)
 
     for c in files:
         c.keepref = "%s/%s" % (pdh, c.fn)
index 5e066f014598560ed211a215ef8866150a77bac3..f748d52179b2d95f665027eea48dfa5c607fa0d7 100644 (file)
@@ -51,10 +51,10 @@ setup(name='arvados-python-client',
           'google-api-python-client >=1.6.2, <1.7',
           'httplib2 >=0.9.2',
           'pycurl >=7.19.5.1',
-          'ruamel.yaml >=0.13.11, <0.15',
+          'ruamel.yaml >=0.13.11, <0.16',
           'setuptools',
-          'ws4py <0.4',
-          'subprocess32>=3.5.1',
+          'ws4py >=0.4.2',
+          'subprocess32 >=3.5.1',
       ],
       test_suite='tests',
       tests_require=['pbr<1.7.0', 'mock>=1.0', 'PyYAML'],
index 71176056fa3f24c5340796d2ef2fe20069acd666..1cb49f46749054859980471772aa8f4b3f7f51e1 100644 (file)
@@ -78,6 +78,7 @@ class ApplicationController < ActionController::Base
     @distinct = nil
     @response_resource_name = nil
     @attrs = nil
+    @extra_included = nil
   end
 
   def default_url_options
@@ -494,6 +495,9 @@ class ApplicationController < ActionController::Base
       :limit => @limit,
       :items => @objects.as_api_response(nil, {select: @select})
     }
+    if @extra_included
+      list[:included] = @extra_included.as_api_response(nil, {select: @select})
+    end
     case params[:count]
     when nil, '', 'exact'
       if @objects.respond_to? :except
index 33140be8efe2eb05542e5fd11443450772ce3556..a963d1fc4d875d2d69129ebb6ec6606e7f65d666 100644 (file)
@@ -7,6 +7,9 @@ require "trashable"
 class Arvados::V1::GroupsController < ApplicationController
   include TrashableController
 
+  skip_before_filter :find_object_by_uuid, only: :shared
+  skip_before_filter :render_404_if_no_object, only: :shared
+
   def self._index_requires_parameters
     (super rescue {}).
       merge({
@@ -63,6 +66,59 @@ class Arvados::V1::GroupsController < ApplicationController
     })
   end
 
+  def shared
+    # The purpose of this endpoint is to return the toplevel set of
+    # groups which are *not* reachable through a direct ownership
+    # chain of projects starting from the current user account.  In
+    # other words, groups which to which access was granted via a
+    # permission link or chain of links.
+    #
+    # This also returns (in the "included" field) the objects that own
+    # those projects (users or non-project groups).
+    #
+    # select groups that are readable by current user AND
+    #   the owner_uuid is a user (but not the current user) OR
+    #   the owner_uuid is not readable by the current user
+    #   the owner_uuid is a group but group_class is not a project
+    #
+    # The intended use of this endpoint is to support clients which
+    # wish to browse those projects which are visible to the user but
+    # are not part of the "home" project.
+
+    load_limit_offset_order_params
+    load_filters_param
+
+    read_parent_check = if current_user.is_admin
+                          ""
+                        else
+                          "NOT EXISTS(SELECT 1 FROM #{PERMISSION_VIEW} WHERE "+
+                            "user_uuid=(:user_uuid) AND target_uuid=groups.owner_uuid AND perm_level >= 1) OR "
+                        end
+
+    @objects = Group.readable_by(*@read_users).where("groups.owner_uuid IN (SELECT users.uuid FROM users WHERE users.uuid != (:user_uuid)) OR "+
+                                                     read_parent_check+
+                                                     "EXISTS(SELECT 1 FROM groups as gp where gp.uuid=groups.owner_uuid and gp.group_class != 'project')",
+                                            user_uuid: current_user.uuid)
+    apply_where_limit_order_params
+
+    owners = @objects.map(&:owner_uuid).to_a
+
+    if params["include"] == "owner_uuid"
+      @extra_included = []
+      [Group, User].each do |klass|
+        @extra_included += klass.readable_by(*@read_users).where(uuid: owners).to_a
+      end
+    end
+
+    index
+  end
+
+  def self._shared_requires_parameters
+    rp = self._index_requires_parameters
+    rp[:include] = { type: 'string', required: false }
+    rp
+  end
+
   protected
 
   def load_searchable_objects
index 4d3dde0a55a4bca4b76071b4ee57298205d1441a..470388a7c7f6786662b4661a454686d9d48e0d15 100644 (file)
@@ -33,6 +33,7 @@ class ContainerRequest < ArvadosModel
   validates :command, :container_image, :output_path, :cwd, :presence => true
   validates :output_ttl, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
   validates :priority, numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: 1000 }
+  validate :validate_datatypes
   validate :validate_scheduling_parameters
   validate :validate_state_change
   validate :check_update_whitelist
@@ -228,6 +229,43 @@ class ContainerRequest < ArvadosModel
     end
   end
 
+  def validate_datatypes
+    command.each do |c|
+      if !c.is_a? String
+        errors.add(:command, "must be an array of strings but has entry #{c.class}")
+      end
+    end
+    environment.each do |k,v|
+      if !k.is_a?(String) || !v.is_a?(String)
+        errors.add(:environment, "must be an map of String to String but has entry #{k.class} to #{v.class}")
+      end
+    end
+    [:mounts, :secret_mounts].each do |m|
+      self[m].each do |k, v|
+        if !k.is_a?(String) || !v.is_a?(Hash)
+          errors.add(m, "must be an map of String to Hash but is has entry #{k.class} to #{v.class}")
+        end
+        if v["kind"].nil?
+          errors.add(m, "each item must have a 'kind' field")
+        end
+        [[String, ["kind", "portable_data_hash", "uuid", "device_type",
+                   "path", "commit", "repository_name", "git_url"]],
+         [Integer, ["capacity"]]].each do |t, fields|
+          fields.each do |f|
+            if !v[f].nil? && !v[f].is_a?(t)
+              errors.add(m, "#{k}: #{f} must be a #{t} but is #{v[f].class}")
+            end
+          end
+        end
+        ["writable", "exclude_from_output"].each do |f|
+          if !v[f].nil? && !v[f].is_a?(TrueClass) && !v[f].is_a?(FalseClass)
+            errors.add(m, "#{k}: #{f} must be a #{t} but is #{v[f].class}")
+          end
+        end
+      end
+    end
+  end
+
   def validate_scheduling_parameters
     if self.state == Committed
       if scheduling_parameters.include? 'partitions' and
index b0c09840d790db1d634139bd796691d82f2b7c8c..b54c3c5bf170cc431140a0925b9846e7f172b397 100644 (file)
@@ -30,6 +30,7 @@ Server::Application.routes.draw do
       resources :groups do
         get 'contents', on: :collection
         get 'contents', on: :member
+        get 'shared', on: :collection
         post 'trash', on: :member
         post 'untrash', on: :member
       end
diff --git a/services/api/db/migrate/20180820130357_add_pdh_and_trash_index_to_collections.rb b/services/api/db/migrate/20180820130357_add_pdh_and_trash_index_to_collections.rb
new file mode 100644 (file)
index 0000000..8d1cdf3
--- /dev/null
@@ -0,0 +1,9 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+class AddPdhAndTrashIndexToCollections < ActiveRecord::Migration
+  def change
+    add_index :collections, [:portable_data_hash, :trash_at]
+  end
+end
diff --git a/services/api/db/migrate/20180820135808_drop_pdh_index_from_collections.rb b/services/api/db/migrate/20180820135808_drop_pdh_index_from_collections.rb
new file mode 100644 (file)
index 0000000..3d757e4
--- /dev/null
@@ -0,0 +1,9 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+class DropPdhIndexFromCollections < ActiveRecord::Migration
+  def change
+    remove_index :collections, column: :portable_data_hash
+  end
+end
index 12158e51b4568517a73b3d11abec97eada45d527..d7ee1532d0f3807338421824e4564dd35ec4ada8 100644 (file)
@@ -1823,10 +1823,10 @@ CREATE UNIQUE INDEX index_collections_on_owner_uuid_and_name ON public.collectio
 
 
 --
--- Name: index_collections_on_portable_data_hash; Type: INDEX; Schema: public; Owner: -
+-- Name: index_collections_on_portable_data_hash_and_trash_at; Type: INDEX; Schema: public; Owner: -
 --
 
-CREATE INDEX index_collections_on_portable_data_hash ON public.collections USING btree (portable_data_hash);
+CREATE INDEX index_collections_on_portable_data_hash_and_trash_at ON public.collections USING btree (portable_data_hash, trash_at);
 
 
 --
@@ -3121,3 +3121,7 @@ INSERT INTO schema_migrations (version) VALUES ('20180608123145');
 
 INSERT INTO schema_migrations (version) VALUES ('20180806133039');
 
+INSERT INTO schema_migrations (version) VALUES ('20180820130357');
+
+INSERT INTO schema_migrations (version) VALUES ('20180820135808');
+
index 92bd7cf872cfeca1c53d38c5ea05d7836e929f4f..2073d8b1bacccfaa0422643a34ddfe5ed0144461 100644 (file)
@@ -275,6 +275,13 @@ user_foo_in_sharing_group:
   api_token: 2p1pou8p4ls208mcbedeewlotghppenobcyrmyhq8pyf51xd8u
   expires_at: 2038-01-01 00:00:00
 
+user_bar_in_sharing_group:
+  uuid: zzzzz-gj3su-62hryf5fht531mz
+  api_client: untrusted
+  user: user_bar_in_sharing_group
+  api_token: 5vy55akwq85vghh80wc2cuxl4p8psay73lkpqf5c2cxvp6rmm6
+  expires_at: 2038-01-01 00:00:00
+
 user1_with_load:
   uuid: zzzzz-gj3su-357z32aux8dg2s1
   api_client: untrusted
index 68cc76949afc5b21e0f7586fb777944788a9f6cd..92a1ced52841942b60f3898a58b5818d53b3b14f 100644 (file)
@@ -152,6 +152,14 @@ group_for_sharing_tests:
   description: Users who can share objects with each other
   group_class: role
 
+project_owned_by_foo:
+  uuid:  zzzzz-j7d0g-lsjm0ibr0ydwpzx
+  owner_uuid: zzzzz-tpzed-81hsbo6mk8nl05c
+  created_at: 2014-02-03T17:22:54Z
+  modified_at: 2014-02-03T17:22:54Z
+  name: project_owned_by_foo
+  group_class: project
+
 empty_project:
   uuid: zzzzz-j7d0g-9otoxmrksam74q6
   owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
index b2cd366540d81c36fc08c3b662b9cc3ed763ebd2..282e09049e63beab2e591ac71f38b47e9484261d 100644 (file)
@@ -32,7 +32,7 @@ class Arvados::V1::ContainerRequestsControllerTest < ActionController::TestCase
 
     post :create, {
            container_request: minimal_cr.merge(
-             secret_mounts: {'/foo' => {'type' => 'json', 'content' => 'bar'}}),
+             secret_mounts: {'/foo' => {'kind' => 'json', 'content' => 'bar'}}),
          }
     assert_response :success
 
@@ -50,7 +50,7 @@ class Arvados::V1::ContainerRequestsControllerTest < ActionController::TestCase
     patch :update, {
             id: req.uuid,
             container_request: {
-              secret_mounts: {'/foo' => {'type' => 'json', 'content' => 'bar'}},
+              secret_mounts: {'/foo' => {'kind' => 'json', 'content' => 'bar'}},
             },
           }
     assert_response :success
@@ -65,7 +65,7 @@ class Arvados::V1::ContainerRequestsControllerTest < ActionController::TestCase
   test "update without deleting secret_mounts" do
     authorize_with :active
     req = container_requests(:uncommitted)
-    req.update_attributes!(secret_mounts: {'/foo' => {'type' => 'json', 'content' => 'bar'}})
+    req.update_attributes!(secret_mounts: {'/foo' => {'kind' => 'json', 'content' => 'bar'}})
 
     patch :update, {
             id: req.uuid,
index d2b2ad7de824e1d0805fafdda493a7a9eab99fe4..4506f76c6dd56655fb15c03677f0fddc26efb18e 100644 (file)
@@ -719,4 +719,61 @@ class Arvados::V1::GroupsControllerTest < ActionController::TestCase
       assert_not_nil Group.readable_by(users(auth)).where(uuid: groups(:trashed_subproject).uuid).first
     end
   end
+
+  test 'get shared owned by another user' do
+    authorize_with :user_bar_in_sharing_group
+
+    act_as_system_user do
+      Link.create!(
+        tail_uuid: users(:user_bar_in_sharing_group).uuid,
+        link_class: 'permission',
+        name: 'can_read',
+        head_uuid: groups(:project_owned_by_foo).uuid)
+    end
+
+    get :shared, {:filters => [["group_class", "=", "project"]], :include => "owner_uuid"}
+
+    assert_equal 1, json_response['items'].length
+    assert_equal json_response['items'][0]["uuid"], groups(:project_owned_by_foo).uuid
+
+    assert_equal 1, json_response['included'].length
+    assert_equal json_response['included'][0]["uuid"], users(:user_foo_in_sharing_group).uuid
+  end
+
+  test 'get shared, owned by unreadable project' do
+    authorize_with :user_bar_in_sharing_group
+
+    act_as_system_user do
+      Group.find_by_uuid(groups(:project_owned_by_foo).uuid).update!(owner_uuid: groups(:aproject).uuid)
+      Link.create!(
+        tail_uuid: users(:user_bar_in_sharing_group).uuid,
+        link_class: 'permission',
+        name: 'can_read',
+        head_uuid: groups(:project_owned_by_foo).uuid)
+    end
+
+    get :shared, {:filters => [["group_class", "=", "project"]], :include => "owner_uuid"}
+
+    assert_equal 1, json_response['items'].length
+    assert_equal json_response['items'][0]["uuid"], groups(:project_owned_by_foo).uuid
+
+    assert_equal 0, json_response['included'].length
+  end
+
+  test 'get shared, owned by non-project' do
+    authorize_with :user_bar_in_sharing_group
+
+    act_as_system_user do
+      Group.find_by_uuid(groups(:project_owned_by_foo).uuid).update!(owner_uuid: groups(:group_for_sharing_tests).uuid)
+    end
+
+    get :shared, {:filters => [["group_class", "=", "project"]], :include => "owner_uuid"}
+
+    assert_equal 1, json_response['items'].length
+    assert_equal json_response['items'][0]["uuid"], groups(:project_owned_by_foo).uuid
+
+    assert_equal 1, json_response['included'].length
+    assert_equal json_response['included'][0]["uuid"], groups(:group_for_sharing_tests).uuid
+  end
+
 end
index 1f6a73c11c781e6a3297adfd1c064ce616b4669d..81b49ff4fcce525b5e7fba88ff0c6f78087e7686 100644 (file)
@@ -69,7 +69,7 @@ class ContainerRequestTest < ActiveSupport::TestCase
     cr.container_image = "img3"
     cr.cwd = "/tmp3"
     cr.environment = {"BUP" => "BOP"}
-    cr.mounts = {"BAR" => "BAZ"}
+    cr.mounts = {"BAR" => {"kind" => "BAZ"}}
     cr.output_path = "/tmp4"
     cr.priority = 2
     cr.runtime_constraints = {"vcpus" => 4}
@@ -81,29 +81,33 @@ class ContainerRequestTest < ActiveSupport::TestCase
   end
 
   [
-    {"vcpus" => 1},
-    {"vcpus" => 1, "ram" => nil},
-    {"vcpus" => 0, "ram" => 123},
-    {"vcpus" => "1", "ram" => "123"}
-  ].each do |invalid_constraints|
-    test "Create with #{invalid_constraints}" do
+    {"runtime_constraints" => {"vcpus" => 1}},
+    {"runtime_constraints" => {"vcpus" => 1, "ram" => nil}},
+    {"runtime_constraints" => {"vcpus" => 0, "ram" => 123}},
+    {"runtime_constraints" => {"vcpus" => "1", "ram" => "123"}},
+    {"mounts" => {"FOO" => "BAR"}},
+    {"mounts" => {"FOO" => {}}},
+    {"mounts" => {"FOO" => {"kind" => "tmp", "capacity" => 42.222}}},
+    {"command" => ["echo", 55]},
+    {"environment" => {"FOO" => 55}}
+  ].each do |value|
+    test "Create with invalid #{value}" do
       set_user_from_auth :active
       assert_raises(ActiveRecord::RecordInvalid) do
-        cr = create_minimal_req!(state: "Committed",
-                                 priority: 1,
-                                 runtime_constraints: invalid_constraints)
+        cr = create_minimal_req!({state: "Committed",
+               priority: 1}.merge(value))
         cr.save!
       end
     end
 
-    test "Update with #{invalid_constraints}" do
+    test "Update with invalid #{value}" do
       set_user_from_auth :active
       cr = create_minimal_req!(state: "Uncommitted", priority: 1)
       cr.save!
       assert_raises(ActiveRecord::RecordInvalid) do
         cr = ContainerRequest.find_by_uuid cr.uuid
-        cr.update_attributes!(state: "Committed",
-                              runtime_constraints: invalid_constraints)
+        cr.update_attributes!({state: "Committed",
+                               priority: 1}.merge(value))
       end
     end
   end
index 7ee5921e0ca37842168608f9eafa63d16dd1d90f..83ab59b607b31a9e09b70e29c0c0792f09e5c559 100644 (file)
@@ -84,7 +84,7 @@ class ContainerTest < ActiveSupport::TestCase
   test "Container create" do
     act_as_system_user do
       c, _ = minimal_new(environment: {},
-                      mounts: {"BAR" => "FOO"},
+                      mounts: {"BAR" => {"kind" => "FOO"}},
                       output_path: "/tmp",
                       priority: 1,
                       runtime_constraints: {"vcpus" => 1, "ram" => 1})
@@ -101,7 +101,7 @@ class ContainerTest < ActiveSupport::TestCase
   test "Container valid priority" do
     act_as_system_user do
       c, _ = minimal_new(environment: {},
-                      mounts: {"BAR" => "FOO"},
+                      mounts: {"BAR" => {"kind" => "FOO"}},
                       output_path: "/tmp",
                       priority: 1,
                       runtime_constraints: {"vcpus" => 1, "ram" => 1})
@@ -133,8 +133,8 @@ class ContainerTest < ActiveSupport::TestCase
 
 
   test "Container serialized hash attributes sorted before save" do
-    env = {"C" => 3, "B" => 2, "A" => 1}
-    m = {"F" => {"kind" => 3}, "E" => {"kind" => 2}, "D" => {"kind" => 1}}
+    env = {"C" => "3", "B" => "2", "A" => "1"}
+    m = {"F" => {"kind" => "3"}, "E" => {"kind" => "2"}, "D" => {"kind" => "1"}}
     rc = {"vcpus" => 1, "ram" => 1, "keep_cache_ram" => 1}
     c, _ = minimal_new(environment: env, mounts: m, runtime_constraints: rc)
     assert_equal c.environment.to_json, Container.deep_sort_hash(env).to_json
index 279327ba18811ba8ad6339600cc124460f2fc35c..fc10393626be103c17b01b5b1bfde615ed470bc9 100644 (file)
@@ -10,7 +10,6 @@ import (
        "context"
        "flag"
        "fmt"
-       "log"
        "os"
        "os/exec"
        "os/signal"
@@ -21,6 +20,7 @@ import (
        "git.curoverse.com/arvados.git/sdk/go/arvados"
        "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
        "git.curoverse.com/arvados.git/sdk/go/dispatch"
+       "github.com/Sirupsen/logrus"
 )
 
 var version = "dev"
@@ -28,7 +28,7 @@ var version = "dev"
 func main() {
        err := doMain()
        if err != nil {
-               log.Fatalf("%q", err)
+               logrus.Fatalf("%q", err)
        }
 }
 
@@ -40,6 +40,14 @@ var (
 )
 
 func doMain() error {
+       logger := logrus.StandardLogger()
+       if os.Getenv("DEBUG") != "" {
+               logger.SetLevel(logrus.DebugLevel)
+       }
+       logger.Formatter = &logrus.JSONFormatter{
+               TimestampFormat: "2006-01-02T15:04:05.000000000Z07:00",
+       }
+
        flags := flag.NewFlagSet("crunch-dispatch-local", flag.ExitOnError)
 
        pollInterval := flags.Int(
@@ -66,18 +74,19 @@ func doMain() error {
                return nil
        }
 
-       log.Printf("crunch-dispatch-local %s started", version)
+       logger.Printf("crunch-dispatch-local %s started", version)
 
        runningCmds = make(map[string]*exec.Cmd)
 
        arv, err := arvadosclient.MakeArvadosClient()
        if err != nil {
-               log.Printf("Error making Arvados client: %v", err)
+               logger.Errorf("error making Arvados client: %v", err)
                return err
        }
        arv.Retries = 25
 
        dispatcher := dispatch.Dispatcher{
+               Logger:       logger,
                Arv:          arv,
                RunContainer: run,
                PollPeriod:   time.Duration(*pollInterval) * time.Second,
@@ -92,7 +101,7 @@ func doMain() error {
        c := make(chan os.Signal, 1)
        signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
        sig := <-c
-       log.Printf("Received %s, shutting down", sig)
+       logger.Printf("Received %s, shutting down", sig)
        signal.Stop(c)
 
        cancel()
@@ -138,7 +147,7 @@ func run(dispatcher *dispatch.Dispatcher,
                cmd.Stderr = os.Stderr
                cmd.Stdout = os.Stderr
 
-               log.Printf("Starting container %v", uuid)
+               dispatcher.Logger.Printf("starting container %v", uuid)
 
                // Add this crunch job to the list of runningCmds only if we
                // succeed in starting crunch-run.
@@ -146,7 +155,7 @@ func run(dispatcher *dispatch.Dispatcher,
                runningCmdsMutex.Lock()
                if err := startCmd(container, cmd); err != nil {
                        runningCmdsMutex.Unlock()
-                       log.Printf("Error starting %v for %v: %q", *crunchRunCommand, uuid, err)
+                       dispatcher.Logger.Warnf("error starting %q for %s: %s", *crunchRunCommand, uuid, err)
                        dispatcher.UpdateState(uuid, dispatch.Cancelled)
                } else {
                        runningCmds[uuid] = cmd
@@ -157,9 +166,9 @@ func run(dispatcher *dispatch.Dispatcher,
 
                        go func() {
                                if _, err := cmd.Process.Wait(); err != nil {
-                                       log.Printf("Error while waiting for crunch job to finish for %v: %q", uuid, err)
+                                       dispatcher.Logger.Warnf("error while waiting for crunch job to finish for %v: %q", uuid, err)
                                }
-                               log.Printf("sending done")
+                               dispatcher.Logger.Debugf("sending done")
                                done <- struct{}{}
                        }()
 
@@ -171,14 +180,14 @@ func run(dispatcher *dispatch.Dispatcher,
                                case c := <-status:
                                        // Interrupt the child process if priority changes to 0
                                        if (c.State == dispatch.Locked || c.State == dispatch.Running) && c.Priority == 0 {
-                                               log.Printf("Sending SIGINT to pid %d to cancel container %v", cmd.Process.Pid, uuid)
+                                               dispatcher.Logger.Printf("sending SIGINT to pid %d to cancel container %v", cmd.Process.Pid, uuid)
                                                cmd.Process.Signal(os.Interrupt)
                                        }
                                }
                        }
                        close(done)
 
-                       log.Printf("Finished container run for %v", uuid)
+                       dispatcher.Logger.Printf("finished container run for %v", uuid)
 
                        // Remove the crunch job from runningCmds
                        runningCmdsMutex.Lock()
@@ -191,11 +200,11 @@ func run(dispatcher *dispatch.Dispatcher,
        // If the container is not finalized, then change it to "Cancelled".
        err := dispatcher.Arv.Get("containers", uuid, nil, &container)
        if err != nil {
-               log.Printf("Error getting final container state: %v", err)
+               dispatcher.Logger.Warnf("error getting final container state: %v", err)
        }
        if container.State == dispatch.Locked || container.State == dispatch.Running {
-               log.Printf("After %s process termination, container state for %v is %q.  Updating it to %q",
-                       *crunchRunCommand, container.State, uuid, dispatch.Cancelled)
+               dispatcher.Logger.Warnf("after %q process termination, container state for %v is %q; updating it to %q",
+                       *crunchRunCommand, uuid, container.State, dispatch.Cancelled)
                dispatcher.UpdateState(uuid, dispatch.Cancelled)
        }
 
@@ -203,5 +212,5 @@ func run(dispatcher *dispatch.Dispatcher,
        for range status {
        }
 
-       log.Printf("Finalized container %v", uuid)
+       dispatcher.Logger.Printf("finalized container %v", uuid)
 }
index 534de6916c52168465ee887c5600b0dab86c9000..6da17ea86cc06ccdbce21c37d2b703b1f937fded 100644 (file)
@@ -8,12 +8,11 @@ import (
        "bytes"
        "context"
        "io"
-       "log"
        "net/http"
        "net/http/httptest"
        "os"
        "os/exec"
-       "strings"
+       "regexp"
        "testing"
        "time"
 
@@ -21,6 +20,7 @@ import (
        "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
        "git.curoverse.com/arvados.git/sdk/go/arvadostest"
        "git.curoverse.com/arvados.git/sdk/go/dispatch"
+       "github.com/Sirupsen/logrus"
        . "gopkg.in/check.v1"
 )
 
@@ -41,6 +41,7 @@ func (s *TestSuite) SetUpSuite(c *C) {
        initialArgs = os.Args
        arvadostest.StartAPI()
        runningCmds = make(map[string]*exec.Cmd)
+       logrus.SetFormatter(&logrus.TextFormatter{DisableColors: true})
 }
 
 func (s *TestSuite) TearDownSuite(c *C) {
@@ -133,7 +134,7 @@ func (s *MockArvadosServerSuite) Test_ContainerStillInRunningAfterRun(c *C) {
                arvadostest.StubResponse{200, string(`{"uuid":"zzzzz-dz642-xxxxxxxxxxxxxx2", "state":"Running", "priority":1, "locked_by_uuid": "` + arvadostest.Dispatch1AuthUUID + `"}`)}
 
        testWithServerStub(c, apiStubResponses, "echo",
-               `After echo process termination, container state for Running is "zzzzz-dz642-xxxxxxxxxxxxxx2".  Updating it to "Cancelled"`)
+               `after \\"echo\\" process termination, container state for zzzzz-dz642-xxxxxxxxxxxxxx2 is \\"Running\\"; updating it to \\"Cancelled\\"`)
 }
 
 func (s *MockArvadosServerSuite) Test_ErrorRunningContainer(c *C) {
@@ -144,7 +145,7 @@ func (s *MockArvadosServerSuite) Test_ErrorRunningContainer(c *C) {
        apiStubResponses["/arvados/v1/containers/zzzzz-dz642-xxxxxxxxxxxxxx3/lock"] =
                arvadostest.StubResponse{200, string(`{"uuid":"zzzzz-dz642-xxxxxxxxxxxxxx3", "state":"Locked", "priority":1}`)}
 
-       testWithServerStub(c, apiStubResponses, "nosuchcommand", "Error starting nosuchcommand for zzzzz-dz642-xxxxxxxxxxxxxx3")
+       testWithServerStub(c, apiStubResponses, "nosuchcommand", `error starting \\"nosuchcommand\\" for zzzzz-dz642-xxxxxxxxxxxxxx3`)
 }
 
 func testWithServerStub(c *C, apiStubResponses map[string]arvadostest.StubResponse, crunchCmd string, expected string) {
@@ -165,15 +166,15 @@ func testWithServerStub(c *C, apiStubResponses map[string]arvadostest.StubRespon
        }
 
        buf := bytes.NewBuffer(nil)
-       log.SetOutput(io.MultiWriter(buf, os.Stderr))
-       defer log.SetOutput(os.Stderr)
+       logrus.SetOutput(io.MultiWriter(buf, os.Stderr))
+       defer logrus.SetOutput(os.Stderr)
 
        *crunchRunCommand = crunchCmd
 
        ctx, cancel := context.WithCancel(context.Background())
        dispatcher := dispatch.Dispatcher{
                Arv:        arv,
-               PollPeriod: time.Duration(1) * time.Second,
+               PollPeriod: time.Second / 20,
                RunContainer: func(d *dispatch.Dispatcher, c arvados.Container, s <-chan arvados.Container) {
                        run(d, c, s)
                        cancel()
@@ -186,8 +187,9 @@ func testWithServerStub(c *C, apiStubResponses map[string]arvadostest.StubRespon
                return cmd.Start()
        }
 
+       re := regexp.MustCompile(`(?ms).*` + expected + `.*`)
        go func() {
-               for i := 0; i < 80 && !strings.Contains(buf.String(), expected); i++ {
+               for i := 0; i < 80 && !re.MatchString(buf.String()); i++ {
                        time.Sleep(100 * time.Millisecond)
                }
                cancel()
index 36ef264963d760f04501fc25cee6916c62ef4bf2..ce0360261dab4aa3ab424d27c29c782e268b567f 100644 (file)
@@ -23,9 +23,15 @@ import (
        "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
        "git.curoverse.com/arvados.git/sdk/go/config"
        "git.curoverse.com/arvados.git/sdk/go/dispatch"
+       "github.com/Sirupsen/logrus"
        "github.com/coreos/go-systemd/daemon"
 )
 
+type logger interface {
+       dispatch.Logger
+       Fatalf(string, ...interface{})
+}
+
 const initialNiceValue int64 = 10000
 
 var (
@@ -35,6 +41,7 @@ var (
 
 type Dispatcher struct {
        *dispatch.Dispatcher
+       logger  logrus.FieldLogger
        cluster *arvados.Cluster
        sqCheck *SqueueChecker
        slurm   Slurm
@@ -63,10 +70,17 @@ type Dispatcher struct {
 }
 
 func main() {
-       disp := &Dispatcher{}
+       logger := logrus.StandardLogger()
+       if os.Getenv("DEBUG") != "" {
+               logger.SetLevel(logrus.DebugLevel)
+       }
+       logger.Formatter = &logrus.JSONFormatter{
+               TimestampFormat: "2006-01-02T15:04:05.000000000Z07:00",
+       }
+       disp := &Dispatcher{logger: logger}
        err := disp.Run(os.Args[0], os.Args[1:])
        if err != nil {
-               log.Fatal(err)
+               logrus.Fatalf("%s", err)
        }
 }
 
@@ -104,7 +118,7 @@ func (disp *Dispatcher) configure(prog string, args []string) error {
                return nil
        }
 
-       log.Printf("crunch-dispatch-slurm %s started", version)
+       disp.logger.Printf("crunch-dispatch-slurm %s started", version)
 
        err := disp.readConfig(*configPath)
        if err != nil {
@@ -132,7 +146,7 @@ func (disp *Dispatcher) configure(prog string, args []string) error {
                os.Setenv("ARVADOS_KEEP_SERVICES", strings.Join(disp.Client.KeepServiceURIs, " "))
                os.Setenv("ARVADOS_EXTERNAL_CLIENT", "")
        } else {
-               log.Printf("warning: Client credentials missing from config, so falling back on environment variables (deprecated).")
+               disp.logger.Warnf("Client credentials missing from config, so falling back on environment variables (deprecated).")
        }
 
        if *dumpConfig {
@@ -141,7 +155,7 @@ func (disp *Dispatcher) configure(prog string, args []string) error {
 
        siteConfig, err := arvados.GetConfig(arvados.DefaultConfigFile)
        if os.IsNotExist(err) {
-               log.Printf("warning: no cluster config (%s), proceeding with no node types defined", err)
+               disp.logger.Warnf("no cluster config (%s), proceeding with no node types defined", err)
        } else if err != nil {
                return fmt.Errorf("error loading config: %s", err)
        } else if disp.cluster, err = siteConfig.GetCluster(""); err != nil {
@@ -153,20 +167,25 @@ func (disp *Dispatcher) configure(prog string, args []string) error {
 
 // setup() initializes private fields after configure().
 func (disp *Dispatcher) setup() {
+       if disp.logger == nil {
+               disp.logger = logrus.StandardLogger()
+       }
        arv, err := arvadosclient.MakeArvadosClient()
        if err != nil {
-               log.Fatalf("Error making Arvados client: %v", err)
+               disp.logger.Fatalf("Error making Arvados client: %v", err)
        }
        arv.Retries = 25
 
        disp.slurm = &slurmCLI{}
        disp.sqCheck = &SqueueChecker{
+               Logger:         disp.logger,
                Period:         time.Duration(disp.PollPeriod),
                PrioritySpread: disp.PrioritySpread,
                Slurm:          disp.slurm,
        }
        disp.Dispatcher = &dispatch.Dispatcher{
                Arv:            arv,
+               Logger:         disp.logger,
                BatchSize:      disp.BatchSize,
                RunContainer:   disp.runContainer,
                PollPeriod:     time.Duration(disp.PollPeriod),
@@ -325,7 +344,7 @@ func (disp *Dispatcher) runContainer(_ *dispatch.Dispatcher, ctr arvados.Contain
                case <-ctx.Done():
                        // Disappeared from squeue
                        if err := disp.Arv.Get("containers", ctr.UUID, nil, &ctr); err != nil {
-                               log.Printf("Error getting final container state for %s: %s", ctr.UUID, err)
+                               log.Printf("error getting final container state for %s: %s", ctr.UUID, err)
                        }
                        switch ctr.State {
                        case dispatch.Running:
index 33cad3af1f4341bd909e81502944e6cc4366d9f6..b76ece314d47806afcfb328ba12970b9171b58d5 100644 (file)
@@ -11,7 +11,6 @@ import (
        "fmt"
        "io"
        "io/ioutil"
-       "log"
        "net/http"
        "net/http/httptest"
        "os"
@@ -25,6 +24,7 @@ import (
        "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
        "git.curoverse.com/arvados.git/sdk/go/arvadostest"
        "git.curoverse.com/arvados.git/sdk/go/dispatch"
+       "github.com/Sirupsen/logrus"
        . "gopkg.in/check.v1"
 )
 
@@ -138,7 +138,11 @@ func (s *IntegrationSuite) integrationTest(c *C,
        }
 
        s.disp.slurm = &s.slurm
-       s.disp.sqCheck = &SqueueChecker{Period: 500 * time.Millisecond, Slurm: s.disp.slurm}
+       s.disp.sqCheck = &SqueueChecker{
+               Logger: logrus.StandardLogger(),
+               Period: 500 * time.Millisecond,
+               Slurm:  s.disp.slurm,
+       }
 
        err = s.disp.Dispatcher.Run(ctx)
        <-doneRun
@@ -264,8 +268,8 @@ func (s *StubbedSuite) testWithServerStub(c *C, apiStubResponses map[string]arva
        }
 
        buf := bytes.NewBuffer(nil)
-       log.SetOutput(io.MultiWriter(buf, os.Stderr))
-       defer log.SetOutput(os.Stderr)
+       logrus.SetOutput(io.MultiWriter(buf, os.Stderr))
+       defer logrus.SetOutput(os.Stderr)
 
        s.disp.CrunchRunCommand = []string{crunchCmd}
 
index 0ce4fb6732f4d81bd03966c3857e0b975aa18769..5aee7e087b2658945b2eebe1f2f309d67c351d16 100644 (file)
@@ -7,7 +7,6 @@ package main
 import (
        "bytes"
        "fmt"
-       "log"
        "sort"
        "strings"
        "sync"
@@ -27,6 +26,7 @@ type slurmJob struct {
 // Squeue implements asynchronous polling monitor of the SLURM queue using the
 // command 'squeue'.
 type SqueueChecker struct {
+       Logger         logger
        Period         time.Duration
        PrioritySpread int64
        Slurm          Slurm
@@ -121,7 +121,7 @@ func (sqc *SqueueChecker) reniceAll() {
                }
                err := sqc.Slurm.Renice(job.uuid, niceNew)
                if err != nil && niceNew > slurm15NiceLimit && strings.Contains(err.Error(), "Invalid nice value") {
-                       log.Printf("container %q clamping nice values at %d, priority order will not be correct -- see https://dev.arvados.org/projects/arvados/wiki/SLURM_integration#Limited-nice-values-SLURM-15", job.uuid, slurm15NiceLimit)
+                       sqc.Logger.Warnf("container %q clamping nice values at %d, priority order will not be correct -- see https://dev.arvados.org/projects/arvados/wiki/SLURM_integration#Limited-nice-values-SLURM-15", job.uuid, slurm15NiceLimit)
                        job.hitNiceLimit = true
                }
        }
@@ -143,7 +143,7 @@ func (sqc *SqueueChecker) check() {
        stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
        cmd.Stdout, cmd.Stderr = stdout, stderr
        if err := cmd.Run(); err != nil {
-               log.Printf("Error running %q %q: %s %q", cmd.Path, cmd.Args, err, stderr.String())
+               sqc.Logger.Warnf("Error running %q %q: %s %q", cmd.Path, cmd.Args, err, stderr.String())
                return
        }
 
@@ -156,7 +156,7 @@ func (sqc *SqueueChecker) check() {
                var uuid, state, reason string
                var n, p int64
                if _, err := fmt.Sscan(line, &uuid, &n, &p, &state, &reason); err != nil {
-                       log.Printf("warning: ignoring unparsed line in squeue output: %q", line)
+                       sqc.Logger.Warnf("ignoring unparsed line in squeue output: %q", line)
                        continue
                }
 
@@ -192,10 +192,10 @@ func (sqc *SqueueChecker) check() {
                        // "launch failed requeued held" seems to be
                        // another manifestation of this problem,
                        // resolved the same way.
-                       log.Printf("releasing held job %q (priority=%d, state=%q, reason=%q)", uuid, p, state, reason)
+                       sqc.Logger.Printf("releasing held job %q (priority=%d, state=%q, reason=%q)", uuid, p, state, reason)
                        sqc.Slurm.Release(uuid)
                } else if state != "RUNNING" && p <= 2*slurm15NiceLimit && replacing.wantPriority > 0 {
-                       log.Printf("warning: job %q has low priority %d, nice %d, state %q, reason %q", uuid, p, n, state, reason)
+                       sqc.Logger.Warnf("job %q has low priority %d, nice %d, state %q, reason %q", uuid, p, n, state, reason)
                }
        }
        sqc.lock.Lock()
index ef036dabd781edd425b29fc28f847ae18370d700..de674a1397a0fcc24a64d2c417b262e18e9554ae 100644 (file)
@@ -7,6 +7,7 @@ package main
 import (
        "time"
 
+       "github.com/Sirupsen/logrus"
        . "gopkg.in/check.v1"
 )
 
@@ -24,6 +25,7 @@ func (s *SqueueSuite) TestReleasePending(c *C) {
                queue: uuids[0] + " 10000 4294000000 PENDING Resources\n" + uuids[1] + " 10000 4294000111 PENDING Resources\n" + uuids[2] + " 10000 0 PENDING BadConstraints\n",
        }
        sqc := &SqueueChecker{
+               Logger: logrus.StandardLogger(),
                Slurm:  slurm,
                Period: time.Hour,
        }
@@ -88,6 +90,7 @@ func (s *SqueueSuite) TestReniceAll(c *C) {
                        queue: test.squeue,
                }
                sqc := &SqueueChecker{
+                       Logger:         logrus.StandardLogger(),
                        Slurm:          slurm,
                        PrioritySpread: test.spread,
                        Period:         time.Hour,
@@ -112,6 +115,7 @@ func (s *SqueueSuite) TestReniceInvalidNiceValue(c *C) {
                rejectNice10K: true,
        }
        sqc := &SqueueChecker{
+               Logger:         logrus.StandardLogger(),
                Slurm:          slurm,
                PrioritySpread: 1,
                Period:         time.Hour,
@@ -155,6 +159,7 @@ func (s *SqueueSuite) TestSetPriorityBeforeQueued(c *C) {
 
        slurm := &slurmFake{}
        sqc := &SqueueChecker{
+               Logger: logrus.StandardLogger(),
                Slurm:  slurm,
                Period: time.Hour,
        }
index 769771e7beb52cafd5fa11033921973de501e54c..2d58012fa805b506215d3ce5f1ee4e6699efb3b1 100644 (file)
@@ -925,7 +925,7 @@ class ProjectDirectory(Directory):
         with llfuse.lock_released:
             if not self._current_user:
                 self._current_user = self.api.users().current().execute(num_retries=self.num_retries)
-            return self._current_user["uuid"] in self.project_object["writable_by"]
+            return self._current_user["uuid"] in self.project_object.get("writable_by", [])
 
     def persisted(self):
         return True
@@ -1049,35 +1049,55 @@ class SharedDirectory(Directory):
                 if not self.stale():
                     return
 
-                all_projects = arvados.util.list_all(
-                    self.api.groups().list, self.num_retries,
-                    filters=[['group_class','=','project']],
-                    select=["uuid", "owner_uuid"])
-                objects = {}
-                for ob in all_projects:
-                    objects[ob['uuid']] = ob
-
+                contents = {}
                 roots = []
                 root_owners = set()
-                current_uuid = self.current_user['uuid']
-                for ob in all_projects:
-                    if ob['owner_uuid'] != current_uuid and ob['owner_uuid'] not in objects:
-                        roots.append(ob['uuid'])
-                        root_owners.add(ob['owner_uuid'])
-
-                lusers = arvados.util.list_all(
-                    self.api.users().list, self.num_retries,
-                    filters=[['uuid','in', list(root_owners)]])
-                lgroups = arvados.util.list_all(
-                    self.api.groups().list, self.num_retries,
-                    filters=[['uuid','in', list(root_owners)+roots]])
-
-                for l in lusers:
-                    objects[l["uuid"]] = l
-                for l in lgroups:
-                    objects[l["uuid"]] = l
+                objects = {}
+
+                methods = self.api._rootDesc.get('resources')["groups"]['methods']
+                if 'httpMethod' in methods.get('shared', {}):
+                    page = []
+                    while True:
+                        resp = self.api.groups().shared(filters=[['group_class', '=', 'project']]+page,
+                                                        order="uuid",
+                                                        limit=10000,
+                                                        count="none",
+                                                        include="owner_uuid").execute()
+                        if not resp["items"]:
+                            break
+                        page = [["uuid", ">", resp["items"][len(resp["items"])-1]["uuid"]]]
+                        for r in resp["items"]:
+                            objects[r["uuid"]] = r
+                            roots.append(r["uuid"])
+                        for r in resp["included"]:
+                            objects[r["uuid"]] = r
+                            root_owners.add(r["uuid"])
+                else:
+                    all_projects = arvados.util.list_all(
+                        self.api.groups().list, self.num_retries,
+                        filters=[['group_class','=','project']],
+                        select=["uuid", "owner_uuid"])
+                    for ob in all_projects:
+                        objects[ob['uuid']] = ob
+
+                    current_uuid = self.current_user['uuid']
+                    for ob in all_projects:
+                        if ob['owner_uuid'] != current_uuid and ob['owner_uuid'] not in objects:
+                            roots.append(ob['uuid'])
+                            root_owners.add(ob['owner_uuid'])
+
+                    lusers = arvados.util.list_all(
+                        self.api.users().list, self.num_retries,
+                        filters=[['uuid','in', list(root_owners)]])
+                    lgroups = arvados.util.list_all(
+                        self.api.groups().list, self.num_retries,
+                        filters=[['uuid','in', list(root_owners)+roots]])
+
+                    for l in lusers:
+                        objects[l["uuid"]] = l
+                    for l in lgroups:
+                        objects[l["uuid"]] = l
 
-                contents = {}
                 for r in root_owners:
                     if r in objects:
                         obr = objects[r]
index bb77e5859449f5e7e4783d76d02120c359d51085..912398fa64db5d8b18605178f14a77884e234f1d 100644 (file)
@@ -135,6 +135,11 @@ func (uos *updateOnSuccess) WriteHeader(code int) {
 }
 
 var (
+       corsAllowHeadersHeader = strings.Join([]string{
+               "Authorization", "Content-Type", "Range",
+               // WebDAV request headers:
+               "Depth", "Destination", "If", "Lock-Token", "Overwrite", "Timeout",
+       }, ", ")
        writeMethod = map[string]bool{
                "COPY":   true,
                "DELETE": true,
@@ -206,7 +211,7 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
                        statusCode = http.StatusMethodNotAllowed
                        return
                }
-               w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type, Range")
+               w.Header().Set("Access-Control-Allow-Headers", corsAllowHeadersHeader)
                w.Header().Set("Access-Control-Allow-Methods", "COPY, DELETE, GET, MKCOL, MOVE, OPTIONS, POST, PROPFIND, PUT, RMCOL")
                w.Header().Set("Access-Control-Allow-Origin", "*")
                w.Header().Set("Access-Control-Max-Age", "86400")
index 68ed062160401e59bb79479c71fbfde21a2495e1..bced67ed208012dfafa69fdd7a3e6dd2395b641a 100644 (file)
@@ -48,7 +48,7 @@ func (s *UnitSuite) TestCORSPreflight(c *check.C) {
        c.Check(resp.Body.String(), check.Equals, "")
        c.Check(resp.Header().Get("Access-Control-Allow-Origin"), check.Equals, "*")
        c.Check(resp.Header().Get("Access-Control-Allow-Methods"), check.Equals, "COPY, DELETE, GET, MKCOL, MOVE, OPTIONS, POST, PROPFIND, PUT, RMCOL")
-       c.Check(resp.Header().Get("Access-Control-Allow-Headers"), check.Equals, "Authorization, Content-Type, Range")
+       c.Check(resp.Header().Get("Access-Control-Allow-Headers"), check.Equals, "Authorization, Content-Type, Range, Depth, Destination, If, Lock-Token, Overwrite, Timeout")
 
        // Check preflight for a disallowed request
        resp = httptest.NewRecorder()