pero 0.1.0

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: 41f1941626b9f892ebe021edc387601c571976a69ea65c936b7464943fe7024e
4
+ data.tar.gz: ec1bbb64db216abda864d2d72d1654efb36ab9f8d19f06b155104d4f50d99937
5
+ SHA512:
6
+ metadata.gz: '002196fd8e20ceb0ca7d1665e4155dd163c7bd63a7c093fef166d6eda6233e9126c9e33bff1310b80674a109f1478477a2a62d9ac819f64f2bc89c2d3e085026'
7
+ data.tar.gz: 8c49fc394ee3419d2b233f0776e90c1fe8cb75a15283628650aeb7a60e81a72312964d2b591d4d2191803b020cd2767023ef2a6041a39903ee0d23040af7c66f
@@ -0,0 +1,7 @@
1
+ .vagrant
2
+ ssl
3
+ nodes/
4
+ reports
5
+ run
6
+ yaml
7
+ log
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in pero.gemspec
6
+ gemspec
@@ -0,0 +1,61 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ pero (0.1.0)
5
+ docker-api
6
+ logger
7
+ net-ssh
8
+ parallel
9
+ retryable
10
+ specinfra
11
+ thor
12
+
13
+ GEM
14
+ remote: https://rubygems.org/
15
+ specs:
16
+ diff-lcs (1.4.4)
17
+ docker-api (1.34.2)
18
+ excon (>= 0.47.0)
19
+ multi_json
20
+ excon (0.76.0)
21
+ logger (1.4.2)
22
+ multi_json (1.15.0)
23
+ net-scp (3.0.0)
24
+ net-ssh (>= 2.6.5, < 7.0.0)
25
+ net-ssh (6.1.0)
26
+ net-telnet (0.1.1)
27
+ parallel (1.19.2)
28
+ rake (10.5.0)
29
+ retryable (3.0.5)
30
+ rspec (3.9.0)
31
+ rspec-core (~> 3.9.0)
32
+ rspec-expectations (~> 3.9.0)
33
+ rspec-mocks (~> 3.9.0)
34
+ rspec-core (3.9.2)
35
+ rspec-support (~> 3.9.3)
36
+ rspec-expectations (3.9.2)
37
+ diff-lcs (>= 1.2.0, < 2.0)
38
+ rspec-support (~> 3.9.0)
39
+ rspec-mocks (3.9.1)
40
+ diff-lcs (>= 1.2.0, < 2.0)
41
+ rspec-support (~> 3.9.0)
42
+ rspec-support (3.9.3)
43
+ sfl (2.3)
44
+ specinfra (2.82.18)
45
+ net-scp
46
+ net-ssh (>= 2.7)
47
+ net-telnet (= 0.1.1)
48
+ sfl
49
+ thor (1.0.1)
50
+
51
+ PLATFORMS
52
+ ruby
53
+
54
+ DEPENDENCIES
55
+ bundler (~> 1.17)
56
+ pero!
57
+ rake (~> 10.0)
58
+ rspec (~> 3.0)
59
+
60
+ BUNDLED WITH
61
+ 1.17.3
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 pyama86
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.
@@ -0,0 +1,56 @@
1
+ # Pero
2
+
3
+ It is puppet run tool on local OS.
4
+ ## Installation
5
+
6
+ Add this line to your application's Gemfile:
7
+
8
+ ```ruby
9
+ gem 'pero'
10
+ ```
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install pero
19
+
20
+ ## Depends
21
+
22
+ - Docker
23
+
24
+ ## Usage
25
+
26
+ ### Install puppet
27
+
28
+ ```
29
+ $ pero install --agent-version 3.3.1 10.0.0.1 # hostname is example.com
30
+ ```
31
+
32
+ ### Apply puppet
33
+
34
+ ```
35
+ $ pero apply --server -version 3.3.1 example.com
36
+ ```
37
+
38
+ ### Show Support version
39
+
40
+ ```
41
+ $ pero versions
42
+ ```
43
+
44
+ ## Development
45
+
46
+ 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.
47
+
48
+ 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).
49
+
50
+ ## Contributing
51
+
52
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/pero.
53
+
54
+ ## License
55
+
56
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -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
@@ -0,0 +1,50 @@
1
+ # -*- mode: ruby -*-
2
+ # vi: set ft=ruby :
3
+
4
+ # All Vagrant configuration is done below. The "2" in Vagrant.configure
5
+ # configures the configuration version (we support older styles for
6
+ # backwards compatibility). Please don't change it unless you know what
7
+ # you're doing.
8
+ Vagrant.require_version '>= 1.9.0'
9
+ plugins = [
10
+ {
11
+ plugin: 'vagrant-properties',
12
+ version: '~> 0.9'
13
+ },
14
+ {
15
+ plugin: 'vagrant-itamae',
16
+ version: '~> 0.2'
17
+ }
18
+ ]
19
+
20
+ # 必須プラグインのチェック
21
+ plugins.each do |p|
22
+ unless Vagrant.has_plugin?(p[:plugin], p[:version])
23
+ action = Vagrant.has_plugin?(p[:plugin]) ? 'update' : 'install'
24
+ Dir.chdir(Dir.home) { system "vagrant plugin #{action} #{p[:plugin]}" }
25
+ end
26
+ end
27
+
28
+ Vagrant.configure("2") do |config|
29
+ config.vm.box = "centos6"
30
+
31
+ def define_machine_spec(config, memory=512, cpus=2)
32
+ config.vm.provider :virtualbox do |vbox|
33
+ vbox.customize ["modifyvm", :id, "--memory", memory.to_i]
34
+ vbox.customize ["modifyvm", :id, "--cpus", cpus.to_i]
35
+ end
36
+ end
37
+
38
+ config.vm.define 'client' do |c|
39
+ c.vm.network :private_network, ip: "192.168.100.11"
40
+ c.vm.hostname = "client.dev"
41
+ define_machine_spec(c)
42
+ end
43
+
44
+ config.vm.define 'client7' do |c|
45
+ config.vm.box = "centos/7"
46
+ c.vm.network :private_network, ip: "192.168.100.12"
47
+ c.vm.hostname = "client7.dev"
48
+ define_machine_spec(c)
49
+ end
50
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "pero"
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,8 @@
1
+ module RecipeHelper
2
+ def include_cookbook(name)
3
+ include_recipe File.join(__dir__, 'cookbooks', name, 'default.rb')
4
+ end
5
+ end
6
+
7
+ Itamae::Recipe::EvalContext.include(RecipeHelper)
8
+ include_recipe File.join('roles', node[:role], 'default.rb')
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "pero"
4
+ Pero::CLI.start
5
+
@@ -0,0 +1,19 @@
1
+ require "logger"
2
+ require "specinfra"
3
+ require "pero/version"
4
+ require "pero/cli"
5
+ require "pero/history"
6
+ require "pero/ssh_executable"
7
+ require "pero/docker"
8
+ require "pero/puppet"
9
+ require "pero/puppet/base"
10
+ require "pero/puppet/redhat"
11
+
12
+
13
+ module Pero
14
+ def self.log
15
+ @log ||= Logger.new(STDOUT)
16
+ end
17
+ class Error < StandardError; end
18
+ # Your code goes here...
19
+ end
@@ -0,0 +1,65 @@
1
+ require "pero"
2
+ require "thor"
3
+ require "parallel"
4
+
5
+ module Pero
6
+ class CLI < Thor
7
+ class << self
8
+ def exit_on_failure?
9
+ true
10
+ end
11
+ end
12
+
13
+ def initialize(*)
14
+ super
15
+ Pero.log.level = ::Logger.const_get(options[:log_level].upcase) if options[:log_level]
16
+ end
17
+
18
+ def self.shared_options
19
+ option :log_level, type: :string, aliases: ['-l'], default: 'info'
20
+ option :user, type: :string, aliases: ['-x'], desc: "ssh user"
21
+ option :key, type: :string, aliases: ['-i'], desc: "ssh private key"
22
+ option :port, type: :numeric, aliases: ['-p'], desc: "ssh port"
23
+ option :ssh_config, type: :string, desc: "ssh config path"
24
+ option :environment, type: :string, desc: "puppet environment"
25
+ option :ask_password, type: :boolean, default: false, desc: "ask ssh or sudo password"
26
+ option :vagrant, type: :boolean, default: false, desc: "use vagrarant"
27
+ option :sudo, type: :boolean, default: true, desc: "use sudo"
28
+ option "concurrent", aliases: '-N',default: 3, type: :numeric, desc: "running concurrent"
29
+ end
30
+
31
+ desc "versions", "show support version"
32
+ def versions
33
+ Pero::Puppet::Redhat.show_versions
34
+ end
35
+
36
+ desc "apply", "puppet apply"
37
+ shared_options
38
+ option "server-version", type: :string, default: "6.12.0"
39
+ option :noop, aliases: '-n', default: false, type: :boolean
40
+ option :verbose, aliases: '-v', default: true, type: :boolean
41
+ option :tags, default: nil, type: :array
42
+ option "one-shot", default: false, type: :boolean, desc: "stop puppet server after run"
43
+ def apply(name_regexp)
44
+ nodes = Pero::History.search(name_regexp)
45
+ return unless nodes
46
+ Parallel.each(nodes, in_process: options["concurrent"]) do |n|
47
+ opt = n["last_options"].merge(options)
48
+ puppet = Pero::Puppet.new(opt["host"], opt)
49
+ puppet.apply
50
+ end
51
+ end
52
+
53
+ desc "install", "install puppet"
54
+ shared_options
55
+ option "agent-version", default: "6.17.0", type: :string
56
+ option "node-name", aliases: '-N', default: "", type: :string, desc: "json node name(default hostname)"
57
+ def install(*hosts)
58
+ Parallel.each(hosts, in_process: options["concurrent"]) do |host|
59
+ next if host =~ /^-/
60
+ puppet = Pero::Puppet.new(host, options)
61
+ puppet.install
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,159 @@
1
+ require 'docker'
2
+ require 'digest/md5'
3
+ require "retryable"
4
+ require 'net/https'
5
+ module Pero
6
+ class Docker
7
+ attr_reader :server_version
8
+ def initialize(version, environment)
9
+ @server_version = version
10
+ @environment = environment
11
+ end
12
+
13
+ def build
14
+ Pero.log.info "start build container"
15
+ begin
16
+ image = ::Docker::Image.build(docker_file)
17
+ rescue => e
18
+ Pero.log.debug docker_file
19
+ Pero.log.error "failed build container #{e.inspect}"
20
+ raise e
21
+ end
22
+ Pero.log.info "success build container"
23
+ image
24
+ end
25
+
26
+ def container_name
27
+ "pero-#{server_version}-#{Digest::MD5.hexdigest(Dir.pwd)[0..5]}-#{@environment}"
28
+ end
29
+
30
+ def alerady_run?
31
+ ::Docker::Container.all(:all => true).find do |c|
32
+ c.info["Names"].first == "/#{container_name}" && c.info["State"] != "exited"
33
+ end
34
+ end
35
+
36
+ def run
37
+ ::Docker::Container.all(:all => true).each do |c|
38
+ c.delete(:force => true) if c.info["Names"].first == "/#{container_name}"
39
+ end
40
+
41
+ container = ::Docker::Container.create({
42
+ 'name' => container_name,
43
+ 'Hostname' => 'puppet',
44
+ 'Image' => build.id,
45
+ 'ExposedPorts' => { '8140/tcp' => {} },
46
+ })
47
+
48
+ container.start(
49
+ 'Binds' => [
50
+ "#{Dir.pwd}:/etc/puppetlabs/code/environments/#{@environment}",
51
+ "#{Dir.pwd}/keys:/etc/puppetlabs/puppet/eyaml/",
52
+ ],
53
+ 'PortBindings' => {
54
+ '8140/tcp' => [{ 'HostPort' => "0" }],
55
+ },
56
+ "AutoRemove" => true,
57
+ )
58
+
59
+ container = ::Docker::Container.all(:all => true).find do |c|
60
+ c.info["Names"].first == "/#{container_name}"
61
+ end
62
+
63
+ raise "can't start container" unless container
64
+
65
+ begin
66
+ Retryable.retryable(tries: 20, sleep: 5) do
67
+ https = Net::HTTP.new('localhost', container.info["Ports"].first["PublicPort"])
68
+ https.use_ssl = true
69
+ https.verify_mode = OpenSSL::SSL::VERIFY_NONE
70
+ Pero.log.debug "start server health check"
71
+ https.start {
72
+ response = https.get('/')
73
+ Pero.log.debug "puppet http response #{response}"
74
+ }
75
+ rescue => e
76
+ Pero.log.debug e.inspect
77
+ raise e
78
+ end
79
+ rescue
80
+ container.kill
81
+ raise "can't start puppet server"
82
+ end
83
+
84
+ container
85
+ end
86
+
87
+ def puppet_config
88
+ <<-EOS
89
+ [master]
90
+ vardir = /var/puppet
91
+ certname = puppet
92
+ dns_alt_names = puppet,localhost
93
+ autosign = true
94
+ environment_timeout = unlimited
95
+ codedir = /etc/puppetlabs/code
96
+
97
+ [main]
98
+ server = puppet
99
+ #{@environment && @environment != "" ? "environment = #{@environment}" : nil}
100
+ EOS
101
+
102
+
103
+ end
104
+ def docker_file
105
+ release_package,package_name, conf_dir = if Gem::Version.new("4.0.0") > Gem::Version.new(server_version)
106
+ ["puppetlabs-release-el-#{el}.noarch.rpm", "puppet-server", "/etc/puppet"]
107
+ elsif Gem::Version.new("5.0.0") > Gem::Version.new(server_version) && Gem::Version.new("4.0.0") <= Gem::Version.new(server_version)
108
+ ["puppetlabs-release-pc1-el-#{el}.noarch.rpm", "puppetserver", "/etc/puppetlabs/puppet/"]
109
+ elsif Gem::Version.new("6.0.0") > Gem::Version.new(server_version)&& Gem::Version.new("5.0.0") <= Gem::Version.new(server_version)
110
+ ["puppet5-release-el-#{el}.noarch.rpm", "puppetserver", "/etc/puppetlabs/puppet/"]
111
+ else
112
+ ["puppet6-release-el-#{el}.noarch.rpm", "puppetserver", "/etc/puppetlabs/puppet/"]
113
+ end
114
+
115
+ <<-EOS
116
+ FROM #{from_image}
117
+ RUN curl -L -k -O https://yum.puppetlabs.com/#{release_package} && \
118
+ rpm -ivh #{release_package}
119
+ RUN yum install -y #{package_name}-#{server_version}
120
+ ENV PATH $PATH:/opt/puppetlabs/bin
121
+ RUN echo -e "#{puppet_config.split(/\n/).join("\\n")}" > #{conf_dir}/puppet.conf
122
+ CMD bash -c "rm -rf #{conf_dir}/ssl/* && #{create_ca} && #{run_cmd}"
123
+ EOS
124
+ end
125
+
126
+ def create_ca
127
+ release_package,package_name, conf_dir = if Gem::Version.new("5.0.0") > Gem::Version.new(server_version)
128
+ #'(puppet cert generate `hostname` --dns_alt_names localhost,127.0.0.1 || puppet cert --allow-dns-alt-names sign `hostname`)'
129
+ 'puppet cert generate `hostname` --dns_alt_names localhost,127.0.0.1'
130
+ elsif Gem::Version.new("6.0.0") > Gem::Version.new(server_version)
131
+ 'puppet cert generate `hostname` --dns_alt_names localhost,127.0.0.1'
132
+ else
133
+ 'puppetserver ca setup --ca-name `hostname` --subject-alt-names DNS:localhost'
134
+ end
135
+ end
136
+
137
+ def run_cmd
138
+ release_package,package_name, conf_dir = if Gem::Version.new("5.0.0") > Gem::Version.new(server_version)
139
+ 'puppet master --no-daemonize --verbose'
140
+ elsif Gem::Version.new("6.0.0") > Gem::Version.new(server_version)
141
+ 'puppetserver foreground'
142
+ else
143
+ 'puppetserver foreground'
144
+ end
145
+ end
146
+
147
+ def el
148
+ if Gem::Version.new("3.5.1") > Gem::Version.new(server_version)
149
+ 6
150
+ else
151
+ 7
152
+ end
153
+ end
154
+
155
+ def from_image
156
+ "centos:#{el}"
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,42 @@
1
+ require 'json'
2
+ require "fileutils"
3
+ module Pero
4
+ class History
5
+ def self.search(query, dir="nodes")
6
+ ret = []
7
+ Dir.foreach(dir) do |f|
8
+ next if %w(. ..).include? f
9
+ next unless f =~ /json$/
10
+ File.open(File.join(dir, f)) do |j|
11
+ h = JSON.load(j)
12
+ ret << h if h["name"] =~ /#{query}/
13
+ end
14
+ end
15
+ ret
16
+ end
17
+ end
18
+ end
19
+
20
+ module Pero
21
+ class History
22
+ class Attribute
23
+ def initialize(specinfra, options)
24
+ name = if options["node-name"].empty?
25
+ specinfra.run_command("hostname").stdout.chomp
26
+ else
27
+ options["node-name"]
28
+ end
29
+ options.delete("noop")
30
+ @h = {
31
+ name: name,
32
+ last_options: options
33
+ }
34
+ end
35
+
36
+ def save(dir="nodes")
37
+ FileUtils.mkdir_p(dir)
38
+ File.write("#{File.join(dir, @h[:name])}.json", @h.to_json)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,6 @@
1
+ [main]
2
+ vardir = /var/puppet
3
+ ssldir = $vardir/ssl
4
+ manifestdir = $vardir/manifests
5
+ templatedir = $vardir/templates
6
+ modulepath = $vardir/modules:$vardir/roles:$vardir/vendor/modules
@@ -0,0 +1,169 @@
1
+ require 'net/ssh'
2
+
3
+ Specinfra::Configuration.error_on_missing_backend_type = false
4
+ Specinfra.configuration.backend = :ssh
5
+ module Specinfra
6
+ module Configuration
7
+ def self.sudo_password
8
+ return ENV['SUDO_PASSWORD'] if ENV['SUDO_PASSWORD']
9
+ return @sudo_password if defined?(@sudo_password)
10
+
11
+ # TODO: Fix this dirty hack
12
+ return nil unless caller.any? {|call| call.include?('channel_data') }
13
+
14
+ print "sudo password: "
15
+ @sudo_password = STDIN.noecho(&:gets).strip
16
+ print "\n"
17
+ @sudo_password
18
+ end
19
+ end
20
+ end
21
+
22
+ module Pero
23
+ class Puppet
24
+ extend Pero::SshExecutable
25
+ attr_reader :specinfra
26
+ def initialize(host, options)
27
+ @options = options.dup
28
+
29
+ @options[:host] = host
30
+ so = ssh_options
31
+ @specinfra = Specinfra::Backend::Ssh.new(
32
+ request_pty: true,
33
+ host: so[:host_name],
34
+ ssh_options: so,
35
+ disable_sudo: false,
36
+ )
37
+ end
38
+
39
+ # refs: github.com/itamae-kitchen/itamae
40
+ def ssh_options
41
+ opts = {}
42
+ opts[:host_name] = @options[:host]
43
+
44
+ # from ssh-config
45
+ ssh_config_files = @options["ssh_config"] ? [@options["ssh_config"]] : Net::SSH::Config.default_files
46
+ opts.merge!(Net::SSH::Config.for(@options["host"], ssh_config_files))
47
+ opts[:user] = @options["user"] || opts[:user] || Etc.getlogin
48
+ opts[:password] = @options["password"] if @options["password"]
49
+ opts[:keys] = [@options["key"]] if @options["key"]
50
+ opts[:port] = @options["port"] if @options["port"]
51
+
52
+ if @options["vagrant"]
53
+ config = Tempfile.new('', Dir.tmpdir)
54
+ hostname = opts[:host_name] || 'default'
55
+ vagrant_cmd = "vagrant ssh-config #{hostname} > #{config.path}"
56
+ if defined?(Bundler)
57
+ Bundler.with_clean_env do
58
+ `#{vagrant_cmd}`
59
+ end
60
+ else
61
+ `#{vagrant_cmd}`
62
+ end
63
+ opts.merge!(Net::SSH::Config.for(hostname, [config.path]))
64
+ end
65
+
66
+ if @options["ask_password"]
67
+ print "password: "
68
+ password = STDIN.noecho(&:gets).strip
69
+ print "\n"
70
+ opts.merge!(password: password)
71
+ end
72
+ opts
73
+ end
74
+
75
+ def install
76
+ Pero.log.info "bootstrap puppet"
77
+ osi = specinfra.os_info
78
+ os = case osi[:family]
79
+ when "redhat"
80
+ Redhat.new(specinfra, osi)
81
+ else
82
+ raise "sorry unsupport os, please pull request!!!"
83
+ end
84
+ os.install(@options["agent-version"])
85
+ Pero::History::Attribute.new(specinfra, @options).save
86
+ end
87
+
88
+ def serve_master
89
+ Pero.log.info "start puppet master container"
90
+ container = run_container
91
+ begin
92
+ yield container
93
+ rescue => e
94
+ Pero.log.error e.inspect
95
+ raise e
96
+ ensure
97
+ Pero.log.info "stop puppet master container"
98
+ container.kill if !@options["one-shot"]
99
+ end
100
+ end
101
+
102
+ def run_container
103
+ docker = Pero::Docker.new(@options["server-version"], @options["environment"])
104
+ docker.alerady_run? || docker.run
105
+ end
106
+
107
+ def apply
108
+ serve_master do |container|
109
+ port = container.info["Ports"].first["PublicPort"]
110
+ begin
111
+ tmpdir=(0...8).map{ (65 + rand(26)).chr }.join
112
+ Pero.log.info "start forwarding port:#{port}"
113
+
114
+ in_ssh_forwarding(port) do |host, ssh|
115
+ Pero.log.info "#{host}:puppet cmd[#{puppet_cmd}]"
116
+ cmd = "unshare -m -- /bin/bash -c 'export PATH=$PATH:/opt/puppetlabs/bin/ && mkdir -p /tmp/puppet/#{tmpdir} && \
117
+ mkdir -p `puppet config print ssldir` && mount --bind /tmp/puppet/#{tmpdir} `puppet config print ssldir` && \
118
+ #{puppet_cmd}'"
119
+ Pero.log.debug "run cmd:#{cmd}"
120
+ ssh.exec!(specinfra.build_command(cmd)) do |channel, stream, data|
121
+ Pero.log.info "#{host}:#{data.chomp}" if stream == :stdout && data.chomp != ""
122
+ Pero.log.warn "#{host}:#{data.chomp}" if stream == :stderr && data.chomp != ""
123
+ end
124
+ ssh.exec!(specinfra.build_command("rm -rf /tmp/puppet/#{tmpdir}"))
125
+ ssh.loop {true} if ENV['PERO_DEBUG']
126
+ end
127
+ rescue => e
128
+ Pero.log.error "puppet apply error:#{e.inspect}"
129
+ end
130
+ end
131
+
132
+ Pero::History::Attribute.new(specinfra, @options).save
133
+ end
134
+
135
+ def puppet_cmd
136
+ if Gem::Version.new("5.0.0") > Gem::Version.new(@options["agent-version"])
137
+ "puppet agent --no-daemonize --onetime #{parse_puppet_option(@options)} --server localhost"
138
+ else
139
+ "/opt/puppetlabs/bin/puppet agent --no-daemonize --onetime #{parse_puppet_option(@options)} --server localhost"
140
+ end
141
+ end
142
+
143
+ def parse_puppet_option(options)
144
+ ret = ""
145
+ %w(noop verbose).each do |n|
146
+ ret << " --#{n}" if options[n]
147
+ end
148
+ ret << " --tags #{options["tags"].join(",")}" if options["tags"]
149
+ ret
150
+ end
151
+
152
+ def in_ssh_forwarding(port)
153
+ options = specinfra.get_config(:ssh_options)
154
+
155
+ if !Net::SSH::VALID_OPTIONS.include?(:strict_host_key_checking)
156
+ options.delete(:strict_host_key_checking)
157
+ end
158
+
159
+ Net::SSH.start(
160
+ specinfra.get_config(:host),
161
+ options[:user],
162
+ options
163
+ ) do |ssh|
164
+ ssh.forward.remote(port, 'localhost', 8140)
165
+ yield specinfra.get_config(:host), ssh
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,28 @@
1
+ module Pero
2
+ class Puppet
3
+ class Base
4
+ attr_reader :specinfra, :os_info
5
+ def initialize(specinfra, os)
6
+ @specinfra = specinfra
7
+ @os_info = os
8
+ end
9
+
10
+ def run_specinfra(type, *args)
11
+ command = specinfra.command.get(type, *args)
12
+ if type.to_s.start_with?("check_")
13
+ check_command(command)
14
+ else
15
+ specinfra.run_command(command)
16
+ end
17
+ end
18
+
19
+ def check_command(*args)
20
+ unless args.last.is_a?(Hash)
21
+ args << {}
22
+ end
23
+ specinfra.run_command(*args).exit_status == 0
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,69 @@
1
+ module Pero
2
+ class Puppet
3
+ class Redhat < Base
4
+ def self.show_versions_commands
5
+ [
6
+ %w(rpm -ivh https://yum.puppetlabs.com/puppetlabs-release-el-7.noarch.rpm),
7
+ %w(yum --showduplicates search puppet),
8
+ %w(yum remove -y puppetlabs-release),
9
+ %w(rpm -ivh https://yum.puppetlabs.com/puppetlabs-release-pc1-el-7.noarch.rpm),
10
+ %w(yum --showduplicates search puppet),
11
+ %w(yum remove -y puppetlabs-release),
12
+ %w(rpm -ivh https://yum.puppetlabs.com/puppet5-release-el-7.noarch.rpm),
13
+ %w(yum --showduplicates search puppet),
14
+ %w(yum remove -y puppet5-release),
15
+ %w(rpm -ivh https://yum.puppetlabs.com/puppet6-release-el-7.noarch.rpm),
16
+ %w(yum --showduplicates search puppet),
17
+ ]
18
+ end
19
+
20
+ def self.show_versions
21
+ image = ::Docker::Image.create('fromImage' => 'centos:7')
22
+ init = image.run("/sbin/init")
23
+ ret = []
24
+ show_versions_commands.each do |c|
25
+ init.exec(c, stdout:false, stderr: false) do |stream, chunk|
26
+ chunk.split(/\n/).each do |r|
27
+ ret << r.gsub(/\.el.*/, '') if r =~ /(^puppet-3|^puppet-agent|^puppet-server|^puppetserver)/
28
+ end
29
+ end
30
+ end
31
+ puts ret.sort.join("\n")
32
+ init.delete(:force => true)
33
+ end
34
+
35
+
36
+ def main_release
37
+ os_info[:release].split(/\./)[0]
38
+ end
39
+
40
+ def install(version)
41
+ release_package, package_name = if Gem::Version.new("4.0.0") > Gem::Version.new(version)
42
+ ["puppetlabs-release-el-#{main_release}.noarch.rpm", "puppet"]
43
+ elsif Gem::Version.new("5.0.0") > Gem::Version.new(version) && Gem::Version.new("4.0.0") <= Gem::Version.new(version)
44
+ ["puppetlabs-release-pc1-el-#{main_release}.noarch.rpm", "puppet"]
45
+ elsif Gem::Version.new("6.0.0") > Gem::Version.new(version) && Gem::Version.new("5.0.0") <= Gem::Version.new(version)
46
+ ["puppet5-release-el-#{main_release}.noarch.rpm", "puppet-agent"]
47
+ else
48
+ ["puppet6-release-el-#{main_release}.noarch.rpm", "puppet-agent"]
49
+ end
50
+
51
+ unless run_specinfra(:check_package_is_installed, package_name, version)
52
+ unless run_specinfra(:check_package_is_installed, release_package.gsub(/-el.*/, ''))
53
+ Pero.log.info "install package #{release_package}"
54
+ run_specinfra(:remove_package, "puppetlabs-release")
55
+ run_specinfra(:remove_package, "puppet5-release")
56
+ run_specinfra(:remove_package, "puppet6-release")
57
+ raise "failed package install:#{release_package}" if specinfra.run_command("rpm -ivh https://yum.puppetlabs.com/#{release_package}").exit_status != 0
58
+ end
59
+
60
+ Pero.log.info "install package #{package_name}-#{version}"
61
+ raise "failed package uninstall:#{package_name}" if run_specinfra(:remove_package, package_name).exit_status != 0
62
+ raise "failed package install:#{package_name} version #{version}" if run_specinfra(:install_package, package_name, version).exit_status != 0
63
+ else
64
+ Pero.log.info "puppet-#{version} installed"
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,35 @@
1
+ module Pero
2
+ module SshExecutable
3
+ # ref:https://stackoverflow.com/questions/3386233/how-to-get-exit-status-with-rubys-netssh-library
4
+ def ssh_exec!(ssh, command)
5
+ stdout_data = ""
6
+ stderr_data = ""
7
+ exit_code = nil
8
+ exit_signal = nil
9
+ ssh.open_channel do |channel|
10
+ channel.exec(command) do |ch, success|
11
+ unless success
12
+ abort "FAILED: couldn't execute command (ssh.channel.exec)"
13
+ end
14
+ channel.on_data do |ch,data|
15
+ stdout_data+=data
16
+ end
17
+
18
+ channel.on_extended_data do |ch,type,data|
19
+ stderr_data+=data
20
+ end
21
+
22
+ channel.on_request("exit-status") do |ch,data|
23
+ exit_code = data.read_long
24
+ end
25
+
26
+ channel.on_request("exit-signal") do |ch, data|
27
+ exit_signal = data.read_long
28
+ end
29
+ end
30
+ end
31
+ ssh.loop
32
+ [stdout_data, stderr_data, exit_code, exit_signal]
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ module Pero
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,36 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "pero/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "pero"
8
+ spec.version = Pero::VERSION
9
+ spec.authors = ["pyama86"]
10
+ spec.email = ["www.kazu.com@gmail.com"]
11
+
12
+ spec.summary = %q{tool for puppet apply from our desktop.}
13
+ spec.description = %q{tool for puppet apply from our desktop.}
14
+ spec.homepage = "https://github.com/pyama86/pero"
15
+ spec.license = "MIT"
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ end
22
+ spec.bindir = "exe"
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ["lib"]
25
+
26
+ spec.add_dependency "thor"
27
+ spec.add_dependency "logger"
28
+ spec.add_dependency "docker-api"
29
+ spec.add_dependency "net-ssh"
30
+ spec.add_dependency 'retryable'
31
+ spec.add_dependency 'specinfra'
32
+ spec.add_dependency 'parallel'
33
+ spec.add_development_dependency "bundler", "~> 1.17"
34
+ spec.add_development_dependency "rake", "~> 10.0"
35
+ spec.add_development_dependency "rspec", "~> 3.0"
36
+ end
metadata ADDED
@@ -0,0 +1,206 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pero
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - pyama86
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-08-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: logger
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: docker-api
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: net-ssh
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: retryable
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: specinfra
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: parallel
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: bundler
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.17'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.17'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rake
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '10.0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '10.0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rspec
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '3.0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '3.0'
153
+ description: tool for puppet apply from our desktop.
154
+ email:
155
+ - www.kazu.com@gmail.com
156
+ executables:
157
+ - pero
158
+ extensions: []
159
+ extra_rdoc_files: []
160
+ files:
161
+ - ".gitignore"
162
+ - Gemfile
163
+ - Gemfile.lock
164
+ - LICENSE.txt
165
+ - README.md
166
+ - Rakefile
167
+ - Vagrantfile
168
+ - bin/console
169
+ - bin/setup
170
+ - bootstrap.rb
171
+ - exe/pero
172
+ - lib/pero.rb
173
+ - lib/pero/cli.rb
174
+ - lib/pero/docker.rb
175
+ - lib/pero/history.rb
176
+ - lib/pero/misc/puppet.conf
177
+ - lib/pero/puppet.rb
178
+ - lib/pero/puppet/base.rb
179
+ - lib/pero/puppet/redhat.rb
180
+ - lib/pero/ssh_executable.rb
181
+ - lib/pero/version.rb
182
+ - pero.gemspec
183
+ homepage: https://github.com/pyama86/pero
184
+ licenses:
185
+ - MIT
186
+ metadata: {}
187
+ post_install_message:
188
+ rdoc_options: []
189
+ require_paths:
190
+ - lib
191
+ required_ruby_version: !ruby/object:Gem::Requirement
192
+ requirements:
193
+ - - ">="
194
+ - !ruby/object:Gem::Version
195
+ version: '0'
196
+ required_rubygems_version: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - ">="
199
+ - !ruby/object:Gem::Version
200
+ version: '0'
201
+ requirements: []
202
+ rubygems_version: 3.0.6
203
+ signing_key:
204
+ specification_version: 4
205
+ summary: tool for puppet apply from our desktop.
206
+ test_files: []