opswalrus 1.0.8 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CNAME +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +29 -16
- data/lib/opswalrus/app.rb +21 -7
- data/lib/opswalrus/bootstrap.sh +5 -0
- data/lib/opswalrus/cli.rb +16 -15
- data/lib/opswalrus/host.rb +115 -71
- data/lib/opswalrus/invocation.rb +446 -0
- data/lib/opswalrus/operation_runner.rb +3 -3
- data/lib/opswalrus/ops_file.rb +71 -32
- data/lib/opswalrus/ops_file_script.rb +55 -473
- data/lib/opswalrus/ops_file_script_dsl.rb +297 -0
- data/lib/opswalrus/patches.rb +1 -0
- data/lib/opswalrus/runtime_environment.rb +40 -9
- data/lib/opswalrus/sshkit_ext.rb +6 -3
- data/lib/opswalrus/version.rb +1 -1
- data/opswalrus.gemspec +1 -1
- metadata +5 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 472ae5aafb306653a32149c08a481288237c6c524a3f72761e09ac9887a4c0f4
         | 
| 4 | 
            +
              data.tar.gz: 449e723590784e10faf30bbbebb122d0ae00c6652e02559decc0cbbac0ba670f
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 319c28870d5b1e91680e9aed29834df12c25dae3bbc6abeb0d54566f7d6a7662064dc8d54f7851d9d9ce7dc82d48c73a019abe58b384281e8b7c98ec5d252d99
         | 
| 7 | 
            +
              data.tar.gz: 3e4a3d90ef1f30f29f4acb0acc8a34422e4f6f687f42fef3cba890e052b1d07848f38276b88d5c95f91be3d3e966c5b4a1e3f5c8e1faa8d31b1342dd8364c64e
         | 
    
        data/CNAME
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            opswalrus.com
         | 
    
        data/Gemfile.lock
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | @@ -10,12 +10,18 @@ You have two options: | |
| 10 10 |  | 
| 11 11 | 
             
            ## Rubygems install
         | 
| 12 12 |  | 
| 13 | 
            +
            ```shell
         | 
| 14 | 
            +
            gem install opswalrus
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            ops version
         | 
| 17 | 
            +
            ```
         | 
| 18 | 
            +
             | 
| 13 19 | 
             
            ## Docker install
         | 
| 14 20 |  | 
| 15 21 | 
             
            ```shell
         | 
| 16 22 | 
             
            alias ops='docker run --rm -it -v $HOME/.ssh:/root/.ssh -v /var/run/docker.sock:/var/run/docker.sock -v ${PWD}/:/workdir ghcr.io/opswalrus/ops'
         | 
| 17 23 |  | 
| 18 | 
            -
            ops  | 
| 24 | 
            +
            ops version
         | 
| 19 25 | 
             
            ```
         | 
| 20 26 |  | 
| 21 27 | 
             
            # Examples
         | 
| @@ -206,7 +212,7 @@ davidinfra | |
| 206 212 | 
             
            │   ├── install
         | 
| 207 213 | 
             
            │   │   └── debian.ops
         | 
| 208 214 | 
             
            │   └── install.ops
         | 
| 209 | 
            -
            ├── hosts. | 
| 215 | 
            +
            ├── hosts.yaml
         | 
| 210 216 | 
             
            ├── main.ops
         | 
| 211 217 | 
             
            ├── prepare_host
         | 
| 212 218 | 
             
            │   ├── all.ops
         | 
| @@ -225,9 +231,10 @@ davidinfra | |
| 225 231 | 
             
            │   └── davidinfra
         | 
| 226 232 | 
             
            │       ├── caddy
         | 
| 227 233 | 
             
            │       │   ├── install
         | 
| 228 | 
            -
            │       │   │    | 
| 229 | 
            -
            │       │   └── install.ops
         | 
| 230 | 
            -
            │        | 
| 234 | 
            +
            │       │   │   ├── debian.ops
         | 
| 235 | 
            +
            │       │   │   └── install.ops
         | 
| 236 | 
            +
            │       │   └── restart.ops
         | 
| 237 | 
            +
            │       ├── hosts.yaml
         | 
