module QuiversTaskrunner

Constants

VERSION

Public Instance Methods

configure_subscription(env=nil) click to toggle source
# File lib/quiverstaskrunner/azuresetup.rb, line 131
def configure_subscription(env=nil)

        # 1. Load environment variables
        env = env || EnvHelper.get_env("env")
        pfx_path = EnvHelper.get_env("azure_management_certificate")
        subscription_id = EnvHelper.get_env("azure_subscription_id")

        # 2. Load secrets
        storage = SettingsHelper.get_settings(File.join(Dir.pwd, @@secrets[:storage]), env, @@get_settings_options)
        storage_details = storage["blob"]
        raise StandardError, "No details were found for blob storage for the #{env} environment" unless storage_details

        Azure.configure do |config|
            config.storage_account_name = storage_details["name"]
            config.storage_access_key   = storage_details["key"]
                config.management_certificate = pfx_path
            config.subscription_id        = subscription_id
            config.management_endpoint    = "https://management.core.windows.net"
        end
end
create_azure_files_restore_points(files_to_backup) click to toggle source
# File lib/quiverstaskrunner/restorepoints.rb, line 7
def create_azure_files_restore_points(files_to_backup)

        # convert single file into an array
        files = files_to_backup.is_a?(Array) ? files_to_backup : (files_to_backup.nil? ? [] : [files_to_backup])

        files.each do |f|
                f_ext = File.extname(f)
                f_backup_ext = ".backup#{f_ext}"
                f_backup = f.gsub(f_ext, f_backup_ext)
                # 1. Delete previous backup files if there are still present
                FileUtils.rm_f(f_backup) if FileTest.exists? f_backup
                # 2. Create backup files
                FileUtils.cp f, f_backup
        end
end
delete_staging(cloud_service_name, env=nil) click to toggle source
# File lib/quiverstaskrunner/azuresetup.rb, line 338
def delete_staging(cloud_service_name, env=nil)

        # 1. Load cloud service's details
        env = env || EnvHelper.get_env("env")
        cloud_services = SettingsHelper.get_settings(File.join(Dir.pwd, @@secrets[:cloud_services]), env, @@get_settings_options)
        cloud_service_details = cloud_services[cloud_service_name]
        raise StandardError, "No details were found for cloud service #{cloud_service_name}" unless cloud_service_details
        cloud_srv_name = cloud_service_details["name"]

        # 2. Check if there is a Staging deployment for that Cloud Service
        cloud_srv_mgmt = Azure::CloudServiceManagementService.new
        deployment = cloud_srv_mgmt.get_deployment(cloud_srv_name, {:slot => "staging", :no_exit_on_failure => true})

        # 3. Delete the Staging deployment if it exists
        start_time = Time.now
        if deployment.exists?
                puts "Deleting staging deployment with id #{deployment.private_id} for cloud service #{cloud_srv_name}"
                cloud_srv_mgmt.delete_cloud_service_deployment(cloud_srv_name, "staging")
                ellapsed_time_min = (Time.now - start_time)/60.to_f
                puts "Deleting staging deployment #{deployment.private_id} for cloud service #{cloud_srv_name} done. Operation took #{ellapsed_time_min} minutes to complete".colorize(:green)
        else
                puts "No staging deployment detected for cloud service '#{cloud_srv_name}'".colorize(:magenta)
        end
