puter 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/NOTES.md +54 -0
- data/README.md +124 -0
- data/Rakefile +4 -0
- data/bin/puter +5 -0
- data/examples/Puterfile +25 -0
- data/examples/localfile.txt +1 -0
- data/examples/localfile_with_a_really_really_longname.txt +1 -0
- data/lib/puter.rb +23 -0
- data/lib/puter/backend/ssh.rb +69 -0
- data/lib/puter/cli.rb +57 -0
- data/lib/puter/cli/aws.rb +18 -0
- data/lib/puter/cli/vm.rb +167 -0
- data/lib/puter/providers/vm.rb +107 -0
- data/lib/puter/puterfile.rb +198 -0
- data/lib/puter/ui.rb +24 -0
- data/lib/puter/version.rb +3 -0
- data/puter.gemspec +30 -0
- data/spec/cli/vm_spec.rb +0 -0
- data/spec/fixtures/Puterfile +3 -0
- data/spec/fixtures/Puterfile.bad +4 -0
- data/spec/providers/vm_spec.rb +16 -0
- data/spec/puterfile/parse_operation_spec.rb +162 -0
- data/spec/puterfile/parse_spec.rb +24 -0
- data/spec/spec_helper.rb +19 -0
- metadata +195 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fd5bc41c2b68e84e15dae9f1944aea76afdf75af
|
4
|
+
data.tar.gz: b6651d0335a77c943ce1586b8841863218a4c8c5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 11708fe516cecaa0344435886ee282af29e34ff93aafd4726c12a69acd0c5cfb6cbeca9084c06485a417b2e7686aca21b2c934a9ac7c8d9667d5e3bb11a24817
|
7
|
+
data.tar.gz: 7cc8e3654e93c4cc889ac8a4c047aadb248c93deae086d01f56d715fe1f26756d341aa93331eb41e1679a5ac5f443e15aa98e96055efa317a5efb1ad7316649f
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 TODO: Write your name
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/NOTES.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
## What's been implemented
|
2
|
+
|
3
|
+
VMware provider, using the vmonkey and rbvmomi APIs.
|
4
|
+
Puterfile instructions: FROM, RUN, and COPY
|
5
|
+
puter vm sub-commands: images, build, rmi, ps, create, start, stop, rm, apply
|
6
|
+
|
7
|
+
## What needs to be implemented
|
8
|
+
|
9
|
+
### Enhance puter sub-commands
|
10
|
+
|
11
|
+
`ps` - detailed output, currently only names are output.
|
12
|
+
`images` - detailed output, currently only names are output.
|
13
|
+
|
14
|
+
### Implement puter sub-commands
|
15
|
+
|
16
|
+
`version` -
|
17
|
+
`run` - convenience for create & start
|
18
|
+
`restart` - guest OS restart
|
19
|
+
`exec` - run a command on the guest OS
|
20
|
+
`inspect` - detailed metadata of an image/instance
|
21
|
+
`implode` - completely remove all Puter images, instances, and provider-specific metadata
|
22
|
+
|
23
|
+
`tag` - maybe?
|
24
|
+
`import` - maybe? dups an existing non-Puter image (template, ami) into the set of Puter Images
|
25
|
+
`export` - maybe? dups an existing Puter Image to a non-Puter image (template, ami)
|
26
|
+
|
27
|
+
|
28
|
+
### Implement Puterfile instructions
|
29
|
+
|
30
|
+
`MAINTAINER`
|
31
|
+
`USER`
|
32
|
+
`ENV`
|
33
|
+
`VOLUME`
|
34
|
+
`ADD`
|
35
|
+
`EXPOSE`
|
36
|
+
`ONBUILD`
|
37
|
+
|
38
|
+
### Implement an AWS provider
|
39
|
+
|
40
|
+
|
41
|
+
## VMware provider
|
42
|
+
|
43
|
+
### VMware Puter structure within a Datacenter
|
44
|
+
|
45
|
+
<datacenter>/ # VMonkey config specifies the specific Datacenter to operate from
|
46
|
+
vmFolder/ #
|
47
|
+
Puter/ # top-level vSphere Folder - 'puter vm init' creates this folder
|
48
|
+
Build/ # this folder is used by 'puter vm build' as a working folder
|
49
|
+
Images/ # this folder is where Puter Images (VM templates) are stored
|
50
|
+
Instances/ # this folder is where Puter Images (running and stopped VMs) are stored
|
51
|
+
|
52
|
+
## AWS Puter Structure, within an EC2 Region
|
53
|
+
|
54
|
+
tbd
|
data/README.md
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
# Puter: provision VMs in a Docker-like way
|
2
|
+
|
3
|
+
Puter is a tool to quickly and easily provision virtual machine images and
|
4
|
+
instances directly on virtualization and cloud providers.
|
5
|
+
|
6
|
+
Using a Puterfile syntax that closely resembles Dockerfile syntax, Puter makes
|
7
|
+
it easy and familiar to create new base VM images.
|
8
|
+
|
9
|
+
### Example Puterfile
|
10
|
+
|
11
|
+
## Usage Example
|
12
|
+
Given a repo with the following structure:
|
13
|
+
|
14
|
+
example/
|
15
|
+
|-- Puterfile
|
16
|
+
|-- localfile.txt
|
17
|
+
|-- localfile_with_a_really_really_longname.txt
|
18
|
+
|
19
|
+
### Puterfile
|
20
|
+
FROM myorg.tld/baseos
|
21
|
+
# Start with any base image on your virtualization provider
|
22
|
+
|
23
|
+
# Run some simple commands
|
24
|
+
RUN touch /tmp/puter.txt
|
25
|
+
RUN userdel -f puter1; useradd puter1
|
26
|
+
RUN sudo -u puter1 whoami
|
27
|
+
|
28
|
+
# Copy some files to the new machine
|
29
|
+
COPY localfile.txt /tmp/explicit_name.txt
|
30
|
+
|
31
|
+
# Extend long lines with \ syntax
|
32
|
+
COPY localfile_with_a_really_really_longname.txt \
|
33
|
+
/home/puter1/
|
34
|
+
|
35
|
+
# Don't fear shell redirection and special characters
|
36
|
+
RUN touch /tmp/puter.txt && \
|
37
|
+
echo more commands > /tmp/puter.txt && \
|
38
|
+
echo done
|
39
|
+
RUN echo puter1 >> /tmp/puter.txt
|
40
|
+
|
41
|
+
# output from long running commands is visible along the way
|
42
|
+
RUN for i in `seq 1 5`; do echo $i; sleep 1; done;
|
43
|
+
|
44
|
+
RUN echo complete
|
45
|
+
|
46
|
+
|
47
|
+
### Build an image
|
48
|
+
$ puter vm build myorg.tld/puterWithStuff examples
|
49
|
+
Building '/Puter/Images/myorg.tld/puterWithStuff' FROM '/Puter/Images/c65.small'
|
50
|
+
Waiting for SSH
|
51
|
+
Applying '/Users/pairing/projects/puter/examples/Puterfile' to '/Puter/Build/myorg.tld/puterWithStuff' at 10.0.0.123
|
52
|
+
Step 0 : FROM myorg.tld/baseos
|
53
|
+
Step 1 : RUN touch /tmp/puter.txt
|
54
|
+
Step 2 : RUN userdel -f puter1; useradd puter1
|
55
|
+
userdel: user 'puter1' does not exist
|
56
|
+
Step 3 : RUN sudo -u puter1 whoami
|
57
|
+
puter1
|
58
|
+
Step 4 : COPY localfile.txt /tmp/explicit_name.txt
|
59
|
+
Step 5 : COPY localfile_with_a_really_really_longname.txt /home/puter1/
|
60
|
+
Step 6 : RUN touch /tmp/puter.txt && echo more commands > /tmp/puter.txt && echo done
|
61
|
+
done
|
62
|
+
Step 7 : RUN echo puter1 >> /tmp/puter.txt
|
63
|
+
Step 8 : RUN for i in `seq 1 5`; do echo $i; sleep 1; done;
|
64
|
+
1
|
65
|
+
2
|
66
|
+
3
|
67
|
+
4
|
68
|
+
5
|
69
|
+
Step 9 : RUN echo complete
|
70
|
+
complete
|
71
|
+
Stopping '/Puter/Build/myorg.tld/puterWithStuff' and moving to '/Puter/Images/myorg.tld/puterWithStuff'
|
72
|
+
Successfully built 'myorg.tld/puterWithStuff'
|
73
|
+
|
74
|
+
### Build an image
|
75
|
+
|
76
|
+
|
77
|
+
### Create and start an instance
|
78
|
+
$ puter vm create myorg.tld/puterWithStuff puter1
|
79
|
+
Created instance '/Puter/Instances/puter1' from '/Puter/Images/myorg.tld/puterWithStuff'
|
80
|
+
|
81
|
+
$ puter vm start puter1
|
82
|
+
Starting instance '/Puter/Instances/puter1', waiting for SSH...
|
83
|
+
Started '/Puter/Instances/puter1' at 10.32.30.126.
|
84
|
+
|
85
|
+
|
86
|
+
### Virtualization providers
|
87
|
+
Currently only VMware vSphere is supported.
|
88
|
+
Amazon AWS support is planned.
|
89
|
+
|
90
|
+
## Installation
|
91
|
+
|
92
|
+
Add this line to your application's Gemfile:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
gem 'puter'
|
96
|
+
```
|
97
|
+
|
98
|
+
And then execute:
|
99
|
+
|
100
|
+
$ bundle
|
101
|
+
|
102
|
+
Or install it yourself as:
|
103
|
+
|
104
|
+
$ gem install puter
|
105
|
+
|
106
|
+
## Usage
|
107
|
+
|
108
|
+
$ puter help
|
109
|
+
Commands:
|
110
|
+
puter help [COMMAND] # Describe available commands or one specific command
|
111
|
+
puter version # Display puter version.
|
112
|
+
puter vm # VMware vSphere related tasks. Type puter vm for more help.
|
113
|
+
|
114
|
+
Options:
|
115
|
+
[--version], [--no-version] # Show program version
|
116
|
+
|
117
|
+
|
118
|
+
## Contributing
|
119
|
+
|
120
|
+
1. Fork it ( https://github.com/[my-github-username]/puter/fork )
|
121
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
122
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
123
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
124
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/puter
ADDED
data/examples/Puterfile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
FROM c65.small
|
2
|
+
# Start with any base image on your virtualization provider
|
3
|
+
|
4
|
+
# Run some simple commands
|
5
|
+
RUN touch /tmp/puter.txt
|
6
|
+
RUN userdel -f puter1; useradd puter1
|
7
|
+
RUN sudo -u puter1 whoami
|
8
|
+
|
9
|
+
# Copy some files to the new machine
|
10
|
+
COPY localfile.txt /tmp/explicit_name.txt
|
11
|
+
|
12
|
+
# Extend long lines with \ syntax
|
13
|
+
COPY localfile_with_a_really_really_longname.txt \
|
14
|
+
/home/puter1/
|
15
|
+
|
16
|
+
# Don't fear shell redirection and special characters
|
17
|
+
RUN touch /tmp/puter.txt && \
|
18
|
+
echo more commands > /tmp/puter.txt && \
|
19
|
+
echo done
|
20
|
+
RUN echo puter1 >> /tmp/puter.txt
|
21
|
+
|
22
|
+
# output from long running commands is visible along the way
|
23
|
+
RUN for i in `seq 1 5`; do echo $i; sleep 1; done;
|
24
|
+
|
25
|
+
RUN echo complete
|
@@ -0,0 +1 @@
|
|
1
|
+
from the Puter context
|
@@ -0,0 +1 @@
|
|
1
|
+
from the Puter context
|
data/lib/puter.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Puter
|
4
|
+
autoload :CLI, 'puter/cli'
|
5
|
+
autoload :UI, 'puter/ui'
|
6
|
+
|
7
|
+
Thor::Base.shell.send(:include, Puter::UI)
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def root
|
11
|
+
@root ||= Pathname.new(File.expand_path('../', File.dirname(__FILE__)))
|
12
|
+
end
|
13
|
+
|
14
|
+
def executable_name
|
15
|
+
File.basename($PROGRAM_NAME)
|
16
|
+
end
|
17
|
+
|
18
|
+
def ui
|
19
|
+
@ui ||= Thor::Base.shell.new
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
require 'net/scp'
|
3
|
+
|
4
|
+
module Puter
|
5
|
+
module Backend
|
6
|
+
class SshError < Exception
|
7
|
+
end
|
8
|
+
|
9
|
+
# TODO - pass in a logger, so that stdout and stderr can stream out directly?
|
10
|
+
|
11
|
+
class Ssh
|
12
|
+
def initialize(host, ssh_opts = {})
|
13
|
+
@ssh = Net::SSH.start( host, ssh_opts[:user] || 'root', ssh_opts )
|
14
|
+
@scp = Net::SCP.new(@ssh)
|
15
|
+
end
|
16
|
+
|
17
|
+
def run(command, user='root', opts={}, &output)
|
18
|
+
actual_cmd = "sudo -u #{user} -s -- sh -c '#{command}'"
|
19
|
+
stdout_data = ''
|
20
|
+
stderr_data = ''
|
21
|
+
exit_status = nil
|
22
|
+
exit_signal = nil
|
23
|
+
|
24
|
+
@ssh.open_channel do |channel|
|
25
|
+
# TODO - make request_pty user controllable:
|
26
|
+
# without a pty, RHEL default configs fail to allow sudo
|
27
|
+
# with a pty, openssh comingles stdout and stderr onto stdout
|
28
|
+
channel.request_pty do |ch, success|
|
29
|
+
raise SshError.new "Could not obtain SSH pty " if !success
|
30
|
+
end
|
31
|
+
|
32
|
+
channel.exec(actual_cmd) do |ch, success|
|
33
|
+
raise SshError.new "Could not execute command [ #{actual_cmd} ]" if !success
|
34
|
+
channel.on_data do |ch, data|
|
35
|
+
if output
|
36
|
+
output.call(:stdout, data) if output
|
37
|
+
else
|
38
|
+
stdout_data += data
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
channel.on_extended_data do |ch, type, data|
|
43
|
+
if output
|
44
|
+
output.call(:stderr, data) if output
|
45
|
+
else
|
46
|
+
stderr_data += data
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
channel.on_request("exit-status") do |ch, data|
|
51
|
+
exit_status = data.read_long
|
52
|
+
end
|
53
|
+
|
54
|
+
channel.on_request("exit-signal") do |ch, data|
|
55
|
+
exit_signal = data.read_long
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
@ssh.loop
|
60
|
+
{ :cmd => actual_cmd, :stdout => stdout_data, :stderr => stderr_data, :exit_status => exit_status, :exit_signal => exit_signal }
|
61
|
+
end
|
62
|
+
|
63
|
+
def copy(from, to)
|
64
|
+
@scp.upload! from, to
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/puter/cli.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'puter/version'
|
2
|
+
require 'puter/cli/vm'
|
3
|
+
require 'puter/cli/aws'
|
4
|
+
|
5
|
+
module Puter
|
6
|
+
module CLI
|
7
|
+
EXIT_CODE_ERR = 1
|
8
|
+
|
9
|
+
SSH_OPTS = {}
|
10
|
+
SSH_OPTS[:user] = ENV['SSH_USER'] || 'root'
|
11
|
+
SSH_OPTS[:password] = ENV['SSH_PASSWORD'] if ENV['SSH_PASSWORD']
|
12
|
+
SSH_OPTS[:port] = ENV['SSH_PORT'].to_i if ENV['SSH_PORT']
|
13
|
+
SSH_OPTS[:paranoid] = (ENV['SSH_PARANOID'] == '1') if ENV['SSH_PARANOID']
|
14
|
+
SSH_OPTS[:user_known_hosts_file] = ENV['SSH_KNOWN_HOSTS'] if ENV['SSH_KNOWN_HOSTS']
|
15
|
+
|
16
|
+
def self.run_cli(&block)
|
17
|
+
begin
|
18
|
+
block.call
|
19
|
+
rescue Puter::RunError => re
|
20
|
+
Puter.ui.error re.message
|
21
|
+
Puter.ui.error "[ #{re.cmd} ] returned [ #{re.exit_status} ]"
|
22
|
+
exit re.exit_status
|
23
|
+
rescue Exception => e
|
24
|
+
Puter.ui.error e.message
|
25
|
+
Puter.ui.error e.backtrace.join "\n"
|
26
|
+
exit EXIT_CODE_ERR
|
27
|
+
end
|
28
|
+
exit 0
|
29
|
+
end
|
30
|
+
|
31
|
+
class Cli < Thor
|
32
|
+
def self.exit_on_failure?
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
ALIASES = {
|
37
|
+
'-T' => 'help',
|
38
|
+
'--version' => 'version',
|
39
|
+
'-V' => 'version'
|
40
|
+
}
|
41
|
+
|
42
|
+
map ALIASES
|
43
|
+
|
44
|
+
desc 'vm', "VMware vSphere related tasks. Type #{Puter.executable_name} vm for more help."
|
45
|
+
subcommand 'vm', Vm
|
46
|
+
|
47
|
+
desc 'aws', "Amazon AWS related tasks. Type #{Puter.executable_name} aws for more help. NOT IMPLEMENTED."
|
48
|
+
subcommand 'aws', Aws
|
49
|
+
|
50
|
+
class_option :version, :type => :boolean, :desc => 'Show program version'
|
51
|
+
desc 'version', 'Display puter version.'
|
52
|
+
def version
|
53
|
+
Puter.ui.info "#{Puter.executable_name} #{Puter::VERSION}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Puter
|
2
|
+
module CLI
|
3
|
+
class Aws < Thor
|
4
|
+
desc 'images', 'Lists available puter images.'
|
5
|
+
long_desc <<-LONGDESC
|
6
|
+
Lists available puter images.
|
7
|
+
|
8
|
+
With --region option, lists images found in the given AWS region.
|
9
|
+
LONGDESC
|
10
|
+
option :region, :type => :string, :default => 'us-west-1', :banner => 'aws-region-name'
|
11
|
+
def images(region_name = options[:region])
|
12
|
+
CLI.run_cli do
|
13
|
+
raise 'NOT IMPLEMENTED'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|