1+ import { Construct } from "constructs" ;
2+ import { BuildSpec , LinuxBuildImage , PipelineProject } from "aws-cdk-lib/aws-codebuild" ;
3+ import { IBucket } from "aws-cdk-lib/aws-s3" ;
4+ import { Artifact , Pipeline } from "aws-cdk-lib/aws-codepipeline" ;
5+ import {
6+ CodeBuildAction ,
7+ LambdaInvokeAction ,
8+ ManualApprovalAction ,
9+ S3DeployAction ,
10+ S3SourceAction
11+ } from "aws-cdk-lib/aws-codepipeline-actions" ;
12+ import { Code , Function , Runtime } from "aws-cdk-lib/aws-lambda" ;
13+ import { Aws , Duration } from "aws-cdk-lib" ;
14+ import { PolicyStatement } from "aws-cdk-lib/aws-iam" ;
15+ import { SOURCE_CODE_ZIP } from "../shared-vars" ;
16+
17+
18+ interface PythonBuildPipelineProps {
19+ appName : string
20+ repositoryName : string
21+ deployBucket : IBucket
22+ projectRoot ?: string
23+ deployBucketBasePath ?: string
24+ }
25+
26+ export class PythonBuildPipeline extends Construct {
27+ readonly pipeline : Pipeline
28+
29+ constructor ( scope : Construct , id : string , props : PythonBuildPipelineProps ) {
30+ super ( scope , id ) ;
31+
32+ let directory = "." ;
33+ let s3BasePath = "python-binaries" ;
34+ if ( props . projectRoot ) {
35+ directory = props . projectRoot ;
36+ }
37+ if ( props . deployBucketBasePath ) {
38+ s3BasePath = props . deployBucketBasePath
39+ }
40+
41+ const sourceAsset = new Artifact ( ) ;
42+ const defaultBuildSpec = BuildSpec . fromObject ( {
43+ version : '0.2' ,
44+ phases : {
45+ install : {
46+ "runtime-versions" : {
47+ "python" : "3.x"
48+ }
49+ } ,
50+ build : {
51+ commands : [
52+ `cd ${ directory } ` ,
53+ 'mkdir dependencies' ,
54+ 'pip3 install -r requirements.txt -t dependencies' ,
55+ `mkdir -p ${ s3BasePath } ` ,
56+ `zip -r ${ s3BasePath } /${ props . appName } -latest.zip *.py lib/* dependencies`
57+ ]
58+ }
59+ } ,
60+ artifacts : {
61+ files : [
62+ `${ s3BasePath } /${ props . appName } .zip`
63+ ] ,
64+ 'discard-paths' : true ,
65+ 'base-directory' : directory
66+ }
67+ } ) ;
68+
69+ const project = new PipelineProject ( this , 'Pipeline' , {
70+ environment : {
71+ buildImage : LinuxBuildImage . STANDARD_5_0
72+ } ,
73+ buildSpec : defaultBuildSpec
74+ } ) ;
75+
76+ const buildOutput = new Artifact ( ) ;
77+
78+ this . pipeline = new Pipeline ( this , 'CodePipeline' , {
79+ stages : [
80+ // In real world use code snippet like below to work with repository
81+ //
82+ // {
83+ // stageName: "source", actions: [new GitHubSourceAction({
84+ // actionName: "CodeCheckout",
85+ // oauthToken: SecretValue.secretsManager("github-secret"),
86+ // output: sourceAsset,
87+ // owner: "some-owner",
88+ // repo: props.repositoryName
89+ // })]
90+ // },
91+ {
92+ stageName : "source" , actions : [ new S3SourceAction ( {
93+ output : sourceAsset ,
94+ actionName : "Checkout" ,
95+ bucket : props . deployBucket ,
96+ bucketKey : SOURCE_CODE_ZIP
97+ } ) ]
98+ } ,
99+ {
100+ stageName : "build" , actions : [ new CodeBuildAction ( {
101+ input : sourceAsset , actionName : "CodeBuild" , project : project , outputs : [ buildOutput ]
102+ } ) ]
103+ } , {
104+ stageName : "saveArtifact" , actions : [ new S3DeployAction ( {
105+ bucket : props . deployBucket ,
106+ actionName : "SaveArtifact" ,
107+ input : buildOutput ,
108+ extract : true
109+ } ) ]
110+ } , {
111+ stageName : "approval" ,
112+ actions : [ new ManualApprovalAction ( {
113+ actionName : "Manual"
114+ } ) ]
115+ } ]
116+ } ) ;
117+
118+ const versionUpdateFn = new Function ( this , 'version-update-fn' , {
119+ code : Code . fromAsset ( 'flink-app-redeploy-hook' ) ,
120+ handler : "app.lambda_handler" ,
121+ runtime : Runtime . PYTHON_3_9 ,
122+ environment : {
123+ ASSET_BUCKET_ARN : props . deployBucket . bucketArn ,
124+ FILE_KEY : s3BasePath + "/" + props . appName + "-latest.zip" ,
125+ APP_NAME : props . appName
126+ } ,
127+ timeout : Duration . minutes ( 1 )
128+ } ) ;
129+
130+ versionUpdateFn . addToRolePolicy ( new PolicyStatement ( {
131+ actions : [ "kinesisanalytics:DescribeApplication" , "kinesisanalytics:UpdateApplication" ] ,
132+ resources : [ "arn:aws:kinesisanalytics:" + Aws . REGION + ":" + Aws . ACCOUNT_ID + ":application/" + props . appName ]
133+ } ) ) ;
134+ versionUpdateFn . addToRolePolicy ( new PolicyStatement ( {
135+ resources : [ "*" ] ,
136+ actions : [ "codepipeline:PutJobSuccessResult" , "codepipeline:PutJobFailureResult" ]
137+ } ) ) ;
138+
139+ this . pipeline . addStage ( {
140+ stageName : "deploy" , actions : [ new LambdaInvokeAction ( {
141+ actionName : "Deploy" ,
142+ lambda : versionUpdateFn
143+ } ) ]
144+ } ) ;
145+
146+ }
147+ }
0 commit comments