clc-promote 0.4.5 → 0.7.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -13,18 +13,22 @@ module Promote
13
13
  @uploader = Uploader.new(@config)
14
14
  @promoter = Promoter.new(@config)
15
15
  yield self if block_given?
16
- define_version_cookbook
17
- define_version_cookbooks
18
- define_version_environment
19
- define_version_environments
16
+ define_constrain_environment
17
+ define_promote_environment
18
+ define_promote_environments
20
19
  define_sync_berksfile
21
20
  define_sync_berksfiles
22
- define_upload_cookbooks
23
21
  define_upload_cookbook
24
- define_upload_environment
22
+ define_upload_cookbooks
25
23
  define_upload_data_bags
26
- define_constrain_environment
27
- define_promote_environment
24
+ define_upload_environment
25
+ define_upload_roles
26
+ define_version_cookbook
27
+ define_version_cookbooks
28
+ define_version_environment
29
+ define_version_environments
30
+ define_version_role
31
+ define_version_roles
28
32
  end
29
33
 
30
34
  private
@@ -95,7 +99,17 @@ module Promote
95
99
  def define_promote_environment
96
100
  namespace "Promote" do
97
101
  desc "Promote one environment from another"
98
- task "promote_environment", :source_environment, :destination_environment do |task, args|
102
+ task "promote_environment", :source_environment, :destination_environments do |task, args|
103
+ puts "Promoting constraints in #{args.source_environment} to #{args.destination_environments}"
104
+ @promoter.monitor_promotion(args.source_environment, args.destination_environments, 60 * 5)
105
+ end
106
+ end
107
+ end
108
+
109
+ def define_promote_environments
110
+ namespace "Promote" do
111
+ desc "Promote a list of environments from another"
112
+ task "promote_environments", :source_environment, :destination_environments do |task, args|
99
113
  puts "Promoting constraints in #{args.source_environment} to #{args.destination_environment}"
100
114
  deps = @promoter.promote_to(args.source_environment, args.destination_environment)
101
115
  end
@@ -114,6 +128,27 @@ module Promote
114
128
  end
115
129
  end
116
130
 
131
+ def define_version_role
132
+ namespace "Promote" do
133
+ desc "Version a role"
134
+ task "version_role", :role do |task, args|
135
+ @versioner.version_role(args.role)
136
+ end
137
+ end
138
+ end
139
+
140
+ def define_version_roles
141
+ namespace "Promote" do
142
+ desc "Version all roles"
143
+ task "version_roles" do
144
+ puts "Version stamping changed roles..."
145
+ @versioner.version_roles.each do |result|
146
+ puts "Versioned role: #{result[:artifact]} v#{result[:version]}"
147
+ end
148
+ end
149
+ end
150
+ end
151
+
117
152
  def define_upload_cookbooks
118
153
  namespace "Promote" do
119
154
  desc "Upload all cookbooks in an environment to the chef server"
@@ -144,15 +179,25 @@ module Promote
144
179
  end
145
180
  end
146
181
 
147
- def define_upload_data_bags
148
- namespace "Promote" do
149
- desc "Upload data_bags to the chef server"
150
- task "upload_data_bags" do
151
- file_uploaded = @uploader.upload_data_bags
152
- puts "Uploading #{file_uploaded} to #{@uploader.config.chef_server_url} as #{@uploader.config.node_name} using #{@uploader.config.client_key}"
153
- end
154
- end
155
- end
182
+ def define_upload_roles
183
+ namespace "Promote" do
184
+ desc "Upload roles to the chef server"
185
+ task "upload_roles" do
186
+ file_uploaded = @uploader.upload_roles
187
+ puts "Uploading #{file_uploaded} to #{@uploader.config.chef_server_url} as #{@uploader.config.node_name} using #{@uploader.config.client_key}"
188
+ end
189
+ end
190
+ end
191
+
192
+ def define_upload_data_bags
193
+ namespace "Promote" do
194
+ desc "Upload data_bags to the chef server"
195
+ task "upload_data_bags" do
196
+ file_uploaded = @uploader.upload_data_bags
197
+ puts "Uploading #{file_uploaded} to #{@uploader.config.chef_server_url} as #{@uploader.config.node_name} using #{@uploader.config.client_key}"
198
+ end
199
+ end
200
+ end
156
201
 
157
202
  end
158
203
  end
