Class: Cosmos::PluginModel

Inherits:
Model show all
Defined in:
lib/cosmos/models/plugin_model.rb

Overview

Represents a COSMOS plugin that can consist of targets, interfaces, routers microservices and tools. The PluginModel installs all these pieces as well as destroys them all when the plugin is removed.

Constant Summary collapse

PRIMARY_KEY =
'cosmos_plugins'

Instance Attribute Summary collapse

Attributes inherited from Model

#name, #plugin, #scope, #updated_at

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Model

#as_config, #deploy, #destroy, filter, find_all_by_plugin, from_json, get_all_models, get_model, handle_config, set, #update

Constructor Details

#initialize(name:, variables: {}, updated_at: nil, scope:) ⇒ PluginModel

Returns a new instance of PluginModel.



192
193
194
195
196
197
198
199
200
# File 'lib/cosmos/models/plugin_model.rb', line 192

def initialize(
  name:,
  variables: {},
  updated_at: nil,
  scope:
)
  super("#{scope}__#{PRIMARY_KEY}", name: name, updated_at: updated_at, scope: scope)
  @variables = variables
end

Instance Attribute Details

#variablesObject

Returns the value of attribute variables.



43
44
45
# File 'lib/cosmos/models/plugin_model.rb', line 43

def variables
  @variables
end

Class Method Details

.all(scope: nil) ⇒ Object



55
56
57
# File 'lib/cosmos/models/plugin_model.rb', line 55

def self.all(scope: nil)
  super("#{scope}__#{PRIMARY_KEY}")
end

.get(name:, scope: nil) ⇒ Object

NOTE: The following three class methods are used by the ModelController and are reimplemented to enable various Model class methods to work



47
48
49
# File 'lib/cosmos/models/plugin_model.rb', line 47

def self.get(name:, scope: nil)
  super("#{scope}__#{PRIMARY_KEY}", name: name)
end

.install_phase1(gem_file_path, existing_variables = nil, scope:) ⇒ Object

Called by the PluginsController to parse the plugin variables Doesn’t actaully create the plugin during the phase



61
62
63
64
65
66
67
68
69
70
71
72
73
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
99
100
101
102
103
# File 'lib/cosmos/models/plugin_model.rb', line 61

def self.install_phase1(gem_file_path, existing_variables = nil, scope:)
  gem_filename = File.basename(gem_file_path)

  # Load gem to internal gem server
  Cosmos::GemModel.put(gem_file_path, gem_install: false, scope: scope)

  # Extract gem and process plugin.txt to determine what VARIABLEs need to be filled in
  pkg = Gem::Package.new(gem_file_path)

  temp_dir = Dir.mktmpdir
  begin
    pkg.extract_files(temp_dir)
    plugin_txt_path = File.join(temp_dir, 'plugin.txt')
    if File.exist?(plugin_txt_path)
      parser = Cosmos::ConfigParser.new("http://cosmosc2.com")

      # Phase 1 Gather Variables
      variables = {}
      parser.parse_file(plugin_txt_path,
                        false,
                        true,
                        false) do |keyword, params|
        case keyword
        when 'VARIABLE'
          usage = "#{keyword} <Variable Name> <Default Value>"
          parser.verify_num_parameters(2, nil, usage)
          variable_name = params[0]
          value = params[1..-1].join(" ")
          variables[variable_name] = value
          if existing_variables && existing_variables.key?(variable_name)
            variables[variable_name] = existing_variables[variable_name]
          end
          # Ignore everything else during phase 1
        end
      end

      model = PluginModel.new(name: gem_filename, variables: variables, scope: scope)
      return model.as_json
    end
  ensure
    FileUtils.remove_entry(temp_dir) if temp_dir and File.exist?(temp_dir)
  end
end

.install_phase2(name, variables, scope:) ⇒ Object

Called by the PluginsController to create the plugin Because this uses ERB it must be run in a seperate process from the API to prevent corruption and single require problems in the current proces



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
# File 'lib/cosmos/models/plugin_model.rb', line 108

