class MU::Cloud::CloudFormation::Server

A server as configured in {MU::Config::BasketofKittens::servers}

Attributes

cfm_name[R]
cfm_template[R]
cloud_desc[R]
cloud_id[R]
config[R]
deploy[R]
groomer[R]
mu_name[R]
mu_windows_name[RW]

Public Class Methods

addStdPoliciesToIAMProfile(rolename, cloudformation_data: {}) click to toggle source

Insert a Server's standard IAM role needs into an arbitrary IAM profile

# File modules/mu/providers/cloudformation/server.rb, line 259
        def self.addStdPoliciesToIAMProfile(rolename, cloudformation_data: {})
          policies = Hash.new
          aws_str = MU::Cloud::AWS.isGovCloud?(region) ? "aws-us-gov" : "aws"
          policies['Mu_Bootstrap_Secret_'+MU.deploy_id] ='{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["s3:GetObject"],"Resource":"arn:'+aws_str+':s3:::'+MU.adminBucketName+'/'+"#{MU.deploy_id}-secret"+'"}]}'
# XXX this doesn't work unless we can deliver the stack name somehow, and also make the ARN look like arn:aws:cloudformation:us-east-1:144333873908:stack/CATAPULT/*
#          policies['Mu_Describe_Own_CloudFormation_Stack'] ='{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["cloudformation:DescribeStacks", "cloudformation:ListStacks"],"Resource": {"Ref":"AWS::StackId"}}]}'
          policies['Mu_Describe_Own_CloudFormation_Stack'] ='{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["cloudformation:DescribeStacks", "cloudformation:ListStacks"],"Resource": "*"}]}'
# XXX and then there's this. Doesn't seem to be an easy way to prevent an
# instance from just generating volumes at random, tagging other peoples'
# stuff, etc.
#          policies['Mu_Volume_Management'] ='{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["ec2:CreateTags","ec2:CreateVolume","ec2:AttachVolume","ec2:DescribeInstanceAttribute","ec2:DescribeVolumeAttribute","ec2:DescribeVolumeStatus","ec2:DescribeVolumes"],"Resource":"*"}]}'
          policies.each_pair { |name, doc|
            MU::Cloud::CloudFormation.setCloudFormationProp(
              cloudformation_data[rolename],
              "Policies",
              {
                "PolicyName" => name,
                "PolicyDocument" => JSON.parse(doc)
              }
            )
          }
          return cloudformation_data
        end
cleanup(*args) click to toggle source

Placeholder. This is a NOOP for CloudFormation, which doesn't build resources directly.

# File modules/mu/providers/cloudformation/server.rb, line 339
def self.cleanup(*args)
  MU.log "cleanup() not implemented for CloudFormation layer", MU::DEBUG
  nil
end
createIAMProfile(rolename, base_profile: nil, extra_policies: nil, cloudformation_data: {}) click to toggle source

Create an Amazon IAM instance profile. One of these should get created for each class of instance (each {MU::Cloud::AWS::Server} or {MU::Cloud::AWS::ServerPool}), and will include both baseline Mu policies and whatever other policies are requested. @param rolename [String]: The name of the role to create, generally a {MU::Cloud::AWS::Server} mu_name @return [String]: The name of the instance profile.

# File modules/mu/providers/cloudformation/server.rb, line 289
def self.createIAMProfile(rolename, base_profile: nil, extra_policies: nil, cloudformation_data: {})
  policies = Hash.new

  cfm_role_name, role_cfm_template = MU::Cloud::CloudFormation.cloudFormationBase("iamrole", name: rolename)
  cfm_prof_name, prof_cfm_template = MU::Cloud::CloudFormation.cloudFormationBase("iamprofile", name: rolename)
  cloudformation_data.merge!(role_cfm_template)
  cloudformation_data.merge!(prof_cfm_template)

  if base_profile
    MU.log "Incorporating policies from existing IAM profile '#{base_profile}'"
    resp = MU::Cloud::AWS.iam.get_instance_profile(instance_profile_name: base_profile)
    resp.instance_profile.roles.each { |baserole|
      role_policies = MU::Cloud::AWS.iam.list_role_policies(role_name: baserole.role_name).policy_names
      role_policies.each { |name|
        resp = MU::Cloud::AWS.iam.get_role_policy(
            role_name: baserole.role_name,
            policy_name: name
        )
        policies[name] = CGI.unescape(resp.policy_document)
      }
    }
  end
  if extra_policies
    MU.log "Incorporating other specified policies", details: extra_policies
    extra_policies.each { |policy_set|
      policy_set.each_pair { |name, policy|
        if policies.has_key?(name)
          MU.log "Attempt to add duplicate node policy '#{name}' to '#{rolename}'", MU::WARN, details: policy
          next
        end
        policies[name] = JSON.generate(policy)
      }
    }
  end
  policies.each_pair { |name, doc|
    MU::Cloud::CloudFormation.setCloudFormationProp(cloudformation_data[cfm_role_name], "Policies", { "PolicyName" => name, "PolicyDocument" => JSON.parse(doc) })
  }
  MU::Cloud::CloudFormation.setCloudFormationProp(cloudformation_data[cfm_prof_name], "Roles", { "Ref" => cfm_role_name } )
  MU::Cloud::CloudFormation.setCloudFormationProp(cloudformation_data[cfm_prof_name], "DependsOn", cfm_role_name)
    return [rolename, cfm_role_name, cfm_prof_name]
