12308: Merge branch 'master'
authorTom Clegg <tom@tomclegg.ca>
Fri, 14 Feb 2020 18:42:48 +0000 (13:42 -0500)
committerTom Clegg <tom@tomclegg.ca>
Fri, 14 Feb 2020 18:42:48 +0000 (13:42 -0500)
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom@tomclegg.ca>

37 files changed:
apps/workbench/app/views/users/_virtual_machines.html.erb
apps/workbench/app/views/virtual_machines/_show_help.html.erb
build/package-build-dockerfiles/centos7/Dockerfile
build/package-build-dockerfiles/debian10/Dockerfile
build/package-build-dockerfiles/debian9/Dockerfile
build/package-build-dockerfiles/ubuntu1604/Dockerfile
build/package-build-dockerfiles/ubuntu1804/Dockerfile
build/run-library.sh
doc/_includes/_navbar_top.liquid
doc/admin/metrics.html.textile.liquid
doc/admin/upgrading.html.textile.liquid
doc/css/code.css
doc/install/index.html.textile.liquid
doc/sdk/index.html.textile.liquid
lib/config/config.default.yml
lib/config/export.go
lib/config/generated_config.go
lib/controller/federation/conn.go
lib/controller/handler_test.go
sdk/cwl/arvados_cwl/executor.py
sdk/cwl/arvados_cwl/runner.py
sdk/cwl/setup.py
sdk/cwl/tests/test_submit.py
sdk/cwl/tests/tool/blub.txt.cat [new file with mode: 0644]
sdk/cwl/tests/tool/tool_with_sf.cwl [new file with mode: 0644]
sdk/cwl/tests/tool/tool_with_sf.yml [new file with mode: 0644]
sdk/go/arvados/api.go
sdk/go/arvados/collection.go
sdk/go/arvados/config.go
sdk/go/arvadostest/fixtures.go
sdk/python/setup.py
services/api/app/models/collection.rb
services/api/app/models/container_request.rb
services/api/test/fixtures/users.yml
services/api/test/unit/container_request_test.rb
services/keep-balance/main.go
services/keep-balance/main_test.go [new file with mode: 0644]

index 026f016f8cd7186ebcc6eb39e123ae0dcbb5e9dc..38458593fada290cdc8d7b82a633ec0b60467d09 100644 (file)
@@ -85,7 +85,7 @@ SPDX-License-Identifier: AGPL-3.0 %>
             <td style="word-break:break-all;">
               <% if @my_vm_logins[vm[:uuid]] %>
                 <% @my_vm_logins[vm[:uuid]].each do |login| %>
-                  <code>ssh&nbsp;<%= login %>@<%= vm[:hostname] %>.<%= current_uuid_prefix || 'xyzzy' %></code>
+                  <code>ssh&nbsp;<%= login %>@<%= vm[:hostname] %></code>
                 <% end %>
               <% end %>
             </td>
@@ -105,9 +105,7 @@ SPDX-License-Identifier: AGPL-3.0 %>
   <% end %>
 </div>
 </div>
-  <p>In order to access virtual machines using SSH, <%= link_to ssh_keys_user_path(current_user) do%> add an SSH key to your account<%end%> and add a section like this to your SSH configuration file ( <i>~/.ssh/config</i>):</p>
-    <pre>Host *.<%= current_uuid_prefix || 'xyzzy' %>
-      TCPKeepAlive yes
-      ServerAliveInterval 60
-      ProxyCommand ssh -p2222 turnout@switchyard.<%= current_api_host || 'xyzzy.arvadosapi.com' %> -x -a $SSH_PROXY_FLAGS %h
-    </pre>
+
+<p>In order to access virtual machines using SSH, <%= link_to ssh_keys_user_path(current_user) do%>add an SSH key to your account<%end%>.</p>
+
+<%= raw(Rails.configuration.Workbench.SSHHelpPageHTML) %>
index d989663fe93f49013a384e2ed4e69d7f6fc64830..daf7ba50f4adc5a9f9287cab9f969212874157f3 100644 (file)
@@ -2,29 +2,4 @@
 
 SPDX-License-Identifier: AGPL-3.0 %>
 
-<p>
-Sample <code>~/.ssh/config</code> section:
-</p>
-
-<pre>
-Host *.arvados
-  ProxyCommand ssh -p2222 turnout@switchyard.<%= current_api_host.sub(/:.*$/,"") || 'xyzzy.arvadosapi.com' %> -x -a $SSH_PROXY_FLAGS %h
-<% if @objects.first.andand.current_user_logins.andand.first %>
-  User <%= @objects.first.current_user_logins.andand.first %>
-<% end %>
-</pre>
-
-<p>
-Sample login command:
-</p>
-
-<pre>
-ssh <%= @objects.first.andand.hostname.andand.sub('.'+current_api_host.sub(/:.*$/,""),'') or 'vm-hostname' %>.arvados
-</pre>
-
-<p>
-  See also:
-  <%= link_to raw('Arvados Docs &rarr; User Guide &rarr; SSH access'),
-  "#{Rails.configuration.Workbench.ArvadosDocsite}/user/getting_started/ssh-access-unix.html",
-  target: "_blank"%>.
-</p>
+<%= raw(Rails.configuration.Workbench.SSHHelpPageHTML) %>
index 3a61e64cf4fafeedc64da0826848ac4a09402ce6..9c2660387abfcd40e9ca5372fc8c7afeee0f9783 100644 (file)
@@ -50,7 +50,7 @@ RUN rpm -ivh epel-release-latest-7.noarch.rpm
 RUN git clone --depth 1 git://git.arvados.org/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
 
 # The version of setuptools that comes with CentOS is way too old
-RUN pip install --upgrade setuptools
+RUN pip install --upgrade 'setuptools<45'
 
 ENV WORKSPACE /arvados
 CMD ["scl", "enable", "rh-python36", "/usr/local/rvm/bin/rvm-exec default bash /jenkins/run-build-packages.sh --target centos7"]
index 20527dd45cb6eac5d1461142283a258be8d83c9b..ff86262d38597aa99af8714ec5b94f3d11dfdbc6 100644 (file)
@@ -12,7 +12,7 @@ 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 python3-pip libcurl4-gnutls-dev curl git procps libattr1-dev libfuse-dev libgnutls28-dev libpq-dev python-pip unzip python3-venv python3-dev
 
 # Install virtualenv
-RUN /usr/bin/pip install virtualenv
+RUN /usr/bin/pip install 'virtualenv<20'
 
 # Install RVM
 ADD generated/mpapis.asc /tmp/
index 2aaccf6aa7c68a605709d29891e4db0273bc15ef..257523a72feb58e8d28e78688ccd7d04859e777a 100644 (file)
@@ -12,7 +12,7 @@ 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 python3-pip libcurl4-gnutls-dev curl git procps libattr1-dev libfuse-dev libgnutls28-dev libpq-dev python-pip unzip python3-venv python3-dev
 
 # Install virtualenv
-RUN /usr/bin/pip install virtualenv
+RUN /usr/bin/pip install 'virtualenv<20'
 
 # Install RVM
 ADD generated/mpapis.asc /tmp/
index 23fcca07dfbf925e6a878e1066fe2d01a988d038..e046ae1690f81525836df5d4469a511c05f92583 100644 (file)
@@ -11,7 +11,7 @@ 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 python3-pip libcurl4-gnutls-dev libgnutls-dev curl git libattr1-dev libfuse-dev libpq-dev python-pip unzip tzdata python3-venv python3-dev
 
 # Install virtualenv
-RUN /usr/bin/pip install virtualenv
+RUN /usr/bin/pip install 'virtualenv<20'
 
 # Install RVM
 ADD generated/mpapis.asc /tmp/
index 06c21ca3fa41664acde3c9724e21a098fc34d7b3..c652fe1c0826e9c4d5124c2fddebe07e1946ebef 100644 (file)
@@ -11,7 +11,7 @@ ENV DEBIAN_FRONTEND noninteractive
 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 python3-venv python3-dev
 
 # Install virtualenv
-RUN /usr/bin/pip install virtualenv
+RUN /usr/bin/pip install 'virtualenv<20'
 
 # Install RVM
 ADD generated/mpapis.asc /tmp/
