puter 0.0.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/.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
|