supermarket_sync 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 818056b410af5497f35e4e401e0d527890627e1be89b43733c8c2572b20fecd1
4
+ data.tar.gz: c003e878b9c3986ac4d1996ca37a097af0133475c3913add3c16a5b8b564f0b4
5
+ SHA512:
6
+ metadata.gz: e5063cfe32685f9f93794fa626608963d2aa1432749f9406d6a6ba5b84492384ccec8e4dd16c089a06200e881237a9da9fe8933da4d230ca91e646b6bc3cb018
7
+ data.tar.gz: 9b12d8f1b4dceda17a36783eae45004bd9ff3fa7644ff35e8ab503edba66ad640478432f6a0457723612e66f9bd4cb6c3951c9c190abac5b1f7c271b2f64ac23
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ *~
2
+ *#
3
+ .#*
4
+ \#*#
5
+ .*.sw[a-z]
6
+ *.un~
7
+ pkg/
8
+ vendor/
data/.gitlab-ci.yml ADDED
@@ -0,0 +1,38 @@
1
+ before_script:
2
+ - ruby -v
3
+ - which ruby
4
+ - gem install bundler --no-document
5
+ - bundle install --jobs $(nproc) --path vendor/bundle
6
+
7
+ test:Ruby 2.5:
8
+ image: ruby:2.5
9
+ cache:
10
+ paths:
11
+ - .bundle
12
+ - vendor/bundle
13
+ script:
14
+ - bundle exec rubocop
15
+ tags:
16
+ - ruby
17
+ except:
18
+ - tags
19
+
20
+ release:Ruby 2.5:
21
+ image: ruby:2.5
22
+ cache:
23
+ paths:
24
+ - .bundle
25
+ - vendor/bundle
26
+ script:
27
+ - bundle exec rubocop
28
+ - gem install dpl --no-document
29
+ - find . -type f -exec chmod o--w "{}" \;
30
+ - dpl --provider=rubygems --api-key=$RUBYGEMS_API_KEY --gem=supermarket_sync
31
+ artifacts:
32
+ name: "supermarket_sync-$CI_BUILD_REF_NAME"
33
+ paths:
34
+ - ./*.gem
35
+ tags:
36
+ - ruby
37
+ only:
38
+ - tags
data/.rubocop.yml ADDED
@@ -0,0 +1,23 @@
1
+ AllCops:
2
+ DisplayStyleGuide: true
3
+ DisplayCopNames: true
4
+ TargetRubyVersion: 2.5
5
+ Include:
6
+ - lib/
7
+ - Gemfile
8
+ - Rakefile
9
+
10
+ Layout/EmptyLineAfterMagicComment:
11
+ Enabled: false
12
+
13
+ Metrics/LineLength:
14
+ Max: 120
15
+
16
+ Style/CommentedKeyword:
17
+ Enabled: false
18
+
19
+ Style/Encoding:
20
+ Enabled: false
21
+
22
+ Style/FrozenStringLiteralComment:
23
+ Enabled: false
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ supermarket_sync
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.5.3
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ SupermarketSync Changelog
2
+ =========================
3
+ This file is used to list changes made in each version of the `supermarket_sync` gem.
4
+
5
+ v0.1.0 (2016-11-09)
6
+ -------------------
7
+ - Initial Release TBD
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Brian Dwyer - Broadridge Financial Solutions
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # SupermarketSync
2
+ * Ruby Gem to programmatically synchronize Chef Supermarkets
3
+
4
+ ## Background
5
+ This gem was built from a need to synchronize a private, internal Chef Supermarket from the Public Chef Supermarket.
6
+
7
+ At the same time, being able to send notifications of changes and deprecations was also desired.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'supermarket_sync'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install supermarket_sync
24
+
25
+ ## Usage
26
+
27
+ ```shell
28
+ Usage: supermarket_sync (options)
29
+ -c, --config CONFIG Path to the configuration file to use
30
+ --cookbooks-json CONFIG List of cookbooks to synchronize
31
+ ```
32
+
33
+ You can supply individual configuration for supermarkets invidivually via the configuration file.
34
+
35
+ Otherwise, you can configure global Chef parameters via environment variables. This may prove handy in a CI pipeline scenario.
36
+
37
+ ```
38
+ export SM_USER='supermarket_user'
39
+ export SM_KEY='/path/to/my/key.pem'
40
+ ```
41
+
42
+ #### Configuration Samples
43
+
44
+ `config.json`
45
+ ```json
46
+ {
47
+ "notification": {
48
+ "url": "https://hooks.slack.com/services/T23CHARKH/blah/blahbetyblah",
49
+ "username": "Chef - Supermarket Sync",
50
+ "channels": [
51
+ "@bdwyertech",
52
+ "#chef-pipeline"
53
+ ]
54
+ },
55
+ "supermarkets": {
56
+ "Enterprise BU1": {
57
+ "url": "https://supermarket.bu1.contoso.net"
58
+ },
59
+ "Enterprise BU2": {
60
+ "url": "https://supermarket.bu2.contoso.net",
61
+ "user": "bu2_admin",
62
+ "key": "/path/to/bu2/admin/key.pem"
63
+ }
64
+ }
65
+ }
66
+ ```
67
+
68
+ `cookbooks.json`
69
+ ```json
70
+ {
71
+ "cookbooks": [
72
+ "apt",
73
+ "java",
74
+ "mysql",
75
+ "yum"
76
+ ]
77
+ }
78
+ ```
79
+
80
+ ####
81
+
82
+
83
+ ## Development
84
+
85
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
86
+
87
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
88
+
89
+ ## Contributing
90
+
91
+ Bug reports and pull requests are welcome on GitHub at https://github.com/bdwyertech/supermarket_sync. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
92
+
93
+
94
+ ## License
95
+
96
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require 'bundler/setup'
7
+ require 'supermarket_sync'
8
+
9
+ # You can add fixtures and/or initialization code here to make experimenting
10
+ # with your gem easier. You can also use a different console, if you like.
11
+
12
+ # (If you use this, don't forget to add pry to your Gemfile!)
13
+ # require "pry"
14
+ # Pry.start
15
+
16
+ require 'irb'
17
+ IRB.start
data/bin/setup ADDED
@@ -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
+ {
2
+ "notify": {
3
+ },
4
+ "supermarkets": {
5
+ }
6
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "cookbooks": [
3
+ ]
4
+ }
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+ # Chef Supermarket Sync Utility
4
+ # Brian Dwyer - Intelligent Digital Services - 11/7/18
5
+
6
+ lib = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
8
+
9
+ # => Catch Ctrl+C's to avoid stack traces
10
+ Signal.trap('INT') { abort }
11
+
12
+ require 'supermarket_sync'
13
+
14
+ SupermarketSync::CLI.run(ARGV)
@@ -0,0 +1,69 @@
1
+ # Encoding: UTF-8
2
+ #
3
+ # Gem Name:: supermarket_sync
4
+ # Module:: CLI
5
+ #
6
+ # The MIT License (MIT)
7
+ #
8
+ # Copyright:: 2018, Brian Dwyer - Broadridge Financial Solutions
9
+ #
10
+
11
+ require 'mixlib/cli'
12
+ require 'supermarket_sync/config'
13
+ require 'supermarket_sync/util'
14
+
15
+ module SupermarketSync
16
+ #
17
+ # => SupermarketSync Launcher
18
+ #
19
+ module CLI
20
+ module_function
21
+
22
+ #
23
+ # => Options Parser
24
+ #
25
+ class Options
26
+ # => Mix-In the CLI Option Parser
27
+ include Mixlib::CLI
28
+ option :config_file,
29
+ short: '-c CONFIG',
30
+ long: '--config CONFIG',
31
+ description: 'Path to the configuration file to use'
32
+
33
+ option :cookbooks_file,
34
+ long: '--cookbooks-json CONFIG',
35
+ description: 'List of cookbooks to synchronize'
36
+ end
37
+
38
+ #
39
+ # => Launch the Application
40
+ #
41
+ def run(argv = ARGV) # rubocop: disable AbcSize, MethodLength
42
+ #
43
+ # => Parse CLI Configuration
44
+ #
45
+ cli = Options.new
46
+ cli.parse_options(argv)
47
+
48
+ # => Grab the Default Values
49
+ default = Config.options
50
+
51
+ # => Parse JSON Config File (If Specified and Exists)
52
+ json_config = Util.parse_json(cli.config[:config_file] || Config.config_file)
53
+
54
+ # => Merge Configuration (CLI Wins)
55
+ config = [default, json_config, cli.config].compact.reduce(:merge)
56
+
57
+ # => Apply Configuration
58
+ Config.setup do |cfg|
59
+ cfg.cookbooks_file = config[:cookbooks_file]
60
+ cfg.notification = config[:notification]
61
+ cfg.supermarkets = config[:supermarkets]
62
+ cfg.source = config[:source]
63
+ end
64
+
65
+ # => Start the Sync
66
+ Sync.run!
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,81 @@
1
+ # Encoding: UTF-8
2
+ #
3
+ # Gem Name:: supermarket_sync
4
+ # Module:: Config
5
+ #
6
+ # The MIT License (MIT)
7
+ #
8
+ # Copyright:: 2018, Brian Dwyer - Broadridge Financial Solutions
9
+ #
10
+
11
+ require 'supermarket_sync/helpers/configuration'
12
+ require 'pathname'
13
+
14
+ module SupermarketSync
15
+ # => This is the Configuration module.
16
+ module Config
17
+ module_function
18
+
19
+ extend Configuration
20
+
21
+ # => Gem Root Directory
22
+ define_setting :root, Pathname.new(File.expand_path('../..', __dir__))
23
+
24
+ # => My Name
25
+ define_setting :author, 'Brian Dwyer - Broadridge Financial Services'
26
+
27
+ # => Config File
28
+ define_setting :config_file, File.join(root, 'config', 'config.json')
29
+
30
+ # => Cookbooks File
31
+ define_setting :cookbooks_file, File.join(root, 'config', 'cookbooks.json')
32
+
33
+ # => Supermarket Configuration
34
+ define_setting :supermarkets, {}
35
+
36
+ # => Global Notification
37
+ define_setting :notification, {}
38
+
39
+ # => Global Supermarket Source
40
+ define_setting :source, 'https://supermarket.chef.io'
41
+
42
+ # => Global Chef Defaults
43
+ define_setting :defaults, url: ENV['SM_URL'],
44
+ user: ENV['SM_USER'],
45
+ key: ENV['SM_KEY']
46
+
47
+ #
48
+ # Facilitate Dynamic Addition of Configuration Values
49
+ #
50
+ # @return [class_variable]
51
+ #
52
+ def add(config = {})
53
+ config.each do |key, value|
54
+ define_setting key.to_sym, value
55
+ end
56
+ end
57
+
58
+ #
59
+ # Facilitate Dynamic Removal of Configuration Values
60
+ #
61
+ # @return nil
62
+ #
63
+ def clear(config)
64
+ Array(config).each do |setting|
65
+ delete_setting setting
66
+ end
67
+ end
68
+
69
+ #
70
+ # List the Configurable Keys as a Hash
71
+ #
72
+ # @return [Hash]
73
+ #
74
+ def options
75
+ map = SupermarketSync::Config.class_variables.map do |key|
76
+ [key.to_s.tr('@', '').to_sym, class_variable_get(:"#{key}")]
77
+ end
78
+ Hash[map]
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,57 @@
1
+ # Encoding: UTF-8
2
+ #
3
+ # Gem Name:: supermarket_sync
4
+ # Helper:: Configuration
5
+ #
6
+ # Author: Eli Fatsi - https://www.viget.com/articles/easy-gem-configuration-variables-with-defaults
7
+ # Contributor: Brian Dwyer - Intelligent Digital Services
8
+ #
9
+
10
+ # => Configuration Helper Module
11
+ module Configuration
12
+ #
13
+ # => Provides a method to configure an Application
14
+ # => Example:
15
+ # SupermarketSync::Config.setup do |cfg|
16
+ # cfg.config_file = 'abc.json'
17
+ # cfg.app_name = 'GemBase'
18
+ # end
19
+ #
20
+ def setup
21
+ yield self
22
+ end
23
+
24
+ def define_setting(name, default = nil)
25
+ class_variable_set("@@#{name}", default)
26
+
27
+ define_class_method "#{name}=" do |value|
28
+ class_variable_set("@@#{name}", value)
29
+ end
30
+
31
+ define_class_method name do
32
+ class_variable_get("@@#{name}")
33
+ end
34
+ end
35
+
36
+ def delete_setting(name)
37
+ remove_class_variable("@@#{name}")
38
+
39
+ delete_class_method(name)
40
+ rescue NameError # => Handle Non-Existent Settings
41
+ nil
42
+ end
43
+
44
+ private
45
+
46
+ def define_class_method(name, &block)
47
+ (class << self; self; end).instance_eval do
48
+ define_method name, &block
49
+ end
50
+ end
51
+
52
+ def delete_class_method(name)
53
+ (class << self; self; end).instance_eval do
54
+ undef_method name
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,108 @@
1
+ # Encoding: UTF-8
2
+ #
3
+ # Gem Name:: supermarket_sync
4
+ # Module:: Notifier
5
+ #
6
+ # The MIT License (MIT)
7
+ #
8
+ # Copyright:: 2018, Brian Dwyer - Broadridge Financial Solutions
9
+ #
10
+
11
+ require 'slack-notifier'
12
+
13
+ module SupermarketSync
14
+ #
15
+ # => Notifications
16
+ #
17
+ class Notifier
18
+ attr_accessor :url, :username, :channels
19
+
20
+ def initialize(**args)
21
+ @url = args[:url]
22
+ @channels = args[:channels]
23
+ @username = args[:username]
24
+ @http_opts = args[:http_opts] || {}
25
+ yield self if block_given?
26
+ end
27
+
28
+ def send!
29
+ messages = [build_deprecated, build_updated].compact
30
+ return unless messages.any?
31
+ Array(@channels).each do |channel|
32
+ slack.ping '', attachments: messages, channel: channel
33
+ end
34
+ end
35
+
36
+ def slack
37
+ (cfg = {})[:username] = @username
38
+ cfg[:http_options] = { verify_mode: OpenSSL::SSL::VERIFY_NONE }
39
+
40
+ Slack::Notifier.new @url do
41
+ defaults cfg
42
+ end
43
+ end
44
+
45
+ #
46
+ # => Accumulators
47
+ #
48
+
49
+ def deprecated
50
+ @deprecated ||= []
51
+ end
52
+
53
+ def updated
54
+ @updated ||= []
55
+ end
56
+
57
+ #
58
+ # => Message Constructors
59
+ #
60
+ private def build_deprecated # rubocop: disable MethodLength
61
+ return unless deprecated.any?
62
+ fields = deprecated.map do |cb|
63
+ {
64
+ title: cb[:source]['name'],
65
+ short: true,
66
+ value: <<-MSGBODY.gsub(/^\s+/, '')
67
+ * Replacement: #{cb[:source]['replacement'] || '**NONE**'}
68
+ MSGBODY
69
+ }
70
+ end
71
+
72
+ {
73
+ color: '#FF0000',
74
+ mrkdwn_in: %w[pretext fields],
75
+ fields: fields,
76
+ pretext: <<-MSGBODY.gsub(/^\s+/, '')
77
+ <!here> :warning: **The following cookbooks have been deprecated:**
78
+ MSGBODY
79
+ }
80
+ end
81
+
82
+ private def build_updated # rubocop: disable AbcSize, MethodLength
83
+ return unless updated.any?
84
+ rows = updated.map do |cb|
85
+ [
86
+ '|',
87
+ "[#{cb[:source]['name']}](#{cb[:source]['external_url']})",
88
+ '|',
89
+ ::File.basename(cb[:source]['latest_version']),
90
+ '|',
91
+ cb[:source]['deprecated'] ? ':warning:' : ':white_check_mark:',
92
+ '|'
93
+ ].join(' ')
94
+ end.join("\n")
95
+
96
+ {
97
+ mrkdwn_in: %w[text],
98
+ pretext: '<!here> :checkered_flag: **The following cookbooks have been synchronized:**',
99
+ short: false,
100
+ text: <<-MSGBODY.gsub(/^\s+/, '')
101
+ | Cookbook | Version | Deprecation Status |
102
+ |:--------:|:-------:|:------------------:|
103
+ #{rows}
104
+ MSGBODY
105
+ }
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,150 @@
1
+ # Encoding: UTF-8
2
+ #
3
+ # Gem Name:: supermarket_sync
4
+ # Module:: Sync
5
+ #
6
+ # The MIT License (MIT)
7
+ #
8
+ # Copyright:: 2018, Brian Dwyer - Broadridge Financial Solutions
9
+ #
10
+
11
+ require 'chef/http/json_input'
12
+ require 'chef/http/json_output'
13
+ require 'chef/http/remote_request_id'
14
+ require 'chef/http/simple_json'
15
+ require 'chef/cookbook_site_streaming_uploader'
16
+ require 'mixlib/cli'
17
+ require 'supermarket_sync/notifier'
18
+
19
+ module SupermarketSync
20
+ #
21
+ # => Supermarket Synchronization Logic Controller
22
+ #
23
+ module Sync
24
+ module_function
25
+
26
+ def run! # rubocop: disable AbcSize, CyclomaticComplexity, MethodLength, PerceivedComplexity
27
+ Config.supermarkets.each do |name, cfg| # rubocop: disable BlockLength
28
+ puts "Synchronizing #{name}"
29
+
30
+ # => Set Configuration
31
+ configure(cfg)
32
+
33
+ # => Parse the Cookbooks List
34
+ cookbooks = Array(Util.parse_json(Config.cookbooks_file)[:cookbooks])
35
+
36
+ cookbooks.each do |cookbook| # rubocop: disable BlockLength
37
+ cookbook = cookbook.keys.first if cookbook.is_a?(Hash)
38
+ puts "Checking #{cookbook}"
39
+ # => Grab Source Metadata
40
+ source_meta = begin
41
+ source.get("/api/v1/cookbooks/#{cookbook}")
42
+ rescue Net::HTTPServerException => e
43
+ raise e unless e.response.code == '404'
44
+ puts 'Cookbook not available on Source Supermarket'
45
+ next
46
+ end
47
+ # => Grab Latest Available Version Number
48
+ latest = ::Gem::Version.new(::File.basename(source_meta['latest_version']))
49
+
50
+ # => Grab Destination Metadata
51
+ dest_meta = begin
52
+ dest.get("/api/v1/cookbooks/#{cookbook}")
53
+ rescue Net::HTTPServerException => e
54
+ raise e unless e.response.code == '404'
55
+ # => Cookbook not found -- Initial Upload
56
+ {}['latest_version'] = '0.0.0'
57
+ end
58
+ # => Determine Current Version
59
+ current = ::Gem::Version.new(::File.basename(dest_meta['latest_version']))
60
+
61
+ if latest > current
62
+ puts 'Updating...'
63
+ puts "Source: #{latest}"
64
+ puts "Destination: #{current}"
65
+
66
+ # => Retrieve the Cookbook
67
+ tgz = source.streaming_request("/api/v1/cookbooks/#{cookbook}/versions/#{latest}/download")
68
+
69
+ # => Upload the Cookbook
70
+ upload(source_meta['category'], tgz)
71
+
72
+ # => Remove the Tempfile
73
+ begin
74
+ retries ||= 2
75
+ ::File.delete(tgz)
76
+ rescue => e # rubocop: disable RescueStandardError
77
+ raise e if (retries -= 1).negative?
78
+ puts "#{e.class}::#{e.message}"
79
+ puts 'Could not delete Tempfile... Retrying'
80
+ sleep 2
81
+ retry
82
+ end
83
+ @notify&.updated&.push(source: source_meta, dest: dest_meta)
84
+ end
85
+ # => Identify Deprecated Cookbooks
86
+ next unless source_meta['deprecated'] && !dest_meta['deprecated']
87
+ @notify&.deprecated&.push(source: source_meta, dest: dest_meta)
88
+ end
89
+ ensure
90
+ # => Send Notifications
91
+ @notify&.send!
92
+ end
93
+ end
94
+
95
+ #
96
+ # => Configuration Context
97
+ #
98
+
99
+ private def configure(context) # rubocop: disable AbcSize, MethodLength
100
+ Chef::Config.tap do |cfg|
101
+ cfg.chef_server_url = context[:url]
102
+ cfg.node_name = context[:user] || ENV['SM_USER']
103
+ cfg.client_key = context[:key] || ENV['SM_KEY']
104
+ cfg.ssl_verify_mode = :verify_none
105
+ end
106
+
107
+ if Config.notification.any?
108
+ @notify = Notifier.new do |cfg|
109
+ cfg.url = Config.notification[:url]
110
+ cfg.channels = Config.notification[:channels]
111
+ cfg.username = Config.notification[:username]
112
+ end
113
+ end
114
+
115
+ source context[:source] || Config.source
116
+ dest context[:url]
117
+ end
118
+
119
+ #
120
+ # => API Clients
121
+ #
122
+ private def source(url = nil)
123
+ url ||= @source&.url
124
+ raise ArgumentError, 'No URL supplied!' unless url
125
+ return @source if @source&.url == url
126
+ @source = Chef::HTTP::SimpleJSON.new(url)
127
+ end
128
+
129
+ private def dest(url = nil)
130
+ url ||= @dest&.url
131
+ raise ArgumentError, 'No URL supplied!' unless url
132
+ return @dest if @dest&.url == url
133
+ @dest = Chef::HTTP::SimpleJSON.new(url)
134
+ end
135
+
136
+ private def upload(category, tarball) # rubocop: disable AbcSize, MethodLength
137
+ uri = URI.parse(dest.url)
138
+ uri.path = '/api/v1/cookbooks'
139
+ resp = Chef::CookbookSiteStreamingUploader.post(
140
+ uri.to_s, Chef::Config[:node_name], Chef::Config[:client_key],
141
+ tarball: ::File.open(tarball),
142
+ cookbook: Chef::JSONCompat.to_json(category: category)
143
+ )
144
+ return if %w[200 201].include?(resp.code)
145
+ msg = Chef::JSONCompat.to_json_pretty(Chef::JSONCompat.parse(resp.body)) rescue resp.body # rubocop: disable RescueModifier, LineLength
146
+ puts resp.inspect
147
+ raise "\nSupermarket Upload Error:\n#{msg}"
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,41 @@
1
+ # Encoding: UTF-8
2
+ #
3
+ # Gem Name:: supermarket_sync
4
+ # Module:: Util
5
+ #
6
+ # The MIT License (MIT)
7
+ #
8
+ # Copyright:: 2018, Brian Dwyer - Broadridge Financial Solutions
9
+ #
10
+
11
+ require 'json'
12
+
13
+ module SupermarketSync
14
+ # => Utility Methods
15
+ module Util
16
+ module_function
17
+
18
+ ########################
19
+ # => File I/O <= #
20
+ ########################
21
+
22
+ # => Define JSON Parser
23
+ def parse_json(file = nil, symbolize = true)
24
+ return unless file && ::File.exist?(file.to_s)
25
+ begin
26
+ ::JSON.parse(::File.read(file.to_s), symbolize_names: symbolize)
27
+ rescue JSON::ParserError => e
28
+ puts "#{e.class}:: #{file}"
29
+ return
30
+ end
31
+ end
32
+
33
+ # => Define JSON Writer
34
+ def write_json(file, object)
35
+ return unless file && object
36
+ begin
37
+ File.open(file, 'w') { |f| f.write(JSON.pretty_generate(object)) }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module SupermarketSync
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -0,0 +1,35 @@
1
+ # Encoding: UTF-8
2
+ #
3
+ # Gem Name:: supermarket_sync
4
+ #
5
+ # The MIT License (MIT)
6
+ #
7
+ # Copyright:: 2018, Brian Dwyer - Broadridge Financial Solutions
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ # of this software and associated documentation files (the "Software"), to deal
11
+ # in the Software without restriction, including without limitation the rights
12
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ # copies of the Software, and to permit persons to whom the Software is
14
+ # furnished to do so, subject to the following conditions:
15
+ #
16
+ # The above copyright notice and this permission notice shall be included in
17
+ # all copies or substantial portions of the Software.
18
+ #
19
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
+ # THE SOFTWARE.
26
+
27
+ require 'supermarket_sync/cli'
28
+ require 'supermarket_sync/config'
29
+ require 'supermarket_sync/util'
30
+ require 'supermarket_sync/version'
31
+
32
+ # => Chef Supermarket Synchronization Utility
33
+ module SupermarketSync
34
+ autoload :Sync, 'supermarket_sync/sync'
35
+ end
@@ -0,0 +1,39 @@
1
+ # Encoding: UTF-8
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'supermarket_sync/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'supermarket_sync'
9
+ spec.version = SupermarketSync::VERSION
10
+ spec.authors = ['Brian Dwyer']
11
+ spec.email = ['chef@broadridge.com']
12
+
13
+ spec.summary = 'Utility to synchronize Chef Supermarkets'
14
+ spec.homepage = 'https://github.com/bdwyertech/supermarket_sync'
15
+ spec.license = 'MIT'
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata) # rubocop: disable GuardClause
20
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
21
+ else
22
+ raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
23
+ end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = 'exe'
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ['lib']
29
+
30
+ # => Dependencies
31
+ spec.add_runtime_dependency 'chef', '> 12.19.36'
32
+ spec.add_runtime_dependency 'slack-notifier', '~> 2.3.2'
33
+
34
+ # => Development Dependencies
35
+ spec.add_development_dependency 'bundler', '~> 1.12'
36
+ spec.add_development_dependency 'rake', '~> 10.0'
37
+ spec.add_development_dependency 'rspec', '~> 3.0'
38
+ spec.add_development_dependency 'rubocop', '= 0.55'
39
+ end
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: supermarket_sync
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Brian Dwyer
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-11-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: chef
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">"
18
+ - !ruby/object:Gem::Version
19
+ version: 12.19.36
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">"
25
+ - !ruby/object:Gem::Version
26
+ version: 12.19.36
27
+ - !ruby/object:Gem::Dependency
28
+ name: slack-notifier
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.3.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.3.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.12'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.12'
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
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: '0.55'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: '0.55'
97
+ description:
98
+ email:
99
+ - chef@broadridge.com
100
+ executables:
101
+ - supermarket_sync
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - ".gitlab-ci.yml"
107
+ - ".rubocop.yml"
108
+ - ".ruby-gemset"
109
+ - ".ruby-version"
110
+ - CHANGELOG.md
111
+ - Gemfile
112
+ - LICENSE.txt
113
+ - README.md
114
+ - Rakefile
115
+ - bin/console
116
+ - bin/setup
117
+ - config/config.json
118
+ - config/cookbooks.json
119
+ - exe/supermarket_sync
120
+ - lib/supermarket_sync.rb
121
+ - lib/supermarket_sync/cli.rb
122
+ - lib/supermarket_sync/config.rb
123
+ - lib/supermarket_sync/helpers/configuration.rb
124
+ - lib/supermarket_sync/notifier.rb
125
+ - lib/supermarket_sync/sync.rb
126
+ - lib/supermarket_sync/util.rb
127
+ - lib/supermarket_sync/version.rb
128
+ - supermarket_sync.gemspec
129
+ homepage: https://github.com/bdwyertech/supermarket_sync
130
+ licenses:
131
+ - MIT
132
+ metadata:
133
+ allowed_push_host: https://rubygems.org
134
+ post_install_message:
135
+ rdoc_options: []
136
+ require_paths:
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ required_rubygems_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ requirements: []
149
+ rubyforge_project:
150
+ rubygems_version: 2.7.8
151
+ signing_key:
152
+ specification_version: 4
153
+ summary: Utility to synchronize Chef Supermarkets
154
+ test_files: []