cloud-sh 1.0.0
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/.github/workflows/gempush.yml +32 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +5 -0
- data/.travis.yml +7 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +30 -0
- data/LICENSE.txt +21 -0
- data/README.md +126 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/cloud-sh.gemspec +37 -0
- data/exe/cloud-sh +7 -0
- data/exe/kubetail +357 -0
- data/lib/cloud/sh/cli.rb +73 -0
- data/lib/cloud/sh/commands/base.rb +33 -0
- data/lib/cloud/sh/commands/k8s_exec.rb +88 -0
- data/lib/cloud/sh/commands/k8s_tail.rb +41 -0
- data/lib/cloud/sh/commands/refresh.rb +144 -0
- data/lib/cloud/sh/config.rb +106 -0
- data/lib/cloud/sh/helpers/commands.rb +55 -0
- data/lib/cloud/sh/providers/base.rb +42 -0
- data/lib/cloud/sh/providers/digital_ocean.rb +100 -0
- data/lib/cloud/sh/version.rb +7 -0
- data/lib/cloud/sh.rb +51 -0
- metadata +128 -0
@@ -0,0 +1,144 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cloud/sh/commands/base"
|
4
|
+
|
5
|
+
module Cloud
|
6
|
+
module Sh
|
7
|
+
module Commands
|
8
|
+
class Refresh < Base
|
9
|
+
attr_reader :aliases
|
10
|
+
|
11
|
+
def execute
|
12
|
+
Cloud::Sh::Providers::DigitalOcean.refresh_k8s_configs
|
13
|
+
build_aliases
|
14
|
+
save_aliases
|
15
|
+
end
|
16
|
+
|
17
|
+
def build_aliases
|
18
|
+
@aliases = {}
|
19
|
+
config.accounts.each do |account|
|
20
|
+
puts "Refreshing account #{account.name}"
|
21
|
+
build_ssh_aliases(account)
|
22
|
+
build_db_aliases(account)
|
23
|
+
build_k8s_aliases(account)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def save_aliases
|
28
|
+
print "Saving aliases ... "
|
29
|
+
aliases_text = aliases.map do |alias_name, cmd|
|
30
|
+
"alias #{alias_name}=\"#{cmd}\""
|
31
|
+
end.join("\n")
|
32
|
+
File.write(config.aliases_file, aliases_text)
|
33
|
+
puts "DONE"
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_ssh_aliases(account)
|
37
|
+
print " Refreshing SSH aliases ... "
|
38
|
+
provider = cloud_provider(account)
|
39
|
+
provider.servers do |server|
|
40
|
+
add_alias(:do, account, :ssh, server.name, "ssh #{server.ip}")
|
41
|
+
end
|
42
|
+
puts "DONE"
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_db_aliases(account)
|
46
|
+
print " Refreshing DB aliases ... "
|
47
|
+
provider = cloud_provider(account)
|
48
|
+
provider.databases do |database|
|
49
|
+
next if database.cluster.ignore
|
50
|
+
if database.cluster.engine == "pg"
|
51
|
+
add_alias(:do, account, :psql, database.cluster, database.name, "psql \\\"#{database.uri}\\\"")
|
52
|
+
add_alias(:do, account, :pgdump, database.cluster, database.name, pgdump_command(database))
|
53
|
+
add_alias(:do, account, :pgcli, database.cluster, database.name, "pgcli \\\"#{database.uri}\\\"")
|
54
|
+
elsif database.cluster.engine == "mysql"
|
55
|
+
add_alias(:do, account, :mysql, database.cluster, database.name, mysql_command(database))
|
56
|
+
add_alias(:do, account, :mysqldump, database.cluster, database.name, mysqldump_command(database))
|
57
|
+
add_alias(:do, account, :mycli, database.cluster, database.name, "pgcli \\\"#{database.uri}\\\"")
|
58
|
+
elsif database.cluster.engine == "redis"
|
59
|
+
add_alias(:do, account, :redis, database.cluster, database.name, "redli -u \\\"#{database.uri}\\\"")
|
60
|
+
else
|
61
|
+
puts "Don't know how to handle database engine #{database.cluster.engine}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
puts "DONE"
|
65
|
+
end
|
66
|
+
|
67
|
+
def build_k8s_aliases(account)
|
68
|
+
print " Refreshing K8S aliases ... "
|
69
|
+
add_alias(:k8s, account, :ctl, kubectl)
|
70
|
+
provider = cloud_provider(account)
|
71
|
+
provider.clusters do |cluster|
|
72
|
+
add_alias(:k8s, account, :switch, :to, cluster, kubectl("config use-context", cluster.context))
|
73
|
+
add_alias(:k8s, account, :ctl, cluster, kubectl("--context #{cluster.context}"))
|
74
|
+
cluster.pods.each do |namespace, pods|
|
75
|
+
add_alias(:k8s, account, :tail, namespace, :all, "cloud-sh k8s tail --context #{cluster.context} --namespace #{namespace}")
|
76
|
+
pods.each do |pod|
|
77
|
+
add_alias(:k8s, account, :tail, namespace, pod.name, "cloud-sh k8s tail --context #{cluster.context} --namespace #{namespace} --pod #{pod.name}") unless pod.name == "console"
|
78
|
+
add_alias(:k8s, account, :exec, namespace, pod.name, "cloud-sh k8s exec --context #{cluster.context} --namespace #{namespace} --pod #{pod.name}")
|
79
|
+
add_alias(:k8s, account, namespace, :rails, :console, "cloud-sh k8s exec --context #{cluster.context} --namespace #{namespace} --pod #{pod.name} --cmd 'bundle exec rails console'") if pod.name == "console"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
puts "DONE"
|
84
|
+
end
|
85
|
+
|
86
|
+
def kubectl(*parts)
|
87
|
+
[
|
88
|
+
"kubectl",
|
89
|
+
"--kubeconfig=#{Cloud::Sh::Providers::DigitalOcean.kube_config}",
|
90
|
+
parts
|
91
|
+
].flatten.join(" ")
|
92
|
+
end
|
93
|
+
|
94
|
+
def mysql_command(database)
|
95
|
+
uri = URI.parse(database.uri)
|
96
|
+
[ :mysql, mysql_connection_params(uri), uri.path.delete("/")].join(" ")
|
97
|
+
end
|
98
|
+
|
99
|
+
def mysqldump_command(database)
|
100
|
+
uri = URI.parse(database.uri)
|
101
|
+
dump_name = "#{database.db}-`date +%s`.sql"
|
102
|
+
[ :mysqldump, mysql_connection_params(uri), uri.path.delete("/"), "> #{dump_name}"].join(" ")
|
103
|
+
end
|
104
|
+
|
105
|
+
def mysql_connection_params(uri)
|
106
|
+
[
|
107
|
+
"--host=#{uri.host}",
|
108
|
+
"--user=#{uri.user}",
|
109
|
+
"--password=#{uri.password}",
|
110
|
+
"--port=#{uri.port}",
|
111
|
+
"--ssl-mode=REQUIRED"
|
112
|
+
].join(" ")
|
113
|
+
end
|
114
|
+
|
115
|
+
def pgdump_command(database)
|
116
|
+
dump_name = "#{database.db}-`date +%s`.sql"
|
117
|
+
"pg_dump \\\"#{database.uri}\\\" -f #{dump_name}"
|
118
|
+
end
|
119
|
+
|
120
|
+
def add_alias(*parts, cmd)
|
121
|
+
alias_name = parts.map { |part| normalize_alias_part(part) }.compact.join("-")
|
122
|
+
aliases[alias_name] = cmd
|
123
|
+
end
|
124
|
+
|
125
|
+
def normalize_alias_part(part)
|
126
|
+
return nil if part.respond_to?(:default) && part.default
|
127
|
+
if part.respond_to?(:alias)
|
128
|
+
part = part.alias
|
129
|
+
elsif part.respond_to?(:name)
|
130
|
+
part = part.name
|
131
|
+
elsif part.respond_to?(:to_s)
|
132
|
+
part = part.to_s
|
133
|
+
end
|
134
|
+
part.tr("._", "-")
|
135
|
+
end
|
136
|
+
|
137
|
+
def cloud_provider(account)
|
138
|
+
@cloud_providers ||= {}
|
139
|
+
@cloud_providers[account] ||= Cloud::Sh::Providers.build(account)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cloud
|
4
|
+
module Sh
|
5
|
+
class Config
|
6
|
+
attr_reader :accounts
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@accounts = []
|
10
|
+
read_config
|
11
|
+
end
|
12
|
+
|
13
|
+
def read_config
|
14
|
+
return unless File.exist?(config_file)
|
15
|
+
config = YAML.safe_load(File.read(config_file))
|
16
|
+
config.each do |account_config|
|
17
|
+
accounts << Account.new(account_config)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def config_file
|
22
|
+
File.expand_path(".config/cloud-sh.yml", "~")
|
23
|
+
end
|
24
|
+
|
25
|
+
def aliases_file
|
26
|
+
File.expand_path(".cloud_sh_aliases", "~/")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Account
|
31
|
+
attr_reader :name, :kind, :context, :default, :clusters, :databases
|
32
|
+
|
33
|
+
def initialize(config)
|
34
|
+
@name = config["name"]
|
35
|
+
@kind = config["kind"]
|
36
|
+
@context = config["context"]
|
37
|
+
@default = config.key?("default") && !!config["default"]
|
38
|
+
@clusters = []
|
39
|
+
@databases = []
|
40
|
+
load_clusters(config)
|
41
|
+
load_databases(config)
|
42
|
+
end
|
43
|
+
|
44
|
+
def load_clusters(config)
|
45
|
+
return unless config.key?("clusters")
|
46
|
+
config["clusters"].each do |cluster_config|
|
47
|
+
clusters << Cluster.new(cluster_config)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def load_databases(config)
|
52
|
+
return unless config.key?("databases")
|
53
|
+
config["databases"].each do |database_config|
|
54
|
+
databases << Database.new(database_config)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def find_cluster(name)
|
59
|
+
clusters.find { |cluster| cluster.name == name }
|
60
|
+
end
|
61
|
+
|
62
|
+
def find_database(name)
|
63
|
+
databases.find { |database| database.name == name }
|
64
|
+
end
|
65
|
+
|
66
|
+
def ignore_database?(name)
|
67
|
+
databases.any? do |database|
|
68
|
+
database.name == name && database.ignore
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
class Cluster
|
73
|
+
attr_reader :name, :alias, :default, :ignore
|
74
|
+
|
75
|
+
def initialize(config)
|
76
|
+
@name = config["name"]
|
77
|
+
@alias = config["alias"] || @name
|
78
|
+
@default = config.key?("default") && !!config["default"]
|
79
|
+
@ignore = config.key?("ignore") && !!config["ignore"]
|
80
|
+
end
|
81
|
+
|
82
|
+
def enrich(object)
|
83
|
+
object.alias = @alias
|
84
|
+
object.default = default
|
85
|
+
object.ignore = ignore
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class Database
|
90
|
+
attr_reader :name, :alias, :default, :ignore
|
91
|
+
|
92
|
+
def initialize(config)
|
93
|
+
@name = config["name"]
|
94
|
+
@alias = config["alias"] || @name
|
95
|
+
@default = config.key?("default") && !!config["default"]
|
96
|
+
@ignore = config.key?("ignore") && !!config["ignore"]
|
97
|
+
end
|
98
|
+
|
99
|
+
def enrich(object)
|
100
|
+
object.alias = @alias
|
101
|
+
object.default = default
|
102
|
+
object.ignore = ignore
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cloud
|
4
|
+
module Sh
|
5
|
+
module Helpers
|
6
|
+
module Commands
|
7
|
+
|
8
|
+
def command_chain(base)
|
9
|
+
CmdChain.new(base)
|
10
|
+
end
|
11
|
+
|
12
|
+
class CmdChain
|
13
|
+
def initialize(base)
|
14
|
+
@cmd = [base]
|
15
|
+
end
|
16
|
+
|
17
|
+
def with(val)
|
18
|
+
@cmd << val
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def method_missing(name, *args)
|
23
|
+
if args.empty?
|
24
|
+
@cmd << name.to_s.tr("_", "-")
|
25
|
+
elsif args.first.is_a?(TrueClass)
|
26
|
+
@cmd << "--#{name.to_s.tr("_", "-")}"
|
27
|
+
else
|
28
|
+
@cmd << "--#{name.to_s.tr("_", "-")}=#{args.first}"
|
29
|
+
end
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def map(*fields)
|
34
|
+
execute.lines.map do |line|
|
35
|
+
values = line.split.first(fields.size)
|
36
|
+
OpenStruct.new(fields.zip(values).to_h)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def replace_current_process
|
41
|
+
exec(@cmd.join(" "))
|
42
|
+
end
|
43
|
+
|
44
|
+
def execute
|
45
|
+
cloud_sh_exec(@cmd)
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
@cmd.join(" ")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cloud
|
4
|
+
module Sh
|
5
|
+
module Providers
|
6
|
+
def self.providers
|
7
|
+
@providers ||= {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.add_provider(name, klass)
|
11
|
+
providers[name] = klass
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.build(account)
|
15
|
+
return providers[account.kind].new(account) if providers.key?(account.kind)
|
16
|
+
raise ArgumentError, "Don't know account kind #{account.kind} for account #{account.inspect}"
|
17
|
+
end
|
18
|
+
|
19
|
+
class Base
|
20
|
+
include Cloud::Sh::Helpers::Commands
|
21
|
+
|
22
|
+
attr_reader :account
|
23
|
+
|
24
|
+
def initialize(account)
|
25
|
+
@account = account
|
26
|
+
end
|
27
|
+
|
28
|
+
def servers
|
29
|
+
raise NotImplementedError
|
30
|
+
end
|
31
|
+
|
32
|
+
def databases
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
35
|
+
|
36
|
+
def clusters
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cloud/sh/providers/base"
|
4
|
+
require "open3"
|
5
|
+
|
6
|
+
module Cloud
|
7
|
+
module Sh
|
8
|
+
module Providers
|
9
|
+
class DigitalOcean < Base
|
10
|
+
def self.refresh_k8s_configs
|
11
|
+
return if File.exist?(kube_config) && (Time.now.to_i - File.mtime(kube_config).to_i) < 3600
|
12
|
+
configs = Cloud::Sh.config.accounts.map { |account| new(account).k8s_configs }.flatten.compact
|
13
|
+
config = configs.shift
|
14
|
+
configs.each do |cfg|
|
15
|
+
config["clusters"] += cfg["clusters"]
|
16
|
+
config["contexts"] += cfg["contexts"]
|
17
|
+
config["users"] += cfg["users"]
|
18
|
+
end
|
19
|
+
config["current-context"] = config["contexts"].first["name"]
|
20
|
+
File.write(kube_config, YAML.dump(config))
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.kube_config
|
24
|
+
File.expand_path(".kube/cloud_sh_config", "~/")
|
25
|
+
end
|
26
|
+
|
27
|
+
def servers
|
28
|
+
doctl.compute.droplet.list.format("Name,PublicIPv4").no_header(true).map(:name, :ip).each do |server|
|
29
|
+
yield server if block_given?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def databases
|
34
|
+
list = []
|
35
|
+
doctl.db.list.no_header(true).map(:id, :name, :engine).each do |cluster|
|
36
|
+
account.find_database(cluster.name)&.enrich(cluster)
|
37
|
+
defaultdb_uri = doctl.db.conn.with(cluster.id).format("URI").no_header(true).map(:uri).first.uri
|
38
|
+
dbs = [OpenStruct.new(name: "")] if cluster.engine == "redis"
|
39
|
+
dbs ||= doctl.db.db.list.with(cluster.id).no_header(true).map(:name)
|
40
|
+
dbs.each do |db|
|
41
|
+
uri = URI.parse(defaultdb_uri).tap { |uri| uri.path = "/#{db.name}" }.to_s
|
42
|
+
database = OpenStruct.new(cluster: cluster, name: db.name, uri: uri)
|
43
|
+
yield database if block_given?
|
44
|
+
list << database
|
45
|
+
end
|
46
|
+
end
|
47
|
+
list
|
48
|
+
end
|
49
|
+
|
50
|
+
def clusters
|
51
|
+
doctl.k8s.cluster.list.no_header(true).map(:id, :name).map do |cluster|
|
52
|
+
account.find_cluster(cluster.name)&.enrich(cluster)
|
53
|
+
next if cluster.ignore
|
54
|
+
cluster.context = k8s_context(account, cluster)
|
55
|
+
cluster.pods = kubectl.context(cluster.context).get.pod.all_namespaces(true).no_headers(true).map(:namespace, :name).map do |pod|
|
56
|
+
pod.name = k8s_pod_name(pod.name)
|
57
|
+
pod
|
58
|
+
end.group_by(&:itself).keys.group_by(&:namespace)
|
59
|
+
yield cluster if block_given?
|
60
|
+
end.compact
|
61
|
+
end
|
62
|
+
|
63
|
+
def k8s_configs
|
64
|
+
doctl.k8s.cluster.list.no_header(true).map(:id, :name).map do |cluster|
|
65
|
+
account.find_cluster(cluster.name)&.enrich(cluster)
|
66
|
+
next if cluster.ignore
|
67
|
+
cluster_config = YAML.load(doctl.k8s.cluster.config.show.with(cluster.id).execute)
|
68
|
+
cluster_config["contexts"].first["name"] = k8s_context(account, cluster)
|
69
|
+
cluster_config
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def k8s_context(account, cluster)
|
74
|
+
[
|
75
|
+
account.name,
|
76
|
+
(cluster.alias unless cluster.default)
|
77
|
+
].compact.join("-").tr("._", "-")
|
78
|
+
end
|
79
|
+
|
80
|
+
def k8s_pod_name(name)
|
81
|
+
parts = name.split("-")
|
82
|
+
parts.pop if parts.last =~ /^[a-z0-9]{5}$/
|
83
|
+
parts.pop if parts.last =~ /^[a-f0-9]{8,10}$/
|
84
|
+
parts.pop if parts.last =~ /^[a-f0-9]{8,10}$/
|
85
|
+
parts.join("-")
|
86
|
+
end
|
87
|
+
|
88
|
+
def doctl
|
89
|
+
command_chain("doctl").context(account.context)
|
90
|
+
end
|
91
|
+
|
92
|
+
def kubectl
|
93
|
+
command_chain("kubectl").kubeconfig(Cloud::Sh::Providers::DigitalOcean.kube_config)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
add_provider("do", DigitalOcean)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/lib/cloud/sh.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cloud/sh/version"
|
4
|
+
require "gli"
|
5
|
+
|
6
|
+
require "cloud/sh/config"
|
7
|
+
|
8
|
+
require "cloud/sh/helpers/commands"
|
9
|
+
|
10
|
+
require "cloud/sh/commands/refresh"
|
11
|
+
require "cloud/sh/commands/k8s_tail"
|
12
|
+
require "cloud/sh/commands/k8s_exec"
|
13
|
+
|
14
|
+
require "cloud/sh/providers/digital_ocean"
|
15
|
+
|
16
|
+
module Cloud
|
17
|
+
module Sh
|
18
|
+
class Error < StandardError; end
|
19
|
+
module_function
|
20
|
+
|
21
|
+
def config
|
22
|
+
@config ||= Cloud::Sh::Config.new
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module Kernel
|
28
|
+
def cloud_sh_exec(*cmd, env: nil)
|
29
|
+
cmd = cmd.flatten.map(&:to_s).join(" ")
|
30
|
+
args = env ? [env, cmd] : [cmd]
|
31
|
+
message = "Executing: #{cmd}"
|
32
|
+
message << " (#{env.inspect})" if env
|
33
|
+
stdout, stderr, status = Open3.capture3(*args)
|
34
|
+
unless status.success?
|
35
|
+
puts "Command: #{cmd}"
|
36
|
+
puts "ENV: #{env.inspect}" if env
|
37
|
+
puts "Stdout:\n#{stdout}\n"
|
38
|
+
puts "Stderr:\n#{stderr}\n"
|
39
|
+
raise "Command failed!!!"
|
40
|
+
end
|
41
|
+
stdout
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class OpenStruct
|
46
|
+
def merge(other)
|
47
|
+
other.each_pair do |k, v|
|
48
|
+
self[k] = v
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
metadata
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cloud-sh
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Cristian Bica
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-08-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: gli
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.17'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.17'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
description: Cloud shell helpers.
|
70
|
+
email:
|
71
|
+
- cristian.bica@gmail.com
|
72
|
+
executables:
|
73
|
+
- cloud-sh
|
74
|
+
- kubetail
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- ".github/workflows/gempush.yml"
|
79
|
+
- ".gitignore"
|
80
|
+
- ".rubocop.yml"
|
81
|
+
- ".travis.yml"
|
82
|
+
- Gemfile
|
83
|
+
- Gemfile.lock
|
84
|
+
- LICENSE.txt
|
85
|
+
- README.md
|
86
|
+
- Rakefile
|
87
|
+
- bin/console
|
88
|
+
- bin/setup
|
89
|
+
- cloud-sh.gemspec
|
90
|
+
- exe/cloud-sh
|
91
|
+
- exe/kubetail
|
92
|
+
- lib/cloud/sh.rb
|
93
|
+
- lib/cloud/sh/cli.rb
|
94
|
+
- lib/cloud/sh/commands/base.rb
|
95
|
+
- lib/cloud/sh/commands/k8s_exec.rb
|
96
|
+
- lib/cloud/sh/commands/k8s_tail.rb
|
97
|
+
- lib/cloud/sh/commands/refresh.rb
|
98
|
+
- lib/cloud/sh/config.rb
|
99
|
+
- lib/cloud/sh/helpers/commands.rb
|
100
|
+
- lib/cloud/sh/providers/base.rb
|
101
|
+
- lib/cloud/sh/providers/digital_ocean.rb
|
102
|
+
- lib/cloud/sh/version.rb
|
103
|
+
homepage: https://github.com/cristianbica/cloud-sh
|
104
|
+
licenses:
|
105
|
+
- MIT
|
106
|
+
metadata:
|
107
|
+
homepage_uri: https://github.com/cristianbica/cloud-sh
|
108
|
+
source_code_uri: https://github.com/cristianbica/cloud-sh
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubygems_version: 3.0.3
|
125
|
+
signing_key:
|
126
|
+
specification_version: 4
|
127
|
+
summary: Cloud shell helpers.
|
128
|
+
test_files: []
|