end
deploy_blob_pkg_to_staging(cloud_service_name, blob_pkg_name, pkg_dir, labels, options={}) click to toggle source
# File lib/quiverstaskrunner/azuresetup.rb, line 197
def deploy_blob_pkg_to_staging(cloud_service_name, blob_pkg_name, pkg_dir, labels, options={})
        # 1. Load cloud service's details
        env = options[:env] || EnvHelper.get_env("env")
        timeout = options[:timeout] || 20*60 # 20 minutes
        update_message_on = is_update_message_on
        cloud_services = SettingsHelper.get_settings(File.join(Dir.pwd, @@secrets[:cloud_services]), env, @@get_settings_options)
        cloud_service_details = cloud_services[cloud_service_name]
        raise StandardError, "No details were found for cloud service #{cloud_service_name}" unless cloud_service_details
        # 2. Load storage details
        storage = SettingsHelper.get_settings(File.join(Dir.pwd, @@secrets[:storage]), env, @@get_settings_options)
        storage_details = storage["blob"]
        raise StandardError, "No details were found for blob storage for the #{env} environment" unless storage_details

        # 3. Configure the deployment details
        deployment_name = SecureRandom.uuid
        cloud_srv_name = cloud_service_details["name"]
        package_url = ENV["package_url"]
        if package_url.nil?
                if blob_pkg_name.nil? || blob_pkg_name.empty?
                        raise ArgumentError, "Missing argument. No 'blob_pkg_name' or 'package_url' defined."
                else
                        package_url = "https://#{storage_details['name']}.blob.core.windows.net/mydeployments/#{blob_pkg_name}"
                end
        end
        # This way of getting the service configuration file may seem weird(indeed, why not
        # using the standard File.open method?), but for some reasons that I don't understand,
        # File.open will result in a 'Bad Request : The specified configuration settings for Configuration
        # are invalid.' error
        service_configuration = XmlHelper.get_xml(File.join(pkg_dir, "ServiceConfiguration.#{env.capitalize}.cscfg")).to_xml

        deployment_options = {
                :slot => "staging",
                :label => labels,
                :start_deployment => true,
                :fire_and_forget => true,
                :upgrade_if_exists => true
        }

        # 4. Asynchronously deploy package to staging
        start_time = Time.now
        init_msg = nil
        cloud_srv_mgmt = Azure::CloudServiceManagementService.new
        no_error = true
        begin
                puts "Start deploying package from blob storage #{package_url} to cloud service"
                resp = cloud_srv_mgmt.create_deployment(deployment_name, cloud_srv_name, package_url, service_configuration, deployment_options)
                init_msg = "successfully initiated"
        rescue Exception => ex
                no_error = false
                init_msg = "failed"
                raise ex
        ensure
                ellapsed_time_min = (Time.now - start_time)/60.to_f
                puts "Deployment #{deployment_name} #{init_msg} after #{ellapsed_time_min} minutes"
        end

        # 5. Wait until the deployment is done
        deployment_pending = true
        waiting_start_time = Time.now
        timeout = timeout
        counter = 1
        waiting_symbols = ["=", "+"]
        deployment_id = nil
        while deployment_pending do
                waiting_characters = Array.new(counter, waiting_symbols[0]).join
                if update_message_on
                        print "\r"
                        print "Waiting for confirmation of running instances #{waiting_characters}"
                end
                deployment = cloud_srv_mgmt.get_deployment(cloud_srv_name, {:slot => "staging" })
                raise StandardError, "#{timeout} seconds timeout. Client unable to confirm operation success" if (Time.now - waiting_start_time) > timeout
                sleep 10
                deployment_pending = !deployment.all_vms_running? || deployment.is_transitioning?
                deployment_id = deployment.private_id
                counter+=1
                if counter > 40
                        counter = 1
                        waiting_symbols.reverse! 
                end
        end
        print "\n"
        ellapsed_time_min = (Time.now - start_time)/60.to_f
        puts "Deployment #{deployment_name} with id #{deployment_id} running successfully. Deployment took #{ellapsed_time_min} minutes to complete".colorize(:green)
        return no_error
end
is_update_message_on() click to toggle source
# File lib/quiverstaskrunner/azuresetup.rb, line 37
def is_update_message_on
        update_message_on = true
        begin
                update_message_on = (EnvHelper.get_env("update_message_on") == "true")
        rescue
                update_message_on = true
        end
        return update_message_on
