-
Notifications
You must be signed in to change notification settings - Fork 172
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Andrej Podhradsky <[email protected]>
- Loading branch information
Showing
5 changed files
with
306 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
#!/usr/bin/env ruby | ||
|
||
lib_path = File.expand_path(File.dirname(File.dirname(__FILE__))) | ||
unless $LOAD_PATH.any? {|p| File.expand_path(p) == lib_path} | ||
$LOAD_PATH.unshift(lib_path) | ||
end | ||
|
||
require 'common' | ||
require 'json' | ||
require 'tmpdir' | ||
require 'git' | ||
|
||
module BushSlicer | ||
class OCM | ||
include Common::Helper | ||
|
||
attr_reader :config | ||
attr_reader :token, :token_file, :url, :region, :version, :nodes, :lifespan, :cloud, :cloud_opts, :multi_az | ||
|
||
def initialize(**options) | ||
service_name = ENV['OCM_SERVICE_NAME'] || options[:service_name] || 'ocm' | ||
@opts = default_opts(service_name)&.merge options | ||
unless @opts | ||
@opts = options | ||
end | ||
|
||
# OCM token is mandatory | ||
# it can be defined by token or by token_file | ||
@token = ENV['OCM_TOKEN'] || @opts[:token] | ||
@token_file = @opts[:token_file] | ||
unless @token | ||
if @token_file | ||
token_file_path = expand_private_path(@token_file) | ||
@token = File.read(token_file_path) | ||
else | ||
raise "You need to specify OCM token by 'token' or by 'token_file'" | ||
end | ||
end | ||
|
||
# region is mandatory | ||
# in the future we can extend support for other clouds, e.g. GCP and ARO | ||
@region = ENV['OCM_REGION'] || ENV['AWS_REGION'] || @opts[:region] | ||
|
||
# url defines the OCM environment (prod, integration or stage) | ||
# currently, the url is ignored as many teams use the stage environment | ||
@url = ENV['OCM_URL'] || @opts[:url] || 'https://api.stage.openshift.com' | ||
|
||
# openshift version is optional | ||
@version = ENV['OCM_VERSION'] || ENV['OCP_VERSION'] || @opts[:version] | ||
|
||
# number of worker nodes | ||
# minimum is 2 | ||
# default value is 4 | ||
@nodes = ENV['OCM_NODES'] || @opts[:nodes] | ||
|
||
# lifespan in hours | ||
# default value is 24 hours | ||
@lifespan = ENV['OCM_LIFESPAN'] || @opts[:lifespan] | ||
|
||
# multi_az is optional | ||
# default value is false | ||
@multi_az = ENV['OCM_MULTI_AZ'] || @opts[:multi_az] | ||
|
||
# BYOC (Bring Your Own Cloud) | ||
# you can refer to already defined cloud in config.yaml | ||
# currently, only AWS is supported | ||
@cloud = ENV['OCM_CLOUD'] || @opts[:cloud] | ||
if @cloud | ||
@cloud_opts = default_opts(@cloud) | ||
unless @cloud_opts | ||
raise "Cannot find cloud '#{cloud}' defined in '#{service_name}'" | ||
end | ||
end | ||
end | ||
|
||
# @param service_name [String] the service name of this openstack instance | ||
# to lookup in configuration | ||
def default_opts(service_name) | ||
return conf[:services, service_name.to_sym] | ||
end | ||
|
||
def to_seconds(string) | ||
regex_m = /^(\d+)\s*(m|min|minutes|mins)+$/ | ||
regex_h = /^(\d+)\s*(h|hour|hours|hrs)+$/ | ||
regex_d = /^(\d+)\s*(d|day|days)+$/ | ||
regex_w = /^(\d+)\s*(w|week|weeks|wks)+$/ | ||
case string | ||
when regex_m | ||
return string.match(regex_m)[1].to_i * 60 | ||
when regex_h | ||
return string.match(regex_h)[1].to_i * 60 * 60 | ||
when regex_d | ||
return string.match(regex_d)[1].to_i * 24 * 60 * 60 | ||
when regex_w | ||
return string.match(regex_w)[1].to_i * 7 * 24 * 60 * 60 | ||
else | ||
raise "Cannot convert '#{string}' to seconds!" | ||
end | ||
end | ||
|
||
# create a json which specifies OSD cluster | ||
# in the future we plan to move the logic into the script 'osd-provision.sh' | ||
def generate_json(name) | ||
json_data = { | ||
"name" => name, | ||
"managed" => true, | ||
"multi_az" => false, | ||
"byoc" => false | ||
} | ||
|
||
if @multi_az | ||
json_data.merge!({"multi_az" => @multi_az}) | ||
end | ||
|
||
if @region | ||
json_data.merge!({"region" => {"id" => @region}}) | ||
end | ||
|
||
if @version | ||
json_data.merge!({"version" => {"id" => "openshift-v#{@version}"}}) | ||
end | ||
|
||
if @nodes | ||
json_data.merge!({"nodes" => {"compute" => @nodes.to_i}}) | ||
end | ||
|
||
if @lifespan | ||
expiration = Time.now + to_seconds(@lifespan) | ||
json_data.merge!({"expiration_timestamp" => expiration.strftime("%Y-%m-%dT%H:%M:%SZ")}) | ||
end | ||
|
||
if @cloud_opts | ||
case @cloud_opts[:cloud_type] | ||
when "aws" | ||
aws = Amz_EC2.new(service_name: @cloud) | ||
json_data.merge!({"aws" => {"access_key_id":aws.access_key, "secret_access_key":aws.secret_key, "account_id":aws.account_id}}) | ||
json_data.merge!({"byoc" => true}) | ||
end | ||
end | ||
|
||
return json_data.to_json | ||
end | ||
|
||
# download the script 'osd-provision.sh' which takes care of the OSD installation/uninstallation | ||
def download_osd_script | ||
osd_repo_uri = ENV['GIT_OSD_URI'] || 'https://gitlab.cee.redhat.com/mk-bin-packing/mk-performance-tests.git' | ||
osd_repo_dir = File.join(Dir.tmpdir, 'osd_repo') | ||
FileUtils.rm_rf(osd_repo_dir) | ||
git = BushSlicer::Git.new(uri: osd_repo_uri, dir: osd_repo_dir) | ||
git.clone | ||
osd_script = File.join(osd_repo_dir, 'scripts', 'osd-provision.sh') | ||
if !File.exists?(osd_script) | ||
raise "Cannot find #{osd_script}" | ||
end | ||
return osd_script | ||
end | ||
|
||
def shell(cmd, output = nil) | ||
if output | ||
res = Host.localhost.exec(cmd, single: true, stderr: :stdout, stdout: output, timeout: 3600) | ||
else | ||
res = Host.localhost.exec(cmd, single: true, timeout: 3600) | ||
end | ||
if res[:success] | ||
return res[:response] | ||
else | ||
raise "Error when executing '#{cmd}'. Response: #{res[:response]}" | ||
end | ||
end | ||
|
||
# create OSD cluster | ||
def create_osd(name) | ||
# cerate a temp file with ocm-token | ||
ocm_token_file = Tempfile.new("ocm-token-file", Host.localhost.workdir) | ||
File.write(ocm_token_file, @token) | ||
# create cluster.json in a workdir/install-dir | ||
install_dir = File.join(Host.localhost.workdir, 'install-dir') | ||
FileUtils.mkdir_p(install_dir) | ||
ocm_json_file = File.join(install_dir, 'cluster.json') | ||
File.write(ocm_json_file, generate_json(name)) | ||
# now, download the script which will take care of the OSD cluster installation | ||
osd_script = download_osd_script | ||
shell("#{osd_script} --create --cloud-token-file #{ocm_token_file.path} -f #{ocm_json_file} --wait", STDOUT) | ||
shell("#{osd_script} --get api_url -f #{ocm_json_file}") | ||
shell("#{osd_script} --get credentials -f #{ocm_json_file}") | ||
end | ||
|
||
# delete OSD cluster | ||
def delete_osd(name) | ||
# create a temp file with ocm-token | ||
ocm_token_file = Tempfile.new("ocm-token-file", Host.localhost.workdir) | ||
File.write(ocm_token_file, @token) | ||
# now, download the script which will take care of the OSD cluster installation | ||
osd_script = download_osd_script | ||
shell("#{osd_script} --delete --cloud-token-file #{ocm_token_file.path} -n #{name}") | ||
end | ||
|
||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
ENV['BUSHSLICER_PRIVATE_DIR'] = nil | ||
ENV['OCM_NAME'] = nil | ||
ENV['OCM_TOKEN'] = nil | ||
ENV['OCM_URL'] = nil | ||
ENV['OCM_REGION'] = nil | ||
ENV['OCM_VERSION'] = nil | ||
ENV['OCM_LIFESPAN'] = nil | ||
|
||
lib_path = File.expand_path(File.dirname(File.dirname(__FILE__))) | ||
unless $LOAD_PATH.any? {|p| File.expand_path(p) == lib_path} | ||
$LOAD_PATH.unshift(lib_path) | ||
end | ||
|
||
require 'fileutils' | ||
require 'test/unit' | ||
require_relative './ocm' | ||
|
||
class MyTest < Test::Unit::TestCase | ||
def setup | ||
|
||
end | ||
|
||
# def teardown | ||
# end | ||
|
||
def test_default_url | ||
options = { :token => "abc" } | ||
ocm = BushSlicer::OCM.new(options) | ||
assert_equal('https://api.stage.openshift.com', ocm.url) | ||
end | ||
|
||
def test_generating_json | ||
options = { :token => "abc" } | ||
ocm = BushSlicer::OCM.new(options) | ||
json = ocm.generate_json('myosd4') | ||
assert_equal('{"name":"myosd4","managed":true,"multi_az":false,"byoc":false}', json) | ||
end | ||
|
||
def test_generating_json_with_region | ||
options = { :token => "abc", :region => "us-east-1" } | ||
ocm = BushSlicer::OCM.new(options) | ||
json = ocm.generate_json('myosd4') | ||
assert_equal('{"name":"myosd4","managed":true,"multi_az":false,"byoc":false,"region":{"id":"us-east-1"}}', json) | ||
end | ||
|
||
def test_generating_json_with_version | ||
options = { :token => "abc", :version => "4.6.1" } | ||
ocm = BushSlicer::OCM.new(options) | ||
json = ocm.generate_json('myosd4') | ||
assert_equal('{"name":"myosd4","managed":true,"multi_az":false,"byoc":false,"version":{"id":"openshift-v4.6.1"}}', json) | ||
end | ||
|
||
def test_generating_json_with_lifespan | ||
options = { :token => "abc", :lifespan => "25h" } | ||
ocm = BushSlicer::OCM.new(options) | ||
json = ocm.generate_json('myosd4') | ||
time = Time.now + 60 * 60 * 25 | ||
year = time.strftime("%Y") | ||
month = time.strftime("%m") | ||
day = time.strftime("%d") | ||
assert_match(/.*"expiration_timestamp":"#{year}-#{month}-#{day}T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]Z".*/, json) | ||
end | ||
|
||
def test_generating_json_with_nodes | ||
options = { :token => "abc", :nodes => "8" } | ||
ocm = BushSlicer::OCM.new(options) | ||
json = ocm.generate_json('myosd4') | ||
assert_equal('{"name":"myosd4","managed":true,"multi_az":false,"byoc":false,"nodes":{"compute":8}}', json) | ||
end | ||
|
||
def test_downloading_osd_script | ||
options = { :token => "abc" } | ||
ocm = BushSlicer::OCM.new(options) | ||
osd_script = ocm.download_osd_script | ||
assert(File.exists?(osd_script), "File 'osd-provision.sh' was not downloaded") | ||
content = File.read(osd_script) | ||
assert_match(/.*ocm.*/, content) | ||
end | ||
|
||
def test_executing_shell | ||
hello_script = "/tmp/hello.sh" | ||
File.write(hello_script, "#!/bin/sh\n[[ -z \"$1\" ]] && echo \"Specify a name!\" && exit 1; for i in {1..3}; do echo \"Hello $1\"; sleep 5; done") | ||
File.chmod(0755, hello_script) | ||
options = { :token => "abc" } | ||
ocm = BushSlicer::OCM.new(options) | ||
result = ocm.shell("#{hello_script} World") | ||
assert_equal("Hello World\nHello World\nHello World\n", result) | ||
result = ocm.shell("#{hello_script} World", STDOUT) | ||
assert_equal("", result) | ||
error = assert_raises(RuntimeError) { ocm.shell("#{hello_script} ") } | ||
assert_equal("Error when executing '#{hello_script} '. Response: Specify a name!\n", error.message) | ||
error = assert_raises(RuntimeError) { ocm.shell("#{hello_script} ", STDOUT) } | ||
assert_equal("Error when executing '#{hello_script} '. Response: ", error.message) | ||
end | ||
|
||
end |