compote 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,41 @@
1
+ module Compote
2
+
3
+ class CLI < Thor
4
+
5
+ COMPOSE_COMMANDS = %w()
6
+
7
+ COMPOTE_COMMANDS = %w( env help version )
8
+
9
+
10
+ compose_help = `docker-compose --help`
11
+
12
+ compose_help = compose_help.split "\n"
13
+
14
+ compose_help = compose_help.slice ( compose_help.index( "Commands:" ) + 1 )..-1
15
+
16
+ compose_help.each do | command_info |
17
+
18
+ command, description = command_info.strip.split( /\s+/, 2 )
19
+
20
+ next if COMPOTE_COMMANDS.include? command
21
+
22
+ COMPOSE_COMMANDS.push command
23
+
24
+
25
+ desc command, description
26
+
27
+ define_method "compose_#{ command }" do | *arguments |
28
+
29
+ next exec 'docker-compose', command, *arguments if %w(-h --help).include? arguments[ 0 ]
30
+
31
+ run_compose command, *arguments
32
+
33
+ end
34
+
35
+ map command => "compose_#{ command }"
36
+
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,23 @@
1
+ module Compote
2
+
3
+ class CLI < Thor
4
+
5
+ desc 'env', 'View the resulted compose environment'
6
+
7
+ def env
8
+
9
+ compote_config = load_config
10
+
11
+ compose_environment = compote_config.compose_environment
12
+
13
+ say YAML.dump compose_environment
14
+
15
+ rescue Error => error
16
+
17
+ exit_with_error error
18
+
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,23 @@
1
+ module Compote
2
+
3
+ class CLI < Thor
4
+
5
+ desc 'help', 'Get help on a command'
6
+
7
+ def help ( *arguments )
8
+
9
+ if COMPOSE_COMMANDS.include? arguments.first
10
+
11
+ exec 'docker-compose', 'help', *arguments
12
+
13
+ else
14
+
15
+ super *arguments
16
+
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,31 @@
1
+ module Compote
2
+
3
+ class CLI < Thor
4
+
5
+ desc 'version', 'Show version information'
6
+
7
+ method_option :short, type: :boolean, desc: "Shows only Compote's and Compose's version numbers."
8
+
9
+ def version
10
+
11
+ compote_version = "compote version: #{ Compote::VERSION }"
12
+
13
+ compose_version = if options[ :short ]
14
+
15
+ "docker-compose version: #{ `docker-compose version --short` }"
16
+
17
+ else
18
+
19
+ `docker-compose version`
20
+
21
+ end
22
+
23
+ say compote_version
24
+
25
+ say compose_version
26
+
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,272 @@
1
+ module Compote
2
+
3
+ class Config
4
+
5
+ def initialize ( file_name )
6
+
7
+ @file_name = file_name
8
+
9
+ @directory_name = File.dirname file_name
10
+
11
+
12
+ data = YAML.load_file file_name
13
+
14
+ data = Schema.normalize self, data
15
+
16
+ @data = apply_extends data
17
+
18
+
19
+ @compose_settings = @data.reject { | key, value | %w( compote services ).include? key }
20
+
21
+ @compote_settings = @data.fetch 'compote', {}
22
+
23
+
24
+ @services_configs = {}
25
+
26
+ @data.fetch( 'services', {} ).each do | name, data |
27
+
28
+ service_config = ServiceConfig.new self, name, data
29
+
30
+ @services_configs[ name ] = service_config
31
+
32
+ end
33
+
34
+ end
35
+
36
+ def file_name
37
+
38
+ @file_name
39
+
40
+ end
41
+
42
+ def directory_name
43
+
44
+ @directory_name
45
+
46
+ end
47
+
48
+ def compose_version
49
+
50
+ @compose_settings.fetch 'version'
51
+
52
+ end
53
+
54
+ def compose_environment
55
+
56
+ environment = {}
57
+
58
+
59
+ environment_setting = @compote_settings.fetch 'environment', {}
60
+
61
+ environment_setting.each do | key, value |
62
+
63
+ environment[ key ] = ENV.fetch key, value
64
+
65
+ end
66
+
67
+
68
+ env_file_setting = @compote_settings.fetch 'env_file' do
69
+
70
+ env_file_setting = []
71
+
72
+ env_file_setting.push '.env' if File.exist? get_path '.env'
73
+
74
+ env_file_setting
75
+
76
+ end
77
+
78
+ env_file_setting.each do | env_file_path |
79
+
80
+ begin
81
+
82
+ env_file_path = get_path env_file_path
83
+
84
+ env_file_content = File.read env_file_path
85
+
86
+ env_file_variables = Dotenv::Parser.call env_file_content
87
+
88
+ env_file_variables.each do | key, value |
89
+
90
+ environment[ key ] = ENV.fetch key, value
91
+
92
+ end
93
+
94
+ rescue Errno::ENOENT => error
95
+
96
+ raise EnvFileOpenError.new error: error, config: self
97
+
98
+ rescue Dotenv::FormatError => error
99
+
100
+ raise EnvFileFormatError.new error: error, path: env_file_path, config: self
101
+
102
+ end
103
+
104
+ end
105
+
106
+
107
+ raise ProjectNameNotProvidedError.new unless environment[ 'COMPOSE_PROJECT_NAME' ] || ENV[ 'COMPOSE_PROJECT_NAME' ]
108
+
109
+
110
+ environment
111
+
112
+ end
113
+
114
+ def compose_file
115
+
116
+ file = Tempfile.new 'docker-compose.yml'
117
+
118
+ file.write YAML.dump compose_config
119
+
120
+ file.close
121
+
122
+ file
123
+
124
+ end
125
+
126
+ def commands
127
+
128
+ commands = {}
129
+
130
+ @compote_settings.fetch( 'commands', {} ).each do | name, command |
131
+
132
+ commands[ name ] = command
133
+
134
+ end
135
+
136
+ @services_configs.each do | service_name, service_config |
137
+
138
+ service_config.commands.each do | command_name, command |
139
+
140
+ commands[ "#{ service_name }:#{ command_name }" ] = command
141
+
142
+ end
143
+
144
+ end
145
+
146
+ commands
147
+
148
+ end
149
+
150
+ def compose_config
151
+
152
+ compose_config = {}
153
+
154
+ compose_config[ 'version' ] = @compose_settings.fetch 'version'
155
+
156
+ compose_config[ 'volumes' ] = @compose_settings.fetch 'volumes', {}
157
+
158
+ compose_config[ 'networks' ] = @compose_settings.fetch 'networks', {}
159
+
160
+
161
+ compose_config[ 'services' ] = {}
162
+
163
+ @services_configs.each do | name, service_config |
164
+
165
+ service_compose_config = service_config.compose_config
166
+
167
+ compose_config[ 'services' ].merge! service_compose_config.fetch( 'services', {} )
168
+
169
+ compose_config[ 'volumes' ].merge! service_compose_config.fetch( 'volumes', {} )
170
+
171
+ compose_config[ 'networks' ].merge! service_compose_config.fetch( 'networks', {} )
172
+
173
+ end
174
+
175
+
176
+ compose_config
177
+
178
+ end
179
+
180
+ def get_service_config ( name )
181
+
182
+ @services_configs.fetch name
183
+
184
+ rescue KeyError => error
185
+
186
+ raise ServiceNotFoundError.new service: name, config: self
187
+
188
+ end
189
+
190
+ def has_service_config? ( name )
191
+
192
+ @services_configs.has_key? name
193
+
194
+ end
195
+
196
+ def load_config ( path )
197
+
198
+ Config.load_config path, self
199
+
200
+ end
201
+
202
+ def get_path ( path )
203
+
204
+ Config.get_path path, self
205
+
206
+ end
207
+
208
+
209
+ @@configs = {}
210
+
211
+ def self.load_config ( path, origin_config = nil )
212
+
213
+ path = get_path path, origin_config
214
+
215
+ return origin_config if path == origin_config&.directory_name
216
+
217
+ config = @@configs[ path ]
218
+
219
+ raise ConfigRecursionError path: path, origin_config: origin_config, configs: configs if config.is_a? String
220
+
221
+ return config if config
222
+
223
+ @@configs[ path ] = origin_config&.file_name || ''
224
+
225
+ config = Config.new path
226
+
227
+ @@configs[ path ] = config
228
+
229
+ config
230
+
231
+ rescue Errno::ENOENT => error
232
+
233
+ raise ConfigOpenError.new error: error, origin_config: origin_config
234
+
235
+ end
236
+
237
+ def self.get_path ( path, origin_config = nil )
238
+
239
+ path = File.absolute_path path, origin_config&.directory_name
240
+
241
+ path = Pathname.new( path ).cleanpath.to_path
242
+
243
+ path
244
+
245
+ end
246
+
247
+
248
+ protected
249
+
250
+ def data
251
+
252
+ @data
253
+
254
+ end
255
+
256
+ def apply_extends ( initial_data )
257
+
258
+ Schema.apply_extends initial_data do | path |
259
+
260
+ config = load_config path
261
+
262
+ data = config.data
263
+
264
+ data
265
+
266
+ end
267
+
268
+ end
269
+
270
+ end
271
+
272
+ end
@@ -0,0 +1,144 @@
1
+ module Compote
2
+
3
+ class Error < StandardError
4
+
5
+ attr_writer :message
6
+
7
+ end
8
+
9
+
10
+ class ConfigOpenError < Error
11
+
12
+ def initialize ( error:, origin_config: )
13
+
14
+ message = "Error loading config file: #{ error.message }"
15
+
16
+ message += "\n" + "required from #{ origin_config.file_name }" if origin_config
17
+
18
+ super message
19
+
20
+ end
21
+
22
+ end
23
+
24
+
25
+ class ConfigFormatError < Error
26
+
27
+ def initialize ( error:, data: )
28
+
29
+ message = "Error loading config file: #{ error.message }" + "\n\n" + "#{ YAML.dump data }"
30
+
31
+ super message
32
+
33
+ end
34
+
35
+ end
36
+
37
+
38
+ class ConfigRecursionError < Error
39
+
40
+ def initialize ( path:, origin_config:, configs: )
41
+
42
+ message = "Error loading config file: Recursive loading of config files - #{ path }."
43
+
44
+ message += "\n\n" + "Requiring trace:"
45
+
46
+ message += "\n" + "#{ path }"
47
+
48
+ requiring_path = origin_config.file_name
49
+
50
+ loop do
51
+
52
+ message += "\n" + "#{ requiring_path }"
53
+
54
+ requiring_path = configs[ requiring_path ]
55
+
56
+ break if requiring_path == path
57
+
58
+ end
59
+
60
+ super message
61
+
62
+ end
63
+
64
+ end
65
+
66
+
67
+ class EnvFileOpenError < Error
68
+
69
+ def initialize ( error:, config: )
70
+
71
+ message = "Error loading env file: #{ error.message }"
72
+
73
+ message += "\n" + "required from #{ config.file_name }"
74
+
75
+ super message
76
+
77
+ end
78
+
79
+ end
80
+
81
+
82
+ class EnvFileFormatError < Error
83
+
84
+ def initialize ( error:, path:, config: )
85
+
86
+ message = "Error loading env file: #{ error.message } - \"#{ path }\""
87
+
88
+ message += "\n" + "required from #{ config.file_name }"
89
+
90
+ super message
91
+
92
+ end
93
+
94
+ end
95
+
96
+
97
+ class ServiceNotFoundError < Error
98
+
99
+ attr_reader :service, :config
100
+
101
+
102
+ def initialize ( service:, config: )
103
+
104
+ @service = service
105
+
106
+ @config = config
107
+
108
+ message = "Error extending service: Service \"#{ service }\" not found in config \"#{ config.file_name }\"."
109
+
110
+ super message
111
+
112
+ end
113
+
114
+ end
115
+
116
+
117
+ class ProjectNameNotProvidedError < Error
118
+
119
+ def initialize
120
+
121
+ message = "Error running compote: Make sure that you provided env variable COMPOSE_PROJECT_NAME."
122
+
123
+ super message
124
+
125
+ end
126
+
127
+ end
128
+
129
+
130
+ class UndefinedCommandError < Error
131
+
132
+ def initialize ( service_name:, command_name:, config: )
133
+
134
+ message = "Error running compote command: Can't find command named \#{ command_name }\" for service \"#{ service_name }\" in config \"#{ config.file_name }\"."
135
+
136
+ message += "\n" + "Seems that config doesn't have a service with such name." unless config.has_service_config? service_name
137
+
138
+ super message
139
+
140
+ end
141
+
142
+ end
143
+
144
+ end