alterant 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 58a70e9ec9e231c880a288c9c1ad0c0a839a2721448ed869567ab3b8b926c874
4
+ data.tar.gz: 9584c3391e717d9317a6513ec2d9e7b768d22aac33f368bf488aa3e9babed725
5
+ SHA512:
6
+ metadata.gz: 1d076a7e5e5b1949740d829545223e75095f9fdbd96d7fc7abe0ea3de459ea9ac6a326964f28a27a2f3b07f03e70378c72f2ba44b72359cd76f3370df1152e8b
7
+ data.tar.gz: f2518b91cbe170114b526e3c873906b63894efd52ddac645974cd8094a7c1c82e98713ffd9d2a87a9ca148d1e3d67a56c76390da6513b58adc0c23c79148e65e
@@ -0,0 +1,9 @@
1
+ ![Alterant Logo](https://s3.amazonaws.com/cdn.cloud66.com/images/alterant_logo.png)
2
+
3
+ # Alterant
4
+
5
+ Alterant is a tool to modify JSON and YAML configuration files.
6
+
7
+ ```bash
8
+ alterant modify --in config.yml --out output.yml --modifier script.js
9
+ ```
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/env ruby
2
+ require 'thor'
3
+ require 'yaml'
4
+ require 'colorize'
5
+ require_relative '../lib/alterant'
6
+
7
+ module Alterant
8
+ class AlterantCLI < Thor
9
+ package_name "alterant"
10
+
11
+ desc "version", "Show Alterant version"
12
+ def version
13
+ say "#{::Alterant::APP_NAME} #{::Alterant::VERSION}\n#{::Alterant::COPYRIGHT_MESSAGE}"
14
+ end
15
+
16
+ desc "update", "Update Alterant to the latest version"
17
+ option :version, type: :string, desc: "Force a specific version"
18
+ def update
19
+ say "Updating Alterant..."
20
+ unless options[:version]
21
+ say `gem install alterant --no-ri --no-rdoc`
22
+ else
23
+ say `gem install alterant -v #{options[:version]} --no-ri --no-rdoc`
24
+ end
25
+ end
26
+
27
+ desc "modify", "Runs the given script against a file"
28
+ option :modifier, type: :string, desc: "Alterant JS script"
29
+ option :in, type: :string, desc: "Input configuration file"
30
+ option :out, type: :string, desc: "Output configuration file"
31
+ option :output_format, type: :string, enum: ['yaml', 'json'], default: 'yaml', desc: "Output format"
32
+ option :diff, type: :boolean, default: false, desc: "Return the diff instead of the output itself"
33
+ option :debug, type: :boolean, default: false
34
+ option :input_format, type: :string, enum: ['yaml', 'json'], default: 'yaml', desc: "Input format if it's based on a stream and not a file"
35
+ option :overwrite, type: :boolean, default: false
36
+ def modify
37
+ $debug = options[:debug] || false
38
+ overwrite = options[:overwrite]
39
+ diff = options[:diff]
40
+ output_format = options[:output_format]
41
+ input_format = options[:input_format]
42
+
43
+ in_file = options[:in]
44
+ if !in_file
45
+ STDERR.puts "No input file provided. Use --in option".red
46
+ exit(1)
47
+ end
48
+ if in_file != '-'
49
+ unless File.exists?(in_file)
50
+ STDERR.puts "Input file #{in_file} not found".red
51
+ exit(1)
52
+ end
53
+ # detect the type
54
+ input_ext = File.extname(in_file)
55
+ unless ['.yaml', '.yml', '.json'].include? input_ext
56
+ STDERR.puts "Only yaml and json files are supported for input".red
57
+ exit(1)
58
+ end
59
+ if ['.yaml', '.yml'].include?(input_ext)
60
+ input_format = 'yaml'
61
+ else
62
+ input_format = 'json'
63
+ end
64
+
65
+ output_format = input_format if !output_format
66
+ end
67
+
68
+ modifier_file = options[:modifier]
69
+ if !modifier_file
70
+ STDERR.puts "No script file provided. Use --modifier option".red
71
+ exit(1)
72
+ end
73
+ unless File.exists?(modifier_file)
74
+ STDERR.puts "Modifier file #{modifier_file} not found".red
75
+ exit(1)
76
+ end
77
+
78
+ out_file = options[:out]
79
+ if out_file
80
+ if !overwrite && File.exists?(out_file)
81
+ STDERR.puts "Output file #{out_file} already exists. Use --overwrite flag to overwrite it".red
82
+ exit(1)
83
+ end
84
+ else
85
+ out_file = '-' # output to stdout
86
+ end
87
+
88
+
89
+ data = []
90
+ if in_file == '-'
91
+ input_text = STDIN.read
92
+ else
93
+ input_text = File.read(in_file)
94
+ end
95
+
96
+ if input_format == 'yaml'
97
+ if input_text.empty?
98
+ STDERR.puts "Empty input file".red
99
+ exit(2)
100
+ end
101
+
102
+ input_text.split('---').each_with_index do |part, idx|
103
+ part_data = YAML.load(part)
104
+ data << part_data
105
+ end
106
+
107
+ else
108
+ file_data = JSON.parse(input_text)
109
+ if file_data.is_a? Array
110
+ data = file_data
111
+ else
112
+ data = [file_data]
113
+ end
114
+ end
115
+
116
+ run_context = {}
117
+ modifier = File.read(modifier_file)
118
+ if in_file != '-'
119
+ run_context[:basedir] = File.dirname(in_file)
120
+ end
121
+
122
+ run_context[:js_preload] = ::Alterant::Classes.LoadClasses
123
+ alter = ::Alterant::Alterant.new(input: data, modifier: modifier, filename: modifier_file, options: run_context)
124
+ results = alter.execute(timeout: 500)
125
+
126
+ if results.nil?
127
+ STDERR.puts "Aborting".red
128
+ exit(2)
129
+ end
130
+
131
+ if output_format == 'yaml' || output_format == 'yml'
132
+ converted_as_text = results.map { |r| r.to_yaml }.join("\n")
133
+ input = data.map { |r| r.to_yaml }.join("\n")
134
+ elsif output_format == 'json'
135
+ converted_as_text = JSON.pretty_generate(results)
136
+ input = JSON.pretty_generate(data)
137
+ end
138
+
139
+ if diff
140
+ output_as_text = Diffy::Diff.new(input, converted_as_text)
141
+ else
142
+ output_as_text = converted_as_text
143
+ end
144
+
145
+ if out_file == "-"
146
+ puts output_as_text
147
+ else
148
+ File.open(out_file, 'w') do |file|
149
+ file.write(output_as_text)
150
+ end
151
+ end
152
+ return true
153
+ rescue ::Alterant::ParseError => exc
154
+ STDERR.puts "Syntax error: #{exc.message}".red
155
+ return false
156
+ rescue ::Alterant::RuntimeError => exc
157
+ STDERR.puts "Runtime error: #{exc.message}".red
158
+ return false
159
+ rescue => exc
160
+ if $debug
161
+ raise
162
+ else
163
+ STDERR.puts exc
164
+ end
165
+ return false
166
+ end
167
+ end
168
+
169
+ AlterantCLI.start
170
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "alterant"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,6 @@
1
+ require_relative 'alterant/version'
2
+ require_relative 'alterant/utils'
3
+ require_relative 'alterant/loader'
4
+
5
+ module Alterant
6
+ end
@@ -0,0 +1,59 @@
1
+ module Alterant
2
+ class Alterant
3
+ attr_reader :basedir
4
+
5
+ # input is a hash
6
+ # filename is the modifier filename use for the backtrace
7
+ # modifier is the script in string
8
+ def initialize(input:, modifier:, filename:, options: {})
9
+ @modifier = modifier
10
+ @filename = filename
11
+ @input = input
12
+ @basedir = options[:basedir]
13
+ @js_preload = options[:js_preload] || []
14
+ end
15
+
16
+ # timeout is in ms
17
+ # returns a hash
18
+ def execute(timeout: 500, max_memory: 5000000)
19
+ jpath = ::Alterant::Helpers::Jpath.new
20
+
21
+ result = []
22
+ snapshot = MiniRacer::Snapshot.new("$$ = #{@input.to_json};\n" + @js_preload.join("\n")) # this is more efficient but we lose debug info (filename) of helper classes
23
+
24
+ isolate = MiniRacer::Isolate.new(snapshot)
25
+ @input.each_with_index do |input, idx|
26
+ ctx = ::MiniRacer::Context.new(isolate: isolate, timeout: timeout, max_memory: max_memory)
27
+ ctx.eval("$ = #{input.to_json}")
28
+ ctx.eval("$['fetch'] = function(key) { return jpath.fetch(JSON.stringify($), key); }")
29
+ ctx.attach('jpath.fetch', proc{|x, y| jpath.fetch(x, y)})
30
+ ctx.attach('console.log', proc{|x| STDERR.puts("DEBUG: #{x.inspect}") if $debug })
31
+ ctx.attach('console.exception', proc{|x| raise ::Alterant::RuntimeError, x })
32
+ ctx.attach('$$.push', proc{|x| result << x })
33
+ ctx.attach('$.index', proc{ idx })
34
+ ctx.attach('YamlReader', ::Alterant::Classes::YamlReader.new(self, ctx))
35
+ ctx.attach('JsonReader', ::Alterant::Classes::JsonReader.new(self, ctx))
36
+
37
+ ctx.eval(@modifier, filename: @filename)
38
+ pre_convert = ctx.eval("JSON.stringify($)")
39
+ converted = JSON.parse(pre_convert)
40
+ result << converted
41
+
42
+ ctx.dispose
43
+ isolate.idle_notification(100)
44
+ rescue ::MiniRacer::RuntimeError => exc
45
+ if $debug
46
+ raise
47
+ else
48
+ raise ::Alterant::ParseError, "part: #{idx} - #{exc.message}, #{exc.backtrace.first}"
49
+ end
50
+ rescue ::Alterant::AlterantError => exc
51
+ STDERR.puts exc.message.red
52
+ return nil
53
+ end
54
+
55
+ return result
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,20 @@
1
+ module Alterant
2
+ module Classes
3
+ CLASSES = {}
4
+
5
+ def self.LoadClasses
6
+ # ::Alterant::Classes.constants.map(&::Alterant::Classes.method(:const_get)).grep(Class) do |c|
7
+ # name = c.name.split('::').last
8
+ # ::Alterant::Classes::CLASSES[name] = c
9
+ # end
10
+
11
+ # load all JS files in the classes dir and construct a long text
12
+ js_preload = []
13
+ Dir["#{__dir__}/*.js"].each do |f|
14
+ js_preload << File.read(f)
15
+ end
16
+
17
+ return js_preload
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ class Containers {
2
+ constructor(containers) {
3
+ this.containers = containers
4
+ }
5
+
6
+ by_name(name) {
7
+ for (var c in this.containers) {
8
+ var item = this.containers[c]
9
+ if (item.name == name) {
10
+ return item
11
+ }
12
+ }
13
+
14
+ return null
15
+ }
16
+ }
@@ -0,0 +1,51 @@
1
+ class DockerImage {
2
+ constructor(source_image) {
3
+ var full_image = source_image.trim();
4
+ var full_name = '';
5
+ if (!full_image.includes('/')) {
6
+ full_name = "library/" + full_image;
7
+ }
8
+ if (!full_image.includes('/')) {
9
+ full_image = "library/" + full_image;
10
+ }
11
+ if (!full_image.includes(":")) {
12
+ full_image = full_image + ":latest";
13
+ }
14
+ var proto = 'https://';
15
+ if (/^https/.test(full_image)) {
16
+ proto = 'https://';
17
+ full_image = full_image.replace(/https:\/\//, '');
18
+ }
19
+ else if (/^http/.test(full_image)) {
20
+ full_image = full_image.replace(/http:\/\//, '');
21
+ }
22
+ // trim / from front and back
23
+ full_image = full_image.replace(/^\//, '').replace(/\/$/, '');
24
+ var registry
25
+ // figure out registry
26
+ if (/^library\//.test(full_image) || full_image.split('/').length < 3) {
27
+ // its docker io
28
+ registry = 'index.docker.io';
29
+ }
30
+ else {
31
+ registry = full_image.replace(/\/.*/, '');
32
+ }
33
+ // figure out image name
34
+ full_image = full_image.replace(/#{registry}(\/(v|V)(1|2)|)/i, '').replace(/^\//, '').replace(/\/$/, '');
35
+ var image_parts = full_image.split(':');
36
+ var image_name = image_parts[0];
37
+ var image_tag = image_parts[1];
38
+ // recombine for registry
39
+ var registry_url = proto + registry;
40
+ var fqin = registry_url + "/" + full_image;
41
+ this.fqin = fqin;
42
+ this.registry = registry;
43
+ this.registry_url = registry_url;
44
+ this.name = image_name;
45
+ this.tag = image_tag;
46
+ }
47
+
48
+ address() {
49
+ return this.registry + this.name + ":" + this.tag
50
+ }
51
+ }
@@ -0,0 +1,22 @@
1
+ module Alterant
2
+ module Classes
3
+ class JsonReader
4
+ attr_reader :value
5
+
6
+ def call(file)
7
+ if @alter.basedir.nil?
8
+ raise ::Alterant::RuntimeError, 'no basedir set'
9
+ end
10
+
11
+ content = File.read(File.join(@alter.basedir, file))
12
+ return ::JSON.load(content)
13
+ end
14
+
15
+ def initialize(alter, context)
16
+ @context = context
17
+ @alter = alter
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,16 @@
1
+ class Ports {
2
+ constructor(container) {
3
+ this.ports = container.ports
4
+ }
5
+
6
+ containerPorts() {
7
+ var ports = this.ports;
8
+ var port_numbers = new Array();
9
+
10
+ for (var item in ports) {
11
+ port_numbers.push(ports[item]["containerPort"]);
12
+ }
13
+
14
+ return port_numbers;
15
+ }
16
+ }
@@ -0,0 +1,22 @@
1
+ module Alterant
2
+ module Classes
3
+ class YamlReader
4
+ attr_reader :value
5
+
6
+ def call(file)
7
+ if @alter.basedir.nil?
8
+ raise ::Alterant::RuntimeError, 'no basedir set'
9
+ end
10
+
11
+ content = File.read(File.join(@alter.basedir, file))
12
+ return ::YAML.safe_load(content)
13
+ end
14
+
15
+ def initialize(alter, context)
16
+ @context = context
17
+ @alter = alter
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,5 @@
1
+ module Alterant
2
+ class AlterantError < StandardError; end
3
+ class ParseError < AlterantError; end
4
+ class RuntimeError < AlterantError; end
5
+ end
@@ -0,0 +1,12 @@
1
+ module Alterant
2
+ module Helpers
3
+ class Jpath
4
+ require 'jsonpath'
5
+
6
+ def fetch(context, key)
7
+ return ::JsonPath.on(context, key)
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ require 'mini_racer'
2
+ require 'yaml'
3
+ require 'json'
4
+ require 'diffy'
5
+
6
+ Dir.glob File.join(__dir__, 'helpers', '**', '*.rb'), &method(:require)
7
+ Dir.glob File.join(__dir__, 'classes', '**', '*.rb'), &method(:require)
8
+
9
+ # Load other Alterant classes.
10
+ Dir.glob File.join(__dir__, '**', '*.rb'), &method(:require)
11
+
@@ -0,0 +1,11 @@
1
+ class String
2
+ def underscore
3
+ word = self.dup
4
+ word.gsub!(/::/, '/')
5
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
6
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
7
+ word.tr!("-", "_")
8
+ word.downcase!
9
+ word
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ module Alterant
2
+ VERSION = '0.0.1'
3
+ COPYRIGHT_MESSAGE = "(c) 2018 Cloud66 Inc."
4
+ APP_NAME = 'Alterant'
5
+ end
metadata ADDED
@@ -0,0 +1,190 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: alterant
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Khash Sajadi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-10-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rerun
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.13'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.13'
55
+ - !ruby/object:Gem::Dependency
56
+ name: jsonpath
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.9'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.9'
69
+ - !ruby/object:Gem::Dependency
70
+ name: json
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.4'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: thor
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.20'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.20'
97
+ - !ruby/object:Gem::Dependency
98
+ name: mini_racer
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.2'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.2'
111
+ - !ruby/object:Gem::Dependency
112
+ name: colorize
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.8'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.8'
125
+ - !ruby/object:Gem::Dependency
126
+ name: diffy
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.2'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.2'
139
+ description: Alterant is a tool to alter configuration files
140
+ email:
141
+ - khash@cloud66.com
142
+ executables:
143
+ - alterant
144
+ - console
145
+ - setup
146
+ extensions: []
147
+ extra_rdoc_files: []
148
+ files:
149
+ - README.md
150
+ - bin/alterant
151
+ - bin/console
152
+ - bin/setup
153
+ - lib/alterant.rb
154
+ - lib/alterant/alterant.rb
155
+ - lib/alterant/classes/classes.rb
156
+ - lib/alterant/classes/containers.js
157
+ - lib/alterant/classes/docker_image.js
158
+ - lib/alterant/classes/json_reader.rb
159
+ - lib/alterant/classes/ports.js
160
+ - lib/alterant/classes/yaml_reader.rb
161
+ - lib/alterant/error.rb
162
+ - lib/alterant/helpers/jpath.rb
163
+ - lib/alterant/loader.rb
164
+ - lib/alterant/utils.rb
165
+ - lib/alterant/version.rb
166
+ homepage: https://github.com/cloud66/alterant
167
+ licenses:
168
+ - Nonstandard
169
+ metadata: {}
170
+ post_install_message:
171
+ rdoc_options: []
172
+ require_paths:
173
+ - lib
174
+ required_ruby_version: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ required_rubygems_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ requirements: []
185
+ rubyforge_project:
186
+ rubygems_version: 2.7.7
187
+ signing_key:
188
+ specification_version: 4
189
+ summary: Alterant gem and command line
190
+ test_files: []