end
list_secrets() click to toggle source
# File lib/quiverstaskrunner/azuresetup.rb, line 47
def list_secrets()
        if (defined? ARGV) && (!ARGV.detect { |x| x.index("secrets") == 0 })
                puts "usage: rake list secrets=<args> env=<args>".colorize(:yellow)
                puts 
                puts "This task lists the secrets key value pairs for a specific environment. The optional parameter 'secrets' allows to filter the results.".colorize(:yellow)
                puts "The 'secrets' option is used as follow:".colorize(:yellow)
                puts "     rake list secrets=cloud_services env=test".colorize(:yellow)
                puts "where 'cloud_services' represents all the key value pairs associated with Windows Azure Cloud Services.".colorize(:yellow)
                puts "It is possible to define multiple secrets type by using the '/' separator as follow:".colorize(:yellow)
                puts "     rake list secrets=cloud_services/storage env=test".colorize(:yellow)
                puts
                puts "Allowed secret types are:".colorize(:yellow)
                puts "     all              => List all secret types".colorize(:yellow)
                puts "     cloud_services   => List secrets about the Windows Azure Cloud Services".colorize(:yellow)
                puts "     storage          => List secrets about the Windows Azure Storage".colorize(:yellow)
                puts "     certs            => List all SSL certs currently set up for the consumer pages".colorize(:yellow)
                puts "     global           => List various common global settings used across all Quivers project".colorize(:yellow)
                puts 
                puts "Allowed environments are:".colorize(:yellow)
                puts "          test".colorize(:yellow)
                puts "          demo".colorize(:yellow)
                puts "          prod".colorize(:yellow)
        else
                cli_hash = SettingsHelper.get_cli_settings()
                secrets_list = cli_hash["secrets"]
                white_list = 
                        if (!secrets_list.nil?) && (!secrets_list.empty?) && (secrets_list != "all")
                                white_list = secrets_list
                                        .split('/')
                                        .reject { |x| !@@allowed_secret_types.detect { |k,v| k == x }  }
                                        .map { |x| @@allowed_secret_types[x] }
                        elsif secrets_list == "all"
                                [ :all ]
                        else
                                nil
                        end

                if (!white_list.nil?)
                        env = env || EnvHelper.get_env("env")
                        if white_list[0] != :all
                                @@secrets
                                        .reject { |k,v| !white_list.detect { |x| k == x } }
                                        .each do |k,v| 
                                                puts "#{k}".colorize(:yellow)
                                                puts
                                                puts CliHelper.pretty_print(SettingsHelper.get_settings(File.join(Dir.pwd, v), env, @@get_settings_options)) 
                                        end
                        else
                                @@secrets
                                        .each do |k,v| 
                                                puts "#{k}".colorize(:yellow)
                                                puts
                                                puts CliHelper.pretty_print(SettingsHelper.get_settings(File.join(Dir.pwd, v), env, @@get_settings_options)) 
                                        end
                        end
                else
                        raise StandardError, "None of the provided secrets matches any allowed secret types. Run 'rake list -cs' to get the usage"
                end
        end
end
replace_tokens(files_with_missing_tokens, tokens) click to toggle source
# File lib/quiverstaskrunner/replacetokens.rb, line 4
def replace_tokens(files_with_missing_tokens, tokens)
        files = files_with_missing_tokens.is_a?(Array) ? files_with_missing_tokens : [files_with_missing_tokens]
        raise ArgumentError, "'tokens' must be a Hash" unless tokens.is_a?(Hash)

        files.each { |f_path|
                content = nil
                File.open(f_path, "rb") do |f|
                        content = f.read
                        tokens.each { |k,v|
                                content = content.gsub("{{#{k}}}", v)
                        }
                end

                File.open(f_path, "w") do |f|
                        f.write(content)
                end
        }
end
restore_and_clean_files(files_to_restore) click to toggle source
# File lib/quiverstaskrunner/restorepoints.rb, line 23
def restore_and_clean_files(files_to_restore)

        # convert single file into an array
        files = files_to_restore.is_a?(Array) ? files_to_restore : (files_to_restore.nil? ? [] : [files_to_restore])

        files.each do |f|
                f_ext = File.extname(f)
                f_backup_ext = ".backup#{f_ext}"
                f_backup = f.gsub(f_ext, f_backup_ext)
                # 1. Make sure there are restore files available
                raise StandardError, "Missing restore file #{f_backup}"  unless FileTest.exists? f_backup
                # 2. Delete the modified files
                FileUtils.rm_f(f)
                # 3. Restore modified files
                FileUtils.cp f_backup, f
                # 4. Delete backup files
                FileUtils.rm_f(f_backup)
        end
end
scale_host(cloud_service_name, service_configuration, service_definition, env=nil) click to toggle source
# File lib/quiverstaskrunner/azuresetup.rb, line 108
def scale_host(cloud_service_name, service_configuration, service_definition, env=nil)
        env = env || EnvHelper.get_env("env")

        # 1. Get cloud service's details
        cloud_services = SettingsHelper.get_settings(File.join(Dir.pwd, @@secrets[:cloud_services]), env, @@get_settings_options)
        cloud_service_details = cloud_services[cloud_service_name]
        raise StandardError, "No details were found for cloud service #{cloud_service_name}" unless cloud_service_details
        vmsize = cloud_service_details["vmsize"]
        vmnbr = cloud_service_details["vmnbr"]

        # 2. Load xml files
        service_config_xml = XmlHelper.get_xml(service_configuration)
        service_def_xml = XmlHelper.get_xml(service_definition)

        service_config_xml.update_node_at_css_selector('ServiceConfiguration Role Instances',
                { :count => vmnbr })
        service_def_xml.update_node_at_css_selector('ServiceDefinition WebRole',
                { :vmsize => vmsize })

        XmlHelper.overide_xml(service_configuration, service_config_xml)
        XmlHelper.overide_xml(service_definition, service_def_xml)
