Class: MotherBrain::Gear::DynamicService

Inherits:
Base
  • Object
show all
Includes:
MB::Mixin::Locks, MB::Mixin::Services, Logging
Defined in:
lib/mb/gears/dynamic_service.rb

Constant Summary collapse

START =
"start".freeze
STOP =
"stop".freeze
RESTART =
"restart".freeze
COMMON_STATES =
[
  START,
  STOP,
  RESTART
].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Logging

add_argument_header, dev, filename, #log_exception, logger, #logger, reset, set_logger, setup

Methods inherited from Base

register_gear, #run

Constructor Details

#initialize(component, name) ⇒ DynamicService

Returns a new instance of DynamicService.



54
55
56
57
# File 'lib/mb/gears/dynamic_service.rb', line 54

def initialize(component, name)
  @name      = name
  @component = component
end

Instance Attribute Details

#componentString (readonly)

Returns:

  • (String)


50
51
52
# File 'lib/mb/gears/dynamic_service.rb', line 50

def component
  @component
end

#nameString (readonly)

Returns:

  • (String)


52
53
54
# File 'lib/mb/gears/dynamic_service.rb', line 52

def name
  @name
end

Class Method Details

.change_service_state(service, plugin, environment, state, run_chef = true, options = {}) ⇒ MB::JobTicket

Parses a service, creates a new instance of DynamicService and executes a Chef run to change the state of the service.

Parameters:

  • service (String)

    a dotted string “component.service_name”

  • plugin (MB::Plugin)

    the plugin currently in use

  • environment (String)

    the environment to operate on

  • state (String)

    the state of the service to change to

  • options (Hash) (defaults to: {})

Returns:



20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/mb/gears/dynamic_service.rb', line 20

def change_service_state(service, plugin, environment, state, run_chef = true, options = {})
  job = Job.new(:dynamic_service_state_change)

  component, service_name = service.split('.')
  raise InvalidDynamicService.new(component, service_name) unless component && service_name
  dynamic_service = new(component, service_name)
  dynamic_service.state_change(job, plugin, environment, state, run_chef, options)
  job.report_success
  job.ticket
rescue => ex
  job.report_failure(ex)
ensure
  job.terminate if job && job.alive?
end

Instance Method Details

#get_chef_environment(environment) ⇒ Ridley::EnvironmentObject

Finds and returns the current Chef environment

Parameters:

  • environment (String)

Returns:

  • (Ridley::EnvironmentObject)


152
153
154
# File 'lib/mb/gears/dynamic_service.rb', line 152

def get_chef_environment(environment)
  chef_connection.environment.find(environment)
end

#node_state_change(job, plugin, node, state, run_chef = true) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/mb/gears/dynamic_service.rb', line 100

def node_state_change(job, plugin, node, state, run_chef = true)
  log.warn {
    "Component's service state is being changed to #{state}, which is not one of #{COMMON_STATES}"
  } unless COMMON_STATES.include?(state)

  component_object = plugin.component(component)
  service = component_object.get_service(name)
  set_node_attributes(job, [node], service.service_attribute, state)
  if run_chef
    node_querier.bulk_chef_run(job, [node], service.service_recipe)
  end
end

#remove_node_state_change(job, plugin, node, run_chef = true) ⇒ Object



113
114
115
116
117
118
119
120
121
# File 'lib/mb/gears/dynamic_service.rb', line 113

def remove_node_state_change(job, plugin, node, run_chef = true)
  component_object = plugin.component(component)
  service = component_object.get_service(name)

  unset_node_attributes(job, [node], service.service_attribute)
  if run_chef
    node_querier.bulk_chef_run(job, [node], service.service_recipe)
  end
end

#set_environment_attribute(job, environment, attribute_keys, state) ⇒ Boolean

Finds the environment object, sets an attribute at the override level, and saves the environment back to the Chef server

Parameters:

  • job (MB::Job)

    the job to track status

  • environment (String)

    the environment being operated on

  • attribute_keys (Array<String>)

    an array of dotted paths to attribute keys

  • state (String)

    the state to set the attribute to

Returns:

  • (Boolean)


137
138
139
140
141
142
143
144
145
# File 'lib/mb/gears/dynamic_service.rb', line 137

