gitchefsync 0.6.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/.gitignore +25 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +2 -0
- data/README.md +150 -0
- data/Rakefile +2 -0
- data/bin/gitchefsync +18 -0
- data/build.sh +20 -0
- data/gitchefsync.gemspec +29 -0
- data/lib/gitchefsync/audit.rb +219 -0
- data/lib/gitchefsync/common.rb +178 -0
- data/lib/gitchefsync/config.rb +88 -0
- data/lib/gitchefsync/env_sync.rb +339 -0
- data/lib/gitchefsync/errors.rb +35 -0
- data/lib/gitchefsync/git_util.rb +102 -0
- data/lib/gitchefsync/io_util.rb +83 -0
- data/lib/gitchefsync/knife_util.rb +209 -0
- data/lib/gitchefsync/log.rb +46 -0
- data/lib/gitchefsync/notify.rb +74 -0
- data/lib/gitchefsync/opts.rb +64 -0
- data/lib/gitchefsync/schedule.rb +161 -0
- data/lib/gitchefsync/version.rb +3 -0
- data/lib/gitchefsync.rb +516 -0
- data/spec/README.md +0 -0
- data/spec/config-nogitgroup.json +13 -0
- data/spec/gitchefsync_spec.rb +89 -0
- data/spec/integration.rb +68 -0
- data/spec/sync-config.json +13 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7d42fe53a3355fac66554627bcc30fedc5a28d9a
|
4
|
+
data.tar.gz: 6a277f678f793afdf12308510e8be5466711068a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c6c0d391f1033f08f0e919c38be05033e057b1195d6db064d2033abae366eaf1c1c62156f6a0873f9b8e15509c443c2354e0d39a46dba7e9fcc6969e1b8cd590
|
7
|
+
data.tar.gz: b1f724fa77ee87ab65efa1bd48234fa34bacc96026222e96a25deced9cebee57469ee0207eaa0321295885651bab3018874bb3c35e73f59f0d46750831676486
|
data/.gitignore
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
one.sh
|
24
|
+
.buildpath
|
25
|
+
.project
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
data/README.md
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
# GitChefSync
|
2
|
+
|
3
|
+
This is a Git -> Chef synchronization toolset.
|
4
|
+
|
5
|
+
### Requirements:
|
6
|
+
- Git (1.8.x)
|
7
|
+
- Chef
|
8
|
+
- Berkshelf
|
9
|
+
|
10
|
+
### Gem dependencies:
|
11
|
+
- gitlab + transitive dependencies
|
12
|
+
|
13
|
+
|
14
|
+
To install (from source):
|
15
|
+
- git clone [Mandolin GitChefSync](http://gitlab.rim.net/chef/gitchefsync.git)
|
16
|
+
- cd gitchefsync && rake install
|
17
|
+
|
18
|
+
### Running:
|
19
|
+
|
20
|
+
- Typical "Master" options are:
|
21
|
+
- Cookbook sync: gitchefsync syncCookbooks\[Local\] -c /path_to_config -t xxxyyy
|
22
|
+
- Environment sync: gitchefsync syncEnv -c /path_to_config -t xxxyyy
|
23
|
+
- "Sous" operation:
|
24
|
+
- stage upload: gitchefsync stagedUpload -c /path_to_config -t xxxyyy
|
25
|
+
|
26
|
+
|
27
|
+
### Configurtion
|
28
|
+
- VM configuration of gitchefsync [chefsync](https://gitlab.rim.net/mand-common/chefsync)
|
29
|
+
- sync-config.json is the central config file as is required for all operations
|
30
|
+
- relies on knife and chef in general it's best to create a .chef directory in the working directory and fill it with appropriate values, if not a .chef directory and it's corresponding knife.rb file will be created
|
31
|
+
- The script relies on 3 important pieces: Git, Chef and Berks. Installation options: see chefsync (https://gitlab.rim.net/mand-common/chefsync).
|
32
|
+
- There are 2 modes of operation: pulling from master node in git and pushing those changes into chef server, OR using the working directory (which is a git repository). Running locally can be achieved with the "sync_local flag".
|
33
|
+
- syncCookbooks still however depends on berks upload, which for all intents and purposes requires access to both git and the internet for getting and installing cookbook dependencies.
|
34
|
+
|
35
|
+
### Sample sync-config.json
|
36
|
+
|
37
|
+
{
|
38
|
+
"knife":"/usr/local/bin/knife",
|
39
|
+
"git":"/usr/bin/git",
|
40
|
+
"berks":"/usr/local/bin/berks",
|
41
|
+
"working_directory":"/working_directory",
|
42
|
+
"knife_file":"path_to_knife/knife.rb",
|
43
|
+
"gitlab_group_names": [MAND_IEMS,MAND_BBM],
|
44
|
+
"git_env_repo": ""https://gitlab.rim.net/mand-common/global_chef_env.git",
|
45
|
+
"cookbook_repo_list" : ["https://gitlab.rim.net/mand-ems/iems.git"],
|
46
|
+
"sync_local" : true,
|
47
|
+
"stage_dir": "path_to_stage_",
|
48
|
+
"tmp_dir" : "path_to_store_tmp_data"
|
49
|
+
}
|
50
|
+
|
51
|
+
### Dependencies:
|
52
|
+
- Berks: likely requires you to disable SSL if running through a proxy
|
53
|
+
- Git - goes without saying, need git
|
54
|
+
- Gitlab - https://gitlab.rim.net is hardcoded currently
|
55
|
+
- knife.rb file
|
56
|
+
|
57
|
+
Example of knife.rb :
|
58
|
+
node_name 'admin'
|
59
|
+
client_key '/etc/chef-server/admin.pem'
|
60
|
+
chef_server_url 'https://yourserver'
|
61
|
+
|
62
|
+
### Cookbook synchronization
|
63
|
+
- the current "gitlab_group_names" are the gitlab groups that are the target set of repositories for synchronization
|
64
|
+
- this can work concurrently with "cookbook_repo_list" an explicit set of git urls for those cookbooks
|
65
|
+
- cookbooks are vetted with berks and knife
|
66
|
+
- requires appropriate knife configuration
|
67
|
+
- requires that your gitlab token is set correctly
|
68
|
+
|
69
|
+
### Environment synchronization
|
70
|
+
|
71
|
+
- the "git_env_group" is the repository that houses the standard configuration for environment, data bags and roles
|
72
|
+
- chef-repo
|
73
|
+
- environments/
|
74
|
+
env.json
|
75
|
+
- data_bags/
|
76
|
+
- db_name1/
|
77
|
+
databag.json
|
78
|
+
- db_name2/
|
79
|
+
-roles/
|
80
|
+
|
81
|
+
OR structure:
|
82
|
+
|
83
|
+
- chef-repo
|
84
|
+
- env_identifier1/
|
85
|
+
- environments/
|
86
|
+
env.json
|
87
|
+
- data_bags/
|
88
|
+
- db_name1/
|
89
|
+
databag.json
|
90
|
+
- db_name2/
|
91
|
+
-roles/
|
92
|
+
-env_identifier2/
|
93
|
+
- environments/
|
94
|
+
env.json
|
95
|
+
- data_bags/
|
96
|
+
- db_name1/
|
97
|
+
databag.json
|
98
|
+
- db_name2/
|
99
|
+
-roles/
|
100
|
+
|
101
|
+
See (http://gitlab.rim.net/mand-common/global_chef_env) for example of such
|
102
|
+
|
103
|
+
### Audit
|
104
|
+
- An audit file will be generated for each run of the synchronization, where each cookbook will generate a json structure of which cookbooks were deployed or staged
|
105
|
+
- 2 sets of audit files will be generated, one for the cookbook related synchronization and another for
|
106
|
+
- Audit will be in a parsable json format stored in the staging directory
|
107
|
+
[
|
108
|
+
{
|
109
|
+
"name": "epagent",
|
110
|
+
"ts": 1400702312,
|
111
|
+
"version": "1.0.3"
|
112
|
+
},
|
113
|
+
{
|
114
|
+
"name": "epagent",
|
115
|
+
"ts": 1400702315,
|
116
|
+
"version": "1.0.4"
|
117
|
+
},
|
118
|
+
|
119
|
+
{
|
120
|
+
"name": "bb_jboss-cookbook",
|
121
|
+
"ts": 1400702327,
|
122
|
+
"version": "1.0.0"
|
123
|
+
},
|
124
|
+
{
|
125
|
+
"name": "relay_node-cookbook",
|
126
|
+
"ts": 1400702329,
|
127
|
+
"exception": "Some Exception msg",
|
128
|
+
"version": "1.0.0"
|
129
|
+
},
|
130
|
+
{
|
131
|
+
"name": "bb_jssecert-cookbook",
|
132
|
+
"ts": 1400702333,
|
133
|
+
"version": "1.0.0"
|
134
|
+
},
|
135
|
+
{
|
136
|
+
"name": "nsupdate",
|
137
|
+
"ts": 1400702336,
|
138
|
+
"version": "1.0.0"
|
139
|
+
},
|
140
|
+
{
|
141
|
+
"name": "jdk-certicom",
|
142
|
+
"ts": 1400702339
|
143
|
+
},
|
144
|
+
{
|
145
|
+
"name": "bb_apt-cookbook",
|
146
|
+
"ts": 1400702343,
|
147
|
+
"version": "1.0.0"
|
148
|
+
}
|
149
|
+
|
150
|
+
]
|
data/Rakefile
ADDED
data/bin/gitchefsync
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'gitchefsync'
|
4
|
+
|
5
|
+
if ARGV.length == 0
|
6
|
+
Gitchefsync.help
|
7
|
+
else
|
8
|
+
begin
|
9
|
+
method = Gitchefsync.method( ARGV[0] )
|
10
|
+
#find method opts: stop when encountering arguments --x -y
|
11
|
+
|
12
|
+
options = Gitchefsync.parseAndConfigure( ARGV )
|
13
|
+
method.call
|
14
|
+
rescue Gitchefsync::ConfigError => e
|
15
|
+
puts e.backtrace
|
16
|
+
Gitchefsync.help
|
17
|
+
end
|
18
|
+
end
|
data/build.sh
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#sudo rake install
|
2
|
+
mkdir -p /opt/gitchefsync/working_dir
|
3
|
+
mkdir -p /opt/gitchefsync/staging
|
4
|
+
|
5
|
+
#
|
6
|
+
#export PATH=/opt/chefdk/embedded/bin:~/.chefdk/gem/ruby/2.1.0/bin
|
7
|
+
|
8
|
+
CONF=/opt/gitchefsync/config/sync-config.json
|
9
|
+
rm pkg/*gem
|
10
|
+
rake build
|
11
|
+
wd=`pwd`
|
12
|
+
gem install pkg/*gem
|
13
|
+
cd bin
|
14
|
+
#
|
15
|
+
gitchefsync syncCookbooks --private_token=LQv1BEwgo3Z4RkuKpYyb --config=$CONF
|
16
|
+
#gitchefsync syncCookbooksLocal --private_token=LQv1BEwgo3Z4RkuKpYyb --config=$CONF
|
17
|
+
gitchefsync syncEnv --private_token=LQv1BEwgo3Z4RkuKpYyb --config=$CONF
|
18
|
+
|
19
|
+
gitchefsync stagedUpload --private_token=LQv1BEwgo3Z4RkuKpYyb --config=$CONF
|
20
|
+
cd $wd
|
data/gitchefsync.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'gitchefsync/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "gitchefsync"
|
8
|
+
spec.version = Gitchefsync::VERSION
|
9
|
+
spec.authors = ["Marcus Simonsen"]
|
10
|
+
spec.email = ["msimonsen@blackberry.com"]
|
11
|
+
spec.summary = "Git to Chef sync"
|
12
|
+
spec.description = "Tool(s) to help synchronize Git -> Chef server"
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
##Need to figure out the right dependency versions first, otherwise errors out
|
21
|
+
#spec.add_runtime_dependency 'chef'
|
22
|
+
#spec.add_runtime_dependency 'berkshelf'
|
23
|
+
spec.add_runtime_dependency "gitlab", '~> 3.0.0'
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
25
|
+
spec.add_development_dependency "rake"
|
26
|
+
spec.homepage = "https://gitlab.rim.net/mandolin/gitchefsync/tree/master"
|
27
|
+
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'gitchefsync/config'
|
3
|
+
require 'gitchefsync/io_util'
|
4
|
+
require 'gitchefsync/knife_util'
|
5
|
+
module Gitchefsync
|
6
|
+
|
7
|
+
class Audit
|
8
|
+
|
9
|
+
def initialize (fileLocation, type = 'cb')
|
10
|
+
@fileLocation = fileLocation
|
11
|
+
@ts = Time.now.to_i
|
12
|
+
@list = Array.new
|
13
|
+
@type = type
|
14
|
+
end
|
15
|
+
|
16
|
+
#adds an audit item
|
17
|
+
def add ( item )
|
18
|
+
@list << item.to_hash
|
19
|
+
end
|
20
|
+
|
21
|
+
def addItem(name, version)
|
22
|
+
item = AuditItem.new(name,version)
|
23
|
+
add(item)
|
24
|
+
end
|
25
|
+
|
26
|
+
def addCookbook(cookbook,action="UPDATE",exception=nil)
|
27
|
+
item = AuditItem.new(cookbook.name,cookbook.version,exception,action)
|
28
|
+
item.setCookbook(cookbook)
|
29
|
+
add(item)
|
30
|
+
end
|
31
|
+
|
32
|
+
#This gives enough information
|
33
|
+
def addEnv(name,action='UPDATE',exception=nil)
|
34
|
+
cb = Cookbook.new(name,'','mandolin','mandolin@blackberry.com')
|
35
|
+
addCookbook(cb,action,exception)
|
36
|
+
end
|
37
|
+
|
38
|
+
#writes the audit out
|
39
|
+
#write out a current file audit-type-current.json
|
40
|
+
#and an audit-type-timestamp.json
|
41
|
+
def write
|
42
|
+
begin
|
43
|
+
fileLoc = @fileLocation + "/audit-" +@type+ @ts.to_s + ".json"
|
44
|
+
#fileCurrent = @fileLocation + "/audit-" +@type+ "-current" + ".json"
|
45
|
+
if @list.length > 0
|
46
|
+
Gitchefsync.logger.debug "event_id=write_audit:file_loc=#{fileLoc}"
|
47
|
+
file = File.open(fileLoc, "w")
|
48
|
+
json = JSON.generate(@list)
|
49
|
+
file.write(json)
|
50
|
+
#create sym link to this file
|
51
|
+
latest = @fileLocation+ "/audit_" + @type + "_latest.json"
|
52
|
+
if File.exists? latest
|
53
|
+
File.delete latest
|
54
|
+
end
|
55
|
+
File.symlink(file,latest)
|
56
|
+
else
|
57
|
+
Gitchefsync.logger.debug "event_id=no_write_audit"
|
58
|
+
end
|
59
|
+
rescue IOError => e
|
60
|
+
raise e
|
61
|
+
ensure
|
62
|
+
file.close unless file.nil?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
#returns the latest audit file
|
67
|
+
def latest
|
68
|
+
entries = Dir.glob(@fileLocation + "/audit-#{@type}*")
|
69
|
+
file = nil
|
70
|
+
if entries != nil
|
71
|
+
file = entries.sort[entries.length-1]
|
72
|
+
end
|
73
|
+
file
|
74
|
+
end
|
75
|
+
|
76
|
+
#trims the oldest number of audit files to a max to to_num
|
77
|
+
#Additionally archives the audit file in an audit-archive.tar.gz
|
78
|
+
# in the fileLocation directory
|
79
|
+
#
|
80
|
+
#@param suffix - the audit suffix
|
81
|
+
#@param to_num
|
82
|
+
def trim(to_num)
|
83
|
+
entries = Dir.glob(@fileLocation + "/audit-#{@type}*")
|
84
|
+
Gitchefsync.logger.debug "#{@fileLocation}:#{@type} num audit files: #{entries.length}, keep=#{to_num}"
|
85
|
+
files_trimmed = 0
|
86
|
+
files_to_archive = Array.new
|
87
|
+
if entries != nil
|
88
|
+
#sorted in descending order (timestamp)
|
89
|
+
sorted = entries.sort
|
90
|
+
sl = sorted.length - to_num
|
91
|
+
if sl > 0
|
92
|
+
for i in 0..(sl-1)
|
93
|
+
#File.delete sorted[i]
|
94
|
+
files_to_archive << sorted[i]
|
95
|
+
end
|
96
|
+
files_trimmed = sl
|
97
|
+
end
|
98
|
+
end
|
99
|
+
#Archiving and cleanup
|
100
|
+
begin
|
101
|
+
if files_to_archive.length > 0
|
102
|
+
Gitchefsync.logger.debug "executing: tar uf #{@fileLocation}/audit-archive.tar.gz on #{flatten(files_to_archive)}"
|
103
|
+
FS.cmd("cd #{@fileLocation} && tar uf audit-archive.tar.gz #{flatten(files_to_archive)}")
|
104
|
+
Gitchefsync.logger.info "event_id=audit_archive:files=#{files_to_archive}"
|
105
|
+
#delete them
|
106
|
+
for f in files_to_archive
|
107
|
+
File.delete(f)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
rescue Exception => e
|
111
|
+
Gitchefsync.logger.error "event_id=no_write_audit:msg=#{e.message}:trace=#{e.backtrace}"
|
112
|
+
end
|
113
|
+
Gitchefsync.logger.debug("files trimmed:#{files_trimmed}")
|
114
|
+
files_trimmed
|
115
|
+
end
|
116
|
+
|
117
|
+
def flatten(files_array)
|
118
|
+
str = ""
|
119
|
+
for f in files_array
|
120
|
+
str << File.basename(f) << " "
|
121
|
+
end
|
122
|
+
str
|
123
|
+
end
|
124
|
+
|
125
|
+
#returns json structure of the latest audit
|
126
|
+
def parseLatest
|
127
|
+
file = latest
|
128
|
+
if file != nil
|
129
|
+
json = File.read(file)
|
130
|
+
json = JSON.parse(json)
|
131
|
+
end
|
132
|
+
json
|
133
|
+
end
|
134
|
+
|
135
|
+
def latestAuditItems
|
136
|
+
json = parseLatest
|
137
|
+
if json.nil?
|
138
|
+
raise AuditError, "No json available"
|
139
|
+
end
|
140
|
+
audit_list = Array.new
|
141
|
+
json.each do |audit_item|
|
142
|
+
audit_list << AuditItem.new(nil,nil).from_hash(audit_item)
|
143
|
+
end
|
144
|
+
audit_list
|
145
|
+
end
|
146
|
+
|
147
|
+
#if the json has exceptions
|
148
|
+
def hasError (json_obj)
|
149
|
+
ret = false
|
150
|
+
json_obj.each do |item|
|
151
|
+
if item['exception'] != nil then ret = true end
|
152
|
+
end
|
153
|
+
ret
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
#contains functionality associated to audit information gathering
|
158
|
+
#TODO: just reference Cookbook clas in knife_util
|
159
|
+
class AuditItem
|
160
|
+
|
161
|
+
def initialize(name, version, exception = nil, action = 'UPDATE', ts = Time.now)
|
162
|
+
@name = name
|
163
|
+
@version = version
|
164
|
+
@ts = ts.to_i
|
165
|
+
@exception = exception
|
166
|
+
@action = action
|
167
|
+
end
|
168
|
+
|
169
|
+
def name
|
170
|
+
@name
|
171
|
+
end
|
172
|
+
def ex
|
173
|
+
@exception
|
174
|
+
end
|
175
|
+
|
176
|
+
#types are CB and ENV
|
177
|
+
def setType type
|
178
|
+
@type = type
|
179
|
+
end
|
180
|
+
|
181
|
+
def setCookbook(cb)
|
182
|
+
@cookbook = cb
|
183
|
+
end
|
184
|
+
|
185
|
+
#TODO action should be an enumeration
|
186
|
+
def setAction action
|
187
|
+
@action = action
|
188
|
+
end
|
189
|
+
|
190
|
+
#this method doesn't work when called when exception is created from json (from_hash)
|
191
|
+
def to_hash
|
192
|
+
h = Hash.new
|
193
|
+
h[:name] = @name
|
194
|
+
h[:ts] = @ts
|
195
|
+
if @exception.is_a? Exception
|
196
|
+
h[:exception] = @exception.message unless @exception == nil
|
197
|
+
else
|
198
|
+
h[:exception] = @exception unless @exception == nil
|
199
|
+
end
|
200
|
+
h[:version] = @version unless @version == nil
|
201
|
+
h[:type] = @type unless @type == nil
|
202
|
+
h[:action] = @action unless @action == nil
|
203
|
+
h[:maintainer] = @cookbook.maintainer unless @cookbook == nil
|
204
|
+
h[:maintainer_email] = @cookbook.maintainer_email unless @cookbook == nil
|
205
|
+
h
|
206
|
+
end
|
207
|
+
|
208
|
+
def from_hash(h)
|
209
|
+
@name = h['name']
|
210
|
+
@ts = h['ts']
|
211
|
+
@exception = h['exception']
|
212
|
+
@version = h['version']
|
213
|
+
@type = h['type']
|
214
|
+
@action = h['action']
|
215
|
+
@cookbook = Cookbook.new(@name,@version,h['maintainer'],h['maintainer_email'])
|
216
|
+
return self
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
module Gitchefsync
|
2
|
+
|
3
|
+
extend Gitchefsync::Configuration
|
4
|
+
def self.included base
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
#git installed?
|
9
|
+
def self.checkGit
|
10
|
+
include Git
|
11
|
+
if Git.hasGit == false
|
12
|
+
logger.error "event_id=git_error:msg=Git was not found on the path"
|
13
|
+
raise GitError, "Git was not detected"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.gitDelta(path, remote_ref)
|
18
|
+
include Git
|
19
|
+
env_repo = @config['git_env_repo']
|
20
|
+
|
21
|
+
local = Git.cmd "cd #{path} && #{@git_bin} rev-parse HEAD"
|
22
|
+
#logger.debug "local #{local}: path=#{path}"
|
23
|
+
remote = Git.cmd "cd #{path} && #{@git_bin} ls-remote origin #{remote_ref}"
|
24
|
+
return false if remote.empty?
|
25
|
+
remote = remote.split(/\s/)[0]
|
26
|
+
|
27
|
+
delta = (local.chomp != remote.chomp)
|
28
|
+
logger.debug "event_id=gitDelta:local=#{local.chomp}:remote=#{remote.chomp}:delta=#{delta}"
|
29
|
+
delta
|
30
|
+
end
|
31
|
+
|
32
|
+
# Verify .gitchefsync at HEAD of default_branch
|
33
|
+
def self.checkProjectConfig(cmd, log)
|
34
|
+
begin
|
35
|
+
proc_cmd, cmd_line = cmd
|
36
|
+
return proc_cmd.call(cmd_line)
|
37
|
+
rescue GitError, CmdError
|
38
|
+
proc_log, msg = log
|
39
|
+
proc_log.call(msg)
|
40
|
+
end
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
#@param project - Gitlab project object
|
45
|
+
def self.pullProject(project, verify_yml=false)
|
46
|
+
p_name = project['path_with_namespace'].split('/').join('_')
|
47
|
+
project_path = File.join(@git_local, p_name)
|
48
|
+
|
49
|
+
default_branch = project['default_branch']
|
50
|
+
|
51
|
+
url_type = @config['gitlab_url_type']
|
52
|
+
# "http" is default if @config['gitlab_url_type'] not configured
|
53
|
+
url_type ||= "http"
|
54
|
+
|
55
|
+
if url_type.eql?("http")
|
56
|
+
project_url = project['http_url_to_repo']
|
57
|
+
# Verify .gitchefsync at HEAD of default_branch using `wget -O - url`
|
58
|
+
cmd_line = "wget -qO - #{project['web_url']}/raw/#{default_branch}/.gitchefsync.yml"
|
59
|
+
cmd = [ Proc.new{ |cmd_line| FS.cmd cmd_line }, cmd_line ]
|
60
|
+
|
61
|
+
elsif url_type.eql?("ssh")
|
62
|
+
project_url = project['ssh_url_to_repo']
|
63
|
+
# Verify .gitchefsync at HEAD of default_branch using `git archive` (not currently supported/enabled using https protocol)
|
64
|
+
cmd_line = "git archive --remote=#{project_url} #{default_branch}: .gitchefsync.yml"
|
65
|
+
cmd = [ Proc.new { |cmd_line| Git.cmd("#{cmd_line}") }, cmd_line ]
|
66
|
+
end
|
67
|
+
|
68
|
+
msg = "event_id=project_missing_.gitchefsync.yml:project_url=#{project_url}:default_branch=#{default_branch}"
|
69
|
+
proc_log = Proc.new { |msg| logger.info "#{msg}" }
|
70
|
+
log = [ proc_log, msg ]
|
71
|
+
|
72
|
+
if verify_yml
|
73
|
+
check = checkProjectConfig(cmd, log)
|
74
|
+
if check.nil? || check.empty?
|
75
|
+
proc_log.call(msg)
|
76
|
+
return
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
begin
|
81
|
+
self.updateGit(project_path, project_url)
|
82
|
+
rescue GitError => e
|
83
|
+
logger.error "event_id=git_error:msg=#{e.message}:trace=#{e.backtrace}"
|
84
|
+
logger.error "event_id=remove_project_path: #{project_path}"
|
85
|
+
FS.cmd "rm -rf #{project_path}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
#central place to get cookbooks from server
|
90
|
+
#will be determined once - and will be (eventually) thread safe
|
91
|
+
def self.serverCookbooks
|
92
|
+
if @serverCookbooks.nil?
|
93
|
+
knifeUtil = KnifeUtil.new(@knife, @git_local)
|
94
|
+
@serverCookbooks = knifeUtil.listCookbooks()
|
95
|
+
end
|
96
|
+
@serverCookbooks
|
97
|
+
end
|
98
|
+
|
99
|
+
# Pulls all known projects (determined by configured gitlab-token)
|
100
|
+
def self.pullAllProjects
|
101
|
+
all_projects = []
|
102
|
+
done = false
|
103
|
+
page, per_page = 1, 100
|
104
|
+
while !done do
|
105
|
+
page_opts = { :per_page => per_page, :page => page}
|
106
|
+
repos = Gitlab.projects(page_opts)
|
107
|
+
if (repos.length == 0)
|
108
|
+
done = true
|
109
|
+
else
|
110
|
+
page += 1
|
111
|
+
end
|
112
|
+
repos.each do |project|
|
113
|
+
pullProject(project.to_hash, true)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Get subset of known groups (determined by configured gitlab-token)
|
119
|
+
def self.getAllGroupIDs(group_names=[], group_ids=[])
|
120
|
+
groups = Array.new
|
121
|
+
done = false
|
122
|
+
page, per_page = 1, 100
|
123
|
+
while !done do
|
124
|
+
page_opts = { :per_page => per_page, :page => page}
|
125
|
+
known_groups = Gitlab.groups(page_opts)
|
126
|
+
if (known_groups.length == 0)
|
127
|
+
done = true
|
128
|
+
else
|
129
|
+
page += 1
|
130
|
+
end
|
131
|
+
known_groups.each do |group|
|
132
|
+
name = group.to_hash['name']
|
133
|
+
id = group.to_hash['id']
|
134
|
+
if (group_names.include? name) || (group_ids.include? id)
|
135
|
+
groups << id
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
groups.uniq!
|
140
|
+
groups
|
141
|
+
end
|
142
|
+
|
143
|
+
#TODO: change from git to git with path
|
144
|
+
def self.updateGit (project_path, git_path)
|
145
|
+
include Git
|
146
|
+
begin
|
147
|
+
branch = @rel_branch
|
148
|
+
logger.debug "using release branch: #{branch}"
|
149
|
+
_git = @git_bin
|
150
|
+
if !Git.gitInit(project_path)
|
151
|
+
FS.cmd "mkdir #{project_path}"
|
152
|
+
logger.debug "event_id=git_int:path=#{git_path}:project_path=#{project_path}"
|
153
|
+
Git.cmd "cd #{project_path} && #{_git} init"
|
154
|
+
Git.cmd "cd #{project_path} && #{_git} remote add origin #{git_path}"
|
155
|
+
Git.cmd "cd #{project_path} && #{_git} pull origin #{branch}"
|
156
|
+
else
|
157
|
+
logger.info "event_id=git_repo_exists:project_path=#{project_path}"
|
158
|
+
#wipe tags and re-fetch them
|
159
|
+
tags = Git.cmd "cd #{project_path} && git tag"
|
160
|
+
arr_tags = tags.split(/\n/)
|
161
|
+
arr_tags.each do |tag|
|
162
|
+
Git.cmd "cd #{project_path} && git tag -d #{tag}"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
#get all commits and tags
|
167
|
+
Git.cmd "cd #{project_path} && #{_git} clean -xdf"
|
168
|
+
Git.cmd "cd #{project_path} && #{_git} checkout #{branch}"
|
169
|
+
Git.cmd "cd #{project_path} && #{_git} pull origin #{branch}"
|
170
|
+
|
171
|
+
git_tags = Git.cmd "cd #{project_path} && #{_git} fetch --tags"
|
172
|
+
|
173
|
+
rescue CmdError => e
|
174
|
+
raise GitError, "An error occurred synchronizing cookbooks #{e.message}"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|