Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use OpenShift API to control the Ansible container #15492

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ gem "gettext_i18n_rails", "~>1.7.2"
gem "gettext_i18n_rails_js", "~>1.1.0"
gem "hamlit", "~>2.7.0"
gem "inifile", "~>3.0", :require => false
gem "kubeclient", "~>2.4.0", :require => false # For scaling pods at runtime
gem "manageiq-api-client", "~>0.1.0", :require => false
gem "manageiq-network_discovery", "~>0.1.1", :require => false
gem "mime-types", "~>2.6.1", :path => "mime-types-redirector"
Expand Down
28 changes: 28 additions & 0 deletions lib/container_orchestrator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class ContainerOrchestrator
TOKEN_FILE = "/run/secrets/kubernetes.io/serviceaccount/token".freeze

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kill this blank line please. (You vim users and your leading blank lines :trollface: )

def scale(deployment_config_name, replicas)
connection.patch_deployment_config(deployment_config_name, { :spec => { :replicas => replicas } }, ENV["MY_POD_NAMESPACE"])
end

private

def connection
require 'kubeclient'

@connection ||=
Kubeclient::Client.new(
manager_uri,
:auth_options => { :bearer_token_file => TOKEN_FILE },
:ssl_options => { :verify_ssl => OpenSSL::SSL::VERIFY_NONE }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

friendly reminder: will need something better than VERIFY_NONE eventually :)

This is to be used when MIQ runs inside the cluster, right?
https://kubernetes.io/docs/tasks/tls/managing-tls-in-a-cluster/#trusting-tls-in-a-cluster
sounds like we can use /var/run/secrets/kubernetes.io/serviceaccount/ca.crt

)
end

def manager_uri
URI::HTTPS.build(
:host => ENV["KUBERNETES_SERVICE_HOST"],
:port => ENV["KUBERNETES_SERVICE_PORT"],
:path => "/oapi"
)
end
end
28 changes: 19 additions & 9 deletions lib/embedded_ansible.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class EmbeddedAnsible
HTTPS_PORT = 54_322
WAIT_FOR_ANSIBLE_SLEEP = 1.second
TOWER_VERSION_FILE = "/var/lib/awx/.tower_version".freeze
ANSIBLE_DC_NAME = "manageiq-ansible".freeze

def self.available?
return true if MiqEnvironment::Command.is_container?
Expand Down Expand Up @@ -52,21 +53,15 @@ def self.alive?
end

def self.start
if MiqEnvironment::Command.is_container?
container_start
else
appliance_start
end
MiqEnvironment::Command.is_container? ? container_start : appliance_start
end

def self.stop
return if MiqEnvironment::Command.is_container?
services.each { |service| LinuxAdmin::Service.new(service).stop }
MiqEnvironment::Command.is_container? ? container_stop : appliance_stop
end

def self.disable
return if MiqEnvironment::Command.is_container?
services.each { |service| LinuxAdmin::Service.new(service).stop.disable }
MiqEnvironment::Command.is_container? ? container_stop : appliance_disable
end

def self.services
Expand Down Expand Up @@ -110,8 +105,19 @@ def self.appliance_start
end
private_class_method :appliance_start

def self.appliance_stop
services.each { |service| LinuxAdmin::Service.new(service).stop }
end
private_class_method :appliance_stop

def self.appliance_disable
services.each { |service| LinuxAdmin::Service.new(service).stop.disable }
end
private_class_method :appliance_disable

def self.container_start
miq_database.set_ansible_admin_authentication(:password => ENV["ANSIBLE_ADMIN_PASSWORD"])
ContainerOrchestrator.new.scale(ANSIBLE_DC_NAME, 1)

loop do
break if alive?
Expand All @@ -122,6 +128,10 @@ def self.container_start
end
private_class_method :container_start

def self.container_stop
ContainerOrchestrator.new.scale(ANSIBLE_DC_NAME, 0)
end

def self.run_setup_script(exclude_tags)
json_extra_vars = {
:minimum_var_space => 0,
Expand Down
69 changes: 41 additions & 28 deletions spec/lib/embedded_ansible_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,24 +46,6 @@
end
end

describe ".stop" do
it "doesn't attempt to stop services if running in a container" do
expect(MiqEnvironment::Command).to receive(:is_container?).and_return(true)
expect(LinuxAdmin::Service).not_to receive(:new)

described_class.stop
end
end

describe ".disable" do
it "doesn't attempt to disable services if running in a container" do
expect(MiqEnvironment::Command).to receive(:is_container?).and_return(true)
expect(LinuxAdmin::Service).not_to receive(:new)

described_class.disable
end
end

context "with services" do
let(:nginx_service) { double("nginx service") }
let(:supervisord_service) { double("supervisord service") }
Expand Down Expand Up @@ -417,19 +399,50 @@
end
end

describe ".start when in a container" do
around do |example|
ENV["ANSIBLE_ADMIN_PASSWORD"] = "thepassword"
example.run
ENV.delete("ANSIBLE_ADMIN_PASSWORD")
context "when in a container" do
before do
expect(MiqEnvironment::Command).to receive(:is_container?).and_return(true)
end

it "sets the admin password using the environment variable and waits for the service to respond" do
expect(MiqEnvironment::Command).to receive(:is_container?).and_return(true)
expect(described_class).to receive(:alive?).and_return(true)
describe ".stop" do
it "scales the ansible pod to 0 replicas" do
orch = double("ContainerOrchestrator")
expect(ContainerOrchestrator).to receive(:new).and_return(orch)

described_class.start
expect(miq_database.reload.ansible_admin_authentication.password).to eq("thepassword")
expect(orch).to receive(:scale).with("manageiq-ansible", 0)

described_class.stop
end
end

describe ".disable" do
it "scales the ansible pod to 0 replicas" do
orch = double("ContainerOrchestrator")
expect(ContainerOrchestrator).to receive(:new).and_return(orch)

expect(orch).to receive(:scale).with("manageiq-ansible", 0)

described_class.disable
end
end

describe ".start" do
around do |example|
ENV["ANSIBLE_ADMIN_PASSWORD"] = "thepassword"
example.run
ENV.delete("ANSIBLE_ADMIN_PASSWORD")
end

it "sets the admin password using the environment variable and waits for the service to respond" do
orch = double("ContainerOrchestrator")
expect(ContainerOrchestrator).to receive(:new).and_return(orch)

expect(orch).to receive(:scale).with("manageiq-ansible", 1)
expect(described_class).to receive(:alive?).and_return(true)

described_class.start
expect(miq_database.reload.ansible_admin_authentication.password).to eq("thepassword")
end
end
end

Expand Down