Skip to content
This repository was archived by the owner on Dec 9, 2024. It is now read-only.

Commit f11184c

Browse files
committed
Add more docs for service binding and fix bugs.
1 parent 7ffcbef commit f11184c

File tree

6 files changed

+123
-79
lines changed

6 files changed

+123
-79
lines changed

README.md

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@ resources:
604604
hello: world
605605
```
606606

607-
*Explicit packages support the following properties: `parameters`, `annotations` and `shared`.*
607+
*Explicit packages support the following properties: `parameters`, `annotations`, `services` and `shared`.*
608608

609609
### Binding Packages
610610

@@ -627,6 +627,50 @@ resources:
627627

628628
For more details on package binding, please see the documentation [here](https://github.com/apache/incubator-openwhisk/blob/master/docs/packages.md#creating-and-using-package-bindings).
629629

630+
## Binding Services (IBM Cloud Functions)
631+
632+
***This feature requires the [IBM Cloud CLI](https://console.bluemix.net/docs/cli/reference/bluemix_cli/download_cli.html#download_install) and [IBM Cloud Functions plugin](https://console.bluemix.net/openwhisk/learn/cli) to be installed.***
633+
634+
IBM Cloud Functions supports [automatic binding of service credentials](https://console.bluemix.net/docs/openwhisk/binding_services.html#binding_services) to actions using the CLI.
635+
636+
Bound service credentials will be passed as the `__bx_creds` parameter in the invocation parameters.
637+
638+
This feature is also available through the `serverless.yaml` file using the `bind` property for each function.
639+
640+
```yaml
641+
functions:
642+
my_function:
643+
handler: file_name.handler
644+
bind:
645+
- service:
646+
name: cloud-object-storage
647+
instance: my-cos-storage
648+
```
649+
650+
The `service` configuration supports the following properties.
651+
652+
- `name`: identifier for the cloud service
653+
- `instance`: instance name for service (*optional*)
654+
- `key`: key name for instance and service (*optional*)
655+
656+
*If the `instance` or `key` properties are missing, the first available instance and key found will be used.*
657+
658+
Binding services removes the need to manually create default parameters for service keys from platform services.
659+
660+
More details on binding service credentials to actions can be found in the [official documentation](https://console.bluemix.net/docs/openwhisk/binding_services.html#binding_services) and [this blog post](http://jamesthom.as/blog/2018/06/05/binding-iam-services-to-ibm-cloud-functions/).
661+
662+
Packages defined in the `resources` section can bind services using the same configuration properties.
663+
664+
```yaml
665+
resources:
666+
packages:
667+
myPackage:
668+
bind:
669+
- service:
670+
name: cloud-object-storage
671+
instance: my-cos-storage
672+
```
673+
630674
## Runtime Configuration Properties
631675

632676
The following OpenWhisk configuration properties are supported for functions defined in
@@ -645,6 +689,10 @@ functions:
645689
foo: bar // default parameters
646690
annotations:
647691
foo: bar // action annotations
692+
bind:
693+
- service:
694+
name: cloud-object-storage
695+
instance: my-cos-storage
648696
```
649697

650698
## Writing Sequences

compile/servicebindings/README.md

Lines changed: 28 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,49 @@
1-
# Compile Packages
1+
# Service Bindings
22

