在Gitlab中使用Hook或其他东西来执行git aws.push到AWS Elastic Beanstalk

时间:2015-04-06 20:50:22

标签: elastic-beanstalk gitlab githooks git-push

我们正在使用Gitlab.com作为我们的GIT中央回购。以同样的方式,将应用程序部署到AWS Elastic Beanstalk,使用 git aws.push 进行推送,它在后台执行正常的 git push 到Amazon。我想要一种方法来推动gitlab和 Gitlab 推动Elastic Beanstalk。我知道git别名,使用它我能够使用一个命令执行git push和git aws.push,但是,我想要的只是gitlab和从gitlab到AWS。

我在Gitlab Web Hooks思考,也许有些人已经将它翻译成PHP或某种服务器语言来实现Web Hook?或者从Gitlab部署到AWS Elastic Beanstalk的任何其他解决方案。

git aws.push使用Power Shell中的代码对Elastic Beanstalk进行git推送:

$awsSource = @"
using System;
using System.Globalization;
using System.Text;
using System.Security.Cryptography;

namespace Amazon.DevTools
{
    public class AWSUser
    {
        public string AccessKey
        {
            get;
            set;
        }

        public string SecretKey
        {
            get;
            set;
        }

        protected internal void Validate()
        {
            if (string.IsNullOrEmpty(this.AccessKey))
            {
                throw new InvalidOperationException("[AccessKey]");
            }
            if (string.IsNullOrEmpty(this.SecretKey))
            {
                throw new InvalidOperationException("[SecretKey]");
            }
        }
    }
}

namespace Amazon.DevTools
{
    public abstract class AWSDevToolsRequest
    {
        protected const string METHOD = "GIT";
        protected const string SERVICE = "devtools";

        DateTime dateTime;

        public AWSDevToolsRequest()
            : this(DateTime.UtcNow)
        {
        }

        public AWSDevToolsRequest(DateTime dateTime)
        {
            if (dateTime == null)
            {
                throw new ArgumentNullException("dateTime");
            }
            this.dateTime = dateTime.ToUniversalTime();
        }

        public string DateStamp
        {
            get
            {
                return this.dateTime.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
            }
        }

        public string DateTimeStamp
        {
            get
            {
                return this.dateTime.ToString("yyyyMMddTHHmmss", CultureInfo.InvariantCulture);
            }
        }

        public abstract string DerivePath();

        protected internal abstract string DeriveRequest();

        public string Host
        {
            get;
            set;
        }

        public string Region
        {
            get;
            set;
        }

        public string Service
        {
            get
            {
                return AWSDevToolsRequest.SERVICE;
            }
        }

        protected internal virtual void Validate()
        {
            if (string.IsNullOrEmpty(this.Host))
            {
                throw new InvalidOperationException("[Host]");
            }
            if (string.IsNullOrEmpty(this.Region))
            {
                throw new InvalidOperationException("[Region]");
            }
        }
    }
}

namespace Amazon.DevTools
{
    public class AWSElasticBeanstalkRequest : AWSDevToolsRequest
    {
        public AWSElasticBeanstalkRequest()
            : base()
        {
        }

        public AWSElasticBeanstalkRequest(DateTime dateTime)
            : base(dateTime)
        {
        }

        public string Application
        {
            get;
            set;
        }

        public override string DerivePath()
        {
            this.Validate();

            string path = null;

            if (string.IsNullOrEmpty(this.Environment))
            {
                path = string.Format("/v1/repos/{0}/commitid/{1}"
        , this.Encode(this.Application)
        , this.Encode(this.CommitId));
            }
            else
            {
                path = string.Format("/v1/repos/{0}/commitid/{1}/environment/{2}"
        , this.Encode(this.Application)
        , this.Encode(this.CommitId)
        , this.Encode(this.Environment));
            }
            return path;
        }

        protected internal override string DeriveRequest()
        {
            this.Validate();

            string path = this.DerivePath();
            string request = string.Format("{0}\n{1}\n\nhost:{2}\n\nhost\n", AWSDevToolsRequest.METHOD, path, this.Host);
            return request;
        }

        public string Environment
        {
            get;
            set;
        }

    public string CommitId
    {
       get;
       set;
    }

