Class: Sonar::Connector::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/sonar_connector/connectors/base.rb

Direct Known Subclasses

DummyConnector, SeppukuConnector

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(connector_config, base_config) ⇒ Base

Returns a new instance of Base.

Raises:



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/sonar_connector/connectors/base.rb', line 41

def initialize(connector_config, base_config)
  @base_config = base_config
  @raw_config = connector_config
  
  @name = connector_config["name"]
  @connector = self
  
  # Create STDOUT logger and inherit the logger settings from the base controller config
  @log_file = File.join base_config.log_dir, "connector_#{@name}.log"
  @log = Sonar::Connector::Utils.stdout_logger base_config
  
  # every connector instance must set the repeat delay
  raise InvalidConfig.new("Connector '#{@name}': repeat_delay is missing or blank") if connector_config["repeat_delay"].blank?
  @repeat_delay = connector_config["repeat_delay"].to_i
  raise InvalidConfig.new("Connector '#{@name}': repeat_delay must be >= 1 second") if @repeat_delay < 1
  
  @connector_dir = File.join(base_config.connectors_dir, @name)
  FileUtils.mkdir_p(@connector_dir)
  @state_file = File.join(@connector_dir, "state.yml")
  
  # empty state hash which will get written to by parse, and then potentially over-written by load_state
  @state = {}

  @connector_filestore = Sonar::Connector::FileStore.new(@connector_dir, 
                                                         "#{@name}_filestore", 
                                                         [:working, :error, :complete, :actions],
                                                         :logger=>@log)

  parse connector_config
  load_state
  
  @run = true
end

Instance Attribute Details

#connectorObject (readonly)

this connector



9
10
11
# File 'lib/sonar_connector/connectors/base.rb', line 9

def connector
  @connector
end

#connector_dirObject (readonly)

each connector instance has a working dir for its state and files



15
16
17
# File 'lib/sonar_connector/connectors/base.rb', line 15

def connector_dir
  @connector_dir
end

#connector_filestoreObject (readonly)

filestore for whole connector



33
34
35
# File 'lib/sonar_connector/connectors/base.rb', line 33

def connector_filestore
  @connector_filestore
end

#filestoreObject (readonly)

filestore for an action run



36
37
38
# File 'lib/sonar_connector/connectors/base.rb', line 36

def filestore
  @filestore
end

#logObject (readonly)

logger instance



18
19
20
# File 'lib/sonar_connector/connectors/base.rb', line 18

def log
  @log
end

#nameObject (readonly)

every connector has a unique name



6
7
8
# File 'lib/sonar_connector/connectors/base.rb', line 6

def name
  @name
end

#queueObject (readonly)

central command queue for sending messages back to the controller



27
28
29
# File 'lib/sonar_connector/connectors/base.rb', line 27

def queue
  @queue
end

#raw_configObject (readonly)

Connector-specific config hash



12
13
14
# File 'lib/sonar_connector/connectors/base.rb', line 12

def raw_config
  @raw_config
end

#repeat_delayObject (readonly)

repeat delay which is waited out on each cycle of the run loop



24
25
26
# File 'lib/sonar_connector/connectors/base.rb', line 24

def repeat_delay
  @repeat_delay
end

#runObject (readonly)

run loop flag



30
31
32
# File 'lib/sonar_connector/connectors/base.rb', line 30

def run
  @run
end

#source_connectorsObject

Array of associated connectors that provide source data via the file system



39
40
41
# File 'lib/sonar_connector/connectors/base.rb', line 39

def source_connectors
  @source_connectors
end

#stateObject (readonly)

state hash that is serialized and persisted to disk every cycle of the run loop



21
22
23
# File 'lib/sonar_connector/connectors/base.rb', line 21

def state
  @state
end

Instance Method Details

#cleanupObject

Cleanup routine after connector shutdown



116
117
# File 'lib/sonar_connector/connectors/base.rb', line 116

def cleanup
end

#load_stateObject

Load the state hash from YAML file



