gclouder 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|