end
swap(cloud_service_name, options={}) click to toggle source
# File lib/quiverstaskrunner/azuresetup.rb, line 283
def swap(cloud_service_name, options={})

        # 1. Load cloud service's details
        env = options[:env] || EnvHelper.get_env("env")
        timeout = options[:timeout] || 20*60 # 20 minutes
        update_message_on = is_update_message_on
        cloud_services = SettingsHelper.get_settings(File.join(Dir.pwd, @@secrets[:cloud_services]), env, @@get_settings_options)
        cloud_service_details = cloud_services[cloud_service_name]
        cloud_srv_name = cloud_service_details["name"]

        # 2. Asynchronously swap deployments
        start_time = Time.now
        init_msg = nil
        cloud_srv_mgmt = Azure::CloudServiceManagementService.new
        no_error = true
        begin
                puts "Start swapping deployments for cloud service '#{cloud_srv_name}'"
                resp = cloud_srv_mgmt.swap_deployment(cloud_srv_name, {:fire_and_forget => true})
                init_msg = "successfully initiated"
        rescue Exception => ex
                no_error = false
                init_msg = "failed"
                raise ex
        ensure
                ellapsed_time_min = (Time.now - start_time)/60.to_f
                puts "Swapping deployments for cloud service '#{cloud_srv_name}' #{init_msg} after #{ellapsed_time_min} minutes"
        end

        # 3. Wait until the swapping is done
        deployment_pending = true
        waiting_start_time = Time.now
        counter = 1
        waiting_symbols = ["=", "+"]
        while deployment_pending do
                waiting_characters = Array.new(counter, waiting_symbols[0]).join
                if update_message_on
                        print "\r"
                        print "Waiting for confirmation of running instances #{waiting_characters}"
                end
                deployment = cloud_srv_mgmt.get_deployment(cloud_srv_name, {:slot => "production" })
                raise StandardError, "#{timeout} seconds timeout. Client unable to confirm operation success" if (Time.now - waiting_start_time) > timeout
                sleep 10
                deployment_pending = !deployment.all_vms_running? || deployment.is_transitioning?
                counter+=1
                if counter > 40
                        counter = 1
                        waiting_symbols.reverse! 
                end
        end
        print "\n"
        ellapsed_time_min = (Time.now - start_time)/60.to_f
        puts "Swapping deployments for cloud service '#{cloud_srv_name}' done. Swapping took #{ellapsed_time_min} minutes to complete".colorize(:green)
        return no_error
end
upload_cspkg_to_blob(pkg_name, pkg_dir, options={}) click to toggle source
# File lib/quiverstaskrunner/azuresetup.rb, line 152
def upload_cspkg_to_blob(pkg_name, pkg_dir, options={})
        # 1. Configure the blob object, and load the package inside it
        timeout = options[:timeout] || 20*60 # 20 minutes
        blob_srv = Azure::Blob::BlobService.new
        blob_name =  Time.now.utc.strftime("%Y%m%d_%H%M%S_#{pkg_name}")
        blob_container = "mydeployments"
        block_list = []
        counter = 1

        File.open(File.join(pkg_dir, pkg_name), "rb") do |f|
                f.each_chunk {|chunk|
                        block_id = counter.to_s.rjust(5, '0')
                        block_list << [block_id, :uncommitted]

                        blob_options = {
                                :content_md5 => Base64.strict_encode64(Digest::MD5.digest(chunk)),
                                :content_type => "application/octet-stream",
                                :timeout => timeout
                        }
                        
                        md5 = blob_srv.create_blob_block(blob_container, blob_name, block_id, chunk, blob_options)
                        counter += 1
                }
        end

        # 2. Upload the blob object to the blob storage
        start_time = Time.now
        uploaded_package_name = nil
        no_error = true
        begin
                puts "Start uploading package #{blob_name} to blob storage"
                #new_blob = blob_srv.create_block_blob(blob_container, blob_name, blob_content, blob_options)
                blob_srv.commit_blob_blocks(blob_container, blob_name, block_list)
                uploaded_package_name = blob_name
        rescue Exception => ex
                no_error = false
                raise ex
        ensure
                ellappsed_min = (Time.now - start_time)/60.to_f
                puts "Upload of package #{blob_name} to blob storage succeeded in #{ellappsed_min} minutes".colorize(:green)
        end

        return [uploaded_package_name, no_error]
end