        protected internal override void Validate()
        {
            base.Validate();
            if (string.IsNullOrEmpty(this.Application))
            {
                throw new InvalidOperationException("[Application]");
            }
            if (string.IsNullOrEmpty(this.Host))
            {
                throw new InvalidOperationException("[Host]");
            }
        }

    protected internal string Encode(string plaintext)
    {
        StringBuilder sb = new StringBuilder();
        foreach (byte b in new UTF8Encoding().GetBytes(plaintext))
        {
        sb.Append(b.ToString("x2", CultureInfo.InvariantCulture));
        }
        return sb.ToString();
    }

    }
}

namespace Amazon.DevTools
{
    public class AWSDevToolsAuth
    {
        const string AWS_ALGORITHM = "HMAC-SHA256";
        const string HASH_ALGORITHM = "SHA-256";
        const string HMAC_ALGORITHM = "HMACSHA256";
        const string SCHEME = "AWS4";
        const string TERMINATOR = "aws4_request";

        AWSUser user;
        AWSDevToolsRequest request;

        public AWSDevToolsAuth(AWSUser user, AWSDevToolsRequest request)
        {
            this.user = user;
            this.request = request;
        }

        static byte[] DeriveKey(AWSUser user, AWSDevToolsRequest request)
        {
            string secret = string.Format("{0}{1}", AWSDevToolsAuth.SCHEME, user.SecretKey);
            byte[] kSecret = Encoding.UTF8.GetBytes(secret);
            byte[] kDate = AWSDevToolsAuth.Hash(AWSDevToolsAuth.HMAC_ALGORITHM, kSecret, Encoding.UTF8.GetBytes(request.DateStamp));
            byte[] kRegion = AWSDevToolsAuth.Hash(AWSDevToolsAuth.HMAC_ALGORITHM, kDate, Encoding.UTF8.GetBytes(request.Region));
            byte[] kService = AWSDevToolsAuth.Hash(AWSDevToolsAuth.HMAC_ALGORITHM, kRegion, Encoding.UTF8.GetBytes(request.Service));
            byte[] key = AWSDevToolsAuth.Hash(AWSDevToolsAuth.HMAC_ALGORITHM, kService, Encoding.UTF8.GetBytes(AWSDevToolsAuth.TERMINATOR));
            return key;
        }

        public string DerivePassword()
        {
            this.user.Validate();
            this.request.Validate();

            string signature = AWSDevToolsAuth.SignRequest(this.user, this.request);
            string password = string.Format("{0}Z{1}", this.request.DateTimeStamp, signature);
            return password;
        }

        public Uri DeriveRemote()
        {
            this.request.Validate();

            string path = this.request.DerivePath();
            string password = this.DerivePassword();
            string username = this.DeriveUserName();
            UriBuilder remote = new UriBuilder()
            {
                Host = this.request.Host,
                Path = path,
                Password = password,
                Scheme = "https",
                UserName = username,
            };
            return remote.Uri;
        }

        public string DeriveUserName()
        {
            this.user.Validate();

            return this.user.AccessKey;
        }

        static byte[] Hash(string algorithm, byte[] message)
        {
            HashAlgorithm hash = HashAlgorithm.Create(algorithm);
            byte[] digest = hash.ComputeHash(message);
            return digest;
        }

        static byte[] Hash(string algorithm, byte[] key, byte[] message)
        {
            KeyedHashAlgorithm hash = KeyedHashAlgorithm.Create(algorithm);
            hash.Key = key;
            byte[] digest = hash.ComputeHash(message);
            return digest;
        }

        static string SignRequest(AWSUser user, AWSDevToolsRequest request)
        {
            string scope = string.Format("{0}/{1}/{2}/{3}", request.DateStamp, request.Region, request.Service, AWSDevToolsAuth.TERMINATOR);
            StringBuilder stringToSign = new StringBuilder();
            stringToSign.AppendFormat("{0}-{1}\n{2}\n{3}\n", AWSDevToolsAuth.SCHEME, AWSDevToolsAuth.AWS_ALGORITHM, request.DateTimeStamp, scope);
            byte[] requestBytes = Encoding.UTF8.GetBytes(request.DeriveRequest());
            byte[] requestDigest = AWSDevToolsAuth.Hash(AWSDevToolsAuth.HASH_ALGORITHM, requestBytes);
            stringToSign.Append(AWSDevToolsAuth.ToHex(requestDigest));
            byte[] key = AWSDevToolsAuth.DeriveKey(user, request);
            byte[] digest = AWSDevToolsAuth.Hash(AWSDevToolsAuth.HMAC_ALGORITHM, key, Encoding.UTF8.GetBytes(stringToSign.ToString()));
            string signature = AWSDevToolsAuth.ToHex(digest);
            return signature;
        }

