Class: Onceover::Deploy

Inherits:
Object
  • Object
show all
Defined in:
lib/onceover/deploy.rb

Instance Method Summary collapse

Instance Method Details

#deploy_local(repo = Onceover::Controlrepo.new, opts = {}) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/onceover/deploy.rb', line 4

def deploy_local(repo = Onceover::Controlrepo.new, opts = {})
  require 'onceover/controlrepo'
  require 'pathname'
  require 'fileutils'
  require 'multi_json'

  logger.debug 'Deploying locally (R10K)...'

  # Only default to running r10k if there is a Puppetfile
  skip_r10k_default = !(File.file?(repo.puppetfile))
  skip_r10k = opts[:skip_r10k] || skip_r10k_default
  force     = opts[:force] || false
  # Only attempt to resolve vendored modules if configured to do so
  auto_vendored = opts[:auto_vendored] || false

  require 'onceover/vendored_modules' if auto_vendored

  if repo.tempdir == nil
    repo.tempdir = Dir.mktmpdir('r10k')
  else
    logger.debug "Creating #{repo.tempdir}"
    FileUtils.mkdir_p(repo.tempdir)
  end

  # Overall plan: Copy everything in control repo to a tempdir then move the tempdir to the
  # destination, just in case we get a recursive copy

  # We need to exclude some files:
  # * our own cache
  # * stuff from git
  # * stale puppet modules
  # * random ruby gems (bundler/rvm?)
  # * ...etc
  excluded_dirs = [
    File.join(repo.root, ".onceover"),
    File.join(repo.root, ".git"),
    File.join(repo.root, ".modules"),
    File.join(repo.root, "vendor"),
  ]
  excluded_dirs << ENV['GEM_HOME'] if ENV['GEM_HOME']

  # A Local modules directory likely means that the user installed r10k folders into their local control repo
  # This conflicts with the step where onceover installs r10k after copying the control repo to the temporary
  # .onceover directory.  The following skips copying the modules folder, to not later cause an error.
  if File.directory?("modules")
    logger.warn "Found modules directory in your controlrepo, skipping the copy of this directory.  If you installed modules locally using r10k, this warning is normal, if you have created modules in a local modules directory, onceover does not support testing these files, please rename this directory to conform with Puppet best practices, as this folder will conflict with Puppet's native installation of modules."
  end

  logger.debug "Creating temp dir as a staging directory for copying the controlrepo to #{repo.tempdir}"
  temp_controlrepo = Dir.mktmpdir('controlrepo')

  # onceover stores a big list of all the relative paths it places within its cache directory
  onceover_manifest = []

  Find.find repo.root do |source|
    # work out a relative path to this source, eg:
    # /home/geoff/control-repo/foo/bar -> foo/bar
    relative_source = Pathname.new(source).relative_path_from(Pathname.new(repo.root)).to_s

    target = File.join(temp_controlrepo, relative_source)

    # ignore the path "." which represents the root of our control repo
    if relative_source != "."
      # add to list of files copied to cache by onceover
      onceover_manifest << relative_source

      if File.symlink?(source)
        # Handle symlinks
        link_target = File.readlink(source) # Get the target of the symlink
        FileUtils.ln_s link_target, target, force: true # Create symlink at target
      elsif File.directory? source
        Find.prune if excluded_dirs.include? source
        FileUtils.mkdir target
      else
        FileUtils.copy source, target
      end
    end
  end
  logger.debug "Writing manifest of copied controlrepo files"
  File.write("#{temp_controlrepo}/.onceover_manifest.json", onceover_manifest.to_json)

  # When using puppetfile vs deploy with r10k, we want to respect the :control_branch
  # located in the Puppetfile. To accomplish that, we use git and find the current
  # branch name, then replace strings within the staged puppetfile, prior to copying.
  logger.debug "Checking current working branch"
  git_branch = `git rev-parse --abbrev-ref HEAD`.chomp

  logger.debug "found #{git_branch} as current working branch"
  # Only try to modify Puppetfile if it exists
  unless skip_r10k
    FileUtils.copy repo.puppetfile, "#{temp_controlrepo}/Puppetfile"
    puppetfile_contents = File.read("#{temp_controlrepo}/Puppetfile")

    # Avoid touching thing if we don't need to
    if /:control_branch/.match(puppetfile_contents)
      logger.debug "replacing :control_branch mentions in the Puppetfile with #{git_branch}"
      new_puppetfile_contents = puppetfile_contents.gsub(":control_branch", "'#{git_branch}'")
      File.write("#{temp_controlrepo}/Puppetfile", new_puppetfile_contents)
    end

    if auto_vendored
      tmp_puppetfile = File.join(temp_controlrepo, 'Puppetfile')
      tmp_puppetfile_contents = File.read(tmp_puppetfile)
      vm = Onceover::VendoredModules.new({ repo: repo })
      puppetfile = R10K::ModuleLoader::Puppetfile.new(basedir: temp_controlrepo)
      vm.puppetfile_missing_vendored(puppetfile)
      unless vm.missing_vendored.empty?
        missing_slugs = vm.missing_vendored.map do |missing_mod|
          missing_mod.keys[0]
        end
        logger.debug "Adding #{missing_slugs} to #{tmp_puppetfile}"
        modlines = vm.missing_vendored.map do |missing_mod|
          mod_slug = missing_mod.keys[0]
          "mod '#{mod_slug}',\n  git: '#{missing_mod[mod_slug][:git]}',\n  ref: '#{missing_mod[mod_slug][:ref]}'"
        end.join("\n")
        File.write(tmp_puppetfile, "#{tmp_puppetfile_contents}\n# Onceover Managed Vendored Modules\n#{modlines}")
      end
    end
  end

  # Remove all files written by the last onceover run, but not the ones
  # added by r10k, because that's what we are trying to cache but we don't
  # know what they are
  old_manifest_path = "#{repo.tempdir}/#{repo.environmentpath}/production/.onceover_manifest.json"
  if File.exist? old_manifest_path
    logger.debug "Found manifest from previous run, parsing..."
    old_manifest = MultiJson.load(File.read(old_manifest_path))
    logger.debug "Removing #{old_manifest.count} files"
    old_manifest.reverse.each do |file|
      FileUtils.rm_f(File.join("#{repo.tempdir}/#{repo.environmentpath}/production/",file))
    end
  end
  FileUtils.mkdir_p("#{repo.tempdir}/#{repo.environmentpath}")

  logger.debug "Copying #{temp_controlrepo} to #{repo.tempdir}/#{repo.environmentpath}/production"
  FileUtils.cp_r("#{temp_controlrepo}/.", "#{repo.tempdir}/#{repo.environmentpath}/production")
  FileUtils.rm_rf(temp_controlrepo)

  # Pull the trigger! If it's not already been pulled
  if repo.tempdir and not skip_r10k
    if File.directory?(repo.tempdir)
      # TODO: Change this to call out to r10k directly to do this
      # Probably something like:
      # R10K::Settings.global_settings.evaluate(with_overrides)
      # R10K::Action::Deploy::Environment
      prod_dir = "#{repo.tempdir}/#{repo.environmentpath}/production"
      Dir.chdir(prod_dir) do
        install_cmd = []
        install_cmd << 'r10k puppetfile install --color'
        install_cmd << "--force" if force
        install_cmd << "--config #{repo.r10k_config_file}" if repo.r10k_config_file
        install_cmd << (logger.level > 0 ? "--verbose" : "--verbose debug") # Enable debugging if we're debugging
        install_cmd << "--trace" if opts[:trace]
        install_cmd = install_cmd.join(' ')
        logger.debug "Running #{install_cmd} from #{prod_dir}"
        system(install_cmd)
        raise 'r10k could not install all required modules' unless $?.success?
      end
    else
      raise "#{repo.tempdir} is not a directory"
    end
  end

  # Return repo.tempdir for use
  repo.tempdir
end