lib/plugins/aws/package/compile/functions/index.js/AwsCompileFunctions/compileFunction
class AwsCompileFunctions:  compileFunction(functionName) {
    const newFunction = this.cfLambdaFunctionTemplate();
    const functionObject = this.serverless.service.getFunction(functionName);
    functionObject.package = functionObject.package || {};

    const serviceArtifactFileName = this.provider.naming.getServiceArtifactName();
    const functionArtifactFileName = this.provider.naming.getFunctionArtifactName(functionName);

    let artifactFilePath = functionObject.package.artifact ||
      this.serverless.service.package.artifact;
    if (!artifactFilePath ||
      (this.serverless.service.artifact && !functionObject.package.artifact)) {
      let artifactFileName = serviceArtifactFileName;
      if (this.serverless.service.package.individually || functionObject.package.individually) {
        artifactFileName = functionArtifactFileName;
      }

      artifactFilePath = path.join(this.serverless.config.servicePath
        , '.serverless', artifactFileName);
    }

    if (this.serverless.service.package.deploymentBucket) {
      newFunction.Properties.Code.S3Bucket = this.serverless.service.package.deploymentBucket;
    }

    const s3Folder = this.serverless.service.package.artifactDirectoryName;
    const s3FileName = artifactFilePath.split(path.sep).pop();
    newFunction.Properties.Code.S3Key = `${s3Folder}/${s3FileName}`;

    if (!functionObject.handler) {
      const errorMessage = [
        `Missing "handler" property in function "${functionName}".`,
        ' Please make sure you point to the correct lambda handler.',
        ' For example: handler.hello.',
        ' Please check the docs for more info',
      ].join('');
      throw new this.serverless.classes
        .Error(errorMessage);
    }

    const Handler = functionObject.handler;
    const FunctionName = functionObject.name;
    const MemorySize = Number(functionObject.memorySize)
      || Number(this.serverless.service.provider.memorySize)
      || 1024;
    const Timeout = Number(functionObject.timeout)
      || Number(this.serverless.service.provider.timeout)
      || 6;
    const Runtime = functionObject.runtime
      || this.serverless.service.provider.runtime
      || 'nodejs4.3';

    newFunction.Properties.Handler = Handler;
    newFunction.Properties.FunctionName = FunctionName;
    newFunction.Properties.MemorySize = MemorySize;
    newFunction.Properties.Timeout = Timeout;
    newFunction.Properties.Runtime = Runtime;

    if (functionObject.description) {
      newFunction.Properties.Description = functionObject.description;
    }

    if (functionObject.tags && typeof functionObject.tags === 'object') {
      newFunction.Properties.Tags = [];
      _.forEach(functionObject.tags, (Value, Key) => {
        newFunction.Properties.Tags.push({ Key, Value });
      });
    }

    if (functionObject.onError) {
      const arn = functionObject.onError;

      if (typeof arn === 'string') {
        const splittedArn = arn.split(':');
        if (splittedArn[0] === 'arn' && (splittedArn[2] === 'sns' || splittedArn[2] === 'sqs')) {
          const dlqType = splittedArn[2];
          const iamRoleLambdaExecution = this.serverless.service.provider
            .compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution;
          let stmt;

          newFunction.Properties.DeadLetterConfig = {
            TargetArn: arn,
          };

          if (dlqType === 'sns') {
            stmt = {
              Effect: 'Allow',
              Action: [
                'sns:Publish',
              ],
              Resource: [arn],
            };
          } else if (dlqType === 'sqs') {
            const errorMessage = [
              'onError currently only supports SNS topic arns due to a',
              ' race condition when using SQS queue arns and updating the IAM role.',
              ' Please check the docs for more info.',
            ].join('');
            throw new this.serverless.classes.Error(errorMessage);
          }

          // update the PolicyDocument statements (if default policy is used)
          if (iamRoleLambdaExecution) {
            iamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement.push(stmt);
          }
        } else {
          const errorMessage = 'onError config must be a SNS topic arn or SQS queue arn';
          throw new this.serverless.classes.Error(errorMessage);
        }
      } else {
        const errorMessage = 'onError config must be provided as a string';
        throw new this.serverless.classes.Error(errorMessage);
      }
    }

    let kmsKeyArn;
    const serviceObj = this.serverless.service.serviceObject;
    if ('awsKmsKeyArn' in functionObject) {
      kmsKeyArn = functionObject.awsKmsKeyArn;
    } else if (serviceObj && 'awsKmsKeyArn' in serviceObj) {
      kmsKeyArn = serviceObj.awsKmsKeyArn;
    }