end
find(*args) click to toggle source

Placeholder. This is a NOOP for CloudFormation, which doesn't build resources directly.

# File modules/mu/providers/cloudformation/server.rb, line 333
def self.find(*args)
  MU.log "find() not implemented for CloudFormation layer", MU::DEBUG
  nil
end
imageTimeStamp(ami_id, credentials: nil, region: nil) click to toggle source

Return the date/time a machine image was created. @param ami_id [String]: AMI identifier of an Amazon Machine Image @param credentials [String] @return [DateTime]

# File modules/mu/providers/cloudformation/server.rb, line 348
def self.imageTimeStamp(ami_id, credentials: nil, region: nil)
  MU::Cloud::AWS.imageTimeStamp(ami_id, credentials: credentials, region: region)
end
isGlobal?() click to toggle source

Does this resource type exist as a global (cloud-wide) artifact, or is it localized to a region/zone? @return [Boolean]

# File modules/mu/providers/cloudformation/server.rb, line 379
def self.isGlobal?
  MU::Cloud.resourceClass("AWS", "Server").isGlobal?
end
new(mommacat: nil, kitten_cfg: nil, mu_name: nil, cloud_id: nil) click to toggle source

@param mommacat [MU::MommaCat]: A {MU::Mommacat} object containing the deploy of which this resource is/will be a member. @param kitten_cfg [Hash]: The fully parsed and resolved {MU::Config} resource descriptor as defined in {MU::Config::BasketofKittens::servers}

# File modules/mu/providers/cloudformation/server.rb, line 35
def initialize(mommacat: nil, kitten_cfg: nil, mu_name: nil, cloud_id: nil)
  @deploy = mommacat
  @config = kitten_cfg
  @cloud_id = cloud_id

  @userdata = MU::Cloud.fetchUserdata(
    platform: @config["platform"],
    template_variables: {
      "deployKey" => Base64.urlsafe_encode64(@deploy.public_key),
      "deploySSHKey" => @deploy.ssh_public_key,
      "muID" => MU.deploy_id,
      "muUser" => MU.chef_user,
      "publicIP" => MU.mu_public_ip,
      "mommaCatPort" => MU.mommaCatPort,
      "skipApplyUpdates" => @config['skipinitialupdates'],
      "windowsAdminName" => @config['windows_admin_username'],
      "resourceName" => @config["name"],
      "resourceType" => "server"
    },
    custom_append: @config['userdata_script'],
    scrub_mu_isms: @config['scrub_mu_isms']
  )

  @disk_devices = MU::Cloud.resourceClass("AWS", "Server").disk_devices
  @ephemeral_mappings = MU::Cloud.resourceClass("AWS", "Server").ephemeral_mappings

  if !mu_name.nil?
    @mu_name = mu_name
    @config['mu_name'] = @mu_name
    # describe
    @mu_windows_name = @deploydata['mu_windows_name'] if @mu_windows_name.nil? and @deploydata
  elsif @config['scrub_mu_isms']
    @mu_name = @config['name']
  else
    if kitten_cfg.has_key?("basis")
      @mu_name = @deploy.getResourceName(@config['name'], need_unique_string: true)
    else
      @mu_name = @deploy.getResourceName(@config['name'])
    end
    @config['mu_name'] = @mu_name

    @config['instance_secret'] = Password.random(50)
  end
  @groomer = MU::Groomer.new(self)

end
schema(config) click to toggle source

Cloud-specific configuration properties. @param config [MU::Config]: The calling MU::Config object @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource

# File modules/mu/providers/cloudformation/server.rb, line 355
def self.schema(config)
  MU::Cloud.resourceClass("AWS", "Server").schema(config)
end
validateConfig(server, configurator) click to toggle source

Cloud-specific pre-processing of {MU::Config::BasketofKittens::servers}, bare and unvalidated. @param server [Hash]: The resource to process and validate @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member @return [Boolean]: True if validation succeeded, False otherwise

# File modules/mu/providers/cloudformation/server.rb, line 372
def self.validateConfig(server, configurator)
  MU::Cloud.resourceClass("AWS", "Server").validateConfig(server, configurator)
end
validateInstanceType(size, region) click to toggle source

