modules/mu/clouds/google/vpc.rb in cloud-mu-2.0.0.pre.beta2 vs modules/mu/clouds/google/vpc.rb in cloud-mu-2.0.0.pre.beta3

- old
+ new

@@ -19,10 +19,11 @@ # Creation of Virtual Private Clouds and associated artifacts (routes, subnets, etc). class VPC < MU::Cloud::VPC @deploy = nil @config = nil + @project_id = nil attr_reader :mu_name attr_reader :cloud_id attr_reader :url attr_reader :config @@ -43,10 +44,15 @@ if !mu_name.nil? @mu_name = mu_name if @cloud_id.nil? or @cloud_id.empty? @cloud_id = MU::Cloud::Google.nameStr(@mu_name) end + @config['project'] ||= MU::Cloud::Google.defaultProject(@config['credentials']) + if !@project_id + project = MU::Cloud::Google.projectLookup(@config['project'], @deploy, sibling_only: true, raise_on_fail: false) + @project_id = project.nil? ? @config['project'] : project.cloudobj.cloud_id + end loadSubnets elsif @config['scrub_mu_isms'] @mu_name = @config['name'] else @mu_name = @deploy.getResourceName(@config['name']) @@ -54,18 +60,21 @@ end # Called automatically by {MU::Deploy#createResources} def create + @project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloudobj.cloud_id + networkobj = MU::Cloud::Google.compute(:Network).new( name: MU::Cloud::Google.nameStr(@mu_name), description: @deploy.deploy_id, auto_create_subnetworks: false # i_pv4_range: @config['ip_block'] ) - MU.log "Creating network #{@mu_name} (#{@config['ip_block']}) in project #{@config['project']}", details: networkobj - resp = MU::Cloud::Google.compute(credentials: @config['credentials']).insert_network(@config['project'], networkobj) + MU.log "Creating network #{@mu_name} (#{@config['ip_block']}) in project #{@project_id}", details: networkobj + + resp = MU::Cloud::Google.compute(credentials: @config['credentials']).insert_network(@project_id, networkobj) @url = resp.self_link # XXX needs to go in notify @cloud_id = resp.name if @config['subnets'] subnetthreads = [] @@ -73,19 +82,19 @@ @config['subnets'].each { |subnet| subnetthreads << Thread.new { MU.dupGlobals(parent_thread_id) subnet_name = @config['name']+"-"+subnet['name'] subnet_mu_name = MU::Cloud::Google.nameStr(@deploy.getResourceName(subnet_name)) - MU.log "Creating subnetwork #{subnet_mu_name} (#{subnet['ip_block']}) in project #{@config['project']}", details: subnet + MU.log "Creating subnetwork #{subnet_mu_name} (#{subnet['ip_block']}) in project #{@project_id}", details: subnet subnetobj = MU::Cloud::Google.compute(:Subnetwork).new( name: subnet_mu_name, description: @deploy.deploy_id, ip_cidr_range: subnet['ip_block'], network: @url, region: subnet['availability_zone'] ) - resp = MU::Cloud::Google.compute(credentials: @config['credentials']).insert_subnetwork(@config['project'], subnet['availability_zone'], subnetobj) + resp = MU::Cloud::Google.compute(credentials: @config['credentials']).insert_subnetwork(@project_id, subnet['availability_zone'], subnetobj) } } subnetthreads.each do |t| t.join @@ -118,44 +127,41 @@ # Describe this VPC # @return [Hash] def notify base = MU.structToHash(cloud_desc) base["cloud_id"] = @cloud_id + base["project_id"] = @project_id base.merge!(@config.to_h) -if @config['name'] == "gkeprivate" - pp base.keys - puts base['cloud_id'] -end - base end # Describe this VPC from the cloud platform's perspective # @return [Hash] def cloud_desc - @config['project'] ||= MU::Cloud::Google.defaultProject(@config['credentials']) - resp = MU::Cloud::Google.compute(credentials: @config['credentials']).get_network(@config['project'], @cloud_id) + resp = MU::Cloud::Google.compute(credentials: @config['credentials']).get_network(@project_id, @cloud_id) if @cloud_id.nil? or @cloud_id == "" MU.log "Couldn't describe #{self}, @cloud_id #{@cloud_id.nil? ? "undefined" : "empty" }", MU::ERR return nil end resp = resp.to_h @url ||= resp[:self_link] routes = MU::Cloud::Google.compute(credentials: @config['credentials']).list_routes( - @config['project'], + @project_id, filter: "network eq #{@cloud_id}" ).items resp[:routes] = routes.map { |r| r.to_h } if routes # XXX subnets too resp end # Called automatically by {MU::Deploy#createResources} def groom + @project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloudobj.cloud_id + rtb = @config['route_tables'].first rtb['routes'].each { |route| # If we had a sibling server being spun up as a NAT, rig up the # route that the hosts behind it will need. @@ -167,25 +173,10 @@ if !@config['peers'].nil? count = 0 @config['peers'].each { |peer| if peer['vpc']['vpc_name'] peer_obj = @deploy.findLitterMate(name: peer['vpc']['vpc_name'], type: "vpcs") - if peer_obj - if peer_obj.config['peers'] - skipme = false - peer_obj.config['peers'].each { |peerpeer| - if peerpeer['vpc']['vpc_name'] == @config['name'] and - (peer['vpc']['vpc_name'] <=> @config['name']) == -1 - skipme = true - MU.log "VPCs #{peer['vpc']['vpc_name']} and #{@config['name']} both declare mutual peering connection, ignoring #{@config['name']}'s redundant declaration", MU::DEBUG -# XXX and if deploy_id matches or is unset - end - } - next if skipme - end - end - else tag_key, tag_value = peer['vpc']['tag'].split(/=/, 2) if !peer['vpc']['tag'].nil? if peer['vpc']['deploy_id'].nil? and peer['vpc']['vpc_id'].nil? and tag_key.nil? peer['vpc']['deploy_id'] = @deploy.deploy_id end @@ -217,17 +208,24 @@ name: cnxn_name, auto_create_routes: true, peer_network: url ) - MU.log "Peering #{@url} with #{url}, connection name is #{cnxn_name}", details: peerreq - - MU::Cloud::Google.compute(credentials: @config['credentials']).add_network_peering( - @config['project'], - @cloud_id, - peerreq - ) + begin + MU.log "Peering #{@cloud_id} with #{peer_obj.cloudobj.cloud_id}, connection name is #{cnxn_name}", details: peerreq + MU::Cloud::Google.compute(credentials: @config['credentials']).add_network_peering( + @project_id, + @cloud_id, + peerreq + ) + rescue ::Google::Apis::ClientError => e + if e.message.match(/operation in progress on the local or peer network/) + MU.log e.message, MU::DEBUG, details: peerreq + sleep 10 + retry + end + end count += 1 } end end @@ -291,11 +289,11 @@ found = [] resp = nil MU::Cloud::Google.listRegions(@config['us_only']).each { |r| resp = MU::Cloud::Google.compute(credentials: @config['credentials']).list_subnetworks( - @config['project'], + @project_id, r, filter: "network eq #{network[:self_link]}" ) next if resp.nil? or resp.items.nil? resp.items.each { |subnet| @@ -494,10 +492,16 @@ # @return [Boolean] def self.isGlobal? true end + # Denote whether this resource implementation is experiment, ready for + # testing, or ready for production use. + def self.quality + MU::Cloud::RELEASE + end + # Remove all VPC resources associated with the currently loaded deployment. # @param noop [Boolean]: If true, will only print what would be done # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server # @param region [String]: The cloud provider region # @return [void] @@ -541,10 +545,11 @@ # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member # @return [Boolean]: True if validation succeeded, False otherwise def self.validateConfig(vpc, configurator) ok = true + if vpc['create_standard_subnets'] # Manufacture some generic routes, if applicable. if !vpc['route_tables'] or vpc['route_tables'].empty? vpc['route_tables'] = [ { @@ -555,50 +560,50 @@ "name" => "private", "routes" => [ { "destination_network" => "0.0.0.0/0", "gateway" => "#NAT" } ] } ] end - - # Generate a set of subnets per route, if none are declared - if !vpc['subnets'] or vpc['subnets'].empty? - if vpc['regions'].nil? or vpc['regions'].empty? - vpc['regions'] = MU::Cloud::Google.listRegions(vpc['us_only']) - end - blocks = configurator.divideNetwork(vpc['ip_block'], vpc['regions'].size*vpc['route_tables'].size, 29) - ok = false if blocks.nil? - - vpc["subnets"] = [] - vpc['route_tables'].each { |t| - count = 0 - vpc['regions'].each { |r| - block = blocks.shift - vpc["subnets"] << { - "availability_zone" => r, - "route_table" => t["name"], - "ip_block" => block.to_s, - "name" => "Subnet"+count.to_s+t["name"].capitalize, - "map_public_ips" => true + else + # If create_standard_subnets is off, and no route_tables were + # declared at all, let's assume we want purely self-contained + # private VPC, and create a dummy route accordingly. + vpc['route_tables'] ||= [ + { + "name" => "private", + "routes" => [ + { + "destination_network" => "0.0.0.0/0" } - count = count + 1 - } + ] } - end + ] end - # If create_standard_subnets is off, and no route_tables were - # declared at all, let's assume we want purely self-contained - # private VPCs - vpc['route_tables'] ||= [ - { - "name" => "private", - "routes" => [ - { - "destination_network" => "0.0.0.0/0" + # Generate a set of subnets per route, if none are declared + if !vpc['subnets'] or vpc['subnets'].empty? + if vpc['regions'].nil? or vpc['regions'].empty? + vpc['regions'] = MU::Cloud::Google.listRegions(vpc['us_only']) + end + blocks = configurator.divideNetwork(vpc['ip_block'], vpc['regions'].size*vpc['route_tables'].size, 29) + ok = false if blocks.nil? + + vpc["subnets"] = [] + vpc['route_tables'].each { |t| + count = 0 + vpc['regions'].each { |r| + block = blocks.shift + vpc["subnets"] << { + "availability_zone" => r, + "route_table" => t["name"], + "ip_block" => block.to_s, + "name" => "Subnet"+count.to_s+t["name"].capitalize, + "map_public_ips" => true } - ] + count = count + 1 + } } - ] + end # Google VPCs can't have routes that are anything other than global # (they can be tied to individual instances by tags, but w/e). So we # decompose our VPCs into littler VPCs, one for each declared route # table, so that the routes therein will only apply to the portion of @@ -615,24 +620,30 @@ "credentials" => vpc['credentials'], "virtual_name" => vpc['name'], "ip_block" => blocks.shift, "route_tables" => [tbl], "parent_block" => vpc['ip_block'], - "subnets" => [] + "subnets" => [], + "peers" => vpc['peers'] } MU.log "Splitting VPC #{newvpc['name']} off from #{vpc['name']}", MU::NOTICE vpc.each_pair { |key, val| next if ["name", "route_tables", "subnets", "ip_block"].include?(key) newvpc[key] = val } newvpc['peers'] ||= [] +# Add the peer connections we're generating, in addition peernames.each { |peer| - if peer != vpc['name']+"-"+tbl['name'] + if peer != newvpc['name'] newvpc['peers'] << { "vpc" => { "vpc_name" => peer } } end } + newvpc['peers'].reject! { |p| + p.values.first['vpc_name'] == newvpc['name'] or p.values.first['vpc_name'] == vpc['name'] + } + vpc["subnets"].each { |subnet| newvpc["subnets"] << subnet if subnet["route_table"] == tbl["name"] } ok = false if !configurator.insertKitten(newvpc, "vpcs", true) } @@ -769,19 +780,19 @@ ) end # several other cases missing for various types of routers (raw IPs, instance ids, etc) XXX elsif route['gateway'] == "#DENY" resp = MU::Cloud::Google.compute(credentials: @config['credentials']).list_routes( - @config['project'], + @project_id, filter: "network eq #{network}" ) if !resp.nil? and !resp.items.nil? resp.items.each { |r| next if r.next_hop_gateway.nil? or !r.next_hop_gateway.match(/\/global\/gateways\/default-internet-gateway$/) MU.log "Removing standard route #{r.name} per our #DENY entry" - MU::Cloud::Google.compute(credentials: @config['credentials']).delete_route(@config['project'], r.name) + MU::Cloud::Google.compute(credentials: @config['credentials']).delete_route(@project_id, r.name) } end elsif route['gateway'] == "#INTERNET" routeobj = ::Google::Apis::ComputeBeta::Route.new( name: routename, @@ -794,14 +805,14 @@ ) end if route['gateway'] != "#DENY" and routeobj begin - MU::Cloud::Google.compute(credentials: @config['credentials']).get_route(@config['project'], routename) + MU::Cloud::Google.compute(credentials: @config['credentials']).get_route(@project_id, routename) rescue ::Google::Apis::ClientError, MU::MuError => e if e.message.match(/notFound/) - MU.log "Creating route #{routename} in project #{@config['project']}", details: routeobj - resp = MU::Cloud::Google.compute(credentials: @config['credentials']).insert_route(@config['project'], routeobj) + MU.log "Creating route #{routename} in project #{@project_id}", details: routeobj + resp = MU::Cloud::Google.compute(credentials: @config['credentials']).insert_route(@project_id, routeobj) else # TODO can't update GCP routes, would have to delete and re-create end end end