closh 0.0.1.alpha.1
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +99 -0
- data/bin/closh +5 -0
- data/lib/closh.rb +6 -0
- data/lib/closh/cli.rb +63 -0
- data/lib/closh/session.rb +95 -0
- data/lib/closh/version.rb +3 -0
- metadata +171 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c2ccd50d3128e79f0170e486c6c41960224e9a76
|
4
|
+
data.tar.gz: b9e91861f3fc669bc8cf2dc1ca6d8c1bc45b51fd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 334acd5b103683388a71123a76c0e75c4f1d3ef9123a19a6d5afc9169b77a7b9a8f93d65fbb8707bb0349deb2a81e2501126ea88773e0d6c579d72b0c52b116b
|
7
|
+
data.tar.gz: f767a8e1e8279cefccfc8c63e02b83510d3e6b9d397b6e4e38958789accadc440ad584dd60d3b07c063b3e8c5cdfde53c0e2f2c4c3c573d6d7215ab52151768c
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Vincent Ollivier
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
Cloud Shell
|
2
|
+
===========
|
3
|
+
|
4
|
+
A shell running in the cloud
|
5
|
+
|
6
|
+
|
7
|
+
Installation
|
8
|
+
------------
|
9
|
+
|
10
|
+
$ gem install closh
|
11
|
+
|
12
|
+
Alternatively you can build the gem from its repository:
|
13
|
+
|
14
|
+
$ git clone git://github.com/vinc/closh.git
|
15
|
+
$ cd closh
|
16
|
+
$ gem build closh.gemspec
|
17
|
+
$ gem install closh-0.0.1.gem
|
18
|
+
|
19
|
+
|
20
|
+
Usage
|
21
|
+
-----
|
22
|
+
|
23
|
+
Run a command in the cloud:
|
24
|
+
|
25
|
+
$ closh -x "cut -d . -f 1 /proc/uptime"
|
26
|
+
23
|
27
|
+
|
28
|
+
More verbose:
|
29
|
+
|
30
|
+
$ closh -vx "cut -d . -f 1 /proc/uptime"
|
31
|
+
debug: uploading SSH public key to AWS ...
|
32
|
+
debug: creating server on AWS ...
|
33
|
+
debug: connecting to 'ubuntu@158.116.77.89' ...
|
34
|
+
debug: executing command 'cut -d . -f 1 /proc/uptime' ...
|
35
|
+
23
|
36
|
+
debug: destroying server ...
|
37
|
+
|
38
|
+
Pipe to stdin:
|
39
|
+
|
40
|
+
$ echo "cut -d . -f 1 /proc/uptime" | closh -x "bash -s"
|
41
|
+
|
42
|
+
Run a script remotely:
|
43
|
+
|
44
|
+
$ closh -x "bash -s" < example.sh
|
45
|
+
|
46
|
+
Skip the `-x` flag to keep a session alive for long running scripts.
|
47
|
+
|
48
|
+
Use `-l` to list sessions and `-r` to resume one:
|
49
|
+
|
50
|
+
$ closh -r 158.116.77.89 "tail -f screenlog.0"
|
51
|
+
|
52
|
+
More options:
|
53
|
+
|
54
|
+
$ closh -h
|
55
|
+
Usage: closh <command>
|
56
|
+
|
57
|
+
Cloud Shell v0.0.1
|
58
|
+
Options:
|
59
|
+
-n, --dry-run Create fake server and run locally
|
60
|
+
-c, --config=<s> Config file (default: ~/.closh.yaml)
|
61
|
+
-k, --key=<s> SSH public key (default: ~/.ssh/id_rsa.pub)
|
62
|
+
-r, --resume=<s> Resume session
|
63
|
+
-x, --kill Kill session at the end
|
64
|
+
-l, --list List sessions
|
65
|
+
-v, --verbose Use verbose mode
|
66
|
+
-h, --help Show this message
|
67
|
+
-V, --version Print version and exit
|
68
|
+
|
69
|
+
|
70
|
+
Configure
|
71
|
+
---------
|
72
|
+
|
73
|
+
Cloud Shell is based on [Fog](http://fog.io/) under the hood to create compute
|
74
|
+
servers.
|
75
|
+
|
76
|
+
The configuration is stored in a YAML file (`~/.closh.yaml`). It is divided in
|
77
|
+
two parts: compute and server. The former being fed to `Fog::Compute.new` and
|
78
|
+
the latter to `Fog::Compute::*::Server.new`.
|
79
|
+
|
80
|
+
```yaml
|
81
|
+
---
|
82
|
+
|
83
|
+
compute:
|
84
|
+
provider: AWS
|
85
|
+
aws_access_key_id: <%= ENV["AWS_ACCESS_KEY_ID"] %>
|
86
|
+
aws_secret_access_key: <%= ENV["AWS_SECRET_ACCESS_KEY"] %>
|
87
|
+
region: us-west-2
|
88
|
+
|
89
|
+
server:
|
90
|
+
image_id: ami-70728c08 # ubuntu 17.04 amd64 hvm:ebs-ssd
|
91
|
+
flavor_id: t2.micro
|
92
|
+
username: ubuntu
|
93
|
+
```
|
94
|
+
|
95
|
+
|
96
|
+
License
|
97
|
+
-------
|
98
|
+
|
99
|
+
Copyright (C) 2017 Vincent Ollivier. Released under MIT.
|
data/bin/closh
ADDED
data/lib/closh.rb
ADDED
data/lib/closh/cli.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require "active_support/core_ext/object/blank"
|
2
|
+
require "active_support/hash_with_indifferent_access"
|
3
|
+
require "erb"
|
4
|
+
require "trollop"
|
5
|
+
require "yaml"
|
6
|
+
|
7
|
+
module CloudShell
|
8
|
+
module CLI
|
9
|
+
def self.run
|
10
|
+
opts = self.parse_opts(ARGV)
|
11
|
+
|
12
|
+
config_path = File.expand_path(opts[:config])
|
13
|
+
config = YAML.load(ERB.new(File.read(config_path)).result).deep_symbolize_keys
|
14
|
+
|
15
|
+
shell = CloudShell::Session.new(config, opts)
|
16
|
+
|
17
|
+
if opts[:list]
|
18
|
+
shell.list_servers
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
|
22
|
+
if opts[:resume]
|
23
|
+
shell.find_server(opts[:resume])
|
24
|
+
else
|
25
|
+
key_path = File.expand_path(opts[:key])
|
26
|
+
key_body = File.read(key_path)
|
27
|
+
shell.upload_ssh_key(key_body)
|
28
|
+
|
29
|
+
shell.create_server
|
30
|
+
end
|
31
|
+
|
32
|
+
begin
|
33
|
+
command = ARGV.join(" ").presence || "$SHELL"
|
34
|
+
shell.exec(command) do |channel|
|
35
|
+
unless STDIN.tty?
|
36
|
+
channel.send_data(STDIN.read)
|
37
|
+
channel.eof!
|
38
|
+
end
|
39
|
+
end
|
40
|
+
ensure
|
41
|
+
shell.destroy_server if opts[:kill]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def self.parse_opts(args)
|
48
|
+
Trollop::options(ARGV) do
|
49
|
+
version "Cloud Shell v#{CloudShell::VERSION}"
|
50
|
+
usage "<command>"
|
51
|
+
opt :dry_run, "Create fake server and run locally", short: "n"
|
52
|
+
opt :config, "Config file", type: :string, default: "~/.closh.yaml"
|
53
|
+
opt :key, "SSH public key", type: :string, default: "~/.ssh/id_rsa.pub"
|
54
|
+
opt :resume, "Resume session", type: :string
|
55
|
+
opt :kill, "Kill session at the end", short: "x"
|
56
|
+
opt :list, "List sessions"
|
57
|
+
opt :verbose, "Use verbose mode"
|
58
|
+
opt :help, "Show this message"
|
59
|
+
opt :version, "Print version and exit", short: "V"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require "fog"
|
2
|
+
require "net/ssh"
|
3
|
+
require "rainbow"
|
4
|
+
|
5
|
+
module CloudShell
|
6
|
+
class Session
|
7
|
+
def initialize(config, options)
|
8
|
+
@config = config
|
9
|
+
@provider = config[:compute][:provider]
|
10
|
+
@key_name = config[:compute][:key_name] || "closh"
|
11
|
+
|
12
|
+
@options = options
|
13
|
+
Fog.mock! if options[:dry_run]
|
14
|
+
|
15
|
+
@compute = Fog::Compute.new(config[:compute])
|
16
|
+
end
|
17
|
+
|
18
|
+
def upload_ssh_key(key)
|
19
|
+
if @compute.key_pairs.get(@key_name).nil?
|
20
|
+
debug("uploading SSH public key to #{@provider} ...")
|
21
|
+
@compute.import_key_pair(@key_name, key)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def list_servers
|
26
|
+
debug("listing servers on #{@provider} ...")
|
27
|
+
@compute.servers.each do |server|
|
28
|
+
puts server.public_ip_address if server.public_ip_address
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def find_server(address)
|
33
|
+
debug("finding server on #{@provider} ...")
|
34
|
+
config = @config[:server]
|
35
|
+
@username = config[:username] || "root"
|
36
|
+
@compute.servers.each do |server|
|
37
|
+
return @server = server if server.public_ip_address == address
|
38
|
+
end
|
39
|
+
error("could not find server on #{@provider}")
|
40
|
+
exit 1
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_server
|
44
|
+
debug("creating server on #{@provider} ...")
|
45
|
+
config = @config[:server].merge({ key_name: @key_name })
|
46
|
+
@username = config[:username] || "root"
|
47
|
+
@server = @compute.servers.create(config)
|
48
|
+
@server.wait_for { ready? }
|
49
|
+
end
|
50
|
+
|
51
|
+
def destroy_server
|
52
|
+
debug("destroying server ...")
|
53
|
+
@server.destroy
|
54
|
+
end
|
55
|
+
|
56
|
+
def exec(command, &block)
|
57
|
+
debug("connecting to '#{@username}@#{@server.public_ip_address}' ...")
|
58
|
+
if @options[:dry_run]
|
59
|
+
debug("executing command '#{command}' ...")
|
60
|
+
system(command)
|
61
|
+
else
|
62
|
+
Net::SSH.start(@server.public_ip_address, @username) do |ssh|
|
63
|
+
ssh.open_channel do |channel|
|
64
|
+
debug("executing command `#{command}` ...")
|
65
|
+
channel.exec(command) do |_, success|
|
66
|
+
channel.on_data do |_, data|
|
67
|
+
print data
|
68
|
+
end
|
69
|
+
|
70
|
+
if block_given?
|
71
|
+
block.call(channel)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
ssh.loop
|
76
|
+
end
|
77
|
+
end
|
78
|
+
rescue Errno::ECONNREFUSED
|
79
|
+
n = 10
|
80
|
+
error("connection refused, retrying in #{n} seconds")
|
81
|
+
sleep n
|
82
|
+
retry
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def debug(text)
|
88
|
+
puts Rainbow("debug: ").cyan + text if @options[:verbose]
|
89
|
+
end
|
90
|
+
|
91
|
+
def error(text)
|
92
|
+
puts Rainbow("error: ").yellow + text if @options[:verbose]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
metadata
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: closh
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.alpha.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Vincent Ollivier
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-10-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.1'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 5.1.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '5.1'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 5.1.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: fog
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.42'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 1.42.0
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '1.42'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 1.42.0
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: net-ssh
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '4.1'
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 4.1.0
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '4.1'
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 4.1.0
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: rainbow
|
75
|
+
requirement: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - "~>"
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '2.2'
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 2.2.0
|
83
|
+
type: :runtime
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.2'
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 2.2.0
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: trollop
|
95
|
+
requirement: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - "~>"
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '2.1'
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 2.1.0
|
103
|
+
type: :runtime
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '2.1'
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: 2.1.0
|
113
|
+
- !ruby/object:Gem::Dependency
|
114
|
+
name: rspec
|
115
|
+
requirement: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - "~>"
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '3.6'
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: 3.6.0
|
123
|
+
type: :development
|
124
|
+
prerelease: false
|
125
|
+
version_requirements: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - "~>"
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '3.6'
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: 3.6.0
|
133
|
+
description: Closh automatically bootstrap servers in the cloud to run shell commands
|
134
|
+
email: v@vinc.cc
|
135
|
+
executables:
|
136
|
+
- closh
|
137
|
+
extensions: []
|
138
|
+
extra_rdoc_files: []
|
139
|
+
files:
|
140
|
+
- LICENSE
|
141
|
+
- README.md
|
142
|
+
- bin/closh
|
143
|
+
- lib/closh.rb
|
144
|
+
- lib/closh/cli.rb
|
145
|
+
- lib/closh/session.rb
|
146
|
+
- lib/closh/version.rb
|
147
|
+
homepage: https://github.com/vinc/closh
|
148
|
+
licenses:
|
149
|
+
- MIT
|
150
|
+
metadata: {}
|
151
|
+
post_install_message:
|
152
|
+
rdoc_options: []
|
153
|
+
require_paths:
|
154
|
+
- lib
|
155
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
161
|
+
requirements:
|
162
|
+
- - ">"
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
version: 1.3.1
|
165
|
+
requirements: []
|
166
|
+
rubyforge_project:
|
167
|
+
rubygems_version: 2.6.11
|
168
|
+
signing_key:
|
169
|
+
specification_version: 4
|
170
|
+
summary: A shell running in the cloud
|
171
|
+
test_files: []
|