Confirm that the given instance size is valid for the given region. If someone accidentally specified an equivalent size from some other cloud provider, return something that makes sense. If nothing makes sense, return nil. @param size [String]: Instance type to check @param region [String]: Region to check against @return [String,nil]

# File modules/mu/providers/cloudformation/server.rb, line 364
def self.validateInstanceType(size, region)
  MU::Cloud.resourceClass("AWS", "Server").validateInstanceType(size, region)
end

Public Instance Methods

active?() click to toggle source

Placeholder. This is a NOOP for CloudFormation, which doesn't build resources directly.

# File modules/mu/providers/cloudformation/server.rb, line 244
def active?
end
canonicalIP() click to toggle source

Placeholder. This is a NOOP for CloudFormation, which doesn't build resources directly.

# File modules/mu/providers/cloudformation/server.rb, line 234
def canonicalIP
end
create() click to toggle source

Populate @cfm_template with a resource description for this server in CloudFormation language.

# File modules/mu/providers/cloudformation/server.rb, line 84
        def create
          @cfm_name, @cfm_template = MU::Cloud::CloudFormation.cloudFormationBase(self.class.cfg_name, self, tags: @config['tags'], scrub_mu_isms: @config['scrub_mu_isms']) if @cfm_template.nil?
          @role_cfm_name = @prof_cfm_name = nil
          MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "SourceDestCheck", @config['src_dst_check'])
          MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "InstanceType", @config['size'])
          MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "ImageId", @config['ami_id'])
          MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "KeyName", { "Ref" => "SSHKeyName" })
          if @config['generate_iam_role'] and !@role_generated
            @config['iam_role'], @cfm_role_name, @cfm_prof_name = MU::Cloud::CloudFormation::Server.createIAMProfile(@mu_name, base_profile: @config['iam_role'], extra_policies: @config['iam_policies'], cloudformation_data: @cfm_template)
            @role_generated = true
          end
          if !@config["iam_role"].nil?
            MU::Cloud::CloudFormation::Server.addStdPoliciesToIAMProfile(@cfm_role_name, cloudformation_data: @cfm_template, region: @config['region']) if !@config['scrub_mu_isms']
            MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "DependsOn", @cfm_role_name)
            MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "DependsOn", @cfm_prof_name)
            MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "IamInstanceProfile", { "Ref" => @cfm_prof_name })
          end
          if @config['add_firewall_rules']
            @config['add_firewall_rules'].each { |acl|
              if acl["rule_id"]
                MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "SecurityGroupIds", acl["rule_id"])
              else
                MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "SecurityGroupIds", { "Ref" => @dependencies["firewall_rule"][acl["rule_name"]].cloudobj.cfm_name })
              end
            }
          end

          if !@config['private_ip'].nil?
            MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "PrivateIpAddress", config['private_ip'])
          end

          if !@vpc.nil? and @config.has_key?("vpc")
            if !@config["vpc"]["vpc_name"].nil? and @dependencies.has_key?("vpc") and @dependencies["vpc"].has_key?(@config["vpc"]["vpc_name"])
              MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "DependsOn", @dependencies["vpc"][@config["vpc"]["vpc_name"]].cloudobj.cfm_name)
            end

            if !@config['vpc']['subnet_id'].nil?
              MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "SubnetId", @config['vpc']['subnet_id'])
            elsif @dependencies.has_key?("vpc") and @dependencies["vpc"].has_key?(@config["vpc"]["vpc_name"])
              @dependencies["vpc"][@config["vpc"]["vpc_name"]].subnets.each { |subnet_obj|
                if subnet_obj.name == @config["vpc"]["subnet_name"]
                  MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "DependsOn", subnet_obj.cfm_name)
                  MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "SubnetId", { "Ref" => subnet_obj.cfm_name } )
                end
              }
            end
          end

          if !@config['static_ip'].nil?
            eip_name = eip_template = nil

            eipassoc_name, eipassoc_template = MU::Cloud::CloudFormation.cloudFormationBase("eipassoc", name: @config['name']+"EIP", scrub_mu_isms: @config['scrub_mu_isms'])

            if @config['static_ip']['ip'].nil?
              eip_name, eip_template = MU::Cloud::CloudFormation.cloudFormationBase("eip", name: @config['name']+"EIP", scrub_mu_isms: @config['scrub_mu_isms'])
              MU::Cloud::CloudFormation.setCloudFormationProp(eipassoc_template[eipassoc_name], "DependsOn", eip_name)
              if !@config['vpc'].nil?
                MU::Cloud::CloudFormation.setCloudFormationProp(eip_template[eip_name], "Domain", "vpc")
                MU::Cloud::CloudFormation.setCloudFormationProp(eipassoc_template[eipassoc_name], "AllocationId", { "Fn::GetAtt" => [eip_name, "AllocationId"] })
                if !@vpc.nil? and @config.has_key?("vpc")
                  if !@config["vpc"]["vpc_name"].nil? and @dependencies.has_key?("vpc") and @dependencies["vpc"].has_key?(@config["vpc"]["vpc_name"])
                    igw_name, igw_template = MU::Cloud::CloudFormation.cloudFormationBase("vpcgwattach", name: @dependencies["vpc"][@config["vpc"]["vpc_name"]].cloudobj.mu_name, scrub_mu_isms: @config['scrub_mu_isms'])
                    MU::Cloud::CloudFormation.setCloudFormationProp(eip_template[eip_name], "DependsOn", igw_name)
                  end
                end