3-
This plugins compiles the packages in `serverless.yaml` to corresponding [OpenWhisk Packages](https://github.com/openwhisk/openwhisk/blob/master/docs/packages.md)
4-
definitions.
3+
This plugin binds IBM Cloud platform service credentials to actions and packages in `serverless.yaml`.
54

65
## How it works
76

8-
`Compile Packages` hooks into the [`package:compileEvents`](/lib/plugins/deploy) lifecycle.
7+
`Compile Service Bindings` hooks into the [`package:compileEvents`](/lib/plugins/deploy) lifecycle.
98

10-
It loops over all packages which are defined in `serverless.yaml`.
9+
***This feature requires the [IBM Cloud CLI](https://console.bluemix.net/docs/cli/reference/bluemix_cli/download_cli.html#download_install) and [IBM Cloud Functions plugin](https://console.bluemix.net/openwhisk/learn/cli) to be installed.***
1110

12-
### Implicit Packages
11+
IBM Cloud Functions supports [automatic binding of service credentials](https://console.bluemix.net/docs/openwhisk/binding_services.html#binding_services) to actions using the CLI.
1312

14-
Actions can be assigned to packages by setting the function `name` with a package reference.
13+
Bound service credentials will be passed as the `__bx_creds` parameter in the invocation parameters.
14+
15+
This feature is also available through the `serverless.yaml` file using the `bind` property for each function.
1516

1617
```yaml
1718
functions:
18-
foo:
19-
handler: handler.foo
20-
name: "myPackage/foo"
21-
bar:
22-
handler: handler.bar
23-
name: "myPackage/bar"
19+
my_function:
20+
handler: file_name.handler
21+
bind:
22+
- service:
23+
name: cloud-object-storage
24+
instance: my-cos-storage
2425
```
2526
26-
In this example, two new actions (`foo` & `bar`) will be created using the `myPackage` package.
27+
The `service` configuration supports the following properties.
2728

28-
Packages which do not exist will be automatically created during deployments. When using the `remove` command, any packages referenced in the `serverless.yml` will be deleted.
29+
- `name`: identifier for the cloud service
30+
- `instance`: instance name for service (*optional*)
31+
- `key`: key name for instance and service (*optional*)
2932

30-
### Explicit Packages
33+
*If the `instance` or `key` properties are missing, the first available instance and key found will be used.*
3134

32-
Packages can also be defined explicitly to set shared configuration parameters. Default package parameters are merged into event parameters for each invocation.
35+
Binding services removes the need to manually create default parameters for service keys from platform services.
3336

34-
```yaml
35-
functions:
36-
foo:
37-
handler: handler.foo
38-
name: "myPackage/foo"
39-
40-
resources:
41-
packages:
42-
myPackage:
43-
parameters:
44-
hello: world
45-
```
37+
More details on binding service credentials to actions can be found in the [official documentation](https://console.bluemix.net/docs/openwhisk/binding_services.html#binding_services) and [this blog post](http://jamesthom.as/blog/2018/06/05/binding-iam-services-to-ibm-cloud-functions/).
4638

47-
### Binding Packages
48-
49-
OpenWhisk also supports "binding" external packages into your workspace. Bound packages can have default parameters set for shared actions.
50-
51-
For example, binding the `/whisk.system/cloudant` package into a new package allows you to set default values for the `username`, `password` and `dbname` properties. Actions from this package can then be invoked with having to pass these parameters in.
52-
53-
Define packages explicitly with a `binding` parameter to use this behaviour.
39+
Packages defined in the `resources` section can bind services using the same configuration properties.
5440

5541
```yaml
5642
resources:
5743
packages:
58-
mySamples:
59-
binding: /whisk.system/cloudant
60-
parameters:
61-
username: bernie
62-
password: sanders
63-
dbname: vermont
64-
```
65-
66-
For more details on package binding, please see the documentation [here](https://github.com/apache/incubator-openwhisk/blob/master/docs/packages.md#creating-and-using-package-bindings).
44+
myPackage:
45+
bind:
46+
- service:
47+
name: cloud-object-storage
48+
instance: my-cos-storage
49+
```

compile/servicebindings/index.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,10 @@ class OpenWhiskCompileServiceBindings {
1717
return props.name || `${this.serverless.service.service}_${name}`;
1818
}
1919

20-
parseServiceBindings(action, properties) {
21-
const name = { action: this.calculateFunctionName(action, properties) }
20+
parseServiceBindings(name, properties) {
2221
const bindings = properties.bind || []
2322
const servicebindings = bindings.filter(b => b.service)
24-
.map(b => Object.assign(b.service, name))
23+
.map(b => Object.assign(b.service, { action: name } ))
2524

2625
const serviceNames = new Set()
2726

@@ -42,14 +41,21 @@ class OpenWhiskCompileServiceBindings {
4241

4342
compileFnServiceBindings() {
4443
return this.serverless.service.getAllFunctions()
45-
.map(name => this.parseServiceBindings(name, this.serverless.service.getFunction(name)))
44+
.map(name => {
45+
const fnObj = this.serverless.service.getFunction(name)
46+
const fnName = this.calculateFunctionName(name, fnObj)
47+
return this.parseServiceBindings(fnName, fnObj)
48+
})
49+
.filter(sbs => sbs.length > 0)
4650
}
4751

4852
compilePkgServiceBindings() {
4953
const manifestResources = this.serverless.service.resources || {}
5054
const packages = manifestResources.packages || {}
5155

52-
return Object.keys(packages).map(name => this.parseServiceBindings(name, packages[name]))
56+
return Object.keys(packages)
57+
.map(name => this.parseServiceBindings(name, packages[name]))
58+
.filter(sbs => sbs.length > 0)
5359
}
5460

5561
compileServiceBindings() {
@@ -58,7 +64,11 @@ class OpenWhiskCompileServiceBindings {
5864
const fnServiceBindings = this.compileFnServiceBindings()
5965
const pkgServiceBindings = this.compilePkgServiceBindings()
6066

61-
this.serverless.service.bindings = [].concat(...pkgServiceBindings, ...fnServiceBindings)
67+
this.serverless.service.bindings = {
68+
fns: fnServiceBindings,
69+
packages: pkgServiceBindings
70+
}
71+
6272
return BbPromise.resolve();
6373
}
6474
}

compile/servicebindings/tests/index.js

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ describe('OpenWhiskCompileServiceBindings', () => {
4747
it('should return array with single service binding property', () => {
4848
const action = 'fnName'
4949
const service = { name: 'my-service', instance: 'my-instance', key: 'mykey' }
50-
const response = { action: `serviceName_fnName`, name: 'my-service', instance: 'my-instance', key: 'mykey' }
50+
const response = { action: `fnName`, name: 'my-service', instance: 'my-instance', key: 'mykey' }
5151
const result = openwhiskCompileServiceBindings.parseServiceBindings(action, {bind: [{ service }]})
5252
expect(result).to.deep.equal([response])
5353
})
@@ -93,13 +93,13 @@ describe('OpenWhiskCompileServiceBindings', () => {
9393
service.getFunction = name => fns[name]
9494

9595
const services = [
96-
{ action: 'serviceName_a', name: 'service-name-a' },
97-
{ action: 'serviceName_b', name: 'service-name-b', instance: 'instance-name' },
98-
{ action: 'serviceName_c', name: 'service-name-a' },
99-
{ action: 'serviceName_c', name: 'service-name-b' }
96+
[{ action: 'serviceName_a', name: 'service-name-a' }],
97+
[{ action: 'serviceName_b', name: 'service-name-b', instance: 'instance-name' }],
98+
[{ action: 'serviceName_c', name: 'service-name-a' }, { action: 'serviceName_c', name: 'service-name-b' }]
10099
]
101100
return openwhiskCompileServiceBindings.compileServiceBindings().then(result => {
102-
expect(service.bindings).to.deep.equal(services)
101+
expect(service.bindings.fns).to.deep.equal(services)
102+
expect(service.bindings.packages).to.deep.equal([])
103103
})
104104
})
105105

@@ -112,9 +112,10 @@ describe('OpenWhiskCompileServiceBindings', () => {
112112
service.getAllFunctions = () => Object.keys(fns)
113113
service.getFunction = name => fns[name]
114114

115-
const services = [ { action: 'some_name', name: 'service-name-a' } ]
115+
const services = [ [{ action: 'some_name', name: 'service-name-a' }] ]
116116
return openwhiskCompileServiceBindings.compileServiceBindings().then(result => {
117-
expect(service.bindings).to.deep.equal(services)
117+
expect(service.bindings.fns).to.deep.equal(services)
118+
expect(service.bindings.packages).to.deep.equal([])
118119
})
119120
})
120121

@@ -129,14 +130,14 @@ describe('OpenWhiskCompileServiceBindings', () => {
129130
}
130131

131132
const services = [
132-
{ action: 'serviceName_a', name: 'service-name-a' },
133-
{ action: 'serviceName_b', name: 'service-name-b', instance: 'instance-name' },
134-
{ action: 'serviceName_c', name: 'service-name-a' },
135-
{ action: 'serviceName_c', name: 'service-name-b' }
133+
[{ action: 'a', name: 'service-name-a' }],
134+
[{ action: 'b', name: 'service-name-b', instance: 'instance-name' }],
135+
[{ action: 'c', name: 'service-name-a' }, { action: 'c', name: 'service-name-b' }]
136136
]
137137

138138
return openwhiskCompileServiceBindings.compileServiceBindings().then(() => {
139-
expect(service.bindings).to.deep.equal(services);
139+
expect(service.bindings.packages).to.deep.equal(services);
140+
expect(service.bindings.fns).to.deep.equal([]);
140141
});
141142
});
142143

@@ -153,10 +154,10 @@ describe('OpenWhiskCompileServiceBindings', () => {
153154
service.getAllFunctions = () => Object.keys(fns)
154155
service.getFunction = name => fns[name]
155156

156-
const services = [
157-
{ action: 'serviceName_a', name: 'service-name-a' },
158-
{ action: 'serviceName_b', name: 'service-name-b', instance: 'instance-name' }
159-
]
157+
const services = {
158+
packages: [[{ action: 'a', name: 'service-name-a' }]],
159+
fns: [[{ action: 'serviceName_b', name: 'service-name-b', instance: 'instance-name' }]]
160+
}
160161

161162
return openwhiskCompileServiceBindings.compileServiceBindings().then(() => {
162163
expect(service.bindings).to.deep.equal(services);

deploy/lib/deployServiceBindings.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const { spawn } = require('child_process');
66
module.exports = {
77
configureServiceBinding(binding) {
88
if (this.options.verbose) {
9-
this.serverless.cli.log(`Configuring Service Binding: ${binding}`);
9+
this.serverless.cli.log(`Configuring Service Binding: ${JSON.stringify(binding)}`);
1010
}
1111

1212
return new Promise((resolve, reject) => {
@@ -56,7 +56,7 @@ module.exports = {
5656
return reject(err)
5757
}
5858
if (this.options.verbose) {
59-
this.serverless.cli.log(`Configured Service Binding: ${binding}`);
59+
this.serverless.cli.log(`Configured Service Binding: ${JSON.stringify(binding)}`);
6060
}
6161
resolve()
6262
});
@@ -66,16 +66,18 @@ module.exports = {
6666
configureServiceBindings() {
6767
const bindings = this.getServiceBindings();
6868

69-
if (bindings.length) {
69+
if (bindings.fns.length || bindings.packages.length) {
7070
this.serverless.cli.log('Configuring Service Bindings...');
7171
}
7272

7373
return BbPromise.all(
74-
bindings.map(sb => this.configureServiceBinding(sb))
75-
);
74+
bindings.packages.map(sbs => BbPromise.mapSeries(sbs, sb => this.configureServiceBinding(sb)))
75+
).then(() => BbPromise.all(
76+
bindings.fns.map(sbs => BbPromise.mapSeries(sbs, sb => this.configureServiceBinding(sb)))
77+
));
7678
},
7779

7880
getServiceBindings() {
79-
return this.serverless.service.bindings || [];
81+
return this.serverless.service.bindings || { fns: [], packages: [] } ;
8082
}
8183
};

deploy/tests/deployServiceBindings.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ describe('deployServiceBindings', () => {
4545

4646
describe('#configureServiceBindings()', () => {
4747
it('should call binding command for each binding and return when all finish', () => {
48-
const bindings = [{name: 'a'}, {name: 'a'}, {name: 'a'}]
49-
openwhiskDeploy.serverless.service.bindings = bindings
48+
const bindings = [[{name: 'a'}, {name: 'a'}, {name: 'a'}]]
49+
openwhiskDeploy.serverless.service.bindings = { fns: bindings, packages: bindings }
5050
sandbox.stub(openwhiskDeploy, 'configureServiceBinding', () => {
5151
return Promise.resolve();
5252
});
@@ -56,8 +56,8 @@ describe('deployServiceBindings', () => {
5656
});
5757

5858
it('should reject when function handler fails to deploy with error message', () => {
59-
const bindings = [{name: 'a'}, {name: 'a'}, {name: 'a'}]
60-
openwhiskDeploy.serverless.service.bindings = bindings
59+
const bindings = [[{name: 'a'}, {name: 'a'}, {name: 'a'}]]
60+
openwhiskDeploy.serverless.service.bindings = { fns: bindings, packages: bindings }
6161
const err = { message: 'some reason' };
6262
sandbox.stub(openwhiskDeploy, 'configureServiceBinding', () => {
6363
return Promise.reject(err);

0 commit comments

Comments
 (0)