Merge branch '15888-remove-py2-from-test' into master
authorNico Cesar <nico@nicocesar.com>
Wed, 9 Sep 2020 13:22:20 +0000 (09:22 -0400)
committerNico Cesar <nico@nicocesar.com>
Wed, 9 Sep 2020 13:22:20 +0000 (09:22 -0400)
closes #15888

Arvados-DCO-1.1-Signed-off-by: Nico Cesar <nico@curii.com>

184 files changed:
apps/workbench/app/controllers/work_units_controller.rb
apps/workbench/app/views/users/_virtual_machines.html.erb
apps/workbench/app/views/virtual_machines/webshell.html.erb
apps/workbench/config/initializers/assets.rb
apps/workbench/lib/assets/javascripts/webshell/shell_in_a_box.js [moved from apps/workbench/public/webshell/shell_in_a_box.js with 99% similarity]
apps/workbench/lib/assets/stylesheets/webshell/styles.css [moved from apps/workbench/public/webshell/styles.css with 93% similarity]
apps/workbench/test/integration/anonymous_access_test.rb
apps/workbench/test/integration/work_units_test.rb
build/run-tests.sh
doc/_config.yml
doc/_includes/_0_filter_py.liquid [deleted file]
doc/_includes/_alert-incomplete.liquid [deleted file]
doc/_includes/_alert_stub.liquid [deleted file]
doc/_includes/_arv_copy_expectations.liquid [deleted file]
doc/_includes/_concurrent_hash_script_py.liquid [deleted file]
doc/_includes/_crunch1only_begin.liquid [deleted file]
doc/_includes/_crunch1only_end.liquid [deleted file]
doc/_includes/_example_docker.liquid [deleted file]
doc/_includes/_install_compute_docker.liquid
doc/_includes/_install_rails_reconfigure.liquid [deleted file]
doc/_includes/_install_ruby_and_bundler_sso.liquid [deleted file]
doc/_includes/_install_runit.liquid [deleted file]
doc/_includes/_pipeline_deprecation_notice.liquid [deleted file]
doc/_includes/_run_command_foreach_example.liquid [deleted file]
doc/_includes/_run_command_simple_example.liquid [deleted file]
doc/_includes/_run_md5sum_py.liquid [deleted file]
doc/_includes/_ssh_addkey.liquid
doc/_includes/_tutorial_bwa_sortsam_pipeline.liquid [deleted file]
doc/_includes/_tutorial_cluster_name.liquid [deleted file]
doc/_includes/_tutorial_expectations.liquid
doc/_includes/_tutorial_hash_script_py.liquid [deleted file]
doc/_includes/_tutorial_hello_cwl.liquid [moved from doc/_includes/_install_git.liquid with 50% similarity]
doc/_includes/_tutorial_submit_job.liquid [deleted file]
doc/_includes/_what_is_cwl.liquid
doc/admin/keep-balance.html.textile.liquid
doc/admin/keep-recovering-data.html.textile.liquid [new file with mode: 0644]
doc/admin/recovering-deleted-collections.html.textile.liquid [deleted file]
doc/admin/scoped-tokens.html.textile.liquid
doc/admin/token-expiration-policy.html.textile.liquid [new file with mode: 0644]
doc/admin/user-management-cli.html.textile.liquid
doc/api/methods/collections.html.textile.liquid
doc/architecture/federation.html.textile.liquid
doc/architecture/keep-clients.html.textile.liquid [new file with mode: 0644]
doc/architecture/keep-data-lifecycle.html.textile.liquid [new file with mode: 0644]
doc/architecture/manifest-format.html.textile.liquid [moved from doc/api/storage.html.textile.liquid with 51% similarity]
doc/architecture/storage.html.textile.liquid [new file with mode: 0644]
doc/examples/pipeline_templates/gatk-exome-fq-snp.json [deleted file]
doc/examples/pipeline_templates/rtg-fq-snp.json [deleted file]
doc/examples/ruby/list-active-nodes.rb [deleted file]
doc/images/Keep_manifests.svg
doc/install/arvbox.html.textile.liquid
doc/install/copy_pipeline_from_curoverse.html.textile.liquid [deleted file]
doc/install/crunch2-cloud/install-dispatch-cloud.html.textile.liquid
doc/install/install-shell-server.html.textile.liquid
doc/install/install-webshell.html.textile.liquid
doc/sdk/java-v2/example.html.textile.liquid
doc/sdk/python/arvados-cwl-runner.html.textile.liquid [new file with mode: 0644]
doc/sdk/python/cookbook.html.textile.liquid
doc/sdk/python/events.html.textile.liquid
doc/sdk/ruby/example.html.textile.liquid
doc/start/getting_started/firstpipeline.html.textile.liquid [deleted file]
doc/start/getting_started/nextsteps.html.textile.liquid [deleted file]
doc/start/getting_started/publicproject.html.textile.liquid [deleted file]
doc/start/getting_started/sharedata.html.textile.liquid [deleted file]
doc/start/index.html.textile.liquid [deleted file]
doc/user/cwl/bwa-mem/bwa-mem-input-mixed.yml
doc/user/cwl/bwa-mem/bwa-mem-input-uuids.yml
doc/user/cwl/bwa-mem/bwa-mem.cwl
doc/user/cwl/cwl-run-options.html.textile.liquid
doc/user/cwl/cwl-runner.html.textile.liquid
doc/user/cwl/cwl-style.html.textile.liquid
doc/user/cwl/cwl-versions.html.textile.liquid
doc/user/getting_started/check-environment.html.textile.liquid
doc/user/getting_started/ssh-access-unix.html.textile.liquid
doc/user/getting_started/ssh-access-windows.html.textile.liquid
doc/user/getting_started/vm-login-with-webshell.html.textile.liquid
doc/user/getting_started/workbench.html.textile.liquid
doc/user/index.html.textile.liquid
doc/user/topics/arv-copy.html.textile.liquid
doc/user/topics/arv-docker.html.textile.liquid
doc/user/topics/keep.html.textile.liquid [deleted file]
doc/user/topics/tutorial-gatk-variantfiltration.html.textile.liquid [deleted file]
doc/user/topics/tutorial-job1.html.textile.liquid [deleted file]
doc/user/tutorials/add-new-repository.html.textile.liquid
doc/user/tutorials/git-arvados-guide.html.textile.liquid
doc/user/tutorials/tutorial-keep-collection-lifecycle.html.textile.liquid
doc/user/tutorials/tutorial-keep-get.html.textile.liquid
doc/user/tutorials/tutorial-keep-mount-gnu-linux.html.textile.liquid
doc/user/tutorials/tutorial-keep-mount-os-x.html.textile.liquid
doc/user/tutorials/tutorial-keep-mount-windows.html.textile.liquid
doc/user/tutorials/tutorial-keep.html.textile.liquid
doc/user/tutorials/tutorial-workflow-workbench.html.textile.liquid
doc/user/tutorials/writing-cwl-workflow.html.textile.liquid
docker/jobs/Dockerfile
lib/boot/seed.go
lib/boot/supervisor.go
lib/cloud/azure/azure.go
lib/cloud/azure/azure_test.go
lib/cloud/cloudtest/tester.go
lib/cloud/ec2/ec2.go
lib/cloud/ec2/ec2_test.go
lib/config/cmd.go
lib/config/config.default.yml
lib/config/export.go
lib/config/generated_config.go
lib/controller/federation/conn.go
lib/controller/handler_test.go
lib/controller/integration_test.go
lib/controller/localdb/login_testuser.go
lib/controller/localdb/login_testuser_test.go
lib/controller/rpc/conn.go
lib/controller/semaphore.go
lib/crunchrun/copier.go
lib/crunchrun/crunchrun.go
lib/dispatchcloud/dispatcher_test.go
lib/dispatchcloud/driver.go
lib/dispatchcloud/scheduler/run_queue.go
lib/dispatchcloud/scheduler/run_queue_test.go
lib/dispatchcloud/test/queue.go
lib/dispatchcloud/test/stub_driver.go
lib/dispatchcloud/worker/pool.go
lib/dispatchcloud/worker/pool_test.go
lib/dispatchcloud/worker/verify.go
lib/dispatchcloud/worker/worker.go
sdk/cwl/arvados_cwl/arv-cwl-schema-v1.0.yml
sdk/cwl/arvados_cwl/arv-cwl-schema-v1.1.yml
sdk/cwl/arvados_cwl/arv-cwl-schema-v1.2.yml
sdk/cwl/arvados_cwl/arvworkflow.py
sdk/cwl/arvados_cwl/executor.py
sdk/cwl/fpm-info.sh
sdk/cwl/tests/collection_per_tool/collection_per_tool_packed.cwl
sdk/cwl/tests/test_submit.py
sdk/cwl/tests/wf/expect_upload_packed.cwl [new file with mode: 0644]
sdk/go/arvados/blob_signature.go
sdk/go/arvados/config.go
sdk/go/arvados/keep_service.go
sdk/go/arvadosclient/arvadosclient.go
sdk/go/blockdigest/blockdigest.go
sdk/go/keepclient/root_sorter.go
sdk/go/keepclient/support.go
sdk/python/setup.py
services/api/app/controllers/user_sessions_controller.rb
services/api/app/models/api_client.rb
services/api/app/models/api_client_authorization.rb
services/api/app/models/database_seeds.rb
services/api/config/arvados_config.rb
services/api/lib/current_api_client.rb
services/api/lib/tasks/manage_long_lived_tokens.rake [new file with mode: 0644]
services/api/script/get_anonymous_user_token.rb
services/api/test/fixtures/api_clients.yml
services/api/test/fixtures/workflows.yml
services/api/test/functional/user_sessions_controller_test.rb
services/api/test/integration/api_client_authorizations_api_test.rb
services/api/test/integration/remote_user_test.rb
services/api/test/unit/api_client_test.rb
services/arv-git-httpd/auth_handler_test.go
services/arv-git-httpd/git_handler_test.go
services/arv-git-httpd/integration_test.go
services/fuse/arvados_fuse/fusedir.py
services/fuse/arvados_fuse/unmount.py
services/keep-web/s3.go
services/keep-web/s3_test.go
services/keepproxy/keepproxy_test.go
services/keepstore/proxy_remote_test.go
services/login-sync/bin/arvados-login-sync
services/login-sync/test/test_add_user.rb
services/ws/service_test.go
tools/arvbox/bin/arvbox
tools/arvbox/lib/arvbox/docker/Dockerfile.base
tools/arvbox/lib/arvbox/docker/Dockerfile.demo
tools/arvbox/lib/arvbox/docker/Dockerfile.dev
tools/arvbox/lib/arvbox/docker/api-setup.sh
tools/arvbox/lib/arvbox/docker/cluster-config.sh
tools/arvbox/lib/arvbox/docker/common.sh
tools/arvbox/lib/arvbox/docker/edit_users.py [new file with mode: 0755]
tools/arvbox/lib/arvbox/docker/service/nginx/run
tools/arvbox/lib/arvbox/docker/service/ready/run-service
tools/arvbox/lib/arvbox/docker/service/sso/run [deleted symlink]
tools/arvbox/lib/arvbox/docker/service/sso/run-service [deleted file]
tools/arvbox/lib/arvbox/docker/service/webshell/log/main/.gitstub [moved from tools/arvbox/lib/arvbox/docker/service/sso/log/main/.gitstub with 100% similarity]
tools/arvbox/lib/arvbox/docker/service/webshell/log/run [moved from tools/arvbox/lib/arvbox/docker/service/sso/log/run with 100% similarity]
tools/arvbox/lib/arvbox/docker/service/webshell/run [new file with mode: 0755]
tools/arvbox/lib/arvbox/docker/service/webshell/run-service [new file with mode: 0755]
tools/copy-tutorial/copy-tutorial.sh [new file with mode: 0755]

index 8c4e5e7d9f3f5c64ebb04e4175710c46eefab4ff..ba2f66abe43831052312a7704e7e2d5229fd908c 100644 (file)
@@ -117,6 +117,9 @@ class WorkUnitsController < ApplicationController
               if hint[:keep_cache]
                 keep_cache = hint[:keep_cache]
               end
+              if hint[:acrContainerImage]
+                attrs['container_image'] = hint[:acrContainerImage]
+              end
             end
           end
         end
index e2ce5b39bc1c7e47373c6c8285d8abd8349428d7..57b4d6aa380daed239f919df72cc09daf35cf1f6 100644 (file)
@@ -69,7 +69,7 @@ SPDX-License-Identifier: AGPL-3.0 %>
           <th> Login name </th>
           <th> Command line </th>
           <% if Rails.configuration.Services.WebShell.ExternalURL != URI("") %>
-            <th> Web shell <span class="label label-info">beta</span></th>
+            <th> Web shell</th>
           <% end %>
         </tr>
       </thead>
index 4c63115a1669cb389cf9c97d77fa6fef75a056b3..735583faec8efd2608843be11272bb817ec0ed99 100644 (file)
@@ -30,17 +30,43 @@ SPDX-License-Identifier: AGPL-3.0 %>
 
       function login(username, token) {
         var sh = new ShellInABox("<%= j @webshell_url %>");
-        setTimeout(function() {
-          sh.keysPressed("<%= j params[:login] %>\n");
-          setTimeout(function() {
-            sh.keysPressed("<%= j Thread.current[:arvados_api_token] %>\n");
-            sh.vt100('(sent authentication token)\n');
-          }, 2000);
-        }, 2000);
+
+        var findText = function(txt) {
+          var a = document.querySelectorAll("span.ansi0");
+          for (var i = 0; i < a.length; i++) {
+            if (a[i].textContent.indexOf(txt) > -1) {
+              return true;
+            }
+          }
+          return false;
+        }
+
+        var trySendToken = function() {
+          // change this text when PAM is reconfigured to present a
+          // password prompt that we can wait for.
+          if (findText("assword:")) {
+             sh.keysPressed("<%= j Thread.current[:arvados_api_token] %>\n");
+             sh.vt100('(sent authentication token)\n');
+          } else {
+            setTimeout(trySendToken, 200);
+          }
+        };
+
+        var trySendLogin = function() {
+          if (findText("login:")) {
+            sh.keysPressed("<%= j params[:login] %>\n");
+            // Make this wait shorter when PAM is reconfigured to
+            // present a password prompt that we can wait for.
+            setTimeout(trySendToken, 200);
+          } else {
+            setTimeout(trySendLogin, 200);
+          }
+        };
+
+        trySendLogin();
       }
     // -->
 </script>
-    <link rel="icon" href="<%= asset_path 'favicon.ico' %>" type="image/x-icon">
     <script type="text/javascript" src="<%= asset_path 'webshell/shell_in_a_box.js' %>"></script>
   </head>
   <!-- Load ShellInABox from a timer as Konqueror sometimes fails to
index f02c87b73143fc0e01427ca2ff56e198c5cd2611..2cb9ae908c88ed7e333d16e34864b8cab1567ef0 100644 (file)
@@ -12,4 +12,4 @@ Rails.application.config.assets.version = '1.0'
 
 # Precompile additional assets.
 # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
-# Rails.application.config.assets.precompile += %w( search.js )
+Rails.application.config.assets.precompile += %w( webshell/styles.css webshell/shell_in_a_box.js )
similarity index 99%
rename from apps/workbench/public/webshell/shell_in_a_box.js
rename to apps/workbench/lib/assets/javascripts/webshell/shell_in_a_box.js
index 0c7e800ef8e71aa308fb8d2af6f51b0498d0e8ac..1002f7a9f8846bc39f53a3c559de7bea2dab6e5d 100644 (file)
@@ -1,3 +1,7 @@
+// Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com> All rights reserved.
+//
+// SPDX-License-Identifier: GPL-2.0
+
 // This file contains code from shell_in_a_box.js and vt100.js
 
 
@@ -363,7 +367,7 @@ ShellInABox.prototype.extendContextMenu = function(entries, actions) {
       }
     }
   }
-  
+
 };
 
 ShellInABox.prototype.about = function() {
@@ -738,7 +742,7 @@ VT100.prototype.initializeUserCSSStyles = function() {
         var label                        = userCSSList[i][0];
         var newGroup                     = userCSSList[i][1];
         var enabled                      = userCSSList[i][2];
-      
+
         // Add user style sheet to document
         var style                        = document.createElement('link');
         var id                           = document.createAttribute('id');
@@ -756,7 +760,7 @@ VT100.prototype.initializeUserCSSStyles = function() {
         document.getElementsByTagName('head')[0].appendChild(style);
         style.disabled                   = !enabled;
       }
-    
+
       // Add entry to menu
       if (newGroup || i == userCSSList.length) {
         if (beginOfGroup != 0 && (i - beginOfGroup > 1 || !wasSingleSel)) {
@@ -963,7 +967,7 @@ VT100.prototype.addKeyBinding = function(elem, ch, key, CH, KEY) {
   this.addListener(elem, 'mousedown',
     function(vt100, elem, key) { return function(e) {
       if ((e.which || e.button) == 1) {
-        if (vt100.lastSelectedKey) {       
+        if (vt100.lastSelectedKey) {
           vt100.lastSelectedKey.className= '';
         }
         // Highlight the key while the mouse button is held down.
@@ -1364,7 +1368,7 @@ VT100.prototype.initializeElements = function(container) {
         vt100.indicateSize     = true;
       };
     }(this), 100);
-    this.addListener(window, 'resize', 
+    this.addListener(window, 'resize',
                      function(vt100) {
                        return function() {
                          vt100.hideContextMenu();
@@ -1372,7 +1376,7 @@ VT100.prototype.initializeElements = function(container) {
                          vt100.showCurrentSize();
                         }
                       }(this));
-    
+
     // Hide extra scrollbars attached to window
     document.body.style.margin = '0px';
     try { document.body.style.overflow ='hidden'; } catch (e) { }
@@ -1447,7 +1451,7 @@ VT100.prototype.initializeElements = function(container) {
       // Add a listener for the drop event
       this.addListener(this.scrollable, 'drop', dropEvent(this));
   }
-  
+
   // Initialize the blank terminal window.
   this.currentScreen           = 0;
   this.cursorX                 = 0;
@@ -1514,7 +1518,7 @@ VT100.prototype.repairElements = function(console) {
         for (var span = line.firstChild; span; span = span.nextSibling) {
           var newSpan             = document.createElement(span.tagName);
           newSpan.style.cssText   = span.style.cssText;
-          newSpan.className      = span.className;
+          newSpan.className       = span.className;
           this.setTextContent(newSpan, this.getTextContent(span));
           newLine.appendChild(newSpan);
         }
@@ -1925,7 +1929,7 @@ VT100.prototype.insertBlankLine = function(y, color, style) {
     line                 = document.createElement('div');
     var span             = document.createElement('span');
     span.style.cssText   = style;
-    span.className      = color;
+    span.className       = color;
     this.setTextContent(span, this.spaces(this.terminalWidth));
     line.appendChild(span);
   }
@@ -2062,7 +2066,7 @@ VT100.prototype.putString = function(x, y, text, color, style) {
       this.insertBlankLine(yIdx);
     }
     line                            = console.childNodes[yIdx];
-    
+
     // If necessary, promote blank '\n' line to a <div> tag
     if (line.tagName != 'DIV') {
       var div                       = document.createElement('div');
@@ -2106,7 +2110,7 @@ VT100.prototype.putString = function(x, y, text, color, style) {
           s                        += ' ';
         } while (xPos + s.length < x);
       }
-    
+
       // If styles do not match, create a new <span>
       var del                       = text.length - s.length + x - xPos;
       if (oldColor != color ||
@@ -2165,7 +2169,7 @@ VT100.prototype.putString = function(x, y, text, color, style) {
       }
       this.setTextContent(span, s);
 
-      
+
       // Delete all subsequent <span>'s that have just been overwritten
       sibling                       = span.nextSibling;
       while (del > 0 && sibling) {
@@ -2180,7 +2184,7 @@ VT100.prototype.putString = function(x, y, text, color, style) {
           break;
         }
       }
-      
+
       // Merge <span> with next sibling, if styles are identical
       if (sibling && span.className == sibling.className &&
           span.style.cssText == sibling.style.cssText) {
@@ -2261,7 +2265,7 @@ VT100.prototype.putString = function(x, y, text, color, style) {
                           this.getTextContent(span));
       line.removeChild(sibling);
     }
-    
+
     // Prune white space from the end of the current line
     span                            = line.lastChild;
     while (span &&
@@ -2342,7 +2346,7 @@ VT100.prototype.enableAlternateScreen = function(state) {
     this.resizer();
     return;
   }
-  
+
   // We save the full state of the normal screen, when we switch away from it.
   // But for the alternate screen, no saving is necessary. We always reset
   // it when we switch to it.
@@ -2588,7 +2592,7 @@ VT100.prototype.scrollRegion = function(x, y, w, h, incX, incY,
           while (console.childNodes.length < this.terminalHeight) {
             this.insertBlankLine(this.terminalHeight);
           }
-          
+
           // Add new lines at bottom in order to force scrolling
           for (var i = 0; i < y; i++) {
             this.insertBlankLine(console.childNodes.length, color, style);
@@ -2947,7 +2951,7 @@ VT100.prototype.showContextMenu = function(x, y) {
   this.menu.style.height      =  this.container.offsetHeight + 'px';
   popup.style.left            = '0px';
   popup.style.top             = '0px';
-  
+
   var margin                  = 2;
   if (x + popup.clientWidth >= this.container.offsetWidth - margin) {
     x              = this.container.offsetWidth-popup.clientWidth - margin - 1;
@@ -3035,7 +3039,7 @@ VT100.prototype.handleKey = function(event) {
   ch                                  = this.applyModifiers(ch, event);
 
   // By this point, "ch" is either defined and contains the character code, or
-  // it is undefined and "key" defines the code of a function key 
+  // it is undefined and "key" defines the code of a function key
   if (ch != undefined) {
     this.scrollable.scrollTop         = this.numScrollbackLines *
                                         this.cursorHeight + 1;
@@ -3260,7 +3264,7 @@ VT100.prototype.fixEvent = function(event) {
     case  61: /* = -> + */ u = 61; s =  43; break;
     case  91: /* [ -> { */ u = 91; s = 123; break;
     case  92: /* \ -> | */ u = 92; s = 124; break;
-    case  93: /* ] -> } */ u = 93; s = 125; break; 
+    case  93: /* ] -> } */ u = 93; s = 125; break;
     case  96: /* ` -> ~ */ u = 96; s = 126; break;
 
     case 109: /* - -> _ */ u = 45; s =  95; break;
@@ -3276,7 +3280,7 @@ VT100.prototype.fixEvent = function(event) {
     case 192: /* ` -> ~ */ u = 96; s = 126; break;
     case 219: /* [ -> { */ u = 91; s = 123; break;
     case 220: /* \ -> | */ u = 92; s = 124; break;
-    case 221: /* ] -> } */ u = 93; s = 125; break; 
+    case 221: /* ] -> } */ u = 93; s = 125; break;
     case 222: /* ' -> " */ u = 39; s =  34; break;
     default:                                break;
     }
@@ -3989,7 +3993,7 @@ VT100.prototype.sendControlToPrinter = function(ch) {
           break;
         }
         // Fall through
-      case 3 /* ESgetpars */: 
+      case 3 /* ESgetpars */:
         if (ch == 0x3B /*;*/) {
           this.npar++;
           break;
@@ -4351,7 +4355,7 @@ VT100.prototype.doControl = function(ch) {
       }
       // Fall through
     case 5 /* ESdeviceattr */:
-    case 3 /* ESgetpars */: 
+    case 3 /* ESgetpars */:
 /*;*/ if (ch == 0x3B) {
         this.npar++;
         break;
@@ -4626,7 +4630,7 @@ VT100.prototype.vt100 = function(s) {
        this.utfEnabled && ch >= 128 ||
        !(this.dispCtrl ? this.ctrlAlways : this.ctrlAction)[ch & 0x1F]) &&
       (ch != 0x7F || this.dispCtrl);
-    
+
     if (isNormalCharacter && this.isEsc == 0 /* ESnormal */) {
       if (ch < 256) {
         ch                = this.translate[this.toggleMeta ? (ch | 0x80) : ch];
@@ -4831,5 +4835,3 @@ VT100.prototype.ctrlAlways = [
   false, false, false, false, false, false, false, false,
   false, false, false, true,  false, false, false, false
 ];
-
-
similarity index 93%
rename from apps/workbench/public/webshell/styles.css
rename to apps/workbench/lib/assets/stylesheets/webshell/styles.css
index 3097cb45bf645893f8210d47cff5a5968151fb65..1fc8a67046550ece2da9e5ee079b92136cf6cc02 100644 (file)
@@ -1,9 +1,13 @@
-#vt100 a { 
+/* Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com> All rights reserved.
+   SPDX-License-Identifier: GPL-2.0
+*/
+
+#vt100 a {
   text-decoration:      none;
   color:                inherit;
 }
 
-#vt100 a:hover { 
+#vt100 a:hover {
   text-decoration:      underline;
 }
 
@@ -12,7 +16,7 @@
   z-index:              2;
 }
 
-#vt100 #reconnect input { 
+#vt100 #reconnect input {
   padding:              1ex;
   font-weight:          bold;
   font-size:            x-large;
@@ -29,7 +33,7 @@
   z-index:              2;
 }
 
-#vt100 pre { 
+#vt100 pre {
   margin:               0px;
 }
 
   padding:              1px;
 }
 
-#vt100 #console, #vt100 #alt_console, #vt100 #cursor, #vt100 #lineheight, #vt100 .hidden pre { 
+#vt100 #console, #vt100 #alt_console, #vt100 #cursor, #vt100 #lineheight, #vt100 .hidden pre {
   font-family:          "DejaVu Sans Mono", "Everson Mono", FreeMono, "Andale Mono", monospace;
 }
 
-#vt100 #lineheight { 
+#vt100 #lineheight {
   position:             absolute;
   visibility:           hidden;
 }
@@ -75,7 +79,7 @@
   margin:               -1px;
 }
 
-#vt100 #padding { 
+#vt100 #padding {
   visibility:           hidden;
   width:                1px;
   height:               0px;
@@ -90,7 +94,7 @@
   height:               0px;
 }
 
-#vt100 #menu { 
+#vt100 #menu {
   overflow:             visible;
   position:             absolute;
   z-index:              3;
   position:             absolute;
 }
 
-#vt100 #menu .popup ul { 
+#vt100 #menu .popup ul {
   list-style-type:      none;
   padding:              0px;
   margin:               0px;
   min-width:            10em;
 }
 
-#vt100 #menu .popup li { 
+#vt100 #menu .popup li {
   padding:              3px 0.5ex 3px 0.5ex;
 }
 
   color:                #AAAAAA;
 }
 
-#vt100 #menu .popup hr { 
+#vt100 #menu .popup hr {
   margin:               0.5ex 0px 0.5ex 0px;
 }
 
-#vt100 #menu img { 
+#vt100 #menu img {
   margin-right:         0.5ex;
   width:                1ex;
   height:               1ex;
 #vt100 #scrollable.inverted { color:            #ffffff;
                               background-color: #000000; }
 
-#vt100 #kbd_button { 
+#vt100 #kbd_button {
   float:                left;
   position:             fixed;
   z-index:              0;
   visibility:           hidden;
 }
 
-#vt100 #keyboard .shifted { 
+#vt100 #keyboard .shifted {
   display:              none;
 }
 
     display:            none;
   }
 
-  #vt100 #reconnect, #vt100 #cursor, #vt100 #menu, #vt100 #kbd_button, #vt100 #keyboard { 
+  #vt100 #reconnect, #vt100 #cursor, #vt100 #menu, #vt100 #kbd_button, #vt100 #keyboard {
     visibility:         hidden;
   }
 
-  #vt100 #scrollable { 
+  #vt100 #scrollable {
     overflow:           hidden;
   }
 
-  #vt100 #console, #vt100 #alt_console { 
+  #vt100 #console, #vt100 #alt_console {
     overflow:           hidden;
     width:              1000000ex;
   }
index cbbe28a6f3d1eb2f61ca6a8d11e815aa0702fb3f..e47f1ae2e9ef17f9e7df16d36a351182090e092b 100644 (file)
@@ -197,7 +197,7 @@ class AnonymousAccessTest < ActionDispatch::IntegrationTest
         assert_text 'script version'
         assert_no_selector 'a', text: 'Run this pipeline'
       else
-        within first('tr[data-kind="arvados#workflow"]') do
+        within 'tr[data-kind="arvados#workflow"]', text: "Workflow with default input specifications" do
           click_link 'Show'
         end
 
index 9d4f5905553d96f9fbc3500009dad5d11bdd49b7..43740f192d641772e3515a9ddc2ed92d68b3f5ff 100644 (file)
@@ -283,4 +283,23 @@ class WorkUnitsTest < ActionDispatch::IntegrationTest
     assert_text "This container request was created from the workflow"
     assert_match /Provide a value for .* then click the \"Run\" button to start the workflow/, page.text
   end
+
+  test "create workflow with WorkflowRunnerResources" do
+    visit page_with_token('active', '/workflows/zzzzz-7fd4e-validwithinput3')
+
+    find('a,button', text: 'Run this workflow').click
+
+    # Choose project for the container_request being created
+    within('.modal-dialog') do
+      find('.selectable', text: 'A Project').click
+      find('button', text: 'Choose').click
+    end
+    click_link 'Advanced'
+    click_link("API response")
+    assert_text('"container_image": "arvados/jobs:2.0.4"')
+    assert_text('"vcpus": 2')
+    assert_text('"ram": 1293942784')
+    assert_text('"--collection-cache-size=678"')
+
+  end
 end
index 757f6182c0bdd8ed5078753ee30bc9f38848d8e0..6359fff1defea96c2f2cdbc6094e88f0ef6c0998 100755 (executable)
@@ -162,9 +162,12 @@ temp_preserve=
 
 clear_temp() {
     if [[ -z "$temp" ]]; then
-        # we didn't even get as far as making a temp dir
+        # we did not even get as far as making a temp dir
         :
     elif [[ -z "$temp_preserve" ]]; then
+        # Go creates readonly dirs in the module cache, which cause
+        # "rm -rf" to fail unless we chmod first.
+        chmod -R u+w "$temp"
         rm -rf "$temp"
     else
         echo "Leaving behind temp dirs in $temp"
@@ -538,12 +541,12 @@ setup_ruby_environment() {
 
         tmpdir_gem_home="$(env - PATH="$PATH" HOME="$GEMHOME" gem env gempath | cut -f1 -d:)"
         PATH="$tmpdir_gem_home/bin:$PATH"
-        export GEM_PATH="$tmpdir_gem_home"
+        export GEM_PATH="$tmpdir_gem_home:$(gem env gempath)"
 
         echo "Will install dependencies to $(gem env gemdir)"
-        echo "Will install arvados gems to $tmpdir_gem_home"
+        echo "Will install bundler and arvados gems to $tmpdir_gem_home"
         echo "Gem search path is GEM_PATH=$GEM_PATH"
-        bundle="$(gem env gempath | cut -f1 -d:)/bin/bundle"
+        bundle="$tmpdir_gem_home/bin/bundle"
         (
             export HOME=$GEMHOME
             bundlers="$(gem list --details bundler)"
@@ -990,7 +993,7 @@ test_doc() {
     (
         set -e
         cd "$WORKSPACE/doc"
-        ARVADOS_API_HOST=qr1hi.arvadosapi.com
+        ARVADOS_API_HOST=pirca.arvadosapi.com
         # Make sure python-epydoc is installed or the next line won't
         # do much good!
         PYTHONPATH=$WORKSPACE/sdk/python/ "$bundle" exec rake linkchecker baseurl=file://$WORKSPACE/doc/.site/ arvados_workbench_host=https://workbench.$ARVADOS_API_HOST arvados_api_host=$ARVADOS_API_HOST
index bbab3f307ed88e5eb784c483e692e7173298547a..a85576e450fad92b9af17f3aacdfc990c89305c0 100644 (file)
@@ -27,38 +27,36 @@ navbar:
     - Run a workflow using Workbench:
       - user/getting_started/workbench.html.textile.liquid
       - user/tutorials/tutorial-workflow-workbench.html.textile.liquid
-      - user/composer/composer.html.textile.liquid
+    - Working at the Command Line:
+      - user/getting_started/setup-cli.html.textile.liquid
+      - user/reference/api-tokens.html.textile.liquid
+      - user/getting_started/check-environment.html.textile.liquid
     - Access an Arvados virtual machine:
       - user/getting_started/vm-login-with-webshell.html.textile.liquid
       - user/getting_started/ssh-access-unix.html.textile.liquid
       - user/getting_started/ssh-access-windows.html.textile.liquid
-      - user/getting_started/check-environment.html.textile.liquid
-      - user/reference/api-tokens.html.textile.liquid
     - Working with data sets:
       - user/tutorials/tutorial-keep.html.textile.liquid
       - user/tutorials/tutorial-keep-get.html.textile.liquid
       - user/tutorials/tutorial-keep-mount-gnu-linux.html.textile.liquid
       - user/tutorials/tutorial-keep-mount-os-x.html.textile.liquid
       - user/tutorials/tutorial-keep-mount-windows.html.textile.liquid
-      - user/topics/keep.html.textile.liquid
       - user/tutorials/tutorial-keep-collection-lifecycle.html.textile.liquid
       - user/topics/arv-copy.html.textile.liquid
-      - user/topics/storage-classes.html.textile.liquid
       - user/topics/collection-versioning.html.textile.liquid
-    - Working with git repositories:
-      - user/tutorials/add-new-repository.html.textile.liquid
-      - user/tutorials/git-arvados-guide.html.textile.liquid
-    - Running workflows at the command line:
+      - user/topics/storage-classes.html.textile.liquid
+    - Data Analysis with Workflows:
       - user/cwl/cwl-runner.html.textile.liquid
       - user/cwl/cwl-run-options.html.textile.liquid
-    - Develop an Arvados workflow:
-      - user/tutorials/intro-crunch.html.textile.liquid
       - user/tutorials/writing-cwl-workflow.html.textile.liquid
+      - user/topics/arv-docker.html.textile.liquid
       - user/cwl/cwl-style.html.textile.liquid
-      - user/cwl/federated-workflows.html.textile.liquid
       - user/cwl/cwl-extensions.html.textile.liquid
+      - user/cwl/federated-workflows.html.textile.liquid
       - user/cwl/cwl-versions.html.textile.liquid
-      - user/topics/arv-docker.html.textile.liquid
+    - Working with git repositories:
+      - user/tutorials/add-new-repository.html.textile.liquid
+      - user/tutorials/git-arvados-guide.html.textile.liquid
     - Reference:
       - user/topics/link-accounts.html.textile.liquid
       - user/reference/cookbook.html.textile.liquid
@@ -75,8 +73,9 @@ navbar:
       - sdk/python/example.html.textile.liquid
       - sdk/python/python.html.textile.liquid
       - sdk/python/arvados-fuse.html.textile.liquid
-      - sdk/python/events.html.textile.liquid
+      - sdk/python/arvados-cwl-runner.html.textile.liquid
       - sdk/python/cookbook.html.textile.liquid
+      - sdk/python/events.html.textile.liquid
     - CLI:
       - sdk/cli/install.html.textile.liquid
       - sdk/cli/index.html.textile.liquid
@@ -144,8 +143,14 @@ navbar:
   architecture:
     - Topics:
       - architecture/index.html.textile.liquid
-      - api/storage.html.textile.liquid
+    - Storage in Keep:
+      - architecture/storage.html.textile.liquid
+      - architecture/keep-clients.html.textile.liquid
+      - architecture/keep-data-lifecycle.html.textile.liquid
+      - architecture/manifest-format.html.textile.liquid
+    - Computation with Crunch:
       - api/execution.html.textile.liquid
+    - Other:
       - api/permission-model.html.textile.liquid
       - architecture/federation.html.textile.liquid
   admin:
@@ -162,6 +167,7 @@ navbar:
       - admin/migrating-providers.html.textile.liquid
       - user/topics/arvados-sync-groups.html.textile.liquid
       - admin/scoped-tokens.html.textile.liquid
+      - admin/token-expiration-policy.html.textile.liquid
     - Monitoring:
       - admin/logging.html.textile.liquid
       - admin/metrics.html.textile.liquid
@@ -175,7 +181,7 @@ navbar:
       - admin/logs-table-management.html.textile.liquid
       - admin/workbench2-vocabulary.html.textile.liquid
       - admin/storage-classes.html.textile.liquid
-      - admin/recovering-deleted-collections.html.textile.liquid
+      - admin/keep-recovering-data.html.textile.liquid
     - Cloud:
       - admin/spot-instances.html.textile.liquid
       - admin/cloudtest.html.textile.liquid
diff --git a/doc/_includes/_0_filter_py.liquid b/doc/_includes/_0_filter_py.liquid
deleted file mode 100644 (file)
index 156f0c6..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/env python3
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-# Import the Arvados sdk module
-import arvados
-
-# Get information about the task from the environment
-this_task = arvados.current_task()
-
-this_task_input = arvados.current_job()['script_parameters']['input']
-
-# Create the object access to the collection referred to in the input
-collection = arvados.CollectionReader(this_task_input)
-
-# Create an object to write a new collection as output
-out = arvados.CollectionWriter()
-
-# Create a new file in the output collection
-with out.open('0-filter.txt') as out_file:
-    # Iterate over every input file in the input collection
-    for input_file in collection.all_files():
-        # Output every line in the file that starts with '0'
-        out_file.writelines(line for line in input_file if line.startswith('0'))
-
-# Commit the output to Keep.
-output_locator = out.finish()
-
-# Use the resulting locator as the output for this task.
-this_task.set_output(output_locator)
-
-# Done!
diff --git a/doc/_includes/_alert-incomplete.liquid b/doc/_includes/_alert-incomplete.liquid
deleted file mode 100644 (file)
index 8a62ec7..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-<div class="alert alert-block alert-info">
-  <button type="button" class="close" data-dismiss="alert">&times;</button>
-  <h4>Hi!</h4>
-  <P>This section is incomplete. Please be patient with us as we fill in the blanks &mdash; or <A href="https://dev.arvados.org/projects/arvados/wiki/Documentation#Contributing">contribute to the documentation project.</A></P>
-</div>
diff --git a/doc/_includes/_alert_stub.liquid b/doc/_includes/_alert_stub.liquid
deleted file mode 100644 (file)
index dd56f17..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-<div class="alert alert-block alert-info">
-  <button type="button" class="close" data-dismiss="alert">&times;</button>
-  <h4>Hi!</h4>
-  <p>This section is incomplete. Please be patient with us as we fill in the blanks &mdash; or <A href="https://dev.arvados.org/projects/arvados/wiki/Documentation#Contributing">contribute to the documentation project.</A></p>
-</div>
diff --git a/doc/_includes/_arv_copy_expectations.liquid b/doc/_includes/_arv_copy_expectations.liquid
deleted file mode 100644 (file)
index 2231b06..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-{% include 'notebox_begin' %}
-As stated above, arv-copy is recursive by default and requires a working git repository in the destination cluster. If you do not have a repository created, you can follow the "Adding a new repository":{{site.baseurl}}/user/tutorials/add-new-repository.html page. We will use the *tutorial* repository created in that page as the example.
-
-<br/>In addition, arv-copy requires git when copying to a git repository. Please make sure that git is installed and available.
-
-{% include 'notebox_end' %}
diff --git a/doc/_includes/_concurrent_hash_script_py.liquid b/doc/_includes/_concurrent_hash_script_py.liquid
deleted file mode 100644 (file)
index 399961f..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/usr/bin/env python3
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-import hashlib
-import os
-import arvados
-
-# Jobs consist of one or more tasks.  A task is a single invocation of
-# a crunch script.
-
-# Get the current task
-this_task = arvados.current_task()
-
-# Tasks have a sequence number for ordering.  All tasks
-# with the current sequence number must finish successfully
-# before tasks in the next sequence are started.
-# The first task has sequence number 0
-if this_task['sequence'] == 0:
-    # Get the "input" field from "script_parameters" on the task object
-    job_input = arvados.current_job()['script_parameters']['input']
-
-    # Create a collection reader to read the input
-    cr = arvados.CollectionReader(job_input)
-
-    # Loop over each stream in the collection (a stream is a subset of
-    # files that logically represents a directory)
-    for s in cr.all_streams():
-
-        # Loop over each file in the stream
-        for f in s.all_files():
-
-            # Synthesize a manifest for just this file
-            task_input = f.as_manifest()
-
-            # Set attributes for a new task:
-            # 'job_uuid' the job that this task is part of
-            # 'created_by_job_task_uuid' this task that is creating the new task
-            # 'sequence' the sequence number of the new task
-            # 'parameters' the parameters to be passed to the new task
-            new_task_attrs = {
-                'job_uuid': arvados.current_job()['uuid'],
-                'created_by_job_task_uuid': arvados.current_task()['uuid'],
-                'sequence': 1,
-                'parameters': {
-                    'input':task_input
-                    }
-                }
-
-            # Ask the Arvados API server to create a new task, running the same
-            # script as the parent task specified in 'created_by_job_task_uuid'
-            arvados.api().job_tasks().create(body=new_task_attrs).execute()
-
-    # Now tell the Arvados API server that this task executed successfully,
-    # even though it doesn't have any output.
-    this_task.set_output(None)
-else:
-    # The task sequence was not 0, so it must be a parallel worker task
-    # created by the first task
-
-    # Instead of getting "input" from the "script_parameters" field of
-    # the job object, we get it from the "parameters" field of the
-    # task object
-    this_task_input = this_task['parameters']['input']
-
-    collection = arvados.CollectionReader(this_task_input)
-
-    # There should only be one file in the collection, so get the
-    # first one from the all files iterator.
-    input_file = next(collection.all_files())
-    output_path = os.path.normpath(os.path.join(input_file.stream_name(),
-                                                input_file.name))
-
-    # Everything after this is the same as the first tutorial.
-    digestor = hashlib.new('md5')
-    for buf in input_file.readall():
-        digestor.update(buf)
-
-    out = arvados.CollectionWriter()
-    with out.open('md5sum.txt') as out_file:
-        out_file.write("{} {}\n".format(digestor.hexdigest(), output_path))
-
-    this_task.set_output(out.finish())
-
-# Done!
diff --git a/doc/_includes/_crunch1only_begin.liquid b/doc/_includes/_crunch1only_begin.liquid
deleted file mode 100644 (file)
index 6dc304a..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-{% include 'notebox_begin_warning' %}
-This section assumes the legacy Jobs API is available. Some newer installations have already disabled the Jobs API in favor of the Containers API.
diff --git a/doc/_includes/_crunch1only_end.liquid b/doc/_includes/_crunch1only_end.liquid
deleted file mode 100644 (file)
index a3f2278..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-{% include 'notebox_end' %}
diff --git a/doc/_includes/_example_docker.liquid b/doc/_includes/_example_docker.liquid
deleted file mode 100644 (file)
index 2d6335a..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-{
-    "name": "Example using R in a custom Docker image",
-    "components": {
-        "Rscript": {
-            "script": "run-command",
-            "script_version": "master",
-            "repository": "arvados",
-            "script_parameters": {
-                "command": [
-                    "Rscript",
-                    "$(glob $(file $(myscript))/*.r)",
-                    "$(glob $(dir $(mydata))/*.csv)"
-                ],
-                "myscript": {
-                    "required": true,
-                    "dataclass": "Collection"
-                },
-                "mydata": {
-                    "required": true,
-                    "dataclass": "Collection"
-                }
-            },
-            "runtime_constraints": {
-                "docker_image": "arvados/jobs-with-r"
-            }
-        }
-    }
-}
index 63c54aed72d89df385811d9995bf39970f3b8c09..fd5d88a9c3804349d637b79bc002a55fdd1b025c 100644 (file)
@@ -6,7 +6,7 @@ SPDX-License-Identifier: CC-BY-SA-3.0
 
 h2(#cgroups). Configure Linux cgroups accounting
 
-Linux can report what compute resources are used by processes in a specific cgroup or Docker container.  Crunch can use these reports to share that information with users running compute work.  This can help pipeline authors debug and optimize their workflows.
+Linux can report what compute resources are used by processes in a specific cgroup or Docker container.  Crunch can use these reports to share that information with users running compute work.  This can help workflow authors debug and optimize their workflows.
 
 To enable cgroups accounting, you must boot Linux with the command line parameters @cgroup_enable=memory swapaccount=1@.
 
diff --git a/doc/_includes/_install_rails_reconfigure.liquid b/doc/_includes/_install_rails_reconfigure.liquid
deleted file mode 100644 (file)
index 4687431..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-Now that all your configuration is in place, rerun the {{railspkg}} package configuration to install necessary Ruby Gems and other server dependencies.  On Debian-based systems:
-
-<notextile><pre><code>~$ <span class="userinput">sudo dpkg-reconfigure {{railspkg}}</span>
-</code></pre></notextile>
-
-On Red Hat-based systems:
-
-<notextile><pre><code>~$ <span class="userinput">sudo yum reinstall {{railspkg}}</span>
-</code></pre></notextile>
-
-You only need to do this manual step once, after initial configuration.  When you make configuration changes in the future, you just need to restart Nginx for them to take effect.
\ No newline at end of file
diff --git a/doc/_includes/_install_ruby_and_bundler_sso.liquid b/doc/_includes/_install_ruby_and_bundler_sso.liquid
deleted file mode 100644 (file)
index a8d14ef..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-Ruby 2.3 is recommended; Ruby 2.1 is also known to work.
-
-h4(#rvm). *Option 1: Install with RVM*
-
-<notextile>
-<pre><code><span class="userinput">sudo gpg --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
-\curl -sSL https://get.rvm.io | sudo bash -s stable --ruby=2.3
-</span></code></pre></notextile>
-
-Either log out and log back in to activate RVM, or explicitly load it in all open shells like this:
-
-<notextile>
-<pre><code><span class="userinput">source /usr/local/rvm/scripts/rvm
-</span></code></pre></notextile>
-
-Once RVM is activated in your shell, install Bundler:
-
-<notextile>
-<pre><code>~$ <span class="userinput">gem install bundler</span>
-</code></pre></notextile>
-
-h4(#fromsource). *Option 2: Install from source*
-
-Install prerequisites for Debian 8:
-
-<notextile>
-<pre><code><span class="userinput">sudo apt-get install \
-    bison build-essential gettext libcurl3 libcurl3-gnutls \
-    libcurl4-openssl-dev libpcre3-dev libreadline-dev \
-    libssl-dev libxslt1.1 zlib1g-dev
-</span></code></pre></notextile>
-
-Install prerequisites for CentOS 7:
-
-<notextile>
-<pre><code><span class="userinput">sudo yum install \
-    libyaml-devel glibc-headers autoconf gcc-c++ glibc-devel \
-    patch readline-devel zlib-devel libffi-devel openssl-devel \
-    make automake libtool bison sqlite-devel tar
-</span></code></pre></notextile>
-
-Install prerequisites for Ubuntu 12.04 or 14.04:
-
-<notextile>
-<pre><code><span class="userinput">sudo apt-get install \
-    gawk g++ gcc make libc6-dev libreadline6-dev zlib1g-dev libssl-dev \
-    libyaml-dev libsqlite3-dev sqlite3 autoconf libgdbm-dev \
-    libncurses5-dev automake libtool bison pkg-config libffi-dev curl
-</span></code></pre></notextile>
-
-Build and install Ruby:
-
-<notextile>
-<pre><code><span class="userinput">mkdir -p ~/src
-cd ~/src
-curl -f http://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.3.tar.gz | tar xz
-cd ruby-2.3.3
-./configure --disable-install-rdoc
-make
-sudo make install
-
-sudo -i gem install bundler</span>
-</code></pre></notextile>
diff --git a/doc/_includes/_install_runit.liquid b/doc/_includes/_install_runit.liquid
deleted file mode 100644 (file)
index d5f8341..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-On Debian-based systems:
-
-<notextile>
-<pre><code>~$ <span class="userinput">sudo apt-get install runit</span>
-</code></pre>
-</notextile>
-
-On Red Hat-based systems:
-
-<notextile>
-<pre><code>~$ <span class="userinput">sudo yum install runit</span>
-</code></pre>
-</notextile>
diff --git a/doc/_includes/_pipeline_deprecation_notice.liquid b/doc/_includes/_pipeline_deprecation_notice.liquid
deleted file mode 100644 (file)
index 35c89be..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-{% include 'notebox_begin_warning' %}
-Arvados pipeline templates are deprecated.  The recommended way to develop new workflows for Arvados is using the "Common Workflow Language":{{site.baseurl}}/user/cwl/cwl-runner.html.
-{% include 'notebox_end' %}
diff --git a/doc/_includes/_run_command_foreach_example.liquid b/doc/_includes/_run_command_foreach_example.liquid
deleted file mode 100644 (file)
index 8e3dd71..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-{
-    "name":"run-command example pipeline",
-    "components":{
-        "bwa-mem": {
-            "script": "run-command",
-            "script_version": "master",
-            "repository": "arvados",
-            "script_parameters": {
-                "command": [
-                    "bwa",
-                    "mem",
-                    "-t",
-                    "$(node.cores)",
-                    "$(glob $(dir $(reference_collection))/*.fasta)",
-                    {
-                        "foreach": "read_pair",
-                        "command": "$(read_pair)"
-                    }
-                ],
-                "task.stdout": "$(basename $(glob $(dir $(sample))/*_1.fastq)).sam",
-                "task.foreach": ["sample_subdir", "read_pair"],
-                "reference_collection": {
-                    "required": true,
-                    "dataclass": "Collection"
-                },
-                "sample": {
-                    "required": true,
-                    "dataclass": "Collection"
-                },
-                "sample_subdir": "$(dir $(sample))",
-                "read_pair": {
-                    "value": {
-                        "group": "sample_subdir",
-                        "regex": "(.*)_[12]\\.fastq(\\.gz)?$"
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/doc/_includes/_run_command_simple_example.liquid b/doc/_includes/_run_command_simple_example.liquid
deleted file mode 100644 (file)
index b37ae9a..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-{
-    "name":"run-command example pipeline",
-    "components":{
-         "bwa-mem": {
-            "script": "run-command",
-            "script_version": "master",
-            "repository": "arvados",
-            "script_parameters": {
-                "command": [
-                    "$(dir $(bwa_collection))/bwa",
-                    "mem",
-                    "-t",
-                    "$(node.cores)",
-                    "-R",
-                    "@RG\\\tID:group_id\\\tPL:illumina\\\tSM:sample_id",
-                    "$(glob $(dir $(reference_collection))/*.fasta)",
-                    "$(glob $(dir $(sample))/*_1.fastq)",
-                    "$(glob $(dir $(sample))/*_2.fastq)"
-                ],
-                "reference_collection": {
-                    "required": true,
-                    "dataclass": "Collection"
-                },
-                "bwa_collection": {
-                    "required": true,
-                    "dataclass": "Collection",
-                    "default": "39c6f22d40001074f4200a72559ae7eb+5745"
-                },
-                "sample": {
-                    "required": true,
-                    "dataclass": "Collection"
-                },
-                "task.stdout": "$(basename $(glob $(dir $(sample))/*_1.fastq)).sam"
-            }
-        }
-    }
-}
diff --git a/doc/_includes/_run_md5sum_py.liquid b/doc/_includes/_run_md5sum_py.liquid
deleted file mode 100644 (file)
index 52f61ed..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env python3
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-import arvados
-
-# Automatically parallelize this job by running one task per file.
-arvados.job_setup.one_task_per_input_file(if_sequence=0, and_end_task=True,
-                                          input_as_path=True)
-
-# Get the input file for the task
-input_file = arvados.get_task_param_mount('input')
-
-# Run the external 'md5sum' program on the input file
-stdoutdata, stderrdata = arvados.util.run_command(['md5sum', input_file])
-
-# Save the standard output (stdoutdata) to "md5sum.txt" in the output collection
-out = arvados.CollectionWriter()
-with out.open('md5sum.txt') as out_file:
-    out_file.write(stdoutdata)
-arvados.current_task().set_output(out.finish())
index 7a8a992b68cb84f6312d3e59110838f281771655..de0da6a7673e81612456822cc74ec724536b0934 100644 (file)
@@ -18,6 +18,10 @@ Paste your public key into the text area labeled *Public Key*, and click on the
 
 h1(#login). Using SSH to log into an Arvados VM
 
-To see a list of virtual machines that you have access to and determine the name and login information, click on the dropdown menu icon <span class="fa fa-lg fa-user"></span> <span class="caret"></span> in the upper right corner of the top navigation menu to access the user settings menu and click on the menu item *Virtual machines* to go to the Virtual machines page. This page lists the virtual machines you can access. The *Host name* column lists the name of each available VM.  The *Login name* column will have a list of comma separated values of the form @you@. In this guide the hostname will be *_shell_* and the login will be *_you_*.  Replace these with your hostname and login name as appropriate.
+To see a list of virtual machines that you have access to, click on the dropdown menu icon <span class="fa fa-lg fa-user"></span> <span class="caret"></span> in the upper right corner of the top navigation menu to access the user settings menu, then click on the menu item *Virtual machines* to go to the Virtual machines page.
 
+This page lists the virtual machines you can access. The *Host name* column lists the name of each available VM.  The *Login name* column lists your login name on that VM.  The *Command line* column provides a sample @ssh@ command line.
 
+At the bottom of the page there may be additional instructions for connecting your specific Arvados instance.  If so, follow your site-specific instructions.  If there are no site-specific instructions, you can probably connect directly with @ssh@.
+
+The following are generic instructions.  In the examples the login will be *_you_* and the hostname will be *_shell.ClusterID.example.com_* and .  Replace these with your login name and hostname as appropriate.
diff --git a/doc/_includes/_tutorial_bwa_sortsam_pipeline.liquid b/doc/_includes/_tutorial_bwa_sortsam_pipeline.liquid
deleted file mode 100644 (file)
index 3b39403..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-{
-    "name": "Tutorial align using bwa mem and SortSam",
-    "components": {
-        "bwa-mem": {
-            "script": "run-command",
-            "script_version": "master",
-            "repository": "arvados",
-            "script_parameters": {
-                "command": [
-                    "$(dir $(bwa_collection))/bwa",
-                    "mem",
-                    "-t",
-                    "$(node.cores)",
-                    "-R",
-                    "@RG\\\tID:group_id\\\tPL:illumina\\\tSM:sample_id",
-                    "$(glob $(dir $(reference_collection))/*.fasta)",
-                    "$(glob $(dir $(sample))/*_1.fastq)",
-                    "$(glob $(dir $(sample))/*_2.fastq)"
-                ],
-                "reference_collection": {
-                    "required": true,
-                    "dataclass": "Collection"
-                },
-                "bwa_collection": {
-                    "required": true,
-                    "dataclass": "Collection",
-                    "default": "39c6f22d40001074f4200a72559ae7eb+5745"
-                },
-                "sample": {
-                    "required": true,
-                    "dataclass": "Collection"
-                },
-                "task.stdout": "$(basename $(glob $(dir $(sample))/*_1.fastq)).sam"
-            },
-            "runtime_constraints": {
-                "docker_image": "bcosc/arv-base-java",
-                "arvados_sdk_version": "master"
-            }
-        },
-        "SortSam": {
-            "script": "run-command",
-            "script_version": "847459b3c257aba65df3e0cbf6777f7148542af2",
-            "repository": "arvados",
-            "script_parameters": {
-                "command": [
-                    "java",
-                    "-Xmx4g",
-                    "-Djava.io.tmpdir=$(tmpdir)",
-                    "-jar",
-                    "$(dir $(picard))/SortSam.jar",
-                    "CREATE_INDEX=True",
-                    "SORT_ORDER=coordinate",
-                    "VALIDATION_STRINGENCY=LENIENT",
-                    "INPUT=$(glob $(dir $(input))/*.sam)",
-                    "OUTPUT=$(basename $(glob $(dir $(input))/*.sam)).sort.bam"
-                ],
-                "input": {
-                    "output_of": "bwa-mem"
-                },
-                "picard": {
-                    "required": true,
-                    "dataclass": "Collection",
-                    "default": "88447c464574ad7f79e551070043f9a9+1970"
-                }
-            },
-            "runtime_constraints": {
-                "docker_image": "bcosc/arv-base-java",
-                "arvados_sdk_version": "master"
-            }
-        }
-    }
-}
diff --git a/doc/_includes/_tutorial_cluster_name.liquid b/doc/_includes/_tutorial_cluster_name.liquid
deleted file mode 100644 (file)
index 22fbc46..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-{% include 'notebox_begin' %}
-This tutorial assumes you are using the default Arvados instance, @qr1hi@. If you are using a different instance, replace @qr1hi@ with your instance. See "Accessing Arvados Workbench":{{site.baseurl}}/user/getting_started/workbench.html for more details.
-{% include 'notebox_end' %}
index 6c4fbeb1f3adf32f6ed3a365aeb511a5d4de3d04..09b18f0d4d662ac805f22edfbbe594867a40245d 100644 (file)
@@ -5,5 +5,5 @@ SPDX-License-Identifier: CC-BY-SA-3.0
 {% endcomment %}
 
 {% include 'notebox_begin' %}
-This tutorial assumes that you are logged into an Arvados VM instance (instructions for "Webshell":{{site.baseurl}}/user/getting_started/vm-login-with-webshell.html or "Unix":{{site.baseurl}}/user/getting_started/ssh-access-unix.html#login or "Windows":{{site.baseurl}}/user/getting_started/ssh-access-windows.html#login) or you have installed the Arvados "FUSE Driver":{{site.baseurl}}/sdk/python/arvados-fuse.html and "Python SDK":{{site.baseurl}}/sdk/python/sdk-python.html on your workstation and have a "working environment.":{{site.baseurl}}/user/getting_started/check-environment.html
+This tutorial assumes that you have access to the "Arvados command line tools":/user/getting_started/setup-cli.html and have set the "API token":{{site.baseurl}}/user/reference/api-tokens.html and confirmed a "working environment.":{{site.baseurl}}/user/getting_started/check-environment.html .
 {% include 'notebox_end' %}
diff --git a/doc/_includes/_tutorial_hash_script_py.liquid b/doc/_includes/_tutorial_hash_script_py.liquid
deleted file mode 100644 (file)
index f9cbde1..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/usr/bin/env python3
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-import hashlib      # Import the hashlib module to compute MD5.
-import os           # Import the os module for basic path manipulation
-import arvados      # Import the Arvados sdk module
-
-# Automatically parallelize this job by running one task per file.
-# This means that if the input consists of many files, each file will
-# be processed in parallel on different nodes enabling the job to
-# be completed quicker.
-arvados.job_setup.one_task_per_input_file(if_sequence=0, and_end_task=True,
-                                          input_as_path=True)
-
-# Get object representing the current task
-this_task = arvados.current_task()
-
-# Create the message digest object that will compute the MD5 hash
-digestor = hashlib.new('md5')
-
-# Get the input file for the task
-input_id, input_path = this_task['parameters']['input'].split('/', 1)
-
-# Open the input collection
-input_collection = arvados.CollectionReader(input_id)
-
-# Open the input file for reading
-with input_collection.open(input_path) as input_file:
-    for buf in input_file.readall():  # Iterate the file's data blocks
-        digestor.update(buf)          # Update the MD5 hash object
-
-# Write a new collection as output
-out = arvados.CollectionWriter()
-
-# Write an output file with one line: the MD5 value and input path
-with out.open('md5sum.txt') as out_file:
-    out_file.write("{} {}/{}\n".format(digestor.hexdigest(), input_id,
-                                       os.path.normpath(input_path)))
-
-# Commit the output to Keep.
-output_locator = out.finish()
-
-# Use the resulting locator as the output for this task.
-this_task.set_output(output_locator)
-
-# Done!
similarity index 50%
rename from doc/_includes/_install_git.liquid
rename to doc/_includes/_tutorial_hello_cwl.liquid
index d60379fa30bb73ed515413a640d474c82f41c2cf..ae1ec80ab2026289b318b086b36b56f86f330c09 100644 (file)
@@ -1,9 +1,11 @@
+#!/usr/bin/env cwl-runner
 {% comment %}
 Copyright (C) The Arvados Authors. All rights reserved.
 
 SPDX-License-Identifier: CC-BY-SA-3.0
 {% endcomment %}
-
-{% include 'notebox_begin' %}
-The Arvados API and Git servers require Git 1.7.10 or later.
-{% include 'notebox_end' %}
+cwlVersion: v1.0
+class: CommandLineTool
+inputs: []
+outputs: []
+arguments: ["echo", "hello world!"]
diff --git a/doc/_includes/_tutorial_submit_job.liquid b/doc/_includes/_tutorial_submit_job.liquid
deleted file mode 100644 (file)
index 548a619..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-{
-  "name":"My md5 pipeline",
-  "components":{
-    "do_hash":{
-      "repository":"$USER/$USER",
-      "script":"hash.py",
-      "script_version":"master",
-      "runtime_constraints":{
-        "docker_image":"arvados/jobs"
-      },
-      "script_parameters":{
-        "input":{
-          "required": true,
-          "dataclass": "Collection"
-        }
-      }
-    }
-  }
-}
index fea11b3a32afe9df18afed0f2aaf135c5a46c8a4..7fbe7b815675bb85658ad810f506a9e9169f0106 100644 (file)
@@ -4,4 +4,4 @@ Copyright (C) The Arvados Authors. All rights reserved.
 SPDX-License-Identifier: CC-BY-SA-3.0
 {% endcomment %}
 
-The "Common Workflow Language (CWL)":http://commonwl.org is a multi-vendor open standard for describing analysis tools and workflows that are portable across a variety of platforms.  CWL is the primary way to develop and run workflows for Arvados.  Arvados supports versions "v1.0":http://commonwl.org/v1.0 and "v1.1":http://commonwl.org/v1.1 of the CWL specification.
+The "Common Workflow Language (CWL)":http://commonwl.org is a multi-vendor open standard for describing analysis tools and workflows that are portable across a variety of platforms.  CWL is the primary way to develop and run workflows for Arvados.  Arvados supports versions "v1.0":http://commonwl.org/v1.0 , "v1.1":http://commonwl.org/v1.1 and "v1.2":http://commonwl.org/v1.2 of the CWL standard.
index 5af0a2688033d311a6c8c7b96a39719088a01d7a..2785930de82fc30eb7f07ea359e10f0822181631 100644 (file)
@@ -20,7 +20,7 @@ The keep-balance service determines which blocks are candidates for deletion and
 
 If keep-balance instructs keepstore to trash a block which is older than @BlobSigningTTL@, and @BlobTrashLifetime@ is non-zero, the block will be moved to "trash".  A block which is in the trash is no longer accessible by read requests, but has not yet been permanently deleted.  Blocks which are in the trash may be recovered using the "untrash" API endpoint.  Blocks are permanently deleted after they have been in the trash for the duration in @BlobTrashLifetime@.
 
-Keep-balance is also responsible for balancing the distribution of blocks across keepstore servers by asking servers to pull blocks from other servers (as determined by their "storage class":{{site.baseurl}}/admin/storage-classes.html and "rendezvous hashing order":{{site.baseurl}}/api/storage.html).  Pulling a block makes a copy.  If a block is overreplicated (i.e. there are excess copies) after pulling, it will be subsequently trashed and deleted on the original server, subject to @BlobTrash@ and @BlobTrashLifetime@ settings.
+Keep-balance is also responsible for balancing the distribution of blocks across keepstore servers by asking servers to pull blocks from other servers (as determined by their "storage class":{{site.baseurl}}/admin/storage-classes.html and "rendezvous hashing order":{{site.baseurl}}/architecture/keep-clients.html#rendezvous).  Pulling a block makes a copy.  If a block is overreplicated (i.e. there are excess copies) after pulling, it will be subsequently trashed and deleted on the original server, subject to @BlobTrash@ and @BlobTrashLifetime@ settings.
 
 h3. Scanning
 
@@ -40,4 +40,4 @@ For configuring resource usage tuning and lost block reporting, please see the @
 
 h3. Limitations
 
-Keep-balance does not attempt to discover whether committed pull and trash requests ever get carried out -- only that they are accepted by the Keep services. If some services are full, new copies of under-replicated blocks might never get made, only repeatedly requested.
\ No newline at end of file
+Keep-balance does not attempt to discover whether committed pull and trash requests ever get carried out -- only that they are accepted by the Keep services. If some services are full, new copies of under-replicated blocks might never get made, only repeatedly requested.
diff --git a/doc/admin/keep-recovering-data.html.textile.liquid b/doc/admin/keep-recovering-data.html.textile.liquid
new file mode 100644 (file)
index 0000000..3a9f51e
--- /dev/null
@@ -0,0 +1,109 @@
+---
+layout: default
+navsection: admin
+title: "Recovering data"
+...
+
+{% comment %}
+Copyright (C) The Arvados Authors. All rights reserved.
+
+SPDX-License-Identifier: CC-BY-SA-3.0
+{% endcomment %}
+
+Arvados has several features to prevent accidental loss or deletion of data, but accidents can happen. This page lays out the options to recover deleted or overwritten collections.
+
+For more detail on the data lifecycle in Arvados, see the "Data lifecycle":{{ site.baseurl }}/architecture/keep-data-lifecycle.html page.
+
+h2(#check_the_trash). Check the trash
+
+When a collection is deleted, it is moved to the trash. It will remain there for the duration of @Collections.DefaultTrashLifetime@, and it can be untrashed via workbench or with the cli tools, as described in "Recovering trashed collections":{{ site.baseurl }}/user/tutorials/tutorial-keep-collection-lifecycle.html#trash-recovery.
+
+h2(#check_other_collections). Check for other collections with the same PDH
+
+Multiple collections may share a _portable data hash_, i.e. have the same contents. If another collection exists with the same portable data hash, recovering data is not necessary, everything is still stored in Keep. A new copy of the collection can be made to make the data available in the correct project and with the correct permissions.
+
+h2(#check_collection_versioning). Consider collection versioning
+
+Arvados supports collection versioning. If it has been "enabled":{{ site.baseurl }}/admin/collection-versioning.html on your cluster, the deleted collection may be recoverable from an older version. See "Using collection versioning":{{ site.baseurl }}/user/topics/collection-versioning.html for details.
+
+h2(#recover_collection). Recovering collections
+
+When all the above options fail, it may still be possible to recover a collection that has been deleted.
+
+To recover a collection the manifest is required. Arvados has a built-in audit log, which consists of a row added to the "logs" table in the PostgreSQL database each time an Arvados object is created, modified, or deleted. Collection manifests are included, unless they are listed in the @AuditLogs.UnloggedAttributes@ configuration parameter. The audit log is retained for up to @AuditLogs.MaxAge@.
+
+In some cases, it is possible to recover files that have been lost by modifying or deleting a collection.
+
+Possibility of recovery depends on many factors, including:
+* Whether the collection manifest is still available, e.g., in an audit log entry
+* Whether the data blocks are also referenced by other collections
+* Whether the data blocks have been unreferenced long enough to be marked for deletion/trash by keep-balance
+* Blob signature TTL, trash lifetime, trash check interval, and other config settings
+
+To attempt recovery of a previous version of a deleted/modified collection, use the @arvados-server recover-collection@ command. It should be run on one of your server nodes where the @arvados-server@ package is installed and the @/etc/arvados/config.yml@ file is up to date.
+
+Specify the collection you want to recover by passing either the UUID of an audit log entry, or a file containing the manifest.
+
+If recovery is successful, the @recover-collection@ program saves the recovered data a new collection belonging to the system user, and prints the new collection's UUID on stdout.
+
+<pre>
+# arvados-server recover-collection 9tee4-57u5n-nb5awmk1pahac2t
+INFO[2020-06-05T19:52:29.557761245Z] loaded log entry                              logged_event_time="2020-06-05 16:48:01.438791 +0000 UTC" logged_event_type=update old_collection_uuid=9tee4-4zz18-1ex26g95epmgw5w src=9tee4-57u5n-nb5awmk1pahac2t
+INFO[2020-06-05T19:52:29.642145127Z] recovery succeeded                            UUID=9tee4-4zz18-5trfp4k4xxg97f1 src=9tee4-57u5n-nb5awmk1pahac2t
+9tee4-4zz18-5trfp4k4xxg97f1
+INFO[2020-06-05T19:52:29.644699436Z] exiting
+</pre>
+
+In this example, the original data has been restored and saved in a new collection with UUID @9tee4-4zz18-5trfp4k4xxg97f1@.
+
+For more options, run @arvados-server recover-collection -help@.
+
+h2(#untrashing_lost_blocks). Untrashing lost blocks
+
+In some cases it is possible to recover data blocks that were trashed erroneously by @keep-balance@ (e.g. due to an install/config error).
+
+If you suspect blocks have been trashed erroneously, you should immediately:
+
+* On all keepstore servers: set @BlobTrashCheckInterval@ to a long time like 2400h
+* On all keepstore servers: restart keepstore
+* Stop the keep-balance service
+
+When you think you have corrected the underlying problem, you should:
+
+* Set @Collections.BlobMissingReport@ to a suitable value (perhaps "/tmp/keep-balance-lost-blocks.txt").
+* Start @keep-balance@
+* After @keep-balance@ completes its first sweep, inspect /tmp/keep-balance-lost-blocks.txt. If it's not empty, you can request all keepstores to untrash any blocks that are still recoverable with a script like this:
+
+<notextile>
+<pre><code>
+#!/bin/bash
+set -e
+
+# see Client.AuthToken in /etc/arvados/keep-balance/keep-balance.yml
+token=xxxxxxx-your-system-auth-token-xxxxxxx
+
+# all keep server hostnames
+hosts=(keep0 keep1 keep2 keep3 keep4 keep5)
+
+while read hash pdhs; do
+  echo "${hash}"
+  for h in ${hosts[@]}; do
+    if curl -fgs -H "Authorization: Bearer $token" -X PUT "http://${h}:25107/untrash/$hash"; then
+      echo "${hash} ok ${host}"
+    fi
+  done
+done < /tmp/keep-balance-lost-blocks.txt
+</code>
+</pre>
+</notextile>
+
+Any blocks which were successfully untrashed can be removed from the list of blocks and collections which need to be recovered.
+
+h2(#regenerating_lost_blocks). Regenerating lost blocks
+
+For blocks which were trashed long enough ago that they've been deleted, it may be possible to regenerate them by rerunning the workflows which generated them. To do this, the process is:
+
+* Delete the affected collections so that job reuse doesn't attempt to reuse them (it's likely that if one block is missing, they all are, so they're unlikely to contain any useful data)
+* Resubmit any container requests for which you want the output collections regenerated
+
+The Arvados repository contains a tool that can be used to generate a report to help with this task at "arvados/tools/keep-xref/keep-xref.py":https://github.com/arvados/arvados/blob/master/tools/keep-xref/keep-xref.py
diff --git a/doc/admin/recovering-deleted-collections.html.textile.liquid b/doc/admin/recovering-deleted-collections.html.textile.liquid
deleted file mode 100644 (file)
index 59c576c..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
----
-layout: default
-navsection: admin
-title: Recovering deleted collections
-...
-
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-In some cases, it is possible to recover files that have been lost by modifying or deleting a collection.
-
-Possibility of recovery depends on many factors, including:
-* Whether the collection manifest is still available, e.g., in an audit log entry
-* Whether the data blocks are also referenced by other collections
-* Whether the data blocks have been unreferenced long enough to be marked for deletion/trash by keep-balance
-* Blob signature TTL, trash lifetime, trash check interval, and other config settings
-
-To attempt recovery of a previous version of a deleted/modified collection, use the @arvados-server recover-collection@ command. It should be run on one of your server nodes where the @arvados-server@ package is installed and the @/etc/arvados/config.yml@ file is up to date.
-
-Specify the collection you want to recover by passing either the UUID of an audit log entry, or a file containing the manifest.
-
-If recovery is successful, the @recover-collection@ program saves the recovered data a new collection belonging to the system user, and prints the new collection's UUID on stdout.
-
-<pre>
-# arvados-server recover-collection 9tee4-57u5n-nb5awmk1pahac2t
-INFO[2020-06-05T19:52:29.557761245Z] loaded log entry                              logged_event_time="2020-06-05 16:48:01.438791 +0000 UTC" logged_event_type=update old_collection_uuid=9tee4-4zz18-1ex26g95epmgw5w src=9tee4-57u5n-nb5awmk1pahac2t
-INFO[2020-06-05T19:52:29.642145127Z] recovery succeeded                            UUID=9tee4-4zz18-5trfp4k4xxg97f1 src=9tee4-57u5n-nb5awmk1pahac2t
-9tee4-4zz18-5trfp4k4xxg97f1
-INFO[2020-06-05T19:52:29.644699436Z] exiting
-</pre>
-
-In this example, the original data has been restored and saved in a new collection with UUID @9tee4-4zz18-5trfp4k4xxg97f1@.
-
-For more options, run @arvados-server recover-collection -help@.
index 5bad5f25b3edff7e6ae7321e3742e8d4039ff149..18578a78d683cb02d58c836b03b36362fbabc4bf 100644 (file)
@@ -4,6 +4,12 @@ navsection: admin
 title: Securing API access with scoped tokens
 ...
 
+{% comment %}
+Copyright (C) The Arvados Authors. All rights reserved.
+
+SPDX-License-Identifier: CC-BY-SA-3.0
+{% endcomment %}
+
 By default, Arvados API tokens grant unlimited access to a user account, and admin account tokens have unlimited access to the whole system.  If you want to grant restricted access to a user account, you can create a "scoped token" which is an Arvados API token which is limited to accessing specific APIs.
 
 One use of token scopes is to grant access to data, such as a collection, to users who do not have an Arvados accounts on your cluster.  This is done by creating scoped token that only allows getting a specific record.  An example of this is "creating a collection sharing link.":{{site.baseurl}}/sdk/python/cookbook.html#sharing_link
diff --git a/doc/admin/token-expiration-policy.html.textile.liquid b/doc/admin/token-expiration-policy.html.textile.liquid
new file mode 100644 (file)
index 0000000..f5ee61b
--- /dev/null
@@ -0,0 +1,62 @@
+---
+layout: default
+navsection: admin
+title: Setting token expiration policy
+...
+
+{% comment %}
+Copyright (C) The Arvados Authors. All rights reserved.
+
+SPDX-License-Identifier: CC-BY-SA-3.0
+{% endcomment %}
+
+When a user logs in to Workbench, they receive a newly created token that grants access to the Arvados API on behalf of that user.  By default, this token does not expire until the user explicitly logs off.
+
+Security policies, such as for GxP Compliance, may require that tokens expire by default in order to limit the risk associated with a token being leaked.
+
+The @Login.TokenLifetime@ configuration enables the administrator to set a expiration lifetime for tokens granted through the login flow.
+
+h2. Setting token expiration
+
+Suppose that the organization's security policy requires that user sessions should not be valid for more than 12 hours, the cluster configuration should be set like the following:
+
+<pre>
+Clusters:
+  zzzzz:
+    ...
+    Login:
+      TokenLifetime: 12h
+    ...
+</pre>
+
+With this configuration, users will have to re-login every 12 hours.
+
+When this configuration is active, the workbench client will also be "untrusted" by default.  This means tokens issued to workbench cannot be used to list other tokens issued to the user, and cannot be used to grant new tokens.  This stops an attacker from leveraging a leaked token to aquire other tokens.
+
+The default @TokenLifetime@ is zero, which disables this feature.
+
+h2. Applying policy to existing tokens
+
+If you have an existing Arvados installation and want to set a token lifetime policy, there may be user tokens already granted.  The administrator can use the following @rake@ tasks to enforce the new policy.
+
+The @db:check_long_lived_tokens@ task will list which users have tokens with no expiration date.
+
+<notextile>
+<pre><code># <span class="userinput">bundle exec rake db:check_long_lived_tokens</span>
+Found 6 long-lived tokens from users:
+user2,user2@example.com,zzzzz-tpzed-5vzt5wc62k46p6r
+admin,admin@example.com,zzzzz-tpzed-6drplgwq9nm5cox
+user1,user1@example.com,zzzzz-tpzed-ftz2tfurbpf7xox
+</code></pre>
+</notextile>
+
+To apply the new policy to existing tokens, use the @db:fix_long_lived_tokens@ task.
+
+<notextile>
+<pre><code># <span class="userinput">bundle exec rake db:fix_long_lived_tokens</span>
+Setting token expiration to: 2020-08-25 03:30:50 +0000
+6 tokens updated.
+</code></pre>
+</notextile>
+
+NOTE: These rake tasks adjust the expiration of all tokens except those belonging to the system root user (@zzzzz-tpzed-000000000000000@).  If you have tokens used by automated service accounts that need to be long-lived, you can "create tokens that don't expire using the command line":user-management-cli.html#create-token .
index 33969ea8f85c6b8786e3f88e12f4a88270ccef3a..6892176604a91346fc64eea037820f0c1262e3e6 100644 (file)
@@ -16,7 +16,7 @@ ARVADOS_API_HOST={{ site.arvados_api_host }}
 ARVADOS_API_TOKEN=1234567890qwertyuiopasdfghjklzxcvbnm1234567890zzzz
 </pre>
 
-In these examples, @x1u39-tpzed-3kz0nwtjehhl0u4@ is the sample user account.  Replace with the uuid of the user you wish to manipulate.
+In these examples, @zzzzz-tpzed-3kz0nwtjehhl0u4@ is the sample user account.  Replace with the uuid of the user you wish to manipulate.
 
 See "user management":{{site.baseurl}}/admin/activation.html for an overview of how to use these commands.
 
@@ -24,28 +24,68 @@ h3. Setup a user
 
 This creates a default git repository and VM login.  Enables user to self-activate using Workbench.
 
-<pre>
-arv user setup --uuid x1u39-tpzed-3kz0nwtjehhl0u4
-</pre>
+<notextile>
+<pre><code>$ <span class="userinput">arv user setup --uuid zzzzz-tpzed-3kz0nwtjehhl0u4</span>
+</code></pre>
+</notextile>
+
 
 h3. Deactivate user
 
-<pre>
-arv user unsetup --uuid x1u39-tpzed-3kz0nwtjehhl0u4
-</pre>
+<notextile>
+<pre><code>$ <span class="userinput">arv user unsetup --uuid zzzzz-tpzed-3kz0nwtjehhl0u4</span>
+</code></pre>
+</notextile>
+
 
 When deactivating a user, you may also want to "reassign ownership of their data":{{site.baseurl}}/admin/reassign-ownership.html .
 
 h3. Directly activate user
 
-<pre>
-arv user update --uuid "x1u39-tpzed-3kz0nwtjehhl0u4" --user '{"is_active":true}'
-</pre>
+<notextile>
+<pre><code>$ <span class="userinput">arv user update --uuid "zzzzz-tpzed-3kz0nwtjehhl0u4" --user '{"is_active":true}'</span>
+</code></pre>
+</notextile>
+
+Note: this bypasses user agreements checks, and does not set up the user with a default git repository or VM login.
 
-Note this bypasses user agreements checks, and does not set up the user with a default git repository or VM login.
+h3(#create-token). Create a token for a user
 
+As an admin, you can create tokens for other users.
+
+<notextile>
+<pre><code>$ <span class="userinput">arv api_client_authorization create --api-client-authorization '{"owner_uuid": "zzzzz-tpzed-fr97h9t4m5jffxs"}'</span>
+{
+ "href":"/api_client_authorizations/zzzzz-gj3su-yyyyyyyyyyyyyyy",
+ "kind":"arvados#apiClientAuthorization",
+ "etag":"9yk144t0v6cvyp0342exoh2vq",
+ "uuid":"zzzzz-gj3su-yyyyyyyyyyyyyyy",
+ "owner_uuid":"zzzzz-tpzed-fr97h9t4m5jffxs",
+ "created_at":"2020-03-12T20:36:12.517375422Z",
+ "modified_by_client_uuid":null,
+ "modified_by_user_uuid":null,
+ "modified_at":null,
+ "user_id":3,
+ "api_client_id":7,
+ "api_token":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+ "created_by_ip_address":null,
+ "default_owner_uuid":null,
+ "expires_at":null,
+ "last_used_at":null,
+ "last_used_by_ip_address":null,
+ "scopes":["all"]
+}
+</code></pre>
+</notextile>
+
+
+To get the token string, combine the values of @uuid@ and @api_token@ in the form "v2/$uuid/$api_token".  In this example the string that goes in @ARVADOS_API_TOKEN@ would be:
+
+<pre>
+ARVADOS_API_TOKEN=v2/zzzzz-gj3su-yyyyyyyyyyyyyyy/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+</pre>
 
-h2. Permissions
+h2. Adding Permissions
 
 h3. VM login
 
index 9f60e2ff1699d50415dfbc73397418c855a72daa..4372cc2f5e7dd17bd2c662bf7224cf6fdd5d4c6f 100644 (file)
@@ -19,7 +19,7 @@ Example UUID: @zzzzz-4zz18-0123456789abcde@
 
 h2. Resource
 
-Collections describe sets of files in terms of data blocks stored in Keep.  See "storage in Keep":{{site.baseurl}}/api/storage.html for details.
+Collections describe sets of files in terms of data blocks stored in Keep.  See "Keep - Content-Addressable Storage":{{site.baseurl}}/architecture/storage.html for details.
 
 Each collection has, in addition to the "Common resource fields":{{site.baseurl}}/api/resources.html:
 
index e2b80de7078b3b5cbb28b4ef7154a90e5df57b52..78aa4e16deff226b1dfa1d5f7739826c6cb0a823 100644 (file)
@@ -26,7 +26,7 @@ Clusters are identified by a five-digit alphanumeric id (numbers and lowercase l
 
 Cluster identifiers are mapped API server hosts one of two ways:
 
-* Through DNS resolution, under the @arvadosapi.com@ domain.  For example, the API server for the cluster @qr1hi@ can be found at @qr1hi.arvadosapi.com@.  To register a cluster id for free under @arvadosapi.com@, contact "info@curii.com":mailto:info@curii.com
+* Through DNS resolution, under the @arvadosapi.com@ domain.  For example, the API server for the cluster @pirca@ can be found at @pirca.arvadosapi.com@.  To register a cluster id for free under @arvadosapi.com@, contact "info@curii.com":mailto:info@curii.com
 * Through explicit configuration:
 
 The @RemoteClusters@ section of @/etc/arvados/config.yml@ (for arvados-controller)
@@ -82,23 +82,6 @@ In the REST API, POST requests create new records, so there is no uuid to use fo
 
 h3. Collections and Keep block retrieval
 
-Each collection record has @manifest_text@, which describes how to reassemble keep blocks into files as described in the "Storage in Keep.":{{site.baseurl}}/api/storage.html.  Each block identifier in the manifest has an added signature which is used to confirm permission to read the block.  To read a block from a keepstore server, the client must provide the block identifier, the signature, and the same API token used to retrieve the collection record.
+Each collection record has @manifest_text@, which describes how to reassemble keep blocks into files as described in the "Manifest format":{{site.baseurl}}/architecture/manifest-format.html.  Each block identifier in the manifest has an added signature which is used to confirm permission to read the block.  To read a block from a keepstore server, the client must provide the block identifier, the signature, and the same API token used to retrieve the collection record.
 
-When a collection record is returned through a federation request, the keep blocks listed in the manifest may not be available on the local cluster, and the keep block signatures returned by the remote cluster are not valid for the local cluster.  To solve this, arvados-controller rewrites the signatures in the manifest to "remote cluster" signatures.
-
-A local signature comes after the block identifier and block size, and starts with @+A@:
-
-<code>930625b054ce894ac40596c3f5a0d947+33+A1f27a35dd9af37191d63ad8eb8985624451e7b79@5835c8bc</code>
-
-A remote cluster signature starts with @+R@, then the cluster id of the cluster it originated from (@zzzzz@ in this example), a dash, and then the original signature:
-
-<code>930625b054ce894ac40596c3f5a0d947+33+Rzzzzz-1f27a35dd9af37191d63ad8eb8985624451e7b79@5835c8bc</code>
-
-When the client provides a remote-signed block locator to keepstore, the keepstore proxies the request to the remote cluster.
-
-# keepstore determines the cluster id to contact from the first part of the @+R@ signature
-# creates a salted token using the API token and cluster id
-# contacts the "accessible" endpoint on the remote cluster to determine the remote cluster's keepstore or keepproxy hosts
-# converts the remote signature @+R@ back to a local signature @+A@
-# contacts the remote keepstore or keepproxy host and requests the block using the local signature
-# returns the block contents back to the client
+See "Federation signatures":{{site.baseurl}}/architecture/manifest-format.html#federationsignatures for details on how federation affects block signatures.
diff --git a/doc/architecture/keep-clients.html.textile.liquid b/doc/architecture/keep-clients.html.textile.liquid
new file mode 100644 (file)
index 0000000..31e549f
--- /dev/null
@@ -0,0 +1,39 @@
+---
+layout: default
+navsection: architecture
+title: Keep clients
+...
+{% comment %}
+Copyright (C) The Arvados Authors. All rights reserved.
+
+SPDX-License-Identifier: CC-BY-SA-3.0
+{% endcomment %}
+
+Keep clients are applications such as @arv-get@, @arv-put@ and @arv-mount@ which store and retrieve data from Keep.  In doing so, these programs interact with both the API server (which stores file metadata in the form of @collection@ objects) and individual @keepstore@ servers (which store the actual data blocks).
+
+!(full-width){{site.baseurl}}/images/Keep_reading_writing_block.svg!
+
+h2. Storing a file
+
+# The client discovers keep servers (or proxies) using the @accessible@ method on "keep_services":{{site.baseurl}}/api/methods/keep_services.html
+# Data is split into 64 MiB blocks and the MD5 hash is computed for each block.
+# The client uploads each block to one or more Keep servers, based on the number of desired replicas.  The priority order is determined using rendezvous hashing, described below.
+# The Keep server returns a block locator (the MD5 sum of the block) and a "signed token" which the client can use as proof of knowledge for the block.
+# The client constructs a @manifest@ which lists the blocks by MD5 hash and how to reassemble them into the original files.
+# The client creates a "collection":{{site.baseurl}}/api/methods/collections.html and provides the @manifest_text@
+# The API server accepts the collection after validating the signed tokens (proof of knowledge) for each block.
+
+h2. Fetching a file
+
+# The client requests a @collection@ object including @manifest_text@ from the APIs server
+# The server adds "token signatures" to the @manifest_text@ and returns it to the client.
+# The client discovers keep servers (or proxies) using the @accessible@ method on "keep_services":{{site.baseurl}}/api/methods/keep_services.html
+# For each data block, the client chooses the highest priority server using rendezvous hashing, described below.
+# The client sends the data block request to the keep server, along with the token signature from the API which proves to Keep servers that the client is permitted to read a given block.
+# The server provides the block data after validating the token signature for the block (if the server does not have the block, it returns a 404 and the client tries the next highest priority server)
+
+h2(#rendezvous). Rendezvous hashing
+!(full-width){{site.baseurl}}/images/Keep_rendezvous_hashing.svg!
+
+Each @keep_service@ resource has an assigned uuid.  To determine priority assignments of blocks to servers, for each keep service compute the MD5 sum of the string concatenation of the block locator (hex-coded hash part only) and service uuid, then sort this list in descending order.  Blocks are preferentially placed on servers with the highest weight.
+
diff --git a/doc/architecture/keep-data-lifecycle.html.textile.liquid b/doc/architecture/keep-data-lifecycle.html.textile.liquid
new file mode 100644 (file)
index 0000000..a2e31fb
--- /dev/null
@@ -0,0 +1,59 @@
+---
+layout: default
+navsection: architecture
+title: "Data lifecycle"
+...
+
+{% comment %}
+Copyright (C) The Arvados Authors. All rights reserved.
+
+SPDX-License-Identifier: CC-BY-SA-3.0
+{% endcomment %}
+
+h2(#overview). Overview
+
+Arvados collections consist of a "manifest":{{site.baseurl}}/architecture/manifest-format.html and the data blocks referenced in that manifest. Manifests are stored in the PosgreSQL database, @data blocks@ are stored by a @keepstore@.
+
+Data blocks are frequently shared between collections. Each collection has its own @manifest@. Collection manifests and data blocks have a separate lifecycle, which is described in detail below.
+
+h2(#collection_lifecycle). Collection lifecycle
+
+During its lifetime, a collection can be in various states. These states are *persisted*, *expiring*, *trashed*  and *permanently deleted*.
+
+The nominal state is *persisted* which means the data can be can be accessed normally and will be retained indefinitely.
+
+A collection is *expiring* when it has a *trash_at* time in the future. An expiring collection can be accessed as normal, but is scheduled to be trashed automatically at the *trash_at* time.
+
+A collection is *trashed* when it has a *trash_at* time in the past. The *is_trashed* attribute will also be "true". The delete operation immediately puts the collection in the trash by setting the *trash_at* time to "now", and *delete_at* defaults to "now" + @Collections.DefaultTrashLifetime@. Once trashed, the collection is no longer readable through normal data access APIs. The collection will have *delete_at* set to some time in the future. The trashed collection is recoverable until the *delete_at* time passes, at which point the collection is permanently deleted.
+
+See "Recovering trashed collections":{{ site.baseurl }}/user/tutorials/tutorial-keep-collection-lifecycle.html#trash-recovery for instructions to recover trashed collections.
+
+h3(#collection_attributes). Collection lifecycle attributes
+
+As listed above the attributes that are used to manage a collection lifecycle are *is_trashed*, *trash_at*, and *delete_at*. The table below lists the values of these attributes and how they influence the state of a collection and its accessibility.
+
+table(table table-bordered table-condensed).
+|_. collection state|_. is_trashed|_. trash_at|_. delete_at|_. get|_. list|_. list?include_trash=true|_. can be modified|
+|persisted collection|false |null |null |yes |yes |yes |yes |
+|expiring collection|false |future |future |yes  |yes |yes |yes |
+|trashed collection|true |past |future |no |no |yes |only is_trashed, trash_at and delete_at attribtues|
+|deleted collection|true|past |past |no |no |no |no |
+
+h2(#block_lifecycle). Block lifecycle
+
+During its lifetime, a data block can be in various states. These states are *persisted*, *unreferenced*, *trashed* and *permanently deleted*.
+
+The nominal state is *persisted* which means the block can be can be retrieved normally from a @keepstore@ process.
+
+A block is *unreferenced* when there are no collection manifests in the PostgreSQL collections table that reference it. The block can still be retrieved normally from a @keepstore@ process, e.g. by creating a new collection with a manifest that references the hash of the block. Unreferenced blocks will be moved to the *trashed* state by @keep-balance@ after @BlobSigningTTL@, if @BlobTrash@ is enabled and @keep-balance@ is running and configured to send trash lists to the keepstores.
+
+A block is *trashed* when @keep-balance@ has asked a @keepstore@ to move it to its trash and @BlobTrash@ is enabled. It will stay there for a period of time, subject to the @BlobTrashLifetime@ settings.
+
+A block is *permanently deleted* on the first wakeup of its @keepstore@ trash process after the block has spent @BlobTrashLifetime@ in that keepstore's trash. The trash process wakes up with a frequency defined by the @BlobTrashCheckInterval@.
+
+table(table table-bordered table-condensed).
+|_. block state|_. duration|_. retrievable via Keep|_. can be recovered|
+|persisted block|indefinitely|yes |n/a |
+|unreferenced block|@BlobSigningTTL@ + up to @BalancePeriod@ + duration of keep-balance run|yes |n/a |
+|trashed block|@BlobTrashLifetime@ + up to @BlobTrashCheckInterval@|no |yes |
+|deleted block||no |no |
similarity index 51%
rename from doc/api/storage.html.textile.liquid
rename to doc/architecture/manifest-format.html.textile.liquid
index aa0ed21b9f86788f880721a63a28cca30e7448f8..1780768bc340ac1d823fa8c3bb7e30f499ba08f5 100644 (file)
@@ -1,7 +1,7 @@
 ---
 layout: default
 navsection: architecture
-title: Storage in Keep
+title: Manifest format
 ...
 {% comment %}
 Copyright (C) The Arvados Authors. All rights reserved.
@@ -9,102 +9,10 @@ Copyright (C) The Arvados Authors. All rights reserved.
 SPDX-License-Identifier: CC-BY-SA-3.0
 {% endcomment %}
 
-Keep clients are applications such as @arv-get@, @arv-put@ and @arv-mount@ which store and retrieve data from Keep.  In doing so, these programs interact with both the API server (which stores file metadata in form of Collection objects) and individual Keep servers (which store the actual data blocks).
-
-!(full-width){{site.baseurl}}/images/Keep_reading_writing_block.svg!
-
-h2. Storing a file
-
-# The client discovers keep servers (or proxies) using the @accessible@ method on "keep_services":{{site.baseurl}}/api/methods/keep_services.html
-# Data is split into 64 MiB blocks and the MD5 hash is computed for each block.
-# The client uploads each block to one or more Keep servers, based on the number of desired replicas.  The priority order is determined using rendezvous hashing, described below.
-# The Keep server returns a block locator (the MD5 sum of the block) and a "signed token" which the client can use as proof of knowledge for the block.
-# The client constructs a @manifest@ which lists the blocks by MD5 hash and how to reassemble them into the original files.
-# The client creates a "collection":{{site.baseurl}}/api/methods/collections.html and provides the @manifest_text@
-# The API server accepts the collection after validating the signed tokens (proof of knowledge) for each block.
+Each collection record has a @manifest_text@ field, which describes how to reassemble keep blocks into files. Each block identifier in the manifest has an added signature which is used to confirm permission to read the block.  To read a block from a keepstore server, the client must provide the block identifier, the signature, and the same API token used to retrieve the collection record.
 
 !(full-width){{site.baseurl}}/images/Keep_manifests.svg!
 
-h2. Fetching a file
-
-# The client requests a @collection@ object including @manifest_text@ from the APIs server
-# The server adds "token signatures" to the @manifest_text@ and returns it to the client.
-# The client discovers keep servers (or proxies) using the @accessible@ method on "keep_services":{{site.baseurl}}/api/methods/keep_services.html
-# For each data block, the client chooses the highest priority server using rendezvous hashing, described below.
-# The client sends the data block request to the keep server, along with the token signature from the API which proves to Keep servers that the client is permitted to read a given block.
-# The server provides the block data after validating the token signature for the block (if the server does not have the block, it returns a 404 and the client tries the next highest priority server)
-
-!(full-width){{site.baseurl}}/images/Keep_rendezvous_hashing.svg!
-
-Each @keep_service@ resource has an assigned uuid.  To determine priority assignments of blocks to servers, for each keep service compute the MD5 sum of the string concatenation of the block locator (hex-coded hash part only) and service uuid, then sort this list in descending order.  Blocks are preferentially placed on servers with the highest weight.
-
-h2. Keep server API
-
-The Keep server is accessed via a simple HTTP REST API.
-
-*GET /blocklocator+size+A@token*
-
-Fetch the data block.  Response returns block contents.  If permission checking is enabled, requires a valid token hint.
-
-*PUT /blocklocator*
-
-Body: the block contents.  Responds the block locator consisting of MD5 sum of the data, block size, and signed token hint.
-
-*POST /*
-
-Body: the block contents.  Responds the block locator consisting of MD5 sum of the data, block size, and signed token hint.
-
-h2(#locator). Keep locator format
-
-BNF notation for a valid Keep locator string (with hints).  For example @d41d8cd98f00b204e9800998ecf8427e+0+Z+Ada39a3ee5e6b4b0d3255bfef95601890afd80709@53bed294@
-
-<pre>
-locator        ::= sized-digest hint*
-sized-digest   ::= digest size-hint
-digest         ::= <32 lowercase hexadecimal digits>
-size-hint      ::= "+" [0-9]+
-hint           ::= "+" hint-type hint-content
-hint-type      ::= [A-Z]+
-hint-content   ::= [A-Za-z0-9@_-]*
-sign-hint      ::= "+A" <40 lowercase hexadecimal digits> "@" sign-timestamp
-sign-timestamp ::= <8 lowercase hexadecimal digits>
-</pre>
-
-h3. Token signatures
-
-A token signature (sign-hint) provides proof-of-access for a data block.  It is computed by taking a SHA1 HMAC of the blob signing token (a shared secret between the API server and keep servers), block digest, current API token, expiration timestamp, and blob signature TTL.
-
-When communicating with the Keep store to fetch a block, or the API server to create or update a collection, the service computes the expected token signature for each block and compares it to the token signature that was presented by the client.  Keep clients receive valid block signatures when uploading a block to a keep store (getting back a signed token as proof of knowledge) or, from the API server, getting the manifest text of a collection on which the user has read permission.
-
-Security of a token signature is derived from the following characteristics:
-
-# Valid signatures can only be generated by entities that know the shared secret (the "blob signing token")
-# A signature can only be used by an entity that also know the API token that was used to generate it.
-# It expires after a set date (the expiration time, based on the "blob signature time-to-live (TTL)")
-
-h3. Regular expression to validate locator
-
-<pre>
-/^([0-9a-f]{32})\+([0-9]+)(\+[A-Z][-A-Za-z0-9@_]*)*$/
-</pre>
-
-h3. Valid locators
-
-table(table table-bordered table-condensed).
-|@d41d8cd98f00b204e9800998ecf8427e+0@|
-|@d41d8cd98f00b204e9800998ecf8427e+0+Z@|
-|<code>d41d8cd98f00b204e9800998ecf8427e+0+Z+Ada39a3ee5e6b4b0d3255bfef95601890afd80709@53bed294</code>|
-
-h3. Invalid locators
-
-table(table table-bordered table-condensed).
-||Why|
-|@d41d8cd98f00b204e9800998ecf8427e@|No size hint|
-|@d41d8cd98f00b204e9800998ecf8427e+Z+0@|Other hint before size hint|
-|@d41d8cd98f00b204e9800998ecf8427e+0+0@|Multiple size hints|
-|@d41d8cd98f00b204e9800998ecf8427e+0+z@|Hint does not start with uppercase letter|
-|@d41d8cd98f00b204e9800998ecf8427e+0+Zfoo*bar@|Hint contains invalid character @*@|
-
 h2. Manifest v1
 
 A manifest is utf-8 encoded text, consisting of zero or more newline-terminated streams.
@@ -173,3 +81,104 @@ A manifest containing a file consisting of multiple blocks and a space in the fi
 <pre>
 . c449ed86671e4a34a8b8b9430850beba+67108864 09fcfea01c3a141b89dd0dcfa1b7768e+22534144 0:89643008:Docker\040image.tar
 </pre>
+h2(#locator). Keep locator format
+
+BNF notation for a valid Keep locator string (with hints).  For example: *d41d8cd98f00b204e9800998ecf8427e+0+Z+Ada39a3ee5e6b4b0d3255bfef95601890afd80709@53bed294*
+
+<pre>
+locator          ::= sized-digest hint*
+sized-digest     ::= digest size-hint
+digest           ::= <32 lowercase hexadecimal digits>
+size-hint        ::= "+" [0-9]+
+hint             ::= "+" hint-type hint-content
+hint-type        ::= [A-Z]+
+hint-content     ::= [A-Za-z0-9@_-]*
+sign-hint        ::= "+A" <40 lowercase hexadecimal digits> "@" sign-timestamp
+remote-sign-hint ::= "+R" [A-Za-z0-9]{5} "-" <40 lowercase hexadecimal digits> "@" sign-timestamp
+sign-timestamp   ::= <8 lowercase hexadecimal digits>
+</pre>
+
+h3. Regular expression to validate locator
+
+<pre>
+/^([0-9a-f]{32})\+([0-9]+)(\+[A-Z][-A-Za-z0-9@_]*)*$/
+</pre>
+
+h3. Valid locators
+
+table(table table-bordered table-condensed).
+|@d41d8cd98f00b204e9800998ecf8427e+0@|
+|@d41d8cd98f00b204e9800998ecf8427e+0+Z@|
+|<code>d41d8cd98f00b204e9800998ecf8427e+0+Z+Ada39a3ee5e6b4b0d3255bfef95601890afd80709@53bed294</code>|
+|<code>930625b054ce894ac40596c3f5a0d947+33+Rzzzzz-1f27a35dd9af37191d63ad8eb8985624451e7b79@5835c8bc</code>|
+
+h3. Invalid locators
+
+table(table table-bordered table-condensed).
+||Why|
+|@d41d8cd98f00b204e9800998ecf8427e@|No size hint|
+|@d41d8cd98f00b204e9800998ecf8427e+Z+0@|Other hint before size hint|
+|@d41d8cd98f00b204e9800998ecf8427e+0+0@|Multiple size hints|
+|@d41d8cd98f00b204e9800998ecf8427e+0+z@|Hint does not start with uppercase letter|
+|@d41d8cd98f00b204e9800998ecf8427e+0+Zfoo*bar@|Hint contains invalid character @*@|
+
+h3. Token signatures
+
+A token signature (sign-hint) provides proof-of-access for a data block.  It is computed by taking a SHA1 HMAC of the blob signing token (a shared secret between the API server and keep servers), block digest, current API token, expiration timestamp, and blob signature TTL.
+
+When communicating with the @keepstore@ to fetch a block, or the API server to create or update a collection, the service computes the expected token signature for each block and compares it to the token signature that was presented by the client.  Keep clients receive valid block signatures when uploading a block to a keep store (getting back a signed token as proof of knowledge) or, from the API server, getting the manifest text of a collection on which the user has read permission.
+
+Security of a token signature is derived from the following characteristics:
+
+# Valid signatures can only be generated by entities that know the shared secret (the "blob signing token")
+# A signature can only be used by an entity that also know the API token that was used to generate it.
+# It expires after a set date (the expiration time, based on the "blob signature time-to-live (TTL)")
+
+h3(#federationsignatures). Federation and signatures
+
+When a collection record is returned through a federation request, the keep blocks listed in the manifest may not be available on the local cluster, and the keep block signatures returned by the remote cluster are not valid for the local cluster.  To solve this, @arvados-controller@ rewrites the signatures in the manifest to "remote cluster" signatures.
+
+A local signature comes after the block identifier and block size, and starts with @+A@:
+
+<code>930625b054ce894ac40596c3f5a0d947+33+A1f27a35dd9af37191d63ad8eb8985624451e7b79@5835c8bc</code>
+
+A remote cluster signature starts with @+R@, then the cluster id of the cluster it originated from (@zzzzz@ in this example), a dash, and then the original signature:
+
+<code>930625b054ce894ac40596c3f5a0d947+33+Rzzzzz-1f27a35dd9af37191d63ad8eb8985624451e7b79@5835c8bc</code>
+
+When the client provides a remote-signed block locator to keepstore, the keepstore proxies the request to the remote cluster.
+
+# keepstore determines the cluster id to contact from the first part of the @+R@ signature
+# creates a salted token using the API token and cluster id
+# contacts the "accessible" endpoint on the remote cluster to determine the remote cluster's keepstore or keepproxy hosts
+# converts the remote signature @+R@ back to a local signature @+A@
+# contacts the remote keepstore or keepproxy host and requests the block using the local signature
+# returns the block contents back to the client
+
+h3(#example). Example
+
+This example uses @c1bad4b39ca5a924e481008009d94e32+210@, which is the content hash of a @collection@ that was added to Keep in "how to upload data":{{ site.baseurl }}/user/tutorials/tutorial-keep.html.  Get the collection manifest using @arv-get@:
+
+<notextile>
+<pre><code>~$ <span class="userinput">arv-get c1bad4b39ca5a924e481008009d94e32+210</span>
+. 204e43b8a1185621ca55a94839582e6f+67108864+Aasignatureforthisblockaaaaaaaaaaaaaaaaaa@5f612ee6 b9677abbac956bd3e86b1deb28dfac03+67108864+Aasignatureforthisblockbbbbbbbbbbbbbbbbbb@5f612ee6 fc15aff2a762b13f521baf042140acec+67108864+Aasignatureforthisblockcccccccccccccccccc@5f612ee6 323d2a3ce20370c4ca1d3462a344f8fd+25885655+Aasignatureforthisblockdddddddddddddddddd@5f612ee6 0:227212247:var-GS000016015-ASM.tsv.bz2
+</code></pre>
+</notextile>
+
+This collection includes a single file @var-GS000016015-ASM.tsv.bz2@ which is 227212247 bytes long. It is stored using four sequential data blocks with hashes @204e43b8a1185621ca55a94839582e6f+67108864@, @b9677abbac956bd3e86b1deb28dfac03+67108864@, @fc15aff2a762b13f521baf042140acec+67108864@, and @323d2a3ce20370c4ca1d3462a344f8fd+25885655@. Each of the block hashes is followed by the rest of their "locator":#locator.
+
+Use @arv-get@ to download the first data block:
+
+notextile. <pre><code>~$ <span class="userinput">arv-get 204e43b8a1185621ca55a94839582e6f+67108864+Aasignatureforthisblockaaaaaaaaaaaaaaaaaa@5f612ee6 &gt; block1</span></code></pre>
+
+Inspect the size and compute the MD5 hash of @block1@:
+
+<notextile>
+<pre><code>~$ <span class="userinput">ls -l block1</span>
+-rw-r--r-- 1 you group 67108864 Dec  9 20:14 block1
+~$ <span class="userinput">md5sum block1</span>
+204e43b8a1185621ca55a94839582e6f  block1
+</code></pre>
+</notextile>
+
+As expected, the md5sum of the contents of the block matches the @digest@ part of the "locator":#locator, and the size of the contents matches the @size-hint@.
diff --git a/doc/architecture/storage.html.textile.liquid b/doc/architecture/storage.html.textile.liquid
new file mode 100644 (file)
index 0000000..3218363
--- /dev/null
@@ -0,0 +1,40 @@
+---
+layout: default
+navsection: architecture
+title: Introduction to Keep
+...
+{% comment %}
+Copyright (C) The Arvados Authors. All rights reserved.
+
+SPDX-License-Identifier: CC-BY-SA-3.0
+{% endcomment %}
+
+Keep is a content-addressable storage system that yields high performance for I/O-bound workloads. Keep is designed to run on low-cost commodity hardware or cloud services and is tightly integrated with the rest of the Arvados system. It provides high fault tolerance and high aggregate performance to a large number of clients.
+
+h2. Design goals and core features
+
+* *Scale* - Keep installations are managing petabytes of data today. Keep scales horizontally.
+
+* *Data deduplication* - Keep automatically deduplicates data through its use of content addressing.
+
+* *Flexibility* - Keep can store data in S3, S3-compatible storage systems (e.g. Ceph) and Azure blob storage. Keep can also store data on POSIX file systems.
+
+* *Fault-Tolerance* - Errors and failure are expected. Keep has redundancy and recovery capabilities at its core.
+
+* *Optimized for Aggregate Throughput* - Like S3 and Azure blob storage, Keep is optimized for aggregate throughput. This is optimal in a scenario with many reader/writer processes.
+
+* *Complex Data Management* - Keep operates well in environments where there are many independent users accessing the same data or users who want to organize data in many different ways. Keep facilitates data sharing without expecting users either to agree with one another about directory structures or to create redundant copies of the data.
+
+* *Security* - Keep works well combined with encryption at rest and transport encryption. All data is managed through @collection@ objects, which implement a rich "permission model":{{site.baseurl}}/api/permission-model.html.
+
+h2. How Keep works
+
+Keep is a content-addressable file system.  This means that files are managed using special unique identifiers derived from the _contents_ of the file (specifically, the MD5 hash), rather than human-assigned file names.  This has a number of advantages:
+* Files can be stored and replicated across a cluster of servers without requiring a central name server.
+* Both the server and client systematically validate data integrity because the checksum is built into the identifier.
+* Data duplication is minimized—two files with the same contents will have in the same identifier, and will not be stored twice.
+* It avoids data race conditions, since an identifier always points to the same data.
+
+In Keep, information is stored in @data blocks@.  Data blocks are normally between 1 byte and 64 megabytes in size.  If a file exceeds the maximum size of a single data block, the file will be split across multiple data blocks until the entire file can be stored.  These data blocks may be stored and replicated across multiple disks, servers, or clusters.  Each data block has its own identifier for the contents of that specific data block.
+
+In order to reassemble the file, Keep stores a @collection@ manifest which lists in sequence the data blocks that make up the original file.  A @manifest@ may store the information for multiple files, including a directory structure. See "manifest format":{{site.baseurl}}/architecture/manifest-format.html for more information on how manifests are structured.
diff --git a/doc/examples/pipeline_templates/gatk-exome-fq-snp.json b/doc/examples/pipeline_templates/gatk-exome-fq-snp.json
deleted file mode 100644 (file)
index 481dda3..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-{
- "name":"GATK / exome PE fastq to snp",
- "components":{
-  "extract-reference":{
-   "repository":"arvados",
-   "script_version":"e820bd1c6890f93ea1a84ffd5730bbf0e3d8e153",
-   "script":"file-select",
-   "script_parameters":{
-    "names":[
-     "human_g1k_v37.fasta.gz",
-     "human_g1k_v37.fasta.fai.gz",
-     "human_g1k_v37.dict.gz"
-    ],
-    "input":"d237a90bae3870b3b033aea1e99de4a9+10820+K@qr1hi"
-   },
-   "output_name":false
-  },
-  "bwa-index":{
-   "repository":"arvados",
-   "script_version":"e820bd1c6890f93ea1a84ffd5730bbf0e3d8e153",
-   "script":"bwa-index",
-   "script_parameters":{
-    "input":{
-     "output_of":"extract-reference"
-    },
-    "bwa_tbz":{
-     "value":"8b6e2c4916133e1d859c9e812861ce13+70",
-     "required":true
-    }
-   },
-   "output_name":false
-  },
-  "bwa-aln":{
-   "repository":"arvados",
-   "script_version":"e820bd1c6890f93ea1a84ffd5730bbf0e3d8e153",
-   "script":"bwa-aln",
-   "script_parameters":{
-    "input":{
-     "dataclass":"Collection",
-     "required":"true"
-    },
-    "reference_index":{
-     "output_of":"bwa-index"
-    },
-    "samtools_tgz":{
-     "value":"c777e23cf13e5d5906abfdc08d84bfdb+74",
-     "required":true
-    },
-    "bwa_tbz":{
-     "value":"8b6e2c4916133e1d859c9e812861ce13+70",
-     "required":true
-    }
-   },
-   "runtime_constraints":{
-    "max_tasks_per_node":1
-   },
-   "output_name":false
-  },
-  "picard-gatk2-prep":{
-   "repository":"arvados",
-   "script_version":"e820bd1c6890f93ea1a84ffd5730bbf0e3d8e153",
-   "script":"picard-gatk2-prep",
-   "script_parameters":{
-    "input":{
-     "output_of":"bwa-aln"
-    },
-    "reference":{
-     "output_of":"extract-reference"
-    },
-    "picard_zip":{
-     "value":"687f74675c6a0e925dec619cc2bec25f+77",
-     "required":true
-    }
-   },
-   "runtime_constraints":{
-    "max_tasks_per_node":1
-   },
-   "output_name":false
-  },
-  "GATK2-realign":{
-   "repository":"arvados",
-   "script_version":"e820bd1c6890f93ea1a84ffd5730bbf0e3d8e153",
-   "script":"GATK2-realign",
-   "script_parameters":{
-    "input":{
-     "output_of":"picard-gatk2-prep"
-    },
-    "gatk_bundle":{
-     "value":"d237a90bae3870b3b033aea1e99de4a9+10820+K@qr1hi",
-     "required":true
-    },
-    "picard_zip":{
-     "value":"687f74675c6a0e925dec619cc2bec25f+77",
-     "required":true
-    },
-    "gatk_tbz":{
-     "value":"7e0a277d6d2353678a11f56bab3b13f2+87",
-     "required":true
-    },
-    "regions":{
-     "value":"13b53dbe1ec032dfc495fd974aa5dd4a+87/S02972011_Covered_sort_merged.bed"
-    },
-    "region_padding":{
-     "value":10
-    }
-   },
-   "runtime_constraints":{
-    "max_tasks_per_node":2
-   },
-   "output_name":false
-  },
-  "GATK2-bqsr":{
-   "repository":"arvados",
-   "script_version":"e820bd1c6890f93ea1a84ffd5730bbf0e3d8e153",
-   "script":"GATK2-bqsr",
-   "script_parameters":{
-    "input":{
-     "output_of":"GATK2-realign"
-    },
-    "gatk_bundle":{
-     "value":"d237a90bae3870b3b033aea1e99de4a9+10820+K@qr1hi",
-     "required":true
-    },
-    "picard_zip":{
-     "value":"687f74675c6a0e925dec619cc2bec25f+77",
-     "required":true
-    },
-    "gatk_tbz":{
-     "value":"7e0a277d6d2353678a11f56bab3b13f2+87",
-     "required":true
-    }
-   },
-   "output_name":false
-  },
-  "GATK2-merge-call":{
-   "repository":"arvados",
-   "script_version":"e820bd1c6890f93ea1a84ffd5730bbf0e3d8e153",
-   "script":"GATK2-merge-call",
-   "script_parameters":{
-    "input":{
-     "output_of":"GATK2-bqsr"
-    },
-    "gatk_bundle":{
-     "value":"d237a90bae3870b3b033aea1e99de4a9+10820+K@qr1hi",
-     "required":true
-    },
-    "picard_zip":{
-     "value":"687f74675c6a0e925dec619cc2bec25f+77",
-     "required":true
-    },
-    "gatk_tbz":{
-     "value":"7e0a277d6d2353678a11f56bab3b13f2+87",
-     "required":true
-    },
-    "regions":{
-     "value":"13b53dbe1ec032dfc495fd974aa5dd4a+87/S02972011_Covered_sort_merged.bed"
-    },
-    "region_padding":{
-     "value":10
-    },
-    "GATK2_UnifiedGenotyper_args":{
-     "default":[
-      "-stand_call_conf",
-      "30.0",
-      "-stand_emit_conf",
-      "30.0",
-      "-dcov",
-      "200"
-     ]
-    }
-   },
-   "output_name":"Variant calls from UnifiedGenotyper"
-  }
- }
-}
diff --git a/doc/examples/pipeline_templates/rtg-fq-snp.json b/doc/examples/pipeline_templates/rtg-fq-snp.json
deleted file mode 100644 (file)
index c951c4c..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-{
- "name":"Real Time Genomics / PE fastq to snp",
- "components":{
-  "extract_reference":{
-   "script":"file-select",
-   "script_parameters":{
-    "names":[
-     "human_g1k_v37.fasta.gz"
-    ],
-    "input":"d237a90bae3870b3b033aea1e99de4a9+10820+K@qr1hi"
-   },
-   "script_version":"4c1f8cd1431ece2ef11c130d48bb2edfd2f00ec2"
-  },
-  "reformat_reference":{
-   "script_version":"4c1f8cd1431ece2ef11c130d48bb2edfd2f00ec2",
-   "script":"rtg-fasta2sdf",
-   "script_parameters":{
-    "input":{
-     "output_of":"extract_reference"
-    },
-    "rtg_binary_zip":"5d33618193f763b7dc3a3fdfa11d452e+95+K@qr1hi",
-    "rtg_license":{
-     "optional":false
-    }
-   }
-  },
-  "reformat_reads":{
-   "script_version":"4c1f8cd1431ece2ef11c130d48bb2edfd2f00ec2",
-   "script":"rtg-fastq2sdf",
-   "script_parameters":{
-    "input":{
-     "optional":false
-    },
-    "rtg_binary_zip":"5d33618193f763b7dc3a3fdfa11d452e+95+K@qr1hi",
-    "rtg_license":{
-     "optional":false
-    }
-   }
-  },
-  "map_reads":{
-   "script_version":"4c1f8cd1431ece2ef11c130d48bb2edfd2f00ec2",
-   "script":"rtg-map",
-   "script_parameters":{
-    "input":{
-     "output_of":"reformat_reads"
-    },
-    "reference":{
-     "output_of":"reformat_reference"
-    },
-    "rtg_binary_zip":"5d33618193f763b7dc3a3fdfa11d452e+95+K@qr1hi",
-    "rtg_license":{
-     "optional":false
-    }
-   },
-   "runtime_constraints":{
-    "max_tasks_per_node":1
-   }
-  },
-  "report_snp":{
-   "script_version":"4c1f8cd1431ece2ef11c130d48bb2edfd2f00ec2",
-   "script":"rtg-snp",
-   "script_parameters":{
-    "input":{
-     "output_of":"map_reads"
-    },
-    "reference":{
-     "output_of":"reformat_reference"
-    },
-    "rtg_binary_zip":"5d33618193f763b7dc3a3fdfa11d452e+95+K@qr1hi",
-    "rtg_license":{
-     "optional":false
-    }
-   }
-  }
- }
-}
diff --git a/doc/examples/ruby/list-active-nodes.rb b/doc/examples/ruby/list-active-nodes.rb
deleted file mode 100755 (executable)
index a3eb205..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env ruby
-# Copyright (C) The Arvados Authors. All rights reserved.
-#
-# SPDX-License-Identifier: CC-BY-SA-3.0
-
-abort 'Error: Ruby >= 1.9.3 required.' if RUBY_VERSION < '1.9.3'
-
-require 'arvados'
-
-arv = Arvados.new(api_version: 'v1')
-arv.node.list[:items].each do |node|
-  if node[:crunch_worker_state] != 'down'
-    ping_age = (Time.now - Time.parse(node[:last_ping_at])).to_i rescue -1
-    puts "#{node[:uuid]} #{node[:crunch_worker_state]} #{ping_age}"
-  end
-end
index 568335b1929fa209969506363dd04c961215bd63..eaa0cd3ae7cc25b482575f3c9329e3ba8b3563cb 100644 (file)
@@ -1,7 +1,151 @@
-<?xml version="1.0" standalone="yes"?>
-<!-- Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0 -->
-
-<svg version="1.1" viewBox="0.0 0.0 960.0 540.0" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="g1586814eb6_0_6.0"><path d="m0 0l960.0 0l0 540.0l-960.0 0l0 -540.0z" clip-rule="nonzero"></path></clipPath><g clip-path="url(#g1586814eb6_0_6.0)"><path fill="#ffffff" d="m0 0l960.0 0l0 540.0l-960.0 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m32.72441 46.721786l894.55115 0l0 60.125984l-894.55115 0z" fill-rule="nonzero"></path><path fill="#000000" d="m63.47441 82.28053l3.5 0.875q-1.09375 4.328125 -3.96875 6.59375q-2.859375 2.265625 -6.984375 2.265625q-4.28125 0 -6.96875 -1.734375q-2.6875 -1.75 -4.09375 -5.046875q-1.390625 -3.3125 -1.390625 -7.109375q0 -4.140625 1.578125 -7.21875q1.578125 -3.078125 4.5 -4.671875q2.921875 -1.609375 6.421875 -1.609375q3.96875 0 6.671875 2.03125q2.71875 2.015625 3.796875 5.6875l-3.453125 0.8125q-0.921875 -2.890625 -2.6875 -4.203125q-1.75 -1.328125 -4.40625 -1.328125q-3.046875 0 -5.09375 1.46875q-2.046875 1.453125 -2.890625 3.921875q-0.828125 2.46875 -0.828125 5.09375q0 3.375 0.984375 5.890625q0.984375 2.515625 3.0625 3.765625q2.078125 1.25 4.5 1.25q2.953125 0 4.984375 -1.6875q2.046875 -1.703125 2.765625 -5.046875zm6.441559 -0.3125q0 -5.328125 2.953125 -7.890625q2.484375 -2.140625 6.03125 -2.140625q3.96875 0 6.46875 2.59375q2.515625 2.59375 2.515625 7.171875q0 3.703125 -1.109375 5.828125q-1.109375 2.109375 -3.234375 3.296875q-2.125 1.171875 -4.640625 1.171875q-4.015625 0 -6.5 -2.578125q-2.484375 -2.59375 -2.484375 -7.453125zm3.34375 0q0 3.6875 1.59375 5.53125q1.609375 1.828125 4.046875 1.828125q2.421875 0 4.03125 -1.84375q1.609375 -1.84375 1.609375 -5.625q0 -3.5625 -1.625 -5.390625q-1.609375 -1.828125 -4.015625 -1.828125q-2.4375 0 -4.046875 1.828125q-1.59375 1.8125 -1.59375 5.5zm18.541382 9.59375l0 -26.484375l3.265625 0l0 26.484375l-3.265625 0zm8.293121 0l0 -26.484375l3.265625 0l0 26.484375l-3.265625 0zm21.511871 -6.171875l3.359375 0.40625q-0.796875 2.953125 -2.953125 4.578125q-2.140625 1.625 -5.484375 1.625q-4.21875 0 -6.6875 -2.59375q-2.453125 -2.59375 -2.453125 -7.28125q0 -4.828125 2.484375 -7.5q2.5 -2.6875 6.46875 -2.6875q3.859375 0 6.296875 2.625q2.4375 2.625 2.4375 7.375q0 0.28125 -0.015625 0.859375l-14.3125 0q0.171875 3.171875 1.78125 4.859375q1.609375 1.671875 4.015625 1.671875q1.78125 0 3.046875 -0.9375q1.265625 -0.953125 2.015625 -3.0zm-10.6875 -5.265625l10.71875 0q-0.21875 -2.421875 -1.234375 -3.625q-1.546875 -1.890625 -4.015625 -1.890625q-2.25 0 -3.78125 1.5q-1.515625 1.5 -1.6875 4.015625zm30.822632 4.40625l3.203125 0.421875q-0.515625 3.296875 -2.6875 5.171875q-2.15625 1.875 -5.296875 1.875q-3.9375 0 -6.328125 -2.578125q-2.390625 -2.578125 -2.390625 -7.375q0 -3.109375 1.015625 -5.4375q1.03125 -2.34375 3.140625 -3.5q2.109375 -1.171875 4.578125 -1.171875q3.125 0 5.109375 1.59375q2.0 1.578125 2.546875 4.484375l-3.15625 0.484375q-0.453125 -1.9375 -1.609375 -2.90625q-1.140625 -0.984375 -2.765625 -0.984375q-2.453125 0 -4.0 1.765625q-1.53125 1.765625 -1.53125 5.578125q0 3.859375 1.484375 5.625q1.484375 1.75 3.875 1.75q1.90625 0 3.1875 -1.171875q1.28125 -1.1875 1.625 -3.625zm13.2578125 4.125l0.46875 2.875q-1.375 0.28125 -2.46875 0.28125q-1.765625 0 -2.75 -0.5625q-0.96875 -0.5625 -1.375 -1.46875q-0.390625 -0.90625 -0.390625 -3.84375l0 -11.03125l-2.375 0l0 -2.53125l2.375 0l0 -4.75l3.234375 -1.953125l0 6.703125l3.28125 0l0 2.53125l-3.28125 0l0 11.21875q0 1.390625 0.171875 1.796875q0.171875 0.390625 0.5625 0.625q0.390625 0.234375 1.109375 0.234375q0.546875 0 1.4375 -0.125zm3.2772064 -19.84375l0 -3.734375l3.25 0l0 3.734375l-3.25 0zm0 22.75l0 -19.1875l3.25 0l0 19.1875l-3.25 0zm7.0743713 -9.59375q0 -5.328125 2.953125 -7.890625q2.484375 -2.140625 6.03125 -2.140625q3.96875 0 6.46875 2.59375q2.515625 2.59375 2.515625 7.171875q0 3.703125 -1.109375 5.828125q-1.109375 2.109375 -3.234375 3.296875q-2.125 1.171875 -4.640625 1.171875q-4.015625 0 -6.5 -2.578125q-2.484375 -2.59375 -2.484375 -7.453125zm3.34375 0q0 3.6875 1.59375 5.53125q1.609375 1.828125 4.046875 1.828125q2.421875 0 4.03125 -1.84375q1.609375 -1.84375 1.609375 -5.625q0 -3.5625 -1.625 -5.390625q-1.609375 -1.828125 -4.015625 -1.828125q-2.4375 0 -4.046875 1.828125q-1.59375 1.8125 -1.59375 5.5zm18.619507 9.59375l0 -19.1875l2.921875 0l0 2.734375q2.125 -3.171875 6.109375 -3.171875q1.734375 0 3.1875 0.625q1.453125 0.625 2.171875 1.640625q0.734375 1.015625 1.015625 2.40625q0.1875 0.890625 0.1875 3.15625l0 11.796875l-3.25 0l0 -11.671875q0 -1.984375 -0.390625 -2.96875q-0.375 -0.984375 -1.34375 -1.5625q-0.953125 -0.59375 -2.265625 -0.59375q-2.078125 0 -3.59375 1.3125q-1.5 1.3125 -1.5 5.0l0 10.484375l-3.25 0zm19.463257 -5.734375l3.21875 -0.5q0.265625 1.9375 1.5 2.96875q1.234375 1.03125 3.46875 1.03125q2.234375 0 3.3125 -0.90625q1.09375 -0.921875 1.09375 -2.15625q0 -1.09375 -0.96875 -1.734375q-0.65625 -0.4375 -3.3125 -1.09375q-3.578125 -0.90625 -4.96875 -1.5625q-1.375 -0.671875 -2.09375 -1.828125q-0.703125 -1.171875 -0.703125 -2.578125q0 -1.28125 0.578125 -2.375q0.59375 -1.09375 1.59375 -1.8125q0.765625 -0.5625 2.078125 -0.953125q1.3125 -0.390625 2.8125 -0.390625q2.25 0 3.953125 0.65625q1.71875 0.65625 2.53125 1.765625q0.8125 1.109375 1.109375 2.96875l-3.171875 0.4375q-0.21875 -1.484375 -1.265625 -2.3125q-1.03125 -0.84375 -2.921875 -0.84375q-2.25 0 -3.203125 0.75q-0.953125 0.734375 -0.953125 1.734375q0 0.625 0.390625 1.140625q0.40625 0.515625 1.25 0.859375q0.484375 0.1875 2.875 0.828125q3.453125 0.921875 4.8125 1.515625q1.359375 0.578125 2.140625 1.703125q0.78125 1.125 0.78125 2.78125q0 1.625 -0.953125 3.0625q-0.953125 1.4375 -2.75 2.234375q-1.78125 0.78125 -4.03125 0.78125q-3.75 0 -5.703125 -1.546875q-1.953125 -1.5625 -2.5 -4.625zm20.867188 -9.75l0 -3.703125l3.703125 0l0 3.703125l-3.703125 0zm0 15.484375l0 -3.703125l3.703125 0l0 3.703125l-3.703125 0zm20.148163 0l0 -26.484375l5.265625 0l6.28125 18.75q0.859375 2.625 1.265625 3.921875q0.4375 -1.4375 1.40625 -4.25l6.34375 -18.421875l4.703125 0l0 26.484375l-3.375 0l0 -22.171875l-7.6875 22.171875l-3.171875 0l-7.65625 -22.546875l0 22.546875l-3.375 0zm43.29773 -2.359375q-1.796875 1.53125 -3.46875 2.171875q-1.671875 0.625 -3.59375 0.625q-3.15625 0 -4.859375 -1.546875q-1.6875 -1.546875 -1.6875 -3.953125q0 -1.40625 0.640625 -2.5625q0.640625 -1.171875 1.671875 -1.875q1.046875 -0.703125 2.34375 -1.0625q0.953125 -0.265625 2.890625 -0.5q3.9375 -0.46875 5.796875 -1.109375q0.015625 -0.671875 0.015625 -0.859375q0 -1.984375 -0.921875 -2.796875q-1.25 -1.09375 -3.703125 -1.09375q-2.296875 0 -3.390625 0.796875q-1.09375 0.796875 -1.609375 2.84375l-3.1875 -0.4375q0.4375 -2.03125 1.421875 -3.28125q1.0 -1.265625 2.875 -1.9375q1.890625 -0.6875 4.359375 -0.6875q2.46875 0 4.0 0.578125q1.53125 0.578125 2.25 1.453125q0.734375 0.875 1.015625 2.21875q0.15625 0.828125 0.15625 3.0l0 4.328125q0 4.546875 0.203125 5.75q0.21875 1.1875 0.828125 2.296875l-3.390625 0q-0.5 -1.015625 -0.65625 -2.359375zm-0.265625 -7.265625q-1.765625 0.71875 -5.3125 1.21875q-2.0 0.296875 -2.84375 0.65625q-0.828125 0.359375 -1.28125 1.0625q-0.4375 0.6875 -0.4375 1.53125q0 1.3125 0.984375 2.1875q0.984375 0.859375 2.875 0.859375q1.875 0 3.34375 -0.828125q1.46875 -0.828125 2.15625 -2.25q0.515625 -1.09375 0.515625 -3.25l0 -1.1875zm8.510132 9.625l0 -19.1875l2.921875 0l0 2.734375q2.125 -3.171875 6.109375 -3.171875q1.734375 0 3.1875 0.625q1.453125 0.625 2.171875 1.640625q0.734375 1.015625 1.015625 2.40625q0.1875 0.890625 0.1875 3.15625l0 11.796875l-3.25 0l0 -11.671875q0 -1.984375 -0.390625 -2.96875q-0.375 -0.984375 -1.34375 -1.5625q-0.953125 -0.59375 -2.265625 -0.59375q-2.078125 0 -3.59375 1.3125q-1.5 1.3125 -1.5 5.0l0 10.484375l-3.25 0zm20.775757 -22.75l0 -3.734375l3.25 0l0 3.734375l-3.25 0zm0 22.75l0 -19.1875l3.25 0l0 19.1875l-3.25 0zm9.058746 0l0 -16.65625l-2.875 0l0 -2.53125l2.875 0l0 -2.046875q0 -1.921875 0.34375 -2.859375q0.46875 -1.265625 1.640625 -2.046875q1.1875 -0.796875 3.328125 -0.796875q1.375 0 3.03125 0.328125l-0.484375 2.828125q-1.015625 -0.171875 -1.921875 -0.171875q-1.484375 0 -2.09375 0.640625q-0.609375 0.625 -0.609375 2.359375l0 1.765625l3.734375 0l0 2.53125l-3.734375 0l0 16.65625l-3.234375 0zm22.730347 -6.171875l3.359375 0.40625q-0.796875 2.953125 -2.953125 4.578125q-2.140625 1.625 -5.484375 1.625q-4.21875 0 -6.6875 -2.59375q-2.453125 -2.59375 -2.453125 -7.28125q0 -4.828125 2.484375 -7.5q2.5 -2.6875 6.46875 -2.6875q3.859375 0 6.296875 2.625q2.4375 2.625 2.4375 7.375q0 0.28125 -0.015625 0.859375l-14.3125 0q0.171875 3.171875 1.78125 4.859375q1.609375 1.671875 4.015625 1.671875q1.78125 0 3.046875 -0.9375q1.265625 -0.953125 2.015625 -3.0zm-10.6875 -5.265625l10.71875 0q-0.21875 -2.421875 -1.234375 -3.625q-1.546875 -1.890625 -4.015625 -1.890625q-2.25 0 -3.78125 1.5q-1.515625 1.5 -1.6875 4.015625zm17.010132 5.703125l3.21875 -0.5q0.265625 1.9375 1.5 2.96875q1.234375 1.03125 3.46875 1.03125q2.234375 0 3.3125 -0.90625q1.09375 -0.921875 1.09375 -2.15625q0 -1.09375 -0.96875 -1.734375q-0.65625 -0.4375 -3.3125 -1.09375q-3.578125 -0.90625 -4.96875 -1.5625q-1.375 -0.671875 -2.09375 -1.828125q-0.703125 -1.171875 -0.703125 -2.578125q0 -1.28125 0.578125 -2.375q0.59375 -1.09375 1.59375 -1.8125q0.765625 -0.5625 2.078125 -0.953125q1.3125 -0.390625 2.8125 -0.390625q2.25 0 3.953125 0.65625q1.71875 0.65625 2.53125 1.765625q0.8125 1.109375 1.109375 2.96875l-3.171875 0.4375q-0.21875 -1.484375 -1.265625 -2.3125q-1.03125 -0.84375 -2.921875 -0.84375q-2.25 0 -3.203125 0.75q-0.953125 0.734375 -0.953125 1.734375q0 0.625 0.390625 1.140625q0.40625 0.515625 1.25 0.859375q0.484375 0.1875 2.875 0.828125q3.453125 0.921875 4.8125 1.515625q1.359375 0.578125 2.140625 1.703125q0.78125 1.125 0.78125 2.78125q0 1.625 -0.953125 3.0625q-0.953125 1.4375 -2.75 2.234375q-1.78125 0.78125 -4.03125 0.78125q-3.75 0 -5.703125 -1.546875q-1.953125 -1.5625 -2.5 -4.625zm27.070312 2.828125l0.46875 2.875q-1.375 0.28125 -2.46875 0.28125q-1.765625 0 -2.75 -0.5625q-0.96875 -0.5625 -1.375 -1.46875q-0.390625 -0.90625 -0.390625 -3.84375l0 -11.03125l-2.375 0l0 -2.53125l2.375 0l0 -4.75l3.234375 -1.953125l0 6.703125l3.28125 0l0 2.53125l-3.28125 0l0 11.21875q0 1.390625 0.171875 1.796875q0.171875 0.390625 0.5625 0.625q0.390625 0.234375 1.109375 0.234375q0.546875 0 1.4375 -0.125zm1.9647217 -2.828125l3.21875 -0.5q0.265625 1.9375 1.5 2.96875q1.234375 1.03125 3.46875 1.03125q2.234375 0 3.3125 -0.90625q1.09375 -0.921875 1.09375 -2.15625q0 -1.09375 -0.96875 -1.734375q-0.65625 -0.4375 -3.3125 -1.09375q-3.578125 -0.90625 -4.96875 -1.5625q-1.375 -0.671875 -2.09375 -1.828125q-0.703125 -1.171875 -0.703125 -2.578125q0 -1.28125 0.578125 -2.375q0.59375 -1.09375 1.59375 -1.8125q0.765625 -0.5625 2.078125 -0.953125q1.3125 -0.390625 2.8125 -0.390625q2.25 0 3.953125 0.65625q1.71875 0.65625 2.53125 1.765625q0.8125 1.109375 1.109375 2.96875l-3.171875 0.4375q-0.21875 -1.484375 -1.265625 -2.3125q-1.03125 -0.84375 -2.921875 -0.84375q-2.25 0 -3.203125 0.75q-0.953125 0.734375 -0.953125 1.734375q0 0.625 0.390625 1.140625q0.40625 0.515625 1.25 0.859375q0.484375 0.1875 2.875 0.828125q3.453125 0.921875 4.8125 1.515625q1.359375 0.578125 2.140625 1.703125q0.78125 1.125 0.78125 2.78125q0 1.625 -0.953125 3.0625q-0.953125 1.4375 -2.75 2.234375q-1.78125 0.78125 -4.03125 0.78125q-3.75 0 -5.703125 -1.546875q-1.953125 -1.5625 -2.5 -4.625z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m32.08399 481.27823l894.5512 0l0 46.015717l-894.5512 0z" fill-rule="nonzero"></path><path fill="#000000" d="m46.005863 508.1982l0 -12.0l-4.46875 0l0 -1.59375l10.765625 0l0 1.59375l-4.5 0l0 12.0l-1.796875 0zm14.474106 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.547596 2.265625l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.2187538 -1.328125 -1.2187538 -3.796875q0 -1.59375 0.5156288 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm2.890625 3.609375l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0zm10.375717 0l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm10.391342 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm10.566696 -3.609375l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.328125 2.390625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.047592 4.9375l0 -13.59375l1.671875 0l0 13.59375l-1.671875 0zm8.6875 -2.9375l1.6562424 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.7031174 -0.34375 -1.0781174 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.8281174 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9374924 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm9.999992 6.71875l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm15.610092 1.703125l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.547592 2.265625l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm2.21875 0.671875l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.46875 -5.015625l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0zm0 7.953125l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0z" fill-rule="nonzero"></path><path fill="#0097a7" d="m186.46445 508.1982l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0zm14.031967 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm5.183304 0l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm1.5270538 5.28125l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm8.188217 1.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.46875 -5.015625l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0zm0 7.953125l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0zm3.4645538 0.234375l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm5.183304 0l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm12.823929 -0.234375l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm16.016342 1.75l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm11.844482 5.875l-3.75 -9.859375l1.765625 0l2.125 5.90625q0.34375 0.953125 0.625 1.984375q0.21875 -0.78125 0.625 -1.875l2.1875 -6.015625l1.71875 0l-3.734375 9.859375l-1.5625 0zm7.0625 0l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0zm11.152039 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0632324 4.9375l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm8.9626465 0l-3.75 -9.859375l1.765625 0l2.125 5.90625q0.34375 0.953125 0.625 1.984375q0.21875 -0.78125 0.625 -1.875l2.1875 -6.015625l1.71875 0l-3.734375 9.859375l-1.5625 0zm13.03125 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm10.469482 4.9375l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm8.641357 0q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm8.610077 1.984375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.46875 2.9375l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0zm4.089569 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.266327 4.921875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm5.931427 0.8125l1.609375 0.25q0.109375 0.75 0.578125 1.09375q0.609375 0.453125 1.6875 0.453125q1.171875 0 1.796875 -0.46875q0.625 -0.453125 0.859375 -1.28125q0.125 -0.515625 0.109375 -2.15625q-1.09375 1.296875 -2.71875 1.296875q-2.03125 0 -3.15625 -1.46875q-1.109375 -1.46875 -1.109375 -3.515625q0 -1.40625 0.515625 -2.59375q0.515625 -1.203125 1.484375 -1.84375q0.96875 -0.65625 2.265625 -0.65625q1.75 0 2.875 1.40625l0 -1.1875l1.546875 0l0 8.515625q0 2.3125 -0.46875 3.265625q-0.46875 0.96875 -1.484375 1.515625q-1.015625 0.5625305 -2.5 0.5625305q-1.765625 0 -2.859375 -0.7969055q-1.078125 -0.796875 -1.03125 -2.390625zm1.375 -5.921875q0 1.953125 0.765625 2.84375q0.78125 0.890625 1.9375 0.890625q1.140625 0 1.921875 -0.890625q0.78125 -0.890625 0.78125 -2.78125q0 -1.8125 -0.8125 -2.71875q-0.796875 -0.921875 -1.921875 -0.921875q-1.109375 0 -1.890625 0.90625q-0.78125 0.890625 -0.78125 2.671875zm8.047607 5.34375l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm6.4332886 3.546875l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm8.844482 4.875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm5.6032715 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.281982 -6.734375l0 -1.9375l1.65625 0l0 1.9375l-1.65625 0zm-2.125 15.4844055l0.3125 -1.4219055q0.5 0.125 0.796875 0.125q0.515625 0 0.765625 -0.34375q0.25 -0.328125 0.25 -1.6875l0 -10.359375l1.65625 0l0 10.390625q0 1.828125 -0.46875 2.546875q-0.59375 0.9219055 -2.0 0.9219055q-0.671875 0 -1.3125 -0.171875zm13.019836 -7.0000305l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.547577 2.265625l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm6.546875 2.109375l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm0.8551941 -1.4375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm8.7499695 3.171875l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm12.870789 -1.453125q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0632324 4.9375l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm8.962677 0l-3.75 -9.859375l1.765625 0l2.125 5.90625q0.34375 0.953125 0.625 1.984375q0.21875 -0.78125 0.625 -1.875l2.1875 -6.015625l1.71875 0l-3.734375 9.859375l-1.5625 0zm13.03125 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm10.469452 4.9375l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm8.641357 0q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm8.610107 1.984375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.7812805 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.8437805 -0.46875 -2.5625305 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375305 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.1562805 0 -1.6406555 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.4687805 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.9219055 0 -2.9375305 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm8.7500305 3.171875l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm8.261414 -0.234375l-3.015625 -9.859375l1.71875 0l1.5625 5.6875l0.59375 2.125q0.03125 -0.15625 0.5 -2.03125l1.578125 -5.78125l1.71875 0l1.46875 5.71875l0.484375 1.890625l0.578125 -1.90625l1.6875 -5.703125l1.625 0l-3.078125 9.859375l-1.734375 0l-1.578125 -5.90625l-0.375 -1.671875l-2.0 7.578125l-1.734375 0zm11.660461 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm4.1448364 0l0 -13.59375l1.671875 0l0 7.75l3.953125 -4.015625l2.15625 0l-3.765625 3.65625l4.140625 6.203125l-2.0625 0l-3.25 -5.03125l-1.171875 1.125l0 3.90625l-1.671875 0zm9.328125 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm2.8791504 0.234375l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm6.5739746 -0.234375l0 -13.59375l1.796875 0l0 6.734375l6.765625 -6.734375l2.4375 0l-5.703125 5.5l5.953125 8.09375l-2.375 0l-4.84375 -6.890625l-2.234375 2.171875l0 4.71875l-1.796875 0zm19.052917 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.860046 2.703125l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.110107 9.65625l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm7.3288574 8.65625l0 -1.21875l11.0625 0l0 1.21875l-11.0625 0zm11.906982 -3.78125l0 -9.859375l1.5 0l0 1.390625q0.453125 -0.71875 1.21875 -1.15625q0.78125 -0.453125 1.765625 -0.453125q1.09375 0 1.796875 0.453125q0.703125 0.453125 0.984375 1.28125q1.171875 -1.734375 3.046875 -1.734375q1.46875 0 2.25 0.8125q0.796875 0.8125 0.796875 2.5l0 6.765625l-1.671875 0l0 -6.203125q0 -1.0 -0.15625 -1.4375q-0.15625 -0.453125 -0.59375 -0.71875q-0.421875 -0.265625 -1.0 -0.265625q-1.03125 0 -1.71875 0.6875q-0.6875 0.6875 -0.6875 2.21875l0 5.71875l-1.671875 0l0 -6.40625q0 -1.109375 -0.40625 -1.65625q-0.40625 -0.5625 -1.34375 -0.5625q-0.703125 0 -1.3125 0.375q-0.59375 0.359375 -0.859375 1.078125q-0.265625 0.71875 -0.265625 2.0625l0 5.109375l-1.671875 0zm21.978333 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0787964 4.9375l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm10.391357 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm4.5355225 0l0 -8.546875l-1.484375 0l0 -1.3125l1.484375 0l0 -1.046875q0 -0.984375 0.171875 -1.46875q0.234375 -0.65625 0.84375 -1.046875q0.609375 -0.40625 1.703125 -0.40625q0.703125 0 1.5625 0.15625l-0.25 1.46875q-0.515625 -0.09375 -0.984375 -0.09375q-0.765625 0 -1.078125 0.328125q-0.3125 0.3125 -0.3125 1.203125l0 0.90625l1.921875 0l0 1.3125l-1.921875 0l0 8.546875l-1.65625 0zm11.526978 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.438232 2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm13.65625 1.4375l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm-0.0041503906 5.28125l0 -1.21875l11.0625 0l0 1.21875l-11.0625 0zm12.313232 -3.78125l0 -8.546875l-1.484375 0l0 -1.3125l1.484375 0l0 -1.046875q0 -0.984375 0.171875 -1.46875q0.234375 -0.65625 0.84375 -1.046875q0.609375 -0.40625 1.703125 -0.40625q0.703125 0 1.5625 0.15625l-0.25 1.46875q-0.515625 -0.09375 -0.984375 -0.09375q-0.765625 0 -1.078125 0.328125q-0.3125 0.3125 -0.3125 1.203125l0 0.90625l1.921875 0l0 1.3125l-1.921875 0l0 8.546875l-1.65625 0zm4.1519775 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.266357 4.921875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm6.2283936 0l0 -9.859375l1.5 0l0 1.390625q0.453125 -0.71875 1.21875 -1.15625q0.78125 -0.453125 1.765625 -0.453125q1.09375 0 1.796875 0.453125q0.703125 0.453125 0.984375 1.28125q1.171875 -1.734375 3.046875 -1.734375q1.46875 0 2.25 0.8125q0.796875 0.8125 0.796875 2.5l0 6.765625l-1.671875 0l0 -6.203125q0 -1.0 -0.15625 -1.4375q-0.15625 -0.453125 -0.59375 -0.71875q-0.421875 -0.265625 -1.0 -0.265625q-1.03125 0 -1.71875 0.6875q-0.6875 0.6875 -0.6875 2.21875l0 5.71875l-1.671875 0l0 -6.40625q0 -1.109375 -0.40625 -1.65625q-0.40625 -0.5625 -1.34375 -0.5625q-0.703125 0 -1.3125 0.375q-0.59375 0.359375 -0.859375 1.078125q-0.265625 0.71875 -0.265625 2.0625l0 5.109375l-1.671875 0zm21.978271 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm7.7351074 3.4375l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125z" fill-rule="nonzero"></path><path stroke="#0097a7" stroke-width="1.3671875" stroke-linecap="butt" d="m185.21445 510.1761l560.99927 0" fill-rule="nonzero"></path><a xlink:href="https://www.google.com/url?q=https://dev.arvados.org/projects/arvados/wiki/Keep_manifest_format&amp;sa=D&amp;ust=1478895969188000&amp;usg=AFQjCNHMNIzr5ezz4laFKPqTOrFHC9sgsA" target="_blank" rel="noreferrer"><path fill="transparent" fill-opacity="0" d="m185.21445 512.15173l0 -20.84253l560.99927 0l0 20.84253z" fill-rule="evenodd"></path></a><path fill="#000000" fill-opacity="0.0" d="m178.12337 46.721786l600.2048 0l0 473.36218l-600.2048 0z" fill-rule="nonzero"></path><g transform="matrix(0.6538178477690288 0.0 0.0 0.6538152230971128 178.12336036745407 46.72178477690289)"><clipPath id="g1586814eb6_0_6.1"><path d="m0 1.4210855E-14l918.0 0l0 724.0l-918.0 0z" clip-rule="nonzero"></path></clipPath><image clip-path="url(#g1586814eb6_0_6.1)" fill="#000" width="918.0" height="724.0" x="0.0" y="0.0" preserveAspectRatio="none" xlink:href="data:image/png;base64,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"></image></g></g></svg>
-
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
+   sodipodi:docname="Keep_manifests.svg"
+   id="svg34"
+   stroke-miterlimit="10"
+   stroke-linecap="square"
+   stroke="none"
+   fill="none"
+   viewBox="0.0 0.0 960.0 540.0"
+   version="1.1">
+  <metadata
+     id="metadata40">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs38" />
+  <sodipodi:namedview
+     inkscape:current-layer="svg34"
+     inkscape:window-maximized="0"
+     inkscape:window-y="136"
+     inkscape:window-x="1637"
+     inkscape:cy="270"
+     inkscape:cx="480"
+     inkscape:zoom="1.3739583"
+     showgrid="false"
+     id="namedview36"
+     inkscape:window-height="1789"
+     inkscape:window-width="2203"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0"
+     guidetolerance="10"
+     gridtolerance="10"
+     objecttolerance="10"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff" />
+  <clipPath
+     id="g1586814eb6_0_6.0">
+    <path
+       id="path2"
+       clip-rule="nonzero"
+       d="m0 0l960.0 0l0 540.0l-960.0 0l0 -540.0z" />
+  </clipPath>
+  <g
+     id="g32"
+     clip-path="url(#g1586814eb6_0_6.0)">
+    <path
+       id="path5"
+       fill-rule="nonzero"
+       d="m0 0l960.0 0l0 540.0l-960.0 0z"
+       fill="#ffffff" />
+    <path
+       id="path7"
+       fill-rule="nonzero"
+       d="m32.72441 46.721786l894.55115 0l0 60.125984l-894.55115 0z"
+       fill-opacity="0.0"
+       fill="#000000" />
+    <path
+       id="path9"
+       fill-rule="nonzero"
+       d="m63.47441 82.28053l3.5 0.875q-1.09375 4.328125 -3.96875 6.59375q-2.859375 2.265625 -6.984375 2.265625q-4.28125 0 -6.96875 -1.734375q-2.6875 -1.75 -4.09375 -5.046875q-1.390625 -3.3125 -1.390625 -7.109375q0 -4.140625 1.578125 -7.21875q1.578125 -3.078125 4.5 -4.671875q2.921875 -1.609375 6.421875 -1.609375q3.96875 0 6.671875 2.03125q2.71875 2.015625 3.796875 5.6875l-3.453125 0.8125q-0.921875 -2.890625 -2.6875 -4.203125q-1.75 -1.328125 -4.40625 -1.328125q-3.046875 0 -5.09375 1.46875q-2.046875 1.453125 -2.890625 3.921875q-0.828125 2.46875 -0.828125 5.09375q0 3.375 0.984375 5.890625q0.984375 2.515625 3.0625 3.765625q2.078125 1.25 4.5 1.25q2.953125 0 4.984375 -1.6875q2.046875 -1.703125 2.765625 -5.046875zm6.441559 -0.3125q0 -5.328125 2.953125 -7.890625q2.484375 -2.140625 6.03125 -2.140625q3.96875 0 6.46875 2.59375q2.515625 2.59375 2.515625 7.171875q0 3.703125 -1.109375 5.828125q-1.109375 2.109375 -3.234375 3.296875q-2.125 1.171875 -4.640625 1.171875q-4.015625 0 -6.5 -2.578125q-2.484375 -2.59375 -2.484375 -7.453125zm3.34375 0q0 3.6875 1.59375 5.53125q1.609375 1.828125 4.046875 1.828125q2.421875 0 4.03125 -1.84375q1.609375 -1.84375 1.609375 -5.625q0 -3.5625 -1.625 -5.390625q-1.609375 -1.828125 -4.015625 -1.828125q-2.4375 0 -4.046875 1.828125q-1.59375 1.8125 -1.59375 5.5zm18.541382 9.59375l0 -26.484375l3.265625 0l0 26.484375l-3.265625 0zm8.293121 0l0 -26.484375l3.265625 0l0 26.484375l-3.265625 0zm21.511871 -6.171875l3.359375 0.40625q-0.796875 2.953125 -2.953125 4.578125q-2.140625 1.625 -5.484375 1.625q-4.21875 0 -6.6875 -2.59375q-2.453125 -2.59375 -2.453125 -7.28125q0 -4.828125 2.484375 -7.5q2.5 -2.6875 6.46875 -2.6875q3.859375 0 6.296875 2.625q2.4375 2.625 2.4375 7.375q0 0.28125 -0.015625 0.859375l-14.3125 0q0.171875 3.171875 1.78125 4.859375q1.609375 1.671875 4.015625 1.671875q1.78125 0 3.046875 -0.9375q1.265625 -0.953125 2.015625 -3.0zm-10.6875 -5.265625l10.71875 0q-0.21875 -2.421875 -1.234375 -3.625q-1.546875 -1.890625 -4.015625 -1.890625q-2.25 0 -3.78125 1.5q-1.515625 1.5 -1.6875 4.015625zm30.822632 4.40625l3.203125 0.421875q-0.515625 3.296875 -2.6875 5.171875q-2.15625 1.875 -5.296875 1.875q-3.9375 0 -6.328125 -2.578125q-2.390625 -2.578125 -2.390625 -7.375q0 -3.109375 1.015625 -5.4375q1.03125 -2.34375 3.140625 -3.5q2.109375 -1.171875 4.578125 -1.171875q3.125 0 5.109375 1.59375q2.0 1.578125 2.546875 4.484375l-3.15625 0.484375q-0.453125 -1.9375 -1.609375 -2.90625q-1.140625 -0.984375 -2.765625 -0.984375q-2.453125 0 -4.0 1.765625q-1.53125 1.765625 -1.53125 5.578125q0 3.859375 1.484375 5.625q1.484375 1.75 3.875 1.75q1.90625 0 3.1875 -1.171875q1.28125 -1.1875 1.625 -3.625zm13.2578125 4.125l0.46875 2.875q-1.375 0.28125 -2.46875 0.28125q-1.765625 0 -2.75 -0.5625q-0.96875 -0.5625 -1.375 -1.46875q-0.390625 -0.90625 -0.390625 -3.84375l0 -11.03125l-2.375 0l0 -2.53125l2.375 0l0 -4.75l3.234375 -1.953125l0 6.703125l3.28125 0l0 2.53125l-3.28125 0l0 11.21875q0 1.390625 0.171875 1.796875q0.171875 0.390625 0.5625 0.625q0.390625 0.234375 1.109375 0.234375q0.546875 0 1.4375 -0.125zm3.2772064 -19.84375l0 -3.734375l3.25 0l0 3.734375l-3.25 0zm0 22.75l0 -19.1875l3.25 0l0 19.1875l-3.25 0zm7.0743713 -9.59375q0 -5.328125 2.953125 -7.890625q2.484375 -2.140625 6.03125 -2.140625q3.96875 0 6.46875 2.59375q2.515625 2.59375 2.515625 7.171875q0 3.703125 -1.109375 5.828125q-1.109375 2.109375 -3.234375 3.296875q-2.125 1.171875 -4.640625 1.171875q-4.015625 0 -6.5 -2.578125q-2.484375 -2.59375 -2.484375 -7.453125zm3.34375 0q0 3.6875 1.59375 5.53125q1.609375 1.828125 4.046875 1.828125q2.421875 0 4.03125 -1.84375q1.609375 -1.84375 1.609375 -5.625q0 -3.5625 -1.625 -5.390625q-1.609375 -1.828125 -4.015625 -1.828125q-2.4375 0 -4.046875 1.828125q-1.59375 1.8125 -1.59375 5.5zm18.619507 9.59375l0 -19.1875l2.921875 0l0 2.734375q2.125 -3.171875 6.109375 -3.171875q1.734375 0 3.1875 0.625q1.453125 0.625 2.171875 1.640625q0.734375 1.015625 1.015625 2.40625q0.1875 0.890625 0.1875 3.15625l0 11.796875l-3.25 0l0 -11.671875q0 -1.984375 -0.390625 -2.96875q-0.375 -0.984375 -1.34375 -1.5625q-0.953125 -0.59375 -2.265625 -0.59375q-2.078125 0 -3.59375 1.3125q-1.5 1.3125 -1.5 5.0l0 10.484375l-3.25 0zm19.463257 -5.734375l3.21875 -0.5q0.265625 1.9375 1.5 2.96875q1.234375 1.03125 3.46875 1.03125q2.234375 0 3.3125 -0.90625q1.09375 -0.921875 1.09375 -2.15625q0 -1.09375 -0.96875 -1.734375q-0.65625 -0.4375 -3.3125 -1.09375q-3.578125 -0.90625 -4.96875 -1.5625q-1.375 -0.671875 -2.09375 -1.828125q-0.703125 -1.171875 -0.703125 -2.578125q0 -1.28125 0.578125 -2.375q0.59375 -1.09375 1.59375 -1.8125q0.765625 -0.5625 2.078125 -0.953125q1.3125 -0.390625 2.8125 -0.390625q2.25 0 3.953125 0.65625q1.71875 0.65625 2.53125 1.765625q0.8125 1.109375 1.109375 2.96875l-3.171875 0.4375q-0.21875 -1.484375 -1.265625 -2.3125q-1.03125 -0.84375 -2.921875 -0.84375q-2.25 0 -3.203125 0.75q-0.953125 0.734375 -0.953125 1.734375q0 0.625 0.390625 1.140625q0.40625 0.515625 1.25 0.859375q0.484375 0.1875 2.875 0.828125q3.453125 0.921875 4.8125 1.515625q1.359375 0.578125 2.140625 1.703125q0.78125 1.125 0.78125 2.78125q0 1.625 -0.953125 3.0625q-0.953125 1.4375 -2.75 2.234375q-1.78125 0.78125 -4.03125 0.78125q-3.75 0 -5.703125 -1.546875q-1.953125 -1.5625 -2.5 -4.625zm20.867188 -9.75l0 -3.703125l3.703125 0l0 3.703125l-3.703125 0zm0 15.484375l0 -3.703125l3.703125 0l0 3.703125l-3.703125 0zm20.148163 0l0 -26.484375l5.265625 0l6.28125 18.75q0.859375 2.625 1.265625 3.921875q0.4375 -1.4375 1.40625 -4.25l6.34375 -18.421875l4.703125 0l0 26.484375l-3.375 0l0 -22.171875l-7.6875 22.171875l-3.171875 0l-7.65625 -22.546875l0 22.546875l-3.375 0zm43.29773 -2.359375q-1.796875 1.53125 -3.46875 2.171875q-1.671875 0.625 -3.59375 0.625q-3.15625 0 -4.859375 -1.546875q-1.6875 -1.546875 -1.6875 -3.953125q0 -1.40625 0.640625 -2.5625q0.640625 -1.171875 1.671875 -1.875q1.046875 -0.703125 2.34375 -1.0625q0.953125 -0.265625 2.890625 -0.5q3.9375 -0.46875 5.796875 -1.109375q0.015625 -0.671875 0.015625 -0.859375q0 -1.984375 -0.921875 -2.796875q-1.25 -1.09375 -3.703125 -1.09375q-2.296875 0 -3.390625 0.796875q-1.09375 0.796875 -1.609375 2.84375l-3.1875 -0.4375q0.4375 -2.03125 1.421875 -3.28125q1.0 -1.265625 2.875 -1.9375q1.890625 -0.6875 4.359375 -0.6875q2.46875 0 4.0 0.578125q1.53125 0.578125 2.25 1.453125q0.734375 0.875 1.015625 2.21875q0.15625 0.828125 0.15625 3.0l0 4.328125q0 4.546875 0.203125 5.75q0.21875 1.1875 0.828125 2.296875l-3.390625 0q-0.5 -1.015625 -0.65625 -2.359375zm-0.265625 -7.265625q-1.765625 0.71875 -5.3125 1.21875q-2.0 0.296875 -2.84375 0.65625q-0.828125 0.359375 -1.28125 1.0625q-0.4375 0.6875 -0.4375 1.53125q0 1.3125 0.984375 2.1875q0.984375 0.859375 2.875 0.859375q1.875 0 3.34375 -0.828125q1.46875 -0.828125 2.15625 -2.25q0.515625 -1.09375 0.515625 -3.25l0 -1.1875zm8.510132 9.625l0 -19.1875l2.921875 0l0 2.734375q2.125 -3.171875 6.109375 -3.171875q1.734375 0 3.1875 0.625q1.453125 0.625 2.171875 1.640625q0.734375 1.015625 1.015625 2.40625q0.1875 0.890625 0.1875 3.15625l0 11.796875l-3.25 0l0 -11.671875q0 -1.984375 -0.390625 -2.96875q-0.375 -0.984375 -1.34375 -1.5625q-0.953125 -0.59375 -2.265625 -0.59375q-2.078125 0 -3.59375 1.3125q-1.5 1.3125 -1.5 5.0l0 10.484375l-3.25 0zm20.775757 -22.75l0 -3.734375l3.25 0l0 3.734375l-3.25 0zm0 22.75l0 -19.1875l3.25 0l0 19.1875l-3.25 0zm9.058746 0l0 -16.65625l-2.875 0l0 -2.53125l2.875 0l0 -2.046875q0 -1.921875 0.34375 -2.859375q0.46875 -1.265625 1.640625 -2.046875q1.1875 -0.796875 3.328125 -0.796875q1.375 0 3.03125 0.328125l-0.484375 2.828125q-1.015625 -0.171875 -1.921875 -0.171875q-1.484375 0 -2.09375 0.640625q-0.609375 0.625 -0.609375 2.359375l0 1.765625l3.734375 0l0 2.53125l-3.734375 0l0 16.65625l-3.234375 0zm22.730347 -6.171875l3.359375 0.40625q-0.796875 2.953125 -2.953125 4.578125q-2.140625 1.625 -5.484375 1.625q-4.21875 0 -6.6875 -2.59375q-2.453125 -2.59375 -2.453125 -7.28125q0 -4.828125 2.484375 -7.5q2.5 -2.6875 6.46875 -2.6875q3.859375 0 6.296875 2.625q2.4375 2.625 2.4375 7.375q0 0.28125 -0.015625 0.859375l-14.3125 0q0.171875 3.171875 1.78125 4.859375q1.609375 1.671875 4.015625 1.671875q1.78125 0 3.046875 -0.9375q1.265625 -0.953125 2.015625 -3.0zm-10.6875 -5.265625l10.71875 0q-0.21875 -2.421875 -1.234375 -3.625q-1.546875 -1.890625 -4.015625 -1.890625q-2.25 0 -3.78125 1.5q-1.515625 1.5 -1.6875 4.015625zm17.010132 5.703125l3.21875 -0.5q0.265625 1.9375 1.5 2.96875q1.234375 1.03125 3.46875 1.03125q2.234375 0 3.3125 -0.90625q1.09375 -0.921875 1.09375 -2.15625q0 -1.09375 -0.96875 -1.734375q-0.65625 -0.4375 -3.3125 -1.09375q-3.578125 -0.90625 -4.96875 -1.5625q-1.375 -0.671875 -2.09375 -1.828125q-0.703125 -1.171875 -0.703125 -2.578125q0 -1.28125 0.578125 -2.375q0.59375 -1.09375 1.59375 -1.8125q0.765625 -0.5625 2.078125 -0.953125q1.3125 -0.390625 2.8125 -0.390625q2.25 0 3.953125 0.65625q1.71875 0.65625 2.53125 1.765625q0.8125 1.109375 1.109375 2.96875l-3.171875 0.4375q-0.21875 -1.484375 -1.265625 -2.3125q-1.03125 -0.84375 -2.921875 -0.84375q-2.25 0 -3.203125 0.75q-0.953125 0.734375 -0.953125 1.734375q0 0.625 0.390625 1.140625q0.40625 0.515625 1.25 0.859375q0.484375 0.1875 2.875 0.828125q3.453125 0.921875 4.8125 1.515625q1.359375 0.578125 2.140625 1.703125q0.78125 1.125 0.78125 2.78125q0 1.625 -0.953125 3.0625q-0.953125 1.4375 -2.75 2.234375q-1.78125 0.78125 -4.03125 0.78125q-3.75 0 -5.703125 -1.546875q-1.953125 -1.5625 -2.5 -4.625zm27.070312 2.828125l0.46875 2.875q-1.375 0.28125 -2.46875 0.28125q-1.765625 0 -2.75 -0.5625q-0.96875 -0.5625 -1.375 -1.46875q-0.390625 -0.90625 -0.390625 -3.84375l0 -11.03125l-2.375 0l0 -2.53125l2.375 0l0 -4.75l3.234375 -1.953125l0 6.703125l3.28125 0l0 2.53125l-3.28125 0l0 11.21875q0 1.390625 0.171875 1.796875q0.171875 0.390625 0.5625 0.625q0.390625 0.234375 1.109375 0.234375q0.546875 0 1.4375 -0.125zm1.9647217 -2.828125l3.21875 -0.5q0.265625 1.9375 1.5 2.96875q1.234375 1.03125 3.46875 1.03125q2.234375 0 3.3125 -0.90625q1.09375 -0.921875 1.09375 -2.15625q0 -1.09375 -0.96875 -1.734375q-0.65625 -0.4375 -3.3125 -1.09375q-3.578125 -0.90625 -4.96875 -1.5625q-1.375 -0.671875 -2.09375 -1.828125q-0.703125 -1.171875 -0.703125 -2.578125q0 -1.28125 0.578125 -2.375q0.59375 -1.09375 1.59375 -1.8125q0.765625 -0.5625 2.078125 -0.953125q1.3125 -0.390625 2.8125 -0.390625q2.25 0 3.953125 0.65625q1.71875 0.65625 2.53125 1.765625q0.8125 1.109375 1.109375 2.96875l-3.171875 0.4375q-0.21875 -1.484375 -1.265625 -2.3125q-1.03125 -0.84375 -2.921875 -0.84375q-2.25 0 -3.203125 0.75q-0.953125 0.734375 -0.953125 1.734375q0 0.625 0.390625 1.140625q0.40625 0.515625 1.25 0.859375q0.484375 0.1875 2.875 0.828125q3.453125 0.921875 4.8125 1.515625q1.359375 0.578125 2.140625 1.703125q0.78125 1.125 0.78125 2.78125q0 1.625 -0.953125 3.0625q-0.953125 1.4375 -2.75 2.234375q-1.78125 0.78125 -4.03125 0.78125q-3.75 0 -5.703125 -1.546875q-1.953125 -1.5625 -2.5 -4.625z"
+       fill="#000000" />
+    <path
+       id="path11"
+       fill-rule="nonzero"
+       d="m32.08399 481.27823l894.5512 0l0 46.015717l-894.5512 0z"
+       fill-opacity="0.0"
+       fill="#000000" />
+    <path
+       id="path13"
+       fill-rule="nonzero"
+       d="m46.005863 508.1982l0 -12.0l-4.46875 0l0 -1.59375l10.765625 0l0 1.59375l-4.5 0l0 12.0l-1.796875 0zm14.474106 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.547596 2.265625l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.2187538 -1.328125 -1.2187538 -3.796875q0 -1.59375 0.5156288 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm2.890625 3.609375l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0zm10.375717 0l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm10.391342 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm10.566696 -3.609375l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm9.328125 2.390625q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.047592 4.9375l0 -13.59375l1.671875 0l0 13.59375l-1.671875 0zm8.6875 -2.9375l1.6562424 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.7031174 -0.34375 -1.0781174 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.8281174 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9374924 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm9.999992 6.71875l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm15.610092 1.703125l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.547592 2.265625l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm2.21875 0.671875l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.46875 -5.015625l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0zm0 7.953125l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0z"
+       fill="#000000" />
+    <path
+       id="path15"
+       fill-rule="nonzero"
+       d="m186.46445 508.1982l0 -13.59375l1.671875 0l0 4.875q1.171875 -1.359375 2.953125 -1.359375q1.09375 0 1.890625 0.4375q0.8125 0.421875 1.15625 1.1875q0.359375 0.765625 0.359375 2.203125l0 6.25l-1.671875 0l0 -6.25q0 -1.25 -0.546875 -1.8125q-0.546875 -0.578125 -1.53125 -0.578125q-0.75 0 -1.40625 0.390625q-0.640625 0.375 -0.921875 1.046875q-0.28125 0.65625 -0.28125 1.8125l0 5.390625l-1.671875 0zm14.031967 -1.5l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm5.183304 0l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm1.5270538 5.28125l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm8.188217 1.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.46875 -5.015625l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0zm0 7.953125l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0zm3.4645538 0.234375l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm5.183304 0l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm12.823929 -0.234375l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm16.016342 1.75l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm11.844482 5.875l-3.75 -9.859375l1.765625 0l2.125 5.90625q0.34375 0.953125 0.625 1.984375q0.21875 -0.78125 0.625 -1.875l2.1875 -6.015625l1.71875 0l-3.734375 9.859375l-1.5625 0zm7.0625 0l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0zm11.152039 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0632324 4.9375l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm8.9626465 0l-3.75 -9.859375l1.765625 0l2.125 5.90625q0.34375 0.953125 0.625 1.984375q0.21875 -0.78125 0.625 -1.875l2.1875 -6.015625l1.71875 0l-3.734375 9.859375l-1.5625 0zm13.03125 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm10.469482 4.9375l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm8.641357 0q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm8.610077 1.984375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm10.46875 2.9375l0 -1.90625l1.90625 0l0 1.90625l-1.90625 0zm4.089569 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.266327 4.921875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm5.931427 0.8125l1.609375 0.25q0.109375 0.75 0.578125 1.09375q0.609375 0.453125 1.6875 0.453125q1.171875 0 1.796875 -0.46875q0.625 -0.453125 0.859375 -1.28125q0.125 -0.515625 0.109375 -2.15625q-1.09375 1.296875 -2.71875 1.296875q-2.03125 0 -3.15625 -1.46875q-1.109375 -1.46875 -1.109375 -3.515625q0 -1.40625 0.515625 -2.59375q0.515625 -1.203125 1.484375 -1.84375q0.96875 -0.65625 2.265625 -0.65625q1.75 0 2.875 1.40625l0 -1.1875l1.546875 0l0 8.515625q0 2.3125 -0.46875 3.265625q-0.46875 0.96875 -1.484375 1.515625q-1.015625 0.5625305 -2.5 0.5625305q-1.765625 0 -2.859375 -0.7969055q-1.078125 -0.796875 -1.03125 -2.390625zm1.375 -5.921875q0 1.953125 0.765625 2.84375q0.78125 0.890625 1.9375 0.890625q1.140625 0 1.921875 -0.890625q0.78125 -0.890625 0.78125 -2.78125q0 -1.8125 -0.8125 -2.71875q-0.796875 -0.921875 -1.921875 -0.921875q-1.109375 0 -1.890625 0.90625q-0.78125 0.890625 -0.78125 2.671875zm8.047607 5.34375l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm6.4332886 3.546875l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm8.844482 4.875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm5.6032715 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.281982 -6.734375l0 -1.9375l1.65625 0l0 1.9375l-1.65625 0zm-2.125 15.4844055l0.3125 -1.4219055q0.5 0.125 0.796875 0.125q0.515625 0 0.765625 -0.34375q0.25 -0.328125 0.25 -1.6875l0 -10.359375l1.65625 0l0 10.390625q0 1.828125 -0.46875 2.546875q-0.59375 0.9219055 -2.0 0.9219055q-0.671875 0 -1.3125 -0.171875zm13.019836 -7.0000305l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.547577 2.265625l1.640625 0.21875q-0.265625 1.6875 -1.375 2.65625q-1.109375 0.953125 -2.734375 0.953125q-2.015625 0 -3.25 -1.3125q-1.21875 -1.328125 -1.21875 -3.796875q0 -1.59375 0.515625 -2.78125q0.53125 -1.203125 1.609375 -1.796875q1.09375 -0.609375 2.359375 -0.609375q1.609375 0 2.625 0.8125q1.015625 0.8125 1.3125 2.3125l-1.625 0.25q-0.234375 -1.0 -0.828125 -1.5q-0.59375 -0.5 -1.421875 -0.5q-1.265625 0 -2.0625 0.90625q-0.78125 0.90625 -0.78125 2.859375q0 1.984375 0.765625 2.890625q0.765625 0.890625 1.984375 0.890625q0.984375 0 1.640625 -0.59375q0.65625 -0.609375 0.84375 -1.859375zm6.546875 2.109375l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm0.8551941 -1.4375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm8.7499695 3.171875l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm12.870789 -1.453125q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0632324 4.9375l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm8.962677 0l-3.75 -9.859375l1.765625 0l2.125 5.90625q0.34375 0.953125 0.625 1.984375q0.21875 -0.78125 0.625 -1.875l2.1875 -6.015625l1.71875 0l-3.734375 9.859375l-1.5625 0zm13.03125 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm10.469452 4.9375l0 -1.25q-0.9375 1.46875 -2.75 1.46875q-1.171875 0 -2.171875 -0.640625q-0.984375 -0.65625 -1.53125 -1.8125q-0.53125 -1.171875 -0.53125 -2.6875q0 -1.46875 0.484375 -2.671875q0.5 -1.203125 1.46875 -1.84375q0.984375 -0.640625 2.203125 -0.640625q0.890625 0 1.578125 0.375q0.703125 0.375 1.140625 0.984375l0 -4.875l1.65625 0l0 13.59375l-1.546875 0zm-5.28125 -4.921875q0 1.890625 0.796875 2.828125q0.8125 0.9375 1.890625 0.9375q1.09375 0 1.859375 -0.890625q0.765625 -0.890625 0.765625 -2.734375q0 -2.015625 -0.78125 -2.953125q-0.78125 -0.953125 -1.921875 -0.953125q-1.109375 0 -1.859375 0.90625q-0.75 0.90625 -0.75 2.859375zm8.641357 0q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm8.610107 1.984375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.7812805 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.8437805 -0.46875 -2.5625305 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375305 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.1562805 0 -1.6406555 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.4687805 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.9219055 0 -2.9375305 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm8.7500305 3.171875l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm8.261414 -0.234375l-3.015625 -9.859375l1.71875 0l1.5625 5.6875l0.59375 2.125q0.03125 -0.15625 0.5 -2.03125l1.578125 -5.78125l1.71875 0l1.46875 5.71875l0.484375 1.890625l0.578125 -1.90625l1.6875 -5.703125l1.625 0l-3.078125 9.859375l-1.734375 0l-1.578125 -5.90625l-0.375 -1.671875l-2.0 7.578125l-1.734375 0zm11.660461 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm4.1448364 0l0 -13.59375l1.671875 0l0 7.75l3.953125 -4.015625l2.15625 0l-3.765625 3.65625l4.140625 6.203125l-2.0625 0l-3.25 -5.03125l-1.171875 1.125l0 3.90625l-1.671875 0zm9.328125 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm2.8791504 0.234375l3.9375 -14.0625l1.34375 0l-3.9375 14.0625l-1.34375 0zm6.5739746 -0.234375l0 -13.59375l1.796875 0l0 6.734375l6.765625 -6.734375l2.4375 0l-5.703125 5.5l5.953125 8.09375l-2.375 0l-4.84375 -6.890625l-2.234375 2.171875l0 4.71875l-1.796875 0zm19.052917 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm15.860046 2.703125l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm9.110107 9.65625l0 -13.640625l1.53125 0l0 1.28125q0.53125 -0.75 1.203125 -1.125q0.6875 -0.375 1.640625 -0.375q1.265625 0 2.234375 0.65625q0.96875 0.640625 1.453125 1.828125q0.5 1.1875 0.5 2.59375q0 1.515625 -0.546875 2.734375q-0.546875 1.203125 -1.578125 1.84375q-1.03125 0.640625 -2.171875 0.640625q-0.84375 0 -1.515625 -0.34375q-0.65625 -0.359375 -1.078125 -0.890625l0 4.796875l-1.671875 0zm1.515625 -8.65625q0 1.90625 0.765625 2.8125q0.78125 0.90625 1.875 0.90625q1.109375 0 1.890625 -0.9375q0.796875 -0.9375 0.796875 -2.921875q0 -1.875 -0.78125 -2.8125q-0.765625 -0.9375 -1.84375 -0.9375q-1.0625 0 -1.890625 1.0q-0.8125 1.0 -0.8125 2.890625zm7.3288574 8.65625l0 -1.21875l11.0625 0l0 1.21875l-11.0625 0zm11.906982 -3.78125l0 -9.859375l1.5 0l0 1.390625q0.453125 -0.71875 1.21875 -1.15625q0.78125 -0.453125 1.765625 -0.453125q1.09375 0 1.796875 0.453125q0.703125 0.453125 0.984375 1.28125q1.171875 -1.734375 3.046875 -1.734375q1.46875 0 2.25 0.8125q0.796875 0.8125 0.796875 2.5l0 6.765625l-1.671875 0l0 -6.203125q0 -1.0 -0.15625 -1.4375q-0.15625 -0.453125 -0.59375 -0.71875q-0.421875 -0.265625 -1.0 -0.265625q-1.03125 0 -1.71875 0.6875q-0.6875 0.6875 -0.6875 2.21875l0 5.71875l-1.671875 0l0 -6.40625q0 -1.109375 -0.40625 -1.65625q-0.40625 -0.5625 -1.34375 -0.5625q-0.703125 0 -1.3125 0.375q-0.59375 0.359375 -0.859375 1.078125q-0.265625 0.71875 -0.265625 2.0625l0 5.109375l-1.671875 0zm21.978333 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm4.0787964 4.9375l0 -9.859375l1.5 0l0 1.40625q1.09375 -1.625 3.140625 -1.625q0.890625 0 1.640625 0.328125q0.75 0.3125 1.109375 0.84375q0.375 0.515625 0.53125 1.21875q0.09375 0.46875 0.09375 1.625l0 6.0625l-1.671875 0l0 -6.0q0 -1.015625 -0.203125 -1.515625q-0.1875 -0.515625 -0.6875 -0.8125q-0.5 -0.296875 -1.171875 -0.296875q-1.0625 0 -1.84375 0.671875q-0.765625 0.671875 -0.765625 2.578125l0 5.375l-1.671875 0zm10.391357 -11.6875l0 -1.90625l1.671875 0l0 1.90625l-1.671875 0zm0 11.6875l0 -9.859375l1.671875 0l0 9.859375l-1.671875 0zm4.5355225 0l0 -8.546875l-1.484375 0l0 -1.3125l1.484375 0l0 -1.046875q0 -0.984375 0.171875 -1.46875q0.234375 -0.65625 0.84375 -1.046875q0.609375 -0.40625 1.703125 -0.40625q0.703125 0 1.5625 0.15625l-0.25 1.46875q-0.515625 -0.09375 -0.984375 -0.09375q-0.765625 0 -1.078125 0.328125q-0.3125 0.3125 -0.3125 1.203125l0 0.90625l1.921875 0l0 1.3125l-1.921875 0l0 8.546875l-1.65625 0zm11.526978 -3.171875l1.71875 0.21875q-0.40625 1.5 -1.515625 2.34375q-1.09375 0.828125 -2.8125 0.828125q-2.15625 0 -3.421875 -1.328125q-1.265625 -1.328125 -1.265625 -3.734375q0 -2.484375 1.265625 -3.859375q1.28125 -1.375 3.328125 -1.375q1.984375 0 3.234375 1.34375q1.25 1.34375 1.25 3.796875q0 0.140625 -0.015625 0.4375l-7.34375 0q0.09375 1.625 0.921875 2.484375q0.828125 0.859375 2.0625 0.859375q0.90625 0 1.546875 -0.46875q0.65625 -0.484375 1.046875 -1.546875zm-5.484375 -2.703125l5.5 0q-0.109375 -1.234375 -0.625 -1.859375q-0.796875 -0.96875 -2.078125 -0.96875q-1.140625 0 -1.9375 0.78125q-0.78125 0.765625 -0.859375 2.046875zm8.438232 2.9375l1.65625 -0.265625q0.140625 1.0 0.765625 1.53125q0.640625 0.515625 1.78125 0.515625q1.15625 0 1.703125 -0.46875q0.5625 -0.46875 0.5625 -1.09375q0 -0.5625 -0.484375 -0.890625q-0.34375 -0.21875 -1.703125 -0.5625q-1.84375 -0.46875 -2.5625 -0.796875q-0.703125 -0.34375 -1.078125 -0.9375q-0.359375 -0.609375 -0.359375 -1.328125q0 -0.65625 0.296875 -1.21875q0.3125 -0.5625 0.828125 -0.9375q0.390625 -0.28125 1.0625 -0.484375q0.671875 -0.203125 1.4375 -0.203125q1.171875 0 2.046875 0.34375q0.875 0.328125 1.28125 0.90625q0.421875 0.5625 0.578125 1.515625l-1.625 0.21875q-0.109375 -0.75 -0.65625 -1.171875q-0.53125 -0.4375 -1.5 -0.4375q-1.15625 0 -1.640625 0.390625q-0.484375 0.375 -0.484375 0.875q0 0.328125 0.203125 0.59375q0.203125 0.265625 0.640625 0.4375q0.25 0.09375 1.46875 0.4375q1.765625 0.46875 2.46875 0.765625q0.703125 0.296875 1.09375 0.875q0.40625 0.578125 0.40625 1.4375q0 0.828125 -0.484375 1.578125q-0.484375 0.734375 -1.40625 1.140625q-0.921875 0.390625 -2.078125 0.390625q-1.921875 0 -2.9375 -0.796875q-1.0 -0.796875 -1.28125 -2.359375zm13.65625 1.4375l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125zm-0.0041503906 5.28125l0 -1.21875l11.0625 0l0 1.21875l-11.0625 0zm12.313232 -3.78125l0 -8.546875l-1.484375 0l0 -1.3125l1.484375 0l0 -1.046875q0 -0.984375 0.171875 -1.46875q0.234375 -0.65625 0.84375 -1.046875q0.609375 -0.40625 1.703125 -0.40625q0.703125 0 1.5625 0.15625l-0.25 1.46875q-0.515625 -0.09375 -0.984375 -0.09375q-0.765625 0 -1.078125 0.328125q-0.3125 0.3125 -0.3125 1.203125l0 0.90625l1.921875 0l0 1.3125l-1.921875 0l0 8.546875l-1.65625 0zm4.1519775 -4.921875q0 -2.734375 1.53125 -4.0625q1.265625 -1.09375 3.09375 -1.09375q2.03125 0 3.3125 1.34375q1.296875 1.328125 1.296875 3.671875q0 1.90625 -0.578125 3.0q-0.5625 1.078125 -1.65625 1.6875q-1.078125 0.59375 -2.375 0.59375q-2.0625 0 -3.34375 -1.328125q-1.28125 -1.328125 -1.28125 -3.8125zm1.71875 0q0 1.890625 0.828125 2.828125q0.828125 0.9375 2.078125 0.9375q1.25 0 2.0625 -0.9375q0.828125 -0.953125 0.828125 -2.890625q0 -1.828125 -0.828125 -2.765625q-0.828125 -0.9375 -2.0625 -0.9375q-1.25 0 -2.078125 0.9375q-0.828125 0.9375 -0.828125 2.828125zm9.266357 4.921875l0 -9.859375l1.5 0l0 1.5q0.578125 -1.046875 1.0625 -1.375q0.484375 -0.34375 1.078125 -0.34375q0.84375 0 1.71875 0.546875l-0.578125 1.546875q-0.609375 -0.359375 -1.234375 -0.359375q-0.546875 0 -0.984375 0.328125q-0.421875 0.328125 -0.609375 0.90625q-0.28125 0.890625 -0.28125 1.953125l0 5.15625l-1.671875 0zm6.2283936 0l0 -9.859375l1.5 0l0 1.390625q0.453125 -0.71875 1.21875 -1.15625q0.78125 -0.453125 1.765625 -0.453125q1.09375 0 1.796875 0.453125q0.703125 0.453125 0.984375 1.28125q1.171875 -1.734375 3.046875 -1.734375q1.46875 0 2.25 0.8125q0.796875 0.8125 0.796875 2.5l0 6.765625l-1.671875 0l0 -6.203125q0 -1.0 -0.15625 -1.4375q-0.15625 -0.453125 -0.59375 -0.71875q-0.421875 -0.265625 -1.0 -0.265625q-1.03125 0 -1.71875 0.6875q-0.6875 0.6875 -0.6875 2.21875l0 5.71875l-1.671875 0l0 -6.40625q0 -1.109375 -0.40625 -1.65625q-0.40625 -0.5625 -1.34375 -0.5625q-0.703125 0 -1.3125 0.375q-0.59375 0.359375 -0.859375 1.078125q-0.265625 0.71875 -0.265625 2.0625l0 5.109375l-1.671875 0zm21.978271 -1.21875q-0.9375 0.796875 -1.796875 1.125q-0.859375 0.3125 -1.84375 0.3125q-1.609375 0 -2.484375 -0.78125q-0.875 -0.796875 -0.875 -2.03125q0 -0.734375 0.328125 -1.328125q0.328125 -0.59375 0.859375 -0.953125q0.53125 -0.359375 1.203125 -0.546875q0.5 -0.140625 1.484375 -0.25q2.03125 -0.25 2.984375 -0.578125q0 -0.34375 0 -0.4375q0 -1.015625 -0.46875 -1.4375q-0.640625 -0.5625 -1.90625 -0.5625q-1.171875 0 -1.734375 0.40625q-0.5625 0.40625 -0.828125 1.46875l-1.640625 -0.234375q0.234375 -1.046875 0.734375 -1.6875q0.515625 -0.640625 1.46875 -0.984375q0.96875 -0.359375 2.25 -0.359375q1.265625 0 2.046875 0.296875q0.78125 0.296875 1.15625 0.75q0.375 0.453125 0.515625 1.140625q0.09375 0.421875 0.09375 1.53125l0 2.234375q0 2.328125 0.09375 2.953125q0.109375 0.609375 0.4375 1.171875l-1.75 0q-0.265625 -0.515625 -0.328125 -1.21875zm-0.140625 -3.71875q-0.90625 0.359375 -2.734375 0.625q-1.03125 0.140625 -1.453125 0.328125q-0.421875 0.1875 -0.65625 0.546875q-0.234375 0.359375 -0.234375 0.796875q0 0.671875 0.5 1.125q0.515625 0.4375 1.484375 0.4375q0.96875 0 1.71875 -0.421875q0.75 -0.4375 1.109375 -1.15625q0.265625 -0.578125 0.265625 -1.671875l0 -0.609375zm7.7351074 3.4375l0.234375 1.484375q-0.703125 0.140625 -1.265625 0.140625q-0.90625 0 -1.40625 -0.28125q-0.5 -0.296875 -0.703125 -0.75q-0.203125 -0.46875 -0.203125 -1.984375l0 -5.65625l-1.234375 0l0 -1.3125l1.234375 0l0 -2.4375l1.65625 -1.0l0 3.4375l1.6875 0l0 1.3125l-1.6875 0l0 5.75q0 0.71875 0.078125 0.921875q0.09375 0.203125 0.296875 0.328125q0.203125 0.125 0.578125 0.125q0.265625 0 0.734375 -0.078125z"
+       fill="#0097a7" />
+    <path
+       id="path17"
+       fill-rule="nonzero"
+       d="m185.21445 510.1761l560.99927 0"
+       stroke-linecap="butt"
+       stroke-width="1.3671875"
+       stroke="#0097a7" />
+    <a
+       id="a21"
+       rel="noreferrer"
+       target="_blank"
+       xlink:href="https://www.google.com/url?q=https://dev.arvados.org/projects/arvados/wiki/Keep_manifest_format&amp;sa=D&amp;ust=1478895969188000&amp;usg=AFQjCNHMNIzr5ezz4laFKPqTOrFHC9sgsA">
+      <path
+         id="path19"
+         fill-rule="evenodd"
+         d="m185.21445 512.15173l0 -20.84253l560.99927 0l0 20.84253z"
+         fill-opacity="0"
+         fill="transparent" />
+    </a>
+    <path
+       id="path23"
+       fill-rule="nonzero"
+       d="m178.12337 46.721786l600.2048 0l0 473.36218l-600.2048 0z"
+       fill-opacity="0.0"
+       fill="#000000" />
+    <g
+       id="g30"
+       transform="matrix(0.6538178477690288 0.0 0.0 0.6538152230971128 178.12336036745407 46.72178477690289)">
+      <clipPath
+         id="g1586814eb6_0_6.1">
+        <path
+           id="path25"
+           clip-rule="nonzero"
+           d="m0 1.4210855E-14l918.0 0l0 724.0l-918.0 0z" />
+      </clipPath>
+      <image
+         style="fill:#000000;fill-opacity:0"
+         id="image28"
+         xlink:href="data:image/png;base64,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"
+         preserveAspectRatio="none"
+         y="0.0"
+         x="0.0"
+         height="724.0"
+         width="918.0"
+         fill="#000"
+         clip-path="url(#g1586814eb6_0_6.1)" />
+    </g>
+  </g>
+  <rect
+     y="484.7074"
+     x="41.438316"
+     height="38.649345"
+     width="724.98273"
+     id="rect42"
+     style="fill:#ffffff;fill-rule:evenodd" />
+</svg>
index 3fbd33928a5ae16582c9e58b8b6562a3f8a12f9e..c01ec61fa02248e59baaf1fa25ce30bde977b815 100644 (file)
@@ -17,8 +17,11 @@ h2. Quick start
 $ git clone https://github.com/arvados/arvados.git
 $ cd arvados/tools/arvbox/bin
 $ ./arvbox start localdemo
+$ ./arvbox adduser demouser demo@example.com
 </pre>
 
+You can now log in as @demouser@ using the password you selected.
+
 h2. Requirements
 
 * Linux 3.x+ and Docker 1.9+
@@ -46,6 +49,9 @@ update  <config>   stop, pull latest image, run
 build   <config>   build arvbox Docker image
 reboot  <config>   stop, build arvbox Docker image, run
 rebuild <config>   build arvbox Docker image, no layer cache
+checkpoint         create database backup
+restore            restore checkpoint
+hotreset           reset database and restart API without restarting container
 reset              delete arvbox arvados data (be careful!)
 destroy            delete all arvbox code and data (be careful!)
 log <service>      tail log of specified service
@@ -55,6 +61,11 @@ pipe               run a bash script piped in from stdin
 sv <start|stop|restart> <service>
                    change state of service inside arvbox
 clone <from> <to>  clone dev arvbox
+adduser <username> <email>
+                   add a user login
+removeuser <username>
+                   remove user login
+listusers          list user logins
 </pre>
 
 h2. Install root certificate
@@ -78,9 +89,9 @@ h2. Configs
 
 h3. dev
 
-Development configuration.  Boots a complete Arvados environment inside the container.  The "arvados", "arvado-dev" and "sso-devise-omniauth-provider" code directories along data directories "postgres", "var", "passenger" and "gems" are bind mounted from the host file system for easy access and persistence across container rebuilds.  Services are bound to the Docker container's network IP address and can only be accessed on the local host.
+Development configuration.  Boots a complete Arvados environment inside the container.  The "arvados" and "arvados-dev" code directories along data directories "postgres", "var", "passenger" and "gems" are bind mounted from the host file system for easy access and persistence across container rebuilds.  Services are bound to the Docker container's network IP address and can only be accessed on the local host.
 
-In "dev" mode, you can override the default autogenerated settings of Rails projects by adding "application.yml.override" to any Rails project (sso, api, workbench).  This can be used to test out API server settings or point Workbench at an alternate API server.
+In "dev" mode, you can override the default autogenerated settings of Rails projects by adding "application.yml.override" to any Rails project (api, workbench).  This can be used to test out API server settings or point Workbench at an alternate API server.
 
 h3. localdemo
 
@@ -134,11 +145,6 @@ h3. ARVADOS_DEV_ROOT
 The root directory of the Arvados-dev source tree
 default: $ARVBOX_DATA/arvados-dev
 
-h3. SSO_ROOT
-
-The root directory of the SSO source tree
-default: $ARVBOX_DATA/sso-devise-omniauth-provider
-
 h3. ARVBOX_PUBLISH_IP
 
 The IP address on which to publish services when running in public configuration.  Overrides default detection of the host's IP address.
diff --git a/doc/install/copy_pipeline_from_curoverse.html.textile.liquid b/doc/install/copy_pipeline_from_curoverse.html.textile.liquid
deleted file mode 100644 (file)
index 2c2b3c4..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
----
-layout: default
-navsection: installguide
-title: Copy pipeline from the Arvados Playground
-...
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-This tutorial describes how to find and copy a publicly shared pipeline from the Arvados Playground. Please note that you can use similar steps to copy any template you can access from the Arvados Playground to your cluster.
-
-h3. Access a public pipeline in the Arvados Playground using Workbench
-
-the Arvados Playground provides access to some public data, which can be used to experience Arvados in action. Let's access a public pipeline and copy it to your cluster, so that you can run it in your environment.
-
-Start by visiting the "*Arvados Playground public projects page*":https://playground.arvados.org/projects/public. This page lists all the publicly accessible projects in this arvados installation. Click on one of these projects to open it. We will use "*lobSTR v.3 (Public)*":https://playground.arvados.org/projects/qr1hi-j7d0g-up6qgpqz5ie2vfq as the example in this tutorial.
-
-Once in the "*lobSTR v.3 (Public)*":https://playground.arvados.org/projects/qr1hi-j7d0g-up6qgpqz5ie2vfq project, click on the *Pipeline templates* tab. In the pipeline templates tab, you will see a template named *lobSTR v.3*. Click on the <span class="fa fa-lg fa-gears"></span> *Show* button to the left of this name. This will take to you to the "*lobSTR v.3*":https://playground.arvados.org/pipeline_templates/qr1hi-p5p6p-9pkaxt6qjnkxhhu template page.
-
-Once in this page, you can take the *uuid* of this template from the address bar, which is *qr1hi-p5p6p-9pkaxt6qjnkxhhu*. Next, we will copy this template to your Arvados instance.
-
-h3. Copying a pipeline template from the Arvados Playground to your cluster
-
-As described above, navigate to the publicly shared pipeline template "*lobSTR v.3*":https://playground.arvados.org/pipeline_templates/qr1hi-p5p6p-9pkaxt6qjnkxhhu on the Arvados Playground.  We will now copy this template with uuid *qr1hi-p5p6p-9pkaxt6qjnkxhhu* to your cluster.
-
-{% include 'tutorial_expectations' %}
-
-We will use the Arvados *arv-copy* command to copy this template to your cluster. In order to use arv-copy, first you need to setup the source and destination cluster configuration files. Here, *qr1hi* would be the source cluster and your Arvados instance would be the *dst_cluster*.
-
-During this setup, if you have an account in the Arvados Playground, you can use "your access token":#using-your-token to create the source configuration file. If you do not have an account in the Arvados Playground, you can use the "anonymous access token":#using-anonymous-token for the source cluster configuration.
-
-h4(#using-anonymous-token). *Configuring source and destination setup files using anonymous access token*
-
-Configure the source and destination clusters as described in the "*Using arv-copy*":http://doc.arvados.org/user/topics/arv-copy.html tutorial in user guide, while using *5vqmz9mik2ou2k9objb8pnyce8t97p6vocyaouqo3qalvpmjs5* as the API token for source configuration.
-
-<notextile>
-<pre><code>~$ <span class="userinput">cd ~/.config/arvados</span>
-~$ <span class="userinput">echo "ARVADOS_API_HOST=qr1hi.arvadosapi.com" >> qr1hi.conf</span>
-~$ <span class="userinput">echo "ARVADOS_API_TOKEN=5vqmz9mik2ou2k9objb8pnyce8t97p6vocyaouqo3qalvpmjs5" >> qr1hi.conf</span>
-</code></pre>
-</notextile>
-
-You can now copy the pipeline template from *qr1hi* to *your cluster*. Replace *dst_cluster* with the *ClusterID* of your cluster.
-
-<notextile>
-<pre><code>~$ <span class="userinput"> arv-copy --no-recursive --src qr1hi --dst dst_cluster qr1hi-p5p6p-9pkaxt6qjnkxhhu</span>
-</code></pre>
-</notextile>
-
-*Note:* When you are using anonymous access token to copy the template, you will not be able to do a recursive copy since you will not be able to provide the dst-git-repo parameter. In order to perform a recursive copy of the template, you would need to use the Arvados API token from your account as explained in the "using your token":#using-your-token section below.
-
-h4(#using-your-token). *Configuring source and destination setup files using personal access token*
-
-If you already have an account in the Arvados Playground, you can follow the instructions in the "*Using arv-copy*":http://doc.arvados.org/user/topics/arv-copy.html user guide to get your *Current token* for source and destination clusters, and use them to create the source *qr1hi.conf* and dst_cluster.conf configuration files.
-
-You can now copy the pipeline template from *qr1hi* to *your cluster* with or without recursion. Replace *dst_cluster* with the *ClusterID* of your cluster.
-
-*Non-recursive copy:*
-<notextile>
-<pre><code>~$ <span class="userinput"> arv-copy --no-recursive --src qr1hi --dst dst_cluster qr1hi-p5p6p-9pkaxt6qjnkxhhu</span></code></pre>
-</notextile>
-
-*Recursive copy:*
-<notextile>
-<pre><code>~$ <span class="userinput">arv-copy --src qr1hi --dst dst_cluster --dst-git-repo $USER/tutorial qr1hi-p5p6p-9pkaxt6qjnkxhhu</span></code></pre>
-</notextile>
index 68417784701ce387e7437bb0f0b8e62a2335e5ff..151e211653c0d77d73af31749bf71836124998e1 100644 (file)
@@ -100,6 +100,9 @@ Using managed disks:
       CloudVMs:
         ImageID: "zzzzz-compute-v1597349873"
         Driver: azure
+        # (azure) managed disks: set MaxConcurrentInstanceCreateOps to 20 to avoid timeouts, cf
+        # https://docs.microsoft.com/en-us/azure/virtual-machines/linux/capture-image
+        MaxConcurrentInstanceCreateOps: 20
         DriverParameters:
           # Credentials.
           SubscriptionID: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
index 5ac5e9e6b870a2753287b2b8a59e50c6686d80df..97854e524000894c021e80754a4d871fc1637da6 100644 (file)
@@ -22,9 +22,15 @@ h2(#introduction). Introduction
 
 Arvados support for shell nodes allows you to use Arvados permissions to grant Linux shell accounts to users.
 
-A shell node runs the @arvados-login-sync@ service, and has some additional configuration to make it convenient for users to use Arvados utilites and SDKs.  Users are allowed to log in and run arbitrary programs.  For optimal performance, the Arvados shell server should be on the same LAN as the Arvados cluster.
+A shell node runs the @arvados-login-sync@ service to manage user accounts, and typically has Arvados utilities and SDKs pre-installed.  Users are allowed to log in and run arbitrary programs.  For optimal performance, the Arvados shell server should be on the same LAN as the Arvados cluster.
 
-Because it _contains secrets_ shell nodes should *not* have a copy of the complete @config.yml@.  For example, if users have access to the @docker@ daemon, it is trival to gain *root* access to any file on the system.  Users sharing a shell node should be implicitly trusted, or not given access to Docker.  In more secure environments, the admin should allocate a separate VM for each user.
+Because it _contains secrets_ shell nodes should *not* have a copy of the Arvados @config.yml@.
+
+Shell nodes should be separate virtual machines from the VMs running other Arvados services.  You may choose to grant root access to users so that they can customize the node, for example, installing new programs.  This has security considerations depending on whether a shell node is single-user or multi-user.
+
+A single-user shell node should be set up so that it only stores Arvados access tokens that belong to that user.  In that case, that user can be safely granted root access without compromising other Arvados users.
+
+In the multi-user shell node case, a malicious user with @root@ access could access other user's Arvados tokens.  Users should only be given @root@ access on a multi-user shell node if you would trust them them to be Arvados administrators.  Be aware that with access to the @docker@ daemon, it is trival to gain *root* access to any file on the system, so giving users @docker@ access should be considered equivalent to @root@ access.
 
 h2(#dependencies). Install Dependecies and SDKs
 
@@ -52,51 +58,42 @@ Configure git to use the ARVADOS_API_TOKEN environment variable to authenticate
 
 h2(#vm-record). Create record for VM
 
-This program makes it possible for Arvados users to log in to the shell server -- subject to permissions assigned by the Arvados administrator -- using the SSH keys they upload to Workbench. It sets up login accounts, updates group membership, and adds users' public keys to the appropriate @authorized_keys@ files.
-
-Create an Arvados virtual_machine object representing this shell server. This will assign a UUID.
+As an admin, create an Arvados virtual_machine object representing this shell server. This will return a uuid.
 
 <notextile>
 <pre>
-<code>apiserver:~$ <span class="userinput">arv --format=uuid virtual_machine create --virtual-machine '{"hostname":"<b>your.shell.server.hostname.without.domain</b>"}'</span>
+<code>apiserver:~$ <span class="userinput">arv --format=uuid virtual_machine create --virtual-machine '{"hostname":"<b>shell.ClusterID.example.com</b>"}'</span>
 zzzzz-2x53u-zzzzzzzzzzzzzzz</code>
 </pre>
 </notextile>
 
-h2(#scoped-token). Create scoped token
+h2(#arvados-login-sync). Install arvados-login-sync
+
+The @arvados-login-sync@ service makes it possible for Arvados users to log in to the shell server.  It sets up login accounts, updates group membership, adds each user's SSH public keys to the @~/.ssh/authorized_keys@ file, and adds an Arvados token to @~/.config/arvados/settings.conf@ .
 
-As an Arvados admin user (such as the system root user), create a "scoped token":{{site.baseurl}}/admin/scoped-tokens.html that is permits only reading login information for this VM.  Setting a scope on the token means that even though a user with root access on the shell node can access the token, the token is not usable for admin actions on Arvados.
+Install the @arvados-login-sync@ program from RubyGems.
 
 <notextile>
 <pre>
-<code>apiserver:~$ <span class="userinput">arv api_client_authorization create --api-client-authorization '{"scopes":["GET /arvados/v1/virtual_machines/<b>zzzzz-2x53u-zzzzzzzzzzzzzzz</b>/logins"]}'</span>
-{
- ...
- "api_token":"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz",
- ...
-}</code>
+<code>shellserver:# <span class="userinput">gem install arvados-login-sync</span></code>
 </pre>
 </notextile>
 
-Note the UUID and the API token output by the above commands: you will need them in a minute.
+h2(#arvados-login-sync). Run arvados-login-sync periodically
 
-h2(#arvados-login-sync). Install arvados-login-sync
+Create a cron job to run the @arvados-login-sync@ program every 2 minutes.  This will synchronize user accounts.
 
-Install the arvados-login-sync program from RubyGems.
+If this is a single-user shell node, then @ARVADOS_API_TOKEN@ should be a token for that user.  See "Create a token for a user":{{site.baseurl}}/admin/user-management-cli.html#create-token .
 
-<notextile>
-<pre>
-<code>shellserver:# <span class="userinput">gem install arvados-login-sync</span></code>
-</pre>
-</notextile>
+If this is a multi-user shell node, then @ARVADOS_API_TOKEN@ should be an administrator token such as the @SystemRootToken@.  See discussion in the "introduction":#introduction about security on multi-user shell nodes.
 
-Configure cron to run the @arvados-login-sync@ program every 2 minutes.
+Set @ARVADOS_VIRTUAL_MACHINE_UUID@ to the UUID from "Create record for VM":#vm-record
 
 <notextile>
 <pre>
-<code>shellserver:# <span class="userinput">umask 077; tee /etc/cron.d/arvados-login-sync &lt;&lt;EOF
+<code>shellserver:# <span class="userinput">umask 0700; tee /etc/cron.d/arvados-login-sync &lt;&lt;EOF
 ARVADOS_API_HOST="<strong>ClusterID.example.com</strong>"
-ARVADOS_API_TOKEN="<strong>the_token_you_created_above</strong>"
+ARVADOS_API_TOKEN="<strong>xxxxxxxxxxxxxxxxx</strong>"
 ARVADOS_VIRTUAL_MACHINE_UUID="<strong>zzzzz-2x53u-zzzzzzzzzzzzzzz</strong>"
 */2 * * * * root arvados-login-sync
 EOF</span></code>
@@ -107,8 +104,9 @@ h2(#confirm-working). Confirm working installation
 
 A user should be able to log in to the shell server when the following conditions are satisfied:
 
-# The user has uploaded an SSH public key: Workbench &rarr; Account menu &rarr; "SSH keys" item &rarr; "Add new SSH key" button.
 # As an admin user, you have given the user permission to log in using the Workbench &rarr; Admin menu &rarr; "Users" item &rarr; "Show" button &rarr; "Admin" tab &rarr; "Setup account" button.
 # The cron job has run.
 
+In order to log in via SSH, the user must also upload an SSH public key.  Alternately, if configured, users can log in using "Webshell":install-webshell.html .
+
 See also "how to add a VM login permission link at the command line":../admin/user-management-cli.html
index ae6a8d2109c686a3d6769e515e75d4376c1e8bee..8275a2a831e1fecb2a6f629d061f3da816d57105 100644 (file)
@@ -65,7 +65,7 @@ server {
 
   location /<span class="userinput">shell.ClusterID</span> {
     if ($request_method = 'OPTIONS') {
-       add_header 'Access-Control-Allow-Origin' '*'; 
+       add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
        add_header 'Access-Control-Max-Age' 1728000;
@@ -146,7 +146,7 @@ SHELLINABOX_ARGS="--disable-ssl --no-beep --service=/<span class="userinput">she
 
 h2(#config-pam). Configure pam
 
-Use a text editor to create a new file @/etc/pam.d/shellinabox@ with the following configuration. Options that need attention are marked in <span class="userinput">red</span>.
+Use a text editor to create a new file @/etc/pam.d/shellinabox@ with the following configuration.  Options that need attention are marked in <span class="userinput">red</span>.
 
 <notextile><pre>
 # This example is a stock debian "login" file with pam_arvados
@@ -159,7 +159,11 @@ session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux
 session       required   pam_env.so readenv=1
 session       required   pam_env.so readenv=1 envfile=/etc/default/locale
 
+# The first argument is the address of the API server.  The second
+# argument is this shell node's hostname.  The hostname must match the
+# "hostname" field of the virtual_machine record.
 auth [success=1 default=ignore] /usr/lib/pam_arvados.so <span class="userinput">ClusterID.example.com</span> <span class="userinput">shell.ClusterID.example.com</span>
+
 auth    requisite            pam_deny.so
 auth    required            pam_permit.so
 
@@ -179,5 +183,4 @@ session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux
 
 h2(#confirm-working). Confirm working installation
 
-A user should be able to log in to the shell server, using webshell via workbench. Please refer to "Accessing an Arvados VM with Webshell":{{site.baseurl}}/user/getting_started/vm-login-with-webshell.html
-
+A user should now be able to log in to the shell server, using webshell via workbench. Please refer to "Accessing an Arvados VM with Webshell":{{site.baseurl}}/user/getting_started/vm-login-with-webshell.html
index e73f968c8dd0bbb3208de3f3ed85b0ff5cd8a1e0..8d2fc2f4af086db6072c282ee59fc027fb11e9b3 100644 (file)
@@ -28,7 +28,7 @@ public class CollectionExample {
     public static void main(String[] argv) {
        ConfigProvider conf = ExternalConfigProvider.builder().
            apiProtocol("https").
-           apiHost("qr1hi.arvadosapi.com").
+           apiHost("zzzzz.arvadosapi.com").
            apiPort(443).
            apiToken("...").
            build();
diff --git a/doc/sdk/python/arvados-cwl-runner.html.textile.liquid b/doc/sdk/python/arvados-cwl-runner.html.textile.liquid
new file mode 100644 (file)
index 0000000..9faedb8
--- /dev/null
@@ -0,0 +1,77 @@
+---
+layout: default
+navsection: sdk
+navmenu: Python
+title: Arvados CWL Runner
+...
+{% comment %}
+Copyright (C) The Arvados Authors. All rights reserved.
+
+SPDX-License-Identifier: CC-BY-SA-3.0
+{% endcomment %}
+
+The Arvados FUSE driver is a Python utility that allows you to see the Keep service as a normal filesystem, so that data can be accessed using standard tools. This driver requires the Python SDK installed in order to access Arvados services.
+
+h2. Installation
+
+If you are logged in to a managed Arvados VM, the @arv-mount@ utility should already be installed.
+
+To use the FUSE driver elsewhere, you can install from a distribution package, or PyPI.
+
+h2. Option 1: Install from distribution packages
+
+First, "add the appropriate package repository for your distribution":{{ site.baseurl }}/install/packages.html
+
+{% assign arvados_component = 'python3-arvados-cwl-runner' %}
+
+{% include 'install_packages' %}
+
+h2. Option 2: Install with pip
+
+Run @pip install arvados-cwl-runner@ in an appropriate installation environment, such as a virtualenv.
+
+Note:
+
+The SDK uses @pycurl@ which depends on the @libcurl@ C library.  To build the module you may have to first install additional packages.  On Debian 9 this is:
+
+<pre>
+$ apt-get install git build-essential python-dev libcurl4-openssl-dev libssl1.0-dev python-llfuse
+</pre>
+
+For Python 3 this is:
+
+<pre>
+$ apt-get install git build-essential python3-dev libcurl4-openssl-dev libssl1.0-dev python3-llfuse
+</pre>
+
+h3. Check Docker access
+
+In order to pull and upload Docker images, @arvados-cwl-runner@ requires access to Docker.  You do not need Docker if the Docker images you intend to use are already available in Arvados.
+
+You can determine if you have access to Docker by running @docker version@:
+
+<notextile>
+<pre><code>~$ <span class="userinput">docker version</span>
+Client:
+ Version:      1.9.1
+ API version:  1.21
+ Go version:   go1.4.2
+ Git commit:   a34a1d5
+ Built:        Fri Nov 20 12:59:02 UTC 2015
+ OS/Arch:      linux/amd64
+
+Server:
+ Version:      1.9.1
+ API version:  1.21
+ Go version:   go1.4.2
+ Git commit:   a34a1d5
+ Built:        Fri Nov 20 12:59:02 UTC 2015
+ OS/Arch:      linux/amd64
+</code></pre>
+</notextile>
+
+If this returns an error, contact the sysadmin of your cluster for assistance.
+
+h3. Usage
+
+Please refer to the "Accessing Keep from GNU/Linux":{{site.baseurl}}/user/tutorials/tutorial-keep-mount-gnu-linux.html tutorial for more information.
index 75c51ee5a8126c57b9b23bc95d9cffdcf7fc027c..82741c3ea64f8548a9a5aaa3ce12ca3828ac44f7 100644 (file)
@@ -47,7 +47,7 @@ h2. Get input of a CWL workflow
 {% codeblock as python %}
 import arvados
 api = arvados.api()
-container_request_uuid="qr1hi-xvhdp-zzzzzzzzzzzzzzz"
+container_request_uuid="zzzzz-xvhdp-zzzzzzzzzzzzzzz"
 container_request = api.container_requests().get(uuid=container_request_uuid).execute()
 print(container_request["mounts"]["/var/lib/cwl/cwl.input.json"])
 {% endcodeblock %}
@@ -58,7 +58,7 @@ h2. Get output of a CWL workflow
 import arvados
 import arvados.collection
 api = arvados.api()
-container_request_uuid="qr1hi-xvhdp-zzzzzzzzzzzzzzz"
+container_request_uuid="zzzzz-xvhdp-zzzzzzzzzzzzzzz"
 container_request = api.container_requests().get(uuid=container_request_uuid).execute()
 collection = arvados.collection.CollectionReader(container_request["output_uuid"])
 print(collection.open("cwl.output.json").read())
@@ -89,7 +89,7 @@ def get_cr_state(cr_uuid):
         elif c['runtime_status'].get('warning', None):
             return 'Warning'
     return c['state']
-container_request_uuid = 'qr1hi-xvhdp-zzzzzzzzzzzzzzz'
+container_request_uuid = 'zzzzz-xvhdp-zzzzzzzzzzzzzzz'
 print(get_cr_state(container_request_uuid))
 {% endcodeblock %}
 
@@ -98,7 +98,7 @@ h2. List input of child requests
 {% codeblock as python %}
 import arvados
 api = arvados.api()
-parent_request_uuid = "qr1hi-xvhdp-zzzzzzzzzzzzzzz"
+parent_request_uuid = "zzzzz-xvhdp-zzzzzzzzzzzzzzz"
 namefilter = "bwa%"  # the "like" filter uses SQL pattern match syntax
 container_request = api.container_requests().get(uuid=parent_request_uuid).execute()
 parent_container_uuid = container_request["container_uuid"]
@@ -117,7 +117,7 @@ h2. List output of child requests
 {% codeblock as python %}
 import arvados
 api = arvados.api()
-parent_request_uuid = "qr1hi-xvhdp-zzzzzzzzzzzzzzz"
+parent_request_uuid = "zzzzz-xvhdp-zzzzzzzzzzzzzzz"
 namefilter = "bwa%"  # the "like" filter uses SQL pattern match syntax
 container_request = api.container_requests().get(uuid=parent_request_uuid).execute()
 parent_container_uuid = container_request["container_uuid"]
@@ -136,7 +136,7 @@ h2. List failed child requests
 {% codeblock as python %}
 import arvados
 api = arvados.api()
-parent_request_uuid = "qr1hi-xvhdp-zzzzzzzzzzzzzzz"
+parent_request_uuid = "zzzzz-xvhdp-zzzzzzzzzzzzzzz"
 container_request = api.container_requests().get(uuid=parent_request_uuid).execute()
 parent_container_uuid = container_request["container_uuid"]
 child_requests = api.container_requests().list(filters=[
@@ -155,7 +155,7 @@ h2. Get log of a child request
 import arvados
 import arvados.collection
 api = arvados.api()
-container_request_uuid = "qr1hi-xvhdp-zzzzzzzzzzzzzzz"
+container_request_uuid = "zzzzz-xvhdp-zzzzzzzzzzzzzzz"
 container_request = api.container_requests().get(uuid=container_request_uuid).execute()
 collection = arvados.collection.CollectionReader(container_request["log_uuid"])
 for c in collection:
@@ -169,7 +169,7 @@ h2(#sharing_link). Create a collection sharing link
 import arvados
 api = arvados.api()
 download="https://your.download.server"
-collection_uuid="qr1hi-4zz18-zzzzzzzzzzzzzzz"
+collection_uuid="zzzzz-4zz18-zzzzzzzzzzzzzzz"
 token = api.api_client_authorizations().create(body={"api_client_authorization":{"scopes": [
     "GET /arvados/v1/collections/%s" % collection_uuid,
     "GET /arvados/v1/collections/%s/" % collection_uuid,
@@ -185,8 +185,8 @@ Note, if two collections have files of the same name, the contents will be conca
 import arvados
 import arvados.collection
 api = arvados.api()
-project_uuid = "qr1hi-tpzed-zzzzzzzzzzzzzzz"
-collection_uuids = ["qr1hi-4zz18-aaaaaaaaaaaaaaa", "qr1hi-4zz18-bbbbbbbbbbbbbbb"]
+project_uuid = "zzzzz-tpzed-zzzzzzzzzzzzzzz"
+collection_uuids = ["zzzzz-4zz18-aaaaaaaaaaaaaaa", "zzzzz-4zz18-bbbbbbbbbbbbbbb"]
 combined_manifest = ""
 for u in collection_uuids:
     c = api.collections().get(uuid=u).execute()
@@ -201,7 +201,7 @@ h2. Upload a file into a new collection
 import arvados
 import arvados.collection
 
-project_uuid = "qr1hi-j7d0g-zzzzzzzzzzzzzzz"
+project_uuid = "zzzzz-j7d0g-zzzzzzzzzzzzzzz"
 collection_name = "My collection"
 filename = "file1.txt"
 
@@ -223,7 +223,7 @@ h2. Download a file from a collection
 import arvados
 import arvados.collection
 
-collection_uuid = "qr1hi-4zz18-zzzzzzzzzzzzzzz"
+collection_uuid = "zzzzz-4zz18-zzzzzzzzzzzzzzz"
 filename = "file1.txt"
 
 api = arvados.api()
index e7ac4775037f3fc3ab471501f6f7b3b5a417109a..78fe9272bf8c9816b1e035290ca45486bed2547c 100644 (file)
@@ -2,7 +2,7 @@
 layout: default
 navsection: sdk
 navmenu: Python
-title: Subscribing to events
+title: Subscribing to database events
 ...
 {% comment %}
 Copyright (C) The Arvados Authors. All rights reserved.
index b8c0dcbb8036ac3be675e591ed1373d94575f491..f2ea1c09dfd6b9f8f0c4433e1b1e649cac66631f 100644 (file)
@@ -55,7 +55,7 @@ first_repo = repos[:items][0]
 puts "UUID of first repo returned is #{first_repo[:uuid]}"</code>
 {% endcodeblock %}
 
-UUID of first repo returned is qr1hi-s0uqq-b1bnybpx3u5temz
+UUID of first repo returned is zzzzz-s0uqq-b1bnybpx3u5temz
 
 h2. update
 
diff --git a/doc/start/getting_started/firstpipeline.html.textile.liquid b/doc/start/getting_started/firstpipeline.html.textile.liquid
deleted file mode 100644 (file)
index 43369a3..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
----
-layout: default
-navsection: start 
-title: Run your first pipeline in minutes
-...
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-h2. LobSTR v3 
-
-In this quickstart guide, we'll run an existing pipeline with pre-existing data. Step-by-step instructions are shown below. You can follow along using your own local install or by using the <a href="https://playground.arvados.org/">Arvados Playground</a> (any Google account can be used to log in).
-
-(For more information about this pipeline, see our <a href="https://dev.arvados.org/projects/arvados/wiki/LobSTR_tutorial">detailed lobSTR guide</a>).
-
-<div id="carousel-firstpipe" class="carousel slide" data-interval="false">
-  <!-- Indicators -->
-  <ol class="carousel-indicators">
-    <li data-target="#carousel-firstpipe" data-slide-to="0" class="active"></li>
-    <li data-target="#carousel-firstpipe" data-slide-to="1"></li>
-    <li data-target="#carousel-firstpipe" data-slide-to="2"></li>
-    <li data-target="#carousel-firstpipe" data-slide-to="3"></li>
-    <li data-target="#carousel-firstpipe" data-slide-to="4"></li>
-    <li data-target="#carousel-firstpipe" data-slide-to="5"></li>
-    <li data-target="#carousel-firstpipe" data-slide-to="6"></li>
-  </ol>
-
-  <!-- Wrapper for slides -->
-  <div class="carousel-inner" role="listbox">
-    <div class="item active">
-      <img src="{{ site.baseurl }}/images/quickstart/1.png" alt="Step 1. At the dashboard, click 'Run a pipeline...'.">
-      <div class="carousel-caption">
-        Step 1. At the dashboard, click 'Run a pipeline...'.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/quickstart/2.png" alt="Choose 'lobstr v.3' and hit 'Next'.">
-      <div class="carousel-caption">
-        Choose 'lobstr v.3' and hit 'Next'.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/quickstart/3.png" alt="Rename the pipeline instance, then click 'Run'. Click 'Choose' to change the default inputs.">
-      <div class="carousel-caption">
-        Rename the pipeline instance, then click 'Run'. Click 'Choose' to change the default inputs.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/quickstart/4.png" alt="Here we search for and choose new inputs.">
-      <div class="carousel-caption">
-        Here we search for and choose new inputs.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/quickstart/5.png" alt="After the job completes, you can re-run it with one click.">
-      <div class="carousel-caption">
-        After the job completes, you can re-run it with one click.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/quickstart/6.png" alt="You can inspect details about the pipeline which are automatically logged.">
-      <div class="carousel-caption">
-        You can inspect automatically-logged details about the pipeline.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/quickstart/7.png" alt="Click 'Create sharing link' to share the output files with people outside Arvados. [END]">
-      <div class="carousel-caption">
-        Click 'Create sharing link' to share the output files with people outside Arvados. [END]
-      </div>
-    </div>
-
-  </div>
-
-  <!-- Controls -->
-  <a class="left carousel-control" href="#carousel-firstpipe" role="button" data-slide="prev">
-    <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
-    <span class="sr-only">Previous</span>
-  </a>
-  <a class="right carousel-control" href="#carousel-firstpipe" role="button" data-slide="next">
-    <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
-    <span class="sr-only">Next</span>
-  </a>
-</div>
-
-Tip: You may need to make your browser window bigger to see full-size images in the gallery above.
diff --git a/doc/start/getting_started/nextsteps.html.textile.liquid b/doc/start/getting_started/nextsteps.html.textile.liquid
deleted file mode 100644 (file)
index dd059ea..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
----
-layout: default
-navsection: start 
-title: Check out the User Guide 
-...
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-Now that you've finished the Getting Started guide, check out the "User Guide":{{site.baseurl}}/user/index.html. The User Guide goes into more depth than the Getting Started guide, covers how to develop your own pipelines in addition to using pre-existing pipelines, covers the Arvados command line tools in addition to the Workbench graphical interface to Arvados, and can be referenced in any order.
diff --git a/doc/start/getting_started/publicproject.html.textile.liquid b/doc/start/getting_started/publicproject.html.textile.liquid
deleted file mode 100644 (file)
index 0fabad7..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
----
-layout: default
-navsection: start
-title: Visit an Arvados Public Project
-...
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-h2. <a href="https://workbench.qr1hi.arvadosapi.com/projects/qr1hi-j7d0g-662ij1pcw6bj8uj">Mason Lab - Pathomap / Ancestry Mapper (Public)</a>
-
-You can see Arvados in action by accessing the <a href="https://workbench.qr1hi.arvadosapi.com/projects/qr1hi-j7d0g-662ij1pcw6bj8uj">Mason Lab - Pathomap / Ancestry Mapper (Public) project</a>. By visiting this project, you can see what an Arvados project is, access data collections in this project, and click through a pipeline instance's contents.
-
-You will be accessing this project in read-only mode and will not be able to make any modifications such as running a new pipeline instance.
-
-<div id="carousel-publicproject" class="carousel slide" data-interval="false">
-  <!-- Indicators -->
-  <ol class="carousel-indicators">
-    <li data-target="#carousel-publicproject" data-slide-to="0" class="active"></li>
-    <li data-target="#carousel-publicproject" data-slide-to="1"></li>
-    <li data-target="#carousel-publicproject" data-slide-to="2"></li>
-    <li data-target="#carousel-publicproject" data-slide-to="3"></li>
-    <li data-target="#carousel-publicproject" data-slide-to="4"></li>
-    <li data-target="#carousel-publicproject" data-slide-to="5"></li>
-    <li data-target="#carousel-publicproject" data-slide-to="6"></li>
-    <li data-target="#carousel-publicproject" data-slide-to="7"></li>
-    <li data-target="#carousel-publicproject" data-slide-to="8"></li>
-    <li data-target="#carousel-publicproject" data-slide-to="9"></li>
-    <li data-target="#carousel-publicproject" data-slide-to="10"></li>
-    <li data-target="#carousel-publicproject" data-slide-to="11"></li>
-  </ol>
-
-  <!-- Wrapper for slides -->
-  <div class="carousel-inner" role="listbox">
-    <div class="item active">
-      <img src="{{ site.baseurl }}/images/publicproject/description.png" alt="Step 1. The project's first tab, *Description*, describes what this project is all about.">
-      <div class="carousel-caption">
-        Step 1. The project's first tab, *Description*, describes what this project is all about.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/publicproject/collections.png" alt="The *Data collections* tab contains the various pipeline inputs, logs, and outputs.">
-      <div class="carousel-caption">
-        The *Data collections* tab contains the various pipeline inputs, logs, and outputs.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/publicproject/instances.png" alt="You can see the jobs and pipelines in this project by accessing the *Jobs and pipelines* tab.">
-      <div class="carousel-caption">
-        You can see the jobs and pipelines in this project by accessing the *Jobs and pipelines* tab.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/publicproject/collection-show.png" alt="In the *Data collections* tab, click on the *Show* icon to the left of a collection to see the collection contents.">
-      <div class="carousel-caption">
-        In the *Data collections* tab, click on the *Show* icon to the left of a collection to see the collection contents.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/publicproject/collection-files.png" alt="The collection page lists the details about it. The *Files* tab can be used to view and download individual files in it.">
-      <div class="carousel-caption">
-        The collection page lists the details about it. The *Files* tab can be used to view and download individual files in it.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/publicproject/collection-graph.png" alt="The collection *Provenance graph* tab gives a visual representation of this collection's provenance.">
-      <div class="carousel-caption">
-        The collection *Provenance graph* tab gives a visual representation of this collection's provenance.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/publicproject/instance-show.png" alt="In the project *Jobs and pipelines* tab, click on the *Show* icon to the left of a pipeline to access the pipeline contents.">
-      <div class="carousel-caption">
-        In the project *Jobs and pipelines* tab, click on the *Show* icon to the left of a pipeline to access the pipeline contents.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/publicproject/instance-components.png" alt="The pipeline *Components* tab details the various jobs in it and how long it took to run it.">
-      <div class="carousel-caption">
-        The pipeline *Components* tab details the various jobs in it and how long it took to run it.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/publicproject/instance-job.png" alt="Click on the down arrow in one of the job rows to see the job details. You can also click on the job's output.">
-      <div class="carousel-caption">
-        Click on the down arrow <i class="fa fa-lg fa-fw fa-caret-down"></i> in one of the job rows to see the job details. You can also click on the job's output.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/publicproject/instance-log.png" alt="The *Log* tab can be used to see the log for the pipeline instance.">
-      <div class="carousel-caption">
-        The *Log* tab can be used to see the log for the pipeline instance.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/publicproject/instance-graph.png" alt="The *Graph* tab provides a visual representation of the pipeline run.">
-      <div class="carousel-caption">
-        The *Graph* tab provides a visual representation of the pipeline run.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/publicproject/instance-advanced.png" alt="The *Advanced* tab can be used to access metadata about the pipeline. [END]">
-      <div class="carousel-caption">
-        The *Advanced* tab can be used to access metadata about the pipeline. [END]
-      </div>
-    </div>
-  </div>
-
-  <!-- Controls -->
-  <a class="left carousel-control" href="#carousel-publicproject" role="button" data-slide="prev">
-    <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
-    <span class="sr-only">Previous</span>
-  </a>
-  <a class="right carousel-control" href="#carousel-publicproject" role="button" data-slide="next">
-    <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
-    <span class="sr-only">Next</span>
-  </a>
-</div>
-
-Tip: You may need to make your browser window bigger to see full-size images in the gallery above.
diff --git a/doc/start/getting_started/sharedata.html.textile.liquid b/doc/start/getting_started/sharedata.html.textile.liquid
deleted file mode 100644 (file)
index 02e0b70..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
----
-layout: default
-navsection: start 
-title: Sharing Data 
-...
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-You can easily share data entirely through Workbench, the web interface to Arvados.
-
-h2. Upload and share your existing data
-
-Step-by-step instructions are shown below.
-
-<div id="carousel-sharedata" class="carousel slide" data-interval="false">
-  <!-- Indicators -->
-  <ol class="carousel-indicators">
-    <li data-target="#carousel-sharedata" data-slide-to="0" class="active"></li>
-    <li data-target="#carousel-sharedata" data-slide-to="1"></li>
-    <li data-target="#carousel-sharedata" data-slide-to="2"></li>
-    <li data-target="#carousel-sharedata" data-slide-to="3"></li>
-    <li data-target="#carousel-sharedata" data-slide-to="4"></li>
-    <li data-target="#carousel-sharedata" data-slide-to="5"></li>
-    <li data-target="#carousel-sharedata" data-slide-to="6"></li>
-    <li data-target="#carousel-sharedata" data-slide-to="7"></li>
-  </ol>
-
-  <!-- Wrapper for slides -->
-  <div class="carousel-inner" role="listbox">
-    <div class="item active">
-      <img src="{{ site.baseurl }}/images/uses/gotohome.png" alt="Step 1. From the dashboard, go to your Home project.">
-      <div class="carousel-caption">
-        Step 1. From the dashboard, go to your Home project.
-      </div>
-    </div>
-
-    <div class="item">
-    <img src="{{ site.baseurl }}/images/uses/uploaddata.png" alt="Click 'Add data' &rarr; 'Upload files'.">
-      <div class="carousel-caption">
-        Click 'Add data' &rarr; 'Upload files'.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/uses/choosefiles.png" alt="A new collection is created automatically. Choose files to upload and hit Start.">
-      <div class="carousel-caption">
-        A new collection is created automatically. Choose files to upload and hit Start.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/uses/uploading.png" alt="Files will upload and stay uploaded even if the browser is closed.">
-      <div class="carousel-caption">
-        Files will upload and stay uploaded even if the browser is closed.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/uses/rename.png" alt="Rename the collection appropriately.">
-      <div class="carousel-caption">
-        Rename the collection appropriately.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/uses/sharing.png" alt="Click 'Create sharing link'. You can click 'unshare' at any later point.">
-      <div class="carousel-caption">
-        Click 'Create sharing link'. You can click 'Unshare' at any later point.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/uses/shared.png" alt="Now just share this link with anyone you want.">
-      <div class="carousel-caption">
-        Now just share this link with anyone you want.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/uses/sharedsubdirs.png" alt="Here's a more complex collection. [END]">
-      <div class="carousel-caption">
-        Here's a more complex collection. [END]
-      </div>
-    </div>
-
-  </div>
-
-  <!-- Controls -->
-  <a class="left carousel-control" href="#carousel-sharedata" role="button" data-slide="prev">
-    <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
-    <span class="sr-only">Previous</span>
-  </a>
-  <a class="right carousel-control" href="#carousel-sharedata" role="button" data-slide="next">
-    <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
-    <span class="sr-only">Next</span>
-  </a>
-</div>
-
-Tip: You may need to make your browser window bigger to see full-size images in the gallery above.
diff --git a/doc/start/index.html.textile.liquid b/doc/start/index.html.textile.liquid
deleted file mode 100644 (file)
index cddfb8e..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
----
-layout: default
-navsection: start 
-title: Welcome to Arvados!
-...
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-This guide provides an introduction to using Arvados to solve big data bioinformatics problems.
-
-h2. What is Arvados?
-
-Arvados is a free and open source bioinformatics platform for genomic and biomedical data.
-
-We address the needs of IT directors, lab principals, and bioinformaticians.
-
-h2. Why use Arvados?
-
-Arvados enables you to quickly begin using cloud computing resources in your bioinformatics work. It allows you to track your methods and datasets, share them securely, and easily re-run analyses.
-
-h3. Take a look (Screenshots gallery) 
-
-<div id="carousel-keyfeatures" class="carousel slide" data-interval="false">
-  <!-- Indicators -->
-  <ol class="carousel-indicators">
-    <li data-target="#carousel-keyfeatures" data-slide-to="0" class="active"></li>
-    <li data-target="#carousel-keyfeatures" data-slide-to="1"></li>
-    <li data-target="#carousel-keyfeatures" data-slide-to="2"></li>
-    <li data-target="#carousel-keyfeatures" data-slide-to="3"></li>
-    <li data-target="#carousel-keyfeatures" data-slide-to="4"></li>
-    <li data-target="#carousel-keyfeatures" data-slide-to="5"></li>
-    <li data-target="#carousel-keyfeatures" data-slide-to="6"></li>
-    <li data-target="#carousel-keyfeatures" data-slide-to="7"></li>
-    <li data-target="#carousel-keyfeatures" data-slide-to="8"></li>
-  </ol>
-
-  <!-- Wrapper for slides -->
-  <div class="carousel-inner" role="listbox">
-    <div class="item active">
-      <img src="{{ site.baseurl }}/images/keyfeatures/dashboard2.png" alt="[START] After logging in, you will see Workbench's dashboard.">
-      <div class="carousel-caption">
-        [START] After logging in, you will see Workbench's dashboard.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/keyfeatures/running2.png" alt="Pipelines describe a set of computational tasks (jobs).">
-      <div class="carousel-caption">
-        Pipelines describe a set of computational tasks (jobs).
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/keyfeatures/log.png" alt="The output of all jobs is logged and stored automatically.">
-      <div class="carousel-caption">
-        The output of all jobs is logged and stored automatically.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/keyfeatures/graph.png" alt="Pipelines can also be viewed in auto-generated graph form.">
-      <div class="carousel-caption">
-        Pipelines can also be viewed in auto-generated graph form.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/keyfeatures/rerun.png" alt="Pipelines can easily be re-run exactly as before, or...">
-      <div class="carousel-caption">
-        Pipelines can easily be re-run exactly as before, or...
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/keyfeatures/chooseinputs.png" alt="...you can change parameters or pick new datasets.">
-      <div class="carousel-caption">
-        ...you can change parameters or pick new datasets.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/keyfeatures/webupload.png" alt="With web upload, data can be uploaded right in Workbench.">
-      <div class="carousel-caption">
-        With web upload, data can be uploaded right in Workbench.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/keyfeatures/collectionpage.png" alt="Collections allow sharing datasets and job outputs easily. 'Create sharing link' with one click.">
-      <div class="carousel-caption">
-        Collections allow sharing datasets and job outputs easily. 'Create sharing link' with one click.
-      </div>
-    </div>
-
-    <div class="item">
-      <img src="{{ site.baseurl }}/images/keyfeatures/provenance.png" alt="Data provenance is tracked automatically. [END]">
-      <div class="carousel-caption">
-        Data provenance is tracked automatically. [END]
-      </div>
-    </div>
-
-
-  </div>
-
-  <!-- Controls -->
-  <a class="left carousel-control" href="#carousel-keyfeatures" role="button" data-slide="prev">
-    <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
-    <span class="sr-only">Previous</span>
-  </a>
-  <a class="right carousel-control" href="#carousel-keyfeatures" role="button" data-slide="next">
-    <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
-    <span class="sr-only">Next</span>
-  </a>
-</div>
-
-Note: Workbench is the web interface to Arvados.
-Tip: You may need to make your browser window bigger to see full-size images in the gallery above.
-
-h3. Key Features
-
-<ul>
-<li><strong>Track your methods</strong><br/>
-We log every compute job: software versions, machine images, input and output data hashes. Rely on a computer, not your memory and your note-taking skills.<br/><br/></li>
-<li><strong>Share your methods</strong><br/>
-Show other people what you did. Let them use your workflow on their own data. Publish a permalink to your methods and data, so others can reproduce and build on them easily.<br/><br/></li>
-<li><strong>Track data origin</strong><br/>
-Did you really only use fully consented public data in this analysis?<br/><br/></li>
-<li><strong>Get results sooner</strong><br/>
-Run your compute jobs faster by using multi-nodes and multi-cores, even if your programs are single-threaded.<br/><br/></li>
-</ul>
index 73bd9f599c2e293019eeba2056af9e6fd615fd2a..73dd65c4636538dbc16fb3607b89e2136e87d194 100755 (executable)
@@ -15,15 +15,15 @@ cwl:tool: bwa-mem.cwl
 reference:
   class: File
   location: keep:2463fa9efeb75e099685528b3b9071e0+438/19.fasta.bwt
-  arv:collectionUUID: qr1hi-4zz18-pwid4w22a40jp8l
+  arv:collectionUUID: jutro-4zz18-tv416l321i4r01e
 read_p1:
   class: File
   location: keep:ae480c5099b81e17267b7445e35b4bc7+180/HWI-ST1027_129_D0THKACXX.1_1.fastq
-  arv:collectionUUID: qr1hi-4zz18-h615rgfmqt3wje0
+  arv:collectionUUID: jutro-4zz18-8k5hsvee0izv2g3
 read_p2:
   class: File
   location: keep:ae480c5099b81e17267b7445e35b4bc7+180/HWI-ST1027_129_D0THKACXX.1_2.fastq
-  arv:collectionUUID: qr1hi-4zz18-h615rgfmqt3wje0
+  arv:collectionUUID: jutro-4zz18-8k5hsvee0izv2g3
 group_id: arvados_tutorial
 sample_id: HWI-ST1027_129
 PL: illumina
index 7e71e959ebc44d1d76867610939f30bc64b93110..e76aa78173a6bb33403ab807a608e9841789cc1d 100755 (executable)
@@ -9,13 +9,13 @@
 cwl:tool: bwa-mem.cwl
 reference:
   class: File
-  location: keep:qr1hi-4zz18-pwid4w22a40jp8l/19.fasta.bwt
+  location: keep:jutro-4zz18-tv416l321i4r01e/19.fasta.bwt
 read_p1:
   class: File
-  location: keep:qr1hi-4zz18-h615rgfmqt3wje0/HWI-ST1027_129_D0THKACXX.1_1.fastq
+  location: keep:jutro-4zz18-8k5hsvee0izv2g3/HWI-ST1027_129_D0THKACXX.1_1.fastq
 read_p2:
   class: File
-  location: keep:qr1hi-4zz18-h615rgfmqt3wje0/HWI-ST1027_129_D0THKACXX.1_2.fastq
+  location: keep:jutro-4zz18-8k5hsvee0izv2g3/HWI-ST1027_129_D0THKACXX.1_2.fastq
 group_id: arvados_tutorial
 sample_id: HWI-ST1027_129
 PL: illumina
index 20019712645902dbd1962b86a48bb8e59643c7b9..018867c83e29bdf04bb6d54173e86f059c192d0c 100755 (executable)
@@ -8,13 +8,13 @@ class: CommandLineTool
 
 hints:
   DockerRequirement:
-    dockerPull: lh3lh3/bwa
+    dockerPull: quay.io/biocontainers/bwa:0.7.17--ha92aebf_3
 
-baseCommand: [mem]
+baseCommand: [bwa, mem]
 
 arguments:
   - {prefix: "-t", valueFrom: $(runtime.cores)}
-  - {prefix: "-R", valueFrom: "@RG\tID:$(inputs.group_id)\tPL:$(inputs.PL)\tSM:$(inputs.sample_id)"}
+  - {prefix: "-R", valueFrom: '@RG\\\tID:$(inputs.group_id)\\\tPL:$(inputs.PL)\\\tSM:$(inputs.sample_id)'}
 
 inputs:
   reference:
index 725528f44d14e01e663c81fc317eaba1bde3886d..761d198ee4f504bc477b6575d9d1cde0c5b25085 100644 (file)
@@ -1,7 +1,7 @@
 ---
 layout: default
 navsection: userguide
-title: "Using arvados-cwl-runner"
+title: "arvados-cwl-runner options"
 ...
 {% comment %}
 Copyright (C) The Arvados Authors. All rights reserved.
@@ -74,10 +74,10 @@ Use the @--name@ and @--output-name@ options to specify the name of the workflow
 <pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner --name "Example bwa run" --output-name "Example bwa output" bwa-mem.cwl bwa-mem-input.yml</span>
 arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
 2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Upload local files: "bwa-mem.cwl"
-2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Uploaded to qr1hi-4zz18-h7ljh5u76760ww2
-2016-06-30 14:56:40 arvados.cwl-runner[27002] INFO: Submitted job qr1hi-8i9sb-fm2n3b1w0l6bskg
-2016-06-30 14:56:41 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-fm2n3b1w0l6bskg) is Running
-2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-fm2n3b1w0l6bskg) is Complete
+2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Uploaded to zzzzz-4zz18-h7ljh5u76760ww2
+2016-06-30 14:56:40 arvados.cwl-runner[27002] INFO: Submitted job zzzzz-8i9sb-fm2n3b1w0l6bskg
+2016-06-30 14:56:41 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (zzzzz-8i9sb-fm2n3b1w0l6bskg) is Running
+2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (zzzzz-8i9sb-fm2n3b1w0l6bskg) is Complete
 2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Overall process status is success
 {
     "aligned_sam": {
@@ -98,9 +98,9 @@ To submit a workflow and exit immediately, use the @--no-wait@ option.  This wil
 <pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner --no-wait bwa-mem.cwl bwa-mem-input.yml</span>
 arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
 2016-06-30 15:07:52 arvados.arv-run[12480] INFO: Upload local files: "bwa-mem.cwl"
-2016-06-30 15:07:52 arvados.arv-run[12480] INFO: Uploaded to qr1hi-4zz18-eqnfwrow8aysa9q
-2016-06-30 15:07:52 arvados.cwl-runner[12480] INFO: Submitted job qr1hi-8i9sb-fm2n3b1w0l6bskg
-qr1hi-8i9sb-fm2n3b1w0l6bskg
+2016-06-30 15:07:52 arvados.arv-run[12480] INFO: Uploaded to zzzzz-4zz18-eqnfwrow8aysa9q
+2016-06-30 15:07:52 arvados.cwl-runner[12480] INFO: Submitted job zzzzz-8i9sb-fm2n3b1w0l6bskg
+zzzzz-8i9sb-fm2n3b1w0l6bskg
 </code></pre>
 </notextile>
 
@@ -111,10 +111,10 @@ To run a workflow with local control, use @--local@.  This means that the host w
 <notextile>
 <pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner --local bwa-mem.cwl bwa-mem-input.yml</span>
 arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
-2016-07-01 10:05:19 arvados.cwl-runner[16290] INFO: Pipeline instance qr1hi-d1hrv-92wcu6ldtio74r4
-2016-07-01 10:05:28 arvados.cwl-runner[16290] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-2nzzfbuf9zjrj4g) is Queued
-2016-07-01 10:05:29 arvados.cwl-runner[16290] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-2nzzfbuf9zjrj4g) is Running
-2016-07-01 10:05:45 arvados.cwl-runner[16290] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-2nzzfbuf9zjrj4g) is Complete
+2016-07-01 10:05:19 arvados.cwl-runner[16290] INFO: Pipeline instance zzzzz-d1hrv-92wcu6ldtio74r4
+2016-07-01 10:05:28 arvados.cwl-runner[16290] INFO: Job bwa-mem.cwl (zzzzz-8i9sb-2nzzfbuf9zjrj4g) is Queued
+2016-07-01 10:05:29 arvados.cwl-runner[16290] INFO: Job bwa-mem.cwl (zzzzz-8i9sb-2nzzfbuf9zjrj4g) is Running
+2016-07-01 10:05:45 arvados.cwl-runner[16290] INFO: Job bwa-mem.cwl (zzzzz-8i9sb-2nzzfbuf9zjrj4g) is Complete
 2016-07-01 10:05:46 arvados.cwl-runner[16290] INFO: Overall process status is success
 {
     "aligned_sam": {
index 2be803b52aca1e403d391dff9b58701ba4319231..442a60b04f968706f604b53a2a7484d3f1daeb83 100644 (file)
@@ -1,7 +1,7 @@
 ---
 layout: default
 navsection: userguide
-title: "Running an Arvados workflow"
+title: "Starting a Workflow at the Command Line"
 ...
 {% comment %}
 Copyright (C) The Arvados Authors. All rights reserved.
@@ -13,44 +13,38 @@ SPDX-License-Identifier: CC-BY-SA-3.0
 
 {% include 'tutorial_expectations' %}
 
-{% include 'notebox_begin' %}
-
-By default, the @arvados-cwl-runner@ is installed on Arvados shell nodes.  If you want to submit jobs from somewhere else, such as your workstation, you may install "arvados-cwl-runner.":#setup
-
-{% include 'notebox_end' %}
-
 This tutorial will demonstrate how to submit a workflow at the command line using @arvados-cwl-runner@.
 
-h2. Running arvados-cwl-runner
+# "Get the tutorial files":#get-files
+# "Submitting a workflow to an Arvados cluster":#submitting
+# "Registering a workflow to use in Workbench":#registering
+# "Make a workflow file directly executable":#executable
 
-h3. Get the example files
+h2(#get-files). Get the tutorial files
 
-The tutorial files are located in the "documentation section of the Arvados source repository:":https://github.com/arvados/arvados/tree/master/doc/user/cwl/bwa-mem
+The tutorial files are located in the documentation section of the Arvados source repository, which can be found on "git.arvados.org":https://git.arvados.org/arvados.git/tree/HEAD:/doc/user/cwl/bwa-mem or "github":https://github.com/arvados/arvados/tree/master/doc/user/cwl/bwa-mem
 
 <notextile>
-<pre><code>~$ <span class="userinput">git clone https://github.com/arvados/arvados</span>
+<pre><code>~$ <span class="userinput">git clone https://git.arvados.org/arvados.git</span>
 ~$ <span class="userinput">cd arvados/doc/user/cwl/bwa-mem</span>
 </code></pre>
 </notextile>
 
-The tutorial data is hosted on "https://playground.arvados.org":https://playground.arvados.org (also referred to by the identifier *qr1hi*).  If you are using a different Arvados instance, you may need to copy the data to your own instance.  The easiest way to do this is with "arv-copy":{{site.baseurl}}/user/topics/arv-copy.html (this requires signing up for a free playground.arvados.org account).
+The tutorial data is hosted on "https://playground.arvados.org":https://playground.arvados.org (also referred to by the identifier *pirca*).  If you are using a different Arvados instance, you may need to copy the data to your own instance.  One way to do this is with "arv-copy":{{site.baseurl}}/user/topics/arv-copy.html (this requires signing up for a free playground.arvados.org account).
 
 <notextile>
-<pre><code>~$ <span class="userinput">arv-copy --src qr1hi --dst settings 2463fa9efeb75e099685528b3b9071e0+438</span>
-~$ <span class="userinput">arv-copy --src qr1hi --dst settings ae480c5099b81e17267b7445e35b4bc7+180</span>
-~$ <span class="userinput">arv-copy --src qr1hi --dst settings 655c6cd07550151b210961ed1d3852cf+57</span>
+<pre><code>~$ <span class="userinput">arv-copy --src pirca --dst settings 2463fa9efeb75e099685528b3b9071e0+438</span>
+~$ <span class="userinput">arv-copy --src pirca --dst settings ae480c5099b81e17267b7445e35b4bc7+180</span>
 </code></pre>
 </notextile>
 
 If you do not wish to create an account on "https://playground.arvados.org":https://playground.arvados.org, you may download the files anonymously and upload them to your local Arvados instance:
 
-"https://playground.arvados.org/collections/2463fa9efeb75e099685528b3b9071e0+438":https://playground.arvados.org/collections/2463fa9efeb75e099685528b3b9071e0+438
-
-"https://playground.arvados.org/collections/ae480c5099b81e17267b7445e35b4bc7+180":https://playground.arvados.org/collections/ae480c5099b81e17267b7445e35b4bc7+180
+"https://collections.pirca.arvadosapi.com/c=2463fa9efeb75e099685528b3b9071e0+438/":https://collections.pirca.arvadosapi.com/c=2463fa9efeb75e099685528b3b9071e0+438/
 
-"https://playground.arvados.org/collections/655c6cd07550151b210961ed1d3852cf+57":https://playground.arvados.org/collections/655c6cd07550151b210961ed1d3852cf+57
+"https://collections.pirca.arvadosapi.com/c=ae480c5099b81e17267b7445e35b4bc7+180/":https://collections.pirca.arvadosapi.com/c=ae480c5099b81e17267b7445e35b4bc7+180/
 
-h2. Submitting a workflow to an Arvados cluster
+h2(#submitting). Submitting a workflow to an Arvados cluster
 
 h3. Submit a workflow and wait for results
 
@@ -62,10 +56,10 @@ Use @arvados-cwl-runner@ to submit CWL workflows to Arvados.  After submitting t
 <pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner bwa-mem.cwl bwa-mem-input.yml</span>
 arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
 2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Upload local files: "bwa-mem.cwl"
-2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Uploaded to qr1hi-4zz18-h7ljh5u76760ww2
-2016-06-30 14:56:40 arvados.cwl-runner[27002] INFO: Submitted job qr1hi-8i9sb-fm2n3b1w0l6bskg
-2016-06-30 14:56:41 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-fm2n3b1w0l6bskg) is Running
-2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-fm2n3b1w0l6bskg) is Complete
+2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Uploaded to zzzzz-4zz18-h7ljh5u76760ww2
+2016-06-30 14:56:40 arvados.cwl-runner[27002] INFO: Submitted job zzzzz-8i9sb-fm2n3b1w0l6bskg
+2016-06-30 14:56:41 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (zzzzz-8i9sb-fm2n3b1w0l6bskg) is Running
+2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (zzzzz-8i9sb-fm2n3b1w0l6bskg) is Complete
 2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Overall process status is success
 {
     "aligned_sam": {
@@ -88,15 +82,19 @@ If you reference a file in "arv-mount":{{site.baseurl}}/user/tutorials/tutorial-
 
 If you reference a local file which is not in @arv-mount@, then @arvados-cwl-runner@ will upload the file to Keep and use the Keep URI reference from the upload.
 
-You can also execute CWL files directly from Keep:
+You can also execute CWL files that have been uploaded Keep:
 
 <notextile>
-<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner keep:655c6cd07550151b210961ed1d3852cf+57/bwa-mem.cwl bwa-mem-input.yml</span>
+<pre><code>
+~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arv-put --portable-data-hash --name "bwa-mem.cwl" bwa-mem.cwl</span>
+2020-08-20 13:40:02 arvados.arv_put[12976] INFO: Collection saved as 'bwa-mem.cwl'
+f141fc27e7cfa7f7b6d208df5e0ee01b+59
+~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner keep:f141fc27e7cfa7f7b6d208df5e0ee01b+59/bwa-mem.cwl bwa-mem-input.yml</span>
 arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
-2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Uploaded to qr1hi-4zz18-h7ljh5u76760ww2
-2016-06-30 14:56:40 arvados.cwl-runner[27002] INFO: Submitted job qr1hi-8i9sb-fm2n3b1w0l6bskg
-2016-06-30 14:56:41 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-fm2n3b1w0l6bskg) is Running
-2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-fm2n3b1w0l6bskg) is Complete
+2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Uploaded to zzzzz-4zz18-h7ljh5u76760ww2
+2016-06-30 14:56:40 arvados.cwl-runner[27002] INFO: Submitted job zzzzz-8i9sb-fm2n3b1w0l6bskg
+2016-06-30 14:56:41 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (zzzzz-8i9sb-fm2n3b1w0l6bskg) is Running
+2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (zzzzz-8i9sb-fm2n3b1w0l6bskg) is Complete
 2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Overall process status is success
 {
     "aligned_sam": {
@@ -109,50 +107,128 @@ arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107,
 </code></pre>
 </notextile>
 
+Note: uploading a workflow file to Keep is _not_ the same as registering the workflow for use in Workbench.  See "Registering a workflow to use in Workbench":#registering below.
+
 h3. Work reuse
 
 Workflows submitted with @arvados-cwl-runner@ will take advantage of Arvados job reuse.  If you submit a workflow which is identical to one that has run before, it will short cut the execution and return the result of the previous run.  This also applies to individual workflow steps.  For example, a two step workflow where the first step has run before will reuse results for first step and only execute the new second step.  You can disable this behavior with @--disable-reuse@.
 
 h3. Command line options
 
-See "Using arvados-cwl-runner":{{site.baseurl}}/user/cwl/cwl-run-options.html
+See "arvados-cwl-runner options":{{site.baseurl}}/user/cwl/cwl-run-options.html
 
-h2(#setup). Setting up arvados-cwl-runner
+h2(#registering). Registering a workflow to use in Workbench
 
-By default, the @arvados-cwl-runner@ is installed on Arvados shell nodes.  If you want to submit jobs from somewhere else, such as your workstation, you may install @arvados-cwl-runner@ using @pip@:
+Use @--create-workflow@ to register a CWL workflow with Arvados.  This enables you to share workflows with other Arvados users, and run them by clicking the <span class="btn btn-sm btn-primary"><i class="fa fa-fw fa-gear"></i> Run a process...</span> button on the Workbench Dashboard and on the command line by UUID.
 
 <notextile>
-<pre><code>~$ <span class="userinput">virtualenv ~/venv</span>
-~$ <span class="userinput">. ~/venv/bin/activate</span>
-~$ <span class="userinput">pip install -U setuptools</span>
-~$ <span class="userinput">pip install arvados-cwl-runner</span>
+<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner --create-workflow bwa-mem.cwl</span>
+arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
+2016-07-01 12:21:01 arvados.arv-run[15796] INFO: Upload local files: "bwa-mem.cwl"
+2016-07-01 12:21:01 arvados.arv-run[15796] INFO: Uploaded to zzzzz-4zz18-7e0hedrmkuyoei3
+2016-07-01 12:21:01 arvados.cwl-runner[15796] INFO: Created template zzzzz-p5p6p-rjleou1dwr167v5
+zzzzz-p5p6p-rjleou1dwr167v5
 </code></pre>
 </notextile>
 
-h3. Check Docker access
+You can provide a partial input file to set default values for the workflow input parameters.  You can also use the @--name@ option to set the name of the workflow:
 
-In order to pull and upload Docker images, @arvados-cwl-runner@ requires access to Docker.  You do not need Docker if the Docker images you intend to use are already available in Arvados.
+<notextile>
+<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner --name "My workflow with defaults" --create-workflow bwa-mem.cwl bwa-mem-template.yml</span>
+arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
+2016-07-01 14:09:50 arvados.arv-run[3730] INFO: Upload local files: "bwa-mem.cwl"
+2016-07-01 14:09:50 arvados.arv-run[3730] INFO: Uploaded to zzzzz-4zz18-0f91qkovk4ml18o
+2016-07-01 14:09:50 arvados.cwl-runner[3730] INFO: Created template zzzzz-p5p6p-0deqe6nuuyqns2i
+zzzzz-p5p6p-zuniv58hn8d0qd8
+</code></pre>
+</notextile>
 
-You can determine if you have access to Docker by running @docker version@:
+h3. Running registered workflows at the command line
+
+You can run a registered workflow at the command line by its UUID:
 
 <notextile>
-<pre><code>~$ <span class="userinput">docker version</span>
-Client:
- Version:      1.9.1
- API version:  1.21
- Go version:   go1.4.2
- Git commit:   a34a1d5
- Built:        Fri Nov 20 12:59:02 UTC 2015
- OS/Arch:      linux/amd64
-
-Server:
- Version:      1.9.1
- API version:  1.21
- Go version:   go1.4.2
- Git commit:   a34a1d5
- Built:        Fri Nov 20 12:59:02 UTC 2015
- OS/Arch:      linux/amd64
+<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner pirca-7fd4e-3nqbw08vtjl8ybz --help</span>
+INFO /home/peter/work/scripts/venv3/bin/arvados-cwl-runner 2.1.0.dev20200814195416, arvados-python-client 2.1.0.dev20200814195416, cwltool 3.0.20200807132242
+INFO Resolved 'pirca-7fd4e-3nqbw08vtjl8ybz' to 'arvwf:pirca-7fd4e-3nqbw08vtjl8ybz#main'
+usage: pirca-7fd4e-3nqbw08vtjl8ybz [-h] [--PL PL] [--group_id GROUP_ID]
+                                   [--read_p1 READ_P1] [--read_p2 READ_P2]
+                                   [--reference REFERENCE]
+                                   [--sample_id SAMPLE_ID]
+                                   [job_order]
+
+positional arguments:
+  job_order             Job input json file
+
+optional arguments:
+  -h, --help            show this help message and exit
+  --PL PL
+  --group_id GROUP_ID
+  --read_p1 READ_P1     The reads, in fastq format.
+  --read_p2 READ_P2     For mate paired reads, the second file (optional).
+  --reference REFERENCE
+                        The index files produced by `bwa index`
+  --sample_id SAMPLE_ID
 </code></pre>
 </notextile>
 
-If this returns an error, contact the sysadmin of your cluster for assistance.
+h2(#executable). Make a workflow file directly executable
+
+You can make a workflow file directly executable (@cwl-runner@ should be an alias to @arvados-cwl-runner@) by adding the following line to the top of the file:
+
+<notextile>
+<pre><code>#!/usr/bin/env cwl-runner
+</code></pre>
+</notextile>
+
+<notextile>
+<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">./bwa-mem.cwl bwa-mem-input.yml</span>
+arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
+2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Upload local files: "bwa-mem.cwl"
+2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Uploaded to zzzzz-4zz18-h7ljh5u76760ww2
+2016-06-30 14:56:40 arvados.cwl-runner[27002] INFO: Submitted job zzzzz-8i9sb-fm2n3b1w0l6bskg
+2016-06-30 14:56:41 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (zzzzz-8i9sb-fm2n3b1w0l6bskg) is Running
+2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (zzzzz-8i9sb-fm2n3b1w0l6bskg) is Complete
+2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Overall process status is success
+{
+    "aligned_sam": {
+        "path": "keep:54325254b226664960de07b3b9482349+154/HWI-ST1027_129_D0THKACXX.1_1.sam",
+        "checksum": "sha1$0dc46a3126d0b5d4ce213b5f0e86e2d05a54755a",
+        "class": "File",
+        "size": 30738986
+    }
+}
+</code></pre>
+</notextile>
+
+You can even make an input file directly executable the same way with the following two lines at the top:
+
+<notextile>
+<pre><code>#!/usr/bin/env cwl-runner
+cwl:tool: <span class="userinput">bwa-mem.cwl</span>
+</code></pre>
+</notextile>
+
+<notextile>
+<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">./bwa-mem-input.yml</span>
+arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
+2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Upload local files: "bwa-mem.cwl"
+2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Uploaded to zzzzz-4zz18-h7ljh5u76760ww2
+2016-06-30 14:56:40 arvados.cwl-runner[27002] INFO: Submitted job zzzzz-8i9sb-fm2n3b1w0l6bskg
+2016-06-30 14:56:41 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (zzzzz-8i9sb-fm2n3b1w0l6bskg) is Running
+2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (zzzzz-8i9sb-fm2n3b1w0l6bskg) is Complete
+2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Overall process status is success
+{
+    "aligned_sam": {
+        "path": "keep:54325254b226664960de07b3b9482349+154/HWI-ST1027_129_D0THKACXX.1_1.sam",
+        "checksum": "sha1$0dc46a3126d0b5d4ce213b5f0e86e2d05a54755a",
+        "class": "File",
+        "size": 30738986
+    }
+}
+</code></pre>
+</notextile>
+
+h2(#setup). Setting up arvados-cwl-runner
+
+See "Arvados CWL Runner":{{site.baseurl}}/sdk/python/arvados-cwl-runner.html
index ee36014cb5d93e4cd56e4dc1a65ed10a5d266780..bd07161ce3b203aca424b5287a48362d51d46787 100644 (file)
@@ -1,7 +1,7 @@
 ---
 layout: default
 navsection: userguide
-title: Writing Portable High-Performance Workflows
+title: Guidelines for Writing High-Performance Portable Workflows
 ...
 {% comment %}
 Copyright (C) The Arvados Authors. All rights reserved.
index 5fcfcbe3bc3e8c00b3e4f20467ad224271e47c97..ac679dc15403cc6e557d2ad41045cfab166d023d 100644 (file)
@@ -1,7 +1,7 @@
 ---
 layout: default
 navsection: userguide
-title: CWL version and API support
+title: CWL version support
 ...
 {% comment %}
 Copyright (C) The Arvados Authors. All rights reserved.
@@ -9,6 +9,8 @@ Copyright (C) The Arvados Authors. All rights reserved.
 SPDX-License-Identifier: CC-BY-SA-3.0
 {% endcomment %}
 
+Arvados supports CWL v1.0, v1.1 and v1.2.
+
 h2(#v12). Upgrading your workflows to CWL v1.2
 
 If you are starting from a CWL v1.0 document, see "Upgrading your workflows to CWL v1.1":#v11 below.
index b707891a1e168b0593f2eedc99cf1c08a90ac718..1097e4e9d84f6135624c91cdee24c6e755f6e5d3 100644 (file)
@@ -16,14 +16,14 @@ Check that you are able to access the Arvados API server using @arv user current
 <notextile>
 <pre><code>$ <span class="userinput">arv user current</span>
 {
- "href":"https://qr1hi.arvadosapi.com/arvados/v1/users/qr1hi-xioed-9z2p3pn12yqdaem",
+ "href":"https://zzzzz.arvadosapi.com/arvados/v1/users/zzzzz-xioed-9z2p3pn12yqdaem",
  "kind":"arvados#user",
  "etag":"8u0xwb9f3otb2xx9hto4wyo03",
- "uuid":"qr1hi-tpzed-92d3kxnimy3d4e8",
- "owner_uuid":"qr1hi-tpqed-23iddeohxta2r59",
+ "uuid":"zzzzz-tpzed-92d3kxnimy3d4e8",
+ "owner_uuid":"zzzzz-tpqed-23iddeohxta2r59",
  "created_at":"2013-12-02T17:05:47Z",
- "modified_by_client_uuid":"qr1hi-xxfg8-owxa2oa2s33jyej",
- "modified_by_user_uuid":"qr1hi-tpqed-23iddeohxta2r59",
+ "modified_by_client_uuid":"zzzzz-xxfg8-owxa2oa2s33jyej",
+ "modified_by_user_uuid":"zzzzz-tpqed-23iddeohxta2r59",
  "modified_at":"2013-12-02T17:07:08Z",
  "updated_at":"2013-12-05T19:51:08Z",
  "email":"you@example.com",
index 284d0a1f04aca0117e54737cffba8586c6a57188..80cb3913145c959d9faff32b20a856d2edab95f1 100644 (file)
@@ -9,7 +9,7 @@ Copyright (C) The Arvados Authors. All rights reserved.
 SPDX-License-Identifier: CC-BY-SA-3.0
 {% endcomment %}
 
-This document is for accessing an Arvados VM using SSH keys in Unix environments (Linux, OS X, Cygwin). If you would like to access VM through your browser, please visit the "Accessing an Arvados VM with Webshell":vm-login-with-webshell.html page. If you are using a Windows environment, please visit the "Accessing an Arvados VM with SSH - Windows Environments":ssh-access-windows.html page.
+This document is for accessing an Arvados VM using SSH keys in Unix-like environments (Linux, macOS, Cygwin, Windows Subsystem for Linux). If you would like to access VM through your browser, please visit the "Accessing an Arvados VM with Webshell":vm-login-with-webshell.html page. If you are using a Windows environment, please visit the "Accessing an Arvados VM with SSH - Windows Environments":ssh-access-windows.html page.
 
 {% include 'ssh_intro' %}
 
@@ -49,7 +49,7 @@ ssh-rsa AAAAB3NzaC1ycEDoNotUseExampleKeyDoNotUseExampleKeyDoNotUseExampleKeyDoNo
 
 Now you can set up @ssh-agent@ (next) or proceed with "adding your key to the Arvados Workbench.":#workbench
 
-h3. Set up ssh-agent (recommended)
+h3. Set up ssh-agent (optional)
 
 If you find you are entering your passphrase frequently, you can use @ssh-agent@ to manage your credentials.  Use @ssh-add -l@ to test if you already have ssh-agent running:
 
@@ -80,11 +80,21 @@ When everything is set up, @ssh-add -l@ should yield output that looks something
 
 {% include 'ssh_addkey' %}
 
-h3. Connecting to the virtual machine
+h3. Connecting directly
 
-Use the following command to connect to the _shell_ VM instance as _you_.  Replace *<code>you@shell</code>* at the end of the following command with your *login* and *hostname* from Workbench:
+If the VM is available on the public Internet (or you are on the same private network as the VM) you can connect directly with @ssh@.  You can probably copy-and-paste the text from *Command line* column directly into a terminal.
 
-notextile. <pre><code>$ <span class="userinput">ssh -o "ProxyCommand ssh -p2222 turnout@switchyard.{{ site.arvados_api_host }} -x -a <b>shell</b>" -x <b>you@shell</b></span></code></pre>
+Use the following example command to connect as _you_ to the _shell.ClusterID.example.com_ VM instance.  Replace *<code>you@shell.ClusterID.example.com</code>* at the end of the following command with your *login* and *hostname* from Workbench.
+
+notextile. <pre><code>$ <span class="userinput">ssh <b>you@shell.ClusterID.example.com</b></span></code></pre>
+
+h3. Connecting through switchyard
+
+Some Arvados installations use "switchyard" to isolate shell VMs from the public Internet.
+
+Use the following example command to connect to the _shell_ VM instance as _you_.  Replace *<code>you@shell</code>* at the end of the following command with your *login* and *hostname* from Workbench:
+
+notextile. <pre><code>$ <span class="userinput">ssh -o "ProxyCommand ssh -p2222 turnout@switchyard.ClusterID.example.com -x -a <b>shell</b>" -x <b>you@shell</b></span></code></pre>
 
 This command does several things at once. You usually cannot log in directly to virtual machines over the public Internet.  Instead, you log into a "switchyard" server and then tell the switchyard which virtual machine you want to connect to.
 
@@ -99,7 +109,7 @@ This command does several things at once. You usually cannot log in directly to
 
 You should now be able to log into the Arvados VM and "check your environment.":check-environment.html
 
-h3. Configuration (recommended)
+h4. Configuration (recommended)
 
 The command line above is cumbersome, but you can configure SSH to remember many of these settings.  Add this text to the file @.ssh/config@ in your home directory (create a new file if @.ssh/config@ doesn't exist):
 
index 0406e7c03bd3f52c1491321e4935f2304ddb72d4..5cbe2a3285ea6134b6922910c176c7dadccbfbcd 100644 (file)
@@ -9,13 +9,13 @@ Copyright (C) The Arvados Authors. All rights reserved.
 SPDX-License-Identifier: CC-BY-SA-3.0
 {% endcomment %}
 
-This document is for accessing an Arvados VM using SSH keys in Windows environments. If you would like to use to access VM through your browser, please visit the "Accessing an Arvados VM with Webshell":vm-login-with-webshell.html page. If you are using a Unix environment (Linux, OS X, Cygwin), please visit the "Accessing an Arvados VM with SSH - Unix Environments":ssh-access-unix.html page.
+This document is for accessing an Arvados VM using SSH keys in Windows environments using PuTTY.  If you would like to use to access VM through your browser, please visit the "Accessing an Arvados VM with Webshell":vm-login-with-webshell.html page.  If you are using a Unix-like environment (Linux, macOS, Cygwin, or Windows Subsystem for Linux), please visit the "Accessing an Arvados VM with SSH - Unix Environments":ssh-access-unix.html page.
 
 {% include 'ssh_intro' %}
 
 h1(#gettingkey). Getting your SSH key
 
-(Note: if you are using the SSH client that comes with "Cygwin":http://cygwin.com, please use instructions found in the "Accessing an Arvados VM with SSH - Unix Environments":ssh-access-unix.html page.)
+(Note: If you are using the SSH client that comes with "Cygwin":http://cygwin.com or Windows Subsystem for Linux (WSL) please use instructions found in the "Accessing an Arvados VM with SSH - Unix Environments":ssh-access-unix.html page.)
 
 We will be using PuTTY to connect to Arvados. "PuTTY":http://www.chiark.greenend.org.uk/~sgtatham/putty/ is a free (MIT-licensed) Win32 Telnet and SSH client. PuTTY includes all the tools a Windows user needs to create private keys and make SSH connections to your virtual machines in the Arvados Cloud.
 
@@ -57,6 +57,16 @@ Pageant is a PuTTY utility that manages your private keys so is not necessary to
 
 h3. Initial configuration
 
+h4. Connecting directly
+
+# Open PuTTY from the Start Menu.
+# On the Session screen set the Host Name (or IP address) to “shell.ClusterID.example.com”, which is the hostname listed in the _Virtual Machines_ page.
+# On the Session screen set the Port to “22”.
+# On the Connection %(rarr)&rarr;% Data screen set the Auto-login username to the username listed in the *Login name* column on the Arvados Workbench Virtual machines_ page.
+# Return to the Session screen. In the Saved Sessions box, enter a name for this configuration and click Save.
+
+h4. Connecting through switchyard
+
 # Open PuTTY from the Start Menu.
 # On the Session screen set the Host Name (or IP address) to “shell”, which is the hostname listed in the _Virtual Machines_ page.
 # On the Session screen set the Port to “22”.
index 551002e55eb051e4964025f3156ee8c0a9fb0561..0aeabab11bea1db943c031c9409d1ab6b693b50f 100644 (file)
@@ -15,7 +15,11 @@ h2(#webshell). Access VM using webshell
 
 Webshell gives you access to an arvados virtual machine from your browser with no additional setup.
 
-In the Arvados Workbench, click on the dropdown menu icon <span class="fa fa-lg fa-user"></span> <span class="caret"></span> in the upper right corner of the top navigation menu to access the user settings menu, and click on the menu item *Virtual machines* to see the list of virtual machines you can access.  If you do not have access to any virtual machines, please click on <span class="btn btn-sm btn-primary">Send request for shell access</span> or send an email to "support@curoverse.com":mailto:support@curoverse.com.
+{% include 'notebox_begin' %}
+Some Arvados clusters may not have webshell set up.  If you do not see a "Log in" button or "web shell" column, you will have to follow the "Unix":ssh-access-unix.html or "Windows":ssh-access-windows.html @ssh@ instructions.
+{% include 'notebox_end' %}
+
+In the Arvados Workbench, click on the dropdown menu icon <span class="fa fa-lg fa-user"></span> <span class="caret"></span> in the upper right corner of the top navigation menu to access the user settings menu, and click on the menu item *Virtual machines* to see the list of virtual machines you can access.  If you do not have access to any virtual machines,  please click on <span class="btn btn-sm btn-primary">Send request for shell access</span> (if present) or contact your system administrator.  For the Arvados Playground, this is "info@curii.com":mailto:info@curii.com .
 
 Each row in the Virtual Machines panel lists the hostname of the VM, along with a <code>Log in as *you*</code> button under the column "Web shell". Clicking on this button will open up a webshell terminal for you in a new browser tab and log you in.
 
index fc704227e0a7ebb65ef4524be7e6f2abce094b12..644cf7d2086967b057309d37ae733c782114f724 100644 (file)
@@ -9,14 +9,26 @@ Copyright (C) The Arvados Authors. All rights reserved.
 SPDX-License-Identifier: CC-BY-SA-3.0
 {% endcomment %}
 
-If you are using the default Arvados instance for this guide, you can Access Arvados Workbench using this link:
+{% include 'notebox_begin' %}
+This guide covers the classic Arvados Workbench web application, sometimes referred to as "Workbench 1".  There is also a new Workbench web application under development called "Workbench 2".  Sites which have both Workbench applications installed will have a dropdown menu option "Switch to Workbench 2" to switch between versions.
 
-<a href="{{site.arvados_workbench_host}}/" target="_blank">{{site.arvados_workbench_host}}/</a>
+This guide will be updated to cover "Workbench 2" in the future.
+{% include 'notebox_end' %}
 
-(If you are using a different Arvados instance than the default for this guide, replace *{{ site.arvados_workbench_host }}* with your private instance in all of the examples in this guide.)
+You can access the Arvados Workbench used in this guide using this link:
 
-You may be asked to log in using a Google account.  Arvados uses only your name and email address from Google services for identification, and will never access any personal information.  If you are accessing Arvados for the first time, the Workbench may indicate your account status is *New / inactive*.  If this is the case, contact the administrator of the Arvados instance to request activation of your account.
+<a href="{{site.arvados_workbench_host}}/" target="_blank">{{site.arvados_workbench_host}}</a>
 
-Once your account is active, logging in to the Workbench will present you with the Dashboard. This gives a summary of your projects and recent activity in the Arvados instance.  "You are now ready to run your first pipeline.":{{ site.baseurl }}/user/tutorials/tutorial-workflow-workbench.html
+If you are using a different Arvados instance replace @{{ site.arvados_workbench_host }}@ with your private instance in all of the examples in this guide.
+
+h2. Playground
+
+Curii operates a public demonstration instance of Arvados called the Arvados Playground, which can be found at <a href="https://playground.arvados.org" target="_blank">https://playground.arvados.org</a> .  Some examples in this guide involve getting data from the Playground instance.
+
+h2. Logging in
+
+You will be asked to log in.  Arvados uses only your name and email address for identification, and will never access any personal information.  If you are accessing Arvados for the first time, the Workbench may indicate your account status is *New / inactive*.  If this is the case, contact the administrator of the Arvados instance to request activation of your account.
+
+Once your account is active, logging in to the Workbench will present you with the Dashboard. This gives a summary of your projects and recent activity in the Arvados instance.  You are now ready to "upload data":{{ site.baseurl }}/user/tutorials/tutorial-keep.html or "run your first workflow.":{{ site.baseurl }}/user/tutorials/tutorial-workflow-workbench.html
 
 !{display: block;margin-left: 25px;margin-right: auto;border:1px solid lightgray;}{{ site.baseurl }}/images/workbench-dashboard.png!
index 909394ef47a1cd6a9798881cfd81854073d7ab8c..4b0a443d3ce03d6c12c85ba9e78438a8429c9a96 100644 (file)
@@ -9,14 +9,14 @@ Copyright (C) The Arvados Authors. All rights reserved.
 SPDX-License-Identifier: CC-BY-SA-3.0
 {% endcomment %}
 
-This guide provides a reference for using Arvados to solve scientific big data problems, including:
+Arvados is an open source platform for managing, processing, and sharing genomic and other large scientific and biomedical data.  This guide provides a reference for using Arvados to solve scientific big data problems, including:
 
 * Robust storage of very large files, such as whole genome sequences, using the "Arvados Keep":{{site.baseurl}}/user/tutorials/tutorial-keep.html content-addressable cluster file system.
 * Running compute-intensive scientific analysis pipelines, such as genomic alignment and variant calls using the "Arvados Crunch":{{site.baseurl}}/user/tutorials/intro-crunch.html cluster compute engine.
 * Accessing, organizing, and sharing data, workflows and results using the "Arvados Workbench":{{site.baseurl}}/user/getting_started/workbench.html web application.
 * Running an analysis using multiple clusters (HPC, cloud, or hybrid) with "Federated Multi-Cluster Workflows":{{site.baseurl}}/user/cwl/federated-workflows.html .
 
-The examples in this guide use the public Arvados instance located at <a href="{{site.arvados_workbench_host}}/" target="_blank">{{site.arvados_workbench_host}}</a>.  If you are using a different Arvados instance replace @{{ site.arvados_workbench_host }}@ with your private instance in all of the examples in this guide.
+The examples in this guide use the Arvados instance located at <a href="{{site.arvados_workbench_host}}/" target="_blank">{{site.arvados_workbench_host}}</a>.  If you are using a different Arvados instance replace @{{ site.arvados_workbench_host }}@ with your private instance in all of the examples in this guide.
 
 h2. Typographic conventions
 
index 0f0e40be9c4afdd136cdfdc17d6856c93453b047..d35df4fcec9f92f12e2b9dc3020667be5f4153f0 100644 (file)
@@ -9,103 +9,74 @@ Copyright (C) The Arvados Authors. All rights reserved.
 SPDX-License-Identifier: CC-BY-SA-3.0
 {% endcomment %}
 
-{% include 'crunch1only_begin' %}
-On those sites, the "copy a pipeline template" feature described below is not available. However, "copy a workflow" feature is not yet implemented.
-{% include 'crunch1only_end' %}
-
 This tutorial describes how to copy Arvados objects from one cluster to another by using @arv-copy@.
 
 {% include 'tutorial_expectations' %}
 
 h2. arv-copy
 
-@arv-copy@ allows users to copy collections and pipeline templates from one cluster to another. By default, @arv-copy@ will recursively go through a template and copy all dependencies associated with the object.
+@arv-copy@ allows users to copy collections and workflows from one cluster to another. By default, @arv-copy@ will recursively go through the workflow and copy all dependencies associated with the object.
 
-For example, let's copy from the <a href="https://playground.arvados.org/">Arvados playground</a>, also known as *qr1hi*, to *dst_cluster*. The names *qr1hi* and *dst_cluster* are interchangable with any cluster name. You can find the cluster name from the prefix of the uuid of the object you want to copy. For example, in *qr1hi*-4zz18-tci4vn4fa95w0zx, the cluster name is qr1hi.
+For example, let's copy from the <a href="https://playground.arvados.org/">Arvados playground</a>, also known as *pirca*, to *dstcl*. The names *pirca* and *dstcl* are interchangable with any cluster id. You can find the cluster name from the prefix of the uuid of the object you want to copy. For example, in *zzzzz*-4zz18-tci4vn4fa95w0zx, the cluster name is *zzzzz* .
 
-In order to communicate with both clusters, you must create custom configuration files for each cluster. In the Arvados Workbench, click on the dropdown menu icon <span class="fa fa-lg fa-user"></span> <span class="caret"></span> in the upper right corner of the top navigation menu to access the user settings menu, and click on the menu item *Current token*. Copy the @ARVADOS_API_HOST@ and @ARVADOS_API_TOKEN@ in both of your clusters. Then, create two configuration files, one for each cluster. The names of the files must have the format of *ClusterID.conf*. In our example, let's make two files, one for *qr1hi* and one for *dst_cluster*. From your *Current token* page in *qr1hi* and *dst_cluster*, copy the @ARVADOS_API_HOST@ and @ARVADOS_API_TOKEN@.
+In order to communicate with both clusters, you must create custom configuration files for each cluster. In the Arvados Workbench, click on the dropdown menu icon <span class="fa fa-lg fa-user"></span> <span class="caret"></span> in the upper right corner of the top navigation menu to access the user settings menu, and click on the menu item *Current token*. Copy the @ARVADOS_API_HOST@ and @ARVADOS_API_TOKEN@ in both of your clusters. Then, create two configuration files in @~/.config/arvados@, one for each cluster. The names of the files must have the format of *ClusterID.conf*. Navigate to the *Current token* page on each of *pirca* and *dstcl* to get the @ARVADOS_API_HOST@ and @ARVADOS_API_TOKEN@.
 
 !{display: block;margin-left: 25px;margin-right: auto;}{{ site.baseurl }}/images/api-token-host.png!
 
-Copy your @ARVADOS_API_HOST@ and @ARVADOS_API_TOKEN@ into the config files as shown below in the shell account from which you are executing the commands. For example, the default shell you may have access to is shell.qr1hi. You can add these files in ~/.config/arvados/ in the qr1hi shell terminal.
+The config file consists of two lines, one for ARVADOS_API_HOST and one for ARVADOS_API_TOKEN:
 
-<notextile>
-<pre><code>~$ <span class="userinput">cd ~/.config/arvados</span>
-~$ <span class="userinput">echo "ARVADOS_API_HOST=qr1hi.arvadosapi.com" >> qr1hi.conf</span>
-~$ <span class="userinput">echo "ARVADOS_API_TOKEN=123456789abcdefghijkl" >> qr1hi.conf</span>
-~$ <span class="userinput">echo "ARVADOS_API_HOST=dst_cluster.arvadosapi.com" >> dst_cluster.conf</span>
-~$ <span class="userinput">echo "ARVADOS_API_TOKEN=987654321lkjihgfedcba" >> dst_cluster.conf</span>
-</code></pre>
-</notextile>
+<pre>
+ARVADOS_API_HOST=zzzzz.arvadosapi.com
+ARVADOS_API_TOKEN=v2/zzzzz-gj3su-xxxxxxxxxxxxxxx/123456789abcdefghijkl
+</pre>
+
+Copy your @ARVADOS_API_HOST@ and @ARVADOS_API_TOKEN@ into the config files as shown below in the shell account from which you are executing the commands.  In our example, you need two files, @~/.config/arvados/pirca.conf@ and @~/.config/arvados/dstcl.conf@.
 
-Now you're ready to copy between *qr1hi* and *dst_cluster*!
+Now you're ready to copy between *pirca* and *dstcl*!
 
 h3. How to copy a collection
 
-First, select the uuid of the collection you want to copy from the source cluster. The uuid can be found in the collection display page in the collection summary area (top left box), or from the URL bar (the part after @collections/...@)
+First, determine the uuid or portable data hash of the collection you want to copy from the source cluster. The uuid can be found in the collection display page in the collection summary area (top left box), or from the URL bar (the part after @collections/...@)
 
-Now copy the collection from *qr1hi* to *dst_cluster*. We will use the uuid @qr1hi-4zz18-tci4vn4fa95w0zx@ as an example. You can find this collection in the <a href="https://playground.arvados.org/collections/qr1hi-4zz18-tci4vn4fa95w0zx">lobSTR v.3 project on playground.arvados.org</a>.
+Now copy the collection from *pirca* to *dstcl*. We will use the uuid @jutro-4zz18-tv416l321i4r01e@ as an example. You can find this collection on <a href="https://playground.arvados.org/collections/jutro-4zz18-tv416l321i4r01e">playground.arvados.org</a>.
 <notextile>
-<pre><code>~$ <span class="userinput">arv-copy --src qr1hi --dst dst_cluster qr1hi-4zz18-tci4vn4fa95w0zx</span>
-qr1hi-4zz18-tci4vn4fa95w0zx: 6.1M / 6.1M 100.0%
-arvados.arv-copy[1234] INFO: Success: created copy with uuid dst_cluster-4zz18-8765943210cdbae
-</code></pre>
-</notextile>
-
-The output of arv-copy displays the uuid of the collection generated in the destination cluster. By default, the output is placed in your home project in the destination cluster. If you want to place your collection in a pre-created project, you can specify the project you want it to be in using the tag @--project-uuid@ followed by the project uuid.
-
-For example, this will copy the collection to project dst_cluster-j7d0g-a894213ukjhal12 in the destination cluster.
-
-<notextile> <pre><code>~$ <span class="userinput">arv-copy --src qr1hi --dst dst_cluster --project-uuid dst_cluster-j7d0g-a894213ukjhal12 qr1hi-4zz18-tci4vn4fa95w0zx</span>
+<pre><code>~$ <span class="userinput">arv-copy --src pirca --dst dstcl jutro-4zz18-tv416l321i4r01e</span>
+jutro-4zz18-tv416l321i4r01e: 6.1M / 6.1M 100.0%
+arvados.arv-copy[1234] INFO: Success: created copy with uuid dstcl-4zz18-xxxxxxxxxxxxxxx
 </code></pre>
 </notextile>
 
-h3. How to copy a pipeline template
-
-{% include 'arv_copy_expectations' %}
-
-We will use the uuid @qr1hi-p5p6p-9pkaxt6qjnkxhhu@ as an example pipeline template.
+You can also copy by content address:
 
 <notextile>
-<pre><code>~$ <span class="userinput">arv-copy --src qr1hi --dst dst_cluster --dst-git-repo $USER/tutorial qr1hi-p5p6p-9pkaxt6qjnkxhhu</span>
-To git@git.dst_cluster.arvadosapi.com:$USER/tutorial.git
- * [new branch] git_git_qr1hi_arvadosapi_com_arvados_git_ac21f0d45a76294aaca0c0c0fdf06eb72d03368d -> git_git_qr1hi_arvadosapi_com_arvados_git_ac21f0d45a76294aaca0c0c0fdf06eb72d03368d
-arvados.arv-copy[19694] INFO: Success: created copy with uuid dst_cluster-p5p6p-rym2h5ub9m8ofwj
+<pre><code>~$ <span class="userinput">arv-copy --src pirca --dst dstcl 2463fa9efeb75e099685528b3b9071e0+438</span>
+2463fa9efeb75e099685528b3b9071e0+438: 6.1M / 6.1M 100.0%
+arvados.arv-copy[1234] INFO: Success: created copy with uuid dstcl-4zz18-xxxxxxxxxxxxxxx
 </code></pre>
 </notextile>
 
-New branches in the destination git repo will be created for each branch used in the pipeline template. For example, if your source branch was named ac21f0d45a76294aaca0c0c0fdf06eb72d03368d, your new branch will be named @git_git_qr1hi_arvadosapi_com_reponame_git_ac21f0d45a76294aaca0c0c0fdf06eb72d03368d@.
-
-By default, if you copy a pipeline template recursively, you will find that the template as well as all the dependencies are in your home project.
+The output of arv-copy displays the uuid of the collection generated in the destination cluster. By default, the output is placed in your home project in the destination cluster. If you want to place your collection in an existing project, you can specify the project you want it to be in using the tag @--project-uuid@ followed by the project uuid.
 
-If you would like to copy the object without dependencies, you can use the @--no-recursive@ tag.
+For example, this will copy the collection to project dstcl-j7d0g-a894213ukjhal12 in the destination cluster.
 
-For example, we can copy the same object using this tag.
-
-<notextile>
-<pre><code>~$ <span class="userinput">arv-copy --src qr1hi --dst dst_cluster --dst-git-repo $USER/tutorial --no-recursive qr1hi-p5p6p-9pkaxt6qjnkxhhu</span>
+<notextile> <pre><code>~$ <span class="userinput">arv-copy --src pirca --dst dstcl --project-uuid dstcl-j7d0g-a894213ukjhal12 jutro-4zz18-tv416l321i4r01e
 </code></pre>
 </notextile>
 
 h3. How to copy a workflow
 
-We will use the uuid @zzzzz-7fd4e-sampleworkflow1@ as an example workflow.
+We will use the uuid @jutro-7fd4e-mkmmq53m1ze6apx@ as an example workflow.
 
 <notextile>
-<pre><code>~$ <span class="userinput">arv-copy --src zzzzz --dst dst_cluster --dst-git-repo $USER/tutorial zzzzz-7fd4e-sampleworkflow1</span>
-zzzzz-4zz18-jidprdejysravcr: 1143M / 1143M 100.0%
-2017-01-04 04:11:58 arvados.arv-copy[5906] INFO:
-2017-01-04 04:11:58 arvados.arv-copy[5906] INFO: Success: created copy with uuid dst_cluster-7fd4e-ojtgpne594ubkt7
+<pre><code>~$ <span class="userinput">arv-copy --src jutro --dst pirca --project-uuid pirca-j7d0g-ecak8knpefz8ere jutro-7fd4e-mkmmq53m1ze6apx</span>
+ae480c5099b81e17267b7445e35b4bc7+180: 23M / 23M 100.0%
+2463fa9efeb75e099685528b3b9071e0+438: 156M / 156M 100.0%
+jutro-4zz18-vvvqlops0a0kpdl: 94M / 94M 100.0%
+2020-08-19 17:04:13 arvados.arv-copy[4789] INFO:
+2020-08-19 17:04:13 arvados.arv-copy[4789] INFO: Success: created copy with uuid pirca-7fd4e-s0tw9rfbkpo2fmx
 </code></pre>
 </notextile>
 
-The name, description, and workflow definition from the original workflow will be used for the destination copy. In addition, any *locations* and *docker images* found in the src workflow definition will also be copied to the destination recursively.
+The name, description, and workflow definition from the original workflow will be used for the destination copy. In addition, any *collections* and *docker images* referenced in the source workflow definition will also be copied to the destination.
 
 If you would like to copy the object without dependencies, you can use the @--no-recursive@ flag.
-
-For example, we can copy the same object non-recursively using the following:
-
-<notextile>
-<pre><code>~$ <span class="userinput">arv-copy --src zzzzz --dst dst_cluster --dst-git-repo $USER/tutorial --no-recursive zzzzz-7fd4e-sampleworkflow1</span>
-</code></pre>
-</notextile>
index e9e84502680cd8641150d5dee064d5ba4561e9fc..bb1c7dd53e8cdaffd88d83b95b2177ae571fa55a 100644 (file)
@@ -1,7 +1,7 @@
 ---
 layout: default
 navsection: userguide
-title: "Customizing Crunch environment using Docker"
+title: "Working with Docker images"
 ...
 {% comment %}
 Copyright (C) The Arvados Authors. All rights reserved.
@@ -9,145 +9,80 @@ Copyright (C) The Arvados Authors. All rights reserved.
 SPDX-License-Identifier: CC-BY-SA-3.0
 {% endcomment %}
 
-This page describes how to customize the runtime environment (e.g., the programs, libraries, and other dependencies needed to run a job) that a crunch script will be run in using "Docker.":https://www.docker.com/  Docker is a tool for building and running containers that isolate applications from other applications running on the same node.  For detailed information about Docker, see the "Docker User Guide.":https://docs.docker.com/userguide/
+This page describes how to set up the runtime environment (e.g., the programs, libraries, and other dependencies needed to run a job) that a workflow step will be run in using "Docker.":https://www.docker.com/  Docker is a tool for building and running containers that isolate applications from other applications running on the same node.  For detailed information about Docker, see the "Docker User Guide.":https://docs.docker.com/userguide/
 
-This page will demonstrate how to:
+This page describes:
 
-# Fetch the arvados/jobs Docker image
-# Manually install additional software into the container
-# Create a new custom image
-# Upload that image to Arvados for use by Crunch jobs
-# Share your image with others
+# "Create a custom image using a Dockerfile":#create
+# "Uploading an image to Arvados":#upload
+# "Sources of pre-built bioinformatics Docker images":#sources
 
 {% include 'tutorial_expectations_workstation' %}
 
 You also need ensure that "Docker is installed,":https://docs.docker.com/installation/ the Docker daemon is running, and you have permission to access Docker.  You can test this by running @docker version@.  If you receive a permission denied error, your user account may need to be added to the @docker@ group.  If you have root access, you can add yourself to the @docker@ group using @$ sudo addgroup $USER docker@ then log out and log back in again; otherwise consult your local sysadmin.
 
-h2. Fetch a starting image
+h2(#create). Create a custom image using a Dockerfile
 
-The easiest way to begin is to start from the "arvados/jobs" image which already has the Arvados SDK installed along with other configuration required for use with Crunch.
+This example shows how to create a Docker image and add the R package.
 
-Download the latest "arvados/jobs" image from the Docker registry:
+First, create new directory called @docker-example@, in that directory create a file called @Dockerfile@.
 
 <notextile>
-<pre><code>$ <span class="userinput">docker pull arvados/jobs:latest</span>
-Pulling repository arvados/jobs
-3132168f2acb: Download complete
-a42b7f2c59b6: Download complete
-e5afdf26a7ae: Download complete
-5cae48636278: Download complete
-7a4f91b70558: Download complete
-a04a275c1fd6: Download complete
-c433ff206a22: Download complete
-b2e539b45f96: Download complete
-073b2581c6be: Download complete
-593915af19dc: Download complete
-32260b35005e: Download complete
-6e5b860c1cde: Download complete
-95f0bfb43d4d: Download complete
-c7fd77eedb96: Download complete
-0d7685aafd00: Download complete
+<pre><code>$ <span class="userinput">mkdir docker-example-r-base</span>
+$ <span class="userinput">cd docker-example-r-base</span>
 </code></pre>
 </notextile>
 
-h2. Install new packages
-
-Next, enter the container using @docker run@, providing the arvados/jobs image and the program you want to run (in this case the bash shell).
-
 <notextile>
-<pre><code>$ <span class="userinput">docker run --interactive --tty --user root arvados/jobs /bin/bash</span>
-root@fbf1d0f529d5:/#
+<pre><code>FROM ubuntu:bionic
+RUN apt-get update && apt-get -yq --no-install-recommends install r-base-core
 </code></pre>
 </notextile>
 
-Next, update the package list using @apt-get update@.
+The "RUN" command is executed inside the container and can be any shell command line.  You are not limited to installing Debian packages.  You may compile programs or libraries from source and install them, edit systemwide configuration files, use other package managers such as @pip@ or @gem@, and perform any other customization necessary to run your program.
 
-<notextile>
-<pre><code>root@fbf1d0f529d5:/# apt-get update
-Get:2 http://apt.arvados.org stretch-dev InRelease [3260 B]
-Get:1 http://security-cdn.debian.org/debian-security stretch/updates InRelease [94.3 kB]
-Ign:3 http://cdn-fastly.deb.debian.org/debian stretch InRelease
-Get:4 http://cdn-fastly.deb.debian.org/debian stretch-updates InRelease [91.0 kB]
-Get:5 http://apt.arvados.org stretch-dev/main amd64 Packages [208 kB]
-Get:6 http://cdn-fastly.deb.debian.org/debian stretch Release [118 kB]
-Get:7 http://security-cdn.debian.org/debian-security stretch/updates/main amd64 Packages [499 kB]
-Get:8 http://cdn-fastly.deb.debian.org/debian stretch Release.gpg [2434 B]
-Get:9 http://cdn-fastly.deb.debian.org/debian stretch-updates/main amd64 Packages.diff/Index [10.6 kB]
-Get:10 http://cdn-fastly.deb.debian.org/debian stretch-updates/main amd64 Packages 2019-07-08-0821.07.pdiff [445 B]
-Get:10 http://cdn-fastly.deb.debian.org/debian stretch-updates/main amd64 Packages 2019-07-08-0821.07.pdiff [445 B]
-Fetched 1026 kB in 0s (1384 kB/s)
-Reading package lists... Done
-</code></pre>
-</notextile>
+You can also visit the "Docker tutorial":https://docs.docker.com/get-started/part2/ for more information and examples.
+
+You should add your Dockerfiles to the same source control repository as the Workflows that use them.
 
-In this example, we will install the "R" statistical language Debian package "r-base-core".  Use @apt-get install@:
+h3. Create a new image
+
+We're now ready to create a new Docker image.  Use @docker build@ to create a new image from the Dockerfile.
 
 <notextile>
-<pre><code>root@fbf1d0f529d5:/# <span class="userinput">apt-get install r-base-core</span>
-Reading package lists... Done
-Building dependency tree
-Reading state information... Done
-The following additional packages will be installed:
-[...]
-done.
+<pre><code>docker-example-r-base$ <span class="userinput">docker build -t docker-example-r-base .</span>
 </code></pre>
 </notextile>
 
+h3. Verify image
+
 Now we can verify that "R" is installed:
 
 <notextile>
-<pre><code>root@fbf1d0f529d5:/# <span class="userinput">R</span>
+<pre><code>$ <span class="userinput">docker run -ti docker-example-r-base</span>
+root@57ec8f8b2663:/# R
 
-R version 3.3.3 (2017-03-06) -- "Another Canoe"
-Copyright (C) 2017 The R Foundation for Statistical Computing
+R version 3.4.4 (2018-03-15) -- "Someone to Lean On"
+Copyright (C) 2018 The R Foundation for Statistical Computing
 Platform: x86_64-pc-linux-gnu (64-bit)
-
-R is free software and comes with ABSOLUTELY NO WARRANTY.
-You are welcome to redistribute it under certain conditions.
-Type 'license()' or 'licence()' for distribution details.
-
-R is a collaborative project with many contributors.
-Type 'contributors()' for more information and
-'citation()' on how to cite R or R packages in publications.
-
-Type 'demo()' for some demos, 'help()' for on-line help, or
-'help.start()' for an HTML browser interface to help.
-Type 'q()' to quit R.
-
->
 </code></pre>
 </notextile>
 
-Note that you are not limited to installing Debian packages.  You may compile programs or libraries from source and install them, edit systemwide configuration files, use other package managers such as @pip@ or @gem@, and perform any other customization necessary to run your program.
+h2(#upload). Upload your image
 
-h2. Create a new image
-
-We're now ready to create a new Docker image.  First, quit the container, then use @docker commit@ to create a new image from the stopped container.  The container id can be found in the default hostname of the container displayed in the prompt, in this case @fbf1d0f529d5@:
+Finally, we are ready to upload the new Docker image to Arvados.  Use @arv-keepdocker@ with the image repository name to upload the image.  Without arguments, @arv-keepdocker@ will print out the list of Docker images in Arvados that are available to you.
 
 <notextile>
-<pre><code>root@fbf1d0f529d5:/# <span class="userinput">exit</span>
-$ <span class="userinput">docker commit fbf1d0f529d5 arvados/jobs-with-r</span>
-sha256:2818853ff9f9af5d7f77979803baac9c4710790ad2b84c1a754b02728fdff205
-$ <span class="userinput">docker images</span>
-$ docker images |head
-REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
-arvados/jobs-with-r   latest              2818853ff9f9        9 seconds ago       703.1 MB
-arvados/jobs          latest              12b9f859d48c        4 days ago          362 MB
-</code></pre>
-</notextile>
-
-h2. Upload your image
+<pre><code>$ <span class="userinput">arv-keepdocker docker-example-r-base</span>
+2020-06-29 13:48:19 arvados.arv_put[769] INFO: Creating new cache file at /home/peter/.cache/arvados/arv-put/39ddb51ebf6c5fcb3d713b5969466967
+206M / 206M 100.0% 2020-06-29 13:48:21 arvados.arv_put[769] INFO:
 
-Finally, we are ready to upload the new Docker image to Arvados.  Use @arv-keepdocker@ with the image repository name to upload the image.  Without arguments, @arv-keepdocker@ will print out the list of Docker images in Arvados that are available to you.
+2020-06-29 13:48:21 arvados.arv_put[769] INFO: Collection saved as 'Docker image docker-example-r-base:latest sha256:edd10'
+zzzzz-4zz18-0tayximqcyb6uf8
 
-<notextile>
-<pre><code>$ <span class="userinput">arv-keepdocker arvados/jobs-with-r</span>
-703M / 703M 100.0%
-Collection saved as 'Docker image arvados/jobs-with-r:latest 2818853ff9f9'
-qr1hi-4zz18-abcdefghijklmno
-$ <span class="userinput">arv-keepdocker</span>
+$ <span class="userinput">arv-keepdocker images</span>
 REPOSITORY                      TAG         IMAGE ID      COLLECTION                     CREATED
-arvados/jobs-with-r             latest      2818853ff9f9  qr1hi-4zz18-abcdefghijklmno    Tue Jan 17 20:35:53 2017
+docker-example-r-base           latest      sha256:edd10  zzzzz-4zz18-0tayximqcyb6uf8    Mon Jun 29 17:46:16 2020
 </code></pre>
 </notextile>
 
@@ -156,14 +91,24 @@ You are now able to specify the runtime environment for your program using @Dock
 <pre>
 hints:
   DockerRequirement:
-    dockerPull: arvados/jobs-with-r
+    dockerPull: docker-example-r-base
 </pre>
 
-h2. Share Docker images
+h3. Uploading Docker images to a shared project
 
-Docker images are subject to normal Arvados permissions.  If wish to share your Docker image with others (or wish to share a pipeline template that uses your Docker image) you will need to use @arv-keepdocker@ with the @--project-uuid@ option to upload the image to a shared project.
+Docker images are subject to normal Arvados permissions.  If wish to share your Docker image with others you should use @arv-keepdocker@ with the @--project-uuid@ option to add the image to a shared project and ensure that metadata is set correctly.
 
 <notextile>
-<pre><code>$ <span class="userinput">arv-keepdocker arvados/jobs-with-r --project-uuid qr1hi-j7d0g-xxxxxxxxxxxxxxx</span>
+<pre><code>$ <span class="userinput">arv-keepdocker docker-example-r-base --project-uuid zzzzz-j7d0g-xxxxxxxxxxxxxxx</span>
 </code></pre>
 </notextile>
+
+h2(#sources). Sources of pre-built images
+
+In addition to creating your own contianers, there are a number of resources where you can find bioinformatics tools already wrapped in container images:
+
+"BioContainers":https://biocontainers.pro/
+
+"Dockstore":https://dockstore.org/
+
+"Docker Hub":https://hub.docker.com/
diff --git a/doc/user/topics/keep.html.textile.liquid b/doc/user/topics/keep.html.textile.liquid
deleted file mode 100644 (file)
index 68b6a87..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
----
-layout: default
-navsection: userguide
-title: "How Keep works"
-...
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-The Arvados distributed file system is called *Keep*.  Keep is a content-addressable file system.  This means that files are managed using special unique identifiers derived from the _contents_ of the file (specifically, the MD5 hash), rather than human-assigned file names.  This has a number of advantages:
-* Files can be stored and replicated across a cluster of servers without requiring a central name server.
-* Both the server and client systematically validate data integrity because the checksum is built into the identifier.
-* Data duplication is minimized—two files with the same contents will have in the same identifier, and will not be stored twice.
-* It avoids data race conditions, since an identifier always points to the same data.
-
-In Keep, information is stored in *data blocks*.  Data blocks are normally between 1 byte and 64 megabytes in size.  If a file exceeds the maximum size of a single data block, the file will be split across multiple data blocks until the entire file can be stored.  These data blocks may be stored and replicated across multiple disks, servers, or clusters.  Each data block has its own identifier for the contents of that specific data block.
-
-In order to reassemble the file, Keep stores a *collection* data block which lists in sequence the data blocks that make up the original file.  A collection data block may store the information for multiple files, including a directory structure.
-
-In this example we will use @c1bad4b39ca5a924e481008009d94e32+210@, which we added to Keep in "how to upload data":{{ site.baseurl }}/user/tutorials/tutorial-keep.html.  First let us examine the contents of this collection using @arv-get@:
-
-<notextile>
-<pre><code>~$ <span class="userinput">arv-get c1bad4b39ca5a924e481008009d94e32+210</span>
-. 204e43b8a1185621ca55a94839582e6f+67108864 b9677abbac956bd3e86b1deb28dfac03+67108864 fc15aff2a762b13f521baf042140acec+67108864 323d2a3ce20370c4ca1d3462a344f8fd+25885655 0:227212247:var-GS000016015-ASM.tsv.bz2
-</code></pre>
-</notextile>
-
-The command @arv-get@ fetches the contents of the collection @c1bad4b39ca5a924e481008009d94e32+210@.  In this example, this collection includes a single file @var-GS000016015-ASM.tsv.bz2@ which is 227212247 bytes long, and is stored using four sequential data blocks, @204e43b8a1185621ca55a94839582e6f+67108864@, @b9677abbac956bd3e86b1deb28dfac03+67108864@, @fc15aff2a762b13f521baf042140acec+67108864@, and @323d2a3ce20370c4ca1d3462a344f8fd+25885655@.
-
-Let's use @arv-get@ to download the first data block:
-
-notextile. <pre><code>~$ <span class="userinput">cd /scratch/<b>you</b></span>
-/scratch/<b>you</b>$ <span class="userinput">arv-get 204e43b8a1185621ca55a94839582e6f+67108864 &gt; block1</span></code></pre>
-
-{% include 'notebox_begin' %}
-
-When you run this command, you may get this API warning:
-
-notextile. <pre><code>WARNING:root:API lookup failed for collection 204e43b8a1185621ca55a94839582e6f+67108864 (&lt;class 'apiclient.errors.HttpError'&gt;: &lt;HttpError 404 when requesting https://qr1hi.arvadosapi.com/arvados/v1/collections/204e43b8a1185621ca55a94839582e6f%2B67108864?alt=json returned "Not Found"&gt;)</code></pre>
-
-This happens because @arv-get@ tries to find a collection with this identifier.  When that fails, it emits this warning, then looks for a datablock instead, which succeeds.
-
-{% include 'notebox_end' %}
-
-Let's look at the size and compute the MD5 hash of @block1@:
-
-<notextile>
-<pre><code>/scratch/<b>you</b>$ <span class="userinput">ls -l block1</span>
--rw-r--r-- 1 you group 67108864 Dec  9 20:14 block1
-/scratch/<b>you</b>$ <span class="userinput">md5sum block1</span>
-204e43b8a1185621ca55a94839582e6f  block1
-</code></pre>
-</notextile>
-
-Notice that the block identifer <code>204e43b8a1185621ca55a94839582e6f+67108864</code> consists of:
-* the MD5 hash of @block1@, @204e43b8a1185621ca55a94839582e6f@, plus
-* the size of @block1@, @67108864@.
diff --git a/doc/user/topics/tutorial-gatk-variantfiltration.html.textile.liquid b/doc/user/topics/tutorial-gatk-variantfiltration.html.textile.liquid
deleted file mode 100644 (file)
index 544ccbd..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
----
-layout: default
-navsection: userguide
-title: "Using GATK with Arvados"
-...
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-This tutorial demonstrates how to use the Genome Analysis Toolkit (GATK) with Arvados. In this example we will install GATK and then create a VariantFiltration job to assign pass/fail scores to variants in a VCF file.
-
-{% include 'tutorial_expectations' %}
-
-h2. Installing GATK
-
-Download the GATK binary tarball[1] -- e.g., @GenomeAnalysisTK-2.6-4.tar.bz2@ -- and "copy it to your Arvados VM":{{site.baseurl}}/user/tutorials/tutorial-keep.html.
-
-<notextile>
-<pre><code>~$ <span class="userinput">arv-put GenomeAnalysisTK-2.6-4.tar.bz2</span>
-c905c8d8443a9c44274d98b7c6cfaa32+94
-</code></pre>
-</notextile>
-
-Next, you need the GATK Resource Bundle[2].  This may already be available in Arvados.  If not, you will need to download the files listed below and put them into Keep.
-
-<notextile>
-<pre><code>~$ <span class="userinput">arv keep ls -s d237a90bae3870b3b033aea1e99de4a9+10820</span>
-  50342 1000G_omni2.5.b37.vcf.gz
-      1 1000G_omni2.5.b37.vcf.gz.md5
-    464 1000G_omni2.5.b37.vcf.idx.gz
-      1 1000G_omni2.5.b37.vcf.idx.gz.md5
-  43981 1000G_phase1.indels.b37.vcf.gz
-      1 1000G_phase1.indels.b37.vcf.gz.md5
-    326 1000G_phase1.indels.b37.vcf.idx.gz
-      1 1000G_phase1.indels.b37.vcf.idx.gz.md5
- 537210 CEUTrio.HiSeq.WGS.b37.bestPractices.phased.b37.vcf.gz
-      1 CEUTrio.HiSeq.WGS.b37.bestPractices.phased.b37.vcf.gz.md5
-   3473 CEUTrio.HiSeq.WGS.b37.bestPractices.phased.b37.vcf.idx.gz
-      1 CEUTrio.HiSeq.WGS.b37.bestPractices.phased.b37.vcf.idx.gz.md5
-  19403 Mills_and_1000G_gold_standard.indels.b37.vcf.gz
-      1 Mills_and_1000G_gold_standard.indels.b37.vcf.gz.md5
-    536 Mills_and_1000G_gold_standard.indels.b37.vcf.idx.gz
-      1 Mills_and_1000G_gold_standard.indels.b37.vcf.idx.gz.md5
-  29291 NA12878.HiSeq.WGS.bwa.cleaned.raw.subset.b37.sites.vcf.gz
-      1 NA12878.HiSeq.WGS.bwa.cleaned.raw.subset.b37.sites.vcf.gz.md5
-    565 NA12878.HiSeq.WGS.bwa.cleaned.raw.subset.b37.sites.vcf.idx.gz
-      1 NA12878.HiSeq.WGS.bwa.cleaned.raw.subset.b37.sites.vcf.idx.gz.md5
-  37930 NA12878.HiSeq.WGS.bwa.cleaned.raw.subset.b37.vcf.gz
-      1 NA12878.HiSeq.WGS.bwa.cleaned.raw.subset.b37.vcf.gz.md5
-    592 NA12878.HiSeq.WGS.bwa.cleaned.raw.subset.b37.vcf.idx.gz
-      1 NA12878.HiSeq.WGS.bwa.cleaned.raw.subset.b37.vcf.idx.gz.md5
-5898484 NA12878.HiSeq.WGS.bwa.cleaned.recal.b37.20.bam
-    112 NA12878.HiSeq.WGS.bwa.cleaned.recal.b37.20.bam.bai.gz
-      1 NA12878.HiSeq.WGS.bwa.cleaned.recal.b37.20.bam.bai.gz.md5
-      1 NA12878.HiSeq.WGS.bwa.cleaned.recal.b37.20.bam.md5
-   3837 NA12878.HiSeq.WGS.bwa.cleaned.recal.b37.20.vcf.gz
-      1 NA12878.HiSeq.WGS.bwa.cleaned.recal.b37.20.vcf.gz.md5
-     65 NA12878.HiSeq.WGS.bwa.cleaned.recal.b37.20.vcf.idx.gz
-      1 NA12878.HiSeq.WGS.bwa.cleaned.recal.b37.20.vcf.idx.gz.md5
- 275757 dbsnp_137.b37.excluding_sites_after_129.vcf.gz
-      1 dbsnp_137.b37.excluding_sites_after_129.vcf.gz.md5
-   3735 dbsnp_137.b37.excluding_sites_after_129.vcf.idx.gz
-      1 dbsnp_137.b37.excluding_sites_after_129.vcf.idx.gz.md5
- 998153 dbsnp_137.b37.vcf.gz
-      1 dbsnp_137.b37.vcf.gz.md5
-   3890 dbsnp_137.b37.vcf.idx.gz
-      1 dbsnp_137.b37.vcf.idx.gz.md5
-  58418 hapmap_3.3.b37.vcf.gz
-      1 hapmap_3.3.b37.vcf.gz.md5
-    999 hapmap_3.3.b37.vcf.idx.gz
-      1 hapmap_3.3.b37.vcf.idx.gz.md5
-      3 human_g1k_v37.dict.gz
-      1 human_g1k_v37.dict.gz.md5
-      2 human_g1k_v37.fasta.fai.gz
-      1 human_g1k_v37.fasta.fai.gz.md5
- 849537 human_g1k_v37.fasta.gz
-      1 human_g1k_v37.fasta.gz.md5
-      1 human_g1k_v37.stats.gz
-      1 human_g1k_v37.stats.gz.md5
-      3 human_g1k_v37_decoy.dict.gz
-      1 human_g1k_v37_decoy.dict.gz.md5
-      2 human_g1k_v37_decoy.fasta.fai.gz
-      1 human_g1k_v37_decoy.fasta.fai.gz.md5
- 858592 human_g1k_v37_decoy.fasta.gz
-      1 human_g1k_v37_decoy.fasta.gz.md5
-      1 human_g1k_v37_decoy.stats.gz
-      1 human_g1k_v37_decoy.stats.gz.md5
-</code></pre>
-</notextile>
-
-h2. Submit a GATK job
-
-The Arvados distribution includes an example crunch script ("crunch_scripts/GATK2-VariantFiltration":https://dev.arvados.org/projects/arvados/repository/revisions/master/entry/crunch_scripts/GATK2-VariantFiltration) that runs the GATK VariantFiltration tool with some default settings.
-
-<notextile>
-<pre><code>~$ <span class="userinput">src_version=76588bfc57f33ea1b36b82ca7187f465b73b4ca4</span>
-~$ <span class="userinput">vcf_input=5ee633fe2569d2a42dd81b07490d5d13+82</span>
-~$ <span class="userinput">gatk_binary=c905c8d8443a9c44274d98b7c6cfaa32+94</span>
-~$ <span class="userinput">gatk_bundle=d237a90bae3870b3b033aea1e99de4a9+10820</span>
-~$ <span class="userinput">cat &gt;the_job &lt;&lt;EOF
-{
- "script":"GATK2-VariantFiltration",
- "repository":"arvados",
- "script_version":"$src_version",
- "script_parameters":
- {
-  "input":"$vcf_input",
-  "gatk_binary_tarball":"$gatk_binary",
-  "gatk_bundle":"$gatk_bundle"
- }
-}
-EOF</span>
-</code></pre>
-</notextile>
-
-* @"input"@ is collection containing the source VCF data. Here we are using an exome report from PGP participant hu34D5B9.
-* @"gatk_binary_tarball"@ is a Keep collection containing the GATK 2 binary distribution[1] tar file.
-* @"gatk_bundle"@ is a Keep collection containing the GATK resource bundle[2].
-
-Now start a job:
-
-<notextile>
-<pre><code>~$ <span class="userinput">arv job create --job "$(cat the_job)"</span>
-{
- "href":"https://qr1hi.arvadosapi.com/arvados/v1/jobs/qr1hi-8i9sb-n9k7qyp7bs5b9d4",
- "kind":"arvados#job",
- "etag":"9j99n1feoxw3az448f8ises12",
- "uuid":"qr1hi-8i9sb-n9k7qyp7bs5b9d4",
- "owner_uuid":"qr1hi-tpzed-9zdpkpni2yddge6",
- "created_at":"2013-12-17T19:02:15Z",
- "modified_by_client_uuid":"qr1hi-ozdt8-obw7foaks3qjyej",
- "modified_by_user_uuid":"qr1hi-tpzed-9zdpkpni2yddge6",
- "modified_at":"2013-12-17T19:02:15Z",
- "updated_at":"2013-12-17T19:02:15Z",
- "submit_id":null,
- "priority":null,
- "script":"GATK2-VariantFiltration",
- "script_parameters":{
-  "input":"5ee633fe2569d2a42dd81b07490d5d13+82",
-  "gatk_binary_tarball":"c905c8d8443a9c44274d98b7c6cfaa32+94",
-  "gatk_bundle":"d237a90bae3870b3b033aea1e99de4a9+10820"
- },
- "script_version":"76588bfc57f33ea1b36b82ca7187f465b73b4ca4",
- "cancelled_at":null,
- "cancelled_by_client_uuid":null,
- "cancelled_by_user_uuid":null,
- "started_at":null,
- "finished_at":null,
- "output":null,
- "success":null,
- "running":null,
- "is_locked_by_uuid":null,
- "log":null,
- "runtime_constraints":{},
- "tasks_summary":{}
-}
-</code></pre>
-</notextile>
-
-Once the job completes, the output can be found in hu34D5B9-exome-filtered.vcf:
-
-<notextile><pre><code>~$ <span class="userinput">arv keep ls bedd6ff56b3ae9f90d873b1fcb72f9a3+91</span>
-hu34D5B9-exome-filtered.vcf
-</code></pre>
-</notextile>
-
-h2. Notes
-
-fn1. "Download the GATK tools":http://www.broadinstitute.org/gatk/download
-
-fn2. "Information about the GATK resource bundle":http://gatkforums.broadinstitute.org/discussion/1213/whats-in-the-resource-bundle-and-how-can-i-get-it and "direct download link":ftp://gsapubftp-anonymous@ftp.broadinstitute.org/bundle/2.5/b37/ (if prompted, submit an empty password)
diff --git a/doc/user/topics/tutorial-job1.html.textile.liquid b/doc/user/topics/tutorial-job1.html.textile.liquid
deleted file mode 100644 (file)
index f7a2060..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
----
-layout: default
-navsection: userguide
-title: "Running a Crunch job on the command line"
-...
-{% comment %}
-Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: CC-BY-SA-3.0
-{% endcomment %}
-
-This tutorial introduces how to run individual Crunch jobs using the @arv@ command line tool.
-
-{% include 'tutorial_expectations' %}
-
-You will create a job to run the "hash" Crunch script.  The "hash" script computes the MD5 hash of each file in a collection.
-
-h2. Jobs
-
-Crunch pipelines consist of one or more jobs.  A "job" is a single run of a specific version of a Crunch script with a specific input.  You can also run jobs individually.
-
-A request to run a Crunch job are is described using a JSON object.  For example:
-
-<notextile>
-<pre><code>~$ <span class="userinput">cat &gt;~/the_job &lt;&lt;EOF
-{
- "script": "hash",
- "repository": "arvados",
- "script_version": "master",
- "script_parameters": {
-  "input": "c1bad4b39ca5a924e481008009d94e32+210"
- },
- "no_reuse": "true"
-}
-EOF
-</code></pre>
-</notextile>
-
-* @cat@ is a standard Unix utility that writes a sequence of input to standard output.
-* @<<EOF@ tells the shell to direct the following lines into the standard input for @cat@ up until it sees the line @EOF@.
-* @>~/the_job@ redirects standard output to a file called @~/the_job@.
-* @"repository"@ is the name of a Git repository to search for the script version.  You can access a list of available git repositories on the Arvados Workbench under "*Code repositories*":{{site.arvados_workbench_host}}/repositories.
-* @"script_version"@ specifies the version of the script that you wish to run.  This can be in the form of an explicit Git revision hash, a tag, or a branch.  Arvados logs the script version that was used in the run, enabling you to go back and re-run any past job with the guarantee that the exact same code will be used as was used in the previous run.
-* @"script"@ specifies the name of the script to run.  The script must be given relative to the @crunch_scripts/@ subdirectory of the Git repository.
-* @"script_parameters"@ are provided to the script.  In this case, the input is the PGP data Collection that we "put in Keep earlier":{{site.baseurl}}/user/tutorials/tutorial-keep.html.
-* Setting the @"no_reuse"@ flag tells Crunch not to reuse work from past jobs.  This helps ensure that you can watch a new Job process for the rest of this tutorial, without reusing output from a past run that you made, or somebody else marked as public.  (If you want to experiment, after the first run below finishes, feel free to edit this job to remove the @"no_reuse"@ line and resubmit it.  See what happens!)
-
-Use @arv job create@ to actually submit the job.  It should print out a JSON object which describes the newly created job:
-
-<notextile>
-<pre><code>~$ <span class="userinput">arv job create --job "$(cat ~/the_job)"</span>
-{
- "href":"https://qr1hi.arvadosapi.com/arvados/v1/jobs/qr1hi-8i9sb-1pm1t02dezhupss",
- "kind":"arvados#job",
- "etag":"ax3cn7w9whq2hdh983yxvq09p",
- "uuid":"qr1hi-8i9sb-1pm1t02dezhupss",
- "owner_uuid":"qr1hi-tpzed-9zdpkpni2yddge6",
- "created_at":"2013-12-16T20:44:32Z",
- "modified_by_client_uuid":"qr1hi-ozdt8-obw7foaks3qjyej",
- "modified_by_user_uuid":"qr1hi-tpzed-9zdpkpni2yddge6",
- "modified_at":"2013-12-16T20:44:32Z",
- "updated_at":"2013-12-16T20:44:33Z",
- "submit_id":null,
- "priority":null,
- "script":"hash",
- "script_parameters":{
-  "input":"c1bad4b39ca5a924e481008009d94e32+210"
- },
- "script_version":"d9cd657b733d578ac0d2167dd75967aa4f22e0ac",
- "cancelled_at":null,
- "cancelled_by_client_uuid":null,
- "cancelled_by_user_uuid":null,
- "started_at":null,
- "finished_at":null,
- "output":null,
- "success":null,
- "running":null,
- "is_locked_by_uuid":null,
- "log":null,
- "runtime_constraints":{},
- "tasks_summary":{}
-}
-</code></pre>
-</notextile>
-
-The job is now queued and will start running as soon as it reaches the front of the queue.  Fields to pay attention to include:
-
- * @"uuid"@ is the unique identifier for this specific job.
- * @"script_version"@ is the actual revision of the script used.  This is useful if the version was described using the "repository:branch" format.
-
-h2. Monitor job progress
-
-Go to "*Recent jobs*":{{site.arvados_workbench_host}}/jobs in Workbench.  Your job should be near the top of the table.  This table refreshes automatically.  When the job has completed successfully, it will show <span class="label label-success">finished</span> in the *Status* column.
-
-h2. Inspect the job output
-
-On the "Workbench Dashboard":{{site.arvados_workbench_host}}, look for the *Output* column of the *Recent jobs* table.  Click on the link under *Output* for your job to go to the files page with the job output.  The files page lists all the files that were output by the job.  Click on the link under the *file* column to view a file, or click on the download button <span class="glyphicon glyphicon-download-alt"></span> to download the output file.
-
-On the command line, you can use @arv job get@ to access a JSON object describing the output:
-
-<notextile>
-<pre><code>~$ <span class="userinput">arv job get --uuid qr1hi-8i9sb-xxxxxxxxxxxxxxx</span>
-{
- "href":"https://qr1hi.arvadosapi.com/arvados/v1/jobs/qr1hi-8i9sb-1pm1t02dezhupss",
- "kind":"arvados#job",
- "etag":"1bk98tdj0qipjy0rvrj03ta5r",
- "uuid":"qr1hi-8i9sb-1pm1t02dezhupss",
- "owner_uuid":"qr1hi-tpzed-9zdpkpni2yddge6",
- "created_at":"2013-12-16T20:44:32Z",
- "modified_by_client_uuid":null,
- "modified_by_user_uuid":"qr1hi-tpzed-9zdpkpni2yddge6",
- "modified_at":"2013-12-16T20:44:55Z",
- "updated_at":"2013-12-16T20:44:55Z",
- "submit_id":null,
- "priority":null,
- "script":"hash",
- "script_parameters":{
-  "input":"c1bad4b39ca5a924e481008009d94e32+210"
- },
- "script_version":"d9cd657b733d578ac0d2167dd75967aa4f22e0ac",
- "cancelled_at":null,
- "cancelled_by_client_uuid":null,
- "cancelled_by_user_uuid":null,
- "started_at":"2013-12-16T20:44:36Z",
- "finished_at":"2013-12-16T20:44:53Z",
- "output":"dd755dbc8d49a67f4fe7dc843e4f10a6+54",
- "success":true,
- "running":false,
- "is_locked_by_uuid":"qr1hi-tpzed-9zdpkpni2yddge6",
- "log":"2afdc6c8b67372ffd22d8ce89d35411f+91",
- "runtime_constraints":{},
- "tasks_summary":{
-  "done":2,
-  "running":0,
-  "failed":0,
-  "todo":0
- }
-}
-</code></pre>
-</notextile>
-
-* @"output"@ is the unique identifier for this specific job's output.  This is a Keep collection.  Because the output of Arvados jobs should be deterministic, the known expected output is <code>dd755dbc8d49a67f4fe7dc843e4f10a6+54</code>.
-
-Now you can list the files in the collection:
-
-<notextile>
-<pre><code>~$ <span class="userinput">arv keep ls dd755dbc8d49a67f4fe7dc843e4f10a6+54</span>
-./md5sum.txt
-</code></pre>
-</notextile>
-
-This collection consists of the @md5sum.txt@ file.  Use @arv-get@ to show the contents of the @md5sum.txt@ file:
-
-<notextile>
-<pre><code>~$ <span class="userinput">arv-get dd755dbc8d49a67f4fe7dc843e4f10a6+54/md5sum.txt</span>
-44b8ae3fde7a8a88d2f7ebd237625b4f ./var-GS000016015-ASM.tsv.bz2
-</code></pre>
-</notextile>
-
-This MD5 hash matches the MD5 hash which we "computed earlier":{{site.baseurl}}/user/tutorials/tutorial-keep.html.
-
-h2. The job log
-
-When the job completes, you can access the job log.  On the Workbench, visit "*Recent jobs*":{{site.arvados_workbench_host}}/jobs %(rarr)&rarr;% your job's UUID under the *uuid* column %(rarr)&rarr;% the collection link on the *log* row.
-
-On the command line, the Keep identifier listed in the @"log"@ field from @arv job get@ specifies a collection.  You can list the files in the collection:
-
-<notextile>
-<pre><code>~$ <span class="userinput">arv keep ls xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx+91</span>
-./qr1hi-8i9sb-xxxxxxxxxxxxxxx.log.txt
-</code></pre>
-</notextile>
-
-The log collection consists of one log file named with the job's UUID.  You can access it using @arv-get@:
-
-<notextile>
-<pre><code>~$ <span class="userinput">arv-get xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx+91/qr1hi-8i9sb-xxxxxxxxxxxxxxx.log.txt</span>
-2013-12-16_20:44:35 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  check slurm allocation
-2013-12-16_20:44:35 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  node compute13 - 8 slots
-2013-12-16_20:44:36 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  start
-2013-12-16_20:44:36 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  Install revision d9cd657b733d578ac0d2167dd75967aa4f22e0ac
-2013-12-16_20:44:37 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  Clean-work-dir exited 0
-2013-12-16_20:44:37 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  Install exited 0
-2013-12-16_20:44:37 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  script hash
-2013-12-16_20:44:37 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  script_version d9cd657b733d578ac0d2167dd75967aa4f22e0ac
-2013-12-16_20:44:37 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  script_parameters {"input":"c1bad4b39ca5a924e481008009d94e32+210"}
-2013-12-16_20:44:37 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  runtime_constraints {"max_tasks_per_node":0}
-2013-12-16_20:44:37 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  start level 0
-2013-12-16_20:44:37 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  status: 0 done, 0 running, 1 todo
-2013-12-16_20:44:38 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575 0 job_task qr1hi-ot0gb-23c1k3kwrf8da62
-2013-12-16_20:44:38 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575 0 child 7681 started on compute13.1
-2013-12-16_20:44:38 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  status: 0 done, 1 running, 0 todo
-2013-12-16_20:44:39 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575 0 child 7681 on compute13.1 exit 0 signal 0 success=true
-2013-12-16_20:44:39 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575 0 success in 1 seconds
-2013-12-16_20:44:39 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575 0 output
-2013-12-16_20:44:39 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  wait for last 0 children to finish
-2013-12-16_20:44:39 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  status: 1 done, 0 running, 1 todo
-2013-12-16_20:44:39 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  start level 1
-2013-12-16_20:44:39 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  status: 1 done, 0 running, 1 todo
-2013-12-16_20:44:39 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575 1 job_task qr1hi-ot0gb-iwr0o3unqothg28
-2013-12-16_20:44:39 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575 1 child 7716 started on compute13.1
-2013-12-16_20:44:39 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  status: 1 done, 1 running, 0 todo
-2013-12-16_20:44:52 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575 1 child 7716 on compute13.1 exit 0 signal 0 success=true
-2013-12-16_20:44:52 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575 1 success in 13 seconds
-2013-12-16_20:44:52 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575 1 output dd755dbc8d49a67f4fe7dc843e4f10a6+54
-2013-12-16_20:44:52 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  wait for last 0 children to finish
-2013-12-16_20:44:52 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  status: 2 done, 0 running, 0 todo
-2013-12-16_20:44:52 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  release job allocation
-2013-12-16_20:44:52 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  Freeze not implemented
-2013-12-16_20:44:52 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  collate
-2013-12-16_20:44:53 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  output dd755dbc8d49a67f4fe7dc843e4f10a6+54+K@qr1hi
-2013-12-16_20:44:53 qr1hi-8i9sb-xxxxxxxxxxxxxxx 7575  finish
-</code></pre>
-</notextile>
index 9d8e768a787181828ecf8df82b8c4d9474d28197..e28b9612386d13aa49ff61a0ab8e8aca83dcf6a9 100644 (file)
@@ -9,7 +9,7 @@ Copyright (C) The Arvados Authors. All rights reserved.
 SPDX-License-Identifier: CC-BY-SA-3.0
 {% endcomment %}
 
-Arvados repositories are managed through the Git revision control system. You can use these repositories to store your crunch scripts and run them in the arvados cluster.
+Arvados supports managing git repositories. You can access these repositories using your Arvados credentials and share them with other Arvados users.
 
 {% include 'tutorial_expectations' %}
 
index 2e255219d2a5bc39263aef6db4860f3e8751aecf..ad719a66e4e80ceacbbe8bcb8d925971e32ecbb3 100644 (file)
@@ -9,20 +9,13 @@ Copyright (C) The Arvados Authors. All rights reserved.
 SPDX-License-Identifier: CC-BY-SA-3.0
 {% endcomment %}
 
-This tutorial describes how to work with a new Arvados git repository. Working with an Arvados git repository is analogous to working with other public git repositories. It will show you how to upload custom scripts to a remote Arvados repository, so you can use it in Arvados pipelines.
+This tutorial describes how to work with an Arvados-managed git repository. Working with an Arvados git repository is very similar to working with other public git repositories.
 
 {% include 'tutorial_expectations' %}
 
 {% include 'tutorial_git_repo_expectations' %}
 
-{% include 'notebox_begin' %}
-For more information about using Git, try
-<notextile>
-<pre><code>$ <span class="userinput">man gittutorial</span></code></pre>
-</notextile> or *"search Google for Git tutorials":http://google.com/#q=git+tutorial*.
-{% include 'notebox_end' %}
-
-h2. Cloning an Arvados repository
+h2. Cloning a git repository
 
 Before you start using Git, you should do some basic configuration (you only need to do this the first time):
 
@@ -65,33 +58,22 @@ Create a git branch named *tutorial_branch* in the *tutorial* Arvados git reposi
 
 h2. Adding scripts to an Arvados repository
 
-Arvados crunch scripts need to be added in a *crunch_scripts* subdirectory in the repository. If this subdirectory does not exist, first create it in the local repository and change to that directory:
-
-<notextile>
-<pre><code>~/tutorial$ <span class="userinput">mkdir crunch_scripts</span>
-~/tutorial$ <span class="userinput">cd crunch_scripts</span></code></pre>
-</notextile>
-
-Next, using @nano@ or your favorite Unix text editor, create a new file called @hash.py@ in the @crunch_scripts@ directory.
-
-notextile. <pre>~/tutorial/crunch_scripts$ <code class="userinput">nano hash.py</code></pre>
-
-Add the following code to compute the MD5 hash of each file in a collection
+A git repository is a good place to store the CWL workflows that you run on Arvados.
 
-<notextile> {% code 'tutorial_hash_script_py' as python %} </notextile>
+First, create a simple CWL CommandLineTool:
 
-Make the file executable:
+notextile. <pre>~/tutorials$ <code class="userinput">nano hello.cwl</code></pre>
 
-notextile. <pre><code>~/tutorial/crunch_scripts$ <span class="userinput">chmod +x hash.py</span></code></pre>
+<notextile> {% code 'tutorial_hello_cwl' as yaml %} </notextile>
 
 Next, add the file to the git repository.  This tells @git@ that the file should be included on the next commit.
 
-notextile. <pre><code>~/tutorial/crunch_scripts$ <span class="userinput">git add hash.py</span></code></pre>
+notextile. <pre><code>~/tutorial$ <span class="userinput">git add hello.cwl</span></code></pre>
 
 Next, commit your changes.  All staged changes are recorded into the local git repository:
 
 <notextile>
-<pre><code>~/tutorial/crunch_scripts$ <span class="userinput">git commit -m "my first script"</span>
+<pre><code>~/tutorial$ <span class="userinput">git commit -m "my first script"</span>
 </code></pre>
 </notextile>
 
@@ -102,4 +84,4 @@ Finally, upload your changes to the remote repository:
 </code></pre>
 </notextile>
 
-Although this tutorial shows how to add a python script to Arvados, the same steps can be used to add any of your custom bash, R, or python scripts to an Arvados repository.
+The same steps can be used to add any of your custom bash, R, or python scripts to an Arvados repository.
index 2375e8b3d5bb53f243d0877c6bd2bb8a7280467a..9ddec04f5e7459194f7758e70d14c7d4a751d864 100644 (file)
@@ -1,7 +1,7 @@
 ---
 layout: default
 navsection: userguide
-title: "Keep collection lifecycle"
+title: "Trashing and untrashing data"
 ...
 {% comment %}
 Copyright (C) The Arvados Authors. All rights reserved.
@@ -9,48 +9,40 @@ Copyright (C) The Arvados Authors. All rights reserved.
 SPDX-License-Identifier: CC-BY-SA-3.0
 {% endcomment %}
 
-During it's lifetime, a keep collection can be in various states. These states are *persisted*, *expiring*, *trashed*  and *permanently deleted*.
+Collections have a sophisticated data lifecycle, which is documented in the architecture guide at "Collection lifecycle":{{ site.baseurl }}/architecture/keep-data-lifecycle.html#collection_lifecycle.
 
-A collection is *expiring* when it has a *trash_at* time in the future. An expiring collection can be accessed as normal, but is scheduled to be trashed automatically at the *trash_at* time.
+Arvados supports trashing (deletion) of collections. For a period of time after a collection is trashed, it can be "untrashed". After that period, the collection is permanently deleted, though there may still be ways to recover the data, see "Recovering data":{{ site.baseurl }}/admin/keep-recovering-data.html in the admin guide for more details.
 
-A collection is *trashed* when it has a *trash_at* time in the past. The *is_trashed* attribute will also be "true". The delete operation immediately puts the collection in the trash by setting the *trash_at* time to "now". Once trashed, the collection is no longer readable through normal data access APIs. The collection will have *delete_at* set to some time in the future. The trashed collection is recoverable until the delete_at time passes, at which point the collection is permanently deleted.
-
-# "*Collection lifecycle attributes*":#collection_attributes
-# "*Deleting / trashing collections*":#delete-collection
+# "*Trashing (deleting) collections*":#delete-collection
 # "*Recovering trashed collections*":#trash-recovery
 
 {% include 'tutorial_expectations' %}
 
-h2(#collection_attributes). Collection lifecycle attributes
-
-As listed above the attributes that are used to manage a collection lifecycle are it's *is_trashed*, *trash_at*, and *delete_at*. The table below lists the values of these attributes and how they influence the state of a collection and it's accessibility.
+h2(#delete-collection). Trashing (deleting) collections
 
-table(table table-bordered table-condensed).
-|_. collection state|_. is_trashed|_. trash_at|_. delete_at|_. get|_. list|_. list?include_trash=true|_. can be modified|
-|persisted collection|false |null |null |yes |yes |yes |yes |
-|expiring collection|false |future |future |yes  |yes |yes |yes |
-|trashed collection|true |past |future |no |no |yes |only is_trashed, trash_at and delete_at attribtues|
-|deleted collection|true|past |past |no |no |no |no |
+A collection can be trashed using workbench or the arv command line tool.
 
-h2(#delete-collection). Deleting / trashing collections
+h3. Trashing a collection using workbench
 
-A collection can be deleted using either the arv command line tool or the workbench.
+To trash a collection using workbench, go to the Data collections tab in the project, and use the <i class="fa fa-fw fa-trash-o"></i> trash icon for this collection row.
 
 h3. Trashing a collection using arv command line tool
 
 <pre>
-arv collection delete --uuid=qr1hi-4zz18-xxxxxxxxxxxxxxx
+arv collection delete --uuid=zzzzz-4zz18-xxxxxxxxxxxxxxx
 </pre>
 
-h3. Trashing a collection using workbench
+h2(#trash-recovery). Recovering trashed collections
 
-To trash a collection using workbench, go to the Data collections tab in the project, and use the trash icon for this collection row.
+A collection can be untrashed / recovered using workbench or the arv command line tool.
 
-h2(#trash-recovery). Recovering trashed collections
+h3. Untrashing a collection using workbench
 
-A collection can be un-trashed / recovered using either the arv command line tool or the workbench.
+To untrash a collection using workbench, go to trash page on workbench by clicking on the "Trash" icon in the top navigation in workbench and use the recycle icon or selection dropdown option.
 
-h3. Un-trashing a collection using arv command line tool
+!{display: block;margin-left: 25px;margin-right: auto;border:1px solid lightgray;}{{ site.baseurl }}/images/trash-button-topnav.png!
+
+h3. Untrashing a collection using arv command line tool
 
 You can list the trashed collections using the list command.
 
@@ -61,11 +53,7 @@ arv collection list --include-trash=true --filters '[["is_trashed", "=", "true"]
 You can then untrash a particular collection using arv using it's uuid.
 
 <pre>
-arv collection untrash --uuid=qr1hi-4zz18-xxxxxxxxxxxxxxx
+arv collection untrash --uuid=zzzzz-4zz18-xxxxxxxxxxxxxxx
 </pre>
 
-h3. Un-trashing a collection using workbench
-
-To untrash a collection using workbench, go to trash page on workbench by clicking on the "Trash" icon in the top navigation in workbench and use the recycle icon or selection dropdown option.
-
-!{display: block;margin-left: 25px;margin-right: auto;border:1px solid lightgray;}{{ site.baseurl }}/images/trash-button-topnav.png!
+The architecture section has a more detailed description of the "data lifecycle":{{ site.baseurl }}/architecture/keep-data-lifecycle.html  in Keep.
index f206d302dee334c1287cae1f41c5dee6009fdbe1..05924f8475874a878c1d314e6454f52d76251158 100644 (file)
@@ -11,11 +11,39 @@ SPDX-License-Identifier: CC-BY-SA-3.0
 
 Arvados Data collections can be downloaded using either the arv commands or using Workbench.
 
-# "*Downloading using arv commands*":#download-using-arv
-# "*Downloading using Workbench*":#download-using-workbench
-# "*Downloading a shared collection using Workbench*":#download-shared-collection
+# "*Download using Workbench*":#download-using-workbench
+# "*Sharing collections*":#download-shared-collection
+# "*Download using command line tools*":#download-using-arv
 
-h2(#download-using-arv). Downloading using arv commands
+h2(#download-using-workbench). Download using Workbench
+
+You can also download Arvados data collections using the Workbench.
+
+Visit the Workbench *Dashboard*. Click on *Projects*<span class="caret"></span> dropdown menu in the top navigation menu, select your *Home* project. You will see the *Data collections* tab, which lists the collections in this project.
+
+You can access the contents of a collection by clicking on the *<i class="fa fa-fw fa-archive"></i> Show* button next to the collection. This will take you to the collection's page. Using this page you can see the collection's contents, and download individual files.
+
+You can now download the collection files by clicking on the <span class="btn btn-sm btn-info"><i class="fa fa-download"></i></span> button(s).
+
+h2(#download-shared-collection). Sharing collections
+
+h3. Sharing with other Arvados users
+
+Collections can be shared with other users on the Arvados cluster by sharing the parent project.  Navigate to the parent project using the "breadcrumbs" bar, then click on the *Sharing* tab.  From the sharing tab, you can choose which users or groups to share with, and their level of access.
+
+h3. Creating a special download URL
+
+To share a collection with users that do not have an account on your Arvados cluster, visit the collection page using Workbench as described in the above section. Once on this page, click on the <span class="btn btn-sm btn-primary" >Create sharing link</span> button.
+
+This will create a sharing link for the collection as shown below. You can copy the sharing link in this page and share it with other users.
+
+!{display: block;margin-left: 25px;margin-right: auto;border:1px solid lightgray;}{{ site.baseurl }}/images/shared-collection.png!
+
+A user with this url can download this collection by simply accessing this url using browser. It will present a downloadable version of the collection as shown below.
+
+!{display: block;margin-left: 25px;margin-right: auto;border:1px solid lightgray;}{{ site.baseurl }}/images/download-shared-collection.png!
+
+h2(#download-using-arv). Download using command line tools
 
 {% include 'tutorial_expectations' %}
 
@@ -24,38 +52,35 @@ You can download Arvados data collections using the command line tools @arv-ls@
 Use @arv-ls@ to view the contents of a collection:
 
 <notextile>
-<pre><code>~$ <span class="userinput">arv-ls c1bad4b39ca5a924e481008009d94e32+210</span>
-var-GS000016015-ASM.tsv.bz2
+<pre><code>~$ <span class="userinput">arv-ls ae480c5099b81e17267b7445e35b4bc7+180</span>
+./HWI-ST1027_129_D0THKACXX.1_1.fastq
+./HWI-ST1027_129_D0THKACXX.1_2.fastq
 </code></pre>
 
-<pre><code>~$ <span class="userinput">arv-ls 887cd41e9c613463eab2f0d885c6dd96+83</span>
-alice.txt
-bob.txt
-carol.txt
-</code></pre>
-</notextile>
-
-Use @-s@ to print file sizes rounded up to the nearest kilobyte:
+Use @-s@ to print file sizes, in kilobytes, rounded up:
 
 <notextile>
-<pre><code>~$ <span class="userinput">arv-ls -s c1bad4b39ca5a924e481008009d94e32+210</span>
-221887 var-GS000016015-ASM.tsv.bz2
+<pre><code>~$ <span class="userinput">arv-ls -s ae480c5099b81e17267b7445e35b4bc7+180</span>
+     12258 ./HWI-ST1027_129_D0THKACXX.1_1.fastq
+     12258 ./HWI-ST1027_129_D0THKACXX.1_2.fastq
 </code></pre>
 </notextile>
 
 Use @arv-get@ to download the contents of a collection and place it in the directory specified in the second argument (in this example, @.@ for the current directory):
 
 <notextile>
-<pre><code>~$ <span class="userinput">arv-get c1bad4b39ca5a924e481008009d94e32+210/ .</span>
-~$ <span class="userinput">ls var-GS000016015-ASM.tsv.bz2</span>
-var-GS000016015-ASM.tsv.bz2
+<pre><code>~$ <span class="userinput">$ arv-get ae480c5099b81e17267b7445e35b4bc7+180/ .</span>
+23 MiB / 23 MiB 100.0%
+~$ <span class="userinput">ls</span>
+HWI-ST1027_129_D0THKACXX.1_1.fastq  HWI-ST1027_129_D0THKACXX.1_2.fastq
 </code></pre>
 </notextile>
 
 You can also download individual files:
 
 <notextile>
-<pre><code>~$ <span class="userinput">arv-get 887cd41e9c613463eab2f0d885c6dd96+83/alice.txt .</span>
+<pre><code>~$ <span class="userinput">arv-get ae480c5099b81e17267b7445e35b4bc7+180/HWI-ST1027_129_D0THKACXX.1_1.fastq .</span>
+11 MiB / 11 MiB 100.0%
 </code></pre>
 </notextile>
 
@@ -65,33 +90,9 @@ If your cluster is "configured to be part of a federation":{{site.baseurl}}/admi
 
 If you request a collection by portable data hash, it will first search the home cluster, then search federated clusters.
 
-You may also request a collection by UUID.  In this case, it will contact the cluster named in the UUID prefix (in this example, @qr1hi@).
+You may also request a collection by UUID.  In this case, it will contact the cluster named in the UUID prefix (in this example, @zzzzz@).
 
 <notextile>
-<pre><code>~$ <span class="userinput">arv-get qr1hi-4zz18-fw6dnjxtkvzdewt/ .</span>
+<pre><code>~$ <span class="userinput">arv-get zzzzz-4zz18-fw6dnjxtkvzdewt/ .</span>
 </code></pre>
 </notextile>
-
-h2(#download-using-workbench). Downloading using Workbench
-
-You can also download Arvados data collections using the Workbench.
-
-Visit the Workbench *Dashboard*. Click on *Projects*<span class="caret"></span> dropdown menu in the top navigation menu, select your *Home* project. You will see the *Data collections* tab, which lists the collections in this project.
-
-You can access the contents of a collection by clicking on the *<i class="fa fa-fw fa-archive"></i> Show* button next to the collection. This will take you to the collection's page. Using this page you can see the collection's contents, download individual files, and set sharing options.
-
-You can now download the collection files by clicking on the <span class="btn btn-sm btn-info"><i class="fa fa-download"></i></span> button(s).
-
-h2(#download-shared-collection). Downloading a shared collection using Workbench
-
-Collections can be shared to allow downloads by anonymous users.
-
-To share a collection with anonymous users, visit the collection page using Workbench as described in the above section. Once on this page, click on the <span class="btn btn-sm btn-primary" >Create sharing link</span> button.
-
-This will create a sharing link for the collection as shown below. You can copy the sharing link in this page and share it with other users.
-
-!{display: block;margin-left: 25px;margin-right: auto;border:1px solid lightgray;}{{ site.baseurl }}/images/shared-collection.png!
-
-A user with this url can download this collection by simply accessing this url using browser. It will present a downloadable version of the collection as shown below.
-
-!{display: block;margin-left: 25px;margin-right: auto;border:1px solid lightgray;}{{ site.baseurl }}/images/download-shared-collection.png!
index e1760219920b7e8de3bb9fa0604bece7d716fbb6..060ae2acbe94aeb29ff01cc20d5099c3180008f3 100644 (file)
@@ -1,7 +1,7 @@
 ---
 layout: default
 navsection: userguide
-title: "Accessing Keep from GNU/Linux"
+title: "Access Keep as a GNU/Linux filesystem"
 ...
 {% comment %}
 Copyright (C) The Arvados Authors. All rights reserved.
@@ -9,17 +9,16 @@ Copyright (C) The Arvados Authors. All rights reserved.
 SPDX-License-Identifier: CC-BY-SA-3.0
 {% endcomment %}
 
-This tutoral describes how to access Arvados collections on GNU/Linux using traditional filesystem tools by mounting Keep as a file system using @arv-mount@.
+GNU/Linux users can use @arv-mount@ or Gnome to mount Keep as a file system in order to access Arvados collections using traditional filesystem tools.
 
 {% include 'tutorial_expectations' %}
 
-h2. Arv-mount
+# "*Mounting at the command line with arv-mount*":#arv-mount
+# "*Mounting in Gnome File manager*":#gnome
 
-@arv-mount@ provides several features:
+h2(#arv-mount). Arv-mount
 
-* You can browse, open and read Keep entries as if they are regular files.
-* It is easy for existing tools to access files in Keep.
-* Data is streamed on demand.  It is not necessary to download an entire file or collection to start processing.
+@arv-mount@ provides a file system view of Arvados Keep using File System in Userspace (FUSE).  You can browse, open and read Keep entries as if they are regular files, and existing tools can access files in Keep.  Data is streamed on demand.  It is not necessary to download an entire file or collection to start processing.
 
 The default mode permits browsing any collection in Arvados as a subdirectory under the mount directory.  To avoid having to fetch a potentially large list of all collections, collection directories only come into existence when explicitly accessed by UUID or portable data hash. For instance, a collection may be found by its content hash in the @keep/by_id@ directory.
 
@@ -59,3 +58,11 @@ Not supported:
 If multiple clients (separate instances of arv-mount or other arvados applications) modify the same file in the same collection within a short time interval, this may result in a conflict.  In this case, the most recent commit wins, and the "loser" will be renamed to a conflict file in the form @name~YYYYMMDD-HHMMSS~conflict~@.
 
 Please note this feature is in beta testing.  In particular, the conflict mechanism is itself currently subject to race conditions with potential for data loss when a collection is being modified simultaneously by multiple clients.  This issue will be resolved in future development.
+
+h2(#gnome). Mounting in Gnome File manager
+
+As an alternative to @arv-mount@ you can also access the WebDAV mount through the Gnome File manager.
+
+# Open "Files"
+# On the left sidebar, click on "Other Locations"
+# At the bottom of the window, enter @davs://collections.ClusterID.example.com/@  When prompted for credentials, enter username "arvados" and a valid Arvados token in the @Password@ field.
index 9397d61e05ff2cfdb8ef1e13f3e1e31d1fbd57c1..911b8808eb1dde5f69a4605dfb895b974274ff09 100644 (file)
@@ -1,7 +1,7 @@
 ---
 layout: default
 navsection: userguide
-title: "Accessing Keep from OS X"
+title: "Access Keep from macOS Finder"
 ...
 {% comment %}
 Copyright (C) The Arvados Authors. All rights reserved.
@@ -9,16 +9,16 @@ Copyright (C) The Arvados Authors. All rights reserved.
 SPDX-License-Identifier: CC-BY-SA-3.0
 {% endcomment %}
 
-OS X users can browse Keep read-only via WebDAV. Specific collections can also be accessed read-write via WebDAV.
+Users of macOS can browse Keep read-only via WebDAV. Specific collections can also be accessed read-write via WebDAV.
 
-h3. Browsing Keep (read-only)
+h3. Browsing Keep in Finder (read-only)
 
-In Finder, use "Connect to Server..." under the "Go" menu and enter @https://collections.ClusterID.example.com/@ in popup dialog. When prompted for credentials, put a valid Arvados token in the @Password@ field and anything in the Name field (it will be ignored by Arvados).
+In Finder, use "Connect to Server..." under the "Go" menu and enter @https://collections.ClusterID.example.com/@ in popup dialog. When prompted for credentials, enter username "arvados" and paste a valid Arvados token for the @Password@ field.
 
 This mount is read-only. Write support for the @/users/@ directory is planned for a future release.
 
 h3. Accessing a specific collection in Keep (read-write)
 
-In Finder, use "Connect to Server..." under the "Go" menu and enter @https://collections.ClusterID.example.com/@ in popup dialog. When prompted for credentials, put a valid Arvados token in the @Password@ field and anything in the Name field (it will be ignored by Arvados).
+In Finder, use "Connect to Server..." under the "Go" menu and enter @https://collections.ClusterID.example.com/c=your-collection-uuid@ in popup dialog. When prompted for credentials, put a valid Arvados token in the @Password@ field and anything in the Name field (it will be ignored by Arvados).
 
 This collection is now accessible read/write.
index 29b28fff9ca6d13cfbde96026e537447708dd1c0..a40a997ba1e81974298a2917685fbc248f609a1a 100644 (file)
@@ -1,7 +1,7 @@
 ---
 layout: default
 navsection: userguide
-title: "Accessing Keep from Windows"
+title: "Access Keep from Windows File Explorer"
 ...
 {% comment %}
 Copyright (C) The Arvados Authors. All rights reserved.
@@ -11,7 +11,7 @@ SPDX-License-Identifier: CC-BY-SA-3.0
 
 Windows users can browse Keep read-only via WebDAV. Specific collections can also be accessed read-write via WebDAV.
 
-h3. Browsing Keep (read-only)
+h3. Browsing Keep in File Explorer (read-only)
 
 Use the 'Map network drive' functionality, and enter @https://collections.ClusterID.example.com/@ in the Folder field. When prompted for credentials, you can fill in an arbitrary string for @Username@, it is ignored by Arvados. Windows will not accept an empty @Username@. Put a valid Arvados token in the @Password@ field.
 
index ec7086db96d1fec397013eb24e53f0dd2681854b..21efc475c54b4b5baa5c1023b029aa82708181ef 100644 (file)
@@ -9,13 +9,44 @@ Copyright (C) The Arvados Authors. All rights reserved.
 SPDX-License-Identifier: CC-BY-SA-3.0
 {% endcomment %}
 
-Arvados Data collections can be uploaded using either the @arv-put@ command line tool or using Workbench.
+Arvados Data collections can be uploaded using either Workbench or the @arv-put@ command line tool.
 
-# "*Upload using command line tool*":#upload-using-command
 # "*Upload using Workbench*":#upload-using-workbench
+# "*Creating projects*":#creating-projects
+# "*Upload using command line tool*":#upload-using-command
+
+h2(#upload-using-workbench). Upload using Workbench
+
+To upload using Workbench, visit the Workbench *Dashboard*. Click on *Projects*<span class="caret"></span> dropdown menu in the top navigation menu and select your *Home* project or any other project of your choosing.  You will see the *Data collections* tab for this project, which lists the collections in this project.
+
+To upload files into a new collection, click on *Add data*<span class="caret"></span> dropdown menu and select *Upload files from my computer*.
+
+!{display: block;margin-left: 25px;margin-right: auto;border:1px solid lightgray;}{{ site.baseurl }}/images/upload-using-workbench.png!
+
+<br/>This will create a new empty collection in your chosen project and will take you to the *Upload* tab for that collection.
+
+!{display: block;margin-left: 25px;margin-right: auto;border:1px solid lightgray;}{{ site.baseurl }}/images/upload-tab-in-new-collection.png!
+
+Click on the *Browse...* button and select the files you would like to upload. Selected files will be added to a list of files to be uploaded. After you are done selecting files to upload, click on the *<i class="fa fa-fw fa-play"></i> Start* button to start upload. This will start uploading files to Arvados and Workbench will show you the progress bar. When upload is completed, you will see an indication to that effect.
+
+!{display: block;margin-left: 25px;margin-right: auto;border:1px solid lightgray;}{{ site.baseurl }}/images/files-uploaded.png!
+
+*Note:* If you leave the collection page during the upload, the upload process will be aborted and you will need to upload the files again.
+
+*Note:* You can also use the Upload tab to add additional files to an existing collection.
 
 notextile. <div class="spaced-out">
 
+h2(#creating-projects). Creating projects
+
+Files are organized into Collections, and Collections are organized by Projects.
+
+Click on *Projects*<span class="caret"></span> <span class="rarr">&rarr;</span> <i class="fa fa-fw fa-plus"></i>*Add a new project* to add a top level project.
+
+To create a subproject, navigate to the parent project, and click on <i class="fa fa-fw fa-plus"></i>*Add a subproject*.
+
+See "Sharing collections":tutorial-keep-get.html#download-shared-collection for information about sharing projects and collections with other users.
+
 h2(#upload-using-command). Upload using command line tool
 
 {% include 'tutorial_expectations' %}
@@ -25,12 +56,12 @@ To upload a file to Keep using @arv-put@:
 <pre><code>~$ <span class="userinput">arv-put var-GS000016015-ASM.tsv.bz2</span>
 216M / 216M 100.0%
 Collection saved as ...
-qr1hi-4zz18-xxxxxxxxxxxxxxx
+zzzzz-4zz18-xxxxxxxxxxxxxxx
 </code></pre>
 </notextile>
 
 
-The output value @qr1hi-4zz18-xxxxxxxxxxxxxxx@ is the uuid of the Arvados collection created.
+The output value @zzzzz-4zz18-xxxxxxxxxxxxxxx@ is the uuid of the Arvados collection created.
 
 Note: The file used in this example is a freely available TSV file containing variant annotations from the "Personal Genome Project (PGP)":http://www.pgp-hms.org participant "hu599905":https://my.pgp-hms.org/profile/hu599905), downloadable "here":https://warehouse.pgp-hms.org/warehouse/f815ec01d5d2f11cb12874ab2ed50daa+234+K@ant/var-GS000016015-ASM.tsv.bz2. Alternatively, you can replace @var-GS000016015-ASM.tsv.bz2@ with the name of any file you have locally, or you could get the TSV file by "downloading it from Keep.":{{site.baseurl}}/user/tutorials/tutorial-keep-get.html
 
@@ -44,7 +75,7 @@ Note: The file used in this example is a freely available TSV file containing va
 ~$ <span class="userinput">arv-put tmp</span>
 0M / 0M 100.0%
 Collection saved as ...
-qr1hi-4zz18-yyyyyyyyyyyyyyy
+zzzzz-4zz18-yyyyyyyyyyyyyyy
 </code></pre>
 </notextile>
 
@@ -63,23 +94,3 @@ To move the collection to a different project, check the box at the left of the
 Click on the *<i class="fa fa-fw fa-archive"></i> Show* button next to the collection's listing on a project page to go to the Workbench page for your collection.  On this page, you can see the collection's contents, download individual files, and set sharing options.
 
 notextile. </div>
-
-h2(#upload-using-workbench). Upload using Workbench
-
-To upload using Workbench, visit the Workbench *Dashboard*. Click on *Projects*<span class="caret"></span> dropdown menu in the top navigation menu and select your *Home* project or any other project of your choosing.  You will see the *Data collections* tab for this project, which lists the collections in this project.
-
-To upload files into a new collection, click on *Add data*<span class="caret"></span> dropdown menu and select *Upload files from my computer*.
-
-!{display: block;margin-left: 25px;margin-right: auto;border:1px solid lightgray;}{{ site.baseurl }}/images/upload-using-workbench.png!
-
-<br/>This will create a new empty collection in your chosen project and will take you to the *Upload* tab for that collection.
-
-!{display: block;margin-left: 25px;margin-right: auto;border:1px solid lightgray;}{{ site.baseurl }}/images/upload-tab-in-new-collection.png!
-
-Click on the *Browse...* button and select the files you would like to upload. Selected files will be added to a list of files to be uploaded. After you are done selecting files to upload, click on the *<i class="fa fa-fw fa-play"></i> Start* button to start upload. This will start uploading files to Arvados and Workbench will show you the progress bar. When upload is completed, you will see an indication to that effect.
-
-!{display: block;margin-left: 25px;margin-right: auto;border:1px solid lightgray;}{{ site.baseurl }}/images/files-uploaded.png!
-
-*Note:* If you leave the collection page during the upload, the upload process will be aborted and you will need to upload the files again.
-
-*Note:* You can also use the Upload tab to add additional files to an existing collection.
index 8dcb8e674e55021313dad6bd4cea1902e3187b9f..8a082257231196293c06bf2c6e3b5c879df6e3c7 100644 (file)
@@ -23,13 +23,15 @@ notextile. <div class="spaced-out">
 
 # Start from the *Workbench Dashboard*.  You can access the Dashboard by clicking on *<i class="fa fa-lg fa-fw fa-dashboard"></i> Dashboard* in the upper left corner of any Workbench page.
 # Click on the <span class="btn btn-sm btn-primary"><i class="fa fa-fw fa-gear"></i> Run a process...</span> button.  This will open a dialog box titled *Choose a pipeline or workflow to run*.
-# In the search box, type in *Tutorial bwa mem cwl*.
-# Select *<i class="fa fa-fw fa-gear"></i> Tutorial bwa mem cwl* and click the <span class="btn btn-sm btn-primary" >Next: choose inputs <i class="fa fa-fw fa-arrow-circle-right"></i></span> button.  This will create a new process in your *Home* project and will open it. You can now supply the inputs for the process. Please note that all required inputs are populated with default values and you can change them if you prefer.
-# For example, let's see how to change *"reference" parameter* for this workflow. Click the <span class="btn btn-sm btn-primary">Choose</span> button beneath the *"reference" parameter* header.  This will open a dialog box titled *Choose a dataset for "reference" parameter for cwl-runner in bwa-mem.cwl component*.
-# Open the *Home <span class="caret"></span>* menu and select *All Projects*. Search for and select *<i class="fa fa-fw fa-archive"></i> Tutorial chromosome 19 reference*. You will then see a list of files. Select *<i class="fa fa-fw fa-file"></i> 19-fasta.bwt* and click the <span class="btn btn-sm btn-primary" >OK</span> button.
-# Repeat the previous two steps to set the *"read_p1" parameter for cwl-runner script in bwa-mem.cwl component* and *"read_p2" parameter for cwl-runner script in bwa-mem.cwl component* parameters.
-# Click on the <span class="btn btn-sm btn-primary" >Run <i class="fa fa-fw fa-play"></i></span> button.  The page updates to show you that the process has been submitted to run on the Arvados cluster.
-# After the process starts running, you can track the progress by watching log messages from the component(s).  This page refreshes automatically.  You will see a <span class="label label-success">complete</span> label when the process completes successfully.
+# In the search box, type in *bwa-mem.cwl*.
+# Select *<i class="fa fa-fw fa-gear"></i> bwa-mem.cwl* and click the <span class="btn btn-sm btn-primary" >Next: choose inputs <i class="fa fa-fw fa-arrow-circle-right"></i></span> button.  This will create a new process in your *Home* project and will open it. You can now supply the inputs for the process. Please note that all required inputs are populated with default values and you can change them if you prefer.
+# For example, let's see how to set read pair *read_p1* and *read_p2* for this workflow. Click the <span class="btn btn-sm btn-primary">Choose</span> button beneath the *read_p1* header.  This will open a dialog box titled *Choose a file*.
+# In the file dialog, click on *Home <span class="caret"></span>* menu and then select *All Projects*.
+# Enter *HWI-ST1027* into the search box.  You will see one or more collections. Click on *<i class="fa fa-fw fa-archive"></i>  HWI-ST1027_129_D0THKACXX for CWL tutorial*
+# The right hand panel will list two files.  Click on the first one ending in "_1" and click the <span class="btn btn-sm btn-primary" >OK</span> button.
+# Repeat the steps 5-8 to set the *read_p2* except selecting the second file ending in "_2"
+# Scroll to the bottom of the "Inputs" panel and click on the <span class="btn btn-sm btn-primary" >Run <i class="fa fa-fw fa-play"></i></span> button.  The page updates to show you that the process has been submitted to run on the Arvados cluster.
+# Once the process starts running, you can track the progress by watching log messages from the component(s).  This page refreshes automatically.  You will see a <span class="label label-success">complete</span> label when the process completes successfully.
 # Click on the *Output* link to see the results of the process.  This will load a new page listing the output files from this process.  You'll see the output SAM file from the alignment tool under the *Files* tab.
 # Click on the <span class="btn btn-sm btn-info"><i class="fa fa-download"></i></span> download button to the right of the SAM file to download your results.
 
index dd537c46acefe1019b72842a61d39ad062f17220..0166b8b5253af52174fa03e516d6cbe9cb874aad 100644 (file)
@@ -1,7 +1,7 @@
 ---
 layout: default
 navsection: userguide
-title: "Writing a CWL workflow"
+title: "Developing workflows with CWL"
 ...
 {% comment %}
 Copyright (C) The Arvados Authors. All rights reserved.
@@ -15,7 +15,7 @@ SPDX-License-Identifier: CC-BY-SA-3.0
 
 h2. Developing workflows
 
-For an introduction and and detailed documentation about writing CWL, see the "CWL User Guide":https://www.commonwl.org/user_guide and the "CWL Specification":http://commonwl.org/v1.1 .
+For an introduction and and detailed documentation about writing CWL, see the "CWL User Guide":https://www.commonwl.org/user_guide and the "CWL Specification":http://commonwl.org/v1.2 .
 
 See "Writing Portable High-Performance Workflows":{{site.baseurl}}/user/cwl/cwl-style.html and "Arvados CWL Extensions":{{site.baseurl}}/user/cwl/cwl-extensions.html for additional information about using CWL on Arvados.
 
@@ -23,65 +23,6 @@ See "Repositories of CWL Tools and Workflows":https://www.commonwl.org/#Reposito
 
 See "Software for working with CWL":https://www.commonwl.org/#Software_for_working_with_CWL for links to software tools to help create CWL documents.
 
-h2. Using Composer
-
-You can create new workflows in the browser using "Arvados Composer":{{site.baseurl}}/user/composer/composer.html
-
-h2. Registering a workflow to use in Workbench
-
-Use @--create-workflow@ to register a CWL workflow with Arvados.  This enables you to share workflows with other Arvados users, and run them by clicking the <span class="btn btn-sm btn-primary"><i class="fa fa-fw fa-gear"></i> Run a process...</span> button on the Workbench Dashboard and on the command line by UUID.
-
-<notextile>
-<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner --create-workflow bwa-mem.cwl</span>
-arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
-2016-07-01 12:21:01 arvados.arv-run[15796] INFO: Upload local files: "bwa-mem.cwl"
-2016-07-01 12:21:01 arvados.arv-run[15796] INFO: Uploaded to qr1hi-4zz18-7e0hedrmkuyoei3
-2016-07-01 12:21:01 arvados.cwl-runner[15796] INFO: Created template qr1hi-p5p6p-rjleou1dwr167v5
-qr1hi-p5p6p-rjleou1dwr167v5
-</code></pre>
-</notextile>
-
-You can provide a partial input file to set default values for the workflow input parameters.  You can also use the @--name@ option to set the name of the workflow:
-
-<notextile>
-<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner --name "My workflow with defaults" --create-workflow bwa-mem.cwl bwa-mem-template.yml</span>
-arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
-2016-07-01 14:09:50 arvados.arv-run[3730] INFO: Upload local files: "bwa-mem.cwl"
-2016-07-01 14:09:50 arvados.arv-run[3730] INFO: Uploaded to qr1hi-4zz18-0f91qkovk4ml18o
-2016-07-01 14:09:50 arvados.cwl-runner[3730] INFO: Created template qr1hi-p5p6p-0deqe6nuuyqns2i
-qr1hi-p5p6p-zuniv58hn8d0qd8
-</code></pre>
-</notextile>
-
-h3. Running registered workflows at the command line
-
-You can run a registered workflow at the command line by its UUID:
-
-<notextile>
-<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">arvados-cwl-runner qr1hi-p5p6p-zuniv58hn8d0qd8 --help</span>
-/home/peter/work/scripts/venv/bin/arvados-cwl-runner 0d62edcb9d25bf4dcdb20d8872ea7b438e12fc59 1.0.20161209192028, arvados-python-client 0.1.20161212125425, cwltool 1.0.20161207161158
-Resolved 'qr1hi-p5p6p-zuniv58hn8d0qd8' to 'keep:655c6cd07550151b210961ed1d3852cf+57/bwa-mem.cwl'
-usage: qr1hi-p5p6p-zuniv58hn8d0qd8 [-h] [--PL PL] --group_id GROUP_ID
-                                   --read_p1 READ_P1 [--read_p2 READ_P2]
-                                   [--reference REFERENCE] --sample_id
-                                   SAMPLE_ID
-                                   [job_order]
-
-positional arguments:
-  job_order             Job input json file
-
-optional arguments:
-  -h, --help            show this help message and exit
-  --PL PL
-  --group_id GROUP_ID
-  --read_p1 READ_P1     The reads, in fastq format.
-  --read_p2 READ_P2     For mate paired reads, the second file (optional).
-  --reference REFERENCE
-                        The index files produced by `bwa index`
-  --sample_id SAMPLE_ID
-</code></pre>
-</notextile>
-
 h2. Using cwltool
 
 When developing a workflow, it is often helpful to run it on the local host to avoid the overhead of submitting to the cluster.  To execute a workflow only on the local host (without submitting jobs to an Arvados cluster) you can use the @cwltool@ command.  Note that when using @cwltool@ you must have the input data accessible on the local file system using either @arv-mount@ or @arv-get@ to fetch the data from Keep.
@@ -150,60 +91,3 @@ Final process status is success
 </notextile>
 
 If you get the error @JavascriptException: Long-running script killed after 20 seconds.@ this may be due to the Dockerized Node.js engine taking too long to start.  You may address this by installing Node.js locally (run @apt-get install nodejs@ on Debian or Ubuntu) or by specifying a longer timeout with the @--eval-timeout@ option.  For example, run the workflow with @cwltool --eval-timeout=40@ for a 40-second timeout.
-
-h2. Making workflows directly executable
-
-You can make a workflow file directly executable (@cwl-runner@ should be an alias to @arvados-cwl-runner@) by adding the following line to the top of the file:
-
-<notextile>
-<pre><code>#!/usr/bin/env cwl-runner
-</code></pre>
-</notextile>
-
-<notextile>
-<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">./bwa-mem.cwl bwa-mem-input.yml</span>
-arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
-2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Upload local files: "bwa-mem.cwl"
-2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Uploaded to qr1hi-4zz18-h7ljh5u76760ww2
-2016-06-30 14:56:40 arvados.cwl-runner[27002] INFO: Submitted job qr1hi-8i9sb-fm2n3b1w0l6bskg
-2016-06-30 14:56:41 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-fm2n3b1w0l6bskg) is Running
-2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-fm2n3b1w0l6bskg) is Complete
-2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Overall process status is success
-{
-    "aligned_sam": {
-        "path": "keep:54325254b226664960de07b3b9482349+154/HWI-ST1027_129_D0THKACXX.1_1.sam",
-        "checksum": "sha1$0dc46a3126d0b5d4ce213b5f0e86e2d05a54755a",
-        "class": "File",
-        "size": 30738986
-    }
-}
-</code></pre>
-</notextile>
-
-You can even make an input file directly executable the same way with the following two lines at the top:
-
-<notextile>
-<pre><code>#!/usr/bin/env cwl-runner
-cwl:tool: <span class="userinput">bwa-mem.cwl</span>
-</code></pre>
-</notextile>
-
-<notextile>
-<pre><code>~/arvados/doc/user/cwl/bwa-mem$ <span class="userinput">./bwa-mem-input.yml</span>
-arvados-cwl-runner 1.0.20160628195002, arvados-python-client 0.1.20160616015107, cwltool 1.0.20160629140624
-2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Upload local files: "bwa-mem.cwl"
-2016-06-30 14:56:36 arvados.arv-run[27002] INFO: Uploaded to qr1hi-4zz18-h7ljh5u76760ww2
-2016-06-30 14:56:40 arvados.cwl-runner[27002] INFO: Submitted job qr1hi-8i9sb-fm2n3b1w0l6bskg
-2016-06-30 14:56:41 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-fm2n3b1w0l6bskg) is Running
-2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Job bwa-mem.cwl (qr1hi-8i9sb-fm2n3b1w0l6bskg) is Complete
-2016-06-30 14:57:12 arvados.cwl-runner[27002] INFO: Overall process status is success
-{
-    "aligned_sam": {
-        "path": "keep:54325254b226664960de07b3b9482349+154/HWI-ST1027_129_D0THKACXX.1_1.sam",
-        "checksum": "sha1$0dc46a3126d0b5d4ce213b5f0e86e2d05a54755a",
-        "class": "File",
-        "size": 30738986
-    }
-}
-</code></pre>
-</notextile>
index 15993c4bc322619e125ddb5411a79a2d0f4348f0..69ea34bc81c412f0ec21d6747db904a163f3000f 100644 (file)
@@ -23,12 +23,10 @@ ARG cwl_runner_version
 RUN echo cwl_runner_version $cwl_runner_version python_sdk_version $python_sdk_version
 
 RUN apt-get update -q
-RUN apt-get install -yq --no-install-recommends nodejs \
-    python-arvados-python-client=$python_sdk_version \
-    python3-arvados-cwl-runner=$cwl_runner_version
+RUN apt-get install -yq --no-install-recommends python3-arvados-cwl-runner=$cwl_runner_version
 
 # use the Python executable from the python-arvados-cwl-runner package
-RUN rm -f /usr/bin/python && ln -s /usr/share/python2.7/dist/python-arvados-python-client/bin/python /usr/bin/python
+RUN rm -f /usr/bin/python && ln -s /usr/share/python3/dist/python3-arvados-cwl-runner/bin/python /usr/bin/python
 RUN rm -f /usr/bin/python3 && ln -s /usr/share/python3/dist/python3-arvados-cwl-runner/bin/python /usr/bin/python3
 
 # Install dependencies and set up system.
index d1cf2a870975b662d8318d9ef6a25f08ce204c93..2afccc45b628cc01b00ddac873abdfc4eae20b61 100644 (file)
@@ -24,5 +24,9 @@ func (seedDatabase) Run(ctx context.Context, fail func(error), super *Supervisor
        if err != nil {
                return err
        }
+       err = super.RunProgram(ctx, "services/api", nil, railsEnv, "bundle", "exec", "./script/get_anonymous_user_token.rb")
+       if err != nil {
+               return err
+       }
        return nil
 }
index 3f4fb7482229bc704e9daee1d71a0775aa8ed3fa..3484a1444e786cc5f026f0d0a68ada822b79ffb1 100644 (file)
@@ -617,6 +617,10 @@ func (super *Supervisor) autofillConfig(cfg *arvados.Config) error {
        if cluster.Collections.BlobSigningKey == "" {
                cluster.Collections.BlobSigningKey = randomHexString(64)
        }
+       if cluster.Users.AnonymousUserToken == "" {
+               cluster.Users.AnonymousUserToken = randomHexString(64)
+       }
+
        if super.ClusterType != "production" && cluster.Containers.DispatchPrivateKey == "" {
                buf, err := ioutil.ReadFile(filepath.Join(super.SourcePath, "lib", "dispatchcloud", "test", "sshkey_dispatch"))
                if err != nil {
index ba8a836dd06aa6a30658a6cf5f7516ca178a815e..7f949d9bdb3e2fb83c539a0df5f15efec6ca2612 100644 (file)
@@ -419,7 +419,7 @@ func (az *azureInstanceSet) Create(
                Tags:     tags,
                InterfacePropertiesFormat: &network.InterfacePropertiesFormat{
                        IPConfigurations: &[]network.InterfaceIPConfiguration{
-                               network.InterfaceIPConfiguration{
+                               {
                                        Name: to.StringPtr("ip1"),
                                        InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{
                                                Subnet: &network.Subnet{
@@ -501,7 +501,7 @@ func (az *azureInstanceSet) Create(
                        StorageProfile: storageProfile,
                        NetworkProfile: &compute.NetworkProfile{
                                NetworkInterfaces: &[]compute.NetworkInterfaceReference{
-                                       compute.NetworkInterfaceReference{
+                                       {
                                                ID: nic.ID,
                                                NetworkInterfaceReferenceProperties: &compute.NetworkInterfaceReferenceProperties{
                                                        Primary: to.BoolPtr(true),
index 7b5a34df59798b781222cf52131fee0d1e7eade0..96d6dca69e451c0802220a884e4978fbb906f447 100644 (file)
@@ -127,7 +127,7 @@ var live = flag.String("live-azure-cfg", "", "Test with real azure API, provide
 func GetInstanceSet() (cloud.InstanceSet, cloud.ImageID, arvados.Cluster, error) {
        cluster := arvados.Cluster{
                InstanceTypes: arvados.InstanceTypeMap(map[string]arvados.InstanceType{
-                       "tiny": arvados.InstanceType{
+                       "tiny": {
                                Name:         "tiny",
                                ProviderType: "Standard_D1_v2",
                                VCPUs:        1,
@@ -259,7 +259,7 @@ func (*AzureInstanceSetSuite) TestWrapError(c *check.C) {
                        DetailedError: autorest.DetailedError{
                                Response: &http.Response{
                                        StatusCode: 429,
-                                       Header:     map[string][]string{"Retry-After": []string{"123"}},
+                                       Header:     map[string][]string{"Retry-After": {"123"}},
                                },
                        },
                        ServiceError: &azure.ServiceError{},
index 60938341798a100064e066ee8f78c686e2e953f5..5288b5c76cd2b3f0d3cd87c78f76f6706889e8f7 100644 (file)
@@ -127,7 +127,7 @@ func (t *tester) Run() bool {
        defer t.destroyTestInstance()
 
        bootDeadline := time.Now().Add(t.TimeoutBooting)
-       initCommand := worker.TagVerifier{nil, t.secret}.InitCommand()
+       initCommand := worker.TagVerifier{Instance: nil, Secret: t.secret, ReportVerified: nil}.InitCommand()
 
        t.Logger.WithFields(logrus.Fields{
                "InstanceType":         t.InstanceType.Name,
@@ -160,7 +160,7 @@ func (t *tester) Run() bool {
                // Create() succeeded. Make sure the new instance
                // appears right away in the Instances() list.
                lgrC.WithField("Instance", inst.ID()).Info("created instance")
-               t.testInstance = &worker.TagVerifier{inst, t.secret}
+               t.testInstance = &worker.TagVerifier{Instance: inst, Secret: t.secret, ReportVerified: nil}
                t.showLoginInfo()
                err = t.refreshTestInstance()
                if err == errTestInstanceNotFound {
@@ -236,7 +236,7 @@ func (t *tester) refreshTestInstance() error {
                        "Instance": i.ID(),
                        "Address":  i.Address(),
                }).Info("found our instance in returned list")
-               t.testInstance = &worker.TagVerifier{i, t.secret}
+               t.testInstance = &worker.TagVerifier{Instance: i, Secret: t.secret, ReportVerified: nil}
                if !t.showedLoginInfo {
                        t.showLoginInfo()
                }
index 2de82b1dcf9292f69959fb357839773b6a5df5e7..c329c1f88a4c0b8a2b3c3b49142f3ce60d17e871 100644 (file)
@@ -128,7 +128,7 @@ func (instanceSet *ec2InstanceSet) Create(
        var ok bool
        if keyname, ok = instanceSet.keys[md5keyFingerprint]; !ok {
                keyout, err := instanceSet.client.DescribeKeyPairs(&ec2.DescribeKeyPairsInput{
-                       Filters: []*ec2.Filter{&ec2.Filter{
+                       Filters: []*ec2.Filter{{
                                Name:   aws.String("fingerprint"),
                                Values: []*string{&md5keyFingerprint, &sha1keyFingerprint},
                        }},
@@ -174,7 +174,7 @@ func (instanceSet *ec2InstanceSet) Create(
                KeyName:      &keyname,
 
                NetworkInterfaces: []*ec2.InstanceNetworkInterfaceSpecification{
-                       &ec2.InstanceNetworkInterfaceSpecification{
+                       {
                                AssociatePublicIpAddress: aws.Bool(false),
                                DeleteOnTermination:      aws.Bool(true),
                                DeviceIndex:              aws.Int64(0),
@@ -184,7 +184,7 @@ func (instanceSet *ec2InstanceSet) Create(
                DisableApiTermination:             aws.Bool(false),
                InstanceInitiatedShutdownBehavior: aws.String("terminate"),
                TagSpecifications: []*ec2.TagSpecification{
-                       &ec2.TagSpecification{
+                       {
                                ResourceType: aws.String("instance"),
                                Tags:         ec2tags,
                        }},
@@ -192,7 +192,7 @@ func (instanceSet *ec2InstanceSet) Create(
        }
 
        if instanceType.AddedScratch > 0 {
-               rii.BlockDeviceMappings = []*ec2.BlockDeviceMapping{&ec2.BlockDeviceMapping{
+               rii.BlockDeviceMappings = []*ec2.BlockDeviceMapping{{
                        DeviceName: aws.String("/dev/xvdt"),
                        Ebs: &ec2.EbsBlockDevice{
                                DeleteOnTermination: aws.Bool(true),
index 638f4a77a341e398b4887eed1505284893bdb6b4..6aa6e857ff59b278aa3a54292ab461527502d84b 100644 (file)
@@ -65,7 +65,7 @@ func (e *ec2stub) DescribeKeyPairs(input *ec2.DescribeKeyPairsInput) (*ec2.Descr
 }
 
 func (e *ec2stub) RunInstances(input *ec2.RunInstancesInput) (*ec2.Reservation, error) {
-       return &ec2.Reservation{Instances: []*ec2.Instance{&ec2.Instance{
+       return &ec2.Reservation{Instances: []*ec2.Instance{{
                InstanceId: aws.String("i-123"),
                Tags:       input.TagSpecifications[0].Tags,
        }}}, nil
@@ -86,7 +86,7 @@ func (e *ec2stub) TerminateInstances(input *ec2.TerminateInstancesInput) (*ec2.T
 func GetInstanceSet() (cloud.InstanceSet, cloud.ImageID, arvados.Cluster, error) {
        cluster := arvados.Cluster{
                InstanceTypes: arvados.InstanceTypeMap(map[string]arvados.InstanceType{
-                       "tiny": arvados.InstanceType{
+                       "tiny": {
                                Name:         "tiny",
                                ProviderType: "t2.micro",
                                VCPUs:        1,
@@ -95,7 +95,7 @@ func GetInstanceSet() (cloud.InstanceSet, cloud.ImageID, arvados.Cluster, error)
                                Price:        .02,
                                Preemptible:  false,
                        },
-                       "tiny-with-extra-scratch": arvados.InstanceType{
+                       "tiny-with-extra-scratch": {
                                Name:         "tiny",
                                ProviderType: "t2.micro",
                                VCPUs:        1,
@@ -104,7 +104,7 @@ func GetInstanceSet() (cloud.InstanceSet, cloud.ImageID, arvados.Cluster, error)
                                Preemptible:  false,
                                AddedScratch: 20000000000,
                        },
-                       "tiny-preemptible": arvados.InstanceType{
+                       "tiny-preemptible": {
                                Name:         "tiny",
                                ProviderType: "t2.micro",
                                VCPUs:        1,
index d64106fbce6eaf9a5d2eefb246e15c4bc5017e92..1ea0883ac84730738981cb499fe874983fda6ca5 100644 (file)
@@ -161,9 +161,8 @@ func (checkCommand) RunCommand(prog string, args []string, stdin io.Reader, stdo
 
        if problems {
                return 1
-       } else {
-               return 0
        }
+       return 0
 }
 
 func warnAboutProblems(logger logrus.FieldLogger, cfg *arvados.Config) bool {
index f84c60192f2854571292dc05c466ab40b66d71ec..b1865a2217ce99c48a13ae8a17e4cf10d336cbf0 100644 (file)
@@ -708,6 +708,11 @@ Clusters:
       # remain valid before it needs to be revalidated.
       RemoteTokenRefresh: 5m
 
+      # How long a client token created from a login flow will be valid without
+      # asking the user to re-login. Example values: 60m, 8h.
+      # Default value zero means tokens don't have expiration.
+      TokenLifetime: 0s
+
     Git:
       # Path to git or gitolite-shell executable. Each authenticated
       # request will execute this program with the single argument "http-backend"
@@ -940,6 +945,12 @@ Clusters:
         # unlimited).
         MaxCloudOpsPerSecond: 0
 
+        # Maximum concurrent node creation operations (0 = unlimited). This is
+        # recommended by Azure in certain scenarios (see
+        # https://docs.microsoft.com/en-us/azure/virtual-machines/linux/capture-image)
+        # and can be used with other cloud providers too, if desired.
+        MaxConcurrentInstanceCreateOps: 0
+
         # Interval between cloud provider syncs/updates ("list all
         # instances").
         SyncInterval: 1m
@@ -1309,7 +1320,7 @@ Clusters:
       # a link to the multi-site search page on a "home" Workbench site.
       #
       # Example:
-      #   https://workbench.qr1hi.arvadosapi.com/collections/multisite
+      #   https://workbench.zzzzz.arvadosapi.com/collections/multisite
       MultiSiteSearch: ""
 
       # Should workbench allow management of local git repositories? Set to false if
@@ -1327,6 +1338,10 @@ Clusters:
       VocabularyURL: ""
       FileViewersConfigURL: ""
 
+      # Idle time after which the user's session will be auto closed.
+      # This feature is disabled when set to zero.
+      IdleTimeout: 0s
+
       # Workbench welcome screen, this is HTML text that will be
       # incorporated directly onto the page.
       WelcomePageHTML: |
index 5cdc3dae6a11b3dd5464743aee7213a2bd5d21d6..6ab69d21fe13eaec3dff84a3ef74e8febc88ff1b 100644 (file)
@@ -173,6 +173,7 @@ var whitelist = map[string]bool{
        "Login.Test":                                   true,
        "Login.Test.Enable":                            true,
        "Login.Test.Users":                             false,
+       "Login.TokenLifetime":                          false,
        "Mail":                                         true,
        "Mail.EmailFrom":                               false,
        "Mail.IssueReporterEmailFrom":                  false,
@@ -236,6 +237,7 @@ var whitelist = map[string]bool{
        "Workbench.EnableGettingStartedPopup":          true,
        "Workbench.EnablePublicProjectsPage":           true,
        "Workbench.FileViewersConfigURL":               true,
+       "Workbench.IdleTimeout":                        true,
        "Workbench.InactivePageHTML":                   true,
        "Workbench.LogViewerMaxBytes":                  true,
        "Workbench.MultiSiteSearch":                    true,
index 25eaa7d30cd20221a1eb2330e9867d88de5cbd95..201ae3604537f9f44a9e788320b7262685944f98 100644 (file)
@@ -714,6 +714,11 @@ Clusters:
       # remain valid before it needs to be revalidated.
       RemoteTokenRefresh: 5m
 
+      # How long a client token created from a login flow will be valid without
+      # asking the user to re-login. Example values: 60m, 8h.
+      # Default value zero means tokens don't have expiration.
+      TokenLifetime: 0s
+
     Git:
       # Path to git or gitolite-shell executable. Each authenticated
       # request will execute this program with the single argument "http-backend"
@@ -946,6 +951,12 @@ Clusters:
         # unlimited).
         MaxCloudOpsPerSecond: 0
 
+        # Maximum concurrent node creation operations (0 = unlimited). This is
+        # recommended by Azure in certain scenarios (see
+        # https://docs.microsoft.com/en-us/azure/virtual-machines/linux/capture-image)
+        # and can be used with other cloud providers too, if desired.
+        MaxConcurrentInstanceCreateOps: 0
+
         # Interval between cloud provider syncs/updates ("list all
         # instances").
         SyncInterval: 1m
@@ -1315,7 +1326,7 @@ Clusters:
       # a link to the multi-site search page on a "home" Workbench site.
       #
       # Example:
-      #   https://workbench.qr1hi.arvadosapi.com/collections/multisite
+      #   https://workbench.zzzzz.arvadosapi.com/collections/multisite
       MultiSiteSearch: ""
 
       # Should workbench allow management of local git repositories? Set to false if
@@ -1333,6 +1344,10 @@ Clusters:
       VocabularyURL: ""
       FileViewersConfigURL: ""
 
+      # Idle time after which the user's session will be auto closed.
+      # This feature is disabled when set to zero.
+      IdleTimeout: 0s
+
       # Workbench welcome screen, this is HTML text that will be
       # incorporated directly onto the page.
       WelcomePageHTML: |
index 418b6811beeb82d814c16603e50b694502372522..d715734c6506acc5090f88cf2c7b1a8b1053bdb4 100644 (file)
@@ -111,6 +111,14 @@ func (conn *Conn) chooseBackend(id string) backend {
        }
 }
 
+func (conn *Conn) localOrLoginCluster() backend {
+       if conn.cluster.Login.LoginCluster != "" {
+               return conn.chooseBackend(conn.cluster.Login.LoginCluster)
+       } else {
+               return conn.local
+       }
+}
+
 // Call fn with the local backend; then, if fn returned 404, call fn
 // on the available remote backends (possibly concurrently) until one
 // succeeds.
@@ -462,15 +470,37 @@ func (conn *Conn) UserMerge(ctx context.Context, options arvados.UserMergeOption
 }
 
 func (conn *Conn) UserActivate(ctx context.Context, options arvados.UserActivateOptions) (arvados.User, error) {
-       return conn.chooseBackend(options.UUID).UserActivate(ctx, options)
+       return conn.localOrLoginCluster().UserActivate(ctx, options)
 }
 
 func (conn *Conn) UserSetup(ctx context.Context, options arvados.UserSetupOptions) (map[string]interface{}, error) {
-       return conn.chooseBackend(options.UUID).UserSetup(ctx, options)
+       upstream := conn.localOrLoginCluster()
+       if upstream != conn.local {
+               // When LoginCluster is in effect, and we're setting
+               // up a remote user, and we want to give that user
+               // access to a local VM, we can't include the VM in
+               // the setup call, because the remote cluster won't
+               // recognize it.
+
+               // Similarly, if we want to create a git repo,
+               // it should be created on the local cluster,
+               // not the remote one.
+
+               upstreamOptions := options
+               upstreamOptions.VMUUID = ""
+               upstreamOptions.RepoName = ""
+
+               ret, err := upstream.UserSetup(ctx, upstreamOptions)
+               if err != nil {
+                       return ret, err
+               }
+       }
+
+       return conn.local.UserSetup(ctx, options)
 }
 
 func (conn *Conn) UserUnsetup(ctx context.Context, options arvados.GetOptions) (arvados.User, error) {
-       return conn.chooseBackend(options.UUID).UserUnsetup(ctx, options)
+       return conn.localOrLoginCluster().UserUnsetup(ctx, options)
 }
 
 func (conn *Conn) UserGet(ctx context.Context, options arvados.GetOptions) (arvados.User, error) {
index ef6b9195f10be05b1dd69bcbedda800df66dfdb3..7d8266a85cab13af302cc22dcae44800465dca08 100644 (file)
@@ -334,7 +334,7 @@ func (s *HandlerSuite) TestGetObjects(c *check.C) {
                "api_clients/" + arvadostest.TrustedWorkbenchAPIClientUUID:     nil,
                "api_client_authorizations/" + arvadostest.AdminTokenUUID:      nil,
                "authorized_keys/" + arvadostest.AdminAuthorizedKeysUUID:       nil,
-               "collections/" + arvadostest.CollectionWithUniqueWordsUUID:     map[string]bool{"href": true},
+               "collections/" + arvadostest.CollectionWithUniqueWordsUUID:     {"href": true},
                "containers/" + arvadostest.RunningContainerUUID:               nil,
                "container_requests/" + arvadostest.QueuedContainerRequestUUID: nil,
                "groups/" + arvadostest.AProjectUUID:                           nil,
@@ -343,7 +343,7 @@ func (s *HandlerSuite) TestGetObjects(c *check.C) {
                "logs/" + arvadostest.CrunchstatForRunningJobLogUUID:           nil,
                "nodes/" + arvadostest.IdleNodeUUID:                            nil,
                "repositories/" + arvadostest.ArvadosRepoUUID:                  nil,
-               "users/" + arvadostest.ActiveUserUUID:                          map[string]bool{"href": true},
+               "users/" + arvadostest.ActiveUserUUID:                          {"href": true},
                "virtual_machines/" + arvadostest.TestVMUUID:                   nil,
                "workflows/" + arvadostest.WorkflowWithDefinitionYAMLUUID:      nil,
        }
index a73f5f9f828574b1c234932432a2a4b63c769087..077493ffc836f58260f1abb19448323ea25f45e9 100644 (file)
@@ -139,10 +139,15 @@ func (s *IntegrationSuite) TearDownSuite(c *check.C) {
        }
 }
 
+// Get rpc connection struct initialized to communicate with the
+// specified cluster.
 func (s *IntegrationSuite) conn(clusterID string) *rpc.Conn {
        return rpc.NewConn(clusterID, s.testClusters[clusterID].controllerURL, true, rpc.PassthroughTokenProvider)
 }
 
+// Return Context, Arvados.Client and keepclient structs initialized
+// to connect to the specified cluster (by clusterID) using with the supplied
+// Arvados token.
 func (s *IntegrationSuite) clientsWithToken(clusterID string, token string) (context.Context, *arvados.Client, *keepclient.KeepClient) {
        cl := s.testClusters[clusterID].config.Clusters[clusterID]
        ctx := auth.NewContext(context.Background(), auth.NewCredentials(token))
@@ -159,7 +164,11 @@ func (s *IntegrationSuite) clientsWithToken(clusterID string, token string) (con
        return ctx, ac, kc
 }
 
-func (s *IntegrationSuite) userClients(rootctx context.Context, c *check.C, conn *rpc.Conn, clusterID string, activate bool) (context.Context, *arvados.Client, *keepclient.KeepClient) {
+// Log in as a user called "example", get the user's API token,
+// initialize clients with the API token, set up the user and
+// optionally activate the user.  Return client structs for
+// communicating with the cluster on behalf of the 'example' user.
+func (s *IntegrationSuite) userClients(rootctx context.Context, c *check.C, conn *rpc.Conn, clusterID string, activate bool) (context.Context, *arvados.Client, *keepclient.KeepClient, arvados.User) {
        login, err := conn.UserSessionCreate(rootctx, rpc.UserSessionCreateOptions{
                ReturnTo: ",https://example.com",
                AuthInfo: rpc.UserSessionAuthInfo{
@@ -189,18 +198,26 @@ func (s *IntegrationSuite) userClients(rootctx context.Context, c *check.C, conn
                        c.Fatalf("failed to activate user -- %#v", user)
                }
        }
-       return ctx, ac, kc
+       return ctx, ac, kc, user
 }
 
+// Return Context, arvados.Client and keepclient structs initialized
+// to communicate with the cluster as the system root user.
 func (s *IntegrationSuite) rootClients(clusterID string) (context.Context, *arvados.Client, *keepclient.KeepClient) {
        return s.clientsWithToken(clusterID, s.testClusters[clusterID].config.Clusters[clusterID].SystemRootToken)
 }
 
+// Return Context, arvados.Client and keepclient structs initialized
+// to communicate with the cluster as the anonymous user.
+func (s *IntegrationSuite) anonymousClients(clusterID string) (context.Context, *arvados.Client, *keepclient.KeepClient) {
+       return s.clientsWithToken(clusterID, s.testClusters[clusterID].config.Clusters[clusterID].Users.AnonymousUserToken)
+}
+
 func (s *IntegrationSuite) TestGetCollectionByPDH(c *check.C) {
        conn1 := s.conn("z1111")
        rootctx1, _, _ := s.rootClients("z1111")
        conn3 := s.conn("z3333")
-       userctx1, ac1, kc1 := s.userClients(rootctx1, c, conn1, "z1111", true)
+       userctx1, ac1, kc1, _ := s.userClients(rootctx1, c, conn1, "z1111", true)
 
        // Create the collection to find its PDH (but don't save it
        // anywhere yet)
@@ -234,12 +251,77 @@ func (s *IntegrationSuite) TestGetCollectionByPDH(c *check.C) {
        c.Check(coll.PortableDataHash, check.Equals, pdh)
 }
 
+func (s *IntegrationSuite) TestGetCollectionAsAnonymous(c *check.C) {
+       conn1 := s.conn("z1111")
+       conn3 := s.conn("z3333")
+       rootctx1, rootac1, rootkc1 := s.rootClients("z1111")
+       anonctx3, anonac3, _ := s.anonymousClients("z3333")
+
+       // Make sure anonymous token was set
+       c.Assert(anonac3.AuthToken, check.Not(check.Equals), "")
+
+       // Create the collection to find its PDH (but don't save it
+       // anywhere yet)
+       var coll1 arvados.Collection
+       fs1, err := coll1.FileSystem(rootac1, rootkc1)
+       c.Assert(err, check.IsNil)
+       f, err := fs1.OpenFile("test.txt", os.O_CREATE|os.O_RDWR, 0777)
+       c.Assert(err, check.IsNil)
+       _, err = io.WriteString(f, "IntegrationSuite.TestGetCollectionAsAnonymous")
+       c.Assert(err, check.IsNil)
+       err = f.Close()
+       c.Assert(err, check.IsNil)
+       mtxt, err := fs1.MarshalManifest(".")
+       c.Assert(err, check.IsNil)
+       pdh := arvados.PortableDataHash(mtxt)
+
+       // Save the collection on cluster z1111.
+       coll1, err = conn1.CollectionCreate(rootctx1, arvados.CreateOptions{Attrs: map[string]interface{}{
+               "manifest_text": mtxt,
+       }})
+       c.Assert(err, check.IsNil)
+
+       // Share it with the anonymous users group.
+       var outLink arvados.Link
+       err = rootac1.RequestAndDecode(&outLink, "POST", "/arvados/v1/links", nil,
+               map[string]interface{}{"link": map[string]interface{}{
+                       "link_class": "permission",
+                       "name":       "can_read",
+                       "tail_uuid":  "z1111-j7d0g-anonymouspublic",
+                       "head_uuid":  coll1.UUID,
+               },
+               })
+       c.Check(err, check.IsNil)
+
+       // Current user should be z3 anonymous user
+       outUser, err := anonac3.CurrentUser()
+       c.Check(err, check.IsNil)
+       c.Check(outUser.UUID, check.Equals, "z3333-tpzed-anonymouspublic")
+
+       // Get the token uuid
+       var outAuth arvados.APIClientAuthorization
+       err = anonac3.RequestAndDecode(&outAuth, "GET", "/arvados/v1/api_client_authorizations/current", nil, nil)
+       c.Check(err, check.IsNil)
+
+       // Make a v2 token of the z3 anonymous user, and use it on z1
+       _, anonac1, _ := s.clientsWithToken("z1111", outAuth.TokenV2())
+       outUser2, err := anonac1.CurrentUser()
+       c.Check(err, check.IsNil)
+       // z3 anonymous user will be mapped to the z1 anonymous user
+       c.Check(outUser2.UUID, check.Equals, "z1111-tpzed-anonymouspublic")
+
+       // Retrieve the collection (which is on z1) using anonymous from cluster z3333.
+       coll, err := conn3.CollectionGet(anonctx3, arvados.GetOptions{UUID: coll1.UUID})
+       c.Check(err, check.IsNil)
+       c.Check(coll.PortableDataHash, check.Equals, pdh)
+}
+
 // Get a token from the login cluster (z1111), use it to submit a
 // container request on z2222.
 func (s *IntegrationSuite) TestCreateContainerRequestWithFedToken(c *check.C) {
        conn1 := s.conn("z1111")
        rootctx1, _, _ := s.rootClients("z1111")
-       _, ac1, _ := s.userClients(rootctx1, c, conn1, "z1111", true)
+       _, ac1, _, _ := s.userClients(rootctx1, c, conn1, "z1111", true)
 
        // Use ac2 to get the discovery doc with a blank token, so the
        // SDK doesn't magically pass the z1111 token to z2222 before
@@ -310,7 +392,7 @@ func (s *IntegrationSuite) TestListUsers(c *check.C) {
        rootctx1, _, _ := s.rootClients("z1111")
        conn1 := s.conn("z1111")
        conn3 := s.conn("z3333")
-       userctx1, _, _ := s.userClients(rootctx1, c, conn1, "z1111", true)
+       userctx1, _, _, _ := s.userClients(rootctx1, c, conn1, "z1111", true)
 
        // Make sure LoginCluster is properly configured
        for cls := range s.testClusters {
@@ -374,3 +456,67 @@ func (s *IntegrationSuite) TestListUsers(c *check.C) {
        c.Assert(err, check.IsNil)
        c.Check(user1.IsActive, check.Equals, false)
 }
+
+func (s *IntegrationSuite) TestSetupUserWithVM(c *check.C) {
+       conn1 := s.conn("z1111")
+       conn3 := s.conn("z3333")
+       rootctx1, rootac1, _ := s.rootClients("z1111")
+
+       // Create user on LoginCluster z1111
+       _, _, _, user := s.userClients(rootctx1, c, conn1, "z1111", false)
+
+       // Make a new root token (because rootClients() uses SystemRootToken)
+       var outAuth arvados.APIClientAuthorization
+       err := rootac1.RequestAndDecode(&outAuth, "POST", "/arvados/v1/api_client_authorizations", nil, nil)
+       c.Check(err, check.IsNil)
+
+       // Make a v2 root token to communicate with z3333
+       rootctx3, rootac3, _ := s.clientsWithToken("z3333", outAuth.TokenV2())
+
+       // Create VM on z3333
+       var outVM arvados.VirtualMachine
+       err = rootac3.RequestAndDecode(&outVM, "POST", "/arvados/v1/virtual_machines", nil,
+               map[string]interface{}{"virtual_machine": map[string]interface{}{
+                       "hostname": "example",
+               },
+               })
+       c.Check(outVM.UUID[0:5], check.Equals, "z3333")
+       c.Check(err, check.IsNil)
+
+       // Make sure z3333 user list is up to date
+       _, err = conn3.UserList(rootctx3, arvados.ListOptions{Limit: 1000})
+       c.Check(err, check.IsNil)
+
+       // Try to set up user on z3333 with the VM
+       _, err = conn3.UserSetup(rootctx3, arvados.UserSetupOptions{UUID: user.UUID, VMUUID: outVM.UUID})
+       c.Check(err, check.IsNil)
+
+       var outLinks arvados.LinkList
+       err = rootac3.RequestAndDecode(&outLinks, "GET", "/arvados/v1/links", nil,
+               arvados.ListOptions{
+                       Limit: 1000,
+                       Filters: []arvados.Filter{
+                               {
+                                       Attr:     "tail_uuid",
+                                       Operator: "=",
+                                       Operand:  user.UUID,
+                               },
+                               {
+                                       Attr:     "head_uuid",
+                                       Operator: "=",
+                                       Operand:  outVM.UUID,
+                               },
+                               {
+                                       Attr:     "name",
+                                       Operator: "=",
+                                       Operand:  "can_login",
+                               },
+                               {
+                                       Attr:     "link_class",
+                                       Operator: "=",
+                                       Operand:  "permission",
+                               }}})
+       c.Check(err, check.IsNil)
+
+       c.Check(len(outLinks.Items), check.Equals, 1)
+}
index 5a3d803b89960df03dce638291af9cb338560d78..e9c6e82f6e2c8c6dac3255fce957ff28d199505f 100644 (file)
@@ -5,9 +5,10 @@
 package localdb
 
 import (
+       "bytes"
        "context"
-       "errors"
        "fmt"
+       "html/template"
 
        "git.arvados.org/arvados.git/lib/controller/rpc"
        "git.arvados.org/arvados.git/sdk/go/arvados"
@@ -25,7 +26,16 @@ func (ctrl *testLoginController) Logout(ctx context.Context, opts arvados.Logout
 }
 
 func (ctrl *testLoginController) Login(ctx context.Context, opts arvados.LoginOptions) (arvados.LoginResponse, error) {
-       return arvados.LoginResponse{}, errors.New("interactive login is not available")
+       tmpl, err := template.New("form").Parse(loginform)
+       if err != nil {
+               return arvados.LoginResponse{}, err
+       }
+       var buf bytes.Buffer
+       err = tmpl.Execute(&buf, opts)
+       if err != nil {
+               return arvados.LoginResponse{}, err
+       }
+       return arvados.LoginResponse{HTML: buf}, nil
 }
 
 func (ctrl *testLoginController) UserAuthenticate(ctx context.Context, opts arvados.UserAuthenticateOptions) (arvados.APIClientAuthorization, error) {
@@ -43,3 +53,52 @@ func (ctrl *testLoginController) UserAuthenticate(ctx context.Context, opts arva
        }
        return arvados.APIClientAuthorization{}, fmt.Errorf("authentication failed for user %q with password len=%d", opts.Username, len(opts.Password))
 }
+
+const loginform = `
+<!doctype html>
+<html>
+  <head><title>Arvados test login</title>
+    <script>
+      async function authenticate(event) {
+        event.preventDefault()
+       document.getElementById('error').innerHTML = ''
+       const resp = await fetch('/arvados/v1/users/authenticate', {
+         method: 'POST',
+         mode: 'same-origin',
+         headers: {'Content-Type': 'application/json'},
+         body: JSON.stringify({
+           username: document.getElementById('username').value,
+           password: document.getElementById('password').value,
+         }),
+       })
+       if (!resp.ok) {
+         document.getElementById('error').innerHTML = '<p>Authentication failed.</p><p>The "test login" users are defined in Clusters.[ClusterID].Login.Test.Users section of config.yml</p><p>If you are using arvbox, use "arvbox adduser" to add users.</p>'
+         return
+       }
+       var redir = document.getElementById('return_to').value
+       if (redir.indexOf('?') > 0) {
+         redir += '&'
+       } else {
+         redir += '?'
+       }
+        const respj = await resp.json()
+       document.location = redir + "api_token=" + respj.api_token
+      }
+    </script>
+  </head>
+  <body>
+    <h3>Arvados test login</h3>
+    <form method="POST">
+      <input id="return_to" type="hidden" name="return_to" value="{{.ReturnTo}}">
+      username <input id="username" type="text" name="username" size=16>
+      password <input id="password" type="password" name="password" size=16>
+      <input type="submit" value="Log in">
+      <br>
+      <p id="error"></p>
+    </form>
+  </body>
+  <script>
+    document.getElementsByTagName('form')[0].onsubmit = authenticate
+  </script>
+</html>
+`
index d2d651e205ca5b668d80ef838f74f5dc0ce9bc03..7589088899744efca9187e2cc9d3094b8d39db03 100644 (file)
@@ -92,3 +92,12 @@ func (s *TestUserSuite) TestLogin(c *check.C) {
                }
        }
 }
+
+func (s *TestUserSuite) TestLoginForm(c *check.C) {
+       resp, err := s.ctrl.Login(s.ctx, arvados.LoginOptions{
+               ReturnTo: "https://localhost:12345/example",
+       })
+       c.Check(err, check.IsNil)
+       c.Check(resp.HTML.String(), check.Matches, `(?ms).*<form method="POST".*`)
+       c.Check(resp.HTML.String(), check.Matches, `(?ms).*<input id="return_to" type="hidden" name="return_to" value="https://localhost:12345/example">.*`)
+}
index 729d8bdde09e7ee05d2766ef0a4d1ee72f01a8d1..cd98b64718a0b1f52702f4997f432d3d5210f353 100644 (file)
@@ -26,11 +26,11 @@ import (
 type TokenProvider func(context.Context) ([]string, error)
 
 func PassthroughTokenProvider(ctx context.Context) ([]string, error) {
-       if incoming, ok := auth.FromContext(ctx); !ok {
+       incoming, ok := auth.FromContext(ctx)
+       if !ok {
                return nil, errors.New("no token provided")
-       } else {
-               return incoming.Tokens, nil
        }
+       return incoming.Tokens, nil
 }
 
 type Conn struct {
@@ -170,9 +170,8 @@ func (conn *Conn) relativeToBaseURL(location string) string {
                u.User = nil
                u.Host = ""
                return u.String()
-       } else {
-               return location
        }
+       return location
 }
 
 func (conn *Conn) CollectionCreate(ctx context.Context, options arvados.CreateOptions) (arvados.Collection, error) {
index ff607bbb57ae0c927187699f0284924d751372a0..e1cda33f93ae7b477ba1930f9de62a8a9123f82d 100644 (file)
@@ -8,7 +8,6 @@ func semaphore(max int) (acquire, release func()) {
        if max > 0 {
                ch := make(chan bool, max)
                return func() { ch <- true }, func() { <-ch }
-       } else {
-               return func() {}, func() {}
        }
+       return func() {}, func() {}
 }
index b1497277f2d52971d7a2bbe4c24e90e583500360..1b0f168b88856e8251108f11e928321b5d642c0b 100644 (file)
@@ -195,9 +195,8 @@ func (cp *copier) walkMount(dest, src string, maxSymlinks int, walkMountsBelow b
        }
        if walkMountsBelow {
                return cp.walkMountsBelow(dest, src)
-       } else {
-               return nil
        }
+       return nil
 }
 
 func (cp *copier) walkMountsBelow(dest, src string) error {
index c8f171ca9b83f38d1e2870af16913a4175db6490..c125b27a5f0783fe757bcf29ac0b62674b68df95 100644 (file)
@@ -870,25 +870,24 @@ func (runner *ContainerRunner) LogNodeRecord() error {
                        return err
                }
                return w.Close()
-       } else {
-               // Dispatched via crunch-dispatch-slurm. Look up
-               // apiserver's node record corresponding to
-               // $SLURMD_NODENAME.
-               hostname := os.Getenv("SLURMD_NODENAME")
-               if hostname == "" {
-                       hostname, _ = os.Hostname()
-               }
-               _, err := runner.logAPIResponse("node", "nodes", map[string]interface{}{"filters": [][]string{{"hostname", "=", hostname}}}, func(resp interface{}) {
-                       // The "info" field has admin-only info when
-                       // obtained with a privileged token, and
-                       // should not be logged.
-                       node, ok := resp.(map[string]interface{})
-                       if ok {
-                               delete(node, "info")
-                       }
-               })
-               return err
        }
+       // Dispatched via crunch-dispatch-slurm. Look up
+       // apiserver's node record corresponding to
+       // $SLURMD_NODENAME.
+       hostname := os.Getenv("SLURMD_NODENAME")
+       if hostname == "" {
+               hostname, _ = os.Hostname()
+       }
+       _, err := runner.logAPIResponse("node", "nodes", map[string]interface{}{"filters": [][]string{{"hostname", "=", hostname}}}, func(resp interface{}) {
+               // The "info" field has admin-only info when
+               // obtained with a privileged token, and
+               // should not be logged.
+               node, ok := resp.(map[string]interface{})
+               if ok {
+                       delete(node, "info")
+               }
+       })
+       return err
 }
 
 func (runner *ContainerRunner) logAPIResponse(label, path string, params map[string]interface{}, munge func(interface{})) (logged bool, err error) {
@@ -1489,7 +1488,7 @@ func (runner *ContainerRunner) ContainerToken() (string, error) {
        return runner.token, nil
 }
 
-// UpdateContainerComplete updates the container record state on API
+// UpdateContainerFinal updates the container record state on API
 // server to "Complete" or "Cancelled"
 func (runner *ContainerRunner) UpdateContainerFinal() error {
        update := arvadosclient.Dict{}
index 42decff31d0cada0bb83ce66ba509aa5f5d13448..6e1850410b28bf3394ec4e29c4416a9551ec6d91 100644 (file)
@@ -215,6 +215,12 @@ func (s *DispatcherSuite) TestDispatchToStubDriver(c *check.C) {
        c.Check(resp.Body.String(), check.Matches, `(?ms).*boot_outcomes{outcome="success"} [^0].*`)
        c.Check(resp.Body.String(), check.Matches, `(?ms).*instances_disappeared{state="shutdown"} [^0].*`)
        c.Check(resp.Body.String(), check.Matches, `(?ms).*instances_disappeared{state="unknown"} 0\n.*`)
+       c.Check(resp.Body.String(), check.Matches, `(?ms).*time_to_ssh_seconds{quantile="0.95"} [0-9.]*`)
+       c.Check(resp.Body.String(), check.Matches, `(?ms).*time_to_ssh_seconds_count [0-9]*`)
+       c.Check(resp.Body.String(), check.Matches, `(?ms).*time_to_ssh_seconds_sum [0-9.]*`)
+       c.Check(resp.Body.String(), check.Matches, `(?ms).*time_to_ready_for_container_seconds{quantile="0.95"} [0-9.]*`)
+       c.Check(resp.Body.String(), check.Matches, `(?ms).*time_to_ready_for_container_seconds_count [0-9]*`)
+       c.Check(resp.Body.String(), check.Matches, `(?ms).*time_to_ready_for_container_seconds_sum [0-9.]*`)
 }
 
 func (s *DispatcherSuite) TestAPIPermissions(c *check.C) {
index f2a6c9263027dc2d765fc79720ec8bfeae2606e6..fe498d0484b0d41a0ceb0428aafc68a879033cc6 100644 (file)
@@ -17,7 +17,7 @@ import (
        "golang.org/x/crypto/ssh"
 )
 
-// Map of available cloud drivers.
+// Drivers is a map of available cloud drivers.
 // Clusters.*.Containers.CloudVMs.Driver configuration values
 // correspond to keys in this map.
 var Drivers = map[string]cloud.Driver{
@@ -180,7 +180,6 @@ func (inst instrumentedInstance) SetTags(tags cloud.InstanceTags) error {
 func boolLabelValue(v bool) string {
        if v {
                return "1"
-       } else {
-               return "0"
        }
+       return "0"
 }
index dddb974b326fbe7d61c280148e71f4f5c86e7abe..0e8e1dc2ec38f1394bb4c5899240e1e6cbf7cf1c 100644 (file)
@@ -51,36 +51,35 @@ tryrun:
                                overquota = sorted[i:]
                                break tryrun
                        }
+                       if sch.pool.KillContainer(ctr.UUID, "about to lock") {
+                               logger.Info("not locking: crunch-run process from previous attempt has not exited")
+                               continue
+                       }
                        go sch.lockContainer(logger, ctr.UUID)
                        unalloc[it]--
                case arvados.ContainerStateLocked:
                        if unalloc[it] > 0 {
                                unalloc[it]--
                        } else if sch.pool.AtQuota() {
-                               logger.Debug("not starting: AtQuota and no unalloc workers")
+                               // Don't let lower-priority containers
+                               // starve this one by using keeping
+                               // idle workers alive on different
+                               // instance types.
+                               logger.Debug("unlocking: AtQuota and no unalloc workers")
+                               sch.queue.Unlock(ctr.UUID)
                                overquota = sorted[i:]
                                break tryrun
+                       } else if logger.Info("creating new instance"); sch.pool.Create(it) {
+                               // Success. (Note pool.Create works
+                               // asynchronously and does its own
+                               // logging, so we don't need to.)
                        } else {
-                               logger.Info("creating new instance")
-                               if !sch.pool.Create(it) {
-                                       // (Note pool.Create works
-                                       // asynchronously and logs its
-                                       // own failures, so we don't
-                                       // need to log this as a
-                                       // failure.)
-
-                                       sch.queue.Unlock(ctr.UUID)
-                                       // Don't let lower-priority
-                                       // containers starve this one
-                                       // by using keeping idle
-                                       // workers alive on different
-                                       // instance types.  TODO:
-                                       // avoid getting starved here
-                                       // if instances of a specific
-                                       // type always fail.
-                                       overquota = sorted[i:]
-                                       break tryrun
-                               }
+                               // Failed despite not being at quota,
+                               // e.g., cloud ops throttled.  TODO:
+                               // avoid getting starved here if
+                               // instances of a specific type always
+                               // fail.
+                               continue
                        }
 
                        if dontstart[it] {
@@ -88,7 +87,7 @@ tryrun:
                                // a higher-priority container on the
                                // same instance type. Don't let this
                                // one sneak in ahead of it.
-                       } else if sch.pool.KillContainer(ctr.UUID, "about to lock") {
+                       } else if sch.pool.KillContainer(ctr.UUID, "about to start") {
                                logger.Info("not restarting yet: crunch-run process from previous attempt has not exited")
                        } else if sch.pool.StartContainer(it, ctr) {
                                // Success.
index 992edddfba6370198a16def5a6b57aed18575aa4..530eb5db93d02a9c4c2832f0fc090583c0432f93 100644 (file)
@@ -38,7 +38,7 @@ type stubPool struct {
        idle      map[arvados.InstanceType]int
        unknown   map[arvados.InstanceType]int
        running   map[string]time.Time
-       atQuota   bool
+       quota     int
        canCreate int
        creates   []arvados.InstanceType
        starts    []string
@@ -46,7 +46,11 @@ type stubPool struct {
        sync.Mutex
 }
 
-func (p *stubPool) AtQuota() bool               { return p.atQuota }
+func (p *stubPool) AtQuota() bool {
+       p.Lock()
+       defer p.Unlock()
+       return len(p.unalloc)+len(p.running)+len(p.unknown) >= p.quota
+}
 func (p *stubPool) Subscribe() <-chan struct{}  { return p.notify }
 func (p *stubPool) Unsubscribe(<-chan struct{}) {}
 func (p *stubPool) Running() map[string]time.Time {
@@ -122,11 +126,8 @@ var _ = check.Suite(&SchedulerSuite{})
 
 type SchedulerSuite struct{}
 
-// Assign priority=4 container to idle node. Create a new instance for
-// the priority=3 container. Don't try to start any priority<3
-// containers because priority=3 container didn't start
-// immediately. Don't try to create any other nodes after the failed
-// create.
+// Assign priority=4 container to idle node. Create new instances for
+// the priority=3, 2, 1 containers.
 func (*SchedulerSuite) TestUseIdleWorkers(c *check.C) {
        ctx := ctxlog.Context(context.Background(), ctxlog.TestLogger(c))
        queue := test.Queue{
@@ -172,6 +173,7 @@ func (*SchedulerSuite) TestUseIdleWorkers(c *check.C) {
        }
        queue.Update()
        pool := stubPool{
+               quota: 1000,
                unalloc: map[arvados.InstanceType]int{
                        test.InstanceType(1): 1,
                        test.InstanceType(2): 2,
@@ -184,7 +186,7 @@ func (*SchedulerSuite) TestUseIdleWorkers(c *check.C) {
                canCreate: 0,
        }
        New(ctx, &queue, &pool, time.Millisecond, time.Millisecond).runQueue()
-       c.Check(pool.creates, check.DeepEquals, []arvados.InstanceType{test.InstanceType(1)})
+       c.Check(pool.creates, check.DeepEquals, []arvados.InstanceType{test.InstanceType(1), test.InstanceType(1), test.InstanceType(1)})
        c.Check(pool.starts, check.DeepEquals, []string{test.ContainerUUID(4)})
        c.Check(pool.running, check.HasLen, 1)
        for uuid := range pool.running {
@@ -192,14 +194,14 @@ func (*SchedulerSuite) TestUseIdleWorkers(c *check.C) {
        }
 }
 
-// If Create() fails, shutdown some nodes, and don't call Create()
-// again.  Don't call Create() at all if AtQuota() is true.
+// If pool.AtQuota() is true, shutdown some unalloc nodes, and don't
+// call Create().
 func (*SchedulerSuite) TestShutdownAtQuota(c *check.C) {
        ctx := ctxlog.Context(context.Background(), ctxlog.TestLogger(c))
-       for quota := 0; quota < 2; quota++ {
+       for quota := 1; quota < 3; quota++ {
                c.Logf("quota=%d", quota)
                shouldCreate := []arvados.InstanceType{}
-               for i := 0; i < quota; i++ {
+               for i := 1; i < quota; i++ {
                        shouldCreate = append(shouldCreate, test.InstanceType(3))
                }
                queue := test.Queue{
@@ -227,7 +229,7 @@ func (*SchedulerSuite) TestShutdownAtQuota(c *check.C) {
                }
                queue.Update()
                pool := stubPool{
-                       atQuota: quota == 0,
+                       quota: quota,
                        unalloc: map[arvados.InstanceType]int{
                                test.InstanceType(2): 2,
                        },
@@ -241,8 +243,13 @@ func (*SchedulerSuite) TestShutdownAtQuota(c *check.C) {
                }
                New(ctx, &queue, &pool, time.Millisecond, time.Millisecond).runQueue()
                c.Check(pool.creates, check.DeepEquals, shouldCreate)
-               c.Check(pool.starts, check.DeepEquals, []string{})
-               c.Check(pool.shutdowns, check.Not(check.Equals), 0)
+               if len(shouldCreate) == 0 {
+                       c.Check(pool.starts, check.DeepEquals, []string{})
+                       c.Check(pool.shutdowns, check.Not(check.Equals), 0)
+               } else {
+                       c.Check(pool.starts, check.DeepEquals, []string{test.ContainerUUID(2)})
+                       c.Check(pool.shutdowns, check.Equals, 0)
+               }
        }
 }
 
@@ -251,6 +258,7 @@ func (*SchedulerSuite) TestShutdownAtQuota(c *check.C) {
 func (*SchedulerSuite) TestStartWhileCreating(c *check.C) {
        ctx := ctxlog.Context(context.Background(), ctxlog.TestLogger(c))
        pool := stubPool{
+               quota: 1000,
                unalloc: map[arvados.InstanceType]int{
                        test.InstanceType(1): 2,
                        test.InstanceType(2): 2,
@@ -345,6 +353,7 @@ func (*SchedulerSuite) TestStartWhileCreating(c *check.C) {
 func (*SchedulerSuite) TestKillNonexistentContainer(c *check.C) {
        ctx := ctxlog.Context(context.Background(), ctxlog.TestLogger(c))
        pool := stubPool{
+               quota: 1000,
                unalloc: map[arvados.InstanceType]int{
                        test.InstanceType(2): 0,
                },
@@ -352,7 +361,7 @@ func (*SchedulerSuite) TestKillNonexistentContainer(c *check.C) {
                        test.InstanceType(2): 0,
                },
                running: map[string]time.Time{
-                       test.ContainerUUID(2): time.Time{},
+                       test.ContainerUUID(2): {},
                },
        }
        queue := test.Queue{
index 74b84122f286d912d8f8ef392e3eb860e6b1831d..8a8f686e168ff6372f0c19813d31cd2ced26509c 100644 (file)
@@ -185,18 +185,18 @@ func (q *Queue) Notify(upd arvados.Container) bool {
 }
 
 var allowContainerUpdate = map[arvados.ContainerState]map[arvados.ContainerState]bool{
-       arvados.ContainerStateQueued: map[arvados.ContainerState]bool{
+       arvados.ContainerStateQueued: {
                arvados.ContainerStateQueued:    true,
                arvados.ContainerStateLocked:    true,
                arvados.ContainerStateCancelled: true,
        },
-       arvados.ContainerStateLocked: map[arvados.ContainerState]bool{
+       arvados.ContainerStateLocked: {
                arvados.ContainerStateQueued:    true,
                arvados.ContainerStateLocked:    true,
                arvados.ContainerStateRunning:   true,
                arvados.ContainerStateCancelled: true,
        },
-       arvados.ContainerStateRunning: map[arvados.ContainerState]bool{
+       arvados.ContainerStateRunning: {
                arvados.ContainerStateRunning:   true,
                arvados.ContainerStateCancelled: true,
                arvados.ContainerStateComplete:  true,
index f6e06d3f7cebd05a97c8851f65d8d79812d18297..132bd4d695f0ef88095951b151be592029c31328 100644 (file)
@@ -118,10 +118,8 @@ func (sis *StubInstanceSet) Create(it arvados.InstanceType, image cloud.ImageID,
        }
        if sis.allowCreateCall.After(time.Now()) {
                return nil, RateLimitError{sis.allowCreateCall}
-       } else {
-               sis.allowCreateCall = time.Now().Add(sis.driver.MinTimeBetweenCreateCalls)
        }
-
+       sis.allowCreateCall = time.Now().Add(sis.driver.MinTimeBetweenCreateCalls)
        ak := sis.driver.AuthorizedKeys
        if authKey != nil {
                ak = append([]ssh.PublicKey{authKey}, ak...)
@@ -154,9 +152,8 @@ func (sis *StubInstanceSet) Instances(cloud.InstanceTags) ([]cloud.Instance, err
        defer sis.mtx.RUnlock()
        if sis.allowInstancesCall.After(time.Now()) {
                return nil, RateLimitError{sis.allowInstancesCall}
-       } else {
-               sis.allowInstancesCall = time.Now().Add(sis.driver.MinTimeBetweenInstancesCalls)
        }
+       sis.allowInstancesCall = time.Now().Add(sis.driver.MinTimeBetweenInstancesCalls)
        var r []cloud.Instance
        for _, ss := range sis.servers {
                r = append(r, ss.Instance())
@@ -277,13 +274,11 @@ func (svm *StubVM) Exec(env map[string]string, command string, stdin io.Reader,
                                svm.Lock()
                                defer svm.Unlock()
                                if svm.running[uuid] != pid {
-                                       if !completed {
-                                               bugf := svm.sis.driver.Bugf
-                                               if bugf == nil {
-                                                       bugf = logger.Warnf
-                                               }
-                                               bugf("[test] StubDriver bug or caller bug: pid %d exiting, running[%s]==%d", pid, uuid, svm.running[uuid])
+                                       bugf := svm.sis.driver.Bugf
+                                       if bugf == nil {
+                                               bugf = logger.Warnf
                                        }
+                                       bugf("[test] StubDriver bug or caller bug: pid %d exiting, running[%s]==%d", pid, uuid, svm.running[uuid])
                                } else {
                                        delete(svm.running, uuid)
                                }
@@ -308,7 +303,7 @@ func (svm *StubVM) Exec(env map[string]string, command string, stdin io.Reader,
                        time.Sleep(time.Duration(math_rand.Float64()*20) * time.Millisecond)
 
                        svm.Lock()
-                       killed := svm.running[uuid] != pid
+                       killed := svm.killing[uuid]
                        svm.Unlock()
                        if killed || wantCrashEarly {
                                return
@@ -348,21 +343,9 @@ func (svm *StubVM) Exec(env map[string]string, command string, stdin io.Reader,
        }
        if strings.HasPrefix(command, "crunch-run --kill ") {
                svm.Lock()
-               pid, running := svm.running[uuid]
-               if running && !svm.killing[uuid] {
+               _, running := svm.running[uuid]
+               if running {
                        svm.killing[uuid] = true
-                       go func() {
-                               time.Sleep(time.Duration(math_rand.Float64()*30) * time.Millisecond)
-                               svm.Lock()
-                               defer svm.Unlock()
-                               if svm.running[uuid] == pid {
-                                       // Kill only if the running entry
-                                       // hasn't since been killed and
-                                       // replaced with a different one.
-                                       delete(svm.running, uuid)
-                               }
-                               delete(svm.killing, uuid)
-                       }()
                        svm.Unlock()
                        time.Sleep(time.Duration(math_rand.Float64()*2) * time.Millisecond)
                        svm.Lock()
@@ -372,10 +355,9 @@ func (svm *StubVM) Exec(env map[string]string, command string, stdin io.Reader,
                if running {
                        fmt.Fprintf(stderr, "%s: container is running\n", uuid)
                        return 1
-               } else {
-                       fmt.Fprintf(stderr, "%s: container is not running\n", uuid)
-                       return 0
                }
+               fmt.Fprintf(stderr, "%s: container is not running\n", uuid)
+               return 0
        }
        if command == "true" {
                return 0
index 12bc1cdd71636263cebc0c8f21bd283d791aec04..086887cb44176f05c9446341a68d7176dd5ed7aa 100644 (file)
@@ -96,27 +96,28 @@ func duration(conf arvados.Duration, def time.Duration) time.Duration {
 // cluster configuration.
 func NewPool(logger logrus.FieldLogger, arvClient *arvados.Client, reg *prometheus.Registry, instanceSetID cloud.InstanceSetID, instanceSet cloud.InstanceSet, newExecutor func(cloud.Instance) Executor, installPublicKey ssh.PublicKey, cluster *arvados.Cluster) *Pool {
        wp := &Pool{
-               logger:             logger,
-               arvClient:          arvClient,
-               instanceSetID:      instanceSetID,
-               instanceSet:        &throttledInstanceSet{InstanceSet: instanceSet},
-               newExecutor:        newExecutor,
-               bootProbeCommand:   cluster.Containers.CloudVMs.BootProbeCommand,
-               runnerSource:       cluster.Containers.CloudVMs.DeployRunnerBinary,
-               imageID:            cloud.ImageID(cluster.Containers.CloudVMs.ImageID),
-               instanceTypes:      cluster.InstanceTypes,
-               maxProbesPerSecond: cluster.Containers.CloudVMs.MaxProbesPerSecond,
-               probeInterval:      duration(cluster.Containers.CloudVMs.ProbeInterval, defaultProbeInterval),
-               syncInterval:       duration(cluster.Containers.CloudVMs.SyncInterval, defaultSyncInterval),
-               timeoutIdle:        duration(cluster.Containers.CloudVMs.TimeoutIdle, defaultTimeoutIdle),
-               timeoutBooting:     duration(cluster.Containers.CloudVMs.TimeoutBooting, defaultTimeoutBooting),
-               timeoutProbe:       duration(cluster.Containers.CloudVMs.TimeoutProbe, defaultTimeoutProbe),
-               timeoutShutdown:    duration(cluster.Containers.CloudVMs.TimeoutShutdown, defaultTimeoutShutdown),
-               timeoutTERM:        duration(cluster.Containers.CloudVMs.TimeoutTERM, defaultTimeoutTERM),
-               timeoutSignal:      duration(cluster.Containers.CloudVMs.TimeoutSignal, defaultTimeoutSignal),
-               installPublicKey:   installPublicKey,
-               tagKeyPrefix:       cluster.Containers.CloudVMs.TagKeyPrefix,
-               stop:               make(chan bool),
+               logger:                         logger,
+               arvClient:                      arvClient,
+               instanceSetID:                  instanceSetID,
+               instanceSet:                    &throttledInstanceSet{InstanceSet: instanceSet},
+               newExecutor:                    newExecutor,
+               bootProbeCommand:               cluster.Containers.CloudVMs.BootProbeCommand,
+               runnerSource:                   cluster.Containers.CloudVMs.DeployRunnerBinary,
+               imageID:                        cloud.ImageID(cluster.Containers.CloudVMs.ImageID),
+               instanceTypes:                  cluster.InstanceTypes,
+               maxProbesPerSecond:             cluster.Containers.CloudVMs.MaxProbesPerSecond,
+               maxConcurrentInstanceCreateOps: cluster.Containers.CloudVMs.MaxConcurrentInstanceCreateOps,
+               probeInterval:                  duration(cluster.Containers.CloudVMs.ProbeInterval, defaultProbeInterval),
+               syncInterval:                   duration(cluster.Containers.CloudVMs.SyncInterval, defaultSyncInterval),
+               timeoutIdle:                    duration(cluster.Containers.CloudVMs.TimeoutIdle, defaultTimeoutIdle),
+               timeoutBooting:                 duration(cluster.Containers.CloudVMs.TimeoutBooting, defaultTimeoutBooting),
+               timeoutProbe:                   duration(cluster.Containers.CloudVMs.TimeoutProbe, defaultTimeoutProbe),
+               timeoutShutdown:                duration(cluster.Containers.CloudVMs.TimeoutShutdown, defaultTimeoutShutdown),
+               timeoutTERM:                    duration(cluster.Containers.CloudVMs.TimeoutTERM, defaultTimeoutTERM),
+               timeoutSignal:                  duration(cluster.Containers.CloudVMs.TimeoutSignal, defaultTimeoutSignal),
+               installPublicKey:               installPublicKey,
+               tagKeyPrefix:                   cluster.Containers.CloudVMs.TagKeyPrefix,
+               stop:                           make(chan bool),
        }
        wp.registerMetrics(reg)
        go func() {
@@ -132,26 +133,27 @@ func NewPool(logger logrus.FieldLogger, arvClient *arvados.Client, reg *promethe
 // zero Pool should not be used. Call NewPool to create a new Pool.
 type Pool struct {
        // configuration
-       logger             logrus.FieldLogger
-       arvClient          *arvados.Client
-       instanceSetID      cloud.InstanceSetID
-       instanceSet        *throttledInstanceSet
-       newExecutor        func(cloud.Instance) Executor
-       bootProbeCommand   string
-       runnerSource       string
-       imageID            cloud.ImageID
-       instanceTypes      map[string]arvados.InstanceType
-       syncInterval       time.Duration
-       probeInterval      time.Duration
-       maxProbesPerSecond int
-       timeoutIdle        time.Duration
-       timeoutBooting     time.Duration
-       timeoutProbe       time.Duration
-       timeoutShutdown    time.Duration
-       timeoutTERM        time.Duration
-       timeoutSignal      time.Duration
-       installPublicKey   ssh.PublicKey
-       tagKeyPrefix       string
+       logger                         logrus.FieldLogger
+       arvClient                      *arvados.Client
+       instanceSetID                  cloud.InstanceSetID
+       instanceSet                    *throttledInstanceSet
+       newExecutor                    func(cloud.Instance) Executor
+       bootProbeCommand               string
+       runnerSource                   string
+       imageID                        cloud.ImageID
+       instanceTypes                  map[string]arvados.InstanceType
+       syncInterval                   time.Duration
+       probeInterval                  time.Duration
+       maxProbesPerSecond             int
+       maxConcurrentInstanceCreateOps int
+       timeoutIdle                    time.Duration
+       timeoutBooting                 time.Duration
+       timeoutProbe                   time.Duration
+       timeoutShutdown                time.Duration
+       timeoutTERM                    time.Duration
+       timeoutSignal                  time.Duration
+       installPublicKey               ssh.PublicKey
+       tagKeyPrefix                   string
 
        // private state
        subscribers  map[<-chan struct{}]chan<- struct{}
@@ -168,16 +170,15 @@ type Pool struct {
        runnerMD5    [md5.Size]byte
        runnerCmd    string
 
-       throttleCreate    throttle
-       throttleInstances throttle
-
-       mContainersRunning prometheus.Gauge
-       mInstances         *prometheus.GaugeVec
-       mInstancesPrice    *prometheus.GaugeVec
-       mVCPUs             *prometheus.GaugeVec
-       mMemory            *prometheus.GaugeVec
-       mBootOutcomes      *prometheus.CounterVec
-       mDisappearances    *prometheus.CounterVec
+       mContainersRunning       prometheus.Gauge
+       mInstances               *prometheus.GaugeVec
+       mInstancesPrice          *prometheus.GaugeVec
+       mVCPUs                   *prometheus.GaugeVec
+       mMemory                  *prometheus.GaugeVec
+       mBootOutcomes            *prometheus.CounterVec
+       mDisappearances          *prometheus.CounterVec
+       mTimeToSSH               prometheus.Summary
+       mTimeToReadyForContainer prometheus.Summary
 }
 
 type createCall struct {
@@ -298,7 +299,19 @@ func (wp *Pool) Create(it arvados.InstanceType) bool {
        }
        wp.mtx.Lock()
        defer wp.mtx.Unlock()
-       if time.Now().Before(wp.atQuotaUntil) || wp.throttleCreate.Error() != nil {
+       if time.Now().Before(wp.atQuotaUntil) || wp.instanceSet.throttleCreate.Error() != nil {
+               return false
+       }
+       // The maxConcurrentInstanceCreateOps knob throttles the number of node create
+       // requests in flight. It was added to work around a limitation in Azure's
+       // managed disks, which support no more than 20 concurrent node creation
+       // requests from a single disk image (cf.
+       // https://docs.microsoft.com/en-us/azure/virtual-machines/linux/capture-image).
+       // The code assumes that node creation, from Azure's perspective, means the
+       // period until the instance appears in the "get all instances" list.
+       if wp.maxConcurrentInstanceCreateOps > 0 && len(wp.creating) >= wp.maxConcurrentInstanceCreateOps {
+               logger.Info("reached MaxConcurrentInstanceCreateOps")
+               wp.instanceSet.throttleCreate.ErrorUntil(errors.New("reached MaxConcurrentInstanceCreateOps"), time.Now().Add(5*time.Second), wp.notify)
                return false
        }
        now := time.Now()
@@ -312,7 +325,7 @@ func (wp *Pool) Create(it arvados.InstanceType) bool {
                        wp.tagKeyPrefix + tagKeyIdleBehavior:   string(IdleBehaviorRun),
                        wp.tagKeyPrefix + tagKeyInstanceSecret: secret,
                }
-               initCmd := TagVerifier{nil, secret}.InitCommand()
+               initCmd := TagVerifier{nil, secret, nil}.InitCommand()
                inst, err := wp.instanceSet.Create(it, wp.imageID, tags, initCmd, wp.installPublicKey)
                wp.mtx.Lock()
                defer wp.mtx.Unlock()
@@ -356,6 +369,23 @@ func (wp *Pool) SetIdleBehavior(id cloud.InstanceID, idleBehavior IdleBehavior)
        return nil
 }
 
+// Successful connection to the SSH daemon, update the mTimeToSSH metric
+func (wp *Pool) reportSSHConnected(inst cloud.Instance) {
+       wp.mtx.Lock()
+       defer wp.mtx.Unlock()
+       wkr := wp.workers[inst.ID()]
+       if wkr.state != StateBooting || !wkr.firstSSHConnection.IsZero() {
+               // the node is not in booting state (can happen if a-d-c is restarted) OR
+               // this is not the first SSH connection
+               return
+       }
+
+       wkr.firstSSHConnection = time.Now()
+       if wp.mTimeToSSH != nil {
+               wp.mTimeToSSH.Observe(wkr.firstSSHConnection.Sub(wkr.appeared).Seconds())
+       }
+}
+
 // Add or update worker attached to the given instance.
 //
 // The second return value is true if a new worker is created.
@@ -366,7 +396,7 @@ func (wp *Pool) SetIdleBehavior(id cloud.InstanceID, idleBehavior IdleBehavior)
 // Caller must have lock.
 func (wp *Pool) updateWorker(inst cloud.Instance, it arvados.InstanceType) (*worker, bool) {
        secret := inst.Tags()[wp.tagKeyPrefix+tagKeyInstanceSecret]
-       inst = TagVerifier{inst, secret}
+       inst = TagVerifier{Instance: inst, Secret: secret, ReportVerified: wp.reportSSHConnected}
        id := inst.ID()
        if wkr := wp.workers[id]; wkr != nil {
                wkr.executor.SetTarget(inst)
@@ -615,6 +645,22 @@ func (wp *Pool) registerMetrics(reg *prometheus.Registry) {
                wp.mDisappearances.WithLabelValues(v).Add(0)
        }
        reg.MustRegister(wp.mDisappearances)
+       wp.mTimeToSSH = prometheus.NewSummary(prometheus.SummaryOpts{
+               Namespace:  "arvados",
+               Subsystem:  "dispatchcloud",
+               Name:       "instances_time_to_ssh_seconds",
+               Help:       "Number of seconds between instance creation and the first successful SSH connection.",
+               Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.95: 0.005, 0.99: 0.001},
+       })
+       reg.MustRegister(wp.mTimeToSSH)
+       wp.mTimeToReadyForContainer = prometheus.NewSummary(prometheus.SummaryOpts{
+               Namespace:  "arvados",
+               Subsystem:  "dispatchcloud",
+               Name:       "instances_time_to_ready_for_container_seconds",
+               Help:       "Number of seconds between the first successful SSH connection and ready to run a container.",
+               Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.95: 0.005, 0.99: 0.001},
+       })
+       reg.MustRegister(wp.mTimeToReadyForContainer)
 }
 
 func (wp *Pool) runMetrics() {
index 0c173c107d4a248ec38ca635f5fa0ac219af6a4b..a85f7383ab3cdc59fcc1bd0e7ad936703666ca2f 100644 (file)
@@ -199,6 +199,46 @@ func (suite *PoolSuite) TestDrain(c *check.C) {
        }
 }
 
+func (suite *PoolSuite) TestNodeCreateThrottle(c *check.C) {
+       logger := ctxlog.TestLogger(c)
+       driver := test.StubDriver{HoldCloudOps: true}
+       instanceSet, err := driver.InstanceSet(nil, "test-instance-set-id", nil, logger)
+       c.Assert(err, check.IsNil)
+
+       type1 := test.InstanceType(1)
+       pool := &Pool{
+               logger:                         logger,
+               instanceSet:                    &throttledInstanceSet{InstanceSet: instanceSet},
+               maxConcurrentInstanceCreateOps: 1,
+               instanceTypes: arvados.InstanceTypeMap{
+                       type1.Name: type1,
+               },
+       }
+
+       c.Check(pool.Unallocated()[type1], check.Equals, 0)
+       res := pool.Create(type1)
+       c.Check(pool.Unallocated()[type1], check.Equals, 1)
+       c.Check(res, check.Equals, true)
+
+       res = pool.Create(type1)
+       c.Check(pool.Unallocated()[type1], check.Equals, 1)
+       c.Check(res, check.Equals, false)
+
+       pool.instanceSet.throttleCreate.err = nil
+       pool.maxConcurrentInstanceCreateOps = 2
+
+       res = pool.Create(type1)
+       c.Check(pool.Unallocated()[type1], check.Equals, 2)
+       c.Check(res, check.Equals, true)
+
+       pool.instanceSet.throttleCreate.err = nil
+       pool.maxConcurrentInstanceCreateOps = 0
+
+       res = pool.Create(type1)
+       c.Check(pool.Unallocated()[type1], check.Equals, 3)
+       c.Check(res, check.Equals, true)
+}
+
 func (suite *PoolSuite) TestCreateUnallocShutdown(c *check.C) {
        logger := ctxlog.TestLogger(c)
        driver := test.StubDriver{HoldCloudOps: true}
index 597950fca699a9834795dfbf25f6957cd9fdc92b..559bb28973d27fc81662a262a00cefe5e020627c 100644 (file)
@@ -23,7 +23,8 @@ var (
 
 type TagVerifier struct {
        cloud.Instance
-       Secret string
+       Secret         string
+       ReportVerified func(cloud.Instance)
 }
 
 func (tv TagVerifier) InitCommand() cloud.InitCommand {
@@ -31,6 +32,9 @@ func (tv TagVerifier) InitCommand() cloud.InitCommand {
 }
 
 func (tv TagVerifier) VerifyHostKey(pubKey ssh.PublicKey, client *ssh.Client) error {
+       if tv.ReportVerified != nil {
+               tv.ReportVerified(tv.Instance)
+       }
        if err := tv.Instance.VerifyHostKey(pubKey, client); err != cloud.ErrNotImplemented || tv.Secret == "" {
                // If the wrapped instance indicates it has a way to
                // verify the key, return that decision.
index 5d2360f3ccc64671b7193b281a7807d7b70de23b..9199d4bafe764d806312638328cf13fd3b422e4d 100644 (file)
@@ -103,11 +103,13 @@ type worker struct {
        updated             time.Time
        busy                time.Time
        destroyed           time.Time
+       firstSSHConnection  time.Time
        lastUUID            string
        running             map[string]*remoteRunner // remember to update state idle<->running when this changes
        starting            map[string]*remoteRunner // remember to update state idle<->running when this changes
        probing             chan struct{}
        bootOutcomeReported bool
+       timeToReadyReported bool
 }
 
 func (wkr *worker) onUnkillable(uuid string) {
@@ -140,6 +142,17 @@ func (wkr *worker) reportBootOutcome(outcome BootOutcome) {
        wkr.bootOutcomeReported = true
 }
 
+// caller must have lock.
+func (wkr *worker) reportTimeBetweenFirstSSHAndReadyForContainer() {
+       if wkr.timeToReadyReported {
+               return
+       }
+       if wkr.wp.mTimeToSSH != nil {
+               wkr.wp.mTimeToReadyForContainer.Observe(time.Since(wkr.firstSSHConnection).Seconds())
+       }
+       wkr.timeToReadyReported = true
+}
+
 // caller must have lock.
 func (wkr *worker) setIdleBehavior(idleBehavior IdleBehavior) {
        wkr.logger.WithField("IdleBehavior", idleBehavior).Info("set idle behavior")
@@ -313,6 +326,9 @@ func (wkr *worker) probeAndUpdate() {
 
        // Update state if this was the first successful boot-probe.
        if booted && (wkr.state == StateUnknown || wkr.state == StateBooting) {
+               if wkr.state == StateBooting {
+                       wkr.reportTimeBetweenFirstSSHAndReadyForContainer()
+               }
                // Note: this will change again below if
                // len(wkr.starting)+len(wkr.running) > 0.
                wkr.state = StateIdle
index dce1bd4d0247d2f56af8902f844814633b739b25..8a3fa3173a9dd98baa5d8aa1ab74e19fe4bceb6d 100644 (file)
@@ -240,6 +240,12 @@ $graph:
         MiB.  Default 256 MiB.  Will be added on to the RAM request
         when determining node size to request.
       jsonldPredicate: "http://arvados.org/cwl#RuntimeConstraints/keep_cache"
+    acrContainerImage:
+      type: string?
+      doc: |
+        The container image containing the correct version of
+        arvados-cwl-runner to use when invoking the workflow on
+        Arvados.
 
 - name: ClusterTarget
   type: record
index b9b9e616510817a3731ce308c74d2cf8771ccb6c..95ed0a75bc69bfe94929ce0e2ee80adf6dcec7e6 100644 (file)
@@ -184,6 +184,12 @@ $graph:
         MiB.  Default 256 MiB.  Will be added on to the RAM request
         when determining node size to request.
       jsonldPredicate: "http://arvados.org/cwl#RuntimeConstraints/keep_cache"
+    acrContainerImage:
+      type: string?
+      doc: |
+        The container image containing the correct version of
+        arvados-cwl-runner to use when invoking the workflow on
+        Arvados.
 
 - name: ClusterTarget
   type: record
index b9b9e616510817a3731ce308c74d2cf8771ccb6c..95ed0a75bc69bfe94929ce0e2ee80adf6dcec7e6 100644 (file)
@@ -184,6 +184,12 @@ $graph:
         MiB.  Default 256 MiB.  Will be added on to the RAM request
         when determining node size to request.
       jsonldPredicate: "http://arvados.org/cwl#RuntimeConstraints/keep_cache"
+    acrContainerImage:
+      type: string?
+      doc: |
+        The container image containing the correct version of
+        arvados-cwl-runner to use when invoking the workflow on
+        Arvados.
 
 - name: ClusterTarget
   type: record
index 97c5fafe792fc06ce099e6a9bc6934671ace580d..56c6f39e9d76654816e60b30595841f80b106d34 100644 (file)
@@ -24,9 +24,10 @@ import ruamel.yaml as yaml
 
 from .runner import (upload_dependencies, packed_workflow, upload_workflow_collection,
                      trim_anonymous_location, remove_redundant_fields, discover_secondary_files,
-                     make_builder)
+                     make_builder, arvados_jobs_image)
 from .pathmapper import ArvPathMapper, trim_listing
 from .arvtool import ArvadosCommandTool, set_cluster_target
+from ._version import __version__
 
 from .perf import Perf
 
@@ -37,7 +38,8 @@ max_res_pars = ("coresMin", "coresMax", "ramMin", "ramMax", "tmpdirMin", "tmpdir
 sum_res_pars = ("outdirMin", "outdirMax")
 
 def upload_workflow(arvRunner, tool, job_order, project_uuid, uuid=None,
-                    submit_runner_ram=0, name=None, merged_map=None):
+                    submit_runner_ram=0, name=None, merged_map=None,
+                    submit_runner_image=None):
 
     packed = packed_workflow(arvRunner, tool, merged_map)
 
@@ -57,18 +59,25 @@ def upload_workflow(arvRunner, tool, job_order, project_uuid, uuid=None,
     upload_dependencies(arvRunner, name, tool.doc_loader,
                         packed, tool.tool["id"], False)
 
+    wf_runner_resources = None
+
+    hints = main.get("hints", [])
+    found = False
+    for h in hints:
+        if h["class"] == "http://arvados.org/cwl#WorkflowRunnerResources":
+            wf_runner_resources = h
+            found = True
+            break
+    if not found:
+        wf_runner_resources = {"class": "http://arvados.org/cwl#WorkflowRunnerResources"}
+        hints.append(wf_runner_resources)
+
+    wf_runner_resources["acrContainerImage"] = arvados_jobs_image(arvRunner, submit_runner_image or "arvados/jobs:"+__version__)
+
     if submit_runner_ram:
-        hints = main.get("hints", [])
-        found = False
-        for h in hints:
-            if h["class"] == "http://arvados.org/cwl#WorkflowRunnerResources":
-                h["ramMin"] = submit_runner_ram
-                found = True
-                break
-        if not found:
-            hints.append({"class": "http://arvados.org/cwl#WorkflowRunnerResources",
-                          "ramMin": submit_runner_ram})
-        main["hints"] = hints
+        wf_runner_resources["ramMin"] = submit_runner_ram
+
+    main["hints"] = hints
 
     body = {
         "workflow": {
index e8d1347ddfeec7545b8ab9740de38b78b55b4e75..9ba798ec640f3d0caaecdaf114bafbdb36a1b4ad 100644 (file)
@@ -596,7 +596,8 @@ The 'jobs' API is no longer supported.
                                         uuid=existing_uuid,
                                         submit_runner_ram=runtimeContext.submit_runner_ram,
                                         name=runtimeContext.name,
-                                        merged_map=merged_map),
+                                        merged_map=merged_map,
+                                        submit_runner_image=runtimeContext.submit_runner_image),
                         "success")
 
         self.apply_reqs(job_order, tool)
index 66176b940b0eff5497cbbf995f78ddb65cf0ae5e..50ebd25ff858dbef9cba4520f125d398d34a1e18 100644 (file)
@@ -2,10 +2,9 @@
 #
 # SPDX-License-Identifier: Apache-2.0
 
+fpm_depends+=(nodejs)
+
 case "$TARGET" in
-    debian8)
-        fpm_depends+=(libgnutls-deb0-28 libcurl3-gnutls)
-        ;;
     debian9 | ubuntu1604)
         fpm_depends+=(libcurl3-gnutls)
         ;;
index 9bf1c20aabc6591a4b1d00282e9c871456fca219..1054d8f29bdb627c6b8710429534dada68edddea 100644 (file)
@@ -6,6 +6,12 @@
     "$graph": [
         {
             "class": "Workflow",
+            "hints": [
+                {
+                    "acrContainerImage": "999999999999999999999999999999d3+99",
+                    "class": "http://arvados.org/cwl#WorkflowRunnerResources"
+                }
+            ],
             "id": "#main",
             "inputs": [],
             "outputs": [],
@@ -82,4 +88,4 @@
         }
     ],
     "cwlVersion": "v1.0"
-}
\ No newline at end of file
+}
index 0698db70ff68534ba70aa4176c5487f308cf2559..4da545bf36b91c6f96ba3ec8657e3d0a2a4db265 100644 (file)
@@ -1354,7 +1354,7 @@ class TestSubmit(unittest.TestCase):
 class TestCreateWorkflow(unittest.TestCase):
     existing_workflow_uuid = "zzzzz-7fd4e-validworkfloyml"
     expect_workflow = StripYAMLComments(
-        open("tests/wf/expect_packed.cwl").read())
+        open("tests/wf/expect_upload_packed.cwl").read().rstrip())
 
     @stubs
     def test_create(self, stubs):
@@ -1472,7 +1472,7 @@ class TestCreateWorkflow(unittest.TestCase):
             stubs.capture_stdout, sys.stderr, api_client=stubs.api)
 
         toolfile = "tests/collection_per_tool/collection_per_tool_packed.cwl"
-        expect_workflow = StripYAMLComments(open(toolfile).read())
+        expect_workflow = StripYAMLComments(open(toolfile).read().rstrip())
 
         body = {
             "workflow": {
diff --git a/sdk/cwl/tests/wf/expect_upload_packed.cwl b/sdk/cwl/tests/wf/expect_upload_packed.cwl
new file mode 100644 (file)
index 0000000..9a50fc8
--- /dev/null
@@ -0,0 +1,100 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+
+{
+    "$graph": [
+        {
+            "baseCommand": "cat",
+            "class": "CommandLineTool",
+            "id": "#submit_tool.cwl",
+            "inputs": [
+                {
+                    "default": {
+                        "class": "File",
+                        "location": "keep:5d373e7629203ce39e7c22af98a0f881+52/blub.txt"
+                    },
+                    "id": "#submit_tool.cwl/x",
+                    "inputBinding": {
+                        "position": 1
+                    },
+                    "type": "File"
+                }
+            ],
+            "outputs": [],
+            "requirements": [
+                {
+                    "class": "DockerRequirement",
+                    "dockerPull": "debian:8",
+                    "http://arvados.org/cwl#dockerCollectionPDH": "999999999999999999999999999999d4+99"
+                }
+            ]
+        },
+        {
+            "class": "Workflow",
+            "hints": [
+                {
+                    "acrContainerImage": "999999999999999999999999999999d3+99",
+                    "class": "http://arvados.org/cwl#WorkflowRunnerResources"
+                }
+            ],
+            "id": "#main",
+            "inputs": [
+                {
+                    "default": {
+                        "basename": "blorp.txt",
+                        "class": "File",
+                        "location": "keep:169f39d466a5438ac4a90e779bf750c7+53/blorp.txt",
+                        "nameext": ".txt",
+                        "nameroot": "blorp",
+                        "size": 16
+                    },
+                    "id": "#main/x",
+                    "type": "File"
+                },
+                {
+                    "default": {
+                        "basename": "99999999999999999999999999999998+99",
+                        "class": "Directory",
+                        "location": "keep:99999999999999999999999999999998+99"
+                    },
+                    "id": "#main/y",
+                    "type": "Directory"
+                },
+                {
+                    "default": {
+                        "basename": "anonymous",
+                        "class": "Directory",
+                        "listing": [
+                            {
+                                "basename": "renamed.txt",
+                                "class": "File",
+                                "location": "keep:99999999999999999999999999999998+99/file1.txt",
+                                "nameext": ".txt",
+                                "nameroot": "renamed",
+                                "size": 0
+                            }
+                        ]
+                    },
+                    "id": "#main/z",
+                    "type": "Directory"
+                }
+            ],
+            "outputs": [],
+            "steps": [
+                {
+                    "id": "#main/step1",
+                    "in": [
+                        {
+                            "id": "#main/step1/x",
+                            "source": "#main/x"
+                        }
+                    ],
+                    "out": [],
+                    "run": "#submit_tool.cwl"
+                }
+            ]
+        }
+    ],
+    "cwlVersion": "v1.0"
+}
index 132939547a6180b71266d102f52915e36988eb38..2202016bcc6b8a607c7f7d8241c80166247b87b4 100644 (file)
@@ -57,9 +57,8 @@ func SignManifest(manifest string, apiToken string, expiry time.Time, ttl time.D
        return regexp.MustCompile(`\S+`).ReplaceAllStringFunc(manifest, func(tok string) string {
                if mBlkRe.MatchString(tok) {
                        return SignLocator(mPermHintRe.ReplaceAllString(tok, ""), apiToken, expiry, ttl, permissionSecret)
-               } else {
-                       return tok
                }
+               return tok
        })
 }
 
index c87f880e5e56365e4bbec06c211a72cee9e0ee7c..363d09dafb5452b350077d6933ed2698689fb513 100644 (file)
@@ -17,9 +17,8 @@ import (
 var DefaultConfigFile = func() string {
        if path := os.Getenv("ARVADOS_CONFIG"); path != "" {
                return path
-       } else {
-               return "/etc/arvados/config.yml"
        }
+       return "/etc/arvados/config.yml"
 }()
 
 type Config struct {
@@ -183,6 +182,7 @@ type Cluster struct {
                }
                LoginCluster       string
                RemoteTokenRefresh Duration
+               TokenLifetime      Duration
        }
        Mail struct {
                MailchimpAPIKey                string
@@ -259,6 +259,7 @@ type Cluster struct {
                InactivePageHTML       string
                SSHHelpPageHTML        string
                SSHHelpHostSuffix      string
+               IdleTimeout            Duration
        }
 
        ForceLegacyAPI14 bool
@@ -446,23 +447,24 @@ type ContainersConfig struct {
 type CloudVMsConfig struct {
        Enable bool
 
-       BootProbeCommand     string
-       DeployRunnerBinary   string
-       ImageID              string
-       MaxCloudOpsPerSecond int
-       MaxProbesPerSecond   int
-       PollInterval         Duration
-       ProbeInterval        Duration
-       SSHPort              string
-       SyncInterval         Duration
-       TimeoutBooting       Duration
-       TimeoutIdle          Duration
-       TimeoutProbe         Duration
-       TimeoutShutdown      Duration
-       TimeoutSignal        Duration
-       TimeoutTERM          Duration
-       ResourceTags         map[string]string
-       TagKeyPrefix         string
+       BootProbeCommand               string
+       DeployRunnerBinary             string
+       ImageID                        string
+       MaxCloudOpsPerSecond           int
+       MaxProbesPerSecond             int
+       MaxConcurrentInstanceCreateOps int
+       PollInterval                   Duration
+       ProbeInterval                  Duration
+       SSHPort                        string
+       SyncInterval                   Duration
+       TimeoutBooting                 Duration
+       TimeoutIdle                    Duration
+       TimeoutProbe                   Duration
+       TimeoutShutdown                Duration
+       TimeoutSignal                  Duration
+       TimeoutTERM                    Duration
+       ResourceTags                   map[string]string
+       TagKeyPrefix                   string
 
        Driver           string
        DriverParameters json.RawMessage
index da1710374e1e69ca4bfe6c2f77c8b990a1f7dc4e..eb7988422d80bc1f3d314ce36427c39788835c25 100644 (file)
@@ -140,7 +140,7 @@ func (s *KeepService) Untrash(ctx context.Context, c *Client, blk string) error
        return nil
 }
 
-// Index returns an unsorted list of blocks at the given mount point.
+// IndexMount returns an unsorted list of blocks at the given mount point.
 func (s *KeepService) IndexMount(ctx context.Context, c *Client, mountUUID string, prefix string) ([]KeepServiceIndexEntry, error) {
        return s.index(ctx, c, s.url("mounts/"+mountUUID+"/blocks?prefix="+prefix))
 }
index e2c046662769f4ebd3956394375ab59cde7ebe51..bfcbde2a70632a170734e2664683223f4740d695 100644 (file)
@@ -70,12 +70,11 @@ func (e APIServerError) Error() string {
                        e.HttpStatusCode,
                        e.HttpStatusMessage,
                        e.ServerAddress)
-       } else {
-               return fmt.Sprintf("arvados API server error: %d: %s returned by %s",
-                       e.HttpStatusCode,
-                       e.HttpStatusMessage,
-                       e.ServerAddress)
        }
+       return fmt.Sprintf("arvados API server error: %d: %s returned by %s",
+               e.HttpStatusCode,
+               e.HttpStatusMessage,
+               e.ServerAddress)
 }
 
 // StringBool tests whether s is suggestive of true. It returns true
@@ -420,9 +419,8 @@ func (c *ArvadosClient) Discovery(parameter string) (value interface{}, err erro
        value, found = c.DiscoveryDoc[parameter]
        if found {
                return value, nil
-       } else {
-               return value, ErrInvalidArgument
        }
+       return value, ErrInvalidArgument
 }
 
 func (ac *ArvadosClient) httpClient() *http.Client {
index b9ecc45abc6a29d6d92642f41efcd26689457d52..52aa45f858746a7da6f79b57a5a6c6f32c044f45 100644 (file)
@@ -2,7 +2,7 @@
 //
 // SPDX-License-Identifier: Apache-2.0
 
-// Stores a Block Locator Digest compactly. Can be used as a map key.
+// Package blockdigest stores a Block Locator Digest compactly. Can be used as a map key.
 package blockdigest
 
 import (
index afeb8028496532dc66550ddddf4bd15b285bc7e9..c46b7185e67958b6fc91bb6531364d11a9ff5238 100644 (file)
@@ -33,10 +33,9 @@ func NewRootSorter(serviceRoots map[string]string, hash string) *RootSorter {
 func (rs RootSorter) getWeight(hash string, uuid string) string {
        if len(uuid) == 27 {
                return Md5String(hash + uuid[12:])
-       } else {
-               // Only useful for testing, a set of one service root, etc.
-               return Md5String(hash + uuid)
        }
+       // Only useful for testing, a set of one service root, etc.
+       return Md5String(hash + uuid)
 }
 
 func (rs RootSorter) GetSortedRoots() []string {
index 71b4b5ed2608729a05111dc7ab327886f5332b47..7989e66c03728fbf383946c4fb7a21ed03199ac3 100644 (file)
@@ -127,7 +127,7 @@ func (this *KeepClient) putReplicas(
        sv := NewRootSorter(this.WritableLocalRoots(), hash).GetSortedRoots()
 
        // The next server to try contacting
-       next_server := 0
+       nextServer := 0
 
        // The number of active writers
        active := 0
@@ -162,15 +162,15 @@ func (this *KeepClient) putReplicas(
 
        for retriesRemaining > 0 {
                retriesRemaining -= 1
-               next_server = 0
+               nextServer = 0
                retryServers = []string{}
                for replicasTodo > 0 {
                        for active*replicasPerThread < replicasTodo {
                                // Start some upload requests
-                               if next_server < len(sv) {
-                                       DebugPrintf("DEBUG: [%s] Begin upload %s to %s", reqid, hash, sv[next_server])
-                                       go this.uploadToKeepServer(sv[next_server], hash, getReader(), upload_status, expectedLength, reqid)
-                                       next_server += 1
+                               if nextServer < len(sv) {
+                                       DebugPrintf("DEBUG: [%s] Begin upload %s to %s", reqid, hash, sv[nextServer])
+                                       go this.uploadToKeepServer(sv[nextServer], hash, getReader(), upload_status, expectedLength, reqid)
+                                       nextServer += 1
                                        active += 1
                                } else {
                                        if active == 0 && retriesRemaining == 0 {
@@ -180,9 +180,8 @@ func (this *KeepClient) putReplicas(
                                                }
                                                msg = msg[:len(msg)-2]
                                                return locator, replicasDone, InsufficientReplicasError(errors.New(msg))
-                                       } else {
-                                               break
                                        }
+                                       break
                                }
                        }
                        DebugPrintf("DEBUG: [%s] Replicas remaining to write: %v active uploads: %v",
index 8bd43f5960a65bf6040a7545b75d781f89295652..430350b885971b945b56d625363e8f26e5f06e1f 100644 (file)
@@ -50,7 +50,7 @@ setup(name='arvados-python-client',
           'future',
           'google-api-python-client >=1.6.2, <1.7',
           'httplib2 >=0.9.2',
-          'pycurl >=7.19.5.1',
+          'pycurl >=7.19.5.1, <7.43.0.4', # 7.43.0.4 removes support for python2
           'ruamel.yaml >=0.15.54, <=0.16.5',
           'setuptools',
           'ws4py >=0.4.2',
index 582b98cf2dc9d9e20b88cf0180b7a9db19fbfd8f..8e3c3ac5e3d8b8656d587e86626f86f57c33b045 100644 (file)
@@ -147,10 +147,15 @@ class UserSessionsController < ApplicationController
         find_or_create_by(url_prefix: api_client_url_prefix)
     end
 
+    token_expiration = nil
+    if Rails.configuration.Login.TokenLifetime > 0
+      token_expiration = Time.now + Rails.configuration.Login.TokenLifetime
+    end
     @api_client_auth = ApiClientAuthorization.
       new(user: user,
           api_client: @api_client,
           created_by_ip_address: remote_ip,
+          expires_at: token_expiration,
           scopes: ["all"])
     @api_client_auth.save!
 
index 8ed693f820d5eac0eff9389ac851166e800d6516..c6c48a5b6b13c803d8d54d660a2d8fbd2a265740 100644 (file)
@@ -15,13 +15,16 @@ class ApiClient < ArvadosModel
   end
 
   def is_trusted
-    norm(self.url_prefix) == norm(Rails.configuration.Services.Workbench1.ExternalURL) ||
-      norm(self.url_prefix) == norm(Rails.configuration.Services.Workbench2.ExternalURL) ||
-      super
+    (from_trusted_url && Rails.configuration.Login.TokenLifetime == 0) || super
   end
 
   protected
 
+  def from_trusted_url
+    norm(self.url_prefix) == norm(Rails.configuration.Services.Workbench1.ExternalURL) ||
+      norm(self.url_prefix) == norm(Rails.configuration.Services.Workbench2.ExternalURL)
+  end
+
   def norm url
     # normalize URL for comparison
     url = URI(url)
index a4d49c35c1fc4c73c490375921c7b2bf3a94c97b..ab6fd8000c1f4a966be7e01450402719f7182d5a 100644 (file)
@@ -113,7 +113,7 @@ class ApiClientAuthorization < ArvadosModel
       return ApiClientAuthorization.new(user: User.find_by_uuid(system_user_uuid),
                                         uuid: Rails.configuration.ClusterID+"-gj3su-000000000000000",
                                         api_token: token,
-                                        api_client: ApiClient.new(is_trusted: true, url_prefix: ""))
+                                        api_client: system_root_token_api_client)
     else
       return nil
     end
@@ -226,6 +226,11 @@ class ApiClientAuthorization < ArvadosModel
       # Add or update user and token in local database so we can
       # validate subsequent requests faster.
 
+      if remote_user['uuid'][-22..-1] == '-tpzed-anonymouspublic'
+        # Special case: map the remote anonymous user to local anonymous user
+        remote_user['uuid'] = anonymous_user_uuid
+      end
+
       user = User.find_by_uuid(remote_user['uuid'])
 
       if !user
@@ -257,6 +262,11 @@ class ApiClientAuthorization < ArvadosModel
         user.send(attr+'=', remote_user[attr])
       end
 
+      if remote_user['uuid'][-22..-1] == '-tpzed-000000000000000'
+        user.first_name = "root"
+        user.last_name = "from cluster #{remote_user_prefix}"
+      end
+
       act_as_system_user do
         if user.is_active && !remote_user['is_active']
           user.unsetup
index 39f491503ee583033f80ae72ef982261c6ba0af9..d7c5e04dfd1b82e571a92079e3563523ec065376 100644 (file)
@@ -13,6 +13,7 @@ class DatabaseSeeds
     anonymous_group
     anonymous_group_read_permission
     anonymous_user
+    system_root_token_api_client
     empty_collection
     refresh_permissions
     refresh_trashed
index 035a3972f86c318e758318330c7aa63af44ff9c5..4f831160e9351790143505cc16447e6d0507b0e3 100644 (file)
@@ -111,6 +111,7 @@ arvcfg.declare_config "Login.SSO.ProviderAppSecret", String, :sso_app_secret
 arvcfg.declare_config "Login.SSO.ProviderAppID", String, :sso_app_id
 arvcfg.declare_config "Login.LoginCluster", String
 arvcfg.declare_config "Login.RemoteTokenRefresh", ActiveSupport::Duration
+arvcfg.declare_config "Login.TokenLifetime", ActiveSupport::Duration
 arvcfg.declare_config "TLS.Insecure", Boolean, :sso_insecure
 arvcfg.declare_config "Services.SSO.ExternalURL", String, :sso_provider_url
 arvcfg.declare_config "AuditLogs.MaxAge", ActiveSupport::Duration, :max_audit_log_age
index 98112c858b98445c9bacb8c9c614343f8a7f5ef1..cfdae0bddec58455daf0d791b7a7731dd98e6e8b 100644 (file)
@@ -189,6 +189,16 @@ module CurrentApiClient
     end
   end
 
+  def system_root_token_api_client
+    $system_root_token_api_client = check_cache $system_root_token_api_client do
+      act_as_system_user do
+        ActiveRecord::Base.transaction do
+          ApiClient.find_or_create_by!(is_trusted: true, url_prefix: "", name: "SystemRootToken")
+        end
+      end
+    end
+  end
+
   def empty_collection_pdh
     'd41d8cd98f00b204e9800998ecf8427e+0'
   end
diff --git a/services/api/lib/tasks/manage_long_lived_tokens.rake b/services/api/lib/tasks/manage_long_lived_tokens.rake
new file mode 100644 (file)
index 0000000..7bcf315
--- /dev/null
@@ -0,0 +1,61 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+# Tasks that can be useful when changing token expiration policies by assigning
+# a non-zero value to Login.TokenLifetime config.
+
+require 'set'
+require 'current_api_client'
+
+namespace :db do
+  desc "Apply expiration policy on long lived tokens"
+  task fix_long_lived_tokens: :environment do
+    if Rails.configuration.Login.TokenLifetime == 0
+      puts("No expiration policy set on Login.TokenLifetime.")
+    else
+      exp_date = Time.now + Rails.configuration.Login.TokenLifetime
+      puts("Setting token expiration to: #{exp_date}")
+      token_count = 0
+      ll_tokens.each do |auth|
+        if (auth.user.uuid =~ /-tpzed-000000000000000/).nil?
+          CurrentApiClientHelper.act_as_system_user do
+            auth.update_attributes!(expires_at: exp_date)
+          end
+          token_count += 1
+        end
+      end
+      puts("#{token_count} tokens updated.")
+    end
+  end
+
+  desc "Show users with long lived tokens"
+  task check_long_lived_tokens: :environment do
+    user_ids = Set.new()
+    token_count = 0
+    ll_tokens.each do |auth|
+      if (auth.user.uuid =~ /-tpzed-000000000000000/).nil?
+        user_ids.add(auth.user_id)
+        token_count += 1
+      end
+    end
+
+    if user_ids.size > 0
+      puts("Found #{token_count} long-lived tokens from users:")
+      user_ids.each do |uid|
+        u = User.find(uid)
+        puts("#{u.username},#{u.email},#{u.uuid}") if !u.nil?
+      end
+    else
+      puts("No long-lived tokens found.")
+    end
+  end
+
+  def ll_tokens
+    query = ApiClientAuthorization.where(expires_at: nil)
+    if Rails.configuration.Login.TokenLifetime > 0
+      query = query.or(ApiClientAuthorization.where("expires_at > ?", Time.now + Rails.configuration.Login.TokenLifetime))
+    end
+    query
+  end
+end
index 4bb91e244635d7c6e10dcdff32b72264141ff9de..8775ae59594402a6231ce3169e78669fc49f2740 100755 (executable)
@@ -29,27 +29,37 @@ include ApplicationHelper
 act_as_system_user
 
 def create_api_client_auth(supplied_token=nil)
+  supplied_token = Rails.configuration.Users["AnonymousUserToken"]
 
-  # If token is supplied, see if it exists
-  if supplied_token
-    api_client_auth = ApiClientAuthorization.
-      where(api_token: supplied_token).
-      first
-    if !api_client_auth
-      # fall through to create a token
-    else
-      raise "Token exists, aborting!"
+  if supplied_token.nil? or supplied_token.empty?
+    puts "Users.AnonymousUserToken is empty.  Destroying tokens that belong to anonymous."
+    # Token is empty.  Destroy any anonymous tokens.
+    ApiClientAuthorization.where(user: anonymous_user).destroy_all
+    return nil
+  end
+
+  attr = {user: anonymous_user,
+          api_client_id: 0,
+          scopes: ['GET /']}
+
+  secret = supplied_token
+
+  if supplied_token[0..2] == 'v2/'
+    _, token_uuid, secret, optional = supplied_token.split('/')
+    if token_uuid[0..4] != Rails.configuration.ClusterID
+      # Belongs to a different cluster.
+      puts supplied_token
+      return nil
     end
+    attr[:uuid] = token_uuid
   end
 
-  api_client_auth = ApiClientAuthorization.
-    new(user: anonymous_user,
-        api_client_id: 0,
-        expires_at: Time.now + 100.years,
-        scopes: ['GET /'],
-        api_token: supplied_token)
-  api_client_auth.save!
-  api_client_auth.reload
+  attr[:api_token] = secret
+
+  api_client_auth = ApiClientAuthorization.where(attr).first
+  if !api_client_auth
+    api_client_auth = ApiClientAuthorization.create!(attr)
+  end
   api_client_auth
 end
 
@@ -67,4 +77,6 @@ if !api_client_auth
 end
 
 # print it to the console
-puts api_client_auth.api_token
+if api_client_auth
+  puts "v2/#{api_client_auth.uuid}/#{api_client_auth.api_token}"
+end
index 7b522734abc883f1114da063094b892f07712a8a..9965718f99cc82f182c4d465fe76a5c2681bc98d 100644 (file)
@@ -17,3 +17,10 @@ untrusted:
   name: Untrusted
   url_prefix: https://untrusted.local/
   is_trusted: false
+
+system_root_token_api_client:
+  uuid: zzzzz-ozdt8-pbw7foaks3qjyej
+  owner_uuid: zzzzz-tpzed-000000000000000
+  name: SystemRootToken
+  url_prefix: ""
+  is_trusted: true
index 2859e375a4949ff53e8f53b3458bc4e4fb02ad0a..29b76abb456a7012325a7d79f14e25116cb6fc8f 100644 (file)
@@ -67,3 +67,28 @@ workflow_with_input_defaults:
       id: ex_string_def
       default: hello-testing-123
     outputs: []
+
+workflow_with_wrr:
+  uuid: zzzzz-7fd4e-validwithinput3
+  owner_uuid: zzzzz-j7d0g-zhxawtyetzwc5f0
+  name: Workflow with WorkflowRunnerResources
+  description: this workflow has WorkflowRunnerResources
+  created_at: <%= 1.minute.ago.to_s(:db) %>
+  definition: |
+    cwlVersion: v1.0
+    class: CommandLineTool
+    hints:
+      - class: http://arvados.org/cwl#WorkflowRunnerResources
+        acrContainerImage: arvados/jobs:2.0.4
+        ramMin: 1234
+        coresMin: 2
+        keep_cache: 678
+    baseCommand:
+    - echo
+    inputs:
+    - type: string
+      id: ex_string
+    - type: string
+      id: ex_string_def
+      default: hello-testing-123
+    outputs: []
index fc9475692a5933c2ed01f77e7871f4fd3942d7ec..cd475dea4d1849f6d99374fa3976068767ef1fcb 100644 (file)
@@ -14,7 +14,6 @@ class UserSessionsControllerTest < ActionController::TestCase
     assert_nil assigns(:api_client)
   end
 
-
   test "send token when user is already logged in" do
     authorize_with :inactive
     api_client_page = 'http://client.example.com/home'
@@ -26,6 +25,28 @@ class UserSessionsControllerTest < ActionController::TestCase
     assert_not_nil assigns(:api_client)
   end
 
+  test "login creates token without expiration by default" do
+    assert_equal Rails.configuration.Login.TokenLifetime, 0
+    authorize_with :inactive
+    api_client_page = 'http://client.example.com/home'
+    get :login, params: {return_to: api_client_page}
+    assert_not_nil assigns(:api_client)
+    assert_nil assigns(:api_client_auth).expires_at
+  end
+
+  test "login creates token with configured lifetime" do
+    token_lifetime = 1.hour
+    Rails.configuration.Login.TokenLifetime = token_lifetime
+    authorize_with :inactive
+    api_client_page = 'http://client.example.com/home'
+    get :login, params: {return_to: api_client_page}
+    assert_not_nil assigns(:api_client)
+    api_client_auth = assigns(:api_client_auth)
+    assert_in_delta(api_client_auth.expires_at,
+                    api_client_auth.updated_at + token_lifetime,
+                    1.second)
+  end
+
   test "login with remote param returns a salted token" do
     authorize_with :inactive
     api_client_page = 'http://client.example.com/home'
index b9bfd3a39537e765af505861cc5d9b0b826cfdb9..296ab8a2ff4169167d8c85bffcdab34f67f078e5 100644 (file)
@@ -14,22 +14,40 @@ class ApiClientAuthorizationsApiTest < ActionDispatch::IntegrationTest
     assert_response :success
   end
 
-  test "create token for different user" do
-    post "/arvados/v1/api_client_authorizations",
-      params: {
-        :format => :json,
-        :api_client_authorization => {
-          :owner_uuid => users(:spectator).uuid
-        }
-      },
-      headers: {'HTTP_AUTHORIZATION' => "OAuth2 #{api_client_authorizations(:admin_trustedclient).api_token}"}
-    assert_response :success
+  [:admin_trustedclient, :SystemRootToken].each do |tk|
+    test "create token for different user using #{tk}" do
+      if tk == :SystemRootToken
+        token = "xyzzy-SystemRootToken"
+        Rails.configuration.SystemRootToken = token
+      else
+        token = api_client_authorizations(tk).api_token
+      end
+
+      post "/arvados/v1/api_client_authorizations",
+           params: {
+             :format => :json,
+             :api_client_authorization => {
+               :owner_uuid => users(:spectator).uuid
+             }
+           },
+           headers: {'HTTP_AUTHORIZATION' => "OAuth2 #{token}"}
+      assert_response :success
+
+      get "/arvados/v1/users/current",
+          params: {:format => :json},
+          headers: {'HTTP_AUTHORIZATION' => "OAuth2 #{json_response['api_token']}"}
+      @json_response = nil
+      assert_equal json_response['uuid'], users(:spectator).uuid
+    end
+  end
 
+  test "System root token is system user" do
+    token = "xyzzy-SystemRootToken"
+    Rails.configuration.SystemRootToken = token
     get "/arvados/v1/users/current",
-      params: {:format => :json},
-      headers: {'HTTP_AUTHORIZATION' => "OAuth2 #{json_response['api_token']}"}
-    @json_response = nil
-    assert_equal users(:spectator).uuid, json_response['uuid']
+        params: {:format => :json},
+        headers: {'HTTP_AUTHORIZATION' => "OAuth2 #{token}"}
+    assert_equal json_response['uuid'], system_user_uuid
   end
 
   test "refuse to create token for different user if not trusted client" do
index 04a45420fd4b768c105e89f8bd600739d69c8a6f..8ad09894a16df4bc6281ba95db6c8b6b25be589c 100644 (file)
@@ -79,7 +79,7 @@ class RemoteUsersTest < ActionDispatch::IntegrationTest
     Arvados::V1::SchemaController.any_instance.stubs(:root_url).returns "https://#{@remote_host[0]}"
     @stub_status = 200
     @stub_content = {
-      uuid: 'zbbbb-tpzed-000000000000000',
+      uuid: 'zbbbb-tpzed-000000000000001',
       email: 'foo@example.com',
       username: 'barney',
       is_admin: true,
@@ -98,7 +98,7 @@ class RemoteUsersTest < ActionDispatch::IntegrationTest
       params: {format: 'json'},
       headers: auth(remote: 'zbbbb')
     assert_response :success
-    assert_equal 'zbbbb-tpzed-000000000000000', json_response['uuid']
+    assert_equal 'zbbbb-tpzed-000000000000001', json_response['uuid']
     assert_equal false, json_response['is_admin']
     assert_equal false, json_response['is_active']
     assert_equal 'foo@example.com', json_response['email']
@@ -286,12 +286,12 @@ class RemoteUsersTest < ActionDispatch::IntegrationTest
       params: {format: 'json'},
       headers: auth(remote: 'zbbbb')
     assert_response :success
-    assert_equal 'zbbbb-tpzed-000000000000000', json_response['uuid']
+    assert_equal 'zbbbb-tpzed-000000000000001', json_response['uuid']
     assert_equal false, json_response['is_admin']
     assert_equal false, json_response['is_active']
     assert_equal 'foo@example.com', json_response['email']
     assert_equal 'barney', json_response['username']
-    post '/arvados/v1/users/zbbbb-tpzed-000000000000000/activate',
+    post '/arvados/v1/users/zbbbb-tpzed-000000000000001/activate',
       params: {format: 'json'},
       headers: auth(remote: 'zbbbb')
     assert_response 422
@@ -303,7 +303,7 @@ class RemoteUsersTest < ActionDispatch::IntegrationTest
       params: {format: 'json'},
       headers: auth(remote: 'zbbbb')
     assert_response :success
-    assert_equal 'zbbbb-tpzed-000000000000000', json_response['uuid']
+    assert_equal 'zbbbb-tpzed-000000000000001', json_response['uuid']
     assert_equal false, json_response['is_admin']
     assert_equal true, json_response['is_active']
     assert_equal 'foo@example.com', json_response['email']
@@ -316,7 +316,7 @@ class RemoteUsersTest < ActionDispatch::IntegrationTest
       params: {format: 'json'},
       headers: auth(remote: 'zbbbb')
     assert_response :success
-    assert_equal 'zbbbb-tpzed-000000000000000', json_response['uuid']
+    assert_equal 'zbbbb-tpzed-000000000000001', json_response['uuid']
     assert_equal true, json_response['is_admin']
     assert_equal true, json_response['is_active']
     assert_equal 'foo@example.com', json_response['email']
@@ -412,4 +412,22 @@ class RemoteUsersTest < ActionDispatch::IntegrationTest
     end
   end
 
+  test 'authenticate with remote token, remote user is system user' do
+    @stub_content[:uuid] = 'zbbbb-tpzed-000000000000000'
+    get '/arvados/v1/users/current',
+      params: {format: 'json'},
+      headers: auth(remote: 'zbbbb')
+    assert_equal 'from cluster zbbbb', json_response['last_name']
+  end
+
+  test 'authenticate with remote token, remote user is anonymous user' do
+    @stub_content[:uuid] = 'zbbbb-tpzed-anonymouspublic'
+    get '/arvados/v1/users/current',
+      params: {format: 'json'},
+      headers: auth(remote: 'zbbbb')
+    assert_response :success
+    assert_equal 'zzzzz-tpzed-anonymouspublic', json_response['uuid']
+  end
+
+
 end
index df082c27fd8c35f7a8d1011bcd3faeba3d4bd4d8..93e4c51abf0e9266a1883ab18bf5634ea45722b0 100644 (file)
@@ -7,25 +7,32 @@ require 'test_helper'
 class ApiClientTest < ActiveSupport::TestCase
   include CurrentApiClient
 
-  test "configured workbench is trusted" do
-    Rails.configuration.Services.Workbench1.ExternalURL = URI("http://wb1.example.com")
-    Rails.configuration.Services.Workbench2.ExternalURL = URI("https://wb2.example.com:443")
+  [true, false].each do |token_lifetime_enabled|
+    test "configured workbench is trusted when token lifetime is#{token_lifetime_enabled ? '': ' not'} enabled" do
+      Rails.configuration.Login.TokenLifetime = token_lifetime_enabled ? 8.hours : 0
+      Rails.configuration.Services.Workbench1.ExternalURL = URI("http://wb1.example.com")
+      Rails.configuration.Services.Workbench2.ExternalURL = URI("https://wb2.example.com:443")
 
-    act_as_system_user do
-      [["http://wb0.example.com", false],
-       ["http://wb1.example.com", true],
-       ["http://wb2.example.com", false],
-       ["https://wb2.example.com", true],
-       ["https://wb2.example.com/", true],
-      ].each do |pfx, result|
-        a = ApiClient.create(url_prefix: pfx, is_trusted: false)
-        assert_equal result, a.is_trusted
-      end
+      act_as_system_user do
+        [["http://wb0.example.com", false],
+        ["http://wb1.example.com", true],
+        ["http://wb2.example.com", false],
+        ["https://wb2.example.com", true],
+        ["https://wb2.example.com/", true],
+        ].each do |pfx, result|
+          a = ApiClient.create(url_prefix: pfx, is_trusted: false)
+          if token_lifetime_enabled
+            assert_equal false, a.is_trusted, "API client with url prefix '#{pfx}' shouldn't be trusted"
+          else
+            assert_equal result, a.is_trusted
+          end
+        end
 
-      a = ApiClient.create(url_prefix: "http://example.com", is_trusted: true)
-      a.save!
-      a.reload
-      assert a.is_trusted
+        a = ApiClient.create(url_prefix: "http://example.com", is_trusted: true)
+        a.save!
+        a.reload
+        assert a.is_trusted
+      end
     end
   end
 end
index 4b8f95ef334fd4a0323d242ae023c2846fe4e128..4e1a47dcb25bdff037f05b382e953a4b6627b1fe 100644 (file)
@@ -44,7 +44,7 @@ func (s *AuthHandlerSuite) SetUpTest(c *check.C) {
        s.cluster, err = cfg.GetCluster("")
        c.Assert(err, check.Equals, nil)
 
-       s.cluster.Services.GitHTTP.InternalURLs = map[arvados.URL]arvados.ServiceInstance{arvados.URL{Host: "localhost:0"}: arvados.ServiceInstance{}}
+       s.cluster.Services.GitHTTP.InternalURLs = map[arvados.URL]arvados.ServiceInstance{{Host: "localhost:0"}: {}}
        s.cluster.TLS.Insecure = true
        s.cluster.Git.GitCommand = "/usr/bin/git"
        s.cluster.Git.Repositories = repoRoot
index c14030f95da629c516a5a46968724d5c51602b4f..dafe5d31d7f3b3c0dc49145a6232653ef0c075a5 100644 (file)
@@ -28,7 +28,7 @@ func (s *GitHandlerSuite) SetUpTest(c *check.C) {
        s.cluster, err = cfg.GetCluster("")
        c.Assert(err, check.Equals, nil)
 
-       s.cluster.Services.GitHTTP.InternalURLs = map[arvados.URL]arvados.ServiceInstance{arvados.URL{Host: "localhost:80"}: arvados.ServiceInstance{}}
+       s.cluster.Services.GitHTTP.InternalURLs = map[arvados.URL]arvados.ServiceInstance{{Host: "localhost:80"}: {}}
        s.cluster.Git.GitoliteHome = "/test/ghh"
        s.cluster.Git.Repositories = "/"
 }
index b50c2a2341185807ac0f9668d0175f4bb5494054..12ddc5b770940d767342cdaa871a299bfd3a113a 100644 (file)
@@ -68,7 +68,7 @@ func (s *IntegrationSuite) SetUpTest(c *check.C) {
                s.cluster, err = cfg.GetCluster("")
                c.Assert(err, check.Equals, nil)
 
-               s.cluster.Services.GitHTTP.InternalURLs = map[arvados.URL]arvados.ServiceInstance{arvados.URL{Host: "localhost:0"}: arvados.ServiceInstance{}}
+               s.cluster.Services.GitHTTP.InternalURLs = map[arvados.URL]arvados.ServiceInstance{{Host: "localhost:0"}: {}}
                s.cluster.TLS.Insecure = true
                s.cluster.Git.GitCommand = "/usr/bin/git"
                s.cluster.Git.Repositories = s.tmpRepoRoot
index 8b12f73e895a8d59e3f95461218ab7cf14589887..db5020cfefbbc820a73b300926fec858872d331d 100644 (file)
@@ -7,17 +7,17 @@ from __future__ import division
 from future.utils import viewitems
 from future.utils import itervalues
 from builtins import dict
-import logging
-import re
-import time
-import llfuse
-import arvados
 import apiclient
+import arvados
+import errno
 import functools
+import llfuse
+import logging
+import re
+import sys
 import threading
-from apiclient import errors as apiclient_errors
-import errno
 import time
+from apiclient import errors as apiclient_errors
 
 from .fusefile import StringFile, ObjectFile, FuncToJSONFile, FuseArvadosFile
 from .fresh import FreshBase, convertTime, use_counter, check_update
@@ -689,7 +689,6 @@ and the directory will appear if it exists.
                 e = self.inodes.add_entry(ProjectDirectory(
                     self.inode, self.inodes, self.api, self.num_retries, project[u'items'][0]))
             else:
-                import sys
                 e = self.inodes.add_entry(CollectionDirectory(
                         self.inode, self.inodes, self.api, self.num_retries, k))
 
index 1f06d8c91cf21608e02ddab7ae3bdd63d8ee45f2..dbfea1f90449cb14f3c12df15e6b37001b131bcc 100644 (file)
@@ -6,6 +6,7 @@ import collections
 import errno
 import os
 import subprocess
+import sys
 import time
 
 
index 01bc8b704788da25b835f9b11ef43db0f079fc26..52cfede46642120dce520868d2d38758fb97cc12 100644 (file)
@@ -348,12 +348,30 @@ func (h *handler) s3list(w http.ResponseWriter, r *http.Request, fs arvados.Cust
                walkpath = ""
        }
 
-       resp := s3.ListResp{
-               Name:      strings.SplitN(r.URL.Path[1:], "/", 2)[0],
-               Prefix:    params.prefix,
-               Delimiter: params.delimiter,
-               Marker:    params.marker,
-               MaxKeys:   params.maxKeys,
+       type commonPrefix struct {
+               Prefix string
+       }
+       type listResp struct {
+               XMLName string `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListBucketResult"`
+               s3.ListResp
+               // s3.ListResp marshals an empty tag when
+               // CommonPrefixes is nil, which confuses some clients.
+               // Fix by using this nested struct instead.
+               CommonPrefixes []commonPrefix
+               // Similarly, we need omitempty here, because an empty
+               // tag confuses some clients (e.g.,
+               // github.com/aws/aws-sdk-net never terminates its
+               // paging loop).
+               NextMarker string `xml:"NextMarker,omitempty"`
+       }
+       resp := listResp{
+               ListResp: s3.ListResp{
+                       Name:      strings.SplitN(r.URL.Path[1:], "/", 2)[0],
+                       Prefix:    params.prefix,
+                       Delimiter: params.delimiter,
+                       Marker:    params.marker,
+                       MaxKeys:   params.maxKeys,
+               },
        }
        commonPrefixes := map[string]bool{}
        err := walkFS(fs, strings.TrimSuffix(bucketdir+"/"+walkpath, "/"), true, func(path string, fi os.FileInfo) error {
@@ -435,18 +453,15 @@ func (h *handler) s3list(w http.ResponseWriter, r *http.Request, fs arvados.Cust
                return
        }
        if params.delimiter != "" {
+               resp.CommonPrefixes = make([]commonPrefix, 0, len(commonPrefixes))
                for prefix := range commonPrefixes {
-                       resp.CommonPrefixes = append(resp.CommonPrefixes, prefix)
-                       sort.Strings(resp.CommonPrefixes)
+                       resp.CommonPrefixes = append(resp.CommonPrefixes, commonPrefix{prefix})
                }
+               sort.Slice(resp.CommonPrefixes, func(i, j int) bool { return resp.CommonPrefixes[i].Prefix < resp.CommonPrefixes[j].Prefix })
        }
-       wrappedResp := struct {
-               XMLName string `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListBucketResult"`
-               s3.ListResp
-       }{"", resp}
        w.Header().Set("Content-Type", "application/xml")
        io.WriteString(w, xml.Header)
-       if err := xml.NewEncoder(w).Encode(wrappedResp); err != nil {
+       if err := xml.NewEncoder(w).Encode(resp); err != nil {
                ctxlog.FromContext(r.Context()).WithError(err).Error("error writing xml response")
        }
 }
index b82f1efd7818b1fd26f5bbe6ffad5cc9f5fff5ec..66f046b13f14674c35bc783351eb9a0bf5f1b64b 100644 (file)
@@ -154,9 +154,10 @@ func (s *IntegrationSuite) testS3GetObject(c *check.C, bucket *s3.Bucket, prefix
        c.Check(err, check.IsNil)
 
        // HeadObject
-       exists, err = bucket.Exists(prefix + "sailboat.txt")
+       resp, err := bucket.Head(prefix+"sailboat.txt", nil)
        c.Check(err, check.IsNil)
-       c.Check(exists, check.Equals, true)
+       c.Check(resp.StatusCode, check.Equals, http.StatusOK)
+       c.Check(resp.ContentLength, check.Equals, int64(4))
 }
 
 func (s *IntegrationSuite) TestS3CollectionPutObjectSuccess(c *check.C) {
@@ -394,6 +395,43 @@ func (s *IntegrationSuite) TestS3GetBucketVersioning(c *check.C) {
        }
 }
 
+// If there are no CommonPrefixes entries, the CommonPrefixes XML tag
+// should not appear at all.
+func (s *IntegrationSuite) TestS3ListNoCommonPrefixes(c *check.C) {
+       stage := s.s3setup(c)
+       defer stage.teardown(c)
+
+       req, err := http.NewRequest("GET", stage.collbucket.URL("/"), nil)
+       c.Assert(err, check.IsNil)
+       req.Header.Set("Authorization", "AWS "+arvadostest.ActiveTokenV2+":none")
+       req.URL.RawQuery = "prefix=asdfasdfasdf&delimiter=/"
+       resp, err := http.DefaultClient.Do(req)
+       c.Assert(err, check.IsNil)
+       buf, err := ioutil.ReadAll(resp.Body)
+       c.Assert(err, check.IsNil)
+       c.Check(string(buf), check.Not(check.Matches), `(?ms).*CommonPrefixes.*`)
+}
+
+// If there is no delimiter in the request, or the results are not
+// truncated, the NextMarker XML tag should not appear in the response
+// body.
+func (s *IntegrationSuite) TestS3ListNoNextMarker(c *check.C) {
+       stage := s.s3setup(c)
+       defer stage.teardown(c)
+
+       for _, query := range []string{"prefix=e&delimiter=/", ""} {
+               req, err := http.NewRequest("GET", stage.collbucket.URL("/"), nil)
+               c.Assert(err, check.IsNil)
+               req.Header.Set("Authorization", "AWS "+arvadostest.ActiveTokenV2+":none")
+               req.URL.RawQuery = query
+               resp, err := http.DefaultClient.Do(req)
+               c.Assert(err, check.IsNil)
+               buf, err := ioutil.ReadAll(resp.Body)
+               c.Assert(err, check.IsNil)
+               c.Check(string(buf), check.Not(check.Matches), `(?ms).*NextMarker.*`)
+       }
+}
+
 func (s *IntegrationSuite) TestS3CollectionList(c *check.C) {
        stage := s.s3setup(c)
        defer stage.teardown(c)
index 94ed05bff1dbdb0585226741a792d2b77fa7fd29..6a02ab9bd3a8374dd5c7fed5888edd5c9a4217f8 100644 (file)
@@ -131,7 +131,7 @@ func runProxy(c *C, bogusClientToken bool, loadKeepstoresFromConfig bool) *keepc
                cluster.Services.Keepstore.InternalURLs = make(map[arvados.URL]arvados.ServiceInstance)
        }
 
-       cluster.Services.Keepproxy.InternalURLs = map[arvados.URL]arvados.ServiceInstance{arvados.URL{Host: ":0"}: arvados.ServiceInstance{}}
+       cluster.Services.Keepproxy.InternalURLs = map[arvados.URL]arvados.ServiceInstance{{Host: ":0"}: {}}
 
        listener = nil
        go func() {
index a244561dfadb55abb3e1a72fc1840d7138f5c54b..00161bf236eadfdd8f6881d4fe1a2194e91cf145 100644 (file)
@@ -91,7 +91,7 @@ func (s *ProxyRemoteSuite) SetUpTest(c *check.C) {
        s.cluster.Collections.BlobSigningKey = knownKey
        s.cluster.SystemRootToken = arvadostest.SystemRootToken
        s.cluster.RemoteClusters = map[string]arvados.RemoteCluster{
-               s.remoteClusterID: arvados.RemoteCluster{
+               s.remoteClusterID: {
                        Host:     strings.Split(s.remoteAPI.URL, "//")[1],
                        Proxy:    true,
                        Scheme:   "http",
index e00495c04db7db621ba0bf377cbe62072b82feba..8162e22a2ff6f815ac7904d28ad6ec0413faa7c3 100755 (executable)
@@ -36,7 +36,7 @@ begin
 
   logins = arv.virtual_machine.logins(:uuid => vm_uuid)[:items]
   logins = [] if logins.nil?
-  logins = logins.reject { |l| l[:username].nil? or l[:hostname].nil? or l[:public_key].nil? or l[:virtual_machine_uuid] != vm_uuid }
+  logins = logins.reject { |l| l[:username].nil? or l[:hostname].nil? or l[:virtual_machine_uuid] != vm_uuid }
 
   # No system users
   uid_min = 1000
@@ -79,48 +79,77 @@ begin
   logins.each do |l|
     keys[l[:username]] = Array.new() if not keys.has_key?(l[:username])
     key = l[:public_key]
-    # Handle putty-style ssh public keys
-    key.sub!(/^(Comment: "r[^\n]*\n)(.*)$/m,'ssh-rsa \2 \1')
-    key.sub!(/^(Comment: "d[^\n]*\n)(.*)$/m,'ssh-dss \2 \1')
-    key.gsub!(/\n/,'')
-    key.strip
-
-    keys[l[:username]].push(key) if not keys[l[:username]].include?(key)
+    if !key.nil?
+      # Handle putty-style ssh public keys
+      key.sub!(/^(Comment: "r[^\n]*\n)(.*)$/m,'ssh-rsa \2 \1')
+      key.sub!(/^(Comment: "d[^\n]*\n)(.*)$/m,'ssh-dss \2 \1')
+      key.gsub!(/\n/,'')
+      key.strip
+
+      keys[l[:username]].push(key) if not keys[l[:username]].include?(key)
+    end
   end
 
   seen = Hash.new()
-  devnull = open("/dev/null", "w")
+
+  current_user_groups = Hash.new
+  while (ent = Etc.getgrent()) do
+    ent.mem.each do |member|
+      current_user_groups[member] ||= Array.new
+      current_user_groups[member].push ent.name
+    end
+  end
+  Etc.endgrent()
 
   logins.each do |l|
     next if seen[l[:username]]
     seen[l[:username]] = true
 
+    username = l[:username]
+
     unless pwnam[l[:username]]
       STDERR.puts "Creating account #{l[:username]}"
-      groups = l[:groups] || []
-      # Adding users to the FUSE group has long been hardcoded behavior.
-      groups << "fuse"
-      groups.select! { |g| Etc.getgrnam(g) rescue false }
       # Create new user
       unless system("useradd", "-m",
-                "-c", l[:username],
+                "-c", username,
                 "-s", "/bin/bash",
-                "-G", groups.join(","),
-                l[:username],
-                out: devnull)
+                username)
         STDERR.puts "Account creation failed for #{l[:username]}: #{$?}"
         next
       end
       begin
-        pwnam[l[:username]] = Etc.getpwnam(l[:username])
+        pwnam[username] = Etc.getpwnam(username)
       rescue => e
         STDERR.puts "Created account but then getpwnam() failed for #{l[:username]}: #{e}"
         raise
       end
     end
 
-    @homedir = pwnam[l[:username]].dir
-    userdotssh = File.join(@homedir, ".ssh")
+    existing_groups = current_user_groups[username] || []
+    groups = l[:groups] || []
+    # Adding users to the FUSE group has long been hardcoded behavior.
+    groups << "fuse"
+    groups << username
+    groups.select! { |g| Etc.getgrnam(g) rescue false }
+
+    groups.each do |addgroup|
+      if existing_groups.index(addgroup).nil?
+        # User should be in group, but isn't, so add them.
+        STDERR.puts "Add user #{username} to #{addgroup} group"
+        system("adduser", username, addgroup)
+      end
+    end
+
+    existing_groups.each do |removegroup|
+      if groups.index(removegroup).nil?
+        # User is in a group, but shouldn't be, so remove them.
+        STDERR.puts "Remove user #{username} from #{removegroup} group"
+        system("deluser", username, removegroup)
+      end
+    end
+
+    homedir = pwnam[l[:username]].dir
+    userdotssh = File.join(homedir, ".ssh")
     Dir.mkdir(userdotssh) if !File.exist?(userdotssh)
 
     newkeys = "###\n###\n" + keys[l[:username]].join("\n") + "\n###\n###\n"
@@ -148,13 +177,41 @@ begin
       f.write(newkeys)
       f.close()
     end
+
+    userdotconfig = File.join(homedir, ".config")
+    if !File.exist?(userdotconfig)
+      Dir.mkdir(userdotconfig)
+    end
+
+    configarvados = File.join(userdotconfig, "arvados")
+    Dir.mkdir(configarvados) if !File.exist?(configarvados)
+
+    tokenfile = File.join(configarvados, "settings.conf")
+
+    begin
+      if !File.exist?(tokenfile)
+        user_token = arv.api_client_authorization.create(api_client_authorization: {owner_uuid: l[:user_uuid], api_client_id: 0})
+        f = File.new(tokenfile, 'w')
+        f.write("ARVADOS_API_HOST=#{ENV['ARVADOS_API_HOST']}\n")
+        f.write("ARVADOS_API_TOKEN=v2/#{user_token[:uuid]}/#{user_token[:api_token]}\n")
+        f.close()
+      end
+    rescue => e
+      STDERR.puts "Error setting token for #{l[:username]}: #{e}"
+    end
+
     FileUtils.chown_R(l[:username], nil, userdotssh)
+    FileUtils.chown_R(l[:username], nil, userdotconfig)
     File.chmod(0700, userdotssh)
-    File.chmod(0750, @homedir)
+    File.chmod(0700, userdotconfig)
+    File.chmod(0700, configarvados)
+    File.chmod(0750, homedir)
     File.chmod(0600, keysfile)
+    if File.exist?(tokenfile)
+      File.chmod(0600, tokenfile)
+    end
   end
 
-  devnull.close
 rescue Exception => bang
   puts "Error: " + bang.to_s
   puts bang.backtrace.join("\n")
index e90c16d64fae900df698c1db9d0cd6814022604b..db909ac83fc63bb2bbbca5aac7ea5c943a0a1c05 100644 (file)
@@ -16,20 +16,15 @@ class TestAddUser < Minitest::Test
     File.open(@tmpdir+'/succeed', 'w') do |f| end
     invoke_sync binstubs: ['new_user']
     spied = File.read(@tmpdir+'/spy')
-    assert_match %r{useradd -m -c active -s /bin/bash -G (fuse)? active}, spied
-    assert_match %r{useradd -m -c adminroot -s /bin/bash -G #{valid_groups.join(',')} adminroot}, spied
+    assert_match %r{useradd -m -c active -s /bin/bash active}, spied
+    assert_match %r{useradd -m -c adminroot -s /bin/bash adminroot}, spied
   end
 
   def test_useradd_success
     # binstub_new_user/useradd will succeed.
     File.open(@tmpdir+'/succeed', 'w') do |f|
-      f.puts 'useradd -m -c active -s /bin/bash -G fuse active'
-      f.puts 'useradd -m -c active -s /bin/bash -G  active'
-      # Accept either form; see note about groups in test_useradd_error.
-      f.puts 'useradd -m -c adminroot -s /bin/bash -G docker,fuse adminroot'
-      f.puts 'useradd -m -c adminroot -s /bin/bash -G docker,admin,fuse adminroot'
-      f.puts 'useradd -m -c adminroot -s /bin/bash -G docker adminroot'
-      f.puts 'useradd -m -c adminroot -s /bin/bash -G docker,admin adminroot'
+      f.puts 'useradd -m -c active -s /bin/bash -G active'
+      f.puts 'useradd -m -c adminroot -s /bin/bash adminroot'
     end
     $stderr.puts "*** Expect crash after getpwnam() fails:"
     invoke_sync binstubs: ['new_user']
index 13726836a4f4263d281a73539fa37653b4dc284a..4e68d09da27bae14ed9379e2f5ae082011e738a8 100644 (file)
@@ -72,7 +72,7 @@ func (*serviceSuite) testConfig(c *check.C) (*arvados.Cluster, error) {
        cluster.TLS.Insecure = client.Insecure
        cluster.PostgreSQL.Connection = testDBConfig()
        cluster.PostgreSQL.ConnectionPool = 12
-       cluster.Services.Websocket.InternalURLs = map[arvados.URL]arvados.ServiceInstance{arvados.URL{Host: ":"}: arvados.ServiceInstance{}}
+       cluster.Services.Websocket.InternalURLs = map[arvados.URL]arvados.ServiceInstance{{Host: ":"}: {}}
        cluster.ManagementToken = arvadostest.ManagementToken
        return cluster, nil
 }
index 5abaa90e36d1cfc60d286a336fb4551b6e1f5ee6..279d46c08b4ee98a652d78b88f949f1587453d8a 100755 (executable)
@@ -44,10 +44,6 @@ if test -z "$ARVADOS_ROOT" ; then
     ARVADOS_ROOT="$ARVBOX_DATA/arvados"
 fi
 
-if test -z "$SSO_ROOT" ; then
-    SSO_ROOT="$ARVBOX_DATA/sso-devise-omniauth-provider"
-fi
-
 if test -z "$COMPOSER_ROOT" ; then
     COMPOSER_ROOT="$ARVBOX_DATA/composer"
 fi
@@ -99,6 +95,10 @@ EOF
     fi
 }
 
+listusers() {
+    docker exec -ti $ARVBOX_CONTAINER /usr/local/lib/arvbox/edit_users.py /var/lib/arvados/cluster_config.yml $(getclusterid) list
+}
+
 wait_for_arvbox() {
     FF=/tmp/arvbox-fifo-$$
     mkfifo $FF
@@ -126,7 +126,6 @@ wait_for_arvbox() {
 docker_run_dev() {
     docker run \
           "--volume=$ARVADOS_ROOT:/usr/src/arvados:rw" \
-           "--volume=$SSO_ROOT:/usr/src/sso:rw" \
            "--volume=$COMPOSER_ROOT:/usr/src/composer:rw" \
            "--volume=$WORKBENCH2_ROOT:/usr/src/workbench2:rw" \
            "--volume=$PG_DATA:/var/lib/postgresql:rw" \
@@ -207,6 +206,7 @@ run() {
               --publish=25101:25101
               --publish=8001:8001
               --publish=8002:8002
+              --publish=4202:4202
              --publish=45000-45020:45000-45020"
     else
         PUBLIC=""
@@ -239,16 +239,13 @@ run() {
         if ! test -d "$ARVADOS_ROOT" ; then
             git clone https://git.arvados.org/arvados.git "$ARVADOS_ROOT"
         fi
-        if ! test -d "$SSO_ROOT" ; then
-            git clone https://github.com/arvados/sso-devise-omniauth-provider.git "$SSO_ROOT"
-        fi
         if ! test -d "$COMPOSER_ROOT" ; then
             git clone https://github.com/arvados/composer.git "$COMPOSER_ROOT"
             git -C "$COMPOSER_ROOT" checkout arvados-fork
             git -C "$COMPOSER_ROOT" pull
         fi
         if ! test -d "$WORKBENCH2_ROOT" ; then
-            git clone https://github.com/arvados/arvados-workbench2.git "$WORKBENCH2_ROOT"
+            git clone https://git.arvados.org/arvados-workbench2.git "$WORKBENCH2_ROOT"
         fi
 
         if [[ "$CONFIG" = test ]] ; then
@@ -268,11 +265,6 @@ run() {
                        /usr/local/lib/arvbox/runsu.sh \
                        /usr/local/lib/arvbox/waitforpostgres.sh
 
-                docker exec -ti \
-                       $ARVBOX_CONTAINER \
-                       /usr/local/lib/arvbox/runsu.sh \
-                       /var/lib/arvbox/service/sso/run-service --only-setup
-
                 docker exec -ti \
                        $ARVBOX_CONTAINER \
                        /usr/local/lib/arvbox/runsu.sh \
@@ -327,6 +319,11 @@ run() {
             wait_for_arvbox
             echo "The Arvados source code is checked out at: $ARVADOS_ROOT"
            echo "The Arvados testing root certificate is $VAR_DATA/root-cert.pem"
+           if [[ "$(listusers)" =~ ^\{\} ]] ; then
+               echo "No users defined, use 'arvbox adduser' to add user logins"
+           else
+               echo "Use 'arvbox listusers' to see user logins"
+           fi
         else
             echo "Unknown configuration '$CONFIG'"
         fi
@@ -511,6 +508,7 @@ case "$subcmd" in
                     exit 1
                 fi
                 set -x
+                chmod -R u+w "$ARVBOX_DATA"
                 rm -rf "$ARVBOX_DATA"
             else
                 if test "$1" != -f ; then
@@ -573,7 +571,6 @@ case "$subcmd" in
               "$ARVBOX_BASE/$1/gopath" \
               "$ARVBOX_BASE/$1/Rlibs" \
               "$ARVBOX_BASE/$1/arvados" \
-              "$ARVBOX_BASE/$1/sso-devise-omniauth-provider" \
               "$ARVBOX_BASE/$1/composer" \
               "$ARVBOX_BASE/$1/workbench2" \
               "$ARVBOX_BASE/$2"
@@ -632,6 +629,20 @@ sv restart keepproxy
 EOF
        ;;
 
+    adduser)
+       docker exec -ti $ARVBOX_CONTAINER /usr/local/lib/arvbox/edit_users.py /var/lib/arvados/cluster_config.yml.override $(getclusterid) add $@
+       docker exec $ARVBOX_CONTAINER sv restart controller
+       ;;
+
+    removeuser)
+       docker exec -ti $ARVBOX_CONTAINER /usr/local/lib/arvbox/edit_users.py /var/lib/arvados/cluster_config.yml.override $(getclusterid) remove $@
+       docker exec $ARVBOX_CONTAINER sv restart controller
+       ;;
+
+    listusers)
+       listusers
+       ;;
+
     *)
         echo "Arvados-in-a-box             https://doc.arvados.org/install/arvbox.html"
         echo
@@ -662,5 +673,10 @@ EOF
         echo "sv <start|stop|restart> <service> "
        echo "                   change state of service inside arvbox"
         echo "clone <from> <to>  clone dev arvbox"
+       echo "adduser <username> <email>"
+       echo "                   add a user login"
+       echo "removeuser <username>"
+       echo "                   remove user login"
+       echo "listusers          list user logins"
         ;;
 esac
index b6d6c68e31fadd292df47fa6ea9410f979167396..bde5ffe89826c3af5d1e0a8f6bd1e20b3062b842 100644 (file)
@@ -21,7 +21,7 @@ RUN apt-get update && \
     libgnutls28-dev python3-dev vim cadaver cython gnupg dirmngr \
     libsecret-1-dev r-base r-cran-testthat libxml2-dev pandoc \
     python3-setuptools python3-pip openjdk-8-jdk bsdmainutils net-tools \
-    ruby2.3 ruby-dev bundler && \
+    ruby2.3 ruby-dev bundler shellinabox && \
     apt-get clean
 
 ENV RUBYVERSION_MINOR 2.3
@@ -109,7 +109,7 @@ ADD gitolite.rc \
     keep-setup.sh common.sh createusers.sh \
     logger runsu.sh waitforpostgres.sh \
     yml_override.py api-setup.sh \
-    go-setup.sh devenv.sh cluster-config.sh \
+    go-setup.sh devenv.sh cluster-config.sh edit_users.py \
     /usr/local/lib/arvbox/
 
 ADD runit /etc/runit
index 34d3845eafae9ce7dfc89346f933ce2b59b54e35..ed728204fa171976ef316abd4060e8156b0da2ad 100644 (file)
@@ -4,21 +4,17 @@
 
 FROM arvados/arvbox-base
 ARG arvados_version
-ARG sso_version=master
 ARG composer_version=arvados-fork
 ARG workbench2_version=master
 
 RUN cd /usr/src && \
-    git clone --no-checkout https://github.com/arvados/arvados.git && \
+    git clone --no-checkout https://git.arvados.org/arvados.git && \
     git -C arvados checkout ${arvados_version} && \
     git -C arvados pull && \
-    git clone --no-checkout https://github.com/arvados/sso-devise-omniauth-provider.git sso && \
-    git -C sso checkout ${sso_version} && \
-    git -C sso pull && \
     git clone --no-checkout https://github.com/arvados/composer.git && \
     git -C composer checkout ${composer_version} && \
     git -C composer pull && \
-    git clone --no-checkout https://github.com/arvados/arvados-workbench2.git workbench2 && \
+    git clone --no-checkout https://git.arvados.org/arvados-workbench2.git workbench2 && \
     git -C workbench2 checkout ${workbench2_version} && \
     git -C workbench2 pull && \
     chown -R 1000:1000 /usr/src
@@ -27,7 +23,6 @@ ADD service/ /var/lib/arvbox/service
 RUN ln -sf /var/lib/arvbox/service /etc
 RUN mkdir -p /var/lib/arvados
 RUN echo "production" > /var/lib/arvados/api_rails_env
-RUN echo "production" > /var/lib/arvados/sso_rails_env
 RUN echo "production" > /var/lib/arvados/workbench_rails_env
 
 RUN /usr/local/lib/arvbox/createusers.sh
@@ -36,7 +31,6 @@ RUN sudo -u arvbox /var/lib/arvbox/service/api/run-service --only-deps
 RUN sudo -u arvbox /var/lib/arvbox/service/composer/run-service --only-deps
 RUN sudo -u arvbox /var/lib/arvbox/service/workbench2/run-service --only-deps
 RUN sudo -u arvbox /var/lib/arvbox/service/keep-web/run-service --only-deps
-RUN sudo -u arvbox /var/lib/arvbox/service/sso/run-service --only-deps
 RUN sudo -u arvbox /var/lib/arvbox/service/workbench/run-service --only-deps
 RUN sudo -u arvbox /var/lib/arvbox/service/doc/run-service --only-deps
 RUN sudo -u arvbox /var/lib/arvbox/service/vm/run-service --only-deps
index 22668253e1bf038c2bcbd297bff85233b92ee430..c7621e387dbdb51882e08669016a456810da6f24 100644 (file)
@@ -9,7 +9,6 @@ ADD service/ /var/lib/arvbox/service
 RUN ln -sf /var/lib/arvbox/service /etc
 RUN mkdir -p /var/lib/arvados
 RUN echo "development" > /var/lib/arvados/api_rails_env
-RUN echo "development" > /var/lib/arvados/sso_rails_env
 RUN echo "development" > /var/lib/arvados/workbench_rails_env
 
 RUN mkdir /etc/test-service && \
index 4ed25e03c05929bdceecd968d494e194500f7959..6a261bf4c55e17b0423071394d03213013a40a08 100755 (executable)
@@ -28,7 +28,6 @@ else
     secret_token=$(cat /var/lib/arvados/api_secret_token)
     blob_signing_key=$(cat /var/lib/arvados/blob_signing_key)
     management_token=$(cat /var/lib/arvados/management_token)
-    sso_app_secret=$(cat /var/lib/arvados/sso_app_secret)
     database_pw=$(cat /var/lib/arvados/api_database_pw)
     vm_uuid=$(cat /var/lib/arvados/vm-uuid)
 
@@ -37,10 +36,6 @@ $RAILS_ENV:
   uuid_prefix: $uuid_prefix
   secret_token: $secret_token
   blob_signing_key: $blob_signing_key
-  sso_app_secret: $sso_app_secret
-  sso_app_id: arvados-server
-  sso_provider_url: "https://$localip:${services[sso]}"
-  sso_insecure: false
   workbench_address: "https://$localip/"
   websocket_address: "wss://$localip:${services[websockets-ssl]}/websocket"
   git_repo_ssh_base: "git@$localip:"
index 4798cb6ccda8859bfc08376f281f7b7f2d9502cd..bebf983b6bf07dc1af6748b4583fd8d17b43e86c 100755 (executable)
@@ -39,11 +39,6 @@ if ! test -s /var/lib/arvados/system_root_token ; then
 fi
 system_root_token=$(cat /var/lib/arvados/system_root_token)
 
-if ! test -s /var/lib/arvados/sso_app_secret ; then
-    ruby -e 'puts rand(2**400).to_s(36)' > /var/lib/arvados/sso_app_secret
-fi
-sso_app_secret=$(cat /var/lib/arvados/sso_app_secret)
-
 if ! test -s /var/lib/arvados/vm-uuid ; then
     echo $uuid_prefix-2x53u-$(ruby -e 'puts rand(2**400).to_s(36)[0,15]') > /var/lib/arvados/vm-uuid
 fi
@@ -83,8 +78,6 @@ Clusters:
         ExternalURL: "https://$localip:${services[workbench]}"
       Workbench2:
         ExternalURL: "https://$localip:${services[workbench2-ssl]}"
-      SSO:
-        ExternalURL: "https://$localip:${services[sso]}"
       Keepproxy:
         ExternalURL: "https://$localip:${services[keepproxy-ssl]}"
         InternalURLs:
@@ -111,17 +104,15 @@ Clusters:
         InternalURLs:
           "http://localhost:${services[keep-web]}/": {}
         ExternalURL: "https://$localip:${services[keep-web-ssl]}/"
-        InternalURLs:
-          "http://localhost:${services[keep-web]}/": {}
       Composer:
         ExternalURL: "https://$localip:${services[composer]}"
       Controller:
         ExternalURL: "https://$localip:${services[controller-ssl]}"
         InternalURLs:
           "http://localhost:${services[controller]}": {}
-      RailsAPI:
-        InternalURLs:
-          "http://localhost:${services[api]}/": {}
+      WebShell:
+        InternalURLs: {}
+        ExternalURL: "https://$localip:${services[webshell-ssl]}"
     PostgreSQL:
       ConnectionPool: 32 # max concurrent connections per arvados server daemon
       Connection:
@@ -139,10 +130,8 @@ Clusters:
       DefaultReplication: 1
       TrustAllContent: true
     Login:
-      SSO:
+      Test:
         Enable: true
-        ProviderAppSecret: $sso_app_secret
-        ProviderAppID: arvados-server
     Users:
       NewUsersAreActive: true
       AutoAdminFirstUser: true
@@ -175,6 +164,18 @@ EOF
 
 cp /var/lib/arvados/cluster_config.yml /etc/arvados/config.yml
 
+chmod og-rw \
+      /var/lib/arvados/cluster_config.yml.override \
+      /var/lib/arvados/cluster_config.yml \
+      /etc/arvados/config.yml \
+      /var/lib/arvados/api_secret_token \
+      /var/lib/arvados/blob_signing_key \
+      /var/lib/arvados/management_token \
+      /var/lib/arvados/system_root_token \
+      /var/lib/arvados/api_database_pw \
+      /var/lib/arvados/workbench_secret_token \
+      /var/lib/arvados/superuser_token \
+
 mkdir -p /var/lib/arvados/run_tests
 cat >/var/lib/arvados/run_tests/config.yml <<EOF
 Clusters:
index 89864d5d18099cb044c3afac15895e55a0a22f79..e81e8108e249fc7215c4ca19c4e25a4bb81efbd9 100644 (file)
@@ -33,7 +33,6 @@ services=(
   [api]=8004
   [controller]=8003
   [controller-ssl]=8000
-  [sso]=8900
   [composer]=4200
   [arv-git-httpd-ssl]=9000
   [arv-git-httpd]=9001
@@ -47,6 +46,8 @@ services=(
   [doc]=8001
   [websockets]=8005
   [websockets-ssl]=8002
+  [webshell]=4201
+  [webshell-ssl]=4202
 )
 
 if test "$(id arvbox -u 2>/dev/null)" = 0 ; then
diff --git a/tools/arvbox/lib/arvbox/docker/edit_users.py b/tools/arvbox/lib/arvbox/docker/edit_users.py
new file mode 100755 (executable)
index 0000000..ab046b1
--- /dev/null
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+import ruamel.yaml
+import sys
+import getpass
+import os
+
+def print_help():
+    print("%s <path/to/config.yaml> <clusterid> add <username> <email> [pass]" % (sys.argv[0]))
+    print("%s <path/to/config.yaml> <clusterid> remove <username>" % (" " * len(sys.argv[0])))
+    print("%s <path/to/config.yaml> <clusterid> list" % (" " * len(sys.argv[0])))
+    exit()
+
+if len(sys.argv) < 4:
+    print_help()
+
+fn = sys.argv[1]
+cl = sys.argv[2]
+op = sys.argv[3]
+
+if op == "remove" and len(sys.argv) < 5:
+    print_help()
+if op == "add" and len(sys.argv) < 6:
+    print_help()
+
+if op in ("add", "remove"):
+    user = sys.argv[4]
+
+if not os.path.exists(fn):
+    open(fn, "w").close()
+
+with open(fn, "r") as f:
+    conf = ruamel.yaml.round_trip_load(f)
+
+if not conf:
+    conf = {}
+
+conf["Clusters"] = conf.get("Clusters", {})
+conf["Clusters"][cl] = conf["Clusters"].get(cl, {})
+conf["Clusters"][cl]["Login"] = conf["Clusters"][cl].get("Login", {})
+conf["Clusters"][cl]["Login"]["Test"] = conf["Clusters"][cl]["Login"].get("Test", {})
+conf["Clusters"][cl]["Login"]["Test"]["Users"] = conf["Clusters"][cl]["Login"]["Test"].get("Users", {})
+
+users_obj = conf["Clusters"][cl]["Login"]["Test"]["Users"]
+
+if op == "add":
+    email = sys.argv[5]
+    if len(sys.argv) == 7:
+        p = sys.argv[6]
+    else:
+        p = getpass.getpass("Password for %s: " % user)
+
+    users_obj[user] = {
+        "Email": email,
+        "Password": p
+    }
+    print("Added %s" % user)
+elif op == "remove":
+    del users_obj[user]
+    print("Removed %s" % user)
+elif op == "list":
+    print(ruamel.yaml.round_trip_dump(users_obj))
+else:
+    print("Operations are 'add', 'remove' and 'list'")
+
+with open(fn, "w") as f:
+    f.write(ruamel.yaml.round_trip_dump(conf))
index d6fecb4436069e431f80682af5baf66f0d04bf82..cfb7788defc080986bb91fa0e5f1e9b871b27c5f 100755 (executable)
@@ -186,6 +186,51 @@ http {
     }
   }
 
+
+upstream arvados-webshell {
+  server                localhost:${services[webshell]};
+}
+server {
+  listen                ${services[webshell-ssl]} ssl;
+  server_name           arvados-webshell;
+
+  proxy_connect_timeout 90s;
+  proxy_read_timeout    300s;
+
+  ssl                   on;
+  ssl_certificate "${server_cert}";
+  ssl_certificate_key "${server_cert_key}";
+
+  location / {
+    if (\$request_method = 'OPTIONS') {
+       add_header 'Access-Control-Allow-Origin' '*';
+       add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
+       add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
+       add_header 'Access-Control-Max-Age' 1728000;
+       add_header 'Content-Type' 'text/plain charset=UTF-8';
+       add_header 'Content-Length' 0;
+       return 204;
+    }
+    if (\$request_method = 'POST') {
+       add_header 'Access-Control-Allow-Origin' '*';
+       add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
+       add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
+    }
+    if (\$request_method = 'GET') {
+       add_header 'Access-Control-Allow-Origin' '*';
+       add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
+       add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
+    }
+
+    proxy_ssl_session_reuse off;
+    proxy_read_timeout  90;
+    proxy_set_header    X-Forwarded-Proto https;
+    proxy_set_header    Host \$http_host;
+    proxy_set_header    X-Real-IP \$remote_addr;
+    proxy_set_header    X-Forwarded-For \$proxy_add_x_forwarded_for;
+    proxy_pass          http://arvados-webshell;
+  }
+}
 }
 
 EOF
index 470d10537556ab797b95edb1042b06411703f820..21cb7d48c64060d0edf08ca135e8d7f4fa92fd4e 100755 (executable)
@@ -67,8 +67,7 @@ if ! [[ -z "$waiting" ]] ; then
 
         gemlockcount=0
         for l in /usr/src/arvados/services/api/Gemfile.lock \
-                     /usr/src/arvados/apps/workbench/Gemfile.lock \
-                     /usr/src/sso/Gemfile.lock ; do
+                     /usr/src/arvados/apps/workbench/Gemfile.lock ; do
             gc=$(cat $l \
                         | grep -vE "(GEM|PLATFORMS|DEPENDENCIES|BUNDLED|GIT|$^|remote:|specs:|revision:)" \
                         | sed 's/^ *//' | sed 's/(.*)//' | sed 's/ *$//' | sort | uniq | wc -l)
diff --git a/tools/arvbox/lib/arvbox/docker/service/sso/run b/tools/arvbox/lib/arvbox/docker/service/sso/run
deleted file mode 120000 (symlink)
index a388c8b..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/usr/local/lib/arvbox/runsu.sh
\ No newline at end of file
diff --git a/tools/arvbox/lib/arvbox/docker/service/sso/run-service b/tools/arvbox/lib/arvbox/docker/service/sso/run-service
deleted file mode 100755 (executable)
index e30e34f..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/bin/bash
-# Copyright (C) The Arvados Authors. All rights reserved.
-#
-# SPDX-License-Identifier: AGPL-3.0
-
-exec 2>&1
-set -ex -o pipefail
-
-. /usr/local/lib/arvbox/common.sh
-
-cd /usr/src/sso
-if test -s /var/lib/arvados/sso_rails_env ; then
-  export RAILS_ENV=$(cat /var/lib/arvados/sso_rails_env)
-else
-  export RAILS_ENV=development
-fi
-
-run_bundler --without=development
-bundle exec passenger-config build-native-support
-bundle exec passenger-config install-standalone-runtime
-
-if test "$1" = "--only-deps" ; then
-    exit
-fi
-
-set -u
-
-uuid_prefix=$(cat /var/lib/arvados/api_uuid_prefix)
-
-if ! test -s /var/lib/arvados/sso_secret_token ; then
-  ruby -e 'puts rand(2**400).to_s(36)' > /var/lib/arvados/sso_secret_token
-fi
-secret_token=$(cat /var/lib/arvados/sso_secret_token)
-
-openssl verify -CAfile $root_cert $server_cert
-
-cat >config/application.yml <<EOF
-$RAILS_ENV:
-  uuid_prefix: $uuid_prefix
-  secret_token: $secret_token
-  default_link_url: "http://$localip"
-  allow_account_registration: true
-EOF
-
-(cd config && /usr/local/lib/arvbox/yml_override.py application.yml)
-
-if ! test -f /var/lib/arvados/sso_database_pw ; then
-    ruby -e 'puts rand(2**128).to_s(36)' > /var/lib/arvados/sso_database_pw
-fi
-database_pw=$(cat /var/lib/arvados/sso_database_pw)
-
-if ! (psql postgres -c "\du" | grep "^ arvados_sso ") >/dev/null ; then
-    psql postgres -c "create user arvados_sso with password '$database_pw'"
-    psql postgres -c "ALTER USER arvados_sso CREATEDB;"
-fi
-
-sed "s/password:.*/password: $database_pw/" <config/database.yml.example >config/database.yml
-
-if ! test -f /var/lib/arvados/sso_database_setup ; then
-   bundle exec rake db:setup
-
-   app_secret=$(cat /var/lib/arvados/sso_app_secret)
-
-   bundle exec rails console <<EOF
-c = Client.new
-c.name = "joshid"
-c.app_id = "arvados-server"
-c.app_secret = "$app_secret"
-c.save!
-EOF
-
-   touch /var/lib/arvados/sso_database_setup
-fi
-
-rm -rf tmp
-mkdir -p tmp/cache
-
-bundle exec rake assets:precompile
-bundle exec rake db:migrate
-
-set +u
-if test "$1" = "--only-setup" ; then
-    exit
-fi
-
-exec bundle exec passenger start --port=${services[sso]} \
-     --ssl --ssl-certificate=/var/lib/arvados/server-cert-${localip}.pem \
-     --ssl-certificate-key=/var/lib/arvados/server-cert-${localip}.key
diff --git a/tools/arvbox/lib/arvbox/docker/service/webshell/run b/tools/arvbox/lib/arvbox/docker/service/webshell/run
new file mode 100755 (executable)
index 0000000..c2bf42f
--- /dev/null
@@ -0,0 +1,43 @@
+#!/bin/bash
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+exec 2>&1
+set -ex -o pipefail
+
+. /usr/local/lib/arvbox/common.sh
+
+/usr/local/lib/arvbox/runsu.sh $0-service
+
+cat > /etc/pam.d/shellinabox <<EOF
+# This example is a stock debian "login" file with pam_arvados
+# replacing pam_unix. It can be installed as /etc/pam.d/shellinabox .
+
+auth       optional   pam_faildelay.so  delay=3000000
+auth [success=ok new_authtok_reqd=ok ignore=ignore user_unknown=bad default=die] pam_securetty.so
+auth       requisite  pam_nologin.so
+session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
+session       required   pam_env.so readenv=1
+session       required   pam_env.so readenv=1 envfile=/etc/default/locale
+
+auth [success=1 default=ignore] /usr/local/lib/pam_arvados.so $localip:${services[controller-ssl]} $localip
+auth    requisite            pam_deny.so
+auth    required            pam_permit.so
+
+auth       optional   pam_group.so
+session    required   pam_limits.so
+session    optional   pam_lastlog.so
+session    optional   pam_motd.so  motd=/run/motd.dynamic
+session    optional   pam_motd.so
+session    optional   pam_mail.so standard
+
+@include common-account
+@include common-session
+@include common-password
+
+session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
+EOF
+
+exec shellinaboxd --verbose --port ${services[webshell]} --user arvbox --group arvbox \
+                  --disable-ssl --no-beep --service=/$localip:AUTH:HOME:SHELL
\ No newline at end of file
diff --git a/tools/arvbox/lib/arvbox/docker/service/webshell/run-service b/tools/arvbox/lib/arvbox/docker/service/webshell/run-service
new file mode 100755 (executable)
index 0000000..92b0c3d
--- /dev/null
@@ -0,0 +1,13 @@
+#!/bin/bash
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+exec 2>&1
+set -ex -o pipefail
+
+. /usr/local/lib/arvbox/common.sh
+. /usr/local/lib/arvbox/go-setup.sh
+
+flock /var/lib/gopath/gopath.lock go build -buildmode=c-shared -o ${GOPATH}/bin/pam_arvados.so git.arvados.org/arvados.git/lib/pam
+install $GOPATH/bin/pam_arvados.so /usr/local/lib
\ No newline at end of file
diff --git a/tools/copy-tutorial/copy-tutorial.sh b/tools/copy-tutorial/copy-tutorial.sh
new file mode 100755 (executable)
index 0000000..bdc75da
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+set -e
+
+if test -z "$1"  ; then
+  echo "$0: Copies Arvados tutorial resources from public data cluster (jutro)"
+  echo "Usage: copy-tutorial.sh <dest>"
+  echo "<dest> is destination cluster configuration that can be found in ~/.config/arvados"
+  exit
+fi
+
+echo "Copying from public data cluster (jutro) to $1"
+
+for a in $(cat $HOME/.config/arvados/$1.conf) ; do export $a ; done
+
+project_uuid=$(arv --format=uuid group create --group '{"name":"User guide resources", "group_class": "project"}')
+
+# Bwa-mem workflow
+arv-copy --src jutro --dst $1 --project-uuid=$project_uuid f141fc27e7cfa7f7b6d208df5e0ee01b+59
+arv-copy --src jutro --dst $1 --project-uuid=$project_uuid jutro-7fd4e-mkmmq53m1ze6apx
+
+echo "Data copied to \"User guide resources\" at $project_uuid"