bin/mu-adopt
#!/usr/local/ruby-current/bin/ruby
#
# Copyright:: Copyright (c) 2019 eGlobalTech, Inc., all rights reserved
#
# Licensed under the BSD-3 license (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License in the root of the project or at
#
# http://egt-labs.com/mu/LICENSE.html
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require File.expand_path(File.dirname(__FILE__))+"/mu-load-config.rb"
require 'rubygems'
require 'bundler/setup'
require 'optimist'
require 'mu'
available_types = MU::Cloud.resource_types.keys.map { |t| t.to_s }
grouping_options = {
"logical" => "Group resources in logical layers (folders and habitats together, users/roles/groups together, network resources together, etc)",
"omnibus" => "Jam everything into one monolothic configuration"
}
$opt = Optimist::options do
banner <<-EOS
#{$0}
EOS
opt :appname, "The overarching name of the application stack we will generate", :required => false, :default => "mu", :type => :string
opt :types, "The resource types to scan and import. Valid types: #{available_types.join(", ")}", :required => false, :type => :strings, :default => available_types
opt :clouds, "The cloud providers to scan and import.", :required => false, :type => :strings, :default => MU::Cloud.availableClouds
opt :parent, "Where applicable, resources which reside in the root folder or organization are configured with the specified parent in our target BoK", :required => false, :type => :string
opt :billing, "Force-set this billing entity on created resources, instead of copying from the live resources", :required => false, :type => :string
opt :sources, "One or more sets of credentials to use when importing resources. By default we will search and import from all sets of available credentials for each cloud provider specified with --clouds", :required => false, :type => :strings
opt :credentials, "Override the 'credentials' value in our generated Baskets of Kittens to target a single, specific account. Our default behavior is to set each resource to deploy into the account from which it was sourced.", :required => false, :type => :string
opt :savedeploys, "Generate actual deployment metadata in #{MU.dataDir}/deployments, as though the resources we found were created with mu-deploy. If we are generating more than one configuration, and a resource needs to reference another resource (e.g. to declare a VPC in which to reside), this will allow us to reference them as virtual resource, rather than by raw cloud identifier.", :required => false, :type => :boolean, :default => false
opt :diff, "List the differences between what we find and an existing, saved deploy from a previous run, if one exists.", :required => false, :type => :boolean
opt :merge_changes, "When using --diff, merge detected changes into the baseline deploy after reporting on them.", :required => false, :type => :boolean, :default => false
opt :grouping, "Methods for grouping found resources into separate Baskets.\n\n"+MU::Adoption::GROUPMODES.keys.map { |g| "* "+g.to_s+": "+MU::Adoption::GROUPMODES[g] }.join("\n")+"\n\n", :required => false, :type => :string, :default => "logical"
opt :habitats, "Limit scope of searches to the named accounts/projects/subscriptions, instead of search all habitats visible to our credentials.", :required => false, :type => :strings
opt :regions, "Restrict to operating on a subset of available regions, instead of all that we know about.", :require => false, :type => :strings
opt :scrub, "Whether to set scrub_mu_isms in the BoKs we generate", :default => $MU_CFG.has_key?('adopt_scrub_mu_isms') ? $MU_CFG['adopt_scrub_mu_isms'] : false
opt :pattern, "Only adopt resources whose resource name would match this pattern. Must be a valid regular expression. Alphabetical characters will be treated case-insensitively.", :required => false, :type => :string
end
ok = true
app_pattern = Regexp.new('^[a-z][0-9a-z\-_]{0,10}[a-z0-9]$', true)
if !$opt[:appname] or !app_pattern.match($opt[:appname])
MU.log "--appname must match pattern #{app_pattern.to_s}", MU::ERR
exit 1
end
if $opt[:diff]
$opt[:savedeploys] = false
end
pattern = nil
if $opt[:pattern]
begin
pattern = Regexp.new($opt[:pattern], true)
rescue RegexpError => e
MU.log "Invalid --pattern option: #{e.message}", MU::ERR
exit 1
end
end
types = []
$opt[:types].each { |t|
t_name = t.gsub(/-/, "_")
t_name.gsub!(/^[^a-z0-9]|[^a-z0-9]$/i, "")
shortclass, name, plural, classname = MU::Cloud.getResourceNames(t_name)
if !classname
MU.log "'#{t}' does not map to a valid Mu resource type", MU::ERR
ok = false
else
types << shortclass
end
}
clouds = []
if !$opt[:clouds] or $opt[:clouds].empty?
MU.log "At least one cloud must be specified", MU::ERR
ok = false
end
$opt[:clouds].each { |cloud|
found_match = false
MU::Cloud.supportedClouds.each { |known_cloud|
if cloud.match(/^[^a-z0-9]*?#{Regexp.quote(known_cloud)}[^a-z0-9]*?$/i)
clouds << known_cloud
found_match = true
break
end
}
if !found_match
MU.log "'#{cloud}' does not map to a valid Mu cloud layer", MU::ERR
ok = false
end
}
if !ok
puts "Invoke with --help for more information."
exit 1
end
adoption = MU::Adoption.new(clouds: clouds, types: types, parent: $opt[:parent], billing: $opt[:billing], sources: $opt[:sources], credentials: $opt[:credentials], group_by: $opt[:grouping].to_sym, savedeploys: $opt[:savedeploys], diff: $opt[:diff], habitats: $opt[:habitats], scrub_mu_isms: $opt[:scrub], regions: $opt[:regions], merge: $opt[:merge_changes], pattern: pattern)
found = adoption.scrapeClouds
if found.nil? or found.empty?
MU.log "No resources found to adopt", MU::WARN, details: {"clouds" => clouds, "types" => types }
exit
end
MU.log "Generating baskets", MU::DEBUG
boks = adoption.generateBaskets(prefix: $opt[:appname])
boks.each_pair { |appname, bok|
MU.log "Writing to #{appname}.yaml"
File.open("#{appname}.yaml", "w") { |f|
f.write JSON.parse(JSON.generate(bok)).to_yaml
}
# puts stack_conf.to_yaml
MU::Cloud.resource_types.each_pair { |type, cfg|
if bok[cfg[:cfg_plural]]
MU.log "#{bok[cfg[:cfg_plural]].size.to_s} #{cfg[:cfg_plural]}", MU::NOTICE
end
}
}