@@ -0,0 +1,26 @@
1
+ require 'json'
2
+
3
+ module Promote
4
+ class RoleFile
5
+ attr_accessor :name
6
+
7
+ def initialize(role_name, config)
8
+ @name = role_name
9
+ @config = config
10
+ end
11
+
12
+ def file_path
13
+ File.join(@config.role_directory, "#{name}.json")
14
+ end
15
+
16
+ private
17
+
18
+ def content
19
+ @content ||= get_role_content
20
+ end
21
+
22
+ def get_role_content
23
+ JSON.parse(File.read(file_path))
24
+ end
25
+ end
26
+ end
@@ -3,10 +3,11 @@ require 'chef/chef_fs/parallelizer'
3
3
  require 'chef/chef_fs/config'
4
4
  require 'chef/chef_fs/file_pattern'
5
5
  require 'chef/chef_fs/file_system'
6
+ require 'chef/chef_fs/file_system/chef_repository_file_system_root_dir'
6
7
  require 'chef/config'
7
8
  require 'chef/chef_fs/path_utils'
8
9
  require 'chef/knife'
9
- unless defined? Chef::Knife::CookbookUpload
10
+ unless defined? Chef::Knife::CookbookUpload
10
11
  require 'chef/knife/cookbook_upload'
11
12
  end
12
13
 
@@ -41,14 +42,19 @@ module Promote
41
42
 
42
43
  upload_filtered_cookbooks(dirs, environment)
43
44
  end
44
-
45
- def upload_cookbook_directory(directory)
46
- if !Dir.glob(File.join(config.cookbook_directory, "*")).empty?
45
+
46
+ def upload_cookbook_directory(directory, ui = nil)
47
+ if !Dir.glob(File.join(directory, "*")).empty?
48
+ if ui
49
+ ui.info "Uploading cookbooks from #{directory} to #{Chef::Config[:chef_server_url]}"
50
+ end
47
51
  knife = Chef::Knife::CookbookUpload.new()
48
52
  knife.config[:all] = true
49
- knife.config[:freeze] = true
53
+ knife.config[:freeze] = true
50
54
  knife.config[:cookbook_path] = directory
51
55
  knife.run
56
+ elsif ui
57
+ ui.info "No cookbooks found in #{directory}"
52
58
  end
53
59
  end
54
60
 
@@ -60,8 +66,29 @@ module Promote
60
66
  upload_file("/environments/*.json")
61
67
  end
62
68
 
69
+ def upload_roles
70
+ upload_file("/roles/*.json")
71
+ end
72
+
63
73
  def upload_data_bags
64
- upload_file("/data_bags/**/*.json")
74
+ data_bag_directory = File.join(config.temp_directory, "data_bags")
75
+ chef_fs_directory = Chef::ChefFS::FileSystem::ChefRepositoryFileSystemRootDir.new(
76
+ {
77
+ 'data_bags' => [data_bag_directory]
78
+ }
79
+ )
80
+
81
+ config.reset_temp_dir
82
+
83
+ Dir.glob(File.join(config.data_bag_directory,'**/*.json')).
84
+ reject{ |file| file.end_with?("_keys.json")}.each do |file|
85
+ new_path = file.sub(config.repo_root, config.temp_directory)
86
+ new_dir = File.dirname(new_path)
87
+ FileUtils.mkdir_p(new_dir) unless Dir.exist?(new_dir)
88
+ FileUtils.cp(file, new_path)
89
+ end
90
+
91
+ upload_file("/data_bags/**/*.json", chef_fs_directory)
65
92
  end
66
93
 
67
94
  attr_accessor :config
@@ -70,7 +97,7 @@ module Promote
70
97
 
71
98
  def upload_filtered_cookbooks(dirs, environment = nil)
72
99
  anything_to_upload = false
73
-
100
+
74
101
  server_cookbooks = Utils.chef_server_cookbooks(config)
75
102
  if environment.nil?
76
103
  env_versions = nil
@@ -78,8 +105,7 @@ module Promote
78
105
  env_versions = EnvironmentFile.new(environment, config).cookbook_versions
79
106
  end
80
107
 
81
- FileUtils.rm_rf(config.temp_directory) if Dir.exist?(config.temp_directory)
82
- Dir.mkdir(config.temp_directory)
108
+ config.reset_temp_dir
83
109
 
84
110
  dirs.each do | dir |
85
111
  anything_to_upload = cookbooks_copied?(dir, server_cookbooks, env_versions) || anything_to_upload
@@ -108,10 +134,16 @@ module Promote
108
134
  end
109
135
  end
110
136
 
111
- def upload_file(file_path)
112
- fs_config = Chef::ChefFS::Config.new
137
+ def upload_file(file_path, src = Chef::ChefFS::Config.new.local_fs)
113
138
  pattern = Chef::ChefFS::FilePattern.new(file_path)