index 5b1dde46a9f3fec05cee114641564a2fdbacc486..ac5dc718be1c6e36e86e743bf0805ab11c891da7 100755 (executable)
@@ -475,9 +475,9 @@ fpm_build_virtualenv () {
   rm -rf dist/*
 
   # Get the latest setuptools
-  if ! $pip install $DASHQ_UNLESS_DEBUG $CACHE_FLAG -U setuptools; then
+  if ! $pip install $DASHQ_UNLESS_DEBUG $CACHE_FLAG -U 'setuptools<45'; then
     echo "Error, unable to upgrade setuptools with"
-    echo "  $pip install $DASHQ_UNLESS_DEBUG $CACHE_FLAG -U setuptools"
+    echo "  $pip install $DASHQ_UNLESS_DEBUG $CACHE_FLAG -U 'setuptools<45'"
     exit 1
   fi
   # filter a useless warning (when building the cwltest package) from the stderr output
@@ -532,9 +532,9 @@ fpm_build_virtualenv () {
   fi
   echo "pip version:        `build/usr/share/$python/dist/$PYTHON_PKG/bin/$pip --version`"
 
-  if ! build/usr/share/$python/dist/$PYTHON_PKG/bin/$pip install $DASHQ_UNLESS_DEBUG $CACHE_FLAG -U setuptools; then
+  if ! build/usr/share/$python/dist/$PYTHON_PKG/bin/$pip install $DASHQ_UNLESS_DEBUG $CACHE_FLAG -U 'setuptools<45'; then
     echo "Error, unable to upgrade setuptools with"
-    echo "  build/usr/share/$python/dist/$PYTHON_PKG/bin/$pip install $DASHQ_UNLESS_DEBUG $CACHE_FLAG -U setuptools"
+    echo "  build/usr/share/$python/dist/$PYTHON_PKG/bin/$pip install $DASHQ_UNLESS_DEBUG $CACHE_FLAG -U 'setuptools<45'"
     exit 1
   fi
   echo "setuptools version: `build/usr/share/$python/dist/$PYTHON_PKG/bin/$python -c 'import setuptools; print(setuptools.__version__)'`"
index c69a2efbec1eb5f9a76b7fc8aaa97fc411a93553..38c8e0a1db9573fa4726c23148bdc9a83af19a6c 100644 (file)
@@ -57,10 +57,10 @@ SPDX-License-Identifier: CC-BY-SA-3.0
 
    <script>
      function dismissAnnotateNotify() {
-        window.sessionStorage.setItem("dismiss-annotate-notify", "true");
+        window.localStorage.setItem("dismiss-annotate-notify", "true");
          $('#annotate-notify').attr('style', "display: none;");
      }
-     if (window.sessionStorage.getItem("dismiss-annotate-notify") === "true") {
+     if (window.localStorage.getItem("dismiss-annotate-notify") === "true") {
         dismissAnnotateNotify();
      } else {
          $('#annotate-notify').attr('style', "display: inline-block;");
index 893eac1c8325c2033c7d36b398541f1cccedfb0f..9616d4add43a44105d78fbf5ff6f4ae9b8e1c3cd 100644 (file)
@@ -10,172 +10,48 @@ Copyright (C) The Arvados Authors. All rights reserved.
 SPDX-License-Identifier: CC-BY-SA-3.0
 {% endcomment %}
 
-Some Arvados services publish Prometheus/OpenMetrics-compatible metrics at @/metrics@, and some provide additional runtime status at @/status.json@.  Metrics can help you understand how components perform under load, find performance bottlenecks, and detect and diagnose problems.
+Some Arvados services publish Prometheus/OpenMetrics-compatible metrics at @/metrics@. Metrics can help you understand how components perform under load, find performance bottlenecks, and detect and diagnose problems.
 
 To access metrics endpoints, services must be configured with a "management token":management-token.html. When accessing a metrics endpoint, prefix the management token with @"Bearer "@ and supply it in the @Authorization@ request header.
 
-<pre>curl -sfH "Authorization: Bearer your_management_token_goes_here" "https://0.0.0.0:25107/status.json"
+<pre>curl -sfH "Authorization: Bearer your_management_token_goes_here" "https://0.0.0.0:25107/metrics"
 </pre>
 
-h2. Keep-web
+The plain text export format includes "help" messages with a description of each reported metric.
 
-Keep-web exports metrics at @/metrics@ -- e.g., @https://collections.zzzzz.arvadosapi.com/metrics@.
+When configuring Prometheus, use a @bearer_token@ or @bearer_token_file@ option to authenticate requests.
 
-table(table table-bordered table-condensed).
-|_. Name|_. Type|_. Description|
-|request_duration_seconds|summary|elapsed time between receiving a request and sending the last byte of the response body (segmented by HTTP request method and response status code)|
-|time_to_status_seconds|summary|elapsed time between receiving a request and sending the HTTP response status code (segmented by HTTP request method and response status code)|
-
-Metrics in the @arvados_keepweb_collectioncache@ namespace report keep-web's internal cache of Arvados collection metadata.
-
-table(table table-bordered table-condensed).
-|_. Name|_. Type|_. Description|
-|arvados_keepweb_collectioncache_requests|counter|cache lookups|
-|arvados_keepweb_collectioncache_api_calls|counter|outgoing API calls|
-|arvados_keepweb_collectioncache_permission_hits|counter|collection-to-permission cache hits|
-|arvados_keepweb_collectioncache_pdh_hits|counter|UUID-to-PDH cache hits|
-|arvados_keepweb_collectioncache_hits|counter|PDH-to-manifest cache hits|
-|arvados_keepweb_collectioncache_cached_manifests|gauge|number of collections in the cache|
-|arvados_keepweb_collectioncache_cached_manifest_bytes|gauge|memory consumed by cached collection manifests|
-
-h2. Keepstore
-
-Keepstore exports metrics at @/status.json@ -- e.g., @http://keep0.zzzzz.arvadosapi.com:25107/status.json@.
-
-h3. Root
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|
-|Volumes|         array of "volumeStatusEnt":#volumeStatusEnt ||
-|BufferPool|      "PoolStatus":#PoolStatus ||
-|PullQueue|       "WorkQueueStatus":#WorkQueueStatus ||
-|TrashQueue|      "WorkQueueStatus":#WorkQueueStatus ||
-|RequestsCurrent| int ||
-|RequestsMax|     int ||
-|Version|         string ||
-
-h3(#volumeStatusEnt). volumeStatusEnt
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|
-|Label|         string||
-|Status|        "VolumeStatus":#VolumeStatus ||
-|VolumeStats|   "ioStats":#ioStats ||
-
-h3(#VolumeStatus). VolumeStatus
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|
-|MountPoint| string||
-|DeviceNum|  uint64||
-|BytesFree|  uint64||
-|BytesUsed|  uint64||
-
-h3(#ioStats). ioStats
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|
-|Errors|     uint64||
-|Ops|        uint64||
-|CompareOps| uint64||
-|GetOps|     uint64||
-|PutOps|     uint64||
-|TouchOps|   uint64||
-|InBytes|    uint64||
-|OutBytes|   uint64||
-
-h3(#PoolStatus). PoolStatus
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|
-|BytesAllocatedCumulative|      uint64||
-|BuffersMax|   int||
-|BuffersInUse| int||
-
-h3(#WorkQueueStatus). WorkQueueStatus
-
-table(table table-bordered table-condensed).
-|_. Attribute|_. Type|_. Description|
-|InProgress| int||
-|Queued|     int||
-
-h3. Example response
-
-<pre>
-{
-  "Volumes": [
-    {
-      "Label": "[UnixVolume /var/lib/arvados/keep0]",
-      "Status": {
-        "MountPoint": "/var/lib/arvados/keep0",
-        "DeviceNum": 65029,
-        "BytesFree": 222532972544,
-        "BytesUsed": 435456679936
-      },
-      "InternalStats": {
-        "Errors": 0,
-        "InBytes": 1111,
-        "OutBytes": 0,
-        "OpenOps": 1,
-        "StatOps": 4,
-        "FlockOps": 0,
-        "UtimesOps": 0,
-        "CreateOps": 0,
-        "RenameOps": 0,
-        "UnlinkOps": 0,
-        "ReaddirOps": 0
-      }
-    }
-  ],
-  "BufferPool": {
-    "BytesAllocatedCumulative": 67108864,
-    "BuffersMax": 20,
-    "BuffersInUse": 0
-  },
-  "PullQueue": {
-    "InProgress": 0,
-    "Queued": 0
-  },
-  "TrashQueue": {
-    "InProgress": 0,
-    "Queued": 0
-  },
-  "RequestsCurrent": 1,
-  "RequestsMax": 40,
-  "Version": "dev"
-}
+<pre>scrape_configs:
+  - job_name: keepstore
+    bearer_token: your_management_token_goes_here
+    static_configs:
+    - targets:
+      - "keep0.ClusterID.example.com:25107"
 </pre>
 
-h2. Keep-balance
-
-Keep-balance exports metrics at @/metrics@ -- e.g., @http://keep.zzzzz.arvadosapi.com:9005/metrics@.
-
-table(table table-bordered table-condensed).
-|_. Name|_. Type|_. Description|
-|arvados_keep_total_{replicas,blocks,bytes}|gauge|stored data (stored in backend volumes, whether referenced or not)|
-|arvados_keep_garbage_{replicas,blocks,bytes}|gauge|garbage data (unreferenced, and old enough to trash)|
-|arvados_keep_transient_{replicas,blocks,bytes}|gauge|transient data (unreferenced, but too new to trash)|
-|arvados_keep_overreplicated_{replicas,blocks,bytes}|gauge|overreplicated data (more replicas exist than are needed)|
-|arvados_keep_underreplicated_{replicas,blocks,bytes}|gauge|underreplicated data (fewer replicas exist than are needed)|
-|arvados_keep_lost_{replicas,blocks,bytes}|gauge|lost data (referenced by collections, but not found on any backend volume)|
-|arvados_keep_dedup_block_ratio|gauge|deduplication ratio (block references in collections &divide; distinct blocks referenced)|
-|arvados_keep_dedup_byte_ratio|gauge|deduplication ratio (block references in collections &divide; distinct blocks referenced, weighted by block size)|
-|arvados_keepbalance_get_state_seconds|summary|time to get all collections and keepstore volume indexes for one iteration|
-|arvados_keepbalance_changeset_compute_seconds|summary|time to compute changesets for one iteration|
-|arvados_keepbalance_send_pull_list_seconds|summary|time to send pull lists to all keepstore servers for one iteration|
-|arvados_keepbalance_send_trash_list_seconds|summary|time to send trash lists to all keepstore servers for one iteration|
-|arvados_keepbalance_sweep_seconds|summary|time to complete one iteration|
-
-Each @arvados_keep_@ storage state statistic above is presented as a set of three metrics:
-
-table(table table-bordered table-condensed).
-|*_blocks|distinct block hashes|
-|*_bytes|bytes stored on backend volumes|
-|*_replicas|objects/files stored on backend volumes|
+table(table table-bordered table-condensed table-hover).
+|_. Component|_. Metrics endpoint|
+|arvados-api-server||
+|arvados-controller|✓|
+|arvados-dispatch-cloud|✓|
+|arvados-git-httpd||
+|arvados-node-manager||
+|arvados-ws||
+|composer||
+|keepproxy||
+|keepstore|✓|
+|keep-balance|✓|
+|keep-web|✓|
+|sso-provider||
+|workbench1||
+|workbench2||
 
 h2. Node manager
 
-The node manager status end point provides a snapshot of internal status at the time of the most recent wishlist update.
+The node manager does not export prometheus-style metrics, but its @/status.json@ endpoint provides a snapshot of internal status at the time of the most recent wishlist update.
+
+<pre>curl -sfH "Authorization: Bearer your_management_token_goes_here" "http://0.0.0.0:8989/status.json"
+</pre>
 
 table(table table-bordered table-condensed).
 |_. Attribute|_. Type|_. Description|
index 2effec39040a0e119845511a66792728ad6ee122..23d71204385af2e8d7a7a9ae17514b7223bea9c8 100644 (file)
@@ -30,44 +30,37 @@ Note to developers: Add new items at the top. Include the date, issue number, co
 TODO: extract this information based on git commit messages and generate changelogs / release notes automatically.
 {% endcomment %}
 
-h3(#master). development master (as of 2020-01-16)
+<notextile>
+<div class="releasenotes">
+</notextile>
 
-h4. New property vocabulary format for Workbench2
+h2(#master). development master (as of 2020-02-07)
 
-(feature "#14151":https://dev.arvados.org/issues/14151) Workbench2 supports a new vocabulary format and it isn't compatible with the previous one, please read the "workbench2 vocabulary format admin page":{{site.baseurl}}/admin/workbench2-vocabulary.html for more information.
+"Upgrading from 2.0.0":#v2_0_0
 
-h4. "/" prohibited in collection and project names
-
-(issue "#15836":https://dev.arvados.org/issues/15836) By default, Arvados now rejects new names containing the @/@ character when creating or renaming collections and projects. Previously, these names were permitted, but the resulting objects were invisible in the WebDAV "home" tree. If you prefer, you can restore the previous behavior, and optionally configure a substitution string to make the affected objects accessible via WebDAV. See @ForwardSlashNameSubstitution@ in the "configuration reference":config.html.
+None in current development master.
 
-h4. Migrating to centralized config.yml
+h2(#v2_0_0). v2.0.0 (2020-02-07)
 
-See "Migrating Configuration":config-migration.html for notes on migrating legacy per-component configuration files to the new centralized @/etc/arvados/config.yml@.  To ensure a smooth transition, the per-component config files continue to be read, and take precedence over the centralized configuration.
+"Upgrading from 1.4":#v1_4_1
 
-h4. Cloud installations only: node manager replaced by arvados-dispatch-cloud
+Arvados 2.0 is a major upgrade, with many changes.  Please read these upgrade notes carefully before you begin.
 
-Node manager is deprecated and replaced by @arvados-dispatch-cloud@.  No automated config migration is available.  Follow the instructions to "install the cloud dispatcher":../install/install-dispatch-cloud.html
+h3. Migrating to centralized config.yml
 
-*Only one dispatch process should be running at a time.* If you are migrating a system that currently runs Node manager and @crunch-dispatch-slurm@, it is safest to remove the @crunch-dispatch-slurm@ service entirely before installing @arvados-dispatch-cloud@.
+See "Migrating Configuration":config-migration.html for notes on migrating legacy per-component configuration files to the new centralized @/etc/arvados/config.yml@.
 
-<notextile>
-<pre><code>~$ <span class="userinput">sudo systemctl --now disable crunch-dispatch-slurm</span>
-~$ <span class="userinput">sudo apt-get remove crunch-dispatch-slurm</span>
-</code></pre>
-</notextile>
-
-h4. Delete "keep_services" records
-
-After all keepproxy and keepstore configurations have been migrated to the centralized configuration file (see below), all keep_services records you added manually during installation should be removed. System logs from keepstore and keepproxy at startup, as well as the output of @arvados-server config-check@, will remind you to do this.
-
-<notextile><pre><code>$ export ARVADOS_API_HOST=...
-$ export ARVADOS_API_TOKEN=...
-$ arv --format=uuid keep_service list | xargs -n1 arv keep_service delete --uuid
-</code></pre></notextile>
+To ensure a smooth transition, the per-component config files continue to be read, and take precedence over the centralized configuration.  Your cluster should continue to function after upgrade but before doing the full configuration migration.  However, several services (keepstore, keep-web, keepproxy) require a minimal `/etc/arvados/config.yml` to start:
 
-Once these old records are removed, @arv keep_service list@ will instead return the services listed under Services/Keepstore/InternalURLs and Services/Keepproxy/ExternalURL in your centralized configuration file.
+<pre>
+Clusters:
+  zzzzz:
+    Services:
+      Controller:
+        ExternalURL: "https://zzzzz.example.com"
+</pre>
 
-h4. Keep-balance configuration migration
+h3. Keep-balance configuration migration
 
 (feature "#14714":https://dev.arvados.org/issues/14714 ) The keep-balance service can now be configured using the centralized configuration file at @/etc/arvados/config.yml@. The following command line and configuration options have changed.
 
@@ -77,11 +70,11 @@ You can no longer specify individual keep services to balance via the @config.Ke
 
 Please see the "config migration guide":{{site.baseurl}}/admin/config-migration.html and "keep-balance install guide":{{site.baseurl}}/install/install-keep-balance.html for more details.
 
-h4. Arv-git-httpd configuration migration
+h3. Arv-git-httpd configuration migration
 
 (feature "#14712":https://dev.arvados.org/issues/14712 ) The arv-git-httpd package can now be configured using the centralized configuration file at @/etc/arvados/config.yml@. Configuration via individual command line arguments is no longer available. Please see "arv-git-httpd's config migration guide":{{site.baseurl}}/admin/config-migration.html#arv-git-httpd for more details.
 
-h4. Keepstore and keep-web configuration migration
+h3. Keepstore and keep-web configuration migration
 
 keepstore and keep-web no longer support configuration via (previously deprecated) command line configuration flags and environment variables.
 
@@ -89,23 +82,22 @@ keep-web now supports the legacy @keep-web.yml@ config format (used by Arvados 1
 
 keepstore now supports the legacy @keepstore.yml@ config format (used by Arvados 1.4) and the new cluster config file format. Please check the "keepstore config migration notes":{{site.baseurl}}/admin/config-migration.html#keepstore and "keepstore install guide":{{site.baseurl}}/install/install-keepstore.html for more details.
 
-h4. Keepproxy configuration migration
+h3. Keepproxy configuration migration
 
 (feature "#14715":https://dev.arvados.org/issues/14715 ) Keepproxy can now be configured using the centralized config at @/etc/arvados/config.yml@. Configuration via individual command line arguments is no longer available and the @DisableGet@, @DisablePut@, and @PIDFile@ configuration options are no longer supported. If you are still using the legacy config and @DisableGet@ or @DisablePut@ are set to true or @PIDFile@ has a value, keepproxy will produce an error and fail to start. Please see "keepproxy's config migration guide":{{site.baseurl}}/admin/config-migration.html#keepproxy for more details.
 
-h4. Jobs API is read-only
-
-(task "#15133":https://dev.arvados.org/issues/15133 ) The legacy 'jobs' API is now read-only.  It has been superceded since Arvados 1.1 by containers / container_requests (aka crunch v2).  Arvados installations since the end of 2017 (v1.1.0) have probably only used containers, and are unaffected by this change.
-
-So that older Arvados sites don't lose access to legacy records, the API has been converted to read-only.  Creating and updating jobs (and related types job_task, pipeline_template and pipeline_instance) is disabled and much of the business logic related has been removed, along with various other code specific to the jobs API.  Specifically, the following programs associated with the jobs API have been removed: @crunch-dispatch.rb@, @crunch-job@, @crunchrunner@, @arv-run-pipeline-instance@, @arv-run@.
+h3. Delete "keep_services" records
 
-h4. No longer stripping ':' from strings in serialized database columns
+After all keepproxy and keepstore configurations have been migrated to the centralized configuration file, all keep_services records you added manually during installation should be removed. System logs from keepstore and keepproxy at startup, as well as the output of @arvados-server config-check@, will remind you to do this.
 
-(bug "#15311":https://dev.arvados.org/issues/15311 ) Strings read from serialized columns in the database with a leading ':' would have the ':' stripped after loading the record.  This behavior existed due to legacy serialization behavior which stored Ruby symbols with a leading ':'.  Unfortunately this corrupted fields where the leading ":" was intentional.  This behavior has been removed.
+<notextile><pre><code>$ export ARVADOS_API_HOST=...
+$ export ARVADOS_API_TOKEN=...
+$ arv --format=uuid keep_service list | xargs -n1 arv keep_service delete --uuid
+</code></pre></notextile>
 
-You can test if any records in your database are affected by going to the API server directory and running @bundle exec rake symbols:check@.  This will report which records contain fields with a leading ':' that would previously have been stripped.  If there are records to be updated, you can update the database using @bundle exec rake symbols:stringify@.
+Once these old records are removed, @arv keep_service list@ will instead return the services listed under Services/Keepstore/InternalURLs and Services/Keepproxy/ExternalURL in your centralized configuration file.
 
-h4. Enabling Postgres trigram indexes
+h3. Enabling Postgres trigram indexes
 
 Feature "#15106":https://dev.arvados.org/issues/15106 improves the speed and functionality of full text search by introducing trigram indexes on text searchable database columns via a migration. Prior to updating, you must first install the postgresql-contrib package on your system and subsequently run the <code class="userprint">CREATE EXTENSION pg_trgm</code> SQL command on the arvados_production database as a postgres superuser.
 
@@ -134,22 +126,67 @@ The "postgres-contrib package":https://www.postgresql.org/docs/10/contrib.html h
 
 Subsequently, the <code class="userinput">psql -d 'arvados_production' -c '\dx'</code> command will display the installed extensions for the arvados_production database. This list should now contain @pg_trgm@.
 
+h3. New Workbench 2
+
+Workbench 2 is now ready for regular use.  Follow the instructions to "install workbench 2":../install/install-workbench2-app.html
 
-h3(#v1_4_1). v1.4.1 (2019-09-20)
+h3. New property vocabulary format for Workbench2
 
-h4. Centos7 Python 3 dependency upgraded to rh-python36
+(feature "#14151":https://dev.arvados.org/issues/14151) Workbench2 supports a new vocabulary format and it isn't compatible with the previous one, please read the "workbench2 vocabulary format admin page":{{site.baseurl}}/admin/workbench2-vocabulary.html for more information.
+
+h3. Cloud installations only: node manager replaced by arvados-dispatch-cloud
+
+Node manager is deprecated and replaced by @arvados-dispatch-cloud@.  No automated config migration is available.  Follow the instructions to "install the cloud dispatcher":../install/install-dispatch-cloud.html
+
+*Only one dispatch process should be running at a time.* If you are migrating a system that currently runs Node manager and @crunch-dispatch-slurm@, it is safest to remove the @crunch-dispatch-slurm@ service entirely before installing @arvados-dispatch-cloud@.
+
+<notextile>
+<pre><code>~$ <span class="userinput">sudo systemctl --now disable crunch-dispatch-slurm</span>
+~$ <span class="userinput">sudo apt-get remove crunch-dispatch-slurm</span>
+</code></pre>
+</notextile>
+
+h3. Jobs API is read-only
+
+(task "#15133":https://dev.arvados.org/issues/15133 ) The legacy 'jobs' API is now read-only.  It has been superceded since Arvados 1.1 by containers / container_requests (aka crunch v2).  Arvados installations since the end of 2017 (v1.1.0) have probably only used containers, and are unaffected by this change.
+
+So that older Arvados sites don't lose access to legacy records, the API has been converted to read-only.  Creating and updating jobs (and related types job_task, pipeline_template and pipeline_instance) is disabled and much of the business logic related has been removed, along with various other code specific to the jobs API.  Specifically, the following programs associated with the jobs API have been removed: @crunch-dispatch.rb@, @crunch-job@, @crunchrunner@, @arv-run-pipeline-instance@, @arv-run@.
+
+h3. "/" prohibited in collection and project names
+
+(issue "#15836":https://dev.arvados.org/issues/15836) By default, Arvados now rejects new names containing the @/@ character when creating or renaming collections and projects. Previously, these names were permitted, but the resulting objects were invisible in the WebDAV "home" tree. If you prefer, you can restore the previous behavior, and optionally configure a substitution string to make the affected objects accessible via WebDAV. See @ForwardSlashNameSubstitution@ in the "configuration reference":config.html.
+
+h3. No longer stripping ':' from strings in serialized database columns
+
+(bug "#15311":https://dev.arvados.org/issues/15311 ) Strings read from serialized columns in the database with a leading ':' would have the ':' stripped after loading the record.  This behavior existed due to legacy serialization behavior which stored Ruby symbols with a leading ':'.  Unfortunately this corrupted fields where the leading ":" was intentional.  This behavior has been removed.
+
+You can test if any records in your database are affected by going to the API server directory and running @bundle exec rake symbols:check@.  This will report which records contain fields with a leading ':' that would previously have been stripped.  If there are records to be updated, you can update the database using @bundle exec rake symbols:stringify@.
+
+h3. Scoped tokens should use PATCH for updates
+
+The API server accepts both PUT and PATCH for updates, but they will be normalized to PATCH by arvados-controller.  Scoped tokens should be updated accordingly.
+
+
+
+h2(#v1_4_1). v1.4.1 (2019-09-20)
+
+"Upgrading from 1.4.0":#v1_4_0
+
+h3. Centos7 Python 3 dependency upgraded to rh-python36
 
 The Python 3 dependency for Centos7 Arvados packages was upgraded from rh-python35 to rh-python36.
 
-h3(#v1_4_0). v1.4.0 (2019-06-05)
+h2(#v1_4_0). v1.4.0 (2019-06-05)
 
-h4. Populating the new file_count and file_size_total columns on the collections table
+"Upgrading from 1.3.3":#v1_3_3
+
+h3. Populating the new file_count and file_size_total columns on the collections table
 
 As part of story "#14484":https://dev.arvados.org/issues/14484, two new columns were added to the collections table in a database migration. If your installation has a large collections table, this migration may take some time. We've seen it take ~5 minutes on an installation with 250k collections, but your mileage may vary.
 
 The new columns are initialized with a zero value. In order to populate them, it is necessary to run a script called <code class="userinput">populate-file-info-columns-in-collections.rb</code> from the scripts directory of the API server. This can be done out of band, ideally directly after the API server has been upgraded to v1.4.0.
 
-h4. Stricter collection manifest validation on the API server
+h3. Stricter collection manifest validation on the API server
 
 As a consequence of "#14482":https://dev.arvados.org/issues/14482, the Ruby SDK does a more rigorous collection manifest validation. Collections created after 2015-05 are unlikely to be invalid, however you may check for invalid manifests using the script below.
 
@@ -206,7 +243,7 @@ end
 
 The script will return a final report enumerating any invalid collection by UUID, with its creation date and error message so you can take the proper correction measures, if needed.
 
-h4. Python packaging change
+h3. Python packaging change
 
 As part of story "#9945":https://dev.arvados.org/issues/9945, the distribution packaging (deb/rpm) of our Python packages has changed. These packages now include a built-in virtualenv to reduce dependencies on system packages. We have also stopped packaging and publishing backports for all the Python dependencies of our packages, as they are no longer needed.
 
@@ -230,29 +267,33 @@ Or alternatively, by updating the shebang line at the start of the script to:
 </pre>
 </notextile>
 
-h4. python-arvados-cwl-runner deb/rpm package now conflicts with python-cwltool deb/rpm package
+h3. python-arvados-cwl-runner deb/rpm package now conflicts with python-cwltool deb/rpm package
 
 As part of story "#9945":https://dev.arvados.org/issues/9945, the distribution packaging (deb/rpm) of our Python packages has changed. The python-arvados-cwl-runner package now includes a version of cwltool. If present, the python-cwltool and cwltool distribution packages will need to be uninstalled before the python-arvados-cwl-runner deb or rpm package can be installed.
 
-h4. Centos7 Python 3 dependency upgraded to rh-python35
+h3. Centos7 Python 3 dependency upgraded to rh-python35
 
 As part of story "#9945":https://dev.arvados.org/issues/9945, the Python 3 dependency for Centos7 Arvados packages was upgraded from SCL python33 to rh-python35.
 
-h4. Centos7 package for libpam-arvados depends on the python-pam package, which is available from EPEL
+h3. Centos7 package for libpam-arvados depends on the python-pam package, which is available from EPEL
 
 As part of story "#9945":https://dev.arvados.org/issues/9945, it was discovered that the Centos7 package for libpam-arvados was missing a dependency on the python-pam package, which is available from the EPEL repository. The dependency has been added to the libpam-arvados package. This means that going forward, the EPEL repository will need to be enabled to install libpam-arvados on Centos7.
 
-h4. New configuration
+h3. New configuration
 
 Arvados is migrating to a centralized configuration file for all components.  During the migration, legacy configuration files will continue to be loaded.  See "Migrating Configuration":config-migration.html for details.
 
-h3(#v1_3_3). v1.3.3 (2019-05-14)
+h2(#v1_3_3). v1.3.3 (2019-05-14)
+
+"Upgrading from 1.3.0":#v1_3_0
 
 This release corrects a potential data loss issue, if you are running Arvados 1.3.0 or 1.3.1 we strongly recommended disabling @keep-balance@ until you can upgrade to 1.3.3 or 1.4.0. With keep-balance disabled, there is no chance of data loss.
 
 We've put together a "wiki page":https://dev.arvados.org/projects/arvados/wiki/Recovering_lost_data which outlines how to recover blocks which have been put in the trash, but not yet deleted, as well as how to identify any collections which have missing blocks so that they can be regenerated. The keep-balance component has been enhanced to provide a list of missing blocks and affected collections and we've provided a "utility script":https://github.com/arvados/arvados/blob/master/tools/keep-xref/keep-xref.py  which can be used to identify the workflows that generated those collections and who ran those workflows, so that they can be rerun.
 
-h3(#v1_3_0). v1.3.0 (2018-12-05)
+h2(#v1_3_0). v1.3.0 (2018-12-05)
+
+"Upgrading from 1.2":#v1_2_0
 
 This release includes several database migrations, which will be executed automatically as part of the API server upgrade. On large Arvados installations, these migrations will take a while. We've seen the upgrade take 30 minutes or more on installations with a lot of collections.
 
@@ -260,13 +301,15 @@ The @arvados-controller@ component now requires the /etc/arvados/config.yml file
 
 Support for the deprecated "jobs" API is broken in this release.  Users who rely on it should not upgrade.  This will be fixed in an upcoming 1.3.1 patch release, however users are "encouraged to migrate":upgrade-crunch2.html as support for the "jobs" API will be dropped in an upcoming release.  Users who are already using the "containers" API are not affected.
 
-h3(#v1_2_1). v1.2.1 (2018-11-26)
+h2(#v1_2_1). v1.2.1 (2018-11-26)
 
 There are no special upgrade notes for this release.
 
-h3(#v1_2_0). v1.2.0 (2018-09-05)
+h2(#v1_2_0). v1.2.0 (2018-09-05)
 
-h4. Regenerate Postgres table statistics
+"Upgrading from 1.1.2 or 1.1.3":#v1_1_2
+
+h3. Regenerate Postgres table statistics
 
 It is recommended to regenerate the table statistics for Postgres after upgrading to v1.2.0. If autovacuum is enabled on your installation, this script would do the trick:
 
@@ -286,7 +329,7 @@ done
 
 If you also need to do the vacuum, you could adapt the script to run 'vacuum analyze' instead of 'analyze'.
 
-h4. New component: arvados-controller
+h3. New component: arvados-controller
 
 Commit "db5107dca":https://dev.arvados.org/projects/arvados/repository/revisions/db5107dca adds a new system service, arvados-controller. More detail is available in story "#13496":https://dev.arvados.org/issues/13497.
 
@@ -294,9 +337,11 @@ To add the Arvados Controller to your system please refer to the "installation i
 
 Verify your setup by confirming that API calls appear in the controller's logs (_e.g._, @journalctl -fu arvados-controller@) while loading a workbench page.
 
-h3(#v1_1_4). v1.1.4 (2018-04-10)
+h2(#v1_1_4). v1.1.4 (2018-04-10)
+
+"Upgrading from 1.1.3":#v1_1_3
 
-h4. arvados-cwl-runner regressions (2018-04-05)
+h3. arvados-cwl-runner regressions (2018-04-05)
 
 <strong>Secondary files missing from toplevel workflow inputs</strong>
 
@@ -421,13 +466,15 @@ baseCommand: echo
 
 This bug has been fixed in Arvados release v1.2.0.
 
-h3(#v1_1_3). v1.1.3 (2018-02-08)
+h2(#v1_1_3). v1.1.3 (2018-02-08)
 
 There are no special upgrade notes for this release.
 
-h3(#v1_1_2). v1.1.2 (2017-12-22)
+h2(#v1_1_2). v1.1.2 (2017-12-22)
 
-h4. The minimum version for Postgres is now 9.4 (2017-12-08)
+"Upgrading from 1.1.0 or 1.1.1":#v1_1_0
+
+h3. The minimum version for Postgres is now 9.4 (2017-12-08)
 
 As part of story "#11908":https://dev.arvados.org/issues/11908, commit "8f987a9271":https://dev.arvados.org/projects/arvados/repository/revisions/8f987a9271 introduces a dependency on Postgres 9.4. Previously, Arvados required Postgres 9.3.
 
@@ -439,13 +486,13 @@ As part of story "#11908":https://dev.arvados.org/issues/11908, commit "8f987a92
 *# Install the @rh-postgresql94@ backport package from either Software Collections: http://doc.arvados.org/install/install-postgresql.html or the Postgres developers: https://www.postgresql.org/download/linux/redhat/
 *# Restore from the backup using @psql@
 
-h3(#v1_1_1). v1.1.1 (2017-11-30)
+h2(#v1_1_1). v1.1.1 (2017-11-30)
 
 There are no special upgrade notes for this release.
 
-h3(#v1_1_0). v1.1.0 (2017-10-24)
+h2(#v1_1_0). v1.1.0 (2017-10-24)
 
-h4. The minimum version for Postgres is now 9.3 (2017-09-25)
+h3. The minimum version for Postgres is now 9.3 (2017-09-25)
 
 As part of story "#12032":https://dev.arvados.org/issues/12032, commit "68bdf4cbb1":https://dev.arvados.org/projects/arvados/repository/revisions/68bdf4cbb1 introduces a dependency on Postgres 9.3. Previously, Arvados required Postgres 9.1.
 
@@ -457,9 +504,9 @@ As part of story "#12032":https://dev.arvados.org/issues/12032, commit "68bdf4cb
 *# Install the @rh-postgresql94@ backport package from either Software Collections: http://doc.arvados.org/install/install-postgresql.html or the Postgres developers: https://www.postgresql.org/download/linux/redhat/
 *# Restore from the backup using @psql@
 
-h3(#older). Older versions
+h2(#older). Older versions
 
-h4. Upgrade slower than usual (2017-06-30)
+h3. Upgrade slower than usual (2017-06-30)
 
 As part of story "#11807":https://dev.arvados.org/issues/11807, commit "55aafbb":https://dev.arvados.org/projects/arvados/repository/revisions/55aafbb converts old "jobs" database records from YAML to JSON, making the upgrade process slower than usual.
 
@@ -467,13 +514,13 @@ As part of story "#11807":https://dev.arvados.org/issues/11807, commit "55aafbb"
 * The conversion runs as a database migration, i.e., during the deb/rpm package upgrade process, while your API server is unavailable.
 * Expect it to take about 1 minute per 20K jobs that have ever been created/run.
 
-h4. Service discovery overhead change in keep-web (2017-06-05)
+h3. Service discovery overhead change in keep-web (2017-06-05)
 
 As part of story "#9005":https://dev.arvados.org/issues/9005, commit "cb230b0":https://dev.arvados.org/projects/arvados/repository/revisions/cb230b0 reduces service discovery overhead in keep-web requests.
 
 * When upgrading keep-web _or keepproxy_ to/past this version, make sure to update API server as well. Otherwise, a bad token in a request can cause keep-web to fail future requests until either keep-web restarts or API server gets upgraded.
 
-h4. Node manager now has an http endpoint for management (2017-04-12)
+h3. Node manager now has an http endpoint for management (2017-04-12)
 
 As part of story "#11349":https://dev.arvados.org/issues/11349, commit "2c094e2":https://dev.arvados.org/projects/arvados/repository/revisions/2c094e2 adds a "management" http server to nodemanager.
 
@@ -482,7 +529,7 @@ As part of story "#11349":https://dev.arvados.org/issues/11349, commit "2c094e2"
   port = 8989</pre> (see example configuration files in source:services/nodemanager/doc or https://doc.arvados.org/install/install-nodemanager.html for more info)
 * The server responds to @http://{address}:{port}/status.json@ with a summary of how many nodes are in each state (booting, busy, shutdown, etc.)
 
-h4. New websockets component (2017-03-23)
+h3. New websockets component (2017-03-23)
 
 As part of story "#10766":https://dev.arvados.org/issues/10766, commit "e8cc0d7":https://dev.arvados.org/projects/arvados/repository/revisions/e8cc0d7 replaces puma with arvados-ws as the recommended websocket server.
 * See http://doc.arvados.org/install/install-ws.html for install/upgrade instructions.
@@ -494,7 +541,7 @@ $ systemctl disable puma
 $ systemctl stop puma
 </pre>
 
-h4. Change of database encoding for hashes and arrays (2017-03-06)
+h3. Change of database encoding for hashes and arrays (2017-03-06)
 
 As part of story "#11168":https://dev.arvados.org/issues/11168, commit "660a614":https://dev.arvados.org/projects/arvados/repository/revisions/660a614 uses JSON instead of YAML to encode hashes and arrays in the database.
 
@@ -502,14 +549,14 @@ As part of story "#11168":https://dev.arvados.org/issues/11168, commit "660a614"
 * Downgrading past this version is not supported, and is likely to cause errors. If this happens, the solution is to upgrade past this version.
 * After upgrading, make sure to restart puma and crunch-dispatch-* processes.
 
-h4. Docker image format compatibility check (2017-02-03)
+h3. Docker image format compatibility check (2017-02-03)
 
 As part of story "#10969":https://dev.arvados.org/issues/10969, commit "74a9dec":https://dev.arvados.org/projects/arvados/repository/revisions/74a9dec introduces a Docker image format compatibility check: the @arv keep docker@ command prevents users from inadvertently saving docker images that compute nodes won't be able to run.
 * If your compute nodes run a version of *docker older than 1.10* you must override the default by adding to your API server configuration (@/etc/arvados/api/application.yml@): <pre><code class="yaml">docker_image_formats: ["v1"]</code></pre>
 * Refer to the comments above @docker_image_formats@ in @/var/www/arvados-api/current/config/application.default.yml@ or source:services/api/config/application.default.yml or issue "#10969":https://dev.arvados.org/issues/10969 for more detail.
 * *NOTE:* This does *not* include any support for migrating existing Docker images from v1 to v2 format. This will come later: for now, sites running Docker 1.9 or earlier should still *avoid upgrading Docker further than 1.9.*
 
-h4. Debian and RPM packages now have systemd unit files (2016-09-27)
+h3. Debian and RPM packages now have systemd unit files (2016-09-27)
 
 Several Debian and RPM packages -- keep-balance ("d9eec0b":https://dev.arvados.org/projects/arvados/repository/revisions/d9eec0b), keep-web ("3399e63":https://dev.arvados.org/projects/arvados/repository/revisions/3399e63), keepproxy ("6de67b6":https://dev.arvados.org/projects/arvados/repository/revisions/6de67b6), and arvados-git-httpd ("9e27ddf":https://dev.arvados.org/projects/arvados/repository/revisions/9e27ddf) -- now enable their respective components using systemd. These components prefer YAML configuration files over command line flags ("3bbe1cd":https://dev.arvados.org/projects/arvados/repository/revisions/3bbe1cd).
 
@@ -527,41 +574,41 @@ Several Debian and RPM packages -- keep-balance ("d9eec0b":https://dev.arvados.o
 ** keepproxy - /etc/arvados/keepproxy/keepproxy.yml
 ** arvados-git-httpd - /etc/arvados/arv-git-httpd/arv-git-httpd.yml
 
-h4. Installation paths for Python modules and script changed (2016-05-31)
+h3. Installation paths for Python modules and script changed (2016-05-31)
 
 Commits "ae72b172c8":https://dev.arvados.org/projects/arvados/repository/revisions/ae72b172c8 and "3aae316c25":https://dev.arvados.org/projects/arvados/repository/revisions/3aae316c25 change the filesystem location where Python modules and scripts are installed.
 
 * Previous packages installed these files to the distribution's preferred path under @/usr/local@ (or the equivalent location in a Software Collection).  Now they get installed to a path under @/usr@.  This improves compatibility with other Python packages provided by the distribution.  See "#9242":https://dev.arvados.org/issues/9242 for more background.
 * If you simply import Python modules from scripts, or call Python tools relying on $PATH, you don't need to make any changes.  If you have hardcoded full paths to some of these files (e.g., in symbolic links or configuration files), you will need to update those paths after this upgrade.
 
-h4. Crunchrunner package is required on compute and shell nodes (2016-04-25)
+h3. Crunchrunner package is required on compute and shell nodes (2016-04-25)
 
 Commit "eebcb5e":https://dev.arvados.org/projects/arvados/repository/revisions/eebcb5e requires the crunchrunner package to be installed on compute nodes and shell nodes in order to run CWL workflows.
 
 * On each Debian-based compute node and shell node, run: @sudo apt-get install crunchrunner@
 * On each Red Hat-based compute node and shell node, run: @sudo yum install crunchrunner@
 
-h4. Keep permission signature algorithm change (2016-04-21)
+h3. Keep permission signature algorithm change (2016-04-21)
 
 Commit "3c88abd":https://dev.arvados.org/projects/arvados/repository/revisions/3c88abd changes the Keep permission signature algorithm.
 
 * All software components that generate signatures must be upgraded together. These are: keepstore, API server, keep-block-check, and keep-rsync. For example, if keepstore < 0.1.20160421183420 but API server >= 0.1.20160421183420, clients will not be able to read or write data in Keep.
 * Jobs and client operations that are in progress during the upgrade (including arv-put's "resume cache") will fail.
 
-h4. Workbench's "Getting Started" popup disabled by default (2015-01-05)
+h3. Workbench's "Getting Started" popup disabled by default (2015-01-05)
 
 Commit "e1276d6e":https://dev.arvados.org/projects/arvados/repository/revisions/e1276d6e disables Workbench's "Getting Started" popup by default.
 
 * If you want new users to continue seeing this popup, set @enable_getting_started_popup: true@ in Workbench's @application.yml@ configuration.
 
-h4. Crunch jobs now have access to Keep-backed writable scratch storage (2015-12-03)
+h3. Crunch jobs now have access to Keep-backed writable scratch storage (2015-12-03)
 
 Commit "5590c9ac":https://dev.arvados.org/projects/arvados/repository/revisions/5590c9ac makes a Keep-backed writable scratch directory available in crunch jobs (see "#7751":https://dev.arvados.org/issues/7751)
 
 * All compute nodes must be upgraded to arvados-fuse >= 0.1.2015112518060 because crunch-job uses some new arv-mount flags (--mount-tmp, --mount-by-pdh) introduced in merge "346a558":https://dev.arvados.org/projects/arvados/repository/revisions/346a558
 * Jobs will fail if the API server (in particular crunch-job from the arvados-cli gem) is upgraded without upgrading arvados-fuse on compute nodes.
 
-h4. Recommended configuration change for keep-web (2015-11-11)
+h3. Recommended configuration change for keep-web (2015-11-11)
 
 Commit "1e2ace5":https://dev.arvados.org/projects/arvados/repository/revisions/1e2ace5 changes recommended config for keep-web (see "#5824":https://dev.arvados.org/issues/5824)
 
@@ -570,14 +617,18 @@ Commit "1e2ace5":https://dev.arvados.org/projects/arvados/repository/revisions/1
 * Workbench config adds @keep_web_download_url@
 * More info on the (still beta/non-TOC-linked) "keep-web doc page":http://doc.arvados.org/install/install-keep-web.html
 
-h4. Stopped containers are now automatically removed on compute nodes (2015-11-04)
+h3. Stopped containers are now automatically removed on compute nodes (2015-11-04)
 
 Commit "1d1c6de":https://dev.arvados.org/projects/arvados/repository/revisions/1d1c6de removes stopped containers (see "#7444":https://dev.arvados.org/issues/7444)
 
 * arvados-docker-cleaner removes _all_ docker containers as soon as they exit, effectively making @docker run@ default to @--rm@. If you run arvados-docker-cleaner on a host that does anything other than run crunch-jobs, and you still want to be able to use @docker start@, read the "new doc page":http://doc.arvados.org/install/install-compute-node.html to learn how to turn this off before upgrading.
 
-h4. New keep-web service (2015-11-04)
+h3. New keep-web service (2015-11-04)
 
 Commit "21006cf":https://dev.arvados.org/projects/arvados/repository/revisions/21006cf adds a new keep-web service (see "#5824":https://dev.arvados.org/issues/5824).
 
 * Nothing relies on keep-web yet, but early adopters can install it now by following http://doc.arvados.org/install/install-keep-web.html (it is not yet linked in the TOC).
+
+<notextile>
+</div>
+</notextile>
index ff4a58e12c6ed6bd0e0a4af38dcd7ee884839178..a97e1d83204490c4e803d18e1a87349c3532e98b 100644 (file)
@@ -38,3 +38,5 @@ table.CodeRay {
 td.line-numbers {
     width: 2em;
 }
+
+.releasenotes h2 { margin-top: 1.5em; text-decoration: underline; }
index 327ac0c4586e0227a40df7a8c732dd9c22b5c3ae..1a41980e2415e2fccf193e9f38351b8ae35d97c6 100644 (file)
@@ -9,6 +9,10 @@ Copyright (C) The Arvados Authors. All rights reserved.
 SPDX-License-Identifier: CC-BY-SA-3.0
 {% endcomment %}
 
+{% include 'notebox_begin' %}
+This section is about installing an Arvados cluster.  If you are just looking to install Arvados client tools and libraries, "go to the SDK section.":{{site.baseurl}}/sdk
+{% include 'notebox_end' %}
+
 Arvados components run on GNU/Linux systems, and supports AWS, GCP and Azure cloud platforms as well as on-premises installs.  Arvados supports Debian and derivatives such as Ubuntu, as well as Red Hat and derivatives such as CentOS.  "Arvados is Free Software":{{site.baseurl}}/user/copying/copying.html and self-install installations are not limited in any way.  Commercial support and development are also available from "Curii Corporation.":mailto:info@curii.com
 
 Arvados components can be installed and configured in a number of different ways.
index 9c964a553bf1c12dc9735126a73b471a12012a74..b6ed39ed2cdeb93b05f80411dac81589ab0d6d27 100644 (file)
@@ -11,7 +11,7 @@ SPDX-License-Identifier: CC-BY-SA-3.0
 
 This section documents language bindings for the "Arvados API":{{site.baseurl}}/api and Keep that are available for various programming languages.  Not all features are available in every SDK.  The most complete SDK is the Python SDK.  Note that this section only gives a high level overview of each SDK.  Consult the "Arvados API":{{site.baseurl}}/api section for detailed documentation about Arvados API calls available on each resource.
 
-* "Python SDK":{{site.baseurl}}/sdk/python/sdk-python.html
+* "Python SDK":{{site.baseurl}}/sdk/python/sdk-python.html (also includes essential command line tools such as "arv-put" and "arv-get")
 * "Command line SDK":{{site.baseurl}}/sdk/cli/install.html ("arv")
 * "Go SDK":{{site.baseurl}}/sdk/go/index.html
 * "R SDK":{{site.baseurl}}/sdk/R/index.html
index 50fee746b9b057b723c1a58c5f3d48ab6c7bc193..41af15073281b51d7a9087195a7ad9aacb9c0cc2 100644 (file)
@@ -1153,6 +1153,8 @@ Clusters:
         identification, and does not retrieve any other personal
         information.</i>
 
+      # Workbench screen displayed to inactive users.  This is HTML
+      # text that will be incorporated directly onto the page.
       InactivePageHTML: |
         <img src="/arvados-logo-big.png" style="width: 20%; float: right; padding: 1em;" />
         <h3>Hi! You're logged in, but...</h3>
@@ -1160,6 +1162,13 @@ Clusters:
         <p>An administrator must activate your account before you can get
         any further.</p>
 
+      # Connecting to Arvados shell VMs tends to be site-specific.
+      # Put any special instructions here. This is HTML text that will
+      # be incorporated directly onto the Workbench page.
+      SSHHelpPageHTML: |
+        <a href="https://doc.arvados.org/user/getting_started/ssh-access-unix.html">Accessing an Arvados VM with SSH</a> (generic instructions).
+        Site configurations vary.  Contact your local cluster administrator if you have difficulty accessing an Arvados shell node.
+
     # 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
index 5eeda348bf366755c6ae6b8cfc7c78765d731a9b..44c69b6e2e5fdf6af082eecd3a043aa9ff76f828 100644 (file)
@@ -214,6 +214,7 @@ var whitelist = map[string]bool{
        "Workbench.VocabularyURL":                      true,
        "Workbench.WelcomePageHTML":                    true,
        "Workbench.InactivePageHTML":                   true,
+       "Workbench.SSHHelpPageHTML":                    true,
 }
 
 func redactUnsafe(m map[string]interface{}, mPrefix, lookupPrefix string) error {
index 2ee6025077c64be9dff306b46e4a44acabc69394..25fa89394a0ba9132b23a29126866c94f2d7bc08 100644 (file)
@@ -1159,6 +1159,8 @@ Clusters:
         identification, and does not retrieve any other personal
         information.</i>
 
+      # Workbench screen displayed to inactive users.  This is HTML
+      # text that will be incorporated directly onto the page.
       InactivePageHTML: |
         <img src="/arvados-logo-big.png" style="width: 20%; float: right; padding: 1em;" />
         <h3>Hi! You're logged in, but...</h3>
@@ -1166,6 +1168,13 @@ Clusters:
         <p>An administrator must activate your account before you can get
         any further.</p>
 
+      # Connecting to Arvados shell VMs tends to be site-specific.
+      # Put any special instructions here. This is HTML text that will
+      # be incorporated directly onto the Workbench page.
+      SSHHelpPageHTML: |
+        <a href="https://doc.arvados.org/user/getting_started/ssh-access-unix.html">Accessing an Arvados VM with SSH</a> (generic instructions).
+        Site configurations vary.  Contact your local cluster administrator if you have difficulty accessing an Arvados shell node.
+
     # 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
index 1b1ffef24cf3a1c6bb2245f3d4c9cb258118e620..42083cb83df10918cdbc9f14869d0c2e49e51442 100644 (file)
@@ -38,7 +38,11 @@ func New(cluster *arvados.Cluster) *Conn {
                if !remote.Proxy {
                        continue
                }
-               remotes[id] = rpc.NewConn(id, &url.URL{Scheme: remote.Scheme, Host: remote.Host}, remote.Insecure, saltedTokenProvider(local, id))
+               conn := rpc.NewConn(id, &url.URL{Scheme: remote.Scheme, Host: remote.Host}, remote.Insecure, saltedTokenProvider(local, id))
+               // Older versions of controller rely on the Via header
+               // to detect loops.
+               conn.SendHeader = http.Header{"Via": {"HTTP/1.1 arvados-controller"}}
+               remotes[id] = conn
        }
 
        return &Conn{
@@ -116,8 +120,13 @@ func (conn *Conn) chooseBackend(id string) backend {
 // or "" for the local backend.
 //
 // A non-nil error means all backends failed.
-func (conn *Conn) tryLocalThenRemotes(ctx context.Context, fn func(context.Context, string, backend) error) error {
-       if err := fn(ctx, "", conn.local); err == nil || errStatus(err) != http.StatusNotFound {
+func (conn *Conn) tryLocalThenRemotes(ctx context.Context, forwardedFor string, fn func(context.Context, string, backend) error) error {
+       if err := fn(ctx, "", conn.local); err == nil || errStatus(err) != http.StatusNotFound || forwardedFor != "" {
+               // Note: forwardedFor != "" means this request came
+               // from a remote cluster, so we don't take a second
+               // hop. This avoids cycles, redundant calls to a
+               // mutually reachable remote, and use of double-salted
+               // tokens.
                return err
        }
 
@@ -224,8 +233,10 @@ func (conn *Conn) CollectionGet(ctx context.Context, options arvados.GetOptions)
        } else {
                // UUID is a PDH
                first := make(chan arvados.Collection, 1)
-               err := conn.tryLocalThenRemotes(ctx, func(ctx context.Context, remoteID string, be backend) error {
-                       c, err := be.CollectionGet(ctx, options)
+               err := conn.tryLocalThenRemotes(ctx, options.ForwardedFor, func(ctx context.Context, remoteID string, be backend) error {
+                       remoteOpts := options
+                       remoteOpts.ForwardedFor = conn.cluster.ClusterID + "-" + options.ForwardedFor
+                       c, err := be.CollectionGet(ctx, remoteOpts)
                        if err != nil {
                                return err
                        }
index c20d0e77ec79db5ce823175137703525fdc30b04..d54f50cf1ff2a8b9f52c3149548e74257443dbb8 100644 (file)
@@ -6,7 +6,9 @@ package controller
 
 import (
        "context"
+       "crypto/tls"
        "encoding/json"
+       "io/ioutil"
        "net/http"
        "net/http/httptest"
        "net/url"
@@ -216,3 +218,83 @@ func (s *HandlerSuite) TestCreateAPIToken(c *check.C) {
        c.Check(user.UUID, check.Equals, arvadostest.ActiveUserUUID)
        c.Check(user.Authorization.TokenV2(), check.Equals, auth.TokenV2())
 }
+
+func (s *HandlerSuite) CheckObjectType(c *check.C, url string, token string, skippedFields map[string]bool) {
+       var proxied, direct map[string]interface{}
+       var err error
+
+       // Get collection from controller
+       req := httptest.NewRequest("GET", url, nil)
+       req.Header.Set("Authorization", "Bearer "+token)
+       resp := httptest.NewRecorder()
+       s.handler.ServeHTTP(resp, req)
+       c.Assert(resp.Code, check.Equals, http.StatusOK,
+               check.Commentf("Wasn't able to get data from the controller at %q", url))
+       err = json.Unmarshal(resp.Body.Bytes(), &proxied)
+       c.Check(err, check.Equals, nil)
+
+       // Get collection directly from RailsAPI
+       client := &http.Client{
+               Transport: &http.Transport{
+                       TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+               },
+       }
+       resp2, err := client.Get(s.cluster.Services.RailsAPI.ExternalURL.String() + url + "/?api_token=" + token)
+       c.Check(err, check.Equals, nil)
+       defer resp2.Body.Close()
+       db, err := ioutil.ReadAll(resp2.Body)
+       c.Check(err, check.Equals, nil)
+       err = json.Unmarshal(db, &direct)
+       c.Check(err, check.Equals, nil)
+
+       // Check that all RailsAPI provided keys exist on the controller response.
+       for k := range direct {
+               if _, ok := skippedFields[k]; ok {
+                       continue
+               } else if val, ok := proxied[k]; ok {
+                       if direct["kind"] == "arvados#collection" && k == "manifest_text" {
+                               // Tokens differ from request to request
+                               c.Check(strings.Split(val.(string), "+A")[0], check.Equals, strings.Split(direct[k].(string), "+A")[0])
+                       } else {
+                               c.Check(val, check.DeepEquals, direct[k],
+                                       check.Commentf("RailsAPI %s key %q's value %q differs from controller's %q.", direct["kind"], k, direct[k], val))
+                       }
+               } else {
+                       c.Errorf("%s's key %q missing on controller's response.", direct["kind"], k)
+               }
+       }
+}
+
+func (s *HandlerSuite) TestGetObjects(c *check.C) {
+       // Get the 1st keep service's uuid from the running test server.
+       req := httptest.NewRequest("GET", "/arvados/v1/keep_services/", nil)
+       req.Header.Set("Authorization", "Bearer "+arvadostest.AdminToken)
+       resp := httptest.NewRecorder()
+       s.handler.ServeHTTP(resp, req)
+       c.Assert(resp.Code, check.Equals, http.StatusOK)
+       var ksList arvados.KeepServiceList
+       json.Unmarshal(resp.Body.Bytes(), &ksList)
+       c.Assert(len(ksList.Items), check.Not(check.Equals), 0)
+       ksUUID := ksList.Items[0].UUID
+
+       testCases := map[string]map[string]bool{
+               "api_clients/" + arvadostest.TrustedWorkbenchAPIClientUUID:     nil,
+               "api_client_authorizations/" + arvadostest.AdminTokenUUID:      nil,
+               "authorized_keys/" + arvadostest.AdminAuthorizedKeysUUID:       nil,
+               "collections/" + arvadostest.CollectionWithUniqueWordsUUID:     map[string]bool{"href": true},
+               "containers/" + arvadostest.RunningContainerUUID:               nil,
+               "container_requests/" + arvadostest.QueuedContainerRequestUUID: nil,
+               "groups/" + arvadostest.AProjectUUID:                           nil,
+               "keep_services/" + ksUUID:                                      nil,
+               "links/" + arvadostest.ActiveUserCanReadAllUsersLinkUUID:       nil,
+               "logs/" + arvadostest.CrunchstatForRunningJobLogUUID:           nil,
+               "nodes/" + arvadostest.IdleNodeUUID:                            nil,
+               "repositories/" + arvadostest.ArvadosRepoUUID:                  nil,
+               "users/" + arvadostest.ActiveUserUUID:                          map[string]bool{"href": true},
+               "virtual_machines/" + arvadostest.TestVMUUID:                   nil,
+               "workflows/" + arvadostest.WorkflowWithDefinitionYAMLUUID:      nil,
+       }
+       for url, skippedFields := range testCases {
+               s.CheckObjectType(c, "/arvados/v1/"+url, arvadostest.AdminToken, skippedFields)
+       }
+}
index 406ebfd2da064df383105b8e0a7c8f4e7b19a529..99d4c4e9a10a883abc54ce0fe1cbc476af7c7692 100644 (file)
@@ -521,10 +521,10 @@ The 'jobs' API is no longer supported.
             for req in job_reqs:
                 tool.requirements.append(req)
 
-    def arv_executor(self, tool, job_order, runtimeContext, logger=None):
+    def arv_executor(self, updated_tool, job_order, runtimeContext, logger=None):
         self.debug = runtimeContext.debug
 
-        tool.visit(self.check_features)
+        updated_tool.visit(self.check_features)
 
         self.project_uuid = runtimeContext.project_uuid
         self.pipeline = None
@@ -545,16 +545,20 @@ The 'jobs' API is no longer supported.
             raise Exception("--submit-request-uuid requires containers API, but using '{}' api".format(self.work_api))
 
         if not runtimeContext.name:
-            runtimeContext.name = self.name = tool.tool.get("label") or tool.metadata.get("label") or os.path.basename(tool.tool["id"])
+            runtimeContext.name = self.name = updated_tool.tool.get("label") or updated_tool.metadata.get("label") or os.path.basename(updated_tool.tool["id"])
 
         # Upload local file references in the job order.
         job_order = upload_job_order(self, "%s input" % runtimeContext.name,
-                                     tool, job_order)
+                                     updated_tool, job_order)
+
+        # the last clause means: if it is a command line tool, and we
+        # are going to wait for the result, and always_submit_runner
+        # is false, then we don't submit a runner process.
 
         submitting = (runtimeContext.update_workflow or
                       runtimeContext.create_workflow or
                       (runtimeContext.submit and not
-                       (tool.tool["class"] == "CommandLineTool" and
+                       (updated_tool.tool["class"] == "CommandLineTool" and
                         runtimeContext.wait and
                         not runtimeContext.always_submit_runner)))
 
@@ -564,8 +568,11 @@ The 'jobs' API is no longer supported.
         if submitting:
             # Document may have been auto-updated. Reload the original
             # document with updating disabled because we want to
-            # submit the original document, not the auto-updated one.
-            tool = load_tool(tool.tool["id"], loadingContext)
+            # submit the document with its original CWL version, not
+            # the auto-updated one.
+            tool = load_tool(updated_tool.tool["id"], loadingContext)
+        else:
+            tool = updated_tool
 
         # Upload direct dependencies of workflow steps, get back mapping of files to keep references.
         # Also uploads docker images.
@@ -632,22 +639,23 @@ The 'jobs' API is no longer supported.
         if runtimeContext.submit:
             # Submit a runner job to run the workflow for us.
             if self.work_api == "containers":
-                if tool.tool["class"] == "CommandLineTool" and runtimeContext.wait and (not runtimeContext.always_submit_runner):
-                    runtimeContext.runnerjob = tool.tool["id"]
+                if submitting:
+                    tool = RunnerContainer(self, updated_tool,
+                                           tool, loadingContext, runtimeContext.enable_reuse,
+                                           self.output_name,
+                                           self.output_tags,
+                                           submit_runner_ram=runtimeContext.submit_runner_ram,
+                                           name=runtimeContext.name,
+                                           on_error=runtimeContext.on_error,
+                                           submit_runner_image=runtimeContext.submit_runner_image,
+                                           intermediate_output_ttl=runtimeContext.intermediate_output_ttl,
+                                           merged_map=merged_map,
+                                           priority=runtimeContext.priority,
+                                           secret_store=self.secret_store,
+                                           collection_cache_size=runtimeContext.collection_cache_size,
+                                           collection_cache_is_default=self.should_estimate_cache_size)
                 else:
-                    tool = RunnerContainer(self, tool, loadingContext, runtimeContext.enable_reuse,
-                                                self.output_name,
-                                                self.output_tags,
-                                                submit_runner_ram=runtimeContext.submit_runner_ram,
-                                                name=runtimeContext.name,
-                                                on_error=runtimeContext.on_error,
-                                                submit_runner_image=runtimeContext.submit_runner_image,
-                                                intermediate_output_ttl=runtimeContext.intermediate_output_ttl,
-                                                merged_map=merged_map,
-                                                priority=runtimeContext.priority,
-                                                secret_store=self.secret_store,
-                                                collection_cache_size=runtimeContext.collection_cache_size,
-                                                collection_cache_is_default=self.should_estimate_cache_size)
+                    runtimeContext.runnerjob = tool.tool["id"]
 
         if runtimeContext.cwl_runner_job is not None:
             self.uuid = runtimeContext.cwl_runner_job.get('uuid')
index 19a6dd98b332c6dbc8363e989104a075cf90f587..2239e0f9df952b9ab75a7e9a96ab46953ab29f94 100644 (file)
@@ -578,7 +578,8 @@ class Runner(Process):
     """Base class for runner processes, which submit an instance of
     arvados-cwl-runner and wait for the final result."""
 
-    def __init__(self, runner, tool, loadingContext, enable_reuse,
+    def __init__(self, runner, updated_tool,
+                 tool, loadingContext, enable_reuse,
                  output_name, output_tags, submit_runner_ram=0,
                  name=None, on_error=None, submit_runner_image=None,
                  intermediate_output_ttl=0, merged_map=None,
@@ -587,10 +588,9 @@ class Runner(Process):
                  collection_cache_is_default=True):
 
         loadingContext = loadingContext.copy()
-        loadingContext.metadata = loadingContext.metadata.copy()
-        loadingContext.metadata["cwlVersion"] = INTERNAL_VERSION
+        loadingContext.metadata = updated_tool.metadata.copy()
 
-        super(Runner, self).__init__(tool.tool, loadingContext)
+        super(Runner, self).__init__(updated_tool.tool, loadingContext)
 
         self.arvrunner = runner
         self.embedded_tool = tool
index aa68933c6abc17f2d19860c2cd838a3026546257..62ceab2fa11c1895983cd518fb2d4370a49da986 100644 (file)
@@ -60,7 +60,7 @@ setup(name='arvados-cwl-runner',
       ],
       test_suite='tests',
       tests_require=[
-          'mock>=1.0',
+          'mock>=1.0,<4',
           'subprocess32>=3.5.1',
       ],
       zip_safe=True
index 927e43ad76c1fe1e547fd91dc026282726c82a1c..397ae142253d26b41d11aee03586fbc3e352bb31 100644 (file)
@@ -388,6 +388,19 @@ class TestSubmit(unittest.TestCase):
                          stubs.expect_container_request_uuid + '\n')
         self.assertEqual(exited, 0)
 
+
+    @stubs
+    def test_submit_container_tool(self, stubs):
+        # test for issue #16139
+        exited = arvados_cwl.main(
+            ["--submit", "--no-wait", "--api=containers", "--debug",
+                "tests/tool/tool_with_sf.cwl", "tests/tool/tool_with_sf.yml"],
+            stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
+
+        self.assertEqual(stubs.capture_stdout.getvalue(),
+                         stubs.expect_container_request_uuid + '\n')
+        self.assertEqual(exited, 0)
+
     @stubs
     def test_submit_container_no_reuse(self, stubs):
         exited = arvados_cwl.main(
diff --git a/sdk/cwl/tests/tool/blub.txt.cat b/sdk/cwl/tests/tool/blub.txt.cat
new file mode 100644 (file)
index 0000000..d7c4221
--- /dev/null
@@ -0,0 +1 @@
+clipper clupper
diff --git a/sdk/cwl/tests/tool/tool_with_sf.cwl b/sdk/cwl/tests/tool/tool_with_sf.cwl
new file mode 100644 (file)
index 0000000..0beb7ad
--- /dev/null
@@ -0,0 +1,24 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+
+# Test case for arvados-cwl-runner
+#
+# Used to test whether scanning a tool file for dependencies (e.g. default
+# value blub.txt) and uploading to Keep works as intended.
+
+class: CommandLineTool
+cwlVersion: v1.0
+requirements:
+  - class: DockerRequirement
+    dockerPull: debian:8
+inputs:
+  - id: x
+    type: File
+    secondaryFiles:
+      - .cat
+    inputBinding:
+      valueFrom: $(self.path).cat
+      position: 1
+outputs: []
+baseCommand: cat
diff --git a/sdk/cwl/tests/tool/tool_with_sf.yml b/sdk/cwl/tests/tool/tool_with_sf.yml
new file mode 100644 (file)
index 0000000..3f79d57
--- /dev/null
@@ -0,0 +1,3 @@
+x:
+  class: File
+  location: blub.txt
index 6e115a15f6f6232c5e7add504e04aff09309e759..aa670c53921ff0a4be5f6765347b06ede278d0d1 100644 (file)
@@ -62,6 +62,7 @@ type GetOptions struct {
        UUID         string   `json:"uuid"`
        Select       []string `json:"select"`
        IncludeTrash bool     `json:"include_trash"`
+       ForwardedFor string   `json:"forwarded_for"`
 }
 
 type UntrashOptions struct {
index 5ac35e2292e97809cdb68e6d06ab937193cdba4f..35fd3fd7406fcd24b0f167d38fc9dd009f0e4d69 100644 (file)
@@ -32,11 +32,17 @@ type Collection struct {
        ReplicationDesired        *int                   `json:"replication_desired"`
        StorageClassesDesired     []string               `json:"storage_classes_desired"`
        StorageClassesConfirmed   []string               `json:"storage_classes_confirmed"`
-       StorageClassesConfirmedAt time.Time              `json:"storage_classes_confirmed_at"`
+       StorageClassesConfirmedAt *time.Time             `json:"storage_classes_confirmed_at"`
        DeleteAt                  *time.Time             `json:"delete_at"`
        IsTrashed                 bool                   `json:"is_trashed"`
        Properties                map[string]interface{} `json:"properties"`
        WritableBy                []string               `json:"writable_by,omitempty"`
+       FileCount                 int                    `json:"file_count"`
+       FileSizeTotal             int64                  `json:"file_size_total"`
+       Version                   int                    `json:"version"`
+       PreserveVersion           bool                   `json:"preserve_version"`
+       CurrentVersionUUID        string                 `json:"current_version_uuid"`
+       Description               string                 `json:"description"`
 }
 
 func (c Collection) resourceName() string {
index 757d0745e77bf92474f3b4c8c95d116055bbf524..176f1dd2ae795a372f277d0aaf7a36897190f30a 100644 (file)
@@ -214,6 +214,7 @@ type Cluster struct {
                VocabularyURL          string
                WelcomePageHTML        string
                InactivePageHTML       string
+               SSHHelpPageHTML        string
        }
 
        ForceLegacyAPI14 bool
index 10b95c037116da0dc75f01b643a0d9fb587d4c46..5677f4deca5d70f16ab44d4023434f7e94fc73e2 100644 (file)
@@ -11,6 +11,7 @@ const (
        ActiveTokenUUID         = "zzzzz-gj3su-077z32aux8dg2s1"
        ActiveTokenV2           = "v2/zzzzz-gj3su-077z32aux8dg2s1/3kg6k6lzmp9kj5cpkcoxie963cmvjahbt2fod9zru30k1jqdmi"
        AdminToken              = "4axaw8zxe0qm22wa6urpp5nskcne8z88cvbupv653y1njyi05h"
+       AdminTokenUUID          = "zzzzz-gj3su-027z32aux8dg2s1"
        AnonymousToken          = "4kg6k6lzmp9kj4cpkcoxie964cmvjahbt4fod9zru44k4jqdmi"
        DataManagerToken        = "320mkve8qkswstz7ff61glpk3mhgghmg67wmic7elw4z41pke1"
        SystemRootToken         = "systemusertesttoken1234567890aoeuidhtnsqjkxbmwvzpy"
@@ -58,6 +59,20 @@ const (
        WorkflowWithDefinitionYAMLUUID = "zzzzz-7fd4e-validworkfloyml"
 
        CollectionReplicationDesired2Confirmed2UUID = "zzzzz-4zz18-434zv1tnnf2rygp"
+
+       ActiveUserCanReadAllUsersLinkUUID = "zzzzz-o0j2j-ctbysaduejxfrs5"
+
+       TrustedWorkbenchAPIClientUUID = "zzzzz-ozdt8-teyxzyd8qllg11h"
+
+       AdminAuthorizedKeysUUID = "zzzzz-fngyi-12nc9ov4osp8nae"
+
+       CrunchstatForRunningJobLogUUID = "zzzzz-57u5n-tmymyrojrbtnxh1"
+
+       IdleNodeUUID = "zzzzz-7ekkf-2z3mc76g2q73aio"
+
+       TestVMUUID = "zzzzz-2x53u-382brsig8rp3064"
+
+       CollectionWithUniqueWordsUUID = "zzzzz-4zz18-mnt690klmb51aud"
 )
 
 // PathologicalManifest : A valid manifest designed to test
index 87977c2187e014017a2d76c4314ee3b1eede6b9c..ff68e2a7fd8888d10d56b96e13a5a9bf2690a177 100644 (file)
@@ -64,6 +64,6 @@ setup(name='arvados-python-client',
           'Programming Language :: Python :: 3',
       ],
       test_suite='tests',
-      tests_require=['pbr<1.7.0', 'mock>=1.0', 'PyYAML'],
+      tests_require=['pbr<1.7.0', 'mock>=1.0,<4', 'PyYAML'],
       zip_safe=False
       )
index 99933ba7e7df0ef3bc90ec08f8aab49760b68851..caac5611e79c8baa43d30e396b33cc4a92f9d146 100644 (file)
@@ -624,10 +624,10 @@ class Collection < ArvadosModel
       return
     end
     (managed_props.keys - self.properties.keys).each do |key|
-      if managed_props[key].has_key?('Value')
-        self.properties[key] = managed_props[key]['Value']
-      elsif managed_props[key]['Function'].andand == 'original_owner'
+      if managed_props[key]['Function'] == 'original_owner'
         self.properties[key] = self.user_owner_uuid
+      elsif managed_props[key]['Value']
+        self.properties[key] = managed_props[key]['Value']
       else
         logger.warn "Unidentified default property definition '#{key}': #{managed_props[key].inspect}"
       end
index 5a7818147308a6f3fa41966430ffce837ab2cee7..b30b8cc1d9b24cc2bfcbeac7400afaa38cd03fa4 100644 (file)
@@ -125,10 +125,10 @@ class ContainerRequest < ArvadosModel
       # (same order as Container#handle_completed). Locking always
       # reloads the Container and ContainerRequest records.
       c = Container.find_by_uuid(container_uuid)
-      c.lock!
+      c.lock! if !c.nil?
       self.lock!
 
-      if container_uuid != c.uuid
+      if !c.nil? && container_uuid != c.uuid
         # After locking, we've noticed a race, the container_uuid is
         # different than the container record we just loaded.  This
         # can happen if Container#handle_completed scheduled a new
@@ -138,13 +138,18 @@ class ContainerRequest < ArvadosModel
         redo
       end
 
-      if state == Committed && c.final?
-        # The current container is
-        act_as_system_user do
-          leave_modified_by_user_alone do
-            finalize!
+      if !c.nil?
+        if state == Committed && c.final?
+          # The current container is
+          act_as_system_user do
+            leave_modified_by_user_alone do
+              finalize!
+            end
           end
         end
+      elsif state == Committed
+        # Behave as if the container is cancelled
+        update_attributes!(state: Final)
       end
       return true
     end
@@ -154,26 +159,27 @@ class ContainerRequest < ArvadosModel
   # finished/cancelled.
   def finalize!
     container = Container.find_by_uuid(container_uuid)
-    update_collections(container: container)
-
-    if container.state == Container::Complete
-      log_col = Collection.where(portable_data_hash: container.log).first
-      if log_col
-        # Need to save collection
-        completed_coll = Collection.new(
-          owner_uuid: self.owner_uuid,
-          name: "Container log for container #{container_uuid}",
-          properties: {
-            'type' => 'log',
-            'container_request' => self.uuid,
-            'container_uuid' => container_uuid,
-          },
-          portable_data_hash: log_col.portable_data_hash,
-          manifest_text: log_col.manifest_text)
-        completed_coll.save_with_unique_name!
+    if !container.nil?
+      update_collections(container: container)
+
+      if container.state == Container::Complete
+        log_col = Collection.where(portable_data_hash: container.log).first
+        if log_col
+          # Need to save collection
+          completed_coll = Collection.new(
+            owner_uuid: self.owner_uuid,
+            name: "Container log for container #{container_uuid}",
+            properties: {
+              'type' => 'log',
+              'container_request' => self.uuid,
+              'container_uuid' => container_uuid,
+            },
+            portable_data_hash: log_col.portable_data_hash,
+            manifest_text: log_col.manifest_text)
+          completed_coll.save_with_unique_name!
+        end
       end
     end
-
     update_attributes!(state: Final)
   end
 
@@ -181,6 +187,10 @@ class ContainerRequest < ArvadosModel
     collections.each do |out_type|
       pdh = container.send(out_type)
       next if pdh.nil?
+      c = Collection.where(portable_data_hash: pdh).first
+      next if c.nil?
+      manifest = c.manifest_text
+
       coll_name = "Container #{out_type} for request #{uuid}"
       trash_at = nil
       if out_type == 'output'
@@ -191,7 +201,6 @@ class ContainerRequest < ArvadosModel
           trash_at = db_current_time + self.output_ttl
         end
       end
-      manifest = Collection.where(portable_data_hash: pdh).first.manifest_text
 
       coll_uuid = self.send(out_type + '_uuid')
       coll = coll_uuid.nil? ? nil : Collection.where(uuid: coll_uuid).first
@@ -269,34 +278,36 @@ class ContainerRequest < ArvadosModel
       if self.container_count_changed?
         errors.add :container_count, "cannot be updated directly."
         return false
-      else
-        self.container_count += 1
-        if self.container_uuid_was
-          old_container = Container.find_by_uuid(self.container_uuid_was)
-          old_logs = Collection.where(portable_data_hash: old_container.log).first
-          if old_logs
-            log_coll = self.log_uuid.nil? ? nil : Collection.where(uuid: self.log_uuid).first
-            if self.log_uuid.nil?
-              log_coll = Collection.new(
-                owner_uuid: self.owner_uuid,
-                name: coll_name = "Container log for request #{uuid}",
-                manifest_text: "")
-            end
+      end
 
-            # copy logs from old container into CR's log collection
-            src = Arv::Collection.new(old_logs.manifest_text)
-            dst = Arv::Collection.new(log_coll.manifest_text)
-            dst.cp_r("./", "log for container #{old_container.uuid}", src)
-            manifest = dst.manifest_text
-
-            log_coll.assign_attributes(
-              portable_data_hash: Digest::MD5.hexdigest(manifest) + '+' + manifest.bytesize.to_s,
-              manifest_text: manifest)
-            log_coll.save_with_unique_name!
-            self.log_uuid = log_coll.uuid
-          end
-        end
+      self.container_count += 1
+      return if self.container_uuid_was.nil?
+
+      old_container = Container.find_by_uuid(self.container_uuid_was)
+      return if old_container.nil?
+
+      old_logs = Collection.where(portable_data_hash: old_container.log).first
+      return if old_logs.nil?
+
+      log_coll = self.log_uuid.nil? ? nil : Collection.where(uuid: self.log_uuid).first
+      if self.log_uuid.nil?
+        log_coll = Collection.new(
+          owner_uuid: self.owner_uuid,
+          name: coll_name = "Container log for request #{uuid}",
+          manifest_text: "")
       end
+
+      # copy logs from old container into CR's log collection
+      src = Arv::Collection.new(old_logs.manifest_text)
+      dst = Arv::Collection.new(log_coll.manifest_text)
+      dst.cp_r("./", "log for container #{old_container.uuid}", src)
+      manifest = dst.manifest_text
+
+      log_coll.assign_attributes(
+        portable_data_hash: Digest::MD5.hexdigest(manifest) + '+' + manifest.bytesize.to_s,
+        manifest_text: manifest)
+      log_coll.save_with_unique_name!
+      self.log_uuid = log_coll.uuid
     end
   end
 
index ce81c8ff909196e8c803c95488fa81e84f3ad1d5..57633a31203f6ee0b9c8150324ce93a022f4055d 100644 (file)
@@ -72,11 +72,14 @@ active:
   owner_uuid: zzzzz-tpzed-000000000000000
   uuid: zzzzz-tpzed-xurymjxw79nv3jz
   email: active-user@arvados.local
+  modified_by_client_uuid: zzzzz-ozdt8-teyxzyd8qllg11h
+  modified_by_user_uuid: zzzzz-tpzed-xurymjxw79nv3jz
   first_name: Active
   last_name: User
   identity_url: https://active-user.openid.local
   is_active: true
   is_admin: false
+  modified_at: 2015-03-26 12:34:56.789000000 Z
   username: active
   prefs:
     profile:
index b04bae8647a2417d227e84a793043d1207f875cc..b91910d2d66f89081c5bd8ee44ecca20a03da046 100644 (file)
@@ -261,6 +261,67 @@ class ContainerRequestTest < ActiveSupport::TestCase
     assert_equal log.owner_uuid, project.uuid, "Container log should be copied to #{project.uuid}"
   end
 
+  # This tests bug report #16144
+  test "Request is finalized when its container is completed even when log & output don't exist" do
+    set_user_from_auth :active
+    project = groups(:private)
+    cr = create_minimal_req!(owner_uuid: project.uuid,
+                             priority: 1,
+                             state: "Committed")
+    assert_equal users(:active).uuid, cr.modified_by_user_uuid
+
+    output_pdh = '1f4b0bc7583c2a7f9102c395f4ffc5e3+45'
+    log_pdh = 'fa7aeb5140e2848d39b416daeef4ffc5+45'
+
+    c = act_as_system_user do
+      c = Container.find_by_uuid(cr.container_uuid)
+      c.update_attributes!(state: Container::Locked)
+      c.update_attributes!(state: Container::Running,
+                           output: output_pdh,
+                           log: log_pdh)
+      c
+    end
+
+    cr.reload
+    assert_equal "Committed", cr.state
+
+    act_as_system_user do
+      Collection.where(portable_data_hash: output_pdh).delete_all
+      Collection.where(portable_data_hash: log_pdh).delete_all
+      c.update_attributes!(state: Container::Complete)
+    end
+
+    cr.reload
+    assert_equal "Final", cr.state
+  end
+
+  # This tests bug report #16144
+  test "Can destroy CR even if its container doesn't exist" do
+    set_user_from_auth :active
+    project = groups(:private)
+    cr = create_minimal_req!(owner_uuid: project.uuid,
+                             priority: 1,
+                             state: "Committed")
+    assert_equal users(:active).uuid, cr.modified_by_user_uuid
+
+    c = act_as_system_user do
+      c = Container.find_by_uuid(cr.container_uuid)
+      c.update_attributes!(state: Container::Locked)
+      c.update_attributes!(state: Container::Running)
+      c
+    end
+
+    cr.reload
+    assert_equal "Committed", cr.state
+
+    cr_uuid = cr.uuid
+    act_as_system_user do
+      Container.find_by_uuid(cr.container_uuid).destroy
+      cr.destroy
+    end
+    assert_nil ContainerRequest.find_by_uuid(cr_uuid)
+  end
+
   test "Container makes container request, then is cancelled" do
     set_user_from_auth :active
     cr = create_minimal_req!(priority: 5, state: "Committed", container_count_max: 1)
index 6e89df9a5552cc34bac4d13e3bae4356d6acf48a..65bd8d4cf098a17610953810ab8147f678616aee 100644 (file)
@@ -9,6 +9,7 @@ import (
        "flag"
        "fmt"
        "io"
+       "net/http"
        "os"
 
        "git.arvados.org/arvados.git/lib/config"
@@ -50,10 +51,17 @@ func runCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.W
                options.Dumper = dumper
        }
 
-       // Only pass along the version flag, which gets handled in RunCommand
+       // Drop our custom args that would be rejected by the generic
+       // service.Command
        args = nil
+       dropFlag := map[string]bool{
+               "once":         true,
+               "commit-pulls": true,
+               "commit-trash": true,
+               "dump":         true,
+       }
        flags.Visit(func(f *flag.Flag) {
-               if f.Name == "version" {
+               if !dropFlag[f.Name] {
                        args = append(args, "-"+f.Name, f.Value.String())
                }
        })
@@ -75,6 +83,7 @@ func runCommand(prog string, args []string, stdin io.Reader, stdout, stderr io.W
                        }
 
                        srv := &Server{
+                               Handler:    http.NotFoundHandler(),
                                Cluster:    cluster,
                                ArvClient:  ac,
                                RunOptions: options,
diff --git a/services/keep-balance/main_test.go b/services/keep-balance/main_test.go
new file mode 100644 (file)
index 0000000..a644550
--- /dev/null
@@ -0,0 +1,84 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package main
+
+import (
+       "bytes"
+       "io/ioutil"
+       "net"
+       "net/http"
+       "time"
+
+       check "gopkg.in/check.v1"
+)
+
+var _ = check.Suite(&mainSuite{})
+
+type mainSuite struct{}
+
+func (s *mainSuite) TestVersionFlag(c *check.C) {
+       var stdout, stderr bytes.Buffer
+       runCommand("keep-balance", []string{"-version"}, nil, &stdout, &stderr)
+       c.Check(stderr.String(), check.Equals, "")
+       c.Log(stdout.String())
+}
+
+func (s *mainSuite) TestHTTPServer(c *check.C) {
+       ln, err := net.Listen("tcp", ":0")
+       if err != nil {
+               c.Fatal(err)
+       }
+       _, p, err := net.SplitHostPort(ln.Addr().String())
+       ln.Close()
+       config := "Clusters:\n zzzzz:\n  ManagementToken: abcdefg\n  Services: {Keepbalance: {InternalURLs: {'http://localhost:" + p + "/': {}}}}\n"
+
+       var stdout bytes.Buffer
+       go runCommand("keep-balance", []string{"-config", "-"}, bytes.NewBufferString(config), &stdout, &stdout)
+       done := make(chan struct{})
+       go func() {
+               defer close(done)
+               for {
+                       time.Sleep(time.Second / 10)
+                       req, err := http.NewRequest(http.MethodGet, "http://:"+p+"/metrics", nil)
+                       if err != nil {
+                               c.Fatal(err)
+                               return
+                       }
+                       req.Header.Set("Authorization", "Bearer abcdefg")
+                       resp, err := http.DefaultClient.Do(req)
+                       if err != nil {
+                               c.Logf("error %s", err)
+                               continue
+                       }
+                       defer resp.Body.Close()
+                       if resp.StatusCode != http.StatusOK {
+                               c.Logf("http status %d", resp.StatusCode)
+                               continue
+                       }
+                       buf, err := ioutil.ReadAll(resp.Body)
+                       if err != nil {
+                               c.Logf("read body: %s", err)
+                               continue
+                       }
+                       c.Check(string(buf), check.Matches, `(?ms).*arvados_keepbalance_sweep_seconds_sum.*`)
+                       return
+               }
+       }()
+       select {
+       case <-done:
+       case <-time.After(time.Second):
+               c.Log(stdout.String())
+               c.Fatal("timeout")
+       }
+
+       // Check non-metrics URL that gets passed through to us from
+       // service.Command
+       req, err := http.NewRequest(http.MethodGet, "http://:"+p+"/not-metrics", nil)
+       c.Assert(err, check.IsNil)
+       resp, err := http.DefaultClient.Do(req)
+       c.Check(err, check.IsNil)
+       defer resp.Body.Close()
+       c.Check(resp.StatusCode, check.Equals, http.StatusNotFound)
+}