        static string ToHex(byte[] data)
        {
            StringBuilder hex = new StringBuilder();
            foreach (byte b in data)
            {
                hex.Append(b.ToString("x2", CultureInfo.InvariantCulture));
            }
            return hex.ToString();
        }
    }
}

"@

Add-Type -Language CSharpVersion3 -TypeDefinition $awsSource

# -*-powershell-*-

#
# Sets the AWS.push configuration values
#
# Will read values from the pipeline input if none are present the values are read from the console input instead.
#
function Edit-AWSElasticBeanstalkRemote
{
    $data=@($input)
    $used=0

    $config = Read-Config $False $True
    $awsAccessKey = Lookup-Setting $config "global" "AWSAccessKeyId" ("cred","git")
    if (!$awsAccessKey -and (ShouldWrite-Credentials $config $false))
    {
        $awsAccessKeyInput = ($data[$used++] | Input-Data "AWS Access Key")
    }
    if ($awsAccessKeyInput)
    {
        $config = Write-Setting $config "cred" "global" "AWSAccessKeyId" $awsAccessKeyInput
    }

    $awsSecretKey = Lookup-Setting $config "global" "AWSSecretKey" ("cred","git")
    if (!$awsSecretKey -and (ShouldWrite-Credentials $config $false))
    {
        $awsSecretKeyInput = ($data[$used++] | Input-Data "AWS Secret Key")
    }
    if ($awsSecretKeyInput)
    {
        $config = Write-Setting $config "cred" "global" "AWSSecretKey" $awsSecretKeyInput
    }

    $awsRegion = Lookup-Setting $config "global" "Region" ("eb","git")
    if (-not $awsRegion)
    {
        $awsRegion = "us-east-1"
        $config = Write-Setting $config "eb" "global" "Region" $awsRegion
    }
    $awsRegionInput = ($data[$used++] | Input-Data "AWS Region [default to $($awsRegion)]")
    if ($awsRegionInput)
    {
    $awsRegion = $awsRegionInput
        $config = Write-Setting $config "eb" "global" "Region" $awsRegionInput
    }

    $awsHost = Get-Endpoint $awsRegion
    if ($awsHost)
    {
        $config = Write-Setting $config "eb" "global" "DevToolsEndpoint" $awsHost
    }
    else
    {
        $awsHostInput = ($data[$used++] | Input-Data "AWS Host [default to git.elasticbeanstalk.us-east-1.amazonaws.com]")

        if ($awsHostInput)
        {
            $config = Write-Setting $config "eb" "global" "DevToolsEndpoint" $awsHostInput
        }
        else
        {
            $config = Write-Setting $config "eb" "global" "DevToolsEndpoint" "git.elasticbeanstalk.us-east-1.amazonaws.com"
        }
    }

    $awsApplication = Lookup-Setting $config "global" "ApplicationName" ("eb","git")
    if ($awsApplication)
    {
        $awsApplicationInput = ($data[$used++] | Input-Data "AWS Elastic Beanstalk Application [default to $($awsApplication)]")
    }
    else
    {
        $awsApplicationInput = ($data[$used++] | Input-Data "AWS Elastic Beanstalk Application")
    }
    if ($awsApplicationInput)
    {
        $config = Write-Setting $config "eb" "global" "ApplicationName" $awsApplicationInput
    }

    $awsEnvironment = Lookup-Setting $config "global" "EnvironmentName" ("eb","git")
    if ($awsEnvironment)
    {
        $awsEnvironmentInput = ($data[$used++] | Input-Data "AWS Elastic Beanstalk Environment [default to $($awsEnvironment)]")
    }
    else
    {
        $awsEnvironmentInput = ($data[$used++] | Input-Data "AWS Elastic Beanstalk Environment")
    }
    if ($awsEnvironmentInput)
    {
        $config = Write-Setting $config "eb" "global" "EnvironmentName" $awsEnvironmentInput
    }

    Write-Config $config
}