114
- Chef::ChefFS::FileSystem.copy_to(pattern, fs_config.local_fs, fs_config.chef_fs, 1, Chef::Config)
139
+
140
+ Chef::ChefFS::FileSystem.copy_to(
141
+ pattern,
142
+ src,
143
+ Chef::ChefFS::Config.new.chef_fs,
144
+ nil,
145
+ Chef::Config
146
+ )
115
147
  file_path
116
148
  end
117
149
 
@@ -127,4 +159,4 @@ module Promote
127
159
  end
128
160
  end
129
161
  end
130
- end
162
+ end
@@ -1,3 +1,3 @@
1
1
  module Promote
2
- VERSION = '0.4.5'
3
- end
2
+ VERSION = '0.7.8'
3
+ end
@@ -13,9 +13,9 @@ module Promote
13
13
  if cookbook.version.to_s != version
14
14
  cookbook.version = Semverse::Version.new(version)
15
15
  cookbook.stamp_commit(repo.sha1)
16
- return {
17
- :cookbook => cookbook_name,
18
- :version => version,
16
+ return {
17
+ :cookbook => cookbook_name,
18
+ :version => version,
19
19
  :sha1 => repo.sha1
20
20
  }
21
21
  end
@@ -49,6 +49,34 @@ module Promote
49
49
  results
50
50
  end
51
51
 
52
+ def version_role(role_name)
53
+ # if role doesn't include an override_attributes key, create it
54
+ file = File.join(config.role_directory, "#{role_name}.json")
55
+ role = JSON.parse(File.read(file))
56
+ if !role.has_key?("override_attributes")
57
+ overrides_hash = { "override_attributes" => {} }
58
+ role.merge!(overrides_hash)
59
+ File.open(file, 'w') do |out|
60
+ out << JSON.pretty_generate(role)
61
+ end
62
+ end
63
+
64
+ version_json config.role_directory, role_name do | content |
65
+ content['override_attributes']
66
+ end
67
+ end
68
+
69
+ def version_roles
70
+ results = []
71
+ Dir.glob(File.join(config.role_directory, "*.json")).each do |file|
72
+ result = version_role(File.basename(file ,File.extname(file)))
73
+ if !result.nil?
74
+ results << result
75
+ end
76
+ end
77
+ results
78
+ end
79
+
52
80
  def constrain_environment(environment_name, cookbook_name)
53
81
  dependencies = Cookbook.new(cookbook_name, config).dependencies
54
82
  env_file = EnvironmentFile.new(environment_name, config)
@@ -56,12 +84,18 @@ module Promote
56
84
  dependencies
57
85
  end
58
86
 
59
- def is_dirty(source_environment, target_environment, cookbook_name)
87
+ def is_dirty(source_environment, target_environment, cookbook_name, dependency_hash = nil)
60
88
  target_env_file = EnvironmentFile.new(target_environment, config)
61
89
  return true unless target_env_file.cookbook_versions.has_key?(cookbook_name)
62
-
90
+
63
91
  source_env_file = EnvironmentFile.new(source_environment, config)
64
- return source_env_file.cookbook_versions[cookbook_name] != target_env_file.cookbook_versions[cookbook_name]
92
+ target_version_parts = target_env_file.cookbook_versions[cookbook_name].split('.')
93
+ target_version = target_version_parts[0..2].join('.')
94
+ target_hash = nil
95
+ if target_version_parts.count > 3
96
+ target_hash = target_version_parts[3]
97
+ end
98
+ return source_env_file.cookbook_versions[cookbook_name] != target_version || dependency_hash != target_hash
65
99
  end
66
100
 
67
101
  attr_accessor :config
@@ -92,12 +126,13 @@ module Promote
92
126
  File.open(file, 'w') do |out|
93
127
  out << JSON.pretty_generate(content)
94
128
  end
95
- {
96
- :artifact => file_name,
97
- :version => version,
129
+ {
130
+ :artifact => file_name,
131
+ :version => version,
98
132
  :sha1 => repo.sha1
99
133
  }
100
134
  end
135
+
101
136
  end
102
137
  end
103
138
  end
@@ -1,15 +1,17 @@
1
1
  require 'promote'
2
2
 
3
3
  describe Promote::Config do
4
- let(:opts) {{
5
- :repo_root => "root",
6
- :cookbook_directory => "cookbooks",
7
- :environment_directory => "environments",
8
- :data_bag_directory => "data_bags",
9
- :temp_directory => "temp",
10
- :node_name => "user",
11
- :client_key => "key",
12
- :chef_server_url => "url"}}
4
+ let(:opts) {{
5
+ :repo_root => "root",
6
+ :cookbook_directory => "cookbooks",
7
+ :environment_directory => "environments",
8
+ :data_bag_directory => "data_bags",
9
+ :role_directory => "roles",
10
+ :temp_directory => "temp",
11
+ :node_name => "user",
12
+ :client_key => "key",
13
+ :chef_server_url => "url",
14
+ :bags => ['foo']}}
13
15
  subject { Promote::Config.new(opts) }
14
16
 
15
17
  it "assigns options to node_name attribute" do
@@ -33,20 +35,25 @@ describe Promote::Config do
33
35
  it "assigns options to environment_directory attribute" do
34
36
  expect(subject.environment_directory).to eq(opts[:environment_directory])
35
37
  end
38
+ it "assigns options to role_directory attribute" do
39
+ expect(subject.role_directory).to eq(opts[:role_directory])
40
+ end
36
41
  it "assigns options to temp_directory attribute" do
37
42
  expect(subject.temp_directory).to eq(opts[:temp_directory])
38
43
  end
44
+ it "assigns options to bags attribute" do
45
+ expect(subject.bags).to eq(opts[:bags])
46
+ end
39
47
  it "can correctly convert to a hash" do
40
48
  hash = subject.to_hash
41
49
  expect(hash[:repo_root]).to eq(opts[:repo_root])
42
50
  end
43
51
 
44
52
  context "directories are not in options" do
45
- let(:opts) {{
46
- :node_name => "user",
47
- :client_key => "key",
53
+ let(:opts) {{
54
+ :node_name => "user",
55
+ :client_key => "key",
48
56
  :chef_server_url => "url"}}
49
- subject { Promote::Config.new(opts) }
50
57
 
51
58
  it "assigns repo_root to pwd" do
52
59
  expect(subject.repo_root).to eq(Dir.pwd)
@@ -60,18 +67,31 @@ describe Promote::Config do
60
67
  it "assigns environment_directory to environments off root" do
61
68
  expect(subject.environment_directory).to eq(File.join(subject.repo_root, "environments"))
62
69
  end
70
+ it "assigns role_directory to roles off root" do
71
+ expect(subject.role_directory).to eq(File.join(subject.repo_root, "roles"))
72
+ end
63
73
  it "assigns temp_directory to tmp" do
64
74
  expect(subject.temp_directory).to eq("/tmp/promote")
65
75
  end
66
76
  end
67
77
 
78
+ context "no --data-bag option is specified" do
79
+ let(:opts) {{
80
+ :node_name => "user",
81
+ :client_key => "key",
82
+ :chef_server_url => "url"}}
83
+
84
+ it "defaults to secrets_*" do
85
+ expect(subject.bags).to eq(['secrets_*'])
86
+ end
87
+ end
88
+
68
89
  context "directories are not in options but repo root is" do
69
- let(:opts) {{
90
+ let(:opts) {{
70
91
  :repo_root => "root",
71
- :node_name => "user",
72
- :client_key => "key",
92
+ :node_name => "user",
93
+ :client_key => "key",
73
94
  :chef_server_url => "url"}}
74
- subject { Promote::Config.new(opts) }
75
95
 
76
96
  it "assigns repo_root to pwd" do
77
97
  expect(subject.repo_root).to eq(opts[:repo_root])
@@ -85,8 +105,40 @@ describe Promote::Config do
85
105
  it "assigns environment_directory to environments off root" do
86
106
  expect(subject.environment_directory).to eq(File.join(subject.repo_root, "environments"))
87
107
  end
108
+ it "assigns role_directory to roles off root" do
109
+ expect(subject.role_directory).to eq(File.join(subject.repo_root, "roles"))
110
+ end
88
111
  it "assigns temp_directory to tmp" do
89
112
  expect(subject.temp_directory).to eq("/tmp/promote")
90
113
  end
91
114
  end
92
- end
115
+
116
+ context "reset_temp_dir" do
117
+ after {
118
+ FileUtils.rm_rf(opts[:temp_directory])
119
+ }
120
+
121
+ context "temp dir is populated" do
122
+ before {
123
+ opts[:temp_directory] = "/tmp/promote_tests"
124
+ Dir.mkdir(opts[:temp_directory])
125
+ FileUtils.touch(File.join(opts[:temp_directory], 'file.txt'))
126
+ }
127
+
128
+ it "empties the temp directory" do
129
+ subject.reset_temp_dir
130
+
131
+ expect(Dir[File.join(subject.temp_directory, '*')]).to be_empty
132
+ end
133
+ end
134
+
135
+ context "temp dir is populated" do
136
+
137
+ it "creates the temp directory" do
138
+ subject.reset_temp_dir
139
+
140
+ expect(Dir).to exist(subject.temp_directory)
141
+ end
142
+ end
143
+ end
144
+ end