Consume all arguments after arv-mount --exec, and fork subprocess
[arvados.git] / sdk / cli / bin / arv-tag
index 67def8412dace9210473ebadba0f9d4ce5882c5e..9ce3f3d237bc36a47b796294b2f88cd7e3ca10b1 100755 (executable)
@@ -12,49 +12,76 @@ def usage
     "arv tag remove --all\n"
 end
 
-def tag_add(tag, obj_uuid)
-  request_body = {
-    :api_token => ENV['ARVADOS_API_TOKEN'],
-    :link => {
-      :name       => tag,
-      :link_class => :tag,
-      :head_uuid  => obj_uuid,
-    }
-  }
-
-  result = $client.execute(:api_method => $arvados.links.create,
+def api_call(method, parameters:{}, request_body:{})
+  request_body[:api_token] = ENV['ARVADOS_API_TOKEN']
+  result = $client.execute(:api_method => method,
+                           :parameters => parameters,
                            :body => request_body,
                            :authenticated => false)
 
   begin
     results = JSON.parse result.body
   rescue JSON::ParserError => e
-    $stderr.puts "Failed to parse server response:\n" + e.to_s
-    return nil
+    abort "Failed to parse server response:\n" + e.to_s
   end
 
-  if results["errors"] then
-    $stderr.puts "Error: #{results["errors"][0]}"
-    return nil
+  if results["errors"]
+    abort "Error: #{results["errors"][0]}"
   end
 
   return results
 end
 
-def tag_remove(tag, obj_uuid=nil)
-  request_body = {
-    :api_token => ENV['ARVADOS_API_TOKEN'],
-    :link => {
-      :name       => tag,
-      :link_class => :tag,
-      :head_uuid  => obj_uuid,
-    }
-  }
+def tag_add(tag, obj_uuid)
+  return api_call($arvados.links.create,
+                  request_body: {
+                    :link => {
+                      :name       => tag,
+                      :link_class => :tag,
+                      :head_uuid  => obj_uuid,
+                    }
+                  })
+end
+
+def tag_remove(tag, obj_uuids=nil)
+  # If we got a list of objects to untag, look up the uuids for the
+  # links that need to be deleted.
+  link_uuids = []
+  if obj_uuids
+    obj_uuids.each do |uuid|
+      link = api_call($arvados.links.list,
+                      request_body: {
+                        :where => {
+                          :link_class => :tag,
+                          :name => tag,
+                          :head_uuid => uuid,
+                        }
+                      })
+      if link['items_available'] > 0
+        link_uuids.push link['items'][0]['uuid']
+      end
+    end
+  else
+    all_tag_links = api_call($arvados.links.list,
+                             request_body: {
+                               :where => {
+                                 :link_class => :tag,
+                                 :name => tag,
+                               }
+                             })
+    link_uuids = all_tag_links['items'].map { |obj| obj['uuid'] }
+  end
+
+  results = []
+  if link_uuids
+    link_uuids.each do |uuid|
+      results.push api_call($arvados.links.delete, parameters:{ :uuid => uuid })
+    end
+  else
+    $stderr.puts "no tags found to remove"
+  end
 
-  return $client.execute(:api_method => $arvados.links.destroy,
-                         :parameters => params,
-                         :body => request_body,
-                         :authenticated => false)
+  return results
 end
 
 if RUBY_VERSION < '1.9.3' then
@@ -75,6 +102,7 @@ begin
   require 'google/api_client'
   require 'json'
   require 'pp'
+  require 'oj'
   require 'trollop'
 rescue LoadError
   abort <<-EOS
@@ -159,6 +187,11 @@ $arvados = $client.discovered_api('arvados', $arvados_api_version)
 
 results = []
 cmd = ARGV.shift
+
+if ARGV.empty?
+  usage
+end
+
 case cmd
 when 'add'
   ARGV.each do |tag|
@@ -169,11 +202,9 @@ when 'add'
 when 'remove'
   ARGV.each do |tag|
     if $options[:all] then
-      results.push(tag_remove(tag))
+      results.concat tag_remove(tag)
     else
-      $options[:object].each do |obj|
-        results.push(tag_remove(tag, obj))
-      end
+      results.concat tag_remove(tag, $options[:object])
     end
   end
 else
@@ -188,10 +219,7 @@ elsif global_opts[:json] then
   puts Oj.dump(results)
 else
   results.each do |r|
-    next if r == nil
-    if r["items"] and r["kind"].match /list$/i
-      r['items'].each do |i| puts i['uuid'] end
-    elsif r['uuid'].nil?
+    if r['uuid'].nil?
       abort("Response did not include a uuid:\n" +
             Oj.dump(r, :indent => 1) +
             "\n")