| 231 238 | 
             
            │       ├── main.ops
         | 
| 232 239 | 
             
            │       ├── prepare_host
         | 
| 233 240 | 
             
            │       │   ├── all.ops
         | 
| @@ -238,8 +245,9 @@ davidinfra | |
| 238 245 | 
             
            ├── caddy
         | 
| 239 246 | 
             
            │   ├── install
         | 
| 240 247 | 
             
            │   │   └── debian.ops
         | 
| 241 | 
            -
            │   └── install.ops
         | 
| 242 | 
            -
             | 
| 248 | 
            +
            │   │   └── install.ops
         | 
| 249 | 
            +
            │   └── restart.ops
         | 
| 250 | 
            +
            ├── hosts.yaml
         | 
| 243 251 | 
             
            ├── main.ops
         | 
| 244 252 | 
             
            ├── prepare_host
         | 
| 245 253 | 
             
            │   ├── all.ops
         | 
| @@ -255,17 +263,22 @@ The import and symbol resolution rules are as follows: | |
| 255 263 | 
             
               Within the lexical scope of an ops file's ruby script, any ops files or subdirectories that are implicitly imported
         | 
| 256 264 | 
             
               may be referenced by their name.
         | 
| 257 265 | 
             
               For example:
         | 
| 258 | 
            -
               - main.ops may invoke caddy/install.ops with the expression `caddy. | 
| 266 | 
            +
               - main.ops may invoke caddy/install.ops with the expression `caddy.restart(...)`
         | 
| 259 267 | 
             
               - all.ops may invoke hostname.ops with the expression `hostname(...)`
         | 
| 260 | 
            -
            2. If there is an ops file and a directory that share the same name (with the exception of the .ops file extension),
         | 
| 261 | 
            -
               then only the  | 
| 262 | 
            -
                | 
| 263 | 
            -
                | 
| 264 | 
            -
                | 
| 268 | 
            +
            2. If there is an ops file and a directory that share the same name (with the exception of the .ops file extension), and
         | 
| 269 | 
            +
               are both contained by the same parent directory, then only the .ops file may be referenced and invoked by other ops files.
         | 
| 270 | 
            +
               The directory of the same name will be treated as a library directory and if there is a Ruby source file in the library
         | 
| 271 | 
            +
               directory with the same name, then that ruby file will automatically be loaded. Other ruby files within the library directory
         | 
| 272 | 
            +
               will be required/loaded as instructed by the entrypoint .rb file.
         | 
| 273 | 
            +
            3. If there is an ops file and a directory that share the same name (with the exception of the .ops file extension), and
         | 
| 274 | 
            +
               the ops file is contained by the directory of the same name, then the ops file is considered to be the primary API
         | 
| 275 | 
            +
               interface for a sub-module that is implemented by the ops files and ruby scripts contained within the directory.
         | 
| 276 | 
            +
               Consequently, the directory containing the ops file of the same name (with the exception of the .ops file extension)
         | 
| 277 | 
            +
               may be invoked as if it were the primary API interface ops file.
         | 
| 265 278 | 
             
               For example:
         | 
| 266 | 
            -
               - main.ops may invoke `caddy.install(...) | 
