cabal 0.4.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/README.md +40 -20
- data/Rakefile +7 -0
- data/cabal.gemspec +2 -2
- data/exe/cabal +1 -1
- data/lib/cabal.rb +2 -1
- data/lib/cabal/cli.rb +34 -44
- data/lib/cabal/cli/authenticated.rb +27 -0
- data/lib/cabal/cli/key.rb +18 -0
- data/lib/cabal/cli/list.rb +24 -0
- data/lib/cabal/cli/main.rb +29 -0
- data/lib/cabal/cli/ssh.rb +46 -0
- data/lib/cabal/client.rb +39 -4
- data/lib/cabal/identity_manager.rb +19 -2
- data/lib/cabal/ssh.rb +65 -15
- data/lib/cabal/version.rb +1 -1
- metadata +13 -10
- data/lib/cabal/commands/key.rb +0 -32
- data/lib/cabal/commands/ssh.rb +0 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 54c08e765a34142602ac7933207bd41adb423cbf
|
4
|
+
data.tar.gz: d66b1d6dab67a5d5c91be6732a7e00551cf499ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6684c86512ddafaf29c7eb002dc87de2262f9c6e6a2b4bc3656cc3ba0a73acb4399485b0e8a17e8fb464300b56ff05ad4d4d2512ced8066b7d070ea142582060
|
7
|
+
data.tar.gz: e33cdbf4db6a8dc8fde6d86bb2093aa6c8e8e1b6596c96a8bebaaaee7f7a8655ba2a92a00b390db8a865eb492c03a3b5408258a2272ff13a3c21bd0830104be5
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -10,13 +10,13 @@ Cabal is a simple system for SSH key distribution and consumption. This is a CLI
|
|
10
10
|
|
11
11
|
## Usage ##
|
12
12
|
|
13
|
-
To get started, you'll need to create `.cabal.yml` in your user's home directory, and you'll need to provide at least the URL of your Cabal::API. If you plan to use the `ssh` command to connect to a server that has an authorized Cabal key, you'll also need to specify your access
|
13
|
+
To get started, you'll need to create `.cabal.yml` in your user's home directory, and you'll need to provide at least the URL of your Cabal::API. If you plan to use the `ssh` command to connect to a server that has an authorized Cabal key, you'll also need to specify your access token and secret token (provided by your Cabal::API administrator):
|
14
14
|
|
15
15
|
```yaml
|
16
16
|
---
|
17
17
|
:url: http://your-cabal-hostname/path/to/the/api
|
18
|
-
:access_key: your-access-
|
19
|
-
:secret_key: your-secret-
|
18
|
+
:access_key: your-access-token
|
19
|
+
:secret_key: your-secret-token
|
20
20
|
```
|
21
21
|
|
22
22
|
### Getting Help ###
|
@@ -24,38 +24,43 @@ To get started, you'll need to create `.cabal.yml` in your user's home directory
|
|
24
24
|
The `cabal` application provides some nicely-formatted help on the command line. To access the help, provided that you've created your `~/.cabal.yml`, run the following:
|
25
25
|
|
26
26
|
```
|
27
|
-
cabal help
|
27
|
+
cabal --help
|
28
28
|
```
|
29
29
|
|
30
30
|
That will output something like this:
|
31
31
|
|
32
32
|
```
|
33
33
|
NAME
|
34
|
-
|
34
|
+
cabal - An interface to the Cabal API
|
35
35
|
|
36
36
|
SYNOPSIS
|
37
|
-
|
37
|
+
cabal command
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
GLOBAL OPTIONS
|
43
|
-
--help - Show this message
|
44
|
-
--version - Display the program version
|
39
|
+
OPTIONS
|
40
|
+
-h, --help - Shows this message
|
45
41
|
|
46
42
|
COMMANDS
|
47
|
-
|
48
|
-
|
49
|
-
ssh - Connect to a node on a cluster
|
43
|
+
key - Get the public key for a cluster
|
44
|
+
ssh - Connect to a node on a cluster
|
50
45
|
```
|
51
46
|
|
52
|
-
To get help on a specific command, you can `cabal help
|
47
|
+
To get help on a specific command, you can `cabal command --help` (ie `cabal key --help`).
|
53
48
|
|
54
|
-
###
|
49
|
+
### Listing Known Clusters ###
|
50
|
+
|
51
|
+
***To learn more, check out `cabal list --help`***
|
52
|
+
|
53
|
+
This is a privileged call, so you need an access key and a secret key in your configuration file.
|
55
54
|
|
56
|
-
|
55
|
+
To get a list of the clusters that Cabal knows about, you'd do the following:
|
57
56
|
|
58
|
-
|
57
|
+
```
|
58
|
+
cabal list
|
59
|
+
```
|
60
|
+
|
61
|
+
### Getting A Public Key ###
|
62
|
+
|
63
|
+
***To learn more, check out `cabal key --help`***
|
59
64
|
|
60
65
|
To get a public key for a cluster named "totallyarealcluster," you'd do the following:
|
61
66
|
|
@@ -72,9 +77,20 @@ cabal key Totally a real clusteR
|
|
72
77
|
cabal key totallyarealcluster
|
73
78
|
```
|
74
79
|
|
80
|
+
This command is an alternative to hitting the API endpoint via curl (or similar) to grab a public key, which might be used when installing an authorized key on a server. The following example would append the key for "totallyarealcluster" to the current user's authorized_keys file if it wasn't already present:
|
81
|
+
|
82
|
+
```
|
83
|
+
if ! grep -q totallyarealcluster ~/.ssh/authorized_keys
|
84
|
+
then
|
85
|
+
cabal key totallyarealcluster >> ~/.ssh/authorized_keys
|
86
|
+
fi
|
87
|
+
```
|
88
|
+
|
89
|
+
This is an unprivileged call, so you won't actually need an access key or a secret key.
|
90
|
+
|
75
91
|
### Connecting To A Server ###
|
76
92
|
|
77
|
-
***To learn more, check out `cabal help
|
93
|
+
***To learn more, check out `cabal ssh --help`***
|
78
94
|
|
79
95
|
This is a privileged call, so you need an access key and a secret key in your configuration file.
|
80
96
|
|
@@ -91,6 +107,10 @@ This basically does the following:
|
|
91
107
|
* Connects you to the server, forwarding your ssh-agent
|
92
108
|
* Removes the key and **ALL** ssh-agent identities on disconnect
|
93
109
|
|
110
|
+
By default, the forwarded ssh-agent will only have access to the private key for 30 minutes. To change this, you can use the `--lifetime` option to specify a different lifetime for your session.
|
111
|
+
|
112
|
+
For tasks that may take a very long time (cluster upgrades, etc), you can pass a lifetime of `0` to keep the agent forwarding in place for the entirety of your session.
|
113
|
+
|
94
114
|
## Development ##
|
95
115
|
|
96
116
|
Branches and releases for this project are managed by [git-flow](https://github.com/nvie/gitflow).
|
data/Rakefile
CHANGED
data/cabal.gemspec
CHANGED
@@ -26,10 +26,10 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_development_dependency 'factis', '~> 1.0'
|
27
27
|
spec.add_development_dependency 'simplecov', '~> 0.10'
|
28
28
|
spec.add_development_dependency 'webmock', '~> 1.22'
|
29
|
-
spec.add_development_dependency '
|
29
|
+
spec.add_development_dependency 'toady', '~> 0.0'
|
30
30
|
|
31
31
|
spec.add_runtime_dependency 'cabal-util', '~> 0.1'
|
32
|
-
spec.add_runtime_dependency '
|
32
|
+
spec.add_runtime_dependency 'belafonte', '~> 0.5'
|
33
33
|
spec.add_runtime_dependency 'oj', '~> 2.12'
|
34
34
|
spec.add_runtime_dependency 'faraday', '~> 0.9'
|
35
35
|
end
|
data/exe/cabal
CHANGED
data/lib/cabal.rb
CHANGED
data/lib/cabal/cli.rb
CHANGED
@@ -1,60 +1,50 @@
|
|
1
|
-
require 'gli'
|
2
1
|
require 'yaml'
|
3
2
|
require 'cabal'
|
4
|
-
require 'cabal/
|
5
|
-
require 'cabal/
|
3
|
+
require 'cabal/cli/ssh'
|
4
|
+
require 'cabal/cli/key'
|
5
|
+
require 'cabal/cli/list'
|
6
|
+
require 'cabal/cli/main'
|
6
7
|
|
7
8
|
module Cabal
|
8
|
-
|
9
|
-
|
9
|
+
# Bootstrap and configure the CLI application
|
10
|
+
module CLI
|
11
|
+
# The expected config file does not exist
|
12
|
+
NoConfig = Class.new(StandardError)
|
10
13
|
|
11
|
-
|
14
|
+
# The config file does not contain a :url
|
15
|
+
NoURLDefinition = Class.new(StandardError)
|
12
16
|
|
13
|
-
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
def register_command(command)
|
18
|
-
command.bootstrap(self)
|
19
|
-
end
|
17
|
+
# The config file does not contain :access_key and :secret_key
|
18
|
+
NoPrivateConfig = Class.new(StandardError)
|
20
19
|
|
21
|
-
|
22
|
-
|
20
|
+
# Calculates the expected path of the Cabal config file
|
21
|
+
# @return [String] the config file path
|
22
|
+
def self.cabal_yml
|
23
|
+
File.expand_path(File.join(ENV['HOME'], '.cabal.yml'))
|
23
24
|
end
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
program_desc 'An interface to the Cabal API'
|
32
|
-
version Cabal::VERSION
|
33
|
-
subcommand_option_handling :normal
|
34
|
-
arguments :strict
|
35
|
-
|
36
|
-
pre do |global,command,options,args|
|
37
|
-
if File.exist?(cabal_yml)
|
38
|
-
config = YAML.load(File.read(cabal_yml))
|
39
|
-
global.merge!(config)
|
40
|
-
true
|
41
|
-
else
|
42
|
-
stderr.puts "Please create #{cabal_yml} and try again"
|
43
|
-
false
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
post do |global,command,options,args|
|
48
|
-
end
|
26
|
+
# Parses the config file and sets up the global configuration
|
27
|
+
# @return [nil]
|
28
|
+
def self.setup
|
29
|
+
if File.exist?(cabal_yml)
|
30
|
+
@config = YAML.load(File.read(cabal_yml))
|
49
31
|
|
50
|
-
|
51
|
-
|
32
|
+
raise NoURLDefinition.new("#{cabal_yml} must contain a :url: definition") unless config[:url]
|
33
|
+
else
|
34
|
+
raise NoConfig.new("Please create #{cabal_yml} and try again")
|
52
35
|
end
|
36
|
+
end
|
53
37
|
|
54
|
-
|
55
|
-
|
38
|
+
# The global config for the CLI
|
39
|
+
# @return [Hash] the current configuration
|
40
|
+
def self.config
|
41
|
+
@config ||= {}
|
42
|
+
end
|
56
43
|
|
57
|
-
|
44
|
+
# Clears the global configuration
|
45
|
+
# @return [nil]
|
46
|
+
def self.reset
|
47
|
+
@config = {}
|
58
48
|
end
|
59
49
|
end
|
60
50
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'belafonte'
|
2
|
+
require 'cabal/cli'
|
3
|
+
|
4
|
+
module Cabal
|
5
|
+
module CLI
|
6
|
+
module Authenticated
|
7
|
+
def with_authentication(config, &block)
|
8
|
+
begin
|
9
|
+
unless config[:access_key] && config[:secret_key]
|
10
|
+
raise Cabal::CLI::NoPrivateConfig
|
11
|
+
end
|
12
|
+
|
13
|
+
block.call
|
14
|
+
|
15
|
+
rescue Cabal::CLI::NoPrivateConfig
|
16
|
+
stderr.puts "You must have an access key and a secret key"
|
17
|
+
rescue Cabal::Client::UnauthorizedError
|
18
|
+
stderr.puts "You are not authorized to use the private API."
|
19
|
+
kernel.exit(1)
|
20
|
+
rescue Cabal::Client::NotFoundError
|
21
|
+
stderr.puts "The cluster '#{option(:cluster_name)}' does not exist."
|
22
|
+
kernel.exit(1)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'belafonte'
|
2
|
+
require 'cabal/cli'
|
3
|
+
|
4
|
+
module Cabal
|
5
|
+
module CLI
|
6
|
+
class Key < Belafonte::App
|
7
|
+
title 'key'
|
8
|
+
summary 'Get the public key for a cluster'
|
9
|
+
|
10
|
+
arg :cluster_name,
|
11
|
+
times: :unlimited
|
12
|
+
|
13
|
+
def handle
|
14
|
+
stdout.puts partake(:client).public_key(arg(:cluster_name).join(' '))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'belafonte'
|
2
|
+
require 'cabal/cli'
|
3
|
+
require 'cabal/cli/authenticated'
|
4
|
+
|
5
|
+
module Cabal
|
6
|
+
module CLI
|
7
|
+
class List < Belafonte::App
|
8
|
+
include Authenticated
|
9
|
+
|
10
|
+
title 'list'
|
11
|
+
summary 'List all known clusters'
|
12
|
+
description "Displays the list of all known clusters, one line at a time"
|
13
|
+
|
14
|
+
arg :cluster_name,
|
15
|
+
times: :unlimited
|
16
|
+
|
17
|
+
def handle
|
18
|
+
with_authentication(partake(:config)) do
|
19
|
+
stdout.puts partake(:client).clusters.join("\n")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'cabal/cli/key'
|
2
|
+
require 'cabal/cli/list'
|
3
|
+
require 'cabal/cli/ssh'
|
4
|
+
|
5
|
+
module Cabal
|
6
|
+
module CLI
|
7
|
+
class Main < Belafonte::App
|
8
|
+
title 'cabal'
|
9
|
+
summary 'An interface to the Cabal API'
|
10
|
+
|
11
|
+
mount Cabal::CLI::Key
|
12
|
+
mount Cabal::CLI::List
|
13
|
+
mount Cabal::CLI::SSH
|
14
|
+
|
15
|
+
def setup
|
16
|
+
Cabal::CLI.setup
|
17
|
+
config = Cabal::CLI.config
|
18
|
+
client = Cabal::Client.new(
|
19
|
+
api_base: config[:url],
|
20
|
+
access_key: config[:access_key],
|
21
|
+
secret_key: config[:secret_key]
|
22
|
+
)
|
23
|
+
|
24
|
+
share(:config, config)
|
25
|
+
share(:client, client)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'belafonte'
|
2
|
+
require 'cabal/cli'
|
3
|
+
require 'cabal/cli/authenticated'
|
4
|
+
require 'cabal/ssh'
|
5
|
+
|
6
|
+
module Cabal
|
7
|
+
module CLI
|
8
|
+
class SSH < Belafonte::App
|
9
|
+
include Authenticated
|
10
|
+
|
11
|
+
title "ssh"
|
12
|
+
summary "Connect to a node on a cluster"
|
13
|
+
|
14
|
+
option :cluster_name,
|
15
|
+
short: 'c',
|
16
|
+
long: 'cluster-name',
|
17
|
+
argument: 'CLUSTER_NAME',
|
18
|
+
description: 'The name of the cluster to connect'
|
19
|
+
|
20
|
+
option :lifetime,
|
21
|
+
short: 'l',
|
22
|
+
long: 'lifetime',
|
23
|
+
argument: 'MINUTES',
|
24
|
+
description: 'How long should we forward SSH identities (default: 30, 0 for no timeout)'
|
25
|
+
|
26
|
+
arg :ssh_signature
|
27
|
+
|
28
|
+
def handle
|
29
|
+
with_authentication(partake(:config)) do
|
30
|
+
Cabal::SSH.connect(
|
31
|
+
cluster: option(:cluster_name),
|
32
|
+
ssh_signature: arg(:ssh_signature).first,
|
33
|
+
client: partake(:client),
|
34
|
+
identity_timeout: lifetime,
|
35
|
+
kernel: kernel
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def lifetime
|
41
|
+
return nil unless option(:lifetime)
|
42
|
+
option(:lifetime).to_i * 60
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/cabal/client.rb
CHANGED
@@ -2,11 +2,27 @@ require 'faraday'
|
|
2
2
|
require 'oj'
|
3
3
|
|
4
4
|
module Cabal
|
5
|
+
# A client interface for an upstream Cabal::API
|
5
6
|
class Client
|
7
|
+
# The requested upstream resource was not found
|
8
|
+
NotFoundError = Class.new(StandardError)
|
9
|
+
|
10
|
+
# The user is not authorized to use a given upstream resource
|
11
|
+
UnauthorizedError = Class.new(StandardError)
|
12
|
+
|
13
|
+
# @return [String] the base URL for the v3 API
|
6
14
|
attr_reader :api_base
|
7
15
|
|
16
|
+
# Create a new client instance
|
17
|
+
# @param options [Hash]
|
18
|
+
# @option options [String] :api_base the base URL for the upstream API
|
19
|
+
# (required)
|
20
|
+
# @option options [String] :access_key the access key to use for
|
21
|
+
# authenticated API endpoints (optional)
|
22
|
+
# @option options [String] :secret_key the secret key to use for
|
23
|
+
# authenticated API endpoints (optional)
|
8
24
|
def initialize(options = {})
|
9
|
-
@api_base = "#{options[:api_base]}/
|
25
|
+
@api_base = "#{options[:api_base]}/v3"
|
10
26
|
@access_key = options[:access_key]
|
11
27
|
@secret_key = options[:secret_key]
|
12
28
|
@connector = Faraday.new(url: api_base) do |faraday|
|
@@ -15,15 +31,34 @@ module Cabal
|
|
15
31
|
end
|
16
32
|
end
|
17
33
|
|
34
|
+
# Retrieves a list of cluster names from the upstream API
|
35
|
+
# @return [Array<String>] the names of the known clusters
|
36
|
+
def clusters
|
37
|
+
get("clusters").map {|cluster| cluster['name']}
|
38
|
+
end
|
39
|
+
|
40
|
+
# Retrieves a private SSH key from the upstream API
|
41
|
+
# @param cluster_name [String] the name of the cluster
|
42
|
+
# @return [String] the contents of the cluster's private key
|
18
43
|
def private_key(cluster_name)
|
19
44
|
get("private-key/#{URI.encode(cluster_name)}")['private_ssh_key']
|
20
45
|
end
|
21
46
|
|
47
|
+
# Retrieves a public SSH key from teh upstream API
|
48
|
+
# @param cluster_name [String] the name of the cluster
|
49
|
+
# @return [String] the contents of the cluster's public key
|
22
50
|
def public_key(cluster_name)
|
23
51
|
get("key/#{URI.encode(cluster_name)}")['public_ssh_key']
|
24
52
|
end
|
25
53
|
|
26
|
-
|
54
|
+
# Does an HTTP GET against the upstream API for the provided endpoint
|
55
|
+
# @param endpoint [String] the relative API path to GET
|
56
|
+
# @return [Object] the parsed JSON response
|
57
|
+
# @raise [Cabal::Client::NotFoundError] if the upstream API returns a
|
58
|
+
# 404 status
|
59
|
+
# @raise [Cabal::Client::UnauthorizedError] if the upstream API retirns
|
60
|
+
# a 401 status
|
61
|
+
def get(endpoint)
|
27
62
|
response = connector.get {|req|
|
28
63
|
req.url endpoint
|
29
64
|
req.headers['Content-Type'] = 'application/json'
|
@@ -34,9 +69,9 @@ module Cabal
|
|
34
69
|
when 200
|
35
70
|
Oj.load(response.body)
|
36
71
|
when 404
|
37
|
-
raise
|
72
|
+
raise Cabal::Client::NotFoundError
|
38
73
|
when 401
|
39
|
-
raise
|
74
|
+
raise Cabal::Client::UnauthorizedError
|
40
75
|
end
|
41
76
|
end
|
42
77
|
|
@@ -1,15 +1,32 @@
|
|
1
1
|
module Cabal
|
2
|
+
# Manages ssh-agent sessions for forwarding identity information via
|
3
|
+
# Cabal::SSH
|
2
4
|
class IdentityManager
|
5
|
+
|
6
|
+
# @return [String] the environment settings for the running ssh-agent
|
3
7
|
attr_reader :env
|
4
8
|
|
9
|
+
# Spawns an ssh-agent process and records its related environment settings
|
10
|
+
# @return [nil]
|
5
11
|
def start
|
6
|
-
@env =
|
12
|
+
@env = start_agent.
|
7
13
|
split(/(\;|\n)/).
|
8
|
-
select {|
|
14
|
+
select {|line| line =~ /^SSH_/}.
|
9
15
|
join(' ')
|
10
16
|
end
|
11
17
|
|
18
|
+
# Stops a running ssh-agent that was previously spawned
|
19
|
+
# @return [nil]
|
12
20
|
def stop
|
21
|
+
stop_agent
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def start_agent
|
26
|
+
`ssh-agent`
|
27
|
+
end
|
28
|
+
|
29
|
+
def stop_agent
|
13
30
|
`#{env} ssh-agent -k`
|
14
31
|
end
|
15
32
|
end
|
data/lib/cabal/ssh.rb
CHANGED
@@ -7,37 +7,79 @@ require 'uri'
|
|
7
7
|
require 'fileutils'
|
8
8
|
|
9
9
|
module Cabal
|
10
|
+
# Generates SSH sessions for remote server interaction
|
10
11
|
class SSH
|
11
|
-
|
12
|
-
|
12
|
+
# Create a new instance and use it to generate an SSH session
|
13
|
+
# @param options [Hash] the options to pass along to the new instance
|
14
|
+
# @option options [String] :cluster the name of the cluster key to
|
15
|
+
# use (required)
|
16
|
+
# @option options [String] :ssh_signature the user@hostname
|
17
|
+
# that would be passed if the normal ssh CLI utility were used (required)
|
18
|
+
# @option options [Cabal::Client] :client the instance of
|
19
|
+
# Cabal::Client to use for key retrieval (required)
|
20
|
+
# @option options [Integer] :identity_timeout the number of seconds
|
21
|
+
# that keys should be forwarded in the resulting SSH session (optional)
|
22
|
+
# @option options [Kernel] :kernel the Kernel-compliant object used for
|
23
|
+
# issuing calls to #system for the SSH session (optional)
|
24
|
+
# @return [nil]
|
25
|
+
def self.connect(options = {})
|
26
|
+
new(
|
27
|
+
options[:cluster],
|
28
|
+
options[:ssh_signature],
|
29
|
+
options[:client],
|
30
|
+
options[:identity_timeout] || 1800,
|
31
|
+
options[:kernel] || Kernel
|
32
|
+
).connect
|
13
33
|
end
|
14
34
|
|
15
|
-
def initialize(cluster, ssh_signature, client)
|
35
|
+
def initialize(cluster, ssh_signature, client, identity_timeout, kernel)
|
16
36
|
@cluster = cluster
|
17
37
|
@ssh_signature = ssh_signature
|
18
38
|
@client = client
|
19
|
-
@
|
39
|
+
@identity_timeout = identity_timeout
|
40
|
+
@kernel = kernel
|
41
|
+
end
|
42
|
+
|
43
|
+
# The lifetime option that would be passed to ssh-add for this session
|
44
|
+
# @return [String] the empty string for immortal sessions,
|
45
|
+
# "-t identity_timeout" for sessions with a defined lifetime
|
46
|
+
def lifetime
|
47
|
+
return '' if identity_timeout == 0
|
48
|
+
"-t #{identity_timeout}"
|
20
49
|
end
|
21
50
|
|
51
|
+
# Initiate an SSH connection in the current terminal
|
52
|
+
# @return [nil]
|
22
53
|
def connect
|
54
|
+
begin
|
55
|
+
write_private_key
|
56
|
+
initiate_connection
|
57
|
+
ensure
|
58
|
+
terminate_connection
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def write_private_key
|
23
64
|
private_key = client.private_key(cluster)
|
24
65
|
@key_file = Tempfile.new(SecureRandom.hex(8))
|
25
66
|
key_file.write(private_key)
|
26
67
|
key_file.close
|
68
|
+
FileUtils.chmod(0700, key_file.path)
|
69
|
+
end
|
27
70
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
71
|
+
def initiate_connection
|
72
|
+
agent.start
|
73
|
+
kernel.system("#{agent.env} ssh-add #{lifetime} #{key_file.path} > /dev/null 2>&1")
|
74
|
+
kernel.system("#{agent.env} ssh -A #{ssh_signature}")
|
75
|
+
end
|
76
|
+
|
77
|
+
def terminate_connection
|
78
|
+
kernel.system("#{agent.env} ssh-add -D > /dev/null 2>&1")
|
79
|
+
agent.stop
|
80
|
+
key_file.unlink unless key_file.nil?
|
38
81
|
end
|
39
82
|
|
40
|
-
private
|
41
83
|
def key_file
|
42
84
|
@key_file
|
43
85
|
end
|
@@ -57,5 +99,13 @@ module Cabal
|
|
57
99
|
def agent
|
58
100
|
@agent ||= Cabal::IdentityManager.new
|
59
101
|
end
|
102
|
+
|
103
|
+
def identity_timeout
|
104
|
+
@identity_timeout
|
105
|
+
end
|
106
|
+
|
107
|
+
def kernel
|
108
|
+
@kernel
|
109
|
+
end
|
60
110
|
end
|
61
111
|
end
|
data/lib/cabal/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cabal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dennis Walters
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-02-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -123,19 +123,19 @@ dependencies:
|
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '1.22'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
126
|
+
name: toady
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
129
|
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: '0.
|
131
|
+
version: '0.0'
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: '0.
|
138
|
+
version: '0.0'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: cabal-util
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -151,19 +151,19 @@ dependencies:
|
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: '0.1'
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
|
-
name:
|
154
|
+
name: belafonte
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
156
156
|
requirements:
|
157
157
|
- - "~>"
|
158
158
|
- !ruby/object:Gem::Version
|
159
|
-
version: '
|
159
|
+
version: '0.5'
|
160
160
|
type: :runtime
|
161
161
|
prerelease: false
|
162
162
|
version_requirements: !ruby/object:Gem::Requirement
|
163
163
|
requirements:
|
164
164
|
- - "~>"
|
165
165
|
- !ruby/object:Gem::Version
|
166
|
-
version: '
|
166
|
+
version: '0.5'
|
167
167
|
- !ruby/object:Gem::Dependency
|
168
168
|
name: oj
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -213,9 +213,12 @@ files:
|
|
213
213
|
- exe/cabal
|
214
214
|
- lib/cabal.rb
|
215
215
|
- lib/cabal/cli.rb
|
216
|
+
- lib/cabal/cli/authenticated.rb
|
217
|
+
- lib/cabal/cli/key.rb
|
218
|
+
- lib/cabal/cli/list.rb
|
219
|
+
- lib/cabal/cli/main.rb
|
220
|
+
- lib/cabal/cli/ssh.rb
|
216
221
|
- lib/cabal/client.rb
|
217
|
-
- lib/cabal/commands/key.rb
|
218
|
-
- lib/cabal/commands/ssh.rb
|
219
222
|
- lib/cabal/identity_manager.rb
|
220
223
|
- lib/cabal/ssh.rb
|
221
224
|
- lib/cabal/version.rb
|
data/lib/cabal/commands/key.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
module Cabal
|
2
|
-
module Commands
|
3
|
-
module Key
|
4
|
-
def self.bootstrap(obj)
|
5
|
-
obj.instance_eval do
|
6
|
-
desc 'Get the public key for a cluster'
|
7
|
-
long_desc <<-KEYDESC
|
8
|
-
In order to allow logins for Cabal users, you'll need to retrieve
|
9
|
-
a public SSH key for your cluster.
|
10
|
-
KEYDESC
|
11
|
-
|
12
|
-
arg 'cluster name'
|
13
|
-
command :key do |c|
|
14
|
-
c.action do |global_options, options, args|
|
15
|
-
unless global_options[:url]
|
16
|
-
stderr.puts "#{cabal_yml} must contain a :url: definition"
|
17
|
-
exit(1)
|
18
|
-
end
|
19
|
-
|
20
|
-
client = Cabal::Client.new(
|
21
|
-
api_base: global_options[:url],
|
22
|
-
access_key: global_options[:access_key],
|
23
|
-
secret_key: global_options[:secret_key],
|
24
|
-
)
|
25
|
-
stdout.puts client.public_key(args.join(' '))
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
data/lib/cabal/commands/ssh.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
require 'cabal/ssh'
|
2
|
-
|
3
|
-
module Cabal
|
4
|
-
module Commands
|
5
|
-
module SSH
|
6
|
-
def self.bootstrap(obj)
|
7
|
-
obj.instance_eval do
|
8
|
-
desc 'Connect to a node on a cluster'
|
9
|
-
long_desc <<-SSHDESC
|
10
|
-
SSHDESC
|
11
|
-
|
12
|
-
arg 'ssh signature'
|
13
|
-
command :ssh do |c|
|
14
|
-
c.desc 'The name of the cluster'
|
15
|
-
c.arg_name 'cluster'
|
16
|
-
c.flag [:c, :cluster]
|
17
|
-
c.action do |global_options, options, args|
|
18
|
-
unless global_options[:access_key] && global_options[:secret_key]
|
19
|
-
stderr.puts "You must have an access key and secret key"
|
20
|
-
exit(1)
|
21
|
-
end
|
22
|
-
|
23
|
-
client = Cabal::Client.new(
|
24
|
-
api_base: global_options[:url],
|
25
|
-
access_key: global_options[:access_key],
|
26
|
-
secret_key: global_options[:secret_key]
|
27
|
-
)
|
28
|
-
|
29
|
-
Cabal::SSH.connect(options[:cluster], args.shift, client)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|