def self.install_phase2(name, variables, scope:)
  rubys3_client = Aws::S3::Client.new

  # Ensure config bucket exists
  begin
    rubys3_client.head_bucket(bucket: 'config')
  rescue Aws::S3::Errors::NotFound
    rubys3_client.create_bucket(bucket: 'config')
  end

  # Register plugin to aid in uninstall if install fails
  plugin_model = PluginModel.new(name: name, variables: variables, scope: scope)
  plugin_model.create

  temp_dir = Dir.mktmpdir
  begin
    # Get the gem from local gem server
    gem_file_path = Cosmos::GemModel.get(temp_dir, name)

    # Actually install the gem now (slow)
    Cosmos::GemModel.install(gem_file_path)

    # Extract gem contents
    gem_path = File.join(temp_dir, "gem")
    FileUtils.mkdir_p(gem_path)
    pkg = Gem::Package.new(gem_file_path)
    needs_dependencies = pkg.spec.runtime_dependencies.length > 0
    pkg.extract_files(gem_path)

    # Temporarily add all lib folders from the gem to the end of the load path
    load_dirs = []
    begin
      Dir.glob("#{gem_path}/**/*").each do |load_dir|
        if File.directory?(load_dir) and File.basename(load_dir) == 'lib'
          load_dirs << load_dir
          $LOAD_PATH << load_dir
        end
      end

      # Process plugin.txt file
      plugin_txt_path = File.join(gem_path, 'plugin.txt')
      if File.exist?(plugin_txt_path)
        parser = Cosmos::ConfigParser.new("http://cosmosc2.com")

        current_model = nil
        parser.parse_file(plugin_txt_path, false, true, true, variables) do |keyword, params|
          case keyword
          when 'VARIABLE'
            # Ignore during phase 2
          when 'TARGET', 'INTERFACE', 'ROUTER', 'MICROSERVICE', 'TOOL', 'WIDGET'
            if current_model
              current_model.create
              current_model.deploy(gem_path, variables)
              current_model = nil
            end
            current_model = Cosmos.const_get((keyword.capitalize + 'Model').intern).handle_config(parser, keyword, params, plugin: plugin_model.name, needs_dependencies: needs_dependencies, scope: scope)
          else
            if current_model
              current_model.handle_config(parser, keyword, params)
            else
              raise "Invalid keyword #{keyword} in plugin.txt"
            end
          end
        end
        if current_model
          current_model.create
          current_model.deploy(gem_path, variables)
          current_model = nil
        end
      end
    ensure
      load_dirs.each do |load_dir|
        $LOAD_PATH.delete(load_dir)
      end
    end
  rescue => err
    # Install failed - need to cleanup
    plugin_model.destroy
    raise err
  ensure
    FileUtils.remove_entry(temp_dir) if temp_dir and File.exist?(temp_dir)
  end
end

.names(scope: nil) ⇒ Object



51
52
53
# File 'lib/cosmos/models/plugin_model.rb', line 51

def self.names(scope: nil)
  super("#{scope}__#{PRIMARY_KEY}")
end

Instance Method Details

#as_jsonObject



207
208
209
210
211
212
213
# File 'lib/cosmos/models/plugin_model.rb', line 207

def as_json
  {
    'name' => @name,
    'variables' => @variables,
    'updated_at' => @updated_at
  }
end

#create(update: false, force: false) ⇒ Object



202
203
204
205
# File 'lib/cosmos/models/plugin_model.rb', line 202

def create(update: false, force: false)
  @name = @name + "__#{Time.now.utc.strftime("%Y%m%d%H%M%S")}" unless update
  super(update: update, force: force)
end

#undeployObject

Undeploy all models associated with this plugin



216
217
218
219
220
221
222
# File 'lib/cosmos/models/plugin_model.rb', line 216

def undeploy
  [ToolModel, TargetModel, InterfaceModel, RouterModel, MicroserviceModel, WidgetModel].each do |model|
    model.find_all_by_plugin(plugin: @name, scope: @scope).each do |name, model_instance|
      model_instance.destroy
    end
  end
end