af 0.3.20 → 0.3.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +5 -1
- data/lib/cli/commands/apps.rb +137 -9
- data/lib/cli/runner.rb +21 -3
- data/lib/cli/usage.rb +6 -2
- data/lib/cli/version.rb +1 -1
- data/lib/cli/zip_util.rb +26 -1
- data/lib/vmc/client.rb +15 -4
- metadata +38 -38
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 197163395337c5e749f4feb2f2c14b3cc8563cf5
         | 
| 4 | 
            +
              data.tar.gz: 20f9ee7dceb421e7dddceabe267b3752e2e3c1e5
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 49e0e6a72481d754419ac130b6bf23275004ee450ae63347e64c9a0c0513fd06daac84645e94f2cdff29755af1e518312b14b4972b65ce7d88b7d398d3b04e2a
         | 
| 7 | 
            +
              data.tar.gz: 68241d400f940c214dc218a9137a2f693da3c2f08aeba6f80b323a912e3284d67e693f9603a12ebe4ee75075e6b9ae567e72a8a8e5b54c2ce36340c87bad1e1c
         | 
    
        data/README.md
    CHANGED
    
    | @@ -42,6 +42,7 @@ MIT license, please see the LICENSE file.  All rights reserved._ | |
| 42 42 | 
             
                  push [appname] --instances <N>               Set the expected number <N> of instances
         | 
| 43 43 | 
             
                  push [appname] --mem M                       Set the memory reservation for the application
         | 
| 44 44 | 
             
                  push [appname] --no-start                    Do not auto-start the application
         | 
| 45 | 
            +
                  push [appname] --label                       Add specified label to app revision record
         | 
| 45 46 |  | 
| 46 47 | 
             
                Application Download
         | 
| 47 48 | 
             
                  pull <appname> [path]                        Downloads last pushed source to <appname> or [path]
         | 
| @@ -53,7 +54,7 @@ MIT license, please see the LICENSE file.  All rights reserved._ | |
| 53 54 | 
             
                  delete <appname>                             Delete the application
         | 
| 54 55 |  | 
| 55 56 | 
             
                Application Updates
         | 
| 56 | 
            -
                  update <appname> [--path] | 
| 57 | 
            +
                  update <appname> [--path] [--label]          Update the application bits, with optional revision label
         | 
| 57 58 | 
             
                  mem <appname> [memsize]                      Update the memory reservation for an application
         | 
| 58 59 | 
             
                  map <appname> <url>                          Register the application to the url
         | 
| 59 60 | 
             
                  unmap <appname> <url>                        Unregister the application from the url
         | 
| @@ -67,6 +68,9 @@ MIT license, please see the LICENSE file.  All rights reserved._ | |
| 67 68 | 
             
                  files <appname> [path] [--all]               Display directory listing or file download for path
         | 
| 68 69 | 
             
                  stats <appname>                              Display resource usage for the application
         | 
| 69 70 | 
             
                  instances <appname>                          List application instances
         | 
| 71 | 
            +
                  history <appname>                            Show version history of the application
         | 
| 72 | 
            +
                  diff <appname>                               Compare current directory with deployed application
         | 
| 73 | 
            +
                  hash [path] [--full]                         Compute hash of directory, defaults to current
         | 
| 70 74 |  | 
| 71 75 | 
             
                Application Environment
         | 
| 72 76 | 
             
                  env <appname>                                List application environment variables
         | 
    
        data/lib/cli/commands/apps.rb
    CHANGED
    
    | @@ -6,6 +6,7 @@ require 'tmpdir' | |
| 6 6 | 
             
            require 'set'
         | 
| 7 7 | 
             
            require "uuidtools"
         | 
| 8 8 | 
             
            require 'socket'
         | 
| 9 | 
            +
            require 'digest/md5'
         | 
| 9 10 |  | 
| 10 11 | 
             
            module VMC::Cli::Command
         | 
| 11 12 |  | 
| @@ -228,6 +229,12 @@ module VMC::Cli::Command | |
| 228 229 |  | 
| 229 230 | 
             
                def clone(src_appname, dest_appname, dest_infra=nil)
         | 
| 230 231 |  | 
| 232 | 
            +
                  if (@options[:label])
         | 
| 233 | 
            +
                    label = @options[:label]
         | 
| 234 | 
            +
                  else
         | 
| 235 | 
            +
                    label = ''
         | 
| 236 | 
            +
                  end
         | 
| 237 | 
            +
             | 
| 231 238 | 
             
                  # FIXME need to ask for dest_appname if nil
         | 
| 232 239 |  | 
| 233 240 | 
             
                  err "Application '#{dest_appname}' already exists" if app_exists?(dest_appname)
         | 
| @@ -265,7 +272,7 @@ module VMC::Cli::Command | |
| 265 272 | 
             
                    client.create_app(dest_appname, manifest)
         | 
| 266 273 |  | 
| 267 274 | 
             
                    # Stage and upload the app bits.
         | 
| 268 | 
            -
                    upload_app_bits(dest_appname, zip_path, dest_infra)
         | 
| 275 | 
            +
                    upload_app_bits(dest_appname, zip_path, dest_infra, label)
         | 
| 269 276 |  | 
| 270 277 | 
             
                    # Clone services
         | 
| 271 278 | 
             
                    client.services.select { |s| app[:services].include?(s[:name])}.each do |service|
         | 
| @@ -367,13 +374,19 @@ module VMC::Cli::Command | |
| 367 374 | 
             
                end
         | 
| 368 375 |  | 
| 369 376 | 
             
                def update(appname=nil)
         | 
| 377 | 
            +
                  if (@options[:label])
         | 
| 378 | 
            +
                    label = @options[:label]
         | 
| 379 | 
            +
                  else
         | 
| 380 | 
            +
                    label = ''
         | 
| 381 | 
            +
                  end
         | 
| 382 | 
            +
             | 
| 370 383 | 
             
                  if appname
         | 
| 371 384 | 
             
                    app = client.app_info(appname)
         | 
| 372 385 | 
             
                    if @options[:canary]
         | 
| 373 386 | 
             
                      display "[--canary] is deprecated and will be removed in a future version".yellow
         | 
| 374 387 | 
             
                    end
         | 
| 375 388 | 
             
                    infra = app[:infra] ? app[:infra][:provider] : nil
         | 
| 376 | 
            -
                    upload_app_bits(appname, @path, infra)
         | 
| 389 | 
            +
                    upload_app_bits(appname, @path, infra, label)
         | 
| 377 390 | 
             
                    restart appname if app[:state] == 'STARTED'
         | 
| 378 391 | 
             
                  else
         | 
| 379 392 | 
             
                    each_app do |name|
         | 
| @@ -381,13 +394,19 @@ module VMC::Cli::Command | |
| 381 394 |  | 
| 382 395 | 
             
                      app = client.app_info(name)
         | 
| 383 396 | 
             
                      infra = app[:infra] ? app[:infra][:provider] : nil
         | 
| 384 | 
            -
                      upload_app_bits(name, @application, infra)
         | 
| 397 | 
            +
                      upload_app_bits(name, @application, infra, label)
         | 
| 385 398 | 
             
                      restart name if app[:state] == 'STARTED'
         | 
| 386 399 | 
             
                    end
         | 
| 387 400 | 
             
                  end
         | 
| 388 401 | 
             
                end
         | 
| 389 402 |  | 
| 390 403 | 
             
                def push(appname=nil)
         | 
| 404 | 
            +
                  if (@options[:label])
         | 
| 405 | 
            +
                    label = @options[:label]
         | 
| 406 | 
            +
                  else
         | 
| 407 | 
            +
                    label = ''
         | 
| 408 | 
            +
                  end
         | 
| 409 | 
            +
             | 
| 391 410 | 
             
                  unless no_prompt || @options[:path]
         | 