| 267 | 
            -
               - install.ops may invoke ` | 
| 268 | 
            -
             | 
| 279 | 
            +
               - main.ops may invoke `caddy.install(...)` as a shorthand syntax for `caddy.install.install(...)`
         | 
| 280 | 
            +
               - install.ops may invoke `debian(...)`, and reference other files or subpackages within the caddy/install directory
         | 
| 281 | 
            +
            4. Ops files may import packages or relative paths:
         | 
| 269 282 | 
             
               1. a package reference that matches one of the local package names in the dependencies captured in packages.yaml
         | 
| 270 283 | 
             
               2. a package reference that resolves to a relative path pointing at a package directory
         | 
| 271 284 | 
             
               3. a relative path that resolves to a directory containing ops files
         | 
    
        data/lib/opswalrus/app.rb
    CHANGED
    
    | @@ -14,6 +14,7 @@ require_relative "hosts_file" | |
| 14 14 | 
             
            require_relative "operation_runner"
         | 
| 15 15 | 
             
            require_relative "bundler"
         | 
| 16 16 | 
             
            require_relative "package_file"
         | 
| 17 | 
            +
            require_relative "version"
         | 
| 17 18 |  | 
| 18 19 |  | 
| 19 20 | 
             
            module OpsWalrus
         | 
| @@ -40,6 +41,7 @@ module OpsWalrus | |
| 40 41 | 
             
                  @pwd = pwd.to_pathname
         | 
| 41 42 | 
             
                  @bundler = Bundler.new(@pwd)
         | 
| 42 43 | 
             
                  @local_hostname = "localhost"
         | 
| 44 | 
            +
                  @mode = :report     # :report | :script
         | 
| 43 45 | 
             
                end
         | 
| 44 46 |  | 
| 45 47 | 
             
                def to_s
         | 
| @@ -50,12 +52,16 @@ module OpsWalrus | |
| 50 52 | 
             
                  ""  # return empty string because we won't want anyone accidentally printing or inspecting @sudo_password
         | 
| 51 53 | 
             
                end
         | 
| 52 54 |  | 
| 53 | 
            -
                def  | 
| 54 | 
            -
                  @ | 
| 55 | 
            +
                def script_mode!
         | 
| 56 | 
            +
                  @mode = :script
         | 
| 55 57 | 
             
                end
         | 
| 56 58 |  | 
| 57 | 
            -
                def  | 
| 58 | 
            -
                  @ | 
| 59 | 
            +
                def report_mode?
         | 
| 60 | 
            +
                  @mode == :report
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                def script_mode?
         | 
| 64 | 
            +
                  @mode == :script
         | 
| 59 65 | 
             
                end
         | 
| 60 66 |  | 
| 61 67 | 
             
                def set_local_hostname(hostname)
         | 
| @@ -149,8 +155,12 @@ module OpsWalrus | |
| 149 155 | 
             
                    ops_file = set_pwd_to_ops_file_package_directory(ops_file)
         | 
| 150 156 | 
             
                  end
         | 
| 151 157 |  | 
| 158 | 
            +
                  if @verbose
         | 
| 159 | 
            +
                    puts "Running: #{ops_file.ops_file_path}"
         | 
| 160 | 
            +
                  end
         | 
| 161 | 
            +
             | 
| 152 162 | 
             
                  op = OperationRunner.new(self, ops_file)
         | 
| 153 | 
            -
                  result = op.run(operation_kv_args, params_json_hash: @params | 
| 163 | 
            +
                  result = op.run(operation_kv_args, params_json_hash: @params)
         | 
| 154 164 | 
             
                  exit_status = result.exit_status
         | 
| 155 165 |  | 
| 156 166 | 
             
                  if @verbose
         | 
| @@ -161,7 +171,7 @@ module OpsWalrus | |
| 161 171 | 
             
                    puts JSON.pretty_generate(result.value)
         | 
| 162 172 | 
             
                  end
         | 
| 163 173 |  | 
| 164 | 
            -
                  if  | 
| 174 | 
            +
                  if script_mode?
         | 
| 165 175 | 
             
                    puts JSON.pretty_generate(result.value)
         | 
| 166 176 | 
             
                  end
         | 
| 167 177 |  | 
| @@ -302,7 +312,7 @@ module OpsWalrus | |
| 302 312 | 
             
                  tags = @inventory_tag_selections + (tag_selection || [])
         | 
| 303 313 | 
             
                  tags.uniq!
         | 
| 304 314 |  | 
| 305 | 
            -
                  host_references = ["hosts. | 
| 315 | 
            +
                  host_references = ["hosts.yaml"] if (host_references.nil? || host_references.empty?) && File.exist?("hosts.yaml")
         | 
| 306 316 |  | 
| 307 317 | 
             
                  hosts_files, host_strings = host_references.partition {|ref| File.exist?(ref) }
         | 
| 308 318 | 
             
                  hosts_files = hosts_files.map {|file_path| HostsFile.new(file_path) }
         | 
| @@ -327,6 +337,10 @@ module OpsWalrus | |
| 327 337 | 
             
                  selected_hosts.sort_by(&:to_s)
         | 
| 328 338 | 
             
                end
         | 
| 329 339 |  | 
| 340 | 
            +
                def print_version
         | 
| 341 | 
            +
                  puts VERSION
         | 
| 342 | 
            +
                end
         | 
| 343 | 
            +
             | 
| 330 344 | 
             
                def unzip(zip_bundle_file = nil, output_dir = nil)
         | 
| 331 345 | 
             
                  bundler.unzip(zip_bundle_file, output_dir)
         | 
| 332 346 | 
             
                end
         | 
    
        data/lib/opswalrus/bootstrap.sh
    CHANGED
    
    | @@ -7,10 +7,15 @@ if [ -x "$(command -v /home/linuxbrew/.linuxbrew/bin/brew)" ]; then | |
| 7 7 | 
             
              # exit early if ruby already exists
         | 
| 8 8 | 
             
              if [ -x "$(command -v ruby)" ]; then
         | 
| 9 9 | 
             
                echo 'Ruby is already installed.' >&2
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                # make sure the latest opswalrus gem is installed
         | 
| 12 | 
            +
                gem install opswalrus
         | 
| 13 | 
            +
             | 
| 10 14 | 
             
                exit 0
         | 
| 11 15 | 
             
              fi
         | 
| 12 16 | 
             
            fi
         | 
| 13 17 |  | 
| 18 | 
            +
            # https://github.com/chef/os_release documents the contents of /etc/os-release from a bunch of distros
         | 
| 14 19 | 
             
            OS=$(cat /etc/os-release | grep "^ID=")
         | 
| 15 20 | 
             
            if echo $OS | grep -q 'ubuntu'; then
         | 
| 16 21 | 
             
              # update package list
         | 
    
        data/lib/opswalrus/cli.rb
    CHANGED
    
    | @@ -15,10 +15,11 @@ module OpsWalrus | |
| 15 15 |  | 
| 16 16 | 
             
                # this is invoked on an unhandled exception or a call to exit_now!
         | 
| 17 17 | 
             
                on_error do |exception|
         | 
| 18 | 
            -
                   | 
| 19 | 
            -
             | 
| 20 | 
            -
                   | 
| 21 | 
            -
                   | 
| 18 | 
            +
                  next(false) if exception.is_a? GLI::CustomExit
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  puts "catchall exception handler:"
         | 
| 21 | 
            +
                  puts exception.message
         | 
| 22 | 
            +
                  puts exception.backtrace.join("\n")
         | 
| 22 23 | 
             
                  false   # disable built-in exception handling
         | 
| 23 24 | 
             
                end
         | 
| 24 25 |  | 
| @@ -27,12 +28,19 @@ module OpsWalrus | |
| 27 28 | 
             
                desc 'Be verbose'
         | 
| 28 29 | 
             
                switch [:v, :verbose]
         | 
| 29 30 |  | 
| 30 | 
            -
                desc ' | 
| 31 | 
            +
                desc 'Turn on debug mode'
         | 
| 31 32 | 
             
                switch [:d, :debug]
         | 
| 32 33 |  | 
| 33 34 | 
             
                flag [:h, :hosts], multiple: true, desc: "Specify the hosts.yaml file"
         | 
| 34 35 | 
             
                flag [:t, :tags], multiple: true, desc: "Specify a set of tags to filter the hosts by"
         | 
| 35 36 |  | 
| 37 | 
            +
                desc 'Print version'
         | 
| 38 | 
            +
                command :version do |c|
         | 
| 39 | 
            +
                  c.action do |global_options, options, args|
         | 
| 40 | 
            +
                    $app.print_version
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 36 44 | 
             
                desc 'Report on the host inventory'
         | 
| 37 45 | 
             
                long_desc 'Report on the host inventory'
         | 
| 38 46 | 
             
                command :inventory do |c|
         | 
| @@ -53,7 +61,7 @@ module OpsWalrus | |
| 53 61 | 
             
                  c.flag [:u, :user], desc: "Specify the user that the operation will run as"
         | 
| 54 62 | 
             
                  c.switch :pass, desc: "Prompt for a sudo password"
         | 
| 55 63 | 
             
                  c.flag [:p, :params], desc: "JSON string that represents the input parameters for the operation. The JSON string must conform to the params schema for the operation."
         | 
| 56 | 
            -
                  c.switch : | 
| 64 | 
            +
                  c.switch :script, desc: "Script mode"
         | 
| 57 65 |  | 
| 58 66 | 
             
                  c.action do |global_options, options, args|
         | 
| 59 67 | 
             
                    hosts = global_options[:hosts] || []
         | 
| @@ -81,17 +89,10 @@ module OpsWalrus | |
| 81 89 | 
             
                      $app.prompt_sudo_password
         | 
| 82 90 | 
             
                    end
         | 
| 83 91 |  | 
| 84 | 
            -
                    if options[: | 
| 85 | 
            -
                      $app. | 
| 92 | 
            +
                    if options[:script]
         | 
| 93 | 
            +
                      $app.script_mode!
         | 
| 86 94 | 
             
                    end
         | 
| 87 95 |  | 
| 88 | 
            -
                    # puts "verbose"
         | 
| 89 | 
            -
                    # puts verbose.inspect
         | 
| 90 | 
            -
                    # puts "user"
         | 
| 91 | 
            -
                    # puts user.inspect
         | 
| 92 | 
            -
                    # puts "args"
         | 
| 93 | 
            -
                    # puts args.inspect
         | 
| 94 | 
            -
             | 
| 95 96 | 
             
                    exit_status = $app.run(args)
         | 
| 96 97 |  | 
| 97 98 | 
             
                    exit_now!("error", exit_status) unless exit_status == 0
         | 
    
        data/lib/opswalrus/host.rb
    CHANGED
    
    | @@ -5,71 +5,6 @@ require_relative "interaction_handlers" | |
| 5 5 |  | 
| 6 6 | 
             
            module OpsWalrus
         | 
| 7 7 |  | 
| 8 | 
            -
              module HostDSL
         | 
| 9 | 
            -
                # returns the stdout from the command
         | 
| 10 | 
            -
                def sh(desc_or_cmd = nil, cmd = nil, input: nil, &block)
         | 
| 11 | 
            -
                  out, err, status = *shell!(desc_or_cmd, cmd, block, input: input)
         | 
| 12 | 
            -
                  out
         | 
| 13 | 
            -
                end
         | 
| 14 | 
            -
             | 
| 15 | 
            -
                # returns the tuple: [stdout, stderr, exit_status]
         | 
| 16 | 
            -
                def shell(desc_or_cmd = nil, cmd = nil, input: nil, &block)
         | 
| 17 | 
            -
                  shell!(desc_or_cmd, cmd, block, input: input)
         | 
| 18 | 
            -
                end
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                # returns the tuple: [stdout, stderr, exit_status]
         | 
| 21 | 
            -
                def shell!(desc_or_cmd = nil, cmd = nil, block = nil, input: nil)
         | 
| 22 | 
            -
                  # description = nil
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                  return ["", "", 0] if !desc_or_cmd && !cmd && !block    # we were told to do nothing; like hitting enter at the bash prompt; we can do nothing successfully
         | 
| 25 | 
            -
             | 
| 26 | 
            -
                  description = desc_or_cmd if cmd || block
         | 
| 27 | 
            -
                  cmd = block.call if block
         | 
| 28 | 
            -
                  cmd ||= desc_or_cmd
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                  cmd = WalrusLang.render(cmd, block.binding) if block && cmd =~ /{{.*}}/
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                  #cmd = Shellwords.escape(cmd)
         | 
| 33 | 
            -
             | 
| 34 | 
            -
                  if self.alias
         | 
| 35 | 
            -
                    print "[#{self.alias} | #{host}] "
         | 
| 36 | 
            -
                  else
         | 
| 37 | 
            -
                    print "[#{host}] "
         | 
| 38 | 
            -
                  end
         | 
| 39 | 
            -
                  print "#{description}: " if description
         | 
| 40 | 
            -
                  puts cmd
         | 
| 41 | 
            -
             | 
| 42 | 
            -
                  return unless cmd && !cmd.strip.empty?
         | 
| 43 | 
            -
             | 
| 44 | 
            -
                  # puts "shell: #{cmd}"
         | 
| 45 | 
            -
                  # puts "shell: #{cmd.inspect}"
         | 
| 46 | 
            -
                  # puts "sudo_password: #{sudo_password}"
         | 
| 47 | 
            -
             | 
| 48 | 
            -
                  sshkit_cmd = execute_cmd(cmd, input: input)
         | 
| 49 | 
            -
             | 
| 50 | 
            -
                  [sshkit_cmd.full_stdout, sshkit_cmd.full_stderr, sshkit_cmd.exit_status]
         | 
| 51 | 
            -
                end
         | 
| 52 | 
            -
             | 
| 53 | 
            -
                # def init_brew
         | 
| 54 | 
            -
                #   execute('eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"')
         | 
| 55 | 
            -
                # end
         | 
| 56 | 
            -
             | 
| 57 | 
            -
                # runs the specified ops command with the specified command arguments
         | 
| 58 | 
            -
                def run_ops(command, command_arguments, in_bundle_root_dir: true, verbose: false)
         | 
| 59 | 
            -
                  # e.g. /home/linuxbrew/.linuxbrew/bin/gem exec -g opswalrus ops bundle unzip tmpops.zip
         | 
| 60 | 
            -
                  # e.g. /home/linuxbrew/.linuxbrew/bin/gem exec -g opswalrus ops run echo.ops args:foo args:bar
         | 
| 61 | 
            -
             | 
| 62 | 
            -
                  cmd = "/home/linuxbrew/.linuxbrew/bin/gem exec -g opswalrus ops"
         | 
| 63 | 
            -
                  cmd << " -v" if verbose
         | 
| 64 | 
            -
                  cmd << " #{command.to_s}"
         | 
| 65 | 
            -
                  cmd << " #{@tmp_bundle_root_dir}" if in_bundle_root_dir
         | 
| 66 | 
            -
                  cmd << " #{command_arguments}" unless command_arguments.empty?
         | 
| 67 | 
            -
             | 
| 68 | 
            -
                  shell!(cmd)
         | 
| 69 | 
            -
                end
         | 
| 70 | 
            -
             | 
| 71 | 
            -
              end
         | 
| 72 | 
            -
             | 
| 73 8 | 
             
              class HostProxyOpsFileInvocationBuilder
         | 
| 74 9 | 
             
                def initialize(host_proxy, is_invocation_a_call_to_package_in_bundle_dir = false)
         | 
| 75 10 | 
             
                  @host_proxy = host_proxy
         | 
| @@ -116,6 +51,53 @@ module OpsWalrus | |
| 116 51 |  | 
| 117 52 | 
             
              # the subclasses of HostProxy will define methods that handle method dispatch via HostProxyOpsFileInvocationBuilder objects
         | 
| 118 53 | 
             
              class HostProxy
         | 
| 54 | 
            +
                def self.define_host_proxy_class(ops_file)
         | 
| 55 | 
            +
                  klass = Class.new(HostProxy)
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  methods_defined = Set.new
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  # define methods for every import in the script
         | 
| 60 | 
            +
                  ops_file.local_symbol_table.each do |symbol_name, import_reference|
         | 
| 61 | 
            +
                    unless methods_defined.include? symbol_name
         | 
| 62 | 
            +
                      # puts "1. defining: #{symbol_name}(...)"
         | 
| 63 | 
            +
                      klass.define_method(symbol_name) do |*args, **kwargs, &block|
         | 
| 64 | 
            +
                        invocation_builder = case import_reference
         | 
| 65 | 
            +
                        # we know we're dealing with a package dependency reference, so we want to run an ops file contained within the bundle directory,
         | 
| 66 | 
            +
                        # therefore, we want to reference the specified ops file with respect to the bundle dir
         | 
| 67 | 
            +
                        when PackageDependencyReference
         | 
| 68 | 
            +
                          HostProxyOpsFileInvocationBuilder.new(self, true)
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                        # we know we're dealing with a directory reference or OpsFile reference outside of the bundle dir, so we want to reference
         | 
| 71 | 
            +
                        # the specified ops file with respect to the root directory, and not with respect to the bundle dir
         | 
| 72 | 
            +
                        when DirectoryReference, OpsFileReference
         | 
| 73 | 
            +
                          HostProxyOpsFileInvocationBuilder.new(self, false)
         | 
| 74 | 
            +
                        end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                        invocation_builder.send(symbol_name, *args, **kwargs, &block)
         | 
| 77 | 
            +
                      end
         | 
| 78 | 
            +
                      methods_defined << symbol_name
         | 
| 79 | 
            +
                    end
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  # define methods for every Namespace or OpsFile within the namespace that the OpsFile resides within
         | 
| 83 | 
            +
                  sibling_symbol_table = Set.new
         | 
| 84 | 
            +
                  sibling_symbol_table |= ops_file.dirname.glob("*.ops").map {|ops_file_path| ops_file_path.basename(".ops").to_s }   # OpsFiles
         | 
| 85 | 
            +
                  sibling_symbol_table |= ops_file.dirname.glob("*").select(&:directory?).map {|dir_path| dir_path.basename.to_s }    # Namespaces
         | 
| 86 | 
            +
                  sibling_symbol_table.each do |symbol_name|
         | 
| 87 | 
            +
                    unless methods_defined.include? symbol_name
         | 
| 88 | 
            +
                      # puts "2. defining: #{symbol_name}(...)"
         | 
| 89 | 
            +
                      klass.define_method(symbol_name) do |*args, **kwargs, &block|
         | 
| 90 | 
            +
                        invocation_builder = HostProxyOpsFileInvocationBuilder.new(self, false)
         | 
| 91 | 
            +
                        invocation_builder.invoke(symbol_name, *args, **kwargs, &block)
         | 
| 92 | 
            +
                      end
         | 
| 93 | 
            +
                      methods_defined << symbol_name
         | 
| 94 | 
            +
                    end
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                  klass
         | 
| 98 | 
            +
                end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
             | 
| 119 101 | 
             
                attr_accessor :_host
         | 
| 120 102 |  | 
| 121 103 | 
             
                def initialize(host)
         | 
| @@ -129,6 +111,74 @@ module OpsWalrus | |
| 129 111 | 
             
                end
         | 
| 130 112 | 
             
              end
         | 
| 131 113 |  | 
| 114 | 
            +
             | 
| 115 | 
            +
              module HostDSL
         | 
| 116 | 
            +
                # returns the stdout from the command
         | 
| 117 | 
            +
                def sh(desc_or_cmd = nil, cmd = nil, input: nil, &block)
         | 
| 118 | 
            +
                  out, err, status = *shell!(desc_or_cmd, cmd, block, input: input)
         | 
| 119 | 
            +
                  out
         | 
| 120 | 
            +
                end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                # returns the tuple: [stdout, stderr, exit_status]
         | 
| 123 | 
            +
                def shell(desc_or_cmd = nil, cmd = nil, input: nil, &block)
         | 
| 124 | 
            +
                  shell!(desc_or_cmd, cmd, block, input: input)
         | 
| 125 | 
            +
                end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                # returns the tuple: [stdout, stderr, exit_status]
         | 
| 128 | 
            +
                def shell!(desc_or_cmd = nil, cmd = nil, block = nil, input: nil)
         | 
| 129 | 
            +
                  # description = nil
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                  return ["", "", 0] if !desc_or_cmd && !cmd && !block    # we were told to do nothing; like hitting enter at the bash prompt; we can do nothing successfully
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                  description = desc_or_cmd if cmd || block
         | 
| 134 | 
            +
                  cmd = block.call if block
         | 
| 135 | 
            +
                  cmd ||= desc_or_cmd
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                  cmd = WalrusLang.render(cmd, block.binding) if block && cmd =~ /{{.*}}/
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                  #cmd = Shellwords.escape(cmd)
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                  if App.instance.report_mode?
         | 
| 142 | 
            +
                    if self.alias
         | 
| 143 | 
            +
                      print "[#{self.alias} | #{host}] "
         | 
| 144 | 
            +
                    else
         | 
| 145 | 
            +
                      print "[#{host}] "
         | 
| 146 | 
            +
                    end
         | 
| 147 | 
            +
                    print "#{description}: " if description
         | 
| 148 | 
            +
                    puts cmd
         | 
| 149 | 
            +
                  end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                  return unless cmd && !cmd.strip.empty?
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                  # puts "shell: #{cmd}"
         | 
| 154 | 
            +
                  # puts "shell: #{cmd.inspect}"
         | 
| 155 | 
            +
                  # puts "sudo_password: #{sudo_password}"
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                  sshkit_cmd = execute_cmd(cmd, input: input)
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                  [sshkit_cmd.full_stdout, sshkit_cmd.full_stderr, sshkit_cmd.exit_status]
         | 
| 160 | 
            +
                end
         | 
| 161 | 
            +
             | 
| 162 | 
            +
                # def init_brew
         | 
| 163 | 
            +
                #   execute('eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"')
         | 
| 164 | 
            +
                # end
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                # runs the specified ops command with the specified command arguments
         | 
| 167 | 
            +
                def run_ops(command, command_arguments, in_bundle_root_dir: true, verbose: false)
         | 
| 168 | 
            +
                  # e.g. /home/linuxbrew/.linuxbrew/bin/gem exec -g opswalrus ops bundle unzip tmpops.zip
         | 
| 169 | 
            +
                  # e.g. /home/linuxbrew/.linuxbrew/bin/gem exec -g opswalrus ops run echo.ops args:foo args:bar
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                  cmd = "/home/linuxbrew/.linuxbrew/bin/gem exec -g opswalrus ops"
         | 
| 172 | 
            +
                  cmd << " -v" if verbose
         | 
| 173 | 
            +
                  cmd << " #{command.to_s}"
         | 
| 174 | 
            +
                  cmd << " #{@tmp_bundle_root_dir}" if in_bundle_root_dir
         | 
| 175 | 
            +
                  cmd << " #{command_arguments}" unless command_arguments.empty?
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                  shell!(cmd)
         | 
| 178 | 
            +
                end
         | 
| 179 | 
            +
             | 
| 180 | 
            +
              end
         | 
| 181 | 
            +
             | 
| 132 182 | 
             
              class Host
         | 
| 133 183 | 
             
                include HostDSL
         | 
| 134 184 |  | 
| @@ -222,18 +272,12 @@ module OpsWalrus | |
| 222 272 | 
             
                end
         | 
| 223 273 |  | 
| 224 274 | 
             
                def execute(*args, input: nil)
         | 
| 225 | 
            -
                  # puts "interaction handler responds with: #{ssh_password}"
         | 
| 226 | 
            -
                  # @sshkit_backend.capture(*args, interaction_handler: SudoPasswordMapper.new(ssh_password).interaction_handler, verbosity: :info)
         | 
| 227 | 
            -
                  # @sshkit_backend.capture(*args, interaction_handler: SudoPromptInteractionHandler.new, verbosity: :info)
         | 
| 228 | 
            -
             | 
| 229 275 | 
             
                  @runtime_env.handle_input(input, ssh_password) do |interaction_handler|
         | 
| 230 276 | 
             
                    @sshkit_backend.capture(*args, interaction_handler: interaction_handler, verbosity: :info)
         | 
| 231 277 | 
             
                  end
         | 
| 232 | 
            -
             | 
| 233 278 | 
             
                end
         | 
| 234 279 |  | 
| 235 280 | 
             
                def execute_cmd(*args, input: nil)
         | 
| 236 | 
            -
                  # @sshkit_backend.execute_cmd(*args, interaction_handler: SudoPasswordMapper.new(ssh_password).interaction_handler, verbosity: :info)
         | 
| 237 281 | 
             
                  @runtime_env.handle_input(input, ssh_password) do |interaction_handler|
         | 
| 238 282 | 
             
                    @sshkit_backend.execute_cmd(*args, interaction_handler: interaction_handler, verbosity: :info)
         | 
| 239 283 | 
             
                  end
         |