Merge branch 'master' into 2221-complete-docker
authorTim Pierce <twp@curoverse.com>
Mon, 10 Mar 2014 14:49:41 +0000 (10:49 -0400)
committerTim Pierce <twp@curoverse.com>
Mon, 10 Mar 2014 14:49:41 +0000 (10:49 -0400)
13 files changed:
.gitignore
doc/install/client.html.textile.liquid
doc/user/tutorials/tutorial-job-debug.html.textile.liquid
doc/user/tutorials/tutorial-keep.html.textile.liquid
docker/Makefile
docker/api/Dockerfile
docker/base/Dockerfile
docker/config.rb
docker/doc/Dockerfile
docker/install.rb [new file with mode: 0755]
docker/workbench/Dockerfile
sdk/cli/bin/crunch-job
sdk/perl/Makefile.PL [new file with mode: 0644]

index 4d6cc39a237559d2d2530bef984194cb456ee1cf..f27930edc8e21749c176acd1c3e4d4dbc5f794bb 100644 (file)
@@ -4,4 +4,8 @@ docker/*/generated/*
 docker/config.yml
 doc/_site/*
 doc/.site/*
-doc/sdk/python/arvados
\ No newline at end of file
+doc/sdk/python/arvados
+sdk/perl/MYMETA.*
+sdk/perl/Makefile
+sdk/perl/blib/*
+sdk/perl/pm_to_blib
index f8d864af45804ba94fb9372db33d2af70e087f26..1e5151dbcf5165e1485c519f2d75d223a78e420c 100644 (file)
@@ -34,3 +34,17 @@ The arvados package includes the Ruby client library module. The arvados-cli pac
 {% include 'notebox_end' %}
 
 notextile. <pre><code>$ <span class="userinput">sudo gem install arvados arvados-cli</span></code></pre>
+
+h3. Perl
+
+{% include 'notebox_begin' %}
+The Perl client library includes the @Arvados.pm@ module and submodules.
+{% include 'notebox_end' %}
+
+<notextile>
+<pre><code>$ <span class="userinput">cd arvados/sdk/perl</span>
+$ <span class="userinput">perl Makefile.PL</span>
+$ <span class="userinput">sudo make install</span>
+</code></pre>
+</notextile>
+
index 28052089b389829368d511df44f00430ffa041f5..3a3cbace550f7fe85b75b144a0fa8d65b07b13d1 100644 (file)
@@ -75,7 +75,7 @@ bc. 2013-12-12_21:36:42 qr1hi-8i9sb-okzukfzkpbrnhst 29827 0 stderr hello world
 The script's output is captured in the log, which is useful for print statement debugging. However, although this script returned a status code of 0 (success), the job failed.  Why?  For a job to complete successfully scripts must explicitly add their output to Keep, and then tell Arvados about it.  Here is a second try:
 
 <notextile>
-<pre><code>~/<b>you</b>/crunch_scripts$ <span class="userinput">cat &gt;hello-world.py &lt;&lt;EOF
+<pre><code>~/<b>you</b>/crunch_scripts$ <span class="userinput">cat &gt;hello-world-fixed.py &lt;&lt;EOF
 #!/usr/bin/env python
 
 import arvados
@@ -109,7 +109,7 @@ EOF</span>
 2013-12-12_21:56:59 qr1hi-8i9sb-79260ykfew5trzl 31578  check slurm allocation
 2013-12-12_21:56:59 qr1hi-8i9sb-79260ykfew5trzl 31578  node localhost - 1 slots
 2013-12-12_21:57:00 qr1hi-8i9sb-79260ykfew5trzl 31578  start
-2013-12-12_21:57:00 qr1hi-8i9sb-79260ykfew5trzl 31578  script hello-world.py
+2013-12-12_21:57:00 qr1hi-8i9sb-79260ykfew5trzl 31578  script hello-world-fixed.py
 2013-12-12_21:57:00 qr1hi-8i9sb-79260ykfew5trzl 31578  script_version /home/you/you
 2013-12-12_21:57:00 qr1hi-8i9sb-79260ykfew5trzl 31578  script_parameters {}
 2013-12-12_21:57:00 qr1hi-8i9sb-79260ykfew5trzl 31578  runtime_constraints {"max_tasks_per_node":0}
@@ -126,11 +126,13 @@ EOF</span>
 2013-12-12_21:57:02 qr1hi-8i9sb-79260ykfew5trzl 31578  Freeze not implemented
 2013-12-12_21:57:02 qr1hi-8i9sb-79260ykfew5trzl 31578  collate
 2013-12-12_21:57:02 qr1hi-8i9sb-79260ykfew5trzl 31578  output 576c44d762ba241b0a674aa43152b52a+53
+WARNING:root:API lookup failed for collection 576c44d762ba241b0a674aa43152b52a+53 (<class 'apiclient.errors.HttpError'>: <HttpError 404 when requesting https://qr1hi.arvadosapi.com/arvados/v1/collections/576c44d762ba241b0a674aa43152b52a%2B53?alt=json returned "Not Found">)
 2013-12-12_21:57:03 qr1hi-8i9sb-79260ykfew5trzl 31578  finish
-2013-12-12_21:57:04 qr1hi-8i9sb-79260ykfew5trzl 31578  meta key is 9f937693334d0c9234ccc1f808ee7117+1761
 </code></pre>
 </notextile>
 
+(The WARNING issued near the end of the script may be safely ignored here; it is the Arvados SDK letting you know that it could not find a collection named @576c44d762ba241b0a674aa43152b52a+53@ and that it is going to try looking up a block by that name instead.)
+
 The job succeeded, with output in Keep object @576c44d762ba241b0a674aa43152b52a+53@.  Let's look at our output:
 
 <notextile>
index 81963638641a71507086a1617e2dc885ab22179d..f487feb75f89db249aedbbd58b308fafe961b7d9 100644 (file)
@@ -90,21 +90,7 @@ In this example we will use @c1bad4b39ca5a924e481008009d94e32+210@ which we adde
 
 The command @arv keep get@ fetches the contents of the locator @c1bad4b39ca5a924e481008009d94e32+210@.  This is a locator for a collection data block, so it fetches the contents of the collection.  In this example, this collection consists of a single file @var-GS000016015-ASM.tsv.bz2@ which is 227212247 bytes long, and is stored using four sequential data blocks, <code>204e43b8a1185621ca55a94839582e6f+67108864</code>, <code>b9677abbac956bd3e86b1deb28dfac03+67108864</code>, <code>fc15aff2a762b13f521baf042140acec+67108864</code>, <code>323d2a3ce20370c4ca1d3462a344f8fd+25885655</code>.
 
-Let's use @arv keep get@ to download the first datablock:
-
-notextile. <pre><code>/scratch/<b>you</b>$ <span class="userinput">arv keep get 204e43b8a1185621ca55a94839582e6f+67108864 &gt; block1</span></code></pre>
-
-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> of:
+Notice that the block identifer <code>204e43b8a1185621ca55a94839582e6f+67108864</code> consists of:
 * the md5 hash @204e43b8a1185621ca55a94839582e6f@ which matches the md5 hash of @block1@
 * a size hint @67108864@ which matches the size of @block1@
 
@@ -114,7 +100,7 @@ Next, let's use @arv keep get@ to download and reassemble @var-GS000016015-ASM.t
 <pre><code>/scratch/<b>you</b>$ <span class="userinput">arv keep get c1bad4b39ca5a924e481008009d94e32+210/var-GS000016015-ASM.tsv.bz2 .</span>
 </code></pre>
 
-This downloads the file @var-GS000016015-ASM.tsv.bz2@ described by collection @c1bad4b39ca5a924e481008009d94e32+210@ from Keep and places it into the local directory.  Now that we have the file, we can compute the md5 hash of the complete file:
+This downloads the file <code>var-GS000016015-ASM.tsv.bz2</code> described by collection <code>c1bad4b39ca5a924e481008009d94e32+210</code> from Keep and places it into the local directory.  Now that we have the file, we can compute the md5 hash of the complete file:
 
 <notextile>
 <pre><code>/scratch/<b>you</b>$ <span class="userinput">md5sum var-GS000016015-ASM.tsv.bz2</span>
@@ -128,9 +114,9 @@ There are a couple of other ways to access a collection.  You may view the conte
 
 <notextile>
 <pre><code>/scratch/<b>you</b>$ <span class="userinput">arv keep ls c1bad4b39ca5a924e481008009d94e32+210</span>
-var-GS000016015-ASM.tsv.bz2
+./var-GS000016015-ASM.tsv.bz2
 /scratch/<b>you</b>$ <span class="userinput">arv keep ls -s c1bad4b39ca5a924e481008009d94e32+210</span>
-221887 var-GS000016015-ASM.tsv.bz2
+    221886 ./var-GS000016015-ASM.tsv.bz2
 </code></pre>
 </notextile>
 
index c6f3dd7d120decd8f83cd7dda8c67e86a3f5fbe0..d949db059e45294a3f4c7f318cff1169dc23d0ac 100644 (file)
@@ -33,7 +33,7 @@ WAREHOUSE_DEPS = warehouse/Dockerfile \
 
 SSO_DEPS = sso/passenger.conf $(SSO_GENERATED)
 
-BASE_GENERATED = base/generated
+BASE_GENERATED = base/generated/arvados.tar.gz
 
 API_GENERATED = \
         api/generated/apache2_vhost \
@@ -79,6 +79,10 @@ SSO_GENERATED_IN = \
 
 $(BASE_GENERATED): config.yml
        ./config.rb
+       mkdir -p base/generated
+       tar -c -z -f base/generated/arvados.tar.gz -C .. . \
+         --exclude=services/api/log/* --exclude=docker/*
+
 
 $(API_GENERATED): config.yml $(API_GENERATED_IN)
        ./config.rb
@@ -102,7 +106,7 @@ DOCKER_BUILD = docker build -q
 
 api-image: passenger-image $(API_DEPS)
        mkdir -p api/generated
-       tar -c -z -f api/generated/api.tar.gz -C ../services api
+       tar -c -z -f api/generated/api.tar.gz -C ../services api --exclude=api/log/*
        $(DOCKER_BUILD) -t arvados/api api
        echo -n "Built at $(date)" > api-image
 
index d7fe554e92de873d3b6ab8e71ee2a0f0b97d0283..6f3b792f5852f6fbdf5ff37d6042cf7e982c8b17 100644 (file)
@@ -27,7 +27,8 @@ ADD generated/apache2_vhost /etc/apache2/sites-available/arvados
 ENV RAILS_ENV production
 ADD generated/config_databases.sh /tmp/config_databases.sh
 ADD generated/superuser_token /tmp/superuser_token
-RUN sh /tmp/config_databases.sh && \
+RUN bundle install --gemfile=/usr/src/arvados/services/api/Gemfile && \
+    sh /tmp/config_databases.sh && \
     rm /tmp/config_databases.sh && \
     /etc/init.d/postgresql start && \
     cd /usr/src/arvados/services/api && \
index de90a0939452b1c8b80babda7bd3d40b85c56a42..a64d007bf6a92092ea321f5a8772fbfb535d4fa4 100644 (file)
@@ -8,7 +8,7 @@ ENV DEBIAN_FRONTEND noninteractive
 
 # Install prerequisite packages for Arvados
 #   * git, curl, rvm
-#   * Arvados source code in /usr/src/arvados-upstream, for preseeding gem installation
+#   * Arvados source code in /usr/src/arvados, for preseeding gem installation
 
 RUN apt-get update && \
     apt-get -q -y install -q -y openssh-server apt-utils git curl locales postgresql-server-dev-9.1 && \
@@ -16,7 +16,9 @@ RUN apt-get update && \
     /bin/sed -ri 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
     /usr/sbin/locale-gen && \
     curl -L https://get.rvm.io | bash -s stable --ruby=2.1.0 && \
-    git clone https://github.com/curoverse/arvados.git /usr/src/arvados-upstream
+    /bin/mkdir -p /usr/src/arvados
+
+ADD generated/arvados.tar.gz /usr/src/arvados/
 
 # Set up RVM environment. These are just the env variables created by
 # /usr/local/rvm/scripts/rvm, which can't be run from a non-login shell.
@@ -29,9 +31,9 @@ ENV PATH /usr/local/rvm/gems/ruby-2.1.0/bin:/usr/local/rvm/gems/ruby-2.1.0@globa
 # https://github.com/rubygems/rubygems.org/issues/613.
 RUN gem update --system && \
     gem install bundler && \
-    bundle install --gemfile=/usr/src/arvados-upstream/apps/workbench/Gemfile && \
-    bundle install --gemfile=/usr/src/arvados-upstream/services/api/Gemfile && \
-    bundle install --gemfile=/usr/src/arvados-upstream/doc/Gemfile
+    bundle install --gemfile=/usr/src/arvados/apps/workbench/Gemfile && \
+    bundle install --gemfile=/usr/src/arvados/services/api/Gemfile && \
+    bundle install --gemfile=/usr/src/arvados/doc/Gemfile
 
 ADD generated/id_rsa.pub /root/.ssh/authorized_keys
 RUN chown root:root /root/.ssh/authorized_keys
index 5e7242b4d50a23eeeabbc55daab684c94e8f9e6e..ae69fe29eceb2f51fb87efc0d1e91069b3d98c77 100755 (executable)
@@ -64,9 +64,8 @@ end
 # Copy the ssh public key file to base/generated (if a path is given)
 generated_dir = File.join('base/generated')
 Dir.mkdir(generated_dir) unless Dir.exists? generated_dir
-if config.key?('PUBLIC_KEY_PATH') &&
-    ! (config['PUBLIC_KEY_PATH'] == '') &&
-    File.readable?(config['PUBLIC_KEY_PATH'])
+if (config['PUBLIC_KEY_PATH'] != nil and
+    File.readable? config['PUBLIC_KEY_PATH'])
   FileUtils.cp(config['PUBLIC_KEY_PATH'],
                File.join(generated_dir, 'id_rsa.pub'))
 end
index 3992881a8587792d5b41b690abc5a047e5951dd6..6e4d1aa359bfbfaa0000d8e0e447de102f74957c 100644 (file)
@@ -11,7 +11,8 @@ RUN /bin/mkdir -p /usr/src/arvados && \
 ADD generated/doc.tar.gz /usr/src/arvados/
 
 # Build static site
-RUN /bin/sed -ri 's/^baseurl: .*$/baseurl: /' /usr/src/arvados/doc/_config.yml && \
+RUN bundle install --gemfile=/usr/src/arvados/doc/Gemfile && \
+    /bin/sed -ri 's/^baseurl: .*$/baseurl: /' /usr/src/arvados/doc/_config.yml && \
     cd /usr/src/arvados/doc && \
     LANG="en_US.UTF-8" LC_ALL="en_US.UTF-8" rake
 
diff --git a/docker/install.rb b/docker/install.rb
new file mode 100755 (executable)
index 0000000..906178c
--- /dev/null
@@ -0,0 +1,170 @@
+#! /usr/bin/env ruby
+
+require 'tempfile'
+require 'yaml'
+
+def sudo(*cmd)
+  # user can pass a single list in as an argument
+  # to allow usage like: sudo %w(apt-get install foo)
+  if cmd.length == 1 and cmd[0].class == Array
+    cmd = cmd[0]
+  end
+  system '/usr/bin/sudo', *cmd
+end
+
+def is_valid_email? str
+  str.match /^\S+@\S+\.\S+$/
+end
+
+def generate_api_hostname
+  rand(2**256).to_s(36)[0...5]
+end
+
+# ip_forwarding_enabled?
+#   Returns 'true' if IP forwarding is enabled in the kernel
+#
+def ip_forwarding_enabled?
+  %x(/sbin/sysctl --values net.ipv4.ip_forward) == "1\n"
+end
+
+def find_ssh_key key_name
+  # If the user already has a key loaded in their agent, use one of those
+  agent_keys = `ssh-add -l`
+  if agent_keys.empty?
+    # Use a key named arvados_{key_name}_id_rsa, generating
+    # a passphraseless key if necessary.
+    ssh_key_file = "#{ENV['HOME']}/.ssh/arvados_#{key_name}_id_rsa"
+    unless File.exists? ssh_key_file
+      system 'ssh_keygen', '-f', ssh_key_file, '-P', ''
+    end
+  else
+    # choose an agent key at random
+    ssh_key_file = agent_keys.split("\n").first.split[2]
+  end
+
+  return File.exists?("#{ssh_key_file}.pub") ? "#{ssh_key_file}.pub" : nil
+end
+
+if not ip_forwarding_enabled?
+  warn "NOTE: IP forwarding must be enabled in the kernel."
+  warn "Turning IP forwarding on. You may be asked to enter your password."
+  sudo %w(/sbin/sysctl net.ipv4.ip_forward=1)
+end
+
+# Check that:
+#   * Docker is installed and can be found in the user's path
+#   * Docker can be run as a non-root user
+#      - TODO: put the user is in the docker group if necessary
+#      - TODO: mount cgroup automatically
+#      - TODO: start the docker service if not started
+
+docker_path = %x(which docker).chomp
+if docker_path.empty?
+  warn "Docker not found."
+  warn ""
+  warn "Please make sure that Docker has been installed and"
+  warn "can be found in your PATH."
+  warn ""
+  warn "Installation instructions for a variety of platforms can be found at"
+  warn "http://docs.docker.io/en/latest/installation/"
+  exit
+elsif not system 'docker images > /dev/null 2>&1'
+  warn "WARNING: docker could not be run."
+  warn "Please make sure that:"
+  warn "  * You have permission to read and write /var/run/docker.sock"
+  warn "  * a 'cgroup' volume is mounted on your machine"
+  warn "  * the docker daemon is running"
+end
+
+# Generate a config.yml if it does not exist
+if not File.exists? 'config.yml'
+  print "Generating config.yml.\n"
+  print "Arvados needs to know the email address of the administrative user,\n"
+  print "so that when that user logs in they are automatically made an admin.\n"
+  print "This should be the email address you use to log in to Google.\n"
+  print "\n"
+  admin_email_address = ""
+  until is_valid_email? admin_email_address
+    print "Enter your Google ID email address here: "
+    admin_email_address = gets.strip
+    if not is_valid_email? admin_email_address
+      print "That doesn't look like a valid email address. Please try again.\n"
+    end
+  end
+
+  File.open 'config.yml', 'w' do |config_out|
+    config = YAML.load_file 'config.yml.example'
+    config['API_AUTO_ADMIN_USER'] = admin_email_address
+    config['API_HOSTNAME'] = generate_api_hostname
+    config['PUBLIC_KEY_PATH'] = find_ssh_key(config['API_HOSTNAME'])
+    config.each_key do |var|
+      if var.end_with?('_PW') or var.end_with?('_SECRET')
+        config[var] = rand(2**256).to_s(36)
+      end
+      config_out.write "#{var}: #{config[var]}\n"
+    end
+  end
+end
+
+# If all prerequisites are met, go ahead and build.
+if ip_forwarding_enabled? and
+    not docker_path.empty? and
+    File.exists? 'config.yml'
+  warn "Building Arvados."
+  system '/usr/bin/make', *ARGV
+end
+
+# install_docker
+#   Determine which Docker package is suitable for this Linux distro
+#   and install, resolving any dependencies.
+#   NOTE: not in use yet.
+
+def install_docker
+  linux_distro = %x(lsb_release --id).split.last
+  linux_release = %x(lsb_release --release).split.last
+  linux_version = linux_distro + " " + linux_release
+  kernel_release = `uname -r`
+  
+  case linux_distro
+  when 'Ubuntu'
+    if not linux_release.match '^1[234]\.'
+      warn "Arvados requires at least Ubuntu 12.04 (Precise Pangolin)."
+      warn "Your system is Ubuntu #{linux_release}."
+      exit
+    end
+    if linux_release.match '^12' and kernel_release.start_with? '3.2'
+      # Ubuntu Precise ships with a 3.2 kernel and must be upgraded.
+      warn "Your kernel #{kernel_release} must be upgraded to run Docker."
+      warn "To do this:"
+      warn "  sudo apt-get update"
+      warn "  sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring"
+      warn "  sudo reboot"
+      exit
+    else
+      # install AUFS
+      sudo 'apt-get', 'update'
+      sudo 'apt-get', 'install', "linux-image-extra-#{kernel_release}"
+    end
+
+    # add Docker repository
+    sudo %w(/usr/bin/apt-key adv
+              --keyserver keyserver.ubuntu.com
+              --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9)
+    source_file = Tempfile.new('arv')
+    source_file.write("deb http://get.docker.io/ubuntu docker main\n")
+    source_file.close
+    sudo '/bin/mv', source_file.path, '/etc/apt/sources.list.d/docker.list'
+    sudo %w(/usr/bin/apt-get update)
+    sudo %w(/usr/bin/apt-get install lxc-docker)
+
+    # Set up for non-root access
+    sudo %w(/usr/sbin/groupadd docker)
+    sudo '/usr/bin/gpasswd', '-a', ENV['USER'], 'docker'
+    sudo %w(/usr/sbin/service docker restart)
+  when 'Debian'
+  else
+    warn "Must be running a Debian or Ubuntu release in order to run Docker."
+    exit
+  end
+end
+
index fea6947869e9dfe698fb8e0e58bce228571d2ba6..0c5c294916ff51218e00203e19a923e203003594 100644 (file)
@@ -7,12 +7,14 @@ MAINTAINER Ward Vandewege <ward@curoverse.com>
 RUN /bin/mkdir -p /usr/src/arvados/apps
 ADD generated/workbench.tar.gz /usr/src/arvados/apps/
 
-RUN touch /usr/src/arvados/apps/workbench/log/production.log && \
+RUN bundle install --gemfile=/usr/src/arvados/apps/workbench/Gemfile && \
+    touch /usr/src/arvados/apps/workbench/log/production.log && \
     chmod 666 /usr/src/arvados/apps/workbench/log/production.log && \
     touch /usr/src/arvados/apps/workbench/db/production.sqlite3 && \
     bundle install --gemfile=/usr/src/arvados/apps/workbench/Gemfile && \
     cd /usr/src/arvados/apps/workbench && \
-    rake assets:precompile
+    rake assets:precompile && \
+    chown -R www-data:www-data /usr/src/arvados/apps/workbench
 
 # Configure Apache
 ADD generated/apache2_vhost /etc/apache2/sites-available/workbench
index 2ba36f2b25ba939957ee3c6fb81baad66797a16e..87b4fbf8a5385f498c2e016f9225d467a3dd664a 100755 (executable)
@@ -71,9 +71,8 @@ use POSIX ':sys_wait_h';
 use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
 use Arvados;
 use Getopt::Long;
-use Warehouse;
-use Warehouse::Stream;
-use IPC::System::Simple qw(capturex);
+use IPC::Open2;
+use IO::Select;
 
 $ENV{"TMPDIR"} ||= "/tmp";
 unless (defined $ENV{"CRUNCH_TMP"}) {
@@ -166,10 +165,10 @@ else
 }
 $job_id = $Job->{'uuid'};
 
-$metastream = Warehouse::Stream->new(whc => new Warehouse);
-$metastream->clear;
-$metastream->name('.');
-$metastream->write_start($job_id . '.log.txt');
+$metastream = Warehouse::Stream->new(whc => new Warehouse);
+$metastream->clear;
+$metastream->name('.');
+$metastream->write_start($job_id . '.log.txt');
 
 
 $Job->{'runtime_constraints'} ||= {};
@@ -737,7 +736,7 @@ if ($job_has_uuid) {
 if ($Job->{'output'})
 {
   eval {
-    my $manifest_text = capturex("whget", $Job->{'output'});
+    my $manifest_text = `arv keep get $Job->{'output'}`;
     $arv->{'collections'}->{'create'}->execute('collection' => {
       'uuid' => $Job->{'output'},
       'manifest_text' => $manifest_text,
@@ -1037,12 +1036,23 @@ sub process_stderr
   } split ("\n", $jobstep[$job]->{stderr});
 }
 
+sub fetch_block
+{
+  my $hash = shift;
+  my ($child_out, $child_in, $output_block);
+
+  my $pid = open2($child_out, $child_in, 'arv', 'keep', 'get', $hash);
+  sysread($child_out, $output_block, 64 * 1024 * 1024);
+  waitpid($pid, 0);
+  return $output_block;
+}
 
 sub collate_output
 {
-  my $whc = Warehouse->new;
   Log (undef, "collate");
-  $whc->write_start (1);
+
+  my ($child_out, $child_in);
+  my $pid = open2($child_out, $child_in, 'arv', 'keep', 'put', '--raw');
   my $joboutput;
   for (@jobstep)
   {
@@ -1053,26 +1063,31 @@ sub collate_output
     if ($output !~ /^[0-9a-f]{32}(\+\S+)*$/)
     {
       $output_in_keep ||= $output =~ / [0-9a-f]{32}\S*\+K/;
-      $whc->write_data ($output);
+      print $child_in $output;
     }
     elsif (@jobstep == 1)
     {
       $joboutput = $output;
-      $whc->write_finish;
+      last;
     }
-    elsif (defined (my $outblock = $whc->fetch_block ($output)))
+    elsif (defined (my $outblock = fetch_block ($output)))
     {
       $output_in_keep ||= $outblock =~ / [0-9a-f]{32}\S*\+K/;
-      $whc->write_data ($outblock);
+      print $child_in $outblock;
     }
     else
     {
-      my $errstr = $whc->errstr;
-      $whc->write_data ("XXX fetch_block($output) failed: $errstr XXX\n");
+      print $child_in "XXX fetch_block($output) failed XXX\n";
       $main::success = 0;
     }
   }
-  $joboutput = $whc->write_finish if !defined $joboutput;
+  if (!defined $joboutput) {
+    my $s = IO::Select->new($child_out);
+    sysread($child_out, $joboutput, 64 * 1024 * 1024) if $s->can_read(0);
+  }
+  $child_in->close;
+  waitpid($pid, 0);
+
   if ($joboutput)
   {
     Log (undef, "output $joboutput");
@@ -1147,8 +1162,8 @@ sub Log                           # ($jobstep_id, $logmessage)
   }
   print STDERR ((-t STDERR) ? ($datetime." ".$message) : $message);
 
-  return if !$metastream;
-  $metastream->write_data ($datetime . " " . $message);
+  return if !$metastream;
+  $metastream->write_data ($datetime . " " . $message);
 }
 
 
@@ -1176,20 +1191,20 @@ sub cleanup
 
 sub save_meta
 {
-  my $justcheckpoint = shift; # false if this will be the last meta saved
-  my $m = $metastream;
-  $m = $m->copy if $justcheckpoint;
-  $m->write_finish;
-  my $whc = Warehouse->new;
-  my $loglocator = $whc->store_block ($m->as_string);
-  $arv->{'collections'}->{'create'}->execute('collection' => {
-    'uuid' => $loglocator,
-    'manifest_text' => $m->as_string,
-  });
-  undef $metastream if !$justcheckpoint; # otherwise Log() will try to use it
-  Log (undef, "log manifest is $loglocator");
-  $Job->{'log'} = $loglocator;
-  $Job->update_attributes('log', $loglocator) if $job_has_uuid;
+#  my $justcheckpoint = shift; # false if this will be the last meta saved
+#  my $m = $metastream;
+#  $m = $m->copy if $justcheckpoint;
+#  $m->write_finish;
+#  my $whc = Warehouse->new;
+#  my $loglocator = $whc->store_block ($m->as_string);
+#  $arv->{'collections'}->{'create'}->execute('collection' => {
+#    'uuid' => $loglocator,
+#    'manifest_text' => $m->as_string,
+#  });
+#  undef $metastream if !$justcheckpoint; # otherwise Log() will try to use it
+#  Log (undef, "log manifest is $loglocator");
+#  $Job->{'log'} = $loglocator;
+#  $Job->update_attributes('log', $loglocator) if $job_has_uuid;
 }
 
 
@@ -1233,64 +1248,64 @@ sub thaw
 {
   croak ("Thaw not implemented");
 
-  my $whc;
-  my $key = shift;
-  Log (undef, "thaw from $key");
-
-  @jobstep = ();
-  @jobstep_done = ();
-  @jobstep_todo = ();
-  @jobstep_tomerge = ();
-  $jobstep_tomerge_level = 0;
-  my $frozenjob = {};
-
-  my $stream = new Warehouse::Stream ( whc => $whc,
-                                      hash => [split (",", $key)] );
-  $stream->rewind;
-  while (my $dataref = $stream->read_until (undef, "\n\n"))
-  {
-    if ($$dataref =~ /^job /)
-    {
-      foreach (split ("\n", $$dataref))
-      {
-       my ($k, $v) = split ("=", $_, 2);
-       $frozenjob->{$k} = freezeunquote ($v);
-      }
-      next;
-    }
-
-    if ($$dataref =~ /^merge (\d+) (.*)/)
-    {
-      $jobstep_tomerge_level = $1;
-      @jobstep_tomerge
-         = map { freezeunquote ($_) } split ("\n", freezeunquote($2));
-      next;
-    }
-
-    my $Jobstep = { };
-    foreach (split ("\n", $$dataref))
-    {
-      my ($k, $v) = split ("=", $_, 2);
-      $Jobstep->{$k} = freezeunquote ($v) if $k;
-    }
-    $Jobstep->{'failures'} = 0;
-    push @jobstep, $Jobstep;
-
-    if ($Jobstep->{exitcode} eq "0")
-    {
-      push @jobstep_done, $#jobstep;
-    }
-    else
-    {
-      push @jobstep_todo, $#jobstep;
-    }
-  }
-
-  foreach (qw (script script_version script_parameters))
-  {
-    $Job->{$_} = $frozenjob->{$_};
-  }
-  $Job->save if $job_has_uuid;
+  my $whc;
+  my $key = shift;
+  Log (undef, "thaw from $key");
+
+  @jobstep = ();
+  @jobstep_done = ();
+  @jobstep_todo = ();
+  @jobstep_tomerge = ();
+  $jobstep_tomerge_level = 0;
+  my $frozenjob = {};
+
+  my $stream = new Warehouse::Stream ( whc => $whc,
+  #                                   hash => [split (",", $key)] );
+  $stream->rewind;
+  while (my $dataref = $stream->read_until (undef, "\n\n"))
+  {
+    if ($$dataref =~ /^job /)
+    {
+      foreach (split ("\n", $$dataref))
+      {
+  #    my ($k, $v) = split ("=", $_, 2);
+  #    $frozenjob->{$k} = freezeunquote ($v);
+      }
+      next;
+    }
+
+    if ($$dataref =~ /^merge (\d+) (.*)/)
+    {
+      $jobstep_tomerge_level = $1;
+      @jobstep_tomerge
+  #      = map { freezeunquote ($_) } split ("\n", freezeunquote($2));
+      next;
+    }
+
+    my $Jobstep = { };
+    foreach (split ("\n", $$dataref))
+    {
+      my ($k, $v) = split ("=", $_, 2);
+      $Jobstep->{$k} = freezeunquote ($v) if $k;
+    }
+    $Jobstep->{'failures'} = 0;
+    push @jobstep, $Jobstep;
+
+    if ($Jobstep->{exitcode} eq "0")
+    {
+      push @jobstep_done, $#jobstep;
+    }
+    else
+    {
+      push @jobstep_todo, $#jobstep;
+    }
+  }
+
+  foreach (qw (script script_version script_parameters))
+  {
+    $Job->{$_} = $frozenjob->{$_};
+  }
+  $Job->save if $job_has_uuid;
 }
 
 
diff --git a/sdk/perl/Makefile.PL b/sdk/perl/Makefile.PL
new file mode 100644 (file)
index 0000000..21e31ad
--- /dev/null
@@ -0,0 +1,10 @@
+#! /usr/bin/perl
+
+use strict;
+
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+    NAME            => 'Arvados',
+    VERSION_FROM    => 'lib/Arvados.pm'
+);