#
# Looks up the endpoint for a region
#
# @params $region
# @return The endpoint
#
function Get-Endpoint
{
    Param($region)
    switch ($region)
    {
        "us-east-1" { $endpoint = "git.elasticbeanstalk.us-east-1.amazonaws.com" }
        "ap-northeast-1" { $endpoint = "git.elasticbeanstalk.ap-northeast-1.amazonaws.com" }
        "ap-southeast-1" { $endpoint = "git.elasticbeanstalk.ap-southeast-1.amazonaws.com" }
        "ap-southeast-2" { $endpoint = "git.elasticbeanstalk.ap-southeast-2.amazonaws.com" }
        "eu-west-1" { $endpoint = "git.elasticbeanstalk.eu-west-1.amazonaws.com" }
        "sa-east-1" { $endpoint = "git.elasticbeanstalk.sa-east-1.amazonaws.com" }
        "us-west-1" { $endpoint = "git.elasticbeanstalk.us-west-1.amazonaws.com" }
        "us-west-2" { $endpoint = "git.elasticbeanstalk.us-west-2.amazonaws.com" }
    }
    $endpoint
}
#
# Gets the remote URL used for AWS.push
#
# @param $e environment that is deployed too. 
# @param $c commit to deploy
# @return The URL
#
function Get-AWSElasticBeanstalkRemote
{
    Param([string] $e,
          [string] $c,
          [bool] $toPush = $FALSE )
    trap [System.Management.Automation.MethodInvocationException]
    {
        if ($_.Exception -and $_.Exception.InnerException)
        {
            $awsOption = $_.Exception.InnerException.Message
            switch ($awsOption)
            {
                "[AccessKey]" { $awsOption = "aws.accesskey" }
                "[Application]" { $awsOption = "aws.elasticbeanstalk.application" }
                "[Host]" { $awsOption = "aws.elasticbeanstalk.host" }
                "[Region]" { $awsOption = "aws.region" }
                "[SecretKey]" { $awsOption = "aws.secretkey" }
            }
            Write-Host "Missing configuration setting for: $($awsOption)"
        }
        else
        {
            Write-Host "An unknown error occurred while computing your temporary password."
        }
        Write-Host "`nTry running 'git aws.config' to update your repository configuration."
        Exit
    }

    $config = Read-Config
    $awsAccessKey = Lookup-Setting $config "global" "AWSAccessKeyId" ("cred","git")
    $awsSecretKey = Lookup-Setting $config "global" "AWSSecretKey" ("cred","git")
    $awsRegion = Lookup-Setting $config "global" "Region" ("eb","git")
    $awsHost = Lookup-Setting $config "global" "DevToolsEndpoint" ("eb","git")
    $awsApplication = Lookup-Setting $config "global" "ApplicationName" ("eb","git")

    if ($e)
    {  
      $awsEnvironment = $e
    }
    else
    {
      $branchName = &git rev-parse --abbrev-ref HEAD
      $defaultEnv = Lookup-Setting $config "branches" $branchName ("eb")
      if ($defaultEnv)
      {
        $awsEnvironment = $defaultEnv 
      }
      else
      {
        $awsEnvironment = Lookup-Setting $config "global" "EnvironmentName" ("eb","git")  
      }
    }

    $gitCommitId = $c

    $awsUser = New-Object -TypeName Amazon.DevTools.AWSUser
    $awsUser.AccessKey = $awsAccessKey
    $awsUser.SecretKey = $awsSecretKey

    $awsRequest = New-Object -TypeName Amazon.DevTools.AWSElasticBeanstalkRequest
    $awsRequest.Region = $awsRegion
    $awsRequest.Host = $awsHost
    $awsRequest.Application = $awsApplication
    $awsRequest.Environment = $awsEnvironment
    $awsRequest.CommitId = $gitCommitId

    $awsAuth = New-Object -TypeName Amazon.DevTools.AWSDevToolsAuth $awsUser,$awsRequest
    $awsRemote = $awsAuth.DeriveRemote()
    if($toPush) {
       Write-Host "Pushing to environment: $awsEnvironment"  
    }  
    return $awsRemote.ToString()
}

