gclouder 0.1.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/README.md +104 -0
- data/bin/gclouder +7 -0
- data/lib/gclouder/config/arguments.rb +35 -0
- data/lib/gclouder/config/cli_args.rb +77 -0
- data/lib/gclouder/config/cluster.rb +66 -0
- data/lib/gclouder/config/defaults.rb +35 -0
- data/lib/gclouder/config/files/project.rb +24 -0
- data/lib/gclouder/config/project.rb +34 -0
- data/lib/gclouder/config/resource_representations.rb +31 -0
- data/lib/gclouder/config_loader.rb +25 -0
- data/lib/gclouder/config_section.rb +26 -0
- data/lib/gclouder/dependencies.rb +11 -0
- data/lib/gclouder/gcloud.rb +39 -0
- data/lib/gclouder/gsutil.rb +25 -0
- data/lib/gclouder/header.rb +9 -0
- data/lib/gclouder/helpers.rb +77 -0
- data/lib/gclouder/logging.rb +181 -0
- data/lib/gclouder/mappings/argument.rb +31 -0
- data/lib/gclouder/mappings/file.rb +31 -0
- data/lib/gclouder/mappings/property.rb +31 -0
- data/lib/gclouder/mappings/resource_representation.rb +31 -0
- data/lib/gclouder/monkey_patches/array.rb +19 -0
- data/lib/gclouder/monkey_patches/boolean.rb +12 -0
- data/lib/gclouder/monkey_patches/hash.rb +44 -0
- data/lib/gclouder/monkey_patches/ipaddr.rb +10 -0
- data/lib/gclouder/monkey_patches/string.rb +30 -0
- data/lib/gclouder/resource.rb +63 -0
- data/lib/gclouder/resource_cleaner.rb +58 -0
- data/lib/gclouder/resources/compute/addresses.rb +108 -0
- data/lib/gclouder/resources/compute/bgp-vpns.rb +220 -0
- data/lib/gclouder/resources/compute/disks.rb +99 -0
- data/lib/gclouder/resources/compute/firewall_rules.rb +82 -0
- data/lib/gclouder/resources/compute/instances.rb +147 -0
- data/lib/gclouder/resources/compute/networks/subnets.rb +104 -0
- data/lib/gclouder/resources/compute/networks.rb +110 -0
- data/lib/gclouder/resources/compute/project_info/ssh_keys.rb +171 -0
- data/lib/gclouder/resources/compute/routers.rb +83 -0
- data/lib/gclouder/resources/compute/vpns.rb +199 -0
- data/lib/gclouder/resources/container/clusters.rb +257 -0
- data/lib/gclouder/resources/container/node_pools.rb +193 -0
- data/lib/gclouder/resources/dns.rb +390 -0
- data/lib/gclouder/resources/logging/sinks.rb +98 -0
- data/lib/gclouder/resources/project/iam_policy_binding.rb +293 -0
- data/lib/gclouder/resources/project.rb +85 -0
- data/lib/gclouder/resources/project_id.rb +71 -0
- data/lib/gclouder/resources/pubsub/subscriptions.rb +100 -0
- data/lib/gclouder/resources/pubsub/topics.rb +95 -0
- data/lib/gclouder/resources/storagebuckets.rb +103 -0
- data/lib/gclouder/resources/validate/global.rb +27 -0
- data/lib/gclouder/resources/validate/local.rb +68 -0
- data/lib/gclouder/resources/validate/region.rb +28 -0
- data/lib/gclouder/resources/validate/remote.rb +78 -0
- data/lib/gclouder/resources.rb +148 -0
- data/lib/gclouder/shell.rb +71 -0
- data/lib/gclouder/version.rb +5 -0
- data/lib/gclouder.rb +278 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0eb4fecbee428b1cdc7c390f0503ccdc4448cbff
|
4
|
+
data.tar.gz: 3f552005ae332e6ef91fd32cf207f827e4f2c8b6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4ee77003e814011b94d28ab3f438973ad3e49dea34893e2f6ea20f86d132bbe5e113b08a8c7d4affb90dc1d710f0edc6d4a92430cd5ecbab87f8164485248c1b
|
7
|
+
data.tar.gz: 0307d6e85947977a12c5a30a5d054b0c738a71341b99691351b2e1ae1ff51eb103982821a3532fcee33a4d06c968f2f6bcd9c66f6a22cc0ea3591122339619d0
|
data/README.md
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
## GClouder
|
2
|
+
|
3
|
+
Google Cloud Resource Deployer
|
4
|
+
|
5
|
+
### Usage
|
6
|
+
|
7
|
+
#### Authentication
|
8
|
+
|
9
|
+
Authenticate against the GCP API:
|
10
|
+
```
|
11
|
+
gcloud auth login
|
12
|
+
gcloud auth application-default login
|
13
|
+
```
|
14
|
+
|
15
|
+
#### Examples
|
16
|
+
|
17
|
+
```
|
18
|
+
# output validation then exit
|
19
|
+
./bin/gclouder -c conf/<config>.yaml --validate-only
|
20
|
+
|
21
|
+
# only execute non-state-changing commands (e.g: queries)
|
22
|
+
./bin/gclouder -c conf/<config>.yaml --dry-run
|
23
|
+
|
24
|
+
# apply the config
|
25
|
+
./bin/gclouder -c conf/<config>.yaml
|
26
|
+
```
|
27
|
+
|
28
|
+
#### Dependencies
|
29
|
+
|
30
|
+
##### Google Cloud SDK
|
31
|
+
|
32
|
+
Please see: https://cloud.google.com/sdk/downloads
|
33
|
+
|
34
|
+
##### Ruby
|
35
|
+
|
36
|
+
Requires a modern version of Ruby (>= 2.4), you can use rbenv or brew to install one, e.g:
|
37
|
+
|
38
|
+
```
|
39
|
+
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
|
40
|
+
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
|
41
|
+
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
|
42
|
+
. ~/.bashrc
|
43
|
+
rbenv install 2.4.0
|
44
|
+
rbenv global 2.4.0 # or `rbenv local 2.4.0` in gclouder dir
|
45
|
+
```
|
46
|
+
|
47
|
+
##### Gems
|
48
|
+
|
49
|
+
Install `gclouder` gem dependencies:
|
50
|
+
```
|
51
|
+
cd gclouder
|
52
|
+
gem install bundler
|
53
|
+
bundle install
|
54
|
+
```
|
55
|
+
|
56
|
+
### Testing
|
57
|
+
|
58
|
+
|
59
|
+
Test coverage is currently limited to libraries which are peripheral to the core functionality of this app, i.e: tests only exist for methods which can be independently tested of the GCP API.
|
60
|
+
|
61
|
+
There are plans to add integration tests at a later date.
|
62
|
+
|
63
|
+
To run the tests use one of the following:
|
64
|
+
|
65
|
+
Run once:
|
66
|
+
```
|
67
|
+
rake
|
68
|
+
```
|
69
|
+
|
70
|
+
To monitor changes to project files during development:
|
71
|
+
```
|
72
|
+
rake guard
|
73
|
+
```
|
74
|
+
|
75
|
+
### Notes
|
76
|
+
|
77
|
+
Each resource is designed to do the following:
|
78
|
+
|
79
|
+
* validation of local configuration by:
|
80
|
+
* check parameters are valid arguments
|
81
|
+
* check required parameters are set
|
82
|
+
* check types are correct
|
83
|
+
|
84
|
+
* create remote instances which are defined locally
|
85
|
+
|
86
|
+
* check remote instance dont differs from local instance definitions
|
87
|
+
|
88
|
+
* remove instances which are no longer defined locally yet exist remotely
|
89
|
+
|
90
|
+
### Why?
|
91
|
+
* Google Deployment Manager is unable to manipulate resources not managed by itself
|
92
|
+
* It's not possible to resize things like clusters without using gcloud(1)
|
93
|
+
* Not all resources are supported by Google Deployment Manager
|
94
|
+
* Greater control over the magic that happens between our resource definitions and the remote resources
|
95
|
+
* Inter-project resources
|
96
|
+
|
97
|
+
### Gem
|
98
|
+
|
99
|
+
To build and install a gem run:
|
100
|
+
|
101
|
+
```
|
102
|
+
rake build
|
103
|
+
gem install pkg/gclouder-<version>.gem
|
104
|
+
```
|
data/bin/gclouder
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
module GClouder
|
6
|
+
module Config
|
7
|
+
module Arguments
|
8
|
+
include GClouder::Logging
|
9
|
+
|
10
|
+
def self.arguments
|
11
|
+
GClouder::ConfigLoader.load("../../assets/arguments")
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.load
|
15
|
+
arguments
|
16
|
+
end
|
17
|
+
|
18
|
+
def arguments
|
19
|
+
Arguments.arguments
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.included(klass)
|
23
|
+
klass.extend Arguments
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.permitted(section)
|
27
|
+
GClouder::ConfigSection.find(section, arguments)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.required(section)
|
31
|
+
GClouder::ConfigSection.find(section, arguments).delete_if { |key, values| ! values["required"] }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module GClouder
|
4
|
+
module Config
|
5
|
+
module CLIArgs
|
6
|
+
def self.cli_args
|
7
|
+
@cli_args ||= { debug: false }
|
8
|
+
end
|
9
|
+
|
10
|
+
def cli_args
|
11
|
+
CLIArgs.cli_args
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.included(klass)
|
15
|
+
klass.extend CLIArgs
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.valid_resources
|
19
|
+
GClouder.resources.map { |resource| resource[:name] }
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.load
|
23
|
+
option_parser = Trollop::Parser.new do
|
24
|
+
banner GClouder::Header.display + "\n \n "
|
25
|
+
|
26
|
+
# required
|
27
|
+
opt :config, "path to config file\n ", type: :string, required: true
|
28
|
+
|
29
|
+
# level of operation
|
30
|
+
opt :dry_run, "passive mode"
|
31
|
+
opt :purge, "remove unmanaged resources (destructive!)\n "
|
32
|
+
|
33
|
+
# authentication / for automation
|
34
|
+
opt :activate_service_accounts, "activate service account(s) (for use when running using service accounts, i.e: with CI)"
|
35
|
+
opt :keys_dir, "path to directory with service account key files (for use with --activate-service-accounts)\n ", type: :string
|
36
|
+
|
37
|
+
# which resources / actions
|
38
|
+
# FIXME: integrate checks for required permissions into Project module
|
39
|
+
opt :bootstrap, "create project (requires being run as non-service account)"
|
40
|
+
# this should be type: proc and validate that the params match one of: validate, ensure, clean
|
41
|
+
opt :stages, "which stages to run (validate,ensure,clean) [csv]", type: :string
|
42
|
+
opt :resources, "which resources to update [csv]", type: :string
|
43
|
+
opt :skip_cross_project_resources, "skip resources which don't reside in main project\n "
|
44
|
+
|
45
|
+
# output
|
46
|
+
opt :debug, "print commands to be executed, and stack traces"
|
47
|
+
opt :trace, "print stack traces"
|
48
|
+
opt :no_color, "disable color\n \n "
|
49
|
+
end
|
50
|
+
|
51
|
+
@cli_args = Trollop.with_standard_exception_handling(option_parser) do
|
52
|
+
raise Trollop::HelpNeeded if ARGV.empty?
|
53
|
+
option_parser.parse ARGV
|
54
|
+
end
|
55
|
+
|
56
|
+
String.disable_colorization = @cli_args[:no_color]
|
57
|
+
|
58
|
+
if @cli_args[:resources]
|
59
|
+
@cli_args[:resources].split(',').each do |resource|
|
60
|
+
unless valid_resources.include?(resource)
|
61
|
+
puts "valid resources: #{valid_resources.join(', ')}"
|
62
|
+
puts "invalid resource for --resources flag: #{resource}"
|
63
|
+
exit 1
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
check
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.check
|
72
|
+
raise ArgumentError, "config file not specified" unless cli_args[:config_given]
|
73
|
+
raise ArgumentError, "config file not readable: #{cli_args[:config]}" unless File.readable?(cli_args[:config])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module GClouder
|
4
|
+
module Config
|
5
|
+
class Cluster < DeepMergeHash
|
6
|
+
def context(context)
|
7
|
+
dup = self.dup
|
8
|
+
|
9
|
+
permitted_keys = case context
|
10
|
+
when :create_cluster
|
11
|
+
[
|
12
|
+
"additional_zones",
|
13
|
+
"async",
|
14
|
+
"cluster_ipv4_cidr",
|
15
|
+
"disable_addons",
|
16
|
+
"disk_size",
|
17
|
+
"no_enable_cloud_endpoints",
|
18
|
+
"no_enable_cloud_logging",
|
19
|
+
"no_enable_cloud_monitoring",
|
20
|
+
"image_type",
|
21
|
+
"machine_type",
|
22
|
+
"max_nodes_per_pool",
|
23
|
+
"network",
|
24
|
+
"num_nodes",
|
25
|
+
"password",
|
26
|
+
"scopes",
|
27
|
+
"subnetwork",
|
28
|
+
"username",
|
29
|
+
"wait",
|
30
|
+
"zone",
|
31
|
+
]
|
32
|
+
when :create_nodepool
|
33
|
+
# flip value due to differing key name..
|
34
|
+
if self.key?("no_enable_cloud_endpoints")
|
35
|
+
dup[:enable_cloud_endpoints] = self[:no_enable_cloud_endpoints] ? false : true
|
36
|
+
end
|
37
|
+
|
38
|
+
[
|
39
|
+
"disk_size",
|
40
|
+
"enable_cloud_endpoints",
|
41
|
+
"image_type",
|
42
|
+
"machine_type",
|
43
|
+
"num_nodes",
|
44
|
+
"scopes",
|
45
|
+
"zone",
|
46
|
+
"cluster",
|
47
|
+
]
|
48
|
+
when :resize_cluster
|
49
|
+
dup["size"] = self["num_nodes"] if self.key?("num_nodes")
|
50
|
+
|
51
|
+
[
|
52
|
+
"async",
|
53
|
+
"size",
|
54
|
+
"wait",
|
55
|
+
"zone",
|
56
|
+
"node_pool",
|
57
|
+
]
|
58
|
+
else
|
59
|
+
raise StandardError, "invalid context supplied when querying config object: #{context}"
|
60
|
+
end
|
61
|
+
|
62
|
+
dup.delete_if { |k, _| !permitted_keys.include?(k) }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
module GClouder
|
6
|
+
module Config
|
7
|
+
module Defaults
|
8
|
+
include GClouder::Logging
|
9
|
+
|
10
|
+
def self.defaults
|
11
|
+
GClouder::ConfigLoader.load("../../assets/defaults")
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.load
|
15
|
+
defaults
|
16
|
+
end
|
17
|
+
|
18
|
+
def defaults
|
19
|
+
Defaults.defaults
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.included(klass)
|
23
|
+
klass.extend Defaults
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.section(section)
|
27
|
+
GClouder::ConfigSection.find(section, defaults)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.section?(section)
|
31
|
+
GClouder::ConfigSection.find(section, defaults, silent: true)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module GClouder
|
4
|
+
module Config
|
5
|
+
module Files
|
6
|
+
module Project
|
7
|
+
include GClouder::Config::CLIArgs
|
8
|
+
include GClouder::Helpers
|
9
|
+
|
10
|
+
def self.included(klass)
|
11
|
+
klass.extend project
|
12
|
+
end
|
13
|
+
|
14
|
+
def project
|
15
|
+
Project.project
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.project
|
19
|
+
to_deep_merge_hash(YAML.load_file(cli_args[:config]))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module GClouder
|
4
|
+
module Config
|
5
|
+
module Project
|
6
|
+
include GClouder::Logging
|
7
|
+
include GClouder::Helpers
|
8
|
+
|
9
|
+
def self.project
|
10
|
+
@project
|
11
|
+
end
|
12
|
+
|
13
|
+
def project
|
14
|
+
Project.project
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.load
|
18
|
+
@project = GClouder.resources.each_with_object(GClouder::Config::Files::Project.project) do |resource, config|
|
19
|
+
next unless module_exists? "#{resource[:module]}::Config"
|
20
|
+
|
21
|
+
config = resource[:module]::Config.merged(config)
|
22
|
+
end
|
23
|
+
|
24
|
+
fatal "no project_id found in config" unless project.key?("project_id")
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def self.included(klass)
|
30
|
+
klass.extend Project
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
module GClouder
|
6
|
+
module Config
|
7
|
+
module ResourceRepresentations
|
8
|
+
include GClouder::Logging
|
9
|
+
|
10
|
+
def self.properties
|
11
|
+
@properties ||= GClouder::ConfigLoader.load("../../assets/resource_representations")
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.load
|
15
|
+
properties
|
16
|
+
end
|
17
|
+
|
18
|
+
def properties
|
19
|
+
ResourceRepresentations.properties
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.included(klass)
|
23
|
+
klass.extend ResourceRepresentations
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.section(section)
|
27
|
+
GClouder::ConfigSection.find(section, properties)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module GClouder
|
4
|
+
module ConfigLoader
|
5
|
+
def self.load(path)
|
6
|
+
data = DeepMergeHash.new
|
7
|
+
file = File.join(File.dirname(__FILE__), path, "**/*.yml")
|
8
|
+
|
9
|
+
configs = Dir.glob(file)
|
10
|
+
|
11
|
+
configs.each do |config|
|
12
|
+
yaml = YAML.load_file config
|
13
|
+
section_path = to_path(config, path)
|
14
|
+
hash = section_path.reverse.inject(yaml) { |value, key| { key => value } }
|
15
|
+
data = data.deep_merge hash
|
16
|
+
end
|
17
|
+
|
18
|
+
data
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.to_path(config, path)
|
22
|
+
config.gsub(/.*#{path}\//, "").gsub!(/\.yml$/, "").split("/")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module GClouder
|
4
|
+
module ConfigSection
|
5
|
+
include GClouder::Logging
|
6
|
+
|
7
|
+
def self.included(klass)
|
8
|
+
klass.extend ConfigSection
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.find(path, data, silent: false)
|
12
|
+
raise StandardError, "find: path argument must be an array: #{path.inspect}" unless path.is_a?(Array)
|
13
|
+
raise StandardError, "find: data argument must be an hash: #{path.inspect}" unless data.is_a?(Hash)
|
14
|
+
|
15
|
+
section = data.dig(*path)
|
16
|
+
|
17
|
+
if section
|
18
|
+
return silent ? true : section
|
19
|
+
end
|
20
|
+
|
21
|
+
return false if silent
|
22
|
+
|
23
|
+
fatal "can't find key in data: #{path}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module GClouder
|
4
|
+
module GCloud
|
5
|
+
include GClouder::Logging
|
6
|
+
include GClouder::Shell
|
7
|
+
include GClouder::Helpers
|
8
|
+
include GClouder::Config::Project
|
9
|
+
include GClouder::Config::CLIArgs
|
10
|
+
|
11
|
+
def self.included(klass)
|
12
|
+
klass.extend GCloud
|
13
|
+
end
|
14
|
+
|
15
|
+
def gcloud(command, force: false, failure: true, silent: false, project_id: nil)
|
16
|
+
project_id = verify(project_id)
|
17
|
+
|
18
|
+
GClouder::Project::ID.switch(project_id)
|
19
|
+
|
20
|
+
if cli_args[:dry_run] && !force
|
21
|
+
debug "# gcloud --quiet --format json --project=#{project_id} #{command}" if cli_args[:debug]
|
22
|
+
GClouder::Project::ID.reset
|
23
|
+
return
|
24
|
+
end
|
25
|
+
|
26
|
+
result = shell("gcloud --quiet --format json --project=#{project_id} #{command}", failure: failure, silent: silent)
|
27
|
+
|
28
|
+
GClouder::Project::ID.reset
|
29
|
+
|
30
|
+
valid_json?(result) ? JSON.parse(result.to_s) : result
|
31
|
+
end
|
32
|
+
|
33
|
+
def verify(project_id)
|
34
|
+
project_id ||= project["project_id"]
|
35
|
+
return project_id if project_id
|
36
|
+
raise StandardError, "project_id not detected"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module GClouder
|
4
|
+
module GSUtil
|
5
|
+
include GClouder::Shell
|
6
|
+
include GClouder::Config::CLIArgs
|
7
|
+
include GClouder::Config::Project
|
8
|
+
|
9
|
+
def self.included(klass)
|
10
|
+
klass.extend GSUtil
|
11
|
+
end
|
12
|
+
|
13
|
+
def gsutil(command, args, force: false)
|
14
|
+
info "# gsutil #{command} -p #{project['project_id']} #{args}" if cli_args[:debug]
|
15
|
+
|
16
|
+
return if cli_args[:dry_run] && !force
|
17
|
+
|
18
|
+
gsutil_exec(command, args)
|
19
|
+
end
|
20
|
+
|
21
|
+
def gsutil_exec(command, args)
|
22
|
+
shell("gsutil #{command} -p #{project['project_id']} #{args}")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module GClouder
|
4
|
+
module Helpers
|
5
|
+
def self.included(klass)
|
6
|
+
klass.extend Helpers
|
7
|
+
end
|
8
|
+
|
9
|
+
def hash_to_args(hash)
|
10
|
+
raise StandardError, "hash_to_args: input not a hash: #{hash}" unless hash.is_a?(Hash)
|
11
|
+
hash.map { |param, value|
|
12
|
+
next if param == "name"
|
13
|
+
to_arg(param, value)
|
14
|
+
}.join(" ")
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_arg(param, value)
|
18
|
+
param = param.tr("_", "-")
|
19
|
+
|
20
|
+
value = case value
|
21
|
+
when Boolean
|
22
|
+
return value ? "--#{param}" : "--no-#{param}"
|
23
|
+
when Array
|
24
|
+
value.join(",")
|
25
|
+
else
|
26
|
+
value
|
27
|
+
end
|
28
|
+
|
29
|
+
"--#{param}='#{value}'"
|
30
|
+
end
|
31
|
+
|
32
|
+
def valid_json?(object)
|
33
|
+
JSON.parse(object.to_s)
|
34
|
+
return true
|
35
|
+
rescue JSON::ParserError
|
36
|
+
return false
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_deep_merge_hash(hash, hash_type = DeepMergeHash)
|
40
|
+
raise StandardError, "to_deep_merge_hash: argument must be a hash" unless hash.is_a?(Hash)
|
41
|
+
|
42
|
+
hash.each do |k,v|
|
43
|
+
case v
|
44
|
+
when Hash
|
45
|
+
hash_to_deep_merge_hash(hash, k, v, hash_type)
|
46
|
+
when Array
|
47
|
+
array_to_deep_merge_hash(v, hash_type)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
hash_type.new(hash)
|
52
|
+
end
|
53
|
+
|
54
|
+
def module_exists?(name, base = self.class)
|
55
|
+
raise StandardError, "module name must be a string" unless name.is_a?(String)
|
56
|
+
base.const_defined?(name) && base.const_get(name).instance_of?(::Module)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def hash_to_deep_merge_hash(obj, index, hash, hash_type)
|
62
|
+
obj[index] = hash_type.new(hash)
|
63
|
+
to_deep_merge_hash(obj[index], hash_type)
|
64
|
+
end
|
65
|
+
|
66
|
+
def array_to_deep_merge_hash(array, hash_type)
|
67
|
+
array.each_with_index do |e,i|
|
68
|
+
case e
|
69
|
+
when Hash
|
70
|
+
hash_to_deep_merge_hash(array, i, e, hash_type)
|
71
|
+
when Array
|
72
|
+
array_to_deep_merge_hash(array, hash_type)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|