#!/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_clouds = MU::Cloud.supportedClouds available_clouds.reject! { |cloud| cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud) cloudclass.listCredentials.nil? or cloudclass.listCredentials.size == 0 } 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 => available_clouds 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 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 :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 research searching to the named accounts/projects/subscriptions, instead of search all habitats visible to our credentials.", :required => false, :type => :strings 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[:savedeploys_given] $opt[:savedeploys] = !$opt[:diff] 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]) adoption.scrapeClouds 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 } conf_engine = MU::Config.new("#{appname}.yaml") stack_conf = conf_engine.config # puts stack_conf.to_yaml MU.log "#{appname}.yaml validated successfully", MU::NOTICE 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 } }