#
# Performs the aws.push
#
# @param $e environment that is deployed too. 
# @param $c commit to deploy
#
function Invoke-AWSElasticBeanstalkPush
{
    Param([string] $e, [string] $c)
    $remote = Get-AWSElasticBeanstalkRemote $e $c $TRUE
    $src = $c
    $dst = "refs/heads/master"
    $commit = $src + ":" + $dst
    &git push -f $remote $commit
}

#
# Adds the git aliases for aws.push and aws.config to the git repository.
#
function Initialize-AWSElasticBeanstalkRepository
{
    $command = 'Import-Module AWSDevTools; $e, $c = Get-Options $args; Get-AWSElasticBeanstalkRemote $e $c'
    &git config alias.aws.elasticbeanstalk.remote "!powershell -noprofile -executionpolicy bypass -command '& { $command }'"

    $command = 'Import-Module AWSDevTools; $e, $c = Get-Options $args; Invoke-AWSElasticBeanstalkPush $e $c'
    &git config alias.aws.push "!powershell -noprofile -executionpolicy bypass -command '& { $command }'"

    $command = 'Import-Module AWSDevTools; Edit-AWSElasticBeanstalkRemote'
    &git config alias.aws.config "!powershell -noprofile -executionpolicy bypass -command '& { $command }'"
}

#
# Read in data
#
# Will used pipeline data if present, otherwise reads from the console
#
# @param $message The text to display as a prompt
# @return The data collected
#
function Input-Data
{
    Param([string] $message)
    Write-Host -NoNewline "$($message): "
    if (($input.MoveNext()) -and ($input.Current))
    {
        Write-Host $input.Current
        $input.Current
    }
    else
    {
        [Console]::In.ReadLine() 
    }
}

#
# Gets the values for the aws.push and aws.config command options
#
# @param $arr The command line options passed to the command
# @return The options values
#
function Get-Options
{
    Param([string[]] $arr)

    $e = $null;
    $c = $null;

    $optionmappings = @{
        '--environment' = 'environment';
        '-e' = 'environment';
        '--commit' = 'commit';
        '-c' = 'commit';
        '--help' = 'help';
        '-h' = 'help'
    }

    $options = @{}

    for ($i=0; $i -lt $arr.count; $i++)
    {
        $optname = $arr[$i]
        $mappedoption = $optionmappings[$optname]
        if (!$mappedoption) {
            Write-Host("Unknown Option: {0}" -f $arr[$i])
            Write-Help
            Exit
        }
        if ($mappedoption -eq "help") {
            Write-Help
            Exit
        }

        $value = $arr[++$i]
        if (($value -eq $null) -or $optionmappings[$value]) {
            Write-Host("You must provide a value for {0}" -f $optname)
            Write-Help
            Exit
        }
        if ($options[$mappedoption]) {
            Write-Host("--{0} specified twice" -f $mappedoption)
            Exit
        }
        $options[$mappedoption] = $value
    }

    $e = $options["environment"]
    $c = $options["commit"]

    if ($c -eq $null) {
        $c = "HEAD"
    }
    $c = Parse-CommitOption $c
    $result = $e, $c
    $result
}

1 个答案:

答案 0 :(得分:2)

首先在gitlab中设置AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYhttps://gitlab.com/snw/<project_name>/settings/ci_cd

然后将.gitlab-ci.yml文件添加到您的repo的根目录:

variables:
  ARCHIVE_NAME: "archive.zip"
  ARCHIVE_LOCATION: "deployments/"
  S3_BUCKET_NAME: "my-deployments"
  ELASTIC_BEANSTALK_APP_NAME: "myAppName"
  AWS_REGION: "us-east-1"

elastic_beanstalk_deploy:
  image: python:latest
  script:
  - apt-get update --assume-yes
  - apt-get install zip --assume-yes
  - zip -r $ARCHIVE_LOCATION$ARCHIVE_NAME .
  - pip install awscli
  - aws s3 cp $ARCHIVE_LOCATION$ARCHIVE_NAME s3://$S3_BUCKET_NAME/
  - aws elasticbeanstalk create-application-version --application-name $ELASTIC_BEANSTALK_APP_NAME --version-label `date "+%Y%m%d-%H%M%S"` --description "$(git log -1 --pretty=%B)" --source-bundle S3Bucket="$S3_BUCKET_NAME",S3Key="$ARCHIVE_NAME" --region $AWS_REGION

道具: