Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add harness and two test scripts

1. Add test harness
2. Add two test scripts
  - administrative test script
  - autostaging feature on ruby19/sinatra
3. Add Rakefile for "bundle exec rake tests"
4. Add vcap-test-assets as submodule for vcap-yeti repo
5. put assets.yml file to config/ directory

Change-Id: Idb0dffb724050be451e8c2974331f7deb61a693c
Signed-off-by: Pin Xie <pxie@vmware.com>
  • Loading branch information...
commit b194ce9069588657633db0d140fdab2ea205b242 1 parent ed20723
@pxie pxie authored
View
3  .gitmodules
@@ -0,0 +1,3 @@
+[submodule "assets"]
+ path = assets
+ url = https://github.com/cloudfoundry/vcap-test-assets.git
View
8 Gemfile
@@ -0,0 +1,8 @@
+source "http://rubygems.org"
+
+gem "rake"
+gem "rspec"
+gem "cfoundry"
+gem "vcap_logging", ">= 1.0"
+gem "interact"
+gem "curb"
View
37 Gemfile.lock
@@ -0,0 +1,37 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ cfoundry (0.1.0)
+ json_pure
+ rest-client
+ rubyzip
+ curb (0.8.0)
+ diff-lcs (1.1.3)
+ interact (0.4.2)
+ json_pure (1.6.6)
+ mime-types (1.18)
+ rake (0.9.2.2)
+ rest-client (1.6.7)
+ mime-types (>= 1.16)
+ rspec (2.9.0)
+ rspec-core (~> 2.9.0)
+ rspec-expectations (~> 2.9.0)
+ rspec-mocks (~> 2.9.0)
+ rspec-core (2.9.0)
+ rspec-expectations (2.9.1)
+ diff-lcs (~> 1.1.3)
+ rspec-mocks (2.9.0)
+ rubyzip (0.9.7)
+ vcap_logging (1.0.0)
+ rake
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ cfoundry
+ curb
+ interact
+ rake
+ rspec
+ vcap_logging (>= 1.0)
View
18 Rakefile
@@ -0,0 +1,18 @@
+$:.unshift(File.join(File.dirname(__FILE__), "lib"))
+require "harness"
+
+task :default => [:help]
+
+desc "List help commands"
+task :help do
+ puts "Usage: rake [command]"
+ puts " tests\t\t\t\trun all bvts"
+ puts " help\t\t\t\tlist help commands"
+end
+
+desc "Run the Basic Viability Tests"
+task :tests do
+ BVT::Harness::RakeHelper.generate_config_file
+ BVT::Harness::RakeHelper.check_environment
+ sh "bundle exec rspec spec/ --format p -c | tee #{File.join(BVT::Harness::VCAP_BVT_HOME, "error.log")}"
+end
1  assets
@@ -0,0 +1 @@
+Subproject commit ed96aae60034a565459e1dc6b3ebad68fe854533
View
418 config/assets.yml
@@ -0,0 +1,418 @@
+---
+# Timeout value after we which we give up on AppCloud operations that are long running.
+# Within this value, we poll for completion status of the operations with the
+# sleep interval between successive poll requests specified below.
+timeout_secs: 60
+job_timeout_secs: 30
+sleep_secs: 1
+
+simple_app:
+ framework: "http://b20nine.com/unknown"
+ memory: 128
+ path: "assets/sinatra/hello_vcap"
+
+simple_app2:
+ framework: "http://b20nine.com/unknown"
+ memory: 64
+ path: "assets/sinatra/hello_vcap"
+
+simple_app3:
+ framework: "http://b20nine.com/unknown"
+ memory: 64
+ path: "assets/sinatra/hello_vcap"
+
+tiny_java_app:
+ framework: "java_web/1.0"
+ memory: 128
+ path: "assets/java_web/java_tiny_app/target"
+
+simple_db_app:
+ framework: "http://b20nine.com/unknown"
+ memory: 128
+ path: "assets/sinatra/simple_db_app"
+
+redis_lb_app:
+ framework: "http://b20nine.com/unknown"
+ memory: 128
+ path: "assets/sinatra/redis_lb_app"
+
+env_test_app:
+ framework: "http://b20nine.com/unknown"
+ memory: 64
+ path: "assets/sinatra/env_test_app"
+
+broken_app:
+ framework: "http://b20nine.com/unknown"
+ memory: 64
+ path: "assets/sinatra/broken_app"
+
+rails3_app:
+ framework: "rails/1.0"
+ memory: 128
+ path: "assets/rails3/rails3_app"
+
+jpa_app:
+ framework: "spring_web/1.0"
+ memory: 512
+ path: "assets/spring/jpa-guestbook/target"
+
+hibernate_app:
+ framework: "spring_web/1.0"
+ memory: 512
+ path: "assets/spring/hibernate-guestbook/target"
+
+dbrails_app:
+ framework: "rails/1.0"
+ memory: 128
+ path: "assets/rails3/dbrails_app"
+
+dbrails_broken_app:
+ framework: "rails/1.0"
+ memory: 128
+ path: "assets/rails3/dbrails_broken_app"
+
+grails_app:
+ framework: "grails/1.0"
+ memory: 512
+ path: "assets/grails/guestbook/target"
+
+roo_app:
+ framework: "spring_web/1.0"
+ memory: 256
+ path: "assets/spring/roo-guestbook/target"
+
+mochiweb_test:
+ framework: "otp_rebar"
+ memory: 64
+ path: "assets/mochiweb/mochiweb_test/rel/mochiweb_test"
+
+simple-lift-app:
+ framework: "lift/1.0"
+ memory: 256
+ path: "assets/lift/hello_lift/target"
+
+lift-db-app:
+ framework: "lift/1.0"
+ memory: 256
+ path: "assets/lift/lift-db-app/target"
+
+tomcat-version-check-app:
+ framework: "spring_web/1.0"
+ memory: 128
+ tomcat_version: "7.0.26"
+ path: "assets/java_web/tomcat-version-check-app/target"
+
+app_rails_version18:
+ framework: "rails3"
+ runtime: ruby18
+ memory: 256
+ path: "assets/rails3/app_rails_version"
+
+app_rails_version19:
+ framework: "rails3"
+ runtime: ruby19
+ memory: 256
+ path: "assets/rails3/app_rails_version"
+
+app_rails_service:
+ framework: "rails/1.0"
+ memory: 256
+ path: "assets/rails3/app_rails_service"
+
+app_rails_service_autoconfig:
+ framework: "rails/1.0"
+ memory: 256
+ path: "assets/rails3/app_rails_service_autoconfig"
+
+rails_autoconfig_disabled_by_file:
+ framework: "rails/1.0"
+ memory: 256
+ path: "assets/rails3/autoconfig_disabled_by_file"
+
+rails_autoconfig_disabled_by_gem:
+ framework: "rails/1.0"
+ memory: 256
+ path: "assets/rails3/autoconfig_disabled_by_gem"
+
+app_rack_service:
+ framework: "rack"
+ memory: 256
+ path: "assets/rack/app_rack_service"
+
+app_rack_service_autoconfig:
+ framework: "rack"
+ startup: "ruby app.rb"
+ memory: 64
+ path: "assets/rack/app_rack_service_autoconfig"
+
+rack_autoconfig_ruby19:
+ framework: "rack"
+ startup: "ruby app.rb"
+ memory: 64
+ runtime: ruby19
+ path: "assets/rack/autoconfig_ruby19"
+
+rack_autoconfig_disabled_by_file:
+ framework: "rack"
+ startup: "ruby app.rb"
+ memory: 64
+ path: "assets/rack/autoconfig_disabled_by_file"
+
+rack_autoconfig_disabled_by_gem:
+ framework: "rack"
+ startup: "ruby app.rb"
+ memory: 64
+ path: "assets/rack/autoconfig_disabled_by_gem"
+
+app_sinatra_service:
+ framework: "http://b20nine.com/unknown"
+ memory: 256
+ path: "assets/sinatra/app_sinatra_service"
+
+app_sinatra_service2:
+ framework: "http://b20nine.com/unknown"
+ memory: 256
+ path: "assets/sinatra/app_sinatra_service"
+
+app_sinatra_service_autoconfig:
+ framework: "http://b20nine.com/unknown"
+ memory: 64
+ path: "assets/sinatra/app_sinatra_service_autoconfig"
+
+amqp_autoconfig:
+ framework: "sinatra"
+ memory: 64
+ runtime: ruby19
+ path: "assets/sinatra/amqp_autoconfig"
+
+autoconfig_unsupported_versions:
+ framework: "http://b20nine.com/unknown"
+ memory: 64
+ path: "assets/sinatra/autoconfig_unsupported_versions"
+
+autoconfig_unsupported_carrot_version:
+ framework: "http://b20nine.com/unknown"
+ memory: 64
+ path: "assets/sinatra/autoconfig_unsupported_carrot_version"
+
+sinatra_autoconfig_disabled_by_file:
+ framework: "http://b20nine.com/unknown"
+ memory: 64
+ path: "assets/sinatra/autoconfig_disabled_by_file"
+
+sinatra_autoconfig_disabled_by_gem:
+ framework: "http://b20nine.com/unknown"
+ memory: 64
+ path: "assets/sinatra/autoconfig_disabled_by_gem"
+
+app_node_service:
+ framework: "node"
+ runtime: node
+ memory: 512
+ path: "assets/node/app_node_service"
+
+app_node_version04:
+ framework: "node"
+ runtime: node
+ memory: 64
+ path: "assets/node/app_node_version"
+
+app_node_version06:
+ framework: "node"
+ runtime: node06
+ memory: 64
+ path: "assets/node/app_node_version"
+
+app_spring_service:
+ framework: "spring_web/1.0"
+ memory: 512
+ path: "assets/spring/app_spring_service/target"
+
+my_test_app_env_test_app:
+ framework: "http://b20nine.com/unknown"
+ memory: 64
+ path: "assets/sinatra/hello_vcap"
+
+neo4j_app:
+ framework: "http://b20nine.com/unknown"
+ memory: 128
+ path: "assets/sinatra/neo4j_app"
+
+simple_wsgi_app:
+ framework: "wsgi"
+ memory: 64
+ path: "assets/django/simple_wsgi_app"
+
+wsgi_app_with_requirements:
+ framework: "wsgi"
+ memory: 64
+ path: "assets/django/wsgi_app_with_requirements"
+
+simple_django_app:
+ framework: "django"
+ memory: 64
+ path: "assets/django/simple_django_app"
+
+simple_php_app:
+ framework: "php"
+ memory: 64
+ path: "assets/php/simple_php_app"
+
+spring-env-app:
+ framework: "spring_web/1.0"
+ memory: 256
+ path: "assets/spring/spring-env/target"
+
+auto-reconfig-test-app:
+ framework: "spring_web/1.0"
+ memory: 512
+ path: "assets/spring/auto-reconfig-test-app/target"
+
+auto-reconfig-missing-deps-test-app:
+ framework: "spring_web/1.0"
+ memory: 256
+ path: "assets/spring/auto-reconfig-missing-deps-test-app/target"
+
+atmos_app:
+ framework: "http://b20nine.com/unknown"
+ memory: 256
+ path: "assets/sinatra/atmos_app"
+
+simple_kv_app:
+ framework: "http://b20nine.com/unknown"
+ memory: 64
+ path: "assets/sinatra/simple_kv_app"
+
+brokered_service_app:
+ framework: "http://b20nine.com/unknown"
+ memory: 64
+ path: "assets/sinatra/brokered_service_app"
+
+java_app_with_startup_delay:
+ framework: "java_web/1.0"
+ memory: 128
+ path: "assets/java_web/app_with_startup_delay/target"
+
+rails_console_test_app:
+ framework: "rails/1.0"
+ memory: 256
+ path: "assets/rails3/app_rails_service"
+
+vblob_app:
+ framework: "http://b20nine.com/unknown"
+ memory: 256
+ path: "assets/sinatra/vblob_app"
+
+standalone_ruby18_app:
+ framework: "standalone"
+ command: "bundle exec ruby main.rb -p $VCAP_APP_PORT"
+ memory: 256
+ runtime: ruby18
+ path: "assets/standalone/ruby_app"
+
+standalone_ruby19_app:
+ framework: "standalone"
+ command: "bundle exec ruby main.rb -p $VCAP_APP_PORT"
+ memory: 256
+ runtime: ruby19
+ path: "assets/standalone/ruby_app"
+
+standalone_simple_ruby18_app:
+ framework: "standalone"
+ command: "ruby simple.rb"
+ memory: 256
+ runtime: ruby18
+ path: "assets/standalone/simple_ruby_app"
+ no_url: true
+
+standalone_simple_ruby18_quotes_app:
+ framework: "standalone"
+ command: "ruby \"simple.rb\""
+ memory: 256
+ runtime: ruby18
+ path: "assets/standalone/simple_ruby_app"
+ no_url: true
+
+standalone_simple_ruby19_app:
+ framework: "standalone"
+ command: "ruby simple.rb"
+ memory: 256
+ runtime: ruby19
+ path: "assets/standalone/simple_ruby_app"
+ no_url: true
+
+standalone_java_app:
+ framework: "standalone"
+ command: "bin/hello-cloud"
+ runtime: java
+ memory: 256
+ path: "assets/standalone/java_app/target/appassembler"
+ no_url: true
+
+standalone_node_app:
+ framework: "standalone"
+ command: "node app.js"
+ runtime: node
+ memory: 64
+ path: "assets/standalone/node_app"
+
+standalone_node06_app:
+ framework: "standalone"
+ command: "node app.js"
+ runtime: node06
+ memory: 64
+ path: "assets/standalone/node_app"
+
+standalone_ruby18_autoconfig:
+ framework: "standalone"
+ command: "bundle exec ruby app.rb -p $VCAP_APP_PORT"
+ runtime: ruby18
+ memory: 64
+ path: "assets/standalone/ruby_autoconfig"
+
+standalone_ruby19_autoconfig:
+ framework: "standalone"
+ command: "bundle exec ruby app.rb -p $VCAP_APP_PORT"
+ runtime: ruby19
+ memory: 64
+ path: "assets/standalone/ruby_autoconfig"
+
+standalone_ruby_autoconfig_disabled_by_file:
+ framework: "standalone"
+ command: "bundle exec ruby app.rb -p $VCAP_APP_PORT"
+ runtime: ruby18
+ memory: 64
+ path: "assets/standalone/ruby_autoconfig_disabled_by_file"
+
+standalone_ruby_autoconfig_disabled_by_gem:
+ framework: "standalone"
+ command: "bundle exec ruby app.rb -p $VCAP_APP_PORT"
+ runtime: ruby18
+ memory: 64
+ path: "assets/standalone/ruby_autoconfig_disabled_by_gem"
+
+standalone_php_app:
+ framework: "standalone"
+ command: "php index.php"
+ runtime: php
+ memory: 64
+ path: "assets/standalone/php_app"
+ no_url: true
+
+standalone_python_app:
+ framework: "standalone"
+ command: "python HelloWorld.py"
+ runtime: python2
+ memory: 64
+ path: "assets/standalone/python_app"
+ no_url: true
+
+standalone_erlang_app:
+ framework: "standalone"
+ command: "erl -noshell -s helloworld start -s"
+ runtime: erlangR14B02
+ memory: 64
+ path: "assets/standalone/erlang_app"
+ no_url: true
+
+
View
18 lib/harness.rb
@@ -0,0 +1,18 @@
+
+module BVT
+ module Harness
+ VCAP_BVT_HOME = File.join(ENV['HOME'], '.bvt')
+ VCAP_BVT_CONFIG_FILE = File.join(VCAP_BVT_HOME, "config.yml")
+ VCAP_BVT_PROFILE_FILE = File.join(VCAP_BVT_HOME, "profile.yml")
+ VCAP_BVT_LOG_FILE = File.join(VCAP_BVT_HOME, "bvt.log")
+ LOGGER_LEVEL = :debug
+ VCAP_BVT_APP_CONFIG = File.join(File.dirname(__FILE__), "../config/assets.yml")
+ end
+end
+
+require "harness/color_helper"
+require "harness/rake_helper"
+require "harness/cfsession"
+require "harness/app"
+require "harness/service"
+require "harness/user"
View
251 lib/harness/app.rb
@@ -0,0 +1,251 @@
+require "cfoundry"
+
+module BVT::Harness
+ class App
+ attr_reader :name
+
+ def initialize(app, session)
+ @app = app
+ @name = @app.name
+ @session = session
+ @log = @session.log
+ end
+
+ def inspect
+ "#<BVT::Harness::App '#@name'>"
+ end
+
+ # manifest example
+ #
+ #{"instances"=>1,
+ # "staging"=>{"framework"=>"sinatra", "runtime"=>"ruby19"},
+ # "resources"=>{"memory"=>64}
+ #}
+ #
+ def push(manifest)
+ check_framework(manifest['staging']['framework'])
+ check_runtime(manifest['staging']['runtime'])
+ manifest['path'] = get_app_path
+ manifest['uris'] = [get_url,]
+
+ if @app.exists?
+ @app.upload(manifest['path'])
+ restart
+ return
+ end
+
+ @app.total_instances = manifest['instances']
+ @app.urls = manifest['uris']
+ @app.framework = manifest['staging']['framework']
+ @app.runtime = manifest['staging']['runtime']
+ @app.memory = manifest['resources']['memory']
+
+ @log.info "Push App: #{@app.name}"
+ begin
+ @app.create!
+ @app.upload(manifest['path'])
+ rescue
+ @log.error("Push App: #{@app.name} failed. Manifest: #{manifest}")
+ raise RuntimeError, "Push App: #{@app.name} failed. Manifest: #{manifest}"
+ end
+
+ start
+ end
+
+ def delete
+ @log.info("Delete App: #{@app.name}")
+ begin
+ @app.delete!
+ rescue
+ @log.error "Delete App: #{@app.name} failed. "
+ raise RuntimeError, "Delete App: #{@app.name} failed."
+ end
+ end
+
+ def restart
+ stop
+ start
+ end
+
+ def stop
+ unless @app.exists?
+ @log.error "Application: #{@app.name} does not exist!"
+ raise RuntimeError "Application: #{@app.name} does not exist!"
+ end
+
+ unless @app.stopped?
+ @log.info "Stop App: #{@app.name}"
+ begin
+ @app.stop!
+ rescue
+ @log.error "Stop App: #{@app.name} failed. "
+ raise RuntimeError, "Stop App: #{@app.name} failed."
+ end
+ end
+ end
+
+ def start
+ unless @app.exists?
+ @log.error "Application: #{@app.name} does not exist!"
+ raise RuntimeError "Application: #{@app.name} does not exist!"
+ end
+
+ unless @app.running?
+ @log.info "Start App: #{@app.name}"
+ begin
+ @app.start!
+ rescue
+ @log.error "Start App: #{@app.name} failed. "
+ raise RuntimeError, "Start App: #{@app.name} failed."
+ end
+ end
+ check_application
+ end
+
+ def bind(service_name)
+ unless @session.services.collect(&:name).include?(service_name)
+ @log.error("Fail to find service: #{service_name}")
+ raise RuntimeError, "Fail to find service: #{service_name}"
+ end
+ begin
+ @log.info("Application: #{@app.name} bind Service: #{service_name}")
+ @app.bind(service_name)
+ restart
+ rescue
+ @log.error("Fail to bind Service: #{service_name} to Application: #{@app.name}")
+ raise RuntimeError, "Fail to bind Service: #{service_name} to Application: #{@app.name}"
+ end
+ end
+
+ def unbind(service_name)
+ unless @app.services.include?(service_name)
+ @log.error("Fail to find service: #{service_name} binding to application: #{@app.name}")
+ raise RuntimeError, "Fail to find service: #{service_name} binding to application: #{@app.name}"
+ end
+
+ begin
+ @log.info("Application: #{@app.name} unbind Service: #{service_name}")
+ @app.unbind(service_name)
+ restart
+ rescue
+ @log.error("Fail to unbind service: #{service_name} for application: #{@app.name}")
+ raise RuntimeError, "Fail to unbind service: #{service_name} for application: #{@app.name}"
+ end
+ end
+
+ def healthy?
+ @app.healthy?
+ end
+
+ # method should be REST method, only [:get, :put, :post] is supported
+ def get_response(method, relative_path = "/", data = nil)
+ unless [:get, :put, :post].include?(method)
+ @log.error("REST method #{method} is not supported")
+ raise RuntimeError, "REST method #{method} is not supported"
+ end
+
+ easy = Curl::Easy.new
+ easy.url = get_url + relative_path
+ easy.resolve_mode = :ipv4
+ begin
+ case method
+ when :get
+ @log.debug("Get response from URL: #{easy.url}")
+ easy.http_get
+ when :put
+ @log.debug("Put data: #{data} to URL: #{easy.url}")
+ easy.http_put(data)
+ when :post
+ @log.debug("Post data: #{data} to URL: #{easy.url}")
+ easy.http_post(data)
+ else nil
+ end
+ # Time dependency
+ # Some app's post is async. Sleep to ensure the operation is done.
+ sleep 0.1
+ return easy
+ rescue Exception => e
+ @log.error("Cannot #{method} data from/to #{easy.url}\n#{e.to_s}")
+ raise RuntimeError, "Cannot #{method} data from/to #{easy.url}\n#{e.to_s}"
+ end
+ end
+
+ private
+
+ APP_CHECK_LIMIT = 60
+
+ def check_application
+ seconds = 0
+ until @app.healthy?
+ sleep 1
+ seconds += 1
+ if seconds == APP_CHECK_LIMIT
+ @log.error "Application: #{@app.name} cannot be started in #{APP_CHECK_LIMIT} seconds"
+ raise RuntimeError, "Application: #{@app.name} cannot be started in #{APP_CHECK_LIMIT} seconds"
+ end
+ end
+ end
+
+ def check_framework(framework)
+ if File.exists?(VCAP_BVT_PROFILE_FILE)
+ @profile ||= YAML.load_file(VCAP_BVT_PROFILE_FILE)
+ unless @profile.is_a?(Hash)
+ @log.error("Invalid profile file format, #{VCAP_BVT_PROFILE_FILE}")
+ raise "Invalid profile file format, #{VCAP_BVT_PROFILE_FILE}"
+ end
+ frameworks = @profile[:frameworks]
+ end
+ frameworks ||= @session.system_frameworks
+
+ match = true if frameworks.has_key?(framework)
+
+ unless match
+ @log.error("Framework: #{framework} is not available on target: #{@session.TARGET}")
+ pending("Framework: #{framework} is not available on target: #{@session.TARGET}")
+ end
+
+ end
+
+ def check_runtime(runtime)
+ if File.exists?(VCAP_BVT_PROFILE_FILE)
+ @profile ||= YAML.load_file(VCAP_BVT_PROFILE_FILE)
+ unless @profile.is_a?(Hash)
+ @log.error("Invalid profile file format, #{VCAP_BVT_PROFILE_FILE}")
+ raise "Invalid profile file format, #{VCAP_BVT_PROFILE_FILE}"
+ end
+ runtimes = @profile[:runtimes]
+ end
+ runtimes ||= @session.system_runtimes
+
+ match = true if runtimes.has_key?(runtime)
+
+ unless match
+ @log.error("Runtime: #{runtime} is not available on target: #{@session.TARGET}")
+ pending("Runtime: #{runtime} is not available on target: #{@session.TARGET}")
+ end
+ end
+
+ def get_app_path
+ app_config = YAML.load_file(VCAP_BVT_APP_CONFIG)
+ unless app_config.is_a?(Hash)
+ @log.error("Invalid config file format, #{VCAP_BVT_APP_CONFIG}")
+ raise RuntimeError, "Invalid config file format, #{VCAP_BVT_APP_CONFIG}"
+ end
+ appid = @app.name.split('-', 2).last
+ unless app_config.has_key?(appid)
+ @log.error("Cannot find application #{appid} in #{VCAP_BVT_APP_CONFIG}")
+ raise RuntimeError, "Cannot find application #{appid} in #{VCAP_BVT_APP_CONFIG}"
+ end
+
+ File.join(File.dirname(__FILE__), "../..", app_config[appid]['path'])
+ end
+
+ def get_url
+ # URLs synthesized from app names containing '_' are not handled well by the Lift framework.
+ # So we used '-' instead of '_'
+ # '_' is not a valid character for hostname according to RFC 822,
+ # use '-' to replace it.
+ "#{@app.name}.#{@session.TARGET.gsub("http://api.", "")}".gsub("_", "-")
+ end
+ end
+end
View
164 lib/harness/cfsession.rb
@@ -0,0 +1,164 @@
+require "cfoundry"
+require "vcap/logging"
+
+module BVT::Harness
+ class CFSession
+ attr_reader :log, :namespace, :TARGET, :email
+
+ def initialize(is_admin = false, email = nil, passwd = nil)
+ get_test_property
+ @email = email ? email : get_login_email(is_admin)
+ @passwd = passwd ? passwd : get_login_passwd(is_admin)
+ @TARGET = "http://api.#{get_target}"
+
+ @log = get_logger(LOGGER_LEVEL)
+ @namespace = get_namespace
+ login
+ check_privilege(is_admin)
+ end
+
+ def login
+ @log.debug("Login in, target: #{@TARGET}, email = #{@email}, pssswd = #{@passwd}")
+ @client = CFoundry::Client.new(@TARGET)
+ begin
+ @client.login(@email, @passwd)
+ rescue
+ @log.error "Fail to login in, target: #{@TARGET}, user: #{@email}, passwd = #{@passwd}"
+ raise "Cannot login target environment. " +
+ "target = '#{@TARGET}', user: '#{@email}', passwd: '#{@passwd}'"
+ end
+ # TBD - ABS: This is a hack around the 1 sec granularity of our token time stamp
+ sleep(1)
+ end
+
+ def logout
+ @log.debug "logout, target: #{@TARGET}, email = #{@email}, pssswd = #{@passwd}"
+ @client = nil
+ end
+
+ def info
+ @log.debug "get target info, target: #{@TARGET}"
+ @client.info
+ end
+
+ def register(email, password)
+ @log.debug("Register user: #{email}")
+ BVT::Harness::User.new(@client.register(email, password), self)
+ end
+
+ def system_frameworks
+ @log.debug "get system frameworks, target: #{@TARGET}"
+ info = @client.info
+ info["frameworks"] || {}
+ end
+
+ def system_runtimes
+ @log.debug "get system runtimes, target: #{@TARGET}"
+ @client.system_runtimes
+ end
+
+ def system_services
+ @log.debug "get system services, target: #{@TARGET}"
+ @client.system_services
+ end
+
+ def app(name)
+ BVT::Harness::App.new(@client.app("#{@namespace}#{name}"), self)
+ end
+
+ def apps
+ @client.apps.collect {|app| BVT::Harness::App.new(app, self)}
+ end
+
+ def services
+ @client.services.collect {|service| BVT::Harness::Service.new(service, self)}
+ end
+
+ def service(name)
+ BVT::Harness::Service.new(@client.service("#{@namespace}#{name}"), self)
+ end
+
+ def users
+ begin
+ @log.debug("Get Users for target: #{@client.target}, login email: #{@email}")
+ users = @client.users.collect {|user| BVT::Harness::User.new(user, self)}
+ rescue Exception => e
+ @log.error("Fail to list users for target: #{@client.target}, login email: #{@email}")
+ raise RuntimeError, "Fail to list users for target: " +
+ "#{@client.target}, login email: #{@email}\n#{e.to_s}"
+ end
+ end
+
+ def user(email)
+ BVT::Harness::User.new(@client.user(email), self)
+ end
+
+ # It will delete all services and apps belong to login token via client object
+ def cleanup!
+ services.each { |service| service.delete }
+ apps.each { |app| app.delete }
+ end
+
+ private
+
+ def get_logger(level = :error)
+ config = {:level => level, :file => VCAP_BVT_LOG_FILE}
+ VCAP::Logging.setup_from_config(config)
+ VCAP::Logging.logger(File.basename($0))
+ end
+
+ # generate random string as prefix for one test example
+ def get_namespace
+ "t#{rand(2**32).to_s(36)}-"
+ end
+
+ def get_login_email(expected_admin = false)
+ expected_admin ? @config["admin"]["email"] : @config["user"]["email"]
+ end
+
+ def get_login_passwd(expected_admin = false)
+ expected_admin ? @config["admin"]["passwd"] : @config["user"]["passwd"]
+ end
+
+ def get_target
+ @config["target"]
+ end
+
+ def get_test_property
+ # TODO:
+ config_file = File.join(VCAP_BVT_HOME, "config.yml")
+ begin
+ @config = File.open(config_file) do |f|
+ YAML.load(f)
+ end
+ rescue => e
+ puts "Could not read configuration file: #{e}"
+ exit
+ end
+ end
+
+ def check_privilege(expect_admin = false)
+ expect_privilege = expect_admin ? "admin user" : "normal user"
+ actual_privilege = admin? ? "admin user" : "normal user"
+
+ if actual_privilege == expect_privilege
+ @log.info "run bvt as #{expect_privilege}"
+ else
+ @log.error "user type does not match. Expected User Privilege: #{expect_privilege}" +
+ " Actual User Privilege: #{actual_privilege}"
+ raise RuntimeError, "user type does not match.\n" +
+ " Expected User Privilege: #{expect_privilege}" +
+ " Actual User Privilege: #{actual_privilege}"
+ end
+ end
+
+ def admin?
+ user = @client.user(@email)
+ user.manifest
+ user.admin?
+ end
+ end
+end
+
+
+
View
18 lib/harness/color_helper.rb
@@ -0,0 +1,18 @@
+# Displaying text in color
+module BVT::Harness
+ module ColorHelpers
+
+ def red(text)
+ "\e[31m#{text}\e[0m"
+ end
+
+ def green(text)
+ "\e[32m#{text}\e[0m"
+ end
+
+ def yellow(text)
+ "\e[33m#{text}\e[0m"
+ end
+
+ end
+end
View
148 lib/harness/rake_helper.rb
@@ -0,0 +1,148 @@
+require "yaml"
+require "interact"
+require "harness"
+require "curb"
+require "harness/color_helper"
+
+module BVT::Harness
+ module RakeHelper
+ include Interactive, ColorHelpers
+
+ VCAP_BVT_DEFAULT_TARGET = "vcap.me"
+ VCAP_BVT_DEFAULT_USER = "test@vcap.me"
+ VCAP_BVT_DEFAULT_ADMIN = "admin@vcap.me"
+
+ def generate_config_file
+ Dir.mkdir(VCAP_BVT_HOME) unless Dir.exists?(VCAP_BVT_HOME)
+ get_config
+
+ get_target
+ get_user
+ get_user_passwd
+ get_admin_user
+ get_admin_user_passwd
+
+ save_config
+ end
+
+ def check_environment
+ check_network_connection
+
+ client = BVT::Harness::CFSession.new
+ profile = {}
+ profile[:runtimes] = client.system_runtimes
+ profile[:services] = client.system_services
+ profile[:frameworks] = client.system_frameworks
+ profile[:script_hash] = get_script_git_hash
+ File.open(VCAP_BVT_PROFILE_FILE, "w") { |f| f.write YAML.dump(profile) }
+ puts "Environment profile written to #{VCAP_BVT_PROFILE_FILE}"
+ end
+
+ private
+
+ def get_config
+ if File.exists?(VCAP_BVT_CONFIG_FILE)
+ puts "Using config file #{VCAP_BVT_CONFIG_FILE}"
+ @config = YAML.load_file(VCAP_BVT_CONFIG_FILE)
+ raise "Invalid config file format, #{VCAP_BVT_CONFIG_FILE}" unless @config.is_a?(Hash)
+ else
+ puts "Can't find config file at #{VCAP_BVT_CONFIG_FILE}"
+ @config = {}
+ end
+ end
+
+ def get_target
+ if ENV['VCAP_BVT_TARGET']
+ @config['target'] = ENV['VCAP_BVT_TARGET']
+ elsif @config['target'].nil?
+ @config['target'] = ask_and_validate("VCAP Target",
+ '\A.*',
+ VCAP_BVT_DEFAULT_TARGET
+ )
+ end
+ end
+
+ def get_admin_user
+ @config['admin'] = {} if @config['admin'].nil?
+ if ENV['VCAP_BVT_ADMIN_USER']
+ @config['admin']['email'] = ENV['VCAP_BVT_ADMIN_USER']
+ elsif @config['admin']['email'].nil?
+ @config['admin']['email'] = ask_and_validate('Admin User Email ' +
+ '(If you do not know, just type "enter". ' +
+ 'Some admin user cases may be failed)',
+ '\A.*\@',
+ VCAP_BVT_DEFAULT_ADMIN
+ )
+ end
+ end
+
+ def get_admin_user_passwd
+ if ENV['VCAP_BVT_ADMIN_USER_PASSWD']
+ @config['admin']['passwd'] = ENV['VCAP_BVT_ADMIN_USER_PASSWD']
+ elsif @config['admin']['passwd'].nil?
+ @config['admin']['passwd'] = ask_and_validate('Admin User Passwd ' +
+ '(If you do not know, just type "enter". ' +
+ 'Some admin user cases may be failed)',
+ '.*',
+ '*',
+ '*'
+ )
+ end
+ end
+
+ def get_user
+ @config['user'] = {} if @config['user'].nil?
+ if ENV['VCAP_BVT_USER']
+ @config['user']['email'] = ENV['VCAP_BVT_USER']
+ elsif @config['user']['email'].nil?
+ @config['user']['email'] = ask_and_validate('User Email',
+ '\A.*\@',
+ VCAP_BVT_DEFAULT_USER
+ )
+ end
+ end
+
+ def get_user_passwd
+ if ENV['VCAP_BVT_USER_PASSWD']
+ @config['user']['passwd'] = ENV['VCAP_BVT_USER_PASSWD']
+ elsif @config['user'].nil? || @config['user']['passwd'].nil?
+ @config['user']['passwd'] = ask_and_validate('User Passwd', '.*', '*', '*')
+ end
+ end
+
+ def save_config
+ File.open(VCAP_BVT_CONFIG_FILE, "w") { |f| f.write YAML.dump(@config) }
+ puts "Config file written to #{VCAP_BVT_CONFIG_FILE}"
+ end
+
+ def ask_and_validate(question, pattern, default = nil, echo = nil)
+ res = ask(question, :default => default, :echo => echo)
+ while res !~ /#{pattern}/
+ puts "Incorrect input"
+ res = ask(question, :default => default, :echo => echo)
+ end
+ res
+ end
+
+ def get_script_git_hash
+ `git log --pretty=oneline`.split("\n").first
+ end
+
+ def check_network_connection
+ easy = Curl::Easy.new
+ easy.url = "http://api.#{@config['target']}/info"
+ easy.resolve_mode = :ipv4
+ easy.timeout = 10
+ begin
+ easy.http_get
+ rescue Curl::Err::TimeoutError
+ raise RuntimeError, red("Cannot connect to target environment, #{easy.url}\n" +
+ "Please check your network connection to target environment.")
+ end
+ raise RuntimeError, red("URL: #{easy.url} response code does not equal to 200.\n" +
+ "Please check your target environment first.") unless easy.response_code == 200
+ end
+
+ extend self
+ end
+end
View
68 lib/harness/service.rb
@@ -0,0 +1,68 @@
+require "cfoundry"
+
+module BVT::Harness
+ class Service
+ attr_reader :name
+
+ def initialize(service, session)
+ @service = service
+ @session = session
+ @log = @session.log
+ @name = @service.name
+ end
+
+ def inspect
+ "#<BVT::Harness::Service '#@name'>"
+ end
+ # service manifest example
+ #{"vendor"=>"mysql", "version"=>"5.1"}
+ def create(vendor_manifest)
+ match = false
+
+ ## TODO: read services from profile.yml to improve performance
+ @session.system_services.each do |type, vendors|
+ vendors.each do |vendor, versions|
+ versions.each do |version, _|
+ if vendor =~ /#{vendor_manifest['vendor']}/ && version =~ /#{vendor_manifest['version']}/
+ match = true
+ @service.type = type
+ @service.vendor = vendor
+ @service.version = version
+ # TODO: only free service plan is supported
+ @service.tier = "free"
+ break
+ end
+ end
+ break if match
+ end
+ end
+
+ unless match
+ @log.error("Service: #{vendor_manifest['vendor']} #{vendor_manifest['version']} " +
+ "is not available on target: #{@session.TARGET}")
+ pending("Service: #{vendor_manifest['vendor']} #{vendor_manifest['version']} " +
+ "is not available on target: #{@session.TARGET}")
+ end
+
+ @log.info("Create Service (#{@service.vendor} #{@service.version}): #{@service.name}")
+ begin
+ @service.create!
+ rescue Exception => e
+ @log.error("Fail to create service (#{@service.vendor} #{@service.version}): #{@service.name}")
+ raise RuntimeError, "Fail to create service (#{@service.vendor} #{@service.version}): #{@service.name}\n#{e.to_s}"
+ end
+ end
+
+ def delete
+ if @service.exists?
+ @log.info("Delete Service (#{@service.vendor} #{@service.version}): #{@service.name}")
+ begin
+ @service.delete!
+ rescue Exception => e
+ @log.error("Fail to delete service (#{@service.vendor} #{@service.version}): #{@service.name}")
+ raise RuntimeError, "Fail to delete service (#{@service.vendor} #{@service.version}): #{@service.name}\n#{e.to_s}"
+ end
+ end
+ end
+ end
+end
View
52 lib/harness/user.rb
@@ -0,0 +1,52 @@
+require "cfoundry"
+
+module BVT::Harness
+ class User
+ attr_reader :email, :passwd
+
+ def initialize(user, session)
+ @user = user
+ @email = @user.email
+ @session = session
+ @log = @session.log
+ end
+
+ def inspect
+ "#<BVT::Harness::User '#@email'>"
+ end
+
+ def create(passwd)
+ @log.info("Create User: #{@email} via Admin User: #{@session.email}")
+ begin
+ @session.register(@email, passwd)
+ @passwd = passwd
+ rescue
+ @log.error("Failed to create user: #{@email}")
+ raise RuntimeError, "Failed to create user: #{@email}"
+ end
+ end
+
+ def delete
+ @log.info("Delete User: #{@email} via Admin User:#{@session.email}")
+ begin
+ @user.delete!
+ rescue Exception => e
+ @log.error("Failed to delete user")
+ raise RuntimeError, "Failed to delete user.\n#{e.to_s}"
+ end
+ end
+
+ def change_passwd(new_passwd)
+ @log.info "Change User: #{@email} password, new passwd = #{new_passwd}"
+ begin
+ @user.password = new_passwd
+ @user.update!
+ rescue
+ @log.error("Fail to change password for user: #{@email}")
+ raise RuntimeError,
+ "Fail to change passsword for user = #{@email}"
+ end
+ end
+ end
+end
+
View
168 spec/autostaging/ruby19_sinatra_spec.rb
@@ -0,0 +1,168 @@
+require "harness"
+require "spec_helper"
+
+describe BVT::Spec::AutoStaging::Ruby19Sinatra do
+
+ before(:each) do
+ @client = BVT::Harness::CFSession.new
+ end
+
+ after(:each) do
+ @client.cleanup!
+ end
+
+ def bind_service(service_manifest, app)
+ service = @client.service(service_manifest['vendor'])
+ service.create(service_manifest)
+ app.bind(service.name)
+ end
+
+ def verify_service_autostaging(service_manifest, app)
+ key = "abc"
+ data = "#{service_manifest['vendor']}#{key}"
+ if service_manifest['vendor'] == 'mongodb'
+ #TODO
+ # once switch to yeti entirely, need to change app_sinatra_service_autoconfig in assets
+ # in order to keep url consist with service vendor name
+ # and then remove following two statements.
+ app.get_response(:post, "/service/mongo/#{key}", data)
+ app.get_response(:get, "/service/mongo/#{key}").body_str.should == data
+ else
+ app.get_response(:post, "/service/#{service_manifest['vendor']}/#{key}", data)
+ app.get_response(:get, "/service/#{service_manifest['vendor']}/#{key}").body_str.should == data
+ end
+ end
+
+ def verify_unsupported_client_version(service_manifest, app, data)
+ key = "connection"
+ case service_manifest['vendor']
+ when 'mongodb'
+ #TODO
+ # once switch to yeti entirely, need to change autoconfig_unsupported_versions in assets
+ # in order to keep url consist with service vendor name
+ # and then remove following two statements.
+ app.get_response(:get, "/service/mongo/#{key}").body_str.should == data
+ when 'rabbitmq'
+ app.get_response(:get, "/service/amqp/#{key}").body_str.should == data
+ when 'postgresql'
+ app.get_response(:get, "/service/postgres/#{key}").body_str.should == data
+ else
+ app.get_response(:get, "/service/#{service_manifest['vendor']}/#{key}").body_str.should == data
+ end
+ end
+
+ it "services autostaging" do
+ manifest = {"instances"=>1,
+ "staging"=>{"framework"=>"sinatra", "runtime"=>"ruby19"},
+ "resources"=>{"memory"=>64}}
+ app = @client.app("app_sinatra_service_autoconfig")
+ app.push(manifest)
+ app.healthy?.should be_true, "Application #{app.name} is not running"
+ app.get_response(:get, "/crash").body_str.should =~ /502 Bad Gateway/
+
+ # provision service
+ manifests = [{"vendor"=>"mysql", "version"=>"5.1"},
+ {"vendor"=>"redis", "version"=>"2.2"},
+ {"vendor"=>"mongodb", "version"=>"1.8"},
+ {"vendor"=>"rabbitmq", "version"=>"2.4"},
+ {"vendor"=>"postgresql", "version"=>"9.0"}]
+ manifests.each do |service_manifest|
+ bind_service(service_manifest, app)
+ verify_service_autostaging(service_manifest, app)
+ end
+ end
+
+ it "Sinatra AMQP autostaging" do
+ manifest = {"instances"=>1,
+ "staging"=>{"framework"=>"sinatra", "runtime"=>"ruby19"},
+ "resources"=>{"memory"=>64}}
+ app = @client.app("amqp_autoconfig")
+ app.push(manifest)
+ app.healthy?.should be_true, "Application #{app.name} is not running"
+ app.get_response(:get).body_str.should == "hello from sinatra"
+
+ # provision service
+ service_manifest = {"vendor"=>"rabbitmq", "version"=>"2.4"}
+ bind_service(service_manifest, app)
+ data = "#{service_manifest['vendor']}abc"
+ app.get_response(:post, "/service/amqpurl/abc", data)
+ app.get_response(:get, "/service/amqpurl/abc").body_str.should == data
+
+ app.get_response(:post, "/service/amqpoptions/abc", data)
+ app.get_response(:get, "/service/amqpoptions/abc").body_str.should == data
+ end
+
+ it "Autostaging with unsupported client versions" do
+ manifest = {"instances"=>1,
+ "staging"=>{"framework"=>"sinatra", "runtime"=>"ruby19"},
+ "resources"=>{"memory"=>64}}
+ app = @client.app("autoconfig_unsupported_versions")
+ app.push(manifest)
+ app.healthy?.should be_true, "Application #{app.name} is not running"
+ app.get_response(:get).body_str.should == "hello from sinatra"
+
+ # provision service
+ testdata = [{:service => {"vendor"=>"mysql", "version"=>"5.1"},
+ :data => "Can'tconnecttoMySQLserveron'127.0.0.1'(111)"},
+ {:service => {"vendor"=>"redis", "version"=>"2.2"},
+ :data => "Connectionrefused-UnabletoconnecttoRedison127.0.0.1:6379"},
+ {:service => {"vendor"=>"rabbitmq", "version"=>"2.4"},
+ :data => "Couldnotconnecttoserver127.0.0.1:4567"},
+ {:service => {"vendor"=>"postgresql", "version"=>"9.0"},
+ :data => "couldnotconnecttoserver:ConnectionrefusedIstheserverrunningonhost\"127.0.0.1\"andacceptingTCP/IPconnectionsonport8675?"},
+ {:service => {"vendor"=>"mongodb", "version"=>"1.8"},
+ :data => "Failedtoconnecttoamasternodeat127.0.0.1:4567"}]
+ testdata.each do |item|
+ bind_service(item[:service], app)
+ verify_unsupported_client_version(item[:service], app, item[:data])
+ end
+ end
+
+ it "Autostaging with unsupported carrot version" do
+ manifest = {"instances"=>1,
+ "staging"=>{"framework"=>"sinatra", "runtime"=>"ruby19"},
+ "resources"=>{"memory"=>64}}
+ app = @client.app("autoconfig_unsupported_carrot_version")
+ app.push(manifest)
+ app.healthy?.should be_true, "Application #{app.name} is not running"
+ app.get_response(:get).body_str.should == "hello from sinatra"
+
+ # provision service
+ service_manifest = {"vendor"=>"rabbitmq", "version"=>"2.4"}
+ bind_service(service_manifest, app)
+ data = "Connectionrefused-connect(2)-127.0.0.1:1234"
+ app.get_response(:get, "/service/carrot/connection").body_str.should == data
+ end
+
+ it "Sinatra opt-out of autostaging via config file" do
+ manifest = {"instances"=>1,
+ "staging"=>{"framework"=>"sinatra", "runtime"=>"ruby19"},
+ "resources"=>{"memory"=>64}}
+ app = @client.app("sinatra_autoconfig_disabled_by_file")
+ app.push(manifest)
+ app.healthy?.should be_true, "Application #{app.name} is not running"
+ app.get_response(:get).body_str.should == "hello from sinatra"
+
+ # provision service
+ service_manifest = {"vendor"=>"redis", "version"=>"2.2"}
+ bind_service(service_manifest, app)
+ data = "Connectionrefused-UnabletoconnecttoRedison127.0.0.1:6379"
+ app.get_response(:get, "/service/#{service_manifest['vendor']}/connection").body_str.should == data
+ end
+
+ it "Sinatra opt-out of autostaging via cf-runtime gem" do
+ manifest = {"instances"=>1,
+ "staging"=>{"framework"=>"sinatra", "runtime"=>"ruby19"},
+ "resources"=>{"memory"=>64}}
+ app = @client.app("sinatra_autoconfig_disabled_by_gem")
+ app.push(manifest)
+ app.healthy?.should be_true, "Application #{app.name} is not running"
+ app.get_response(:get).body_str.should == "hello from sinatra"
+
+ # provision service
+ service_manifest = {"vendor"=>"redis", "version"=>"2.2"}
+ bind_service(service_manifest, app)
+ data = "Connectionrefused-UnabletoconnecttoRedison127.0.0.1:6379"
+ app.get_response(:get, "/service/#{service_manifest['vendor']}/connection").body_str.should == data
+ end
+end
View
14 spec/spec_helper.rb
@@ -0,0 +1,14 @@
+
+
+module BVT
+ module Spec
+
+ module UsersManagement
+ class AdminUser; end
+ end
+
+ module AutoStaging
+ class Ruby19Sinatra; end
+ end
+ end
+end
View
36 spec/users/admin_user_spec.rb
@@ -0,0 +1,36 @@
+require "harness"
+require "spec_helper"
+
+describe BVT::Spec::UsersManagement::AdminUser do
+
+ before(:each) do
+ @admin_session = BVT::Harness::CFSession.new(true)
+ @test_email = "#{@admin_session.namespace}my_fake@email.address"
+ end
+
+ after(:each) do
+ test_user = @admin_session.user(@test_email)
+ test_user.delete
+ end
+
+ it "test add-user/users/delete-user/passwd command" do
+ # create user
+ test_user = @admin_session.user(@test_email)
+ test_pwd = "test-pwd"
+ test_user.create(test_pwd)
+ @admin_session.users.collect(&:email).include?(test_user.email).should \
+ be_true, "cannot find created user-email, #{test_user.email}"
+
+ # login as created user
+ test_session= BVT::Harness::CFSession.new(false, test_user.email, test_user.passwd)
+
+ # change passwd
+ test_user = test_session.user(@test_email)
+ new_passwd = "new_P@ssw0rd"
+ test_user.change_passwd(new_passwd)
+
+ # login as new passwd
+ test_session = BVT::Harness::CFSession.new(false, test_user.email, new_passwd)
+ end
+
+end
View
BIN  vendor/cache/vcap_logging-1.0.0.gem
Binary file not shown
Please sign in to comment.
Something went wrong with that request. Please try again.