Class: OpenShift::ApplicationContainer

Inherits:
Model
  • Object
show all
Includes:
Utils::ShellExec
Defined in:
lib/openshift-origin-node/model/application_container.rb

Overview

Application Container

Defined Under Namespace

Modules: State

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Utils::ShellExec

run_as, shellCmd, #shellCmd

Constructor Details

#initialize(application_uuid, container_uuid, user_uid = nil, app_name = nil, container_name = nil, namespace = nil, quota_blocks = nil, quota_files = nil, logger = nil) ⇒ ApplicationContainer

Returns a new instance of ApplicationContainer.



40
41
42
43
44
45
46
47
48
49
50
# File 'lib/openshift-origin-node/model/application_container.rb', line 40

def initialize(application_uuid, container_uuid, user_uid = nil,
    app_name = nil, container_name = nil, namespace = nil, quota_blocks = nil, quota_files = nil, logger = nil)
  @logger = logger ||= Logger.new(STDOUT)

  @config = OpenShift::Config.new

  @uuid = container_uuid
  @application_uuid = application_uuid
  @user = UnixUser.new(application_uuid, container_uuid, user_uid,
    app_name, container_name, namespace, quota_blocks, quota_files)
end

Instance Attribute Details

#application_uuidObject (readonly)

Returns the value of attribute application_uuid.



27
28
29
# File 'lib/openshift-origin-node/model/application_container.rb', line 27

def application_uuid
  @application_uuid
end

#userObject (readonly)

Returns the value of attribute user.



27
28
29
# File 'lib/openshift-origin-node/model/application_container.rb', line 27

def user
  @user
end

#uuidObject (readonly)

Returns the value of attribute uuid.



27
28
29
# File 'lib/openshift-origin-node/model/application_container.rb', line 27

def uuid
  @uuid
end

Instance Method Details

#createObject

Create gear - model/unix_user.rb



57
58
59
60
61
# File 'lib/openshift-origin-node/model/application_container.rb', line 57

def create
  notify_observers(:before_container_create)
  @user.create
  notify_observers(:after_container_create)
end

#destroy(skip_hooks = false) ⇒ Object

Destroy gear - model/unix_user.rb



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
104
105
106
107
108
109
110
111
112
113
# File 'lib/openshift-origin-node/model/application_container.rb', line 64

def destroy(skip_hooks=false)
  notify_observers(:before_container_destroy)

  hook_timeout=30

  output = ""
  errout = ""
  retcode = 0

  hooks={}
  ["pre", "post"].each do |hooktype|
    if @user.homedir.nil? || ! File.exists?(@user.homedir)
      hooks[hooktype]=[]
    else
      hooks[hooktype] = Dir.entries(@user.homedir).map { |cart|
        [ File.join(@config.get("CARTRIDGE_BASE_PATH"),cart,"info","hooks","#{hooktype}-destroy"),
          File.join(@config.get("CARTRIDGE_BASE_PATH"),"embedded",cart,"info","hooks","#{hooktype}-destroy"),
        ].select { |hook| File.exists? hook }[0]
      }.select { |hook|
        not hook.nil?
      }.map { |hook|
        "#{hook} #{@user.container_name} #{@user.namespace} #{@user.container_uuid}"
      }
    end
  end

  unless skip_hooks
    hooks["pre"].each do | cmd |
      out,err,rc = shellCmd(cmd, "/", true, 0, hook_timeout)
      errout << err if not err.nil?
      output << out if not out.nil?
      retcode = 121 if rc != 0
    end
  end

  @user.destroy

  unless skip_hooks
    hooks["post"].each do | cmd |
      out,err,rc = shellCmd(cmd, "/", true, 0, hook_timeout)
      errout << err if not err.nil?
      output << out if not out.nil?
      retcode = 121 if rc != 0
    end
  end

  notify_observers(:after_container_destroy)

  return output, errout, retcode
end

#force_stopObject

Public: Sets the app state to “stopped” and causes an immediate forced termination of all gear processes.

TODO: exception handling



155
156
157
158
# File 'lib/openshift-origin-node/model/application_container.rb', line 155

def force_stop
  set_app_state(:STOPPED)
  UnixUser.kill_procs(@user.uid)
end

#get_app_stateObject

Public: Fetch application state from gear. Returns app state as string on Success and ‘unknown’ on Failure



117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/openshift-origin-node/model/application_container.rb', line 117

def get_app_state
  env = load_env
  app_state_file=File.join(env[:OPENSHIFT_HOMEDIR], 'app-root', 'runtime', '.state')
  
  if File.exists?(app_state_file)
    app_state = nil
    File.open(app_state_file) { |input| app_state = input.read.chomp }
  else
    app_state = :UNKNOWN
  end
  app_state
end

#load_envObject

Public: Load a gears environment variables into the environment

Examples

load_env
# => {"OPENSHIFT_APP_NAME"=>"myapp"}

Returns env Array



279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'lib/openshift-origin-node/model/application_container.rb', line 279

def load_env
  env = {}
  # Load environment variables into a hash
  
  Dir["#{user.homedir}/.env/*"].each { | f |
    next if File.directory?(f)
    contents = nil
    File.open(f) {|input|
      contents = input.read.chomp
      index = contents.index('=')
      contents = contents[(index + 1)..-1]
      contents = contents[/'(.*)'/, 1] if contents.start_with?("'")
      contents = contents[/"(.*)"/, 1] if contents.start_with?('"')
    }
    env[File.basename(f).intern] =  contents
  }
  env
end

#nameObject



52
53
54
# File 'lib/openshift-origin-node/model/application_container.rb', line 52

def name
  @uuid
end

#set_app_state(new_state) ⇒ Object

Public: Sets the application state.

