1
- import { CLI , Hooks , Serverless , ServerlessPlugin } from "@xapp/serverless-plugin-type-definitions" ;
2
- import { CloudFormation , SharedIniFileCredentials } from "aws-sdk" ;
1
+ import { CLI , Hooks , Serverless , ServerlessPlugin , ServerlessProvider } from "@xapp/serverless-plugin-type-definitions" ;
2
+ import { CloudFormation , config as AWSConfig , SharedIniFileCredentials , STS } from "aws-sdk" ;
3
3
import * as Path from "path" ;
4
4
import { AWSOptions } from "request" ;
5
5
import * as Request from "request-promise-native" ;
6
- import * as AwsUtils from "./AwsUtils" ;
7
- import Config , { Index , Repository , Template } from "./Config" ;
8
- import * as ServerlessUtils from "./ServerlessObjUtils" ;
6
+ import { findCloudformationExport , parseConfigObject } from "./AwsUtils" ;
7
+ import Config , { Index , Template } from "./Config" ;
8
+ import { getProfile , getProviderName , getRegion , getStackName } from "./ServerlessObjUtils" ;
9
+ import { setupRepo } from "./SetupRepo" ;
9
10
10
11
interface Custom {
11
12
elasticsearch ?: Config ;
@@ -15,27 +16,26 @@ class Plugin implements ServerlessPlugin {
15
16
16
17
private serverless : Serverless < Custom > ;
17
18
private cli : CLI ;
18
- private config : Config ;
19
19
hooks : Hooks ;
20
20
21
21
constructor ( serverless : Serverless < Custom > , context : any ) {
22
22
this . serverless = serverless ;
23
23
this . cli = serverless . cli ;
24
24
25
25
this . hooks = {
26
- "before:aws:deploy:deploy:updateStack" : this . create . bind ( this ) ,
26
+ "before:aws:deploy:deploy:updateStack" : this . validate . bind ( this ) ,
27
27
"after:aws:deploy:deploy:updateStack" : this . setupElasticCache . bind ( this )
28
28
} ;
29
29
}
30
30
31
31
/**
32
32
* Creates the plugin with the fully parsed Serverless object.
33
33
*/
34
- private async create ( ) {
34
+ private async validate ( ) {
35
35
const custom = this . serverless . service . custom || { } ;
36
- this . config = custom . elasticsearch || { } ;
36
+ const config = custom . elasticsearch || { } ;
37
37
38
- if ( ! this . config . endpoint && ! this . config [ "cf-endpoint" ] ) {
38
+ if ( ! config . endpoint && ! config [ "cf-endpoint" ] ) {
39
39
throw new Error ( "Elasticsearch endpoint not specified." ) ;
40
40
}
41
41
}
@@ -44,42 +44,67 @@ class Plugin implements ServerlessPlugin {
44
44
* Sends the mapping information to elasticsearch.
45
45
*/
46
46
private async setupElasticCache ( ) {
47
- let domain = this . config . endpoint ;
48
- if ( this . config [ "cf-endpoint" ] ) {
49
- const cloudFormation = new CloudFormation ( {
50
- region : ServerlessUtils . getRegion ( this . serverless ) ,
51
- credentials : new SharedIniFileCredentials ( {
52
- profile : this . config [ "aws-profile" ] || ServerlessUtils . getProfile ( this . serverless )
53
- } )
54
- } ) ;
55
- domain = await AwsUtils . findCloudformationExport ( cloudFormation , this . config [ "cf-endpoint" ] ) ;
56
- if ( ! domain ) {
57
- throw new Error ( "Endpoint not found at cloudformation export." ) ;
58
- }
59
- }
60
-
61
- const endpoint = domain . startsWith ( "http" ) ? domain : `https://${ domain } ` ;
47
+ const serviceName = await getProviderName ( this . serverless ) ;
48
+ const profile = getProfile ( this . serverless ) ;
49
+ const region = getRegion ( this . serverless ) ;
62
50
63
51
const requestOptions : Partial < Request . Options > = { } ;
64
- if ( this . config [ "aws-profile" ] ) {
65
- const sharedIni = new SharedIniFileCredentials ( { profile : this . config [ "aws-profile" ] } ) ;
52
+ if ( serviceName === "aws" ) {
53
+ AWSConfig . credentials = new SharedIniFileCredentials ( { profile } ) ;
54
+ AWSConfig . region = region ;
66
55
requestOptions . aws = {
67
- key : sharedIni . accessKeyId ,
68
- secret : sharedIni . secretAccessKey ,
56
+ key : AWSConfig . credentials . accessKeyId ,
57
+ secret : AWSConfig . credentials . secretAccessKey ,
69
58
sign_version : 4
70
59
} as AWSOptions ; // The typings are wrong. It need to include "key" and "sign_version"
71
60
}
72
61
62
+ const config = await parseConfig ( this . serverless ) ;
63
+ const endpoint = config . endpoint . startsWith ( "http" ) ? config . endpoint : `https://${ config . endpoint } ` ;
64
+
73
65
this . cli . log ( "Setting up templates..." ) ;
74
- await setupTemplates ( endpoint , this . config . templates , requestOptions ) ;
66
+ await setupTemplates ( endpoint , config . templates , requestOptions ) ;
75
67
this . cli . log ( "Setting up indices..." ) ;
76
- await setupIndices ( endpoint , this . config . indices , requestOptions ) ;
68
+ await setupIndices ( endpoint , config . indices , requestOptions ) ;
77
69
this . cli . log ( "Setting up repositories..." ) ;
78
- await setupRepo ( endpoint , this . config . repositories , requestOptions ) ;
70
+ await setupRepo ( {
71
+ baseUrl : endpoint ,
72
+ sts : new STS ( ) ,
73
+ repos : config . repositories ,
74
+ requestOptions
75
+ } ) ;
79
76
this . cli . log ( "Elasticsearch setup complete." ) ;
80
77
}
81
78
}
82
79
80
+ /**
81
+ * Parses the config object so all attributes are usable values.
82
+ *
83
+ * If the user has defined "cf-Endpoint" then the correct value will be moved to "endpoint".
84
+ *
85
+ * @param serverless
86
+ */
87
+ async function parseConfig ( serverless : Serverless < Custom > ) : Promise < Config > {
88
+ const provider = serverless . service . provider || { } as Partial < ServerlessProvider > ;
89
+ const custom = serverless . service . custom || { } ;
90
+ let config = custom . elasticsearch || { } as Config ;
91
+
92
+ if ( provider . name === "aws" || config [ "cf-endpoint" ] ) {
93
+ const cloudFormation = new CloudFormation ( ) ;
94
+
95
+ config = await parseConfigObject ( cloudFormation , getStackName ( serverless ) , config ) ;
96
+
97
+ if ( config [ "cf-endpoint" ] ) {
98
+ config . endpoint = await findCloudformationExport ( cloudFormation , config [ "cf-endpoint" ] ) ;
99
+ if ( ! config . endpoint ) {
100
+ throw new Error ( "Endpoint not found at cloudformation export." ) ;
101
+ }
102
+ }
103
+ }
104
+
105
+ return config ;
106
+ }
107
+
83
108
/**
84
109
* Sets up all the indices in the given object.
85
110
* @param baseUrl The elasticsearch URL
@@ -132,31 +157,6 @@ function validateTemplate(template: Template) {
132
157
}
133
158
}
134
159
135
- /**
136
- * Sets up all the repos.
137
- * @param baseUrl
138
- * @param repo
139
- */
140
- function setupRepo ( baseUrl : string , repos : Repository [ ] = [ ] , requestOptions : Partial < Request . Options > ) {
141
- const setupPromises : PromiseLike < Request . FullResponse > [ ] = repos . map ( ( repo ) => {
142
- validateRepo ( repo ) ;
143
- const { name, type, settings } = repo ;
144
- const url = `${ baseUrl } /_snapshot/${ name } ` ;
145
- return esPut ( url , { type, settings } , requestOptions ) ;
146
- } ) ;
147
- return Promise . all ( setupPromises ) ;
148
- }
149
-
150
- function validateRepo ( repo : Repository ) {
151
- if ( ! repo . name ) {
152
- throw new Error ( "Repo does not have a name." ) ;
153
- }
154
- if ( ! repo . type ) {
155
- throw new Error ( "Repo does not have a type." ) ;
156
- }
157
- // The settings will be validated by Elasticsearch.
158
- }
159
-
160
160
function esPut ( url : string , settings : object , requestOpts ?: Partial < Request . Options > ) {
161
161
const headers = {
162
162
"Content-Type" : "application/json" ,
0 commit comments