def set_environment_attribute(job, environment, attribute_keys, state)
  chef_environment = get_chef_environment(environment)

  attribute_keys.each do |attribute_key|
    job.set_status("Setting environment attribute '#{attribute_key}' to #{state} on #{environment}")
    chef_environment.set_override_attribute(attribute_key, state)
  end
  chef_environment.save
end

#set_node_attributes(job, nodes, attribute_keys, state) ⇒ Boolean

Sets a default node attribute on the provided array of nodes.

Parameters:

  • job (MB::Job)

    the job to track status

  • nodes (Array<Ridley::NodeObject>)

    the nodes being operated on

  • attribute_keys (Array<String>)

    an array of dotted paths to attribute keys

  • state (String)

    the state to set the attribute to

Returns:

  • (Boolean)


186
187
188
189
190
191
192
193
194
195
# File 'lib/mb/gears/dynamic_service.rb', line 186

def set_node_attributes(job, nodes, attribute_keys, state)
  nodes.concurrent_map do |node|
    node.reload
    attribute_keys.each do |attribute_key|
      job.set_status("Setting node attribute '#{attribute_key}' to #{state.nil? ? 'nil' : state} on #{node.name}")
      node.set_chef_attribute(attribute_key, state)
    end
    node.save
  end
end

#state_change(job, plugin, environment, state, run_chef = true, options = {}) ⇒ MB::JobTicket

Executes a bulk chef run on a group of nodes using the service recipe. Default behavior is to set a node attribute on each individual node serially and then execute the chef run.

Parameters:

  • job (MB::Job)
  • plugin (MB::Plugin)

    the plugin currently in use

  • environment (String)

    the environment to execute on

  • state (String)

    the state to change the service to

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • node_filter (String)

    filter to apply to the list of nodes

Returns:



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/mb/gears/dynamic_service.rb', line 74

def state_change(job, plugin, environment, state, run_chef = true, options = {})
  log.warn {
    "Component's service state is being changed to #{state}, which is not one of #{COMMON_STATES}"
  } unless COMMON_STATES.include?(state)

  chef_synchronize(chef_environment: environment, force: options[:force]) do
    component_object = plugin.component(component)
    service_object = component_object.get_service(name)
    group = component_object.group(service_object.service_group)
    nodes = group.nodes(environment)
    nodes = MB::NodeFilter.filter(options[:node_filter], nodes) if options[:node_filter]

    job.report_running("preparing to change the #{name} service to #{state}")

    if options[:cluster_override]
      set_environment_attribute(job, environment, service_object.service_attribute, state)
    else
      unset_environment_attribute(job, environment, service_object.service_attribute)
      set_node_attributes(job, nodes, service_object.service_attribute, state)
    end
    if run_chef
      node_querier.bulk_chef_run(job, nodes, service_object.service_recipe)
    end
  end
end

#unset_environment_attribute(job, environment, attribute_keys) ⇒ Boolean

Deletes an override attribute from the environment

Parameters:

  • job (MB::Job)
  • environment (String)
  • attribute_keys (Array<String>)

Returns:

  • (Boolean)


163
164
165
166
167
168
169
170
171
# File 'lib/mb/gears/dynamic_service.rb', line 163

def unset_environment_attribute(job, environment, attribute_keys)
  chef_environment = get_chef_environment(environment)

  attribute_keys.each do |attribute_key|
    job.set_status("Unsetting environment attribute '#{attribute_key}' on #{environment}")
    chef_environment.delete_override_attribute(attribute_key)
  end
  chef_environment.save
end

#unset_node_attributes(job, nodes, attribute_keys) ⇒ Boolean

Removes a default node attribute on the provided array of nodes.

Parameters:

  • job (MB::Job)

    the job to track status

  • nodes (Array<Ridley::NodeObject>)

    the nodes being operated on

  • attribute_keys (Array<String>)

    an array of dotted paths to attribute keys

Returns:

  • (Boolean)


208
209
210
211
212
213
214
215
216
217
# File 'lib/mb/gears/dynamic_service.rb', line 208

def unset_node_attributes(job, nodes, attribute_keys)
  nodes.concurrent_map do |node|
    node.reload
    attribute_keys.each do |attribute_key|
      job.set_status("Unsetting node attribute '#{attribute_key}' to respect default and environment attributes.")
      node.unset_chef_attribute(attribute_key)
    end
    node.save
  end
end