Class: OpenC3::MicroserviceOperator

Inherits:
Operator show all
Defined in:
lib/openc3/operators/microservice_operator.rb

Overview

Creates new OperatorProcess objects based on querying the Redis key value store. Any keys under ‘openc3_microservices’ will be created into microservices.

Constant Summary

Constants inherited from Operator

Operator::CYCLE_TIME, Operator::PROCESS_SHUTDOWN_SECONDS

Instance Attribute Summary

Attributes inherited from Operator

#cycle_time, #processes

Instance Method Summary collapse

Methods inherited from Operator

instance, processes, #remove_old, #respawn_changed, #respawn_dead, run, #run, #shutdown, #shutdown_processes, #start_new, #stop

Constructor Details

#initializeMicroserviceOperator

Returns a new instance of MicroserviceOperator.



35
36
37
38
39
40
41
42
43
44
45
# File 'lib/openc3/operators/microservice_operator.rb', line 35

def initialize
  Logger.microservice_name = "MicroserviceOperator"
  super

  @secrets = Secrets.getClient
  @microservices = {}
  @previous_microservices = {}
  @new_microservices = {}
  @changed_microservices = {}
  @removed_microservices = {}
end

Instance Method Details

#convert_microservice_to_process_definition(microservice_name, microservice_config) ⇒ Object



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
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/openc3/operators/microservice_operator.rb', line 47

def convert_microservice_to_process_definition(microservice_name, microservice_config)
  process_definition = ["ruby", "plugin_microservice.rb"]
  work_dir = "/openc3/lib/openc3/microservices"
  env = microservice_config["env"].dup
  if microservice_config["needs_dependencies"]
    env['GEM_HOME'] = '/gems'
    env['PYTHONUSERBASE'] = '/gems/python_packages'
  else
    env['GEM_HOME'] = nil
    env['PYTHONUSERBASE'] = nil
  end
  env['OPENC3_MICROSERVICE_NAME'] = microservice_name
  container = microservice_config["container"]
  scope = microservice_name.split("__")[0]

  # Setup secrets for microservice
  secrets = microservice_config["secrets"]
  if secrets
    secrets.each do |type, secret_name, env_name_or_path, secret_store|
      secret_value = @secrets.get(secret_name, secret_store: secret_store, scope: scope)
      if secret_value
        case type
        when 'ENV'
          env[env_name_or_path] = secret_value
        when 'FILE'
          FileUtils.mkdir_p(File.dirname(env_name_or_path))
          File.open(env_name_or_path, 'wb') do |file|
            file.write(secret_value)
          end
        end
      else
        Logger.error("Microservice #{microservice_name} references unknown secret: #{secret_name}")
      end
    end
  end

  return process_definition, work_dir, env, scope, container
end

#updateObject



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/openc3/operators/microservice_operator.rb', line 86

def update
  @previous_microservices = @microservices.dup
  # Get all the microservice configuration
  @microservices = MicroserviceModel.all

  # Detect new and changed microservices
  @new_microservices = {}
  @changed_microservices = {}
  @removed_microservices = {}
  @microservices.each do |microservice_name, microservice_config|
    parent = microservice_config['parent']
    if @previous_microservices[microservice_name]
      previous_parent = @previous_microservices[microservice_name]['parent']
      if @previous_microservices[microservice_name] != microservice_config
        # CHANGED
        scope = microservice_name.split("__")[0]
        Logger.info("Changed microservice detected: #{microservice_name}\nWas: #{@previous_microservices[microservice_name]}\nIs: #{microservice_config}", scope: scope)
        if parent or previous_parent
          if parent == previous_parent
            # Same Parent - Respawn parent
            @changed_microservices[parent] = @microservices[parent] if @microservices[parent] and @previous_microservices[parent]
          elsif parent and previous_parent
            # Parent changed - Respawn both parents
            @changed_microservices[parent] = @microservices[parent] if @microservices[parent] and @previous_microservices[parent]
            @changed_microservices[previous_parent] = @microservices[previous_parent] if @microservices[previous_parent] and @previous_microservices[previous_parent]
          elsif parent
            # Moved under a parent - Respawn parent and kill standalone
            @changed_microservices[parent] = @microservices[parent] if @microservices[parent] and @previous_microservices[parent]
            @removed_microservices[microservice_name] = microservice_config
          else # previous_parent
            # Moved to standalone - Respawn previous parent and make new
            @changed_microservices[previous_parent] = @microservices[previous_parent] if @microservices[previous_parent] and @previous_microservices[previous_parent]
            @new_microservices[microservice_name] = microservice_config
          end
        else
          # Respawn regular microservice
          @changed_microservices[microservice_name] = microservice_config
        end
      end
    else
      # NEW
      scope = microservice_name.split("__")[0]
      Logger.info("New microservice detected: #{microservice_name}", scope: scope)
      if parent
        # Respawn parent
        @changed_microservices[parent] = @microservices[parent] if @microservices[parent] and @previous_microservices[parent]
      else
        # New process be spawned
        @new_microservices[microservice_name] = microservice_config
      end
    end
  end

  # Detect removed microservices
  @previous_microservices.each do |microservice_name, microservice_config|
    previous_parent = microservice_config['parent']
    unless @microservices[microservice_name]
      # REMOVED
      scope = microservice_name.split("__")[0]
      Logger.info("Removed microservice detected: #{microservice_name}", scope: scope)
      if previous_parent
        # Respawn previous parent
        @changed_microservices[previous_parent] = @microservices[previous_parent] if @microservices[previous_parent] and @previous_microservices[previous_parent]
      else
        # Regular process to be removed
        @removed_microservices[microservice_name] = microservice_config
      end
    end
  end

  # Convert to processes
  @mutex.synchronize do
    @new_microservices.each do |microservice_name, microservice_config|
      cmd_array, work_dir, env, scope, container = convert_microservice_to_process_definition(microservice_name, microservice_config)
      if cmd_array
        process = OperatorProcess.new(cmd_array, work_dir: work_dir, env: env, scope: scope, container: container, config: microservice_config)
        @new_processes[microservice_name] = process
        @processes[microservice_name] = process
      end
    end

    @changed_microservices.each do |microservice_name, microservice_config|
      cmd_array, work_dir, env, scope, container = convert_microservice_to_process_definition(microservice_name, microservice_config)
      if cmd_array
        process = @processes[microservice_name]
        if process
          process.process_definition = cmd_array
          process.work_dir = work_dir
          process.new_temp_dir = nil
          process.env = env
          @changed_processes[microservice_name] = process
        else
          # This shouldn't be possible, but still needs to be handled
          Logger.error("Changed microservice #{microservice_name} does not exist. Creating new...", scope: scope)
          process = OperatorProcess.new(cmd_array, work_dir: work_dir, env: env, scope: scope, container: container, config: microservice_config)
          @new_processes[microservice_name] = process
          @processes[microservice_name] = process
        end
      end
    end

    @removed_microservices.each do |microservice_name, microservice_config|
      process = @processes[microservice_name]
      @processes.delete(microservice_name)
      @removed_processes[microservice_name] = process
    end
  end
end