new_state - The new state to assign. Must be an ApplicationContainer::State.



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/openshift-origin-node/model/application_container.rb', line 133

def set_app_state(new_state)
  new_state_val = nil
  begin
    new_state_val = State.const_get(new_state)
  rescue
    raise ArgumentError, "Invalid state '#{new_state}' specified"
  end

  env = load_env
  app_state_file = File.join(env[:OPENSHIFT_HOMEDIR], 'app-root', 'runtime', '.state')
  
  raise "Couldn't find app state file at #{app_state_file}" unless File.exists?(app_state_file)

  File.open(app_state_file, File::WRONLY|File::TRUNC|File::CREAT, 0o0660) {|file|
    file.write "#{new_state_val}\n"
  }
end

#tidyObject

Public: Cleans up the gear, providing any installed cartridges with the opportinity to perform their own cleanup operations via the tidy hook.

The generic gear-level cleanup flow is:

  • Stop the gear

  • Git cleanup

  • Gear temp dir cleanup

  • Cartridge tidy hook executions

  • Start the gear

Raises an Exception if an internal error occurs, and ignores failed cartridge tidy hook executions.



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/openshift-origin-node/model/application_container.rb', line 174

def tidy
  @logger.debug("Starting tidy on gear #{@uuid}")

  env = load_env
  gear_dir = env[:OPENSHIFT_HOMEDIR]
  app_name = env[:OPENSHIFT_APP_NAME]
  gear_repo_dir = File.join(gear_dir, 'git', "#{app_name}.git")
  gear_tmp_dir = File.join(gear_dir, '.tmp')

  begin
    # Stop the gear. If this fails, consider the tidy a failure.
    out, err, rc = shellCmd("/usr/sbin/oo-admin-ctl-gears stopgear #{@user.uuid}", gear_dir, false, 0)
    @logger.debug("Stopped gear #{@uuid}. Output:\n#{out}")
  rescue OpenShift::Utils::ShellExecutionException => e
    @logger.error(%Q{
      Couldn't stop gear #{@uuid} for tidy: #{e.message}
      --- stdout ---\n#{e.stdout}
      --- stderr ---\n#{e.stderr}
      })
    raise "Tidy failed on gear #{@uuid}; the gear couldn't be stopped successfully"
  end

  # Perform the individual tidy actions. At this point, the gear has been stopped,
  # and so we'll attempt to start the gear no matter what tidy operations fail.
  begin
    # Git pruning
    tidy_action do
      out, err, rc = shellCmd("git prune", gear_repo_dir, false, 0)
      @logger.debug("Pruned git directory at #{gear_repo_dir}. Output:\n#{out}")
    end

    # Git GC
    tidy_action do
      out, err, rc = shellCmd("git gc --aggressive", gear_repo_dir, false, 0)
      @logger.debug("Executed git gc for repo #{gear_repo_dir}. Output:\n#{out}")
    end

    # Temp dir cleanup
    tidy_action do
      FileUtils.rm_rf(Dir.glob(File.join(gear_tmp_dir, "*")))
      @logger.debug("Cleaned gear temp dir at #{gear_tmp_dir}")
    end

    # Execute the tidy hooks in any installed carts, in the context
    # of the gear user. For now, we detect cart installations by iterating
    # over the gear subdirs and using the dir names to construct a path
    # to cart scripts in the base cartridge directory. If such a file exists,
    # it's assumed the cart is installed on the gear.
    cart_tidy_timeout = 30
    Dir.entries(gear_dir).each do |gear_subdir|
      tidy_script = File.join(@config.get("CARTRIDGE_BASE_PATH"), gear_subdir, "info", "hooks", "tidy")
      
      next unless File.exists?(tidy_script)

      begin
        # Execute the hook in the context of the gear user
        @logger.debug("Executing cart tidy script #{tidy_script} in gear #{@uuid} as user #{@user.uid}:#{@user.gid}")
        OpenShift::Utils::ShellExec.run_as(@user.uid, @user.gid, tidy_script, gear_dir, false, 0, cart_tidy_timeout)
      rescue OpenShift::Utils::ShellExecutionException => e
        @logger.warn("Cartridge tidy operation failed on gear #{@uuid} for cart #{gear_dir}: #{e.message} (rc=#{e.rc})")
      end
    end
  rescue Exception => e
    @logger.warn("An unknown exception occured during tidy for gear #{@uuid}: #{e.message}\n#{e.backtrace}")
  ensure
    begin
      # Start the gear, and if that fails raise an exception, as the app is now
      # in a bad state.
      out, err, rc = shellCmd("/usr/sbin/oo-admin-ctl-gears startgear #{@user.uuid}", gear_dir)
      @logger.debug("Started gear #{@uuid}. Output:\n#{out}")
    rescue OpenShift::Utils::ShellExecutionException => e
      @logger.error(%Q{
        Failed to restart gear #{@uuid} following tidy: #{e.message}
        --- stdout ---\n#{e.stdout}
        --- stderr ---\n#{e.stderr}
        })
      raise "Tidy of gear #{@uuid} failed, and the gear was not successfuly restarted"
    end
  end

  @logger.debug("Completed tidy for gear #{@uuid}")      
end

#tidy_actionObject

Executes a block, trapping ShellExecutionExceptions and treating them as warnings. Any other exceptions are unexpected and will bubble out.



259
260
261
262
263
264
265
266
267
268
269
# File 'lib/openshift-origin-node/model/application_container.rb', line 259

def tidy_action
  begin
    yield
  rescue OpenShift::Utils::ShellExecutionException => e
    @logger.warn(%Q{
      Tidy operation failed on gear #{@uuid}: #{e.message}
      --- stdout ---\n#{e.stdout}
      --- stderr ---\n#{e.stderr}
      })
  end
end