Class: OpenC3::OperatorProcess

Inherits:
Object
  • Object
show all
Defined in:
lib/openc3/operators/operator.rb

Direct Known Subclasses

ProcessManagerProcess

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(process_definition, work_dir: '/openc3/lib/openc3/microservices', temp_dir: nil, env: {}, scope:, container: nil, config: nil) ⇒ OperatorProcess

container is not used, it’s just here for Enterprise



93
94
95
96
97
98
99
100
101
102
103
# File 'lib/openc3/operators/operator.rb', line 93

def initialize(process_definition, work_dir: '/openc3/lib/openc3/microservices', temp_dir: nil, env: {}, scope:, container: nil, config: nil)
  @process = nil
  @process_definition = process_definition
  @work_dir = work_dir
  @temp_dir = temp_dir
  @new_temp_dir = temp_dir
  @env = env
  @scope = scope
  # @config only used in start to help print a better Logger message
  @config = config
end

Instance Attribute Details

#envObject

Returns the value of attribute env.



82
83
84
# File 'lib/openc3/operators/operator.rb', line 82

def env
  @env
end

#nameObject (readonly)

Returns the value of attribute name.



86
87
88
# File 'lib/openc3/operators/operator.rb', line 86

def name
  @name
end

#new_temp_dirObject

Returns the value of attribute new_temp_dir.



83
84
85
# File 'lib/openc3/operators/operator.rb', line 83

def new_temp_dir
  @new_temp_dir
end

#process_definitionObject

Returns the value of attribute process_definition.



80
81
82
# File 'lib/openc3/operators/operator.rb', line 80

def process_definition
  @process_definition
end

#scopeObject (readonly)

Returns the value of attribute scope.



85
86
87
# File 'lib/openc3/operators/operator.rb', line 85

def scope
  @scope
end

#temp_dirObject (readonly)

Returns the value of attribute temp_dir.



84
85
86
# File 'lib/openc3/operators/operator.rb', line 84

def temp_dir
  @temp_dir
end

#work_dirObject

Returns the value of attribute work_dir.



81
82
83
# File 'lib/openc3/operators/operator.rb', line 81

def work_dir
  @work_dir
end

Class Method Details

.setupObject



88
89
90
# File 'lib/openc3/operators/operator.rb', line 88

def self.setup
  # Perform any setup steps necessary
end

Instance Method Details

#alive?Boolean

Returns:

  • (Boolean)


146
147
148
149
150
151
152
# File 'lib/openc3/operators/operator.rb', line 146

def alive?
  if @process
    @process.alive?
  else
    false
  end
end

#cmd_lineObject



105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/openc3/operators/operator.rb', line 105

def cmd_line
  # In ProcessManager processes, the process_definition is the actual thing run
  # e.g. OpenC3::ProcessManager.instance.spawn(["ruby", "/openc3/bin/openc3cli", "load", ...])
  # However, if the MicroserviceOperator is spawning the processes it sets
  # process_definition = ["ruby", "plugin_microservice.rb"]
  # which then calls exec(*@config["cmd"]) to actually run
  # So check if the @config['cmd'] is defined to give the user more info in the log
  cmd_line_text = @process_definition.join(' ')
  if @config && @config['cmd']
    cmd_line_text = @config['cmd'].join(' ')
  end
  return cmd_line_text
end

#exit_codeObject



154
155
156
157
158
159
160
# File 'lib/openc3/operators/operator.rb', line 154

def exit_code
  if @process
    @process.exit_code
  else
    nil
  end
end

#extract_outputObject

This is method is used in here and in ProcessManager



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/openc3/operators/operator.rb', line 212

def extract_output
  output = ''
  if @process
    stdout = @process.io.stdout.finalize
    stderr = @process.io.stderr.finalize

    # Always include the Stdout header for consistency and to show the option
    output << "Stdout:\n"
    output << stdout

    # Always include the nStderr header for consistency and to show the option
    output << "\nStderr:\n"
    output << stderr
  end
  output
end

#hard_stopObject



173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/openc3/operators/operator.rb', line 173

def hard_stop
  if @process and !@process.exited?
    # Redis may be down at this point so just catch any Logger errors
    begin
      Logger.info("Hard shutting down process: #{cmd_line()}", scope: @scope)
    rescue Exception
    end
    @process.stop
  end
  FileUtils.remove_entry(@temp_dir) if @temp_dir and File.exist?(@temp_dir)
  @process = nil
end

#output_incrementObject



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/openc3/operators/operator.rb', line 194

def output_increment
  if @process
    stdout = @process.io.stdout.extract
    if stdout.length > 0
      message = "STDOUT #{stdout.length} bytes from #{cmd_line()}:"
      STDOUT.puts Logger.build_log_data(Logger::INFO_LEVEL, message, user: nil, type: OpenC3::Logger::LOG, url: nil).as_json(:allow_nan => true).to_json(:allow_nan => true)
      STDOUT.puts stdout
    end
    stderr = @process.io.stderr.extract
    if stderr.length > 0
      message = "STDERR #{stderr.length} bytes from #{cmd_line()}:"
      STDERR.puts Logger.build_log_data(Logger::ERROR_LEVEL, message, user: nil, type: OpenC3::Logger::LOG, url: nil).as_json(:allow_nan => true).to_json(:allow_nan => true)
      STDERR.puts stderr
    end
  end
end

#soft_stopObject



162
163
164
165
166
167
168
169
170
171
# File 'lib/openc3/operators/operator.rb', line 162

def soft_stop
  Thread.new do
    Logger.info("Soft shutting down process: #{cmd_line()}", scope: @scope)
    begin
      Process.kill("SIGINT", @process.pid) if @process # Signal the process to stop
    rescue Errno::ESRCH
      # Process already gone
    end
  end
end

#startObject



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/openc3/operators/operator.rb', line 119

def start
  @temp_dir = @new_temp_dir
  @new_temp_dir = nil

  Logger.info("Starting: #{cmd_line()}", scope: @scope)

  @process = ChildProcess.build(*@process_definition)
  # This lets the ChildProcess use the parent IO ... but it breaks unit tests
  # @process.io.inherit!
  @process.cwd = @work_dir
  # Spawned process should not be controlled by same Bundler constraints as spawning process
  ENV.each do |key, _value|
    if key =~ /^BUNDLER/
      @process.environment[key] = nil
    end
  end
  @env['RUBYOPT'] = nil # Removes loading bundler setup
  @env.each do |key, value|
    @process.environment[key] = value
  end
  @process.environment['OPENC3_SCOPE'] = @scope
  @process.io.stdout = OperatorProcessIO.new('microservice-stdout')
  @process.io.stderr = OperatorProcessIO.new('microservice-stderr')
  @process.start
  @name = "#{Socket.gethostname}__#{@process.pid}"
end

#stderrObject



190
191
192
# File 'lib/openc3/operators/operator.rb', line 190

def stderr
  @process.io.stderr
end

#stdoutObject



186
187
188
# File 'lib/openc3/operators/operator.rb', line 186

def stdout
  @process.io.stdout
end