| 392 411 | 
             
                    proceed = ask(
         | 
| 393 412 | 
             
                      'Would you like to deploy from the current directory?',
         | 
| @@ -402,13 +421,73 @@ module VMC::Cli::Command | |
| 402 421 | 
             
                  pushed = false
         | 
| 403 422 | 
             
                  each_app(false) do |name|
         | 
| 404 423 | 
             
                    display "Pushing application '#{name}'..." if name
         | 
| 405 | 
            -
                    do_push(name)
         | 
| 424 | 
            +
                    do_push(label, name)
         | 
| 406 425 | 
             
                    pushed = true
         | 
| 407 426 | 
             
                  end
         | 
| 408 427 |  | 
| 409 428 | 
             
                  unless pushed
         | 
| 410 429 | 
             
                    @application = @path
         | 
| 411 | 
            -
                    do_push(appname)
         | 
| 430 | 
            +
                    do_push(label, appname)
         | 
| 431 | 
            +
                  end
         | 
| 432 | 
            +
                end
         | 
| 433 | 
            +
             | 
| 434 | 
            +
                def history(appname)
         | 
| 435 | 
            +
                  if app_exists?(appname)
         | 
| 436 | 
            +
                    history = client.app_history(appname)
         | 
| 437 | 
            +
             | 
| 438 | 
            +
                    # return display JSON.pretty_generate(history) if @options[:json]
         | 
| 439 | 
            +
                    return display "No history available" if history.empty?
         | 
| 440 | 
            +
                    history_table = table do |t|
         | 
| 441 | 
            +
                      t.headings = 'Label', 'Release ', 'By User', 'Release Date', 'Hash', 'Changed'
         | 
| 442 | 
            +
                      history.each do |app|
         | 
| 443 | 
            +
                        a = [app[:label], "v" << app[:release].to_s, app[:updated_by], Time.parse(app[:updated_at]).to_time, app[:update_hash][0..9], app[:is_changed]==true ? "Yes" : "No"]
         | 
| 444 | 
            +
                        t << a
         | 
| 445 | 
            +
                      end
         | 
| 446 | 
            +
                    end
         | 
| 447 | 
            +
                    display "\n"
         | 
| 448 | 
            +
                    display history_table
         | 
| 449 | 
            +
                  else
         | 
| 450 | 
            +
                    display "No application named '" + appname + "' found"
         | 
| 451 | 
            +
                  end
         | 
| 452 | 
            +
                end
         | 
| 453 | 
            +
             | 
| 454 | 
            +
                def hash(path=nil)
         | 
| 455 | 
            +
                  if (@options[:full])
         | 
| 456 | 
            +
                    full = true
         | 
| 457 | 
            +
                  else
         | 
| 458 | 
            +
                    full = false
         | 
| 459 | 
            +
                  end
         | 
| 460 | 
            +
             | 
| 461 | 
            +
                  current_dir = false
         | 
| 462 | 
            +
                  if not path
         | 
| 463 | 
            +
                    current_dir = true
         | 
| 464 | 
            +
                    path = @path
         | 
| 465 | 
            +
                  end
         | 
| 466 | 
            +
             | 
| 467 | 
            +
                  hash = hash_app_bits(File.expand_path(path))
         | 
| 468 | 
            +
             | 
| 469 | 
            +
                  if full
         | 
| 470 | 
            +
                    display hash.to_s
         | 
| 471 | 
            +
                  else
         | 
| 472 | 
            +
                    if current_dir
         | 
| 473 | 
            +
                      display "The hash for the current directory is: " + hash.to_s[0..9]
         | 
| 474 | 
            +
                    else
         | 
| 475 | 
            +
                      display "The hash for " + path + " is: " + hash.to_s[0..9]
         | 
| 476 | 
            +
                    end
         | 
| 477 | 
            +
                  end
         | 
| 478 | 
            +
                end
         | 
| 479 | 
            +
             | 
| 480 | 
            +
                def diff(appname)
         | 
| 481 | 
            +
                  if app_exists?(appname)
         | 
| 482 | 
            +
                    diff = client.app_diff(appname)[0]
         | 
| 483 | 
            +
                    return display "No diff available" if diff.nil? or diff.empty?
         | 
| 484 | 
            +
                    hash = hash_app_bits(@path)
         | 
| 485 | 
            +
             | 
| 486 | 
            +
                    comp = (hash == diff[:update_hash])
         | 
| 487 | 
            +
                    comparison = comp ? "Deployed app '" + appname + "' matches app in current directory" : "Deployed app '" + appname + "' does not match app in current directory"
         | 
| 488 | 
            +
                    display comparison
         | 
| 489 | 
            +
                  else
         | 
| 490 | 
            +
                    display "No application named '" + appname + "' found"
         | 
| 412 491 | 
             
                  end
         | 
| 413 492 | 
             
                end
         | 
| 414 493 |  | 
| @@ -492,7 +571,7 @@ module VMC::Cli::Command | |
| 492 571 | 
             
                  err "Can't deploy applications from staging directory: [#{Dir.tmpdir}]"
         | 
| 493 572 | 
             
                end
         | 
| 494 573 |  | 
| 495 | 
            -
                def upload_app_bits(appname, path, infra)
         | 
| 574 | 
            +
                def upload_app_bits(appname, path, infra, label=nil)
         | 
| 496 575 | 
             
                  display 'Uploading Application:'
         | 
| 497 576 |  | 
| 498 577 | 
             
                  upload_file, file = "#{Dir.tmpdir}/#{appname}.zip", nil
         | 
| @@ -529,6 +608,9 @@ module VMC::Cli::Command | |
| 529 608 | 
             
                    end
         | 
| 530 609 | 
             
                  end
         | 
| 531 610 |  | 
| 611 | 
            +
                  # compute hash for versioning info
         | 
| 612 | 
            +
                  tarfile = VMC::Cli::ZipUtil.tar(explode_dir)
         | 
| 613 | 
            +
                  hash = Digest::MD5.file(tarfile)
         | 
| 532 614 |  | 
| 533 615 | 
             
                  # Send the resource list to the cloudcontroller, the response will tell us what it already has..
         | 
| 534 616 | 
             
                  unless @options[:noresources]
         | 
| @@ -595,7 +677,7 @@ module VMC::Cli::Command | |
| 595 677 | 
             
                  FileWithPercentOutput.upload_size = File.size(upload_file);
         | 
| 596 678 | 
             
                  file = FileWithPercentOutput.open(upload_file, 'rb')
         | 
| 597 679 |  | 
| 598 | 
            -
                  client.upload_app(appname, file, appcloud_resources)
         | 
| 680 | 
            +
                  client.upload_app(appname, file, hash, label.nil? ? "": label, appcloud_resources)
         | 
| 599 681 | 
             
                  display 'OK'.green if VMC::Cli::ZipUtil.get_files_to_pack(explode_dir).empty?
         | 
| 600 682 |  | 
| 601 683 | 
             
                  display 'Push Status: ', false
         | 
| @@ -605,6 +687,52 @@ module VMC::Cli::Command | |
| 605 687 | 
             
                    # Cleanup if we created an exploded directory.
         | 
| 606 688 | 
             
                    FileUtils.rm_f(upload_file) if upload_file
         | 
| 607 689 | 
             
                    FileUtils.rm_rf(explode_dir) if explode_dir
         | 
| 690 | 
            +
                    FileUtils.rm_rf(tarfile) if tarfile
         | 
| 691 | 
            +
                end
         | 
| 692 | 
            +
             | 
| 693 | 
            +
                # To support the hash command
         | 
| 694 | 
            +
                def hash_app_bits(path)
         | 
| 695 | 
            +
                  explode_dir = "#{Dir.tmpdir}/.vmc_temp_files"
         | 
| 696 | 
            +
                  FileUtils.rm_rf(explode_dir) # Make sure we didn't have anything left over..
         | 
| 697 | 
            +
             | 
| 698 | 
            +
                  if path =~ /\.(war|zip)$/
         | 
| 699 | 
            +
                    #single file that needs unpacking
         | 
| 700 | 
            +
                    VMC::Cli::ZipUtil.unpack(path, explode_dir)
         | 
| 701 | 
            +
                  elsif !File.directory? path
         | 
| 702 | 
            +
                    #single file that doesn't need unpacking
         | 
| 703 | 
            +
                    FileUtils.mkdir(explode_dir)
         | 
| 704 | 
            +
                    FileUtils.cp(path,explode_dir)
         | 
| 705 | 
            +
                  else
         | 
| 706 | 
            +
                    Dir.chdir(path) do
         | 
| 707 | 
            +
                      # Stage the app appropriately and do the appropriate fingerprinting, etc.
         | 
| 708 | 
            +
                      if war_file = Dir.glob('*.war').first
         | 
| 709 | 
            +
                        VMC::Cli::ZipUtil.unpack(war_file, explode_dir)
         | 
| 710 | 
            +
                      elsif zip_file = Dir.glob('*.zip').first
         | 
| 711 | 
            +
                        VMC::Cli::ZipUtil.unpack(zip_file, explode_dir)
         | 
| 712 | 
            +
                      else
         | 
| 713 | 
            +
                        FileUtils.mkdir(explode_dir)
         | 
| 714 | 
            +
             | 
| 715 | 
            +
                        afi = VMC::Cli::FileHelper::AppFogIgnore.from_file("#{path}")
         | 
| 716 | 
            +
             | 
| 717 | 
            +
                        files = Dir.glob("#{path}/**/*", File::FNM_DOTMATCH)
         | 
| 718 | 
            +
                        check_unreachable_links(path,afi.included_files(files))
         | 
| 719 | 
            +
             | 
| 720 | 
            +
                        copy_files( path, ignore_sockets( afi.included_files(files)), explode_dir )
         | 
| 721 | 
            +
             | 
| 722 | 
            +
                      end
         | 
| 723 | 
            +
                    end
         | 
| 724 | 
            +
                  end
         | 
| 725 | 
            +
             | 
| 726 | 
            +
                  # compute hash for versioning info
         | 
| 727 | 
            +
                  tarfile = VMC::Cli::ZipUtil.tar(explode_dir)
         | 
| 728 | 
            +
                  hash = Digest::MD5.file(tarfile)
         | 
| 729 | 
            +
             | 
| 730 | 
            +
                  ensure
         | 
| 731 | 
            +
                    # Cleanup if we created an exploded directory.
         | 
| 732 | 
            +
                    FileUtils.rm_rf(explode_dir) if explode_dir
         | 
| 733 | 
            +
                    FileUtils.rm_rf(tarfile) if tarfile
         | 
| 734 | 
            +
             | 
| 735 | 
            +
                  hash
         | 
| 608 736 | 
             
                end
         | 
| 609 737 |  | 
| 610 738 | 
             
                def check_app_limit
         | 
| @@ -991,7 +1119,7 @@ module VMC::Cli::Command | |
| 991 1119 | 
             
                  display 'OK'.green
         | 
| 992 1120 | 
             
                end
         | 
| 993 1121 |  | 
| 994 | 
            -
                def do_push(appname=nil)
         | 
| 1122 | 
            +
                def do_push(label, appname=nil)
         | 
| 995 1123 |  | 
| 996 1124 | 
             
                  unless @app_info || no_prompt
         | 
| 997 1125 | 
             
                    @manifest = { "applications" => { @path => { "name" => appname } } }
         | 
| @@ -1138,7 +1266,7 @@ module VMC::Cli::Command | |
| 1138 1266 | 
             
                  end
         | 
| 1139 1267 |  | 
| 1140 1268 | 
             
                  # Stage and upload the app bits.
         | 
| 1141 | 
            -
                  upload_app_bits(appname, @application, infra)
         | 
| 1269 | 
            +
                  upload_app_bits(appname, @application, infra, label)
         | 
| 1142 1270 |  | 
| 1143 1271 | 
             
                  start(appname, true) unless no_start
         | 
| 1144 1272 | 
             
                end
         | 
    
        data/lib/cli/runner.rb
    CHANGED
    
    | @@ -40,10 +40,12 @@ class VMC::Cli::Runner | |
| 40 40 | 
             
                  opts.on('--url URL')         { |url|   @options[:url] = url }
         | 
| 41 41 | 
             
                  opts.on('--mem MEM')         { |mem|   @options[:mem] = mem }
         | 
| 42 42 | 
             
                  opts.on('--path PATH')       { |path|  @options[:path] = path }
         | 
| 43 | 
            +
                  opts.on('--label LABEL')     { |label| @options[:label] = label }
         | 
| 43 44 | 
             
                  opts.on('--no-start')        {         @options[:nostart] = true }
         | 
| 44 45 | 
             
                  opts.on('--nostart')         {         @options[:nostart] = true }
         | 
| 45 46 | 
             
                  opts.on('--force')           {         @options[:force] = true }
         | 
| 46 47 | 
             
                  opts.on('--all')             {         @options[:all] = true }
         | 
| 48 | 
            +
                  opts.on('--full')            {         @options[:full] = true }
         | 
| 47 49 |  | 
| 48 50 | 
             
                  # generic tracing and debugging
         | 
| 49 51 | 
             
                  opts.on('-t [TKEY]')         { |tkey|  @options[:trace] = tkey || true }
         | 
| @@ -325,7 +327,7 @@ class VMC::Cli::Runner | |
| 325 327 | 
             
                  set_cmd(:apps, :crashlogs, 1)
         | 
| 326 328 |  | 
| 327 329 | 
             
                when 'push'
         | 
| 328 | 
            -
                  usage('af push [appname] [--path PATH] [--url URL] [--instances N] [--mem] [--runtime RUNTIME] [--no-start] [--infra infraname]')
         | 
| 330 | 
            +
                  usage('af push [appname] [--path PATH] [--url URL] [--instances N] [--mem] [--runtime RUNTIME] [--no-start] [--infra infraname] [--label LABEL]')
         | 
| 329 331 | 
             
                  if @args.size == 1
         | 
| 330 332 | 
             
                    set_cmd(:apps, :push, 1)
         | 
| 331 333 | 
             
                  else
         | 
| @@ -333,9 +335,25 @@ class VMC::Cli::Runner | |
| 333 335 | 
             
                  end
         | 
| 334 336 |  | 
| 335 337 | 
             
                when 'update'
         | 
| 336 | 
            -
                  usage('af update <appname> [--path PATH]')
         | 
| 338 | 
            +
                  usage('af update <appname> [--path PATH] [--label LABEL]')
         | 
| 337 339 | 
             
                  set_cmd(:apps, :update, @args.size == 1 ? 1 : 0)
         | 
| 338 340 |  | 
| 341 | 
            +
                when 'history'
         | 
| 342 | 
            +
                  usage('af history <appname>')
         | 
| 343 | 
            +
                  set_cmd(:apps, :history, 1)
         | 
| 344 | 
            +
             | 
| 345 | 
            +
                when 'hash'
         | 
| 346 | 
            +
                  usage('hash [path] [--full]')
         | 
| 347 | 
            +
                  if @args.size == 1
         | 
| 348 | 
            +
                    set_cmd(:apps, :hash, 1)
         | 
| 349 | 
            +
                  else
         | 
| 350 | 
            +
                    set_cmd(:apps, :hash, 0)
         | 
| 351 | 
            +
                  end
         | 
| 352 | 
            +
             | 
| 353 | 
            +
                when 'diff'
         | 
| 354 | 
            +
                  usage('af diff <appname>')
         | 
| 355 | 
            +
                  set_cmd(:apps, :diff, 1)
         | 
| 356 | 
            +
             | 
| 339 357 | 
             
                when 'services'
         | 
| 340 358 | 
             
                  usage('af services')
         | 
| 341 359 | 
             
                  set_cmd(:services, :services)
         | 
| @@ -396,7 +414,7 @@ class VMC::Cli::Runner | |
| 396 414 | 
             
                  set_cmd(:services, :import_service, 2)
         | 
| 397 415 |  | 
| 398 416 | 
             
                when 'clone'
         | 
| 399 | 
            -
                  usage('af clone <src-app> <dest-app> [<infra>]')
         | 
| 417 | 
            +
                  usage('af clone <src-app> <dest-app> [<infra>] --label LABEL')
         | 
| 400 418 | 
             
                  set_cmd(:apps, :clone, 2) if @args.size == 2
         | 
| 401 419 | 
             
                  set_cmd(:apps, :clone, 3) if @args.size == 3
         | 
| 402 420 |  | 
    
        data/lib/cli/usage.rb
    CHANGED
    
    | @@ -42,16 +42,17 @@ Currently available af commands are: | |
| 42 42 | 
             
                push [appname] --runtime RUNTIME             Set the runtime to use for the application
         | 
| 43 43 | 
             
                push [appname] --debug [MODE]                Push application and start in a debug mode
         | 
| 44 44 | 
             
                push [appname] --no-start                    Do not auto-start the application
         | 
| 45 | 
            +
                push [appname] --label                       Add specified label to app revision record
         | 
| 45 46 |  | 
| 46 47 | 
             
              Application Operations
         | 
| 47 48 | 
             
                start <appname> [--debug [MODE]]             Start the application
         | 
| 48 49 | 
             
                stop  <appname>                              Stop the application
         | 
| 49 50 | 
             
                restart <appname> [--debug [MODE]]           Restart the application
         | 
| 50 51 | 
             
                delete <appname>                             Delete the application
         | 
| 51 | 
            -
                clone <src-app> <dest-app> [infra] | 
| 52 | 
            +
                clone <src-app> <dest-app> [infra] --label LABEL     Clone the application and services
         | 
| 52 53 |  | 
| 53 54 | 
             
              Application Updates
         | 
| 54 | 
            -
                update <appname> [--path,--debug [MODE]]     Update the application bits
         | 
| 55 | 
            +
                update <appname> [--path,--debug [MODE],--label]     Update the application bits
         | 
| 55 56 | 
             
                mem <appname> [memsize]                      Update the memory reservation for an application
         | 
| 56 57 | 
             
                map <appname> <url>                          Register the application to the url
         | 
| 57 58 | 
             
                unmap <appname> <url>                        Unregister the application from the url
         | 
| @@ -64,6 +65,9 @@ Currently available af commands are: | |
| 64 65 | 
             
                files <appname> [path] [--all]               Display directory listing or file download for [path]
         | 
| 65 66 | 
             
                stats <appname>                              Display resource usage for the application
         | 
| 66 67 | 
             
                instances <appname>                          List application instances
         | 
| 68 | 
            +
                history <appname>                            Show version history of the application
         | 
| 69 | 
            +
                diff <appname>                               Compare current directory with deployed application
         | 
| 70 | 
            +
                hash [path] [--full]                         Compute hash of directory, defaults to current
         | 
| 67 71 |  | 
| 68 72 | 
             
              Application Download
         | 
| 69 73 | 
             
                pull <appname> [path]                        Downloads last pushed source to <appname> or [path]
         | 
    
        data/lib/cli/version.rb
    CHANGED
    
    
    
        data/lib/cli/zip_util.rb
    CHANGED
    
    | @@ -1,5 +1,6 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            require 'fileutils'
         | 
| 2 2 | 
             
            require 'zip/zipfilesystem'
         | 
| 3 | 
            +
            require 'pathname'
         | 
| 3 4 |  | 
| 4 5 | 
             
            module VMC::Cli
         | 
| 5 6 |  | 
| @@ -72,6 +73,30 @@ module VMC::Cli | |
| 72 73 | 
             
                    end
         | 
| 73 74 | 
             
                  end
         | 
| 74 75 |  | 
| 76 | 
            +
                  BLOCKSIZE_TO_READ = 1024 * 1000
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  # not a valid tar file, since tar files include last modified date which breaks it for
         | 
| 79 | 
            +
                  # use in hashing. this method just wraps up everything for use in hashing.
         | 
| 80 | 
            +
                  def tar(path)
         | 
| 81 | 
            +
                    tar_filename = Pathname.new(path).realpath.to_path + '.tar'
         | 
| 82 | 
            +
                    File.open(tar_filename, 'wb') do |tarfile|
         | 
| 83 | 
            +
                      get_files_to_pack(path).each do |file|
         | 
| 84 | 
            +
                        if File.file?(file)
         | 
| 85 | 
            +
                          File.open(file, 'rb') do |f|
         | 
| 86 | 
            +
                            while buffer = f.read(BLOCKSIZE_TO_READ)
         | 
| 87 | 
            +
                              tarfile.write buffer
         | 
| 88 | 
            +
                            end
         | 
| 89 | 
            +
                          end
         | 
| 90 | 
            +
                        else
         | 
| 91 | 
            +
                          tarfile.write file.gsub(path, '')
         | 
| 92 | 
            +
                        end
         | 
| 93 | 
            +
                      end
         | 
| 94 | 
            +
                    end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                    tar_filename
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
             | 
| 75 100 | 
             
                end
         | 
| 76 101 | 
             
              end
         | 
| 77 102 | 
             
            end
         | 
    
        data/lib/vmc/client.rb
    CHANGED
    
    | @@ -91,7 +91,7 @@ class VMC::Client | |
| 91 91 | 
             
                json_put(path(VMC::APPS_PATH, name), manifest)
         | 
| 92 92 | 
             
              end
         | 
| 93 93 |  | 
| 94 | 
            -
              def upload_app(name, zipfile, resource_manifest=nil)
         | 
| 94 | 
            +
              def upload_app(name, zipfile, hash, label, resource_manifest=nil)
         | 
| 95 95 | 
             
                #FIXME, manifest should be allowed to be null, here for compatability with old cc's
         | 
| 96 96 | 
             
                resource_manifest ||= []
         | 
| 97 97 | 
             
                check_login_status
         | 
| @@ -104,8 +104,9 @@ class VMC::Client | |
| 104 104 | 
             
                  end
         | 
| 105 105 | 
             
                  upload_data[:application] = file
         | 
| 106 106 | 
             
                end
         | 
| 107 | 
            +
                headers = {:hash => hash, :label => label}
         | 
| 107 108 | 
             
                upload_data[:resources] = resource_manifest.to_json if resource_manifest
         | 
| 108 | 
            -
                http_post(path(VMC::APPS_PATH, name, "application"), upload_data)
         | 
| 109 | 
            +
                http_post(path(VMC::APPS_PATH, name, "application"), upload_data, nil, headers)
         | 
| 109 110 | 
             
              rescue RestClient::ServerBrokeConnection
         | 
| 110 111 | 
             
                retry
         | 
| 111 112 | 
             
              end
         | 
| @@ -149,6 +150,16 @@ class VMC::Client | |
| 149 150 | 
             
                json_get(path(VMC::APPS_PATH, name, "crashes"))
         | 
| 150 151 | 
             
              end
         | 
| 151 152 |  | 
| 153 | 
            +
              def app_history(name)
         | 
| 154 | 
            +
                check_login_status
         | 
| 155 | 
            +
                json_get(path(VMC::APPS_PATH, name, "history"))
         | 
| 156 | 
            +
              end
         | 
| 157 | 
            +
             | 
| 158 | 
            +
              def app_diff(name)
         | 
| 159 | 
            +
                check_login_status
         | 
| 160 | 
            +
                json_get(path(VMC::APPS_PATH, name, "diff"))
         | 
| 161 | 
            +
              end
         | 
| 162 | 
            +
             | 
| 152 163 | 
             
              # List the directory or download the actual file indicated by
         | 
| 153 164 | 
             
              # the path.
         | 
| 154 165 | 
             
              def app_files(name, path, instance='0')
         | 
| @@ -444,8 +455,8 @@ class VMC::Client | |
| 444 455 | 
             
                request(:get, path, content_type)
         | 
| 445 456 | 
             
              end
         | 
| 446 457 |  | 
| 447 | 
            -
              def http_post(path, body, content_type=nil)
         | 
| 448 | 
            -
                request(:post, path, content_type, body)
         | 
| 458 | 
            +
              def http_post(path, body, content_type=nil, headers = {})
         | 
| 459 | 
            +
                request(:post, path, content_type, body, headers)
         | 
| 449 460 | 
             
              end
         | 
| 450 461 |  | 
| 451 462 | 
             
              def http_put(path, body, content_type=nil)
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: af
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.3. | 
| 4 | 
            +
              version: 0.3.22
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - AppFog
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2015-01-15 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: caldecott
         | 
| @@ -28,166 +28,166 @@ dependencies: | |
| 28 28 | 
             
              name: json_pure
         | 
| 29 29 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 30 | 
             
                requirements:
         | 
| 31 | 
            -
                - -  | 
| 31 | 
            +
                - - ">="
         | 
| 32 32 | 
             
                  - !ruby/object:Gem::Version
         | 
| 33 33 | 
             
                    version: 1.5.1
         | 
| 34 | 
            -
                - - <
         | 
| 34 | 
            +
                - - "<"
         | 
| 35 35 | 
             
                  - !ruby/object:Gem::Version
         | 
| 36 36 | 
             
                    version: 1.7.0
         | 
| 37 37 | 
             
              type: :runtime
         | 
| 38 38 | 
             
              prerelease: false
         | 
| 39 39 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 40 40 | 
             
                requirements:
         | 
| 41 | 
            -
                - -  | 
| 41 | 
            +
                - - ">="
         | 
| 42 42 | 
             
                  - !ruby/object:Gem::Version
         | 
| 43 43 | 
             
                    version: 1.5.1
         | 
| 44 | 
            -
                - - <
         | 
| 44 | 
            +
                - - "<"
         | 
| 45 45 | 
             
                  - !ruby/object:Gem::Version
         | 
| 46 46 | 
             
                    version: 1.7.0
         | 
| 47 47 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 48 48 | 
             
              name: rubyzip
         | 
| 49 49 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 50 50 | 
             
                requirements:
         | 
| 51 | 
            -
                - - ~>
         | 
| 51 | 
            +
                - - "~>"
         | 
| 52 52 | 
             
                  - !ruby/object:Gem::Version
         | 
| 53 53 | 
             
                    version: 0.9.4
         | 
| 54 54 | 
             
              type: :runtime
         | 
| 55 55 | 
             
              prerelease: false
         | 
| 56 56 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 57 57 | 
             
                requirements:
         | 
| 58 | 
            -
                - - ~>
         | 
| 58 | 
            +
                - - "~>"
         | 
| 59 59 | 
             
                  - !ruby/object:Gem::Version
         | 
| 60 60 | 
             
                    version: 0.9.4
         | 
| 61 61 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 62 62 | 
             
              name: rest-client
         | 
| 63 63 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 64 64 | 
             
                requirements:
         | 
| 65 | 
            -
                - -  | 
| 65 | 
            +
                - - ">="
         | 
| 66 66 | 
             
                  - !ruby/object:Gem::Version
         | 
| 67 67 | 
             
                    version: 1.6.1
         | 
| 68 | 
            -
                - - <
         | 
| 68 | 
            +
                - - "<"
         | 
| 69 69 | 
             
                  - !ruby/object:Gem::Version
         | 
| 70 70 | 
             
                    version: 1.7.0
         | 
| 71 71 | 
             
              type: :runtime
         | 
| 72 72 | 
             
              prerelease: false
         | 
| 73 73 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 74 74 | 
             
                requirements:
         | 
| 75 | 
            -
                - -  | 
| 75 | 
            +
                - - ">="
         | 
| 76 76 | 
             
                  - !ruby/object:Gem::Version
         | 
| 77 77 | 
             
                    version: 1.6.1
         | 
| 78 | 
            -
                - - <
         | 
| 78 | 
            +
                - - "<"
         | 
| 79 79 | 
             
                  - !ruby/object:Gem::Version
         | 
| 80 80 | 
             
                    version: 1.7.0
         | 
| 81 81 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 82 82 | 
             
              name: terminal-table
         | 
| 83 83 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 84 84 | 
             
                requirements:
         | 
| 85 | 
            -
                - - ~>
         | 
| 85 | 
            +
                - - "~>"
         | 
| 86 86 | 
             
                  - !ruby/object:Gem::Version
         | 
| 87 87 | 
             
                    version: 1.4.2
         | 
| 88 88 | 
             
              type: :runtime
         | 
| 89 89 | 
             
              prerelease: false
         | 
| 90 90 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 91 91 | 
             
                requirements:
         | 
| 92 | 
            -
                - - ~>
         | 
| 92 | 
            +
                - - "~>"
         | 
| 93 93 | 
             
                  - !ruby/object:Gem::Version
         | 
| 94 94 | 
             
                    version: 1.4.2
         | 
| 95 95 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 96 96 | 
             
              name: interact
         | 
| 97 97 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 98 98 | 
             
                requirements:
         | 
| 99 | 
            -
                - - ~>
         | 
| 99 | 
            +
                - - "~>"
         | 
| 100 100 | 
             
                  - !ruby/object:Gem::Version
         | 
| 101 101 | 
             
                    version: 0.4.0
         | 
| 102 102 | 
             
              type: :runtime
         | 
| 103 103 | 
             
              prerelease: false
         | 
| 104 104 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 105 105 | 
             
                requirements:
         | 
| 106 | 
            -
                - - ~>
         | 
| 106 | 
            +
                - - "~>"
         | 
| 107 107 | 
             
                  - !ruby/object:Gem::Version
         | 
| 108 108 | 
             
                    version: 0.4.0
         | 
| 109 109 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 110 110 | 
             
              name: addressable
         | 
| 111 111 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 112 112 | 
             
                requirements:
         | 
| 113 | 
            -
                - - ~>
         | 
| 113 | 
            +
                - - "~>"
         | 
| 114 114 | 
             
                  - !ruby/object:Gem::Version
         | 
| 115 115 | 
             
                    version: 2.2.6
         | 
| 116 116 | 
             
              type: :runtime
         | 
| 117 117 | 
             
              prerelease: false
         | 
| 118 118 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 119 119 | 
             
                requirements:
         | 
| 120 | 
            -
                - - ~>
         | 
| 120 | 
            +
                - - "~>"
         | 
| 121 121 | 
             
                  - !ruby/object:Gem::Version
         | 
| 122 122 | 
             
                    version: 2.2.6
         | 
| 123 123 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 124 124 | 
             
              name: uuidtools
         | 
| 125 125 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 126 126 | 
             
                requirements:
         | 
| 127 | 
            -
                - - ~>
         | 
| 127 | 
            +
                - - "~>"
         | 
| 128 128 | 
             
                  - !ruby/object:Gem::Version
         | 
| 129 129 | 
             
                    version: 2.1.0
         | 
| 130 130 | 
             
              type: :runtime
         | 
| 131 131 | 
             
              prerelease: false
         | 
| 132 132 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 133 133 | 
             
                requirements:
         | 
| 134 | 
            -
                - - ~>
         | 
| 134 | 
            +
                - - "~>"
         | 
| 135 135 | 
             
                  - !ruby/object:Gem::Version
         | 
| 136 136 | 
             
                    version: 2.1.0
         | 
| 137 137 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 138 138 | 
             
              name: rb-readline
         | 
| 139 139 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 140 140 | 
             
                requirements:
         | 
| 141 | 
            -
                - - ~>
         | 
| 141 | 
            +
                - - "~>"
         | 
| 142 142 | 
             
                  - !ruby/object:Gem::Version
         | 
| 143 143 | 
             
                    version: 0.4.2
         | 
| 144 144 | 
             
              type: :runtime
         | 
| 145 145 | 
             
              prerelease: false
         | 
| 146 146 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 147 147 | 
             
                requirements:
         | 
| 148 | 
            -
                - - ~>
         | 
| 148 | 
            +
                - - "~>"
         | 
| 149 149 | 
             
                  - !ruby/object:Gem::Version
         | 
| 150 150 | 
             
                    version: 0.4.2
         | 
| 151 151 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 152 152 | 
             
              name: rake
         | 
| 153 153 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 154 154 | 
             
                requirements:
         | 
| 155 | 
            -
                - -  | 
| 155 | 
            +
                - - ">="
         | 
| 156 156 | 
             
                  - !ruby/object:Gem::Version
         | 
| 157 157 | 
             
                    version: '0'
         | 
| 158 158 | 
             
              type: :development
         | 
| 159 159 | 
             
              prerelease: false
         | 
| 160 160 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 161 161 | 
             
                requirements:
         | 
| 162 | 
            -
                - -  | 
| 162 | 
            +
                - - ">="
         | 
| 163 163 | 
             
                  - !ruby/object:Gem::Version
         | 
| 164 164 | 
             
                    version: '0'
         | 
| 165 165 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 166 166 | 
             
              name: rspec
         | 
| 167 167 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 168 168 | 
             
                requirements:
         | 
| 169 | 
            -
                - - ~>
         | 
| 169 | 
            +
                - - "~>"
         | 
| 170 170 | 
             
                  - !ruby/object:Gem::Version
         | 
| 171 171 | 
             
                    version: 1.3.0
         | 
| 172 172 | 
             
              type: :development
         | 
| 173 173 | 
             
              prerelease: false
         | 
| 174 174 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 175 175 | 
             
                requirements:
         | 
| 176 | 
            -
                - - ~>
         | 
| 176 | 
            +
                - - "~>"
         | 
| 177 177 | 
             
                  - !ruby/object:Gem::Version
         | 
| 178 178 | 
             
                    version: 1.3.0
         | 
| 179 179 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 180 180 | 
             
              name: webmock
         | 
| 181 181 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 182 182 | 
             
                requirements:
         | 
| 183 | 
            -
                - - ~>
         | 
| 183 | 
            +
                - - "~>"
         | 
| 184 184 | 
             
                  - !ruby/object:Gem::Version
         | 
| 185 185 | 
             
                    version: 1.5.0
         | 
| 186 186 | 
             
              type: :development
         | 
| 187 187 | 
             
              prerelease: false
         | 
| 188 188 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 189 189 | 
             
                requirements:
         | 
| 190 | 
            -
                - - ~>
         | 
| 190 | 
            +
                - - "~>"
         | 
| 191 191 | 
             
                  - !ruby/object:Gem::Version
         | 
| 192 192 | 
             
                    version: 1.5.0
         | 
| 193 193 | 
             
            description: AppFog.com CLI
         | 
| @@ -202,10 +202,15 @@ files: | |
| 202 202 | 
             
            - LICENSE
         | 
| 203 203 | 
             
            - README.md
         | 
| 204 204 | 
             
            - Rakefile
         | 
| 205 | 
            +
            - bin/af
         | 
| 206 | 
            +
            - caldecott_helper/Gemfile
         | 
| 207 | 
            +
            - caldecott_helper/Gemfile.lock
         | 
| 208 | 
            +
            - caldecott_helper/server.rb
         | 
| 205 209 | 
             
            - config/clients.yml
         | 
| 206 210 | 
             
            - config/micro/offline.conf
         | 
| 207 211 | 
             
            - config/micro/paths.yml
         | 
| 208 212 | 
             
            - config/micro/refresh_ip.rb
         | 
| 213 | 
            +
            - lib/cli.rb
         | 
| 209 214 | 
             
            - lib/cli/commands/admin.rb
         | 
| 210 215 | 
             
            - lib/cli/commands/apps.rb
         | 
| 211 216 | 
             
            - lib/cli/commands/base.rb
         | 
| @@ -227,21 +232,16 @@ files: | |
| 227 232 | 
             
            - lib/cli/usage.rb
         | 
| 228 233 | 
             
            - lib/cli/version.rb
         | 
| 229 234 | 
             
            - lib/cli/zip_util.rb
         | 
| 230 | 
            -
            - lib/ | 
| 235 | 
            +
            - lib/vmc.rb
         | 
| 231 236 | 
             
            - lib/vmc/client.rb
         | 
| 232 237 | 
             
            - lib/vmc/const.rb
         | 
| 238 | 
            +
            - lib/vmc/micro.rb
         | 
| 233 239 | 
             
            - lib/vmc/micro/switcher/base.rb
         | 
| 234 240 | 
             
            - lib/vmc/micro/switcher/darwin.rb
         | 
| 235 241 | 
             
            - lib/vmc/micro/switcher/dummy.rb
         | 
| 236 242 | 
             
            - lib/vmc/micro/switcher/linux.rb
         | 
| 237 243 | 
             
            - lib/vmc/micro/switcher/windows.rb
         | 
| 238 244 | 
             
            - lib/vmc/micro/vmrun.rb
         | 
| 239 | 
            -
            - lib/vmc/micro.rb
         | 
| 240 | 
            -
            - lib/vmc.rb
         | 
| 241 | 
            -
            - caldecott_helper/Gemfile
         | 
| 242 | 
            -
            - caldecott_helper/Gemfile.lock
         | 
| 243 | 
            -
            - caldecott_helper/server.rb
         | 
| 244 | 
            -
            - bin/af
         | 
| 245 245 | 
             
            homepage: http://appfog.com
         | 
| 246 246 | 
             
            licenses: []
         | 
| 247 247 | 
             
            metadata: {}
         | 
| @@ -251,17 +251,17 @@ require_paths: | |
| 251 251 | 
             
            - lib
         | 
| 252 252 | 
             
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 253 253 | 
             
              requirements:
         | 
| 254 | 
            -
              - -  | 
| 254 | 
            +
              - - ">="
         | 
| 255 255 | 
             
                - !ruby/object:Gem::Version
         | 
| 256 256 | 
             
                  version: '0'
         | 
| 257 257 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 258 258 | 
             
              requirements:
         | 
| 259 | 
            -
              - -  | 
| 259 | 
            +
              - - ">="
         | 
| 260 260 | 
             
                - !ruby/object:Gem::Version
         | 
| 261 261 | 
             
                  version: '0'
         | 
| 262 262 | 
             
            requirements: []
         | 
| 263 263 | 
             
            rubyforge_project: 
         | 
| 264 | 
            -
            rubygems_version: 2. | 
| 264 | 
            +
            rubygems_version: 2.2.2
         | 
| 265 265 | 
             
            signing_key: 
         | 
| 266 266 | 
             
            specification_version: 4
         | 
| 267 267 | 
             
            summary: AppFog.com CLI
         |