#                @cfm_template[@cfm_name]["DependsOn"].dup.each { |parent_dep|
#                  MU::Cloud::CloudFormation.setCloudFormationProp(eip_template[eip_name], "DependsOn", parent_dep)
#
#                }
              else
                MU::Cloud::CloudFormation.setCloudFormationProp(eipassoc_template[eipassoc_name], "EIP", @config['static_ip']['ip'])
              end
            else
              MU.log "Cannot currently target a pre-existing EIP by name when targeting CloudFormation", MU::WARN
            end
            MU::Cloud::CloudFormation.setCloudFormationProp(eipassoc_template[eipassoc_name], "InstanceId", { "Ref" => @cfm_name })

#            MU::Cloud::CloudFormation.setCloudFormationProp(eip_template[eip_name], "EIP", @config['static_ip']['ip'])
            @cfm_template.merge!(eip_template) if !eip_template.nil?
            @cfm_template.merge!(eipassoc_template)
          end

          if !@userdata.nil? and !@userdata.empty?
            MU::Cloud::CloudFormation.setCloudFormationProp(
              @cfm_template[@cfm_name],
              "UserData",
              {
                "Fn::Base64" => {
                  "Fn::Join" => [
                    "",
                    [
                      "#!/bin/bash\n",
                      "echo '",
                      {
                        "Ref" => "AWS::StackName"
                      },
                      "' > /etc/aws_cloudformation_stack\n\n",
                      @userdata
                    ]
                  ]
                }
              }
            )
          end

          configured_storage = Array.new
          cfm_volume_map = {}
          if @config["storage"]
            @config["storage"].each { |vol|
              mapping, cfm_mapping = MU::Cloud.resourceClass("AWS", "Server").convertBlockDeviceMapping(vol)
              configured_storage << mapping
#                vol_name, vol_template = MU::Cloud::CloudFormation.cloudFormationBase("volume", name: "volume"+@cfm_name+mapping[:device_name])
#                MU::Cloud::CloudFormation.setCloudFormationProp(vol_template[vol_name], "Size", mapping[:ebs][:volume_size].to_s)
#                MU::Cloud::CloudFormation.setCloudFormationProp(vol_template[vol_name], "VolumeType", mapping[:ebs][:volume_type])
#                @cfm_template.merge!(vol_template)
#                cfm_volume_map[mapping[:device_name]] = { "Ref" => vol_name }
              if cfm_mapping.size > 0
                MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "BlockDeviceMappings", cfm_mapping)
              end
            }
          end

          cfm_volume_map.each_pair{ |dev, vol|
#            MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "Volumes", { "Device" => dev, "VolumeId" => vol })
          }
          @ephemeral_mappings.each { |mapping|
            MU::Cloud::CloudFormation.setCloudFormationProp(@cfm_template[@cfm_name], "BlockDeviceMappings", { "DeviceName" => mapping[:device_name], "VirtualName" => mapping[:virtual_name] })
          }

          return @config
        end
getSSHConfig() click to toggle source

Placeholder. This is a NOOP for CloudFormation, which doesn't build resources directly.

# File modules/mu/providers/cloudformation/server.rb, line 229
def getSSHConfig
end
getWindowsAdminPassword() click to toggle source

Placeholder. This is a NOOP for CloudFormation, which doesn't build resources directly.

# File modules/mu/providers/cloudformation/server.rb, line 239
def getWindowsAdminPassword
end
groom() click to toggle source

Placeholder. This is a NOOP for CloudFormation, which doesn't build resources directly.

# File modules/mu/providers/cloudformation/server.rb, line 218
def groom
  return create
end
notify() click to toggle source

Placeholder. This is a NOOP for CloudFormation, which doesn't build resources directly.

# File modules/mu/providers/cloudformation/server.rb, line 254
def notify
  {}
end
postBoot() click to toggle source

Placeholder. This is a NOOP for CloudFormation, which doesn't build resources directly.

# File modules/mu/providers/cloudformation/server.rb, line 224
def postBoot
end
reboot() click to toggle source

Placeholder. This is a NOOP for CloudFormation, which doesn't build resources directly.

# File modules/mu/providers/cloudformation/server.rb, line 249
def reboot
end