    if (kmsKeyArn) {
      const arn = kmsKeyArn;

      if (typeof arn === 'string') {
        const splittedArn = arn.split(':');
        if (splittedArn[0] === 'arn' && (splittedArn[2] === 'kms')) {
          const iamRoleLambdaExecution = this.serverless.service.provider
            .compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution;

          newFunction.Properties.KmsKeyArn = arn;

          const stmt = {
            Effect: 'Allow',
            Action: [
              'kms:Decrypt',
            ],
            Resource: [arn],
          };

          // update the PolicyDocument statements (if default policy is used)
          if (iamRoleLambdaExecution) {
            iamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement = _.unionWith(
              iamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement,
              [stmt],
              _.isEqual
            );
          }
        } else {
          const errorMessage = 'awsKmsKeyArn config must be a KMS key arn';
          throw new this.serverless.classes.Error(errorMessage);
        }
      } else {
        const errorMessage = 'awsKmsKeyArn config must be provided as a string';
        throw new this.serverless.classes.Error(errorMessage);
      }
    }

    if (functionObject.environment || this.serverless.service.provider.environment) {
      newFunction.Properties.Environment = {};
      newFunction.Properties.Environment.Variables = Object.assign(
        {},
        this.serverless.service.provider.environment,
        functionObject.environment
      );

      Object.keys(newFunction.Properties.Environment.Variables).forEach((key) => {
        // taken from the bash man pages
        if (!key.match(/^[A-Za-z_][a-zA-Z0-9_]*$/)) {
          const errorMessage = 'Invalid characters in environment variable';
          throw new this.serverless.classes.Error(errorMessage);
        }
      });
    }

    if ('role' in functionObject) {
      this.compileRole(newFunction, functionObject.role);
    } else if ('role' in this.serverless.service.provider) {
      this.compileRole(newFunction, this.serverless.service.provider.role);
    } else {
      this.compileRole(newFunction, 'IamRoleLambdaExecution');
    }

    if (!functionObject.vpc) functionObject.vpc = {};
    if (!this.serverless.service.provider.vpc) this.serverless.service.provider.vpc = {};

    newFunction.Properties.VpcConfig = {
      SecurityGroupIds: functionObject.vpc.securityGroupIds ||
      this.serverless.service.provider.vpc.securityGroupIds,
      SubnetIds: functionObject.vpc.subnetIds || this.serverless.service.provider.vpc.subnetIds,
    };

    if (!newFunction.Properties.VpcConfig.SecurityGroupIds
      || !newFunction.Properties.VpcConfig.SubnetIds) {
      delete newFunction.Properties.VpcConfig;
    }

    newFunction.DependsOn = [this.provider.naming.getLogGroupLogicalId(functionName)]
      .concat(newFunction.DependsOn || []);

    const functionLogicalId = this.provider.naming
      .getLambdaLogicalId(functionName);
    const newFunctionObject = {
      [functionLogicalId]: newFunction,
    };

    _.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources,
      newFunctionObject);

    const newVersion = this.cfLambdaVersionTemplate();

    const content = fs.readFileSync(artifactFilePath);
    const hash = crypto.createHash('sha256');
    hash.setEncoding('base64');
    hash.write(content);
    hash.end();

    newVersion.Properties.CodeSha256 = hash.read();
    newVersion.Properties.FunctionName = { Ref: functionLogicalId };
    if (functionObject.description) {
      newVersion.Properties.Description = functionObject.description;
    }

    // use the SHA in the logical resource ID of the version because
    // AWS::Lambda::Version resource will not support updates
    const versionLogicalId = this.provider.naming.getLambdaVersionLogicalId(
            functionName, newVersion.Properties.CodeSha256);
    const newVersionObject = {
      [versionLogicalId]: newVersion,
    };

    if (this.serverless.service.provider.versionFunctions) {
      _.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources,
        newVersionObject);
    }

    // Add function versions to Outputs section
    const functionVersionOutputLogicalId = this.provider.naming
      .getLambdaVersionOutputLogicalId(functionName);
    const newVersionOutput = this.cfOutputLatestVersionTemplate();

    newVersionOutput.Value = { Ref: versionLogicalId };

    if (this.serverless.service.provider.versionFunctions) {
      _.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Outputs, {
        [functionVersionOutputLogicalId]: newVersionOutput,
      });
    }
  }