91
92
93
# File 'lib/sonar_connector/connectors/base.rb', line 91

def load_state
  @state.merge! read_state
end

#parse(config) ⇒ Object

Connector subclasses can overload the parse method.



171
172
173
# File 'lib/sonar_connector/connectors/base.rb', line 171

def parse(config)
  log.warn "Method #parse called on connector base class. Connector #{name} should define #parse method."
end

#prepare(queue) ⇒ Object



75
76
77
78
79
80
81
# File 'lib/sonar_connector/connectors/base.rb', line 75

def prepare(queue)
  @queue = queue
  switch_to_log_file
  
  cleanup_old_action_filestores # in case we were interrupted mid-action
  cleanup # before we begin
end

#read_stateObject

Read state file



96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/sonar_connector/connectors/base.rb', line 96

def read_state
  s = {}
  if File.exist?(state_file)
    ds = YAML.load_file state_file
    raise "State file did not contain a serialised hash." unless ds.is_a?(Hash)
    s = ds  # only return the parsed value if it is actually a hash
  end
rescue Exception => e
  log.error "Error loading #{state_file} so it was ignored. Original error: #{e.message}\n" + e.backtrace.join("\n")
ensure 
  return s
end

#run_loopObject



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/sonar_connector/connectors/base.rb', line 133

def run_loop
  while run
    begin
      run_once
      sleep_for repeat_delay
    rescue ThreadTerminator
      break
    rescue Exception => e
      log.error ["Connector '#{name} raised an unhandled exception:",
                 e.class.to_s,
                 e.message,
                 *e.backtrace].join("\n")
      log.info "Connector blew up with an exception - waiting 5 seconds before retrying."
      queue << Sonar::Connector::UpdateStatusCommand.new(connector, 'last_action', Sonar::Connector::ACTION_FAILED)
      sleep_for 5
    end
  end
end

#run_onceObject



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/sonar_connector/connectors/base.rb', line 152

def run_once
  log.info "beginning action"

  with_action_filestore do
    action

    save_state
    log.info "finished action,saved state"
    log.info "working: #{filestore.count(:working)}, error: #{filestore.count(:error)}, complete: #{filestore.count(:complete)}"
    
    queue << Sonar::Connector::UpdateStatusCommand.new(connector, 'last_action', Sonar::Connector::ACTION_OK)
    queue << Sonar::Connector::UpdateDiskUsageCommand.new(connector)
    queue << Sonar::Connector::UpdateStatusCommand.new(connector, 'working_count', filestore.count(:working))
    queue << Sonar::Connector::UpdateStatusCommand.new(connector, 'error_count', filestore.count(:error))
    queue << Sonar::Connector::UpdateStatusCommand.new(connector, 'complete_count', filestore.count(:complete))
  end
end

#save_stateObject

Save the state hash to a YAML file



110
111
112
113
# File 'lib/sonar_connector/connectors/base.rb', line 110

def save_state
  make_dir
  File.open(state_file, "w"){|f| f << state.to_yaml }
end

#startObject

the main run loop that every connector executes indefinitely until Thread.raise is called on this instance.



121
122
123
124
125
126
127
128
129
130
131
# File 'lib/sonar_connector/connectors/base.rb', line 121

def start
  begin
    run_loop

    @run = false
    cleanup
    true
  rescue Exception=>e
    log.error([e.class.to_s, e.message, *e.backtrace].join("\n"))
  end
end

#switch_to_log_fileObject

Logging defaults to use STDOUT. After initialization we need to switch the logger to use an output file.



85
86
87
88
# File 'lib/sonar_connector/connectors/base.rb', line 85

def switch_to_log_file
  @log = Sonar::Connector::Utils.disk_logger(log_file, base_config)
  @connector_filestore.logger = @log if @connector_filestore
end

#to_sObject Also known as: inspect



175
176
177
# File 'lib/sonar_connector/connectors/base.rb', line 175

def to_s
  "#{self.class} '#{name}'"
end