Class: OpsWalrus::RemoteImportInvocationContext

Inherits:
ImportInvocationContext show all
Defined in:
lib/opswalrus/invocation.rb

Instance Method Summary collapse

Methods inherited from ImportInvocationContext

#_bang_method?, #_non_bang_method

Constructor Details

#initialize(runtime_env, host_proxy, namespace_or_ops_file, is_invocation_a_call_to_package_in_bundle_dir = false, ops_prompt_for_sudo_password: nil) ⇒ RemoteImportInvocationContext

Returns a new instance of RemoteImportInvocationContext.

[View source]

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/opswalrus/invocation.rb', line 38

def initialize(runtime_env, host_proxy, namespace_or_ops_file, is_invocation_a_call_to_package_in_bundle_dir = false, ops_prompt_for_sudo_password: nil)
  @runtime_env = runtime_env
  @host_proxy = host_proxy
  @initial_namespace_or_ops_file = @namespace_or_ops_file = namespace_or_ops_file
  @is_invocation_a_call_to_package_in_bundle_dir = is_invocation_a_call_to_package_in_bundle_dir

  initial_method_name = case @namespace_or_ops_file
  when Namespace
    @namespace_or_ops_file.dirname.basename
  when OpsFile
    @namespace_or_ops_file.basename
  end
  @method_chain = [initial_method_name]
  @ops_prompt_for_sudo_password = ops_prompt_for_sudo_password
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, **kwargs, &block) ⇒ Object

[View source]

54
55
56
# File 'lib/opswalrus/invocation.rb', line 54

def method_missing(name, *args, **kwargs, &block)
  _resolve_method_and_invoke(name, *args, **kwargs)
end

Instance Method Details

#_invoke(*args, **kwargs) ⇒ Object

[View source]

87
88
89
90
91
92
93
94
# File 'lib/opswalrus/invocation.rb', line 87

def _invoke(*args, **kwargs)
  case @namespace_or_ops_file
  when Namespace
    self
  when OpsFile
    _invoke_remote(*args, **kwargs)
  end
end

#_invoke_if_namespace_has_ops_file_of_same_name(*args, **kwargs, &block) ⇒ Object

if this namespace contains an OpsFile of the same name as the namespace, e.g. pkg/install/install.ops, then this method invokes the OpsFile of that same name and returns the result; otherwise we return this namespace object

[View source]

77
78
79
80
81
82
83
84
85
# File 'lib/opswalrus/invocation.rb', line 77

def _invoke_if_namespace_has_ops_file_of_same_name(*args, **kwargs, &block)
  method_name = @namespace_or_ops_file.dirname.basename
  resolved_symbol = @namespace_or_ops_file.resolve_symbol(method_name)
  if resolved_symbol.is_a? OpsFile
    _resolve_method_and_invoke(method_name)
  else
    self
  end
end

#_invoke_remote(*args, **kwargs) ⇒ Object

[View source]

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
# File 'lib/opswalrus/invocation.rb', line 96

def _invoke_remote(*args, **kwargs)
  # when there are args or kwargs, then the method invocation represents an attempt to run an OpsFile on a remote host,
  # so we want to build up a command and send it to the remote host via HostDSL#run_ops
  @method_chain.unshift(Bundler::BUNDLE_DIR) if @is_invocation_a_call_to_package_in_bundle_dir

  remote_run_command_args = @method_chain.join(" ")

  unless args.empty?
    remote_run_command_args << " "
    remote_run_command_args << args.join(" ")
  end

  # unless kwargs.empty?
  #   remote_run_command_args << " "
  #   remote_run_command_args << kwargs.map do |k, v|
  #     case v
  #     when Array
  #       v.map {|v_element| "#{k}:#{v_element}" }
  #     else
  #       "#{k}:#{v}"
  #     end
  #   end.join(" ")
  # end
  begin
    json = JSON.dump(kwargs) unless kwargs.empty?
    if json
      # write the kwargs to a tempfile
      json_kwargs_tempfile = Tempfile.create('ops_invoke_kwargs')
      json_kwargs_tempfile.write(json)
      json_kwargs_tempfile.close()   # we want to close the file without unlinking so that we can copy it to the remote host before deleting it

      # upload the kwargs file to the remote host
      json_kwargs_tempfile_path = json_kwargs_tempfile.path.to_pathname
      remote_json_kwargs_tempfile_basename = json_kwargs_tempfile_path.basename
      @host_proxy.upload(json_kwargs_tempfile_path, remote_json_kwargs_tempfile_basename)
    end

    # invoke the ops command on the remote host to run the specified ops script on the remote host
    ops_command_options = "--script"
    ops_command_options << " --pass" if @ops_prompt_for_sudo_password
    ops_command_options << " --params #{remote_json_kwargs_tempfile_basename}" if remote_json_kwargs_tempfile_basename

    output, stderr, exit_status = if ops_command_options.empty?
      @host_proxy.run_ops(:run, remote_run_command_args, ops_prompt_for_sudo_password: @ops_prompt_for_sudo_password)
    else
      @host_proxy.run_ops(:run, ops_command_options, remote_run_command_args, ops_prompt_for_sudo_password: @ops_prompt_for_sudo_password)
    end

    App.instance.debug("Remote invocation failed:\n  cmd: ops run #{ops_command_options.to_s} #{remote_run_command_args.to_s}\n  stdout: #{output}\n") unless exit_status == 0

    RemoteInvocation.parse_remote_script_invocation_result(output)
  ensure
    if json_kwargs_tempfile
      json_kwargs_tempfile.close rescue nil
      File.unlink(json_kwargs_tempfile) rescue nil
    end
    # todo: make sure this cleanup is present
    if remote_json_kwargs_tempfile_basename
      # this may fail if the remote host reboots and we try to subsequently connect and delete the file
      @host_proxy.execute(:rm, "-f", remote_json_kwargs_tempfile_basename) rescue nil
    end
  end
end

#_resolve_method_and_invoke(name, *args, **kwargs) ⇒ Object

[View source]

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/opswalrus/invocation.rb', line 58

def _resolve_method_and_invoke(name, *args, **kwargs)
  if _bang_method?(name)      # foo! is an attempt to invoke the module's default entrypoint
    method_name = _non_bang_method(name)

    @method_chain << method_name

    @namespace_or_ops_file = @namespace_or_ops_file.resolve_symbol(method_name)
    _invoke_if_namespace_has_ops_file_of_same_name(*args, **kwargs)
  else
    @method_chain << name.to_s

    @namespace_or_ops_file = @namespace_or_ops_file.resolve_symbol(name)
    _invoke(*args, **kwargs)
  end
end