clever-tools 3.5.2 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,9 +2,11 @@
2
2
 
3
3
  Deploy on Clever Cloud and control you applications, add-ons, services from command line.
4
4
 
5
+ - [Create a Clever Cloud account](https://console.clever-cloud.com)
6
+
5
7
  ## Installation
6
8
 
7
- Clever Tools are availables from many channels. The simpler way to install them, if you already have Node.js on your system, is through `npm` package manager:
9
+ Clever Tools are available from many channels. The simpler way to install them, if you already have Node.js on your system, is through `npm` package manager:
8
10
 
9
11
  ```sh
10
12
  npm install -g clever-tools
@@ -43,112 +45,18 @@ clever --zsh-autocomplete-script $(which clever) | sudo tee /usr/share/zsh/site-
43
45
 
44
46
  ## How to use
45
47
 
46
- ### Login
47
-
48
- To use `clever-tools`, you have to login.
48
+ You can then login and check everything is working:
49
49
 
50
- ```sh
50
+ ```bash
51
51
  clever login
52
+ clever profile
52
53
  ```
53
54
 
54
- It will open the Web console in your browser and reuse your existing session if you're already logged in.
55
-
56
- `clever login` tries to open a browser through `xdg-open` on GNU/Linux systems (and in bash for windows).
57
- Make sure you have `xdg-utils` available as well as a default browser set, or you can copy and paste the URL displayed in the console.
58
-
59
- ### Create an application
60
-
61
- ```sh
62
- clever create <name> --type <type> \
63
- [--region <region>] \
64
- [--org <organisation>] \
65
- [--alias <alias>]
66
- ```
67
-
68
- Where `type` is one of:
69
-
70
- - `docker`: for Docker-based applications
71
- - `go`: for Go applications
72
- - `gradle`: for applications launched with gradle
73
- - `haskell`: for haskell applications
74
- - `jar`: for applications deployed as standalone jar files
75
- - `maven`: for applications launched with maven
76
- - `meteor`: for Meteor applications launched with Node.js
77
- - `node`: for Node.js applications
78
- - `php`: for PHP applications
79
- - `play1`: for Play1 applications
80
- - `play2`: for Play2 applications
81
- - `python`: for python27 and python3 applications
82
- - `ruby`: for ruby applications
83
- - `rust`: for rust applications
84
- - `sbt`: for applications launched with SBT
85
- - `static-apache`: for static (HTML only) websites
86
- - `war`: for applications deployed as war files
87
-
88
- Where region is one of:
89
-
90
- - `par` (Paris, [Clever Cloud](https://www.clever-cloud.com/infrastructure/))
91
- - `grahds` (Gravelines, HDS servers, OVHcloud)
92
- - `rbx` (Roubaix, OVHcloud)
93
- - `rbxhds` (Roubaix, HDS servers, OVHcloud)
94
- - `scw` (Paris, [Scaleway DC5](https://www.clever-cloud.com/blog/press/2023/01/17/clever-cloud-and-scaleway-join-forces-to-unveil-a-sovereign-european-paas-offering/))
95
- - `mtl` (Montreal, OVHcloud)
96
- - `sgp` (Singapore, OVHcloud)
97
- - `syd` (Sydney, OVHcloud)
98
- - `wsw` (Warsaw, OVHcloud)
99
-
100
- `--org` allows you to chose the organisation in which your app is created.
101
-
102
- `--alias` allows you to deploy the same application in multiple environments on Clever Cloud (eg: production, testing, …)
103
-
104
- ### Link an existing application
105
-
106
- ```sh
107
- clever link [--org <ORG-NAME>] <APP-NAME> [--alias <alias>]
108
- ```
109
-
110
- Where `APP-NAME` is the name of your application, and `ORG-NAME` is the name
111
- of the organisation it's in. You can specify a complete application id instead
112
- of its name (in that case, `--org` can be omitted).
113
-
114
- ### Deploy an application
115
-
116
- ```sh
117
- clever deploy [--alias <alias>]
118
- ```
119
-
120
- `--alias` allows you to deploy your application several times on Clever Cloud
121
- (eg: production, testing, …)
122
-
123
- ### Application status
124
-
125
- ```sh
126
- clever status [--alias <alias>]
127
- ```
128
-
129
- ### Change application scalability
130
-
131
- ```sh
132
- clever scale [--alias <alias>] [--min-flavor <minflavor>] [--max-flavor <maxflavor>] [--min-instances <mininstances>] [--max-instances <maxinstances>]
133
- ```
134
-
135
- ### Display help
136
-
137
- You can display help about each command with `clever help`.
138
-
139
- ```sh
140
- clever help
141
- clever help deploy
142
- ```
55
+ Discover how to use Clever Tools through [our documentation](docs/).
143
56
 
144
57
  ## Examples
145
58
 
146
- ```sh
147
- cd node_project
148
- clever login
149
- clever create "Node.js application" -t node -r mtl
150
- clever deploy
151
- ```
59
+ Discover how to deploy many applications on Clever Cloud within [our guides](https://developers.clever-cloud.com/guides/).
152
60
 
153
61
  ## How to send feedback?
154
62
 
@@ -159,7 +67,6 @@ clever deploy
159
67
  This project uses GitHub Actions to build binaries, package them and release them automatically on the various repositories.
160
68
  If you want to know more or if you need to release a new version, please read [RELEASE.md](./RELEASE.md) carefully.
161
69
 
162
-
163
70
  ## License
164
71
 
165
72
  This project is licensed under the [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html).
package/bin/clever.js CHANGED
@@ -80,11 +80,11 @@ function run () {
80
80
  // ARGUMENTS
81
81
  const args = {
82
82
  addonIdOrName: cliparse.argument('addon-id', {
83
- description: 'Addon ID (or name, if unambiguous)',
83
+ description: 'Add-on ID (or name, if unambiguous)',
84
84
  parser: Parsers.addonIdOrName,
85
85
  }),
86
- addonName: cliparse.argument('addon-name', { description: 'Addon name' }),
87
- addonProvider: cliparse.argument('addon-provider', { description: 'Addon provider' }),
86
+ addonName: cliparse.argument('addon-name', { description: 'Add-on name' }),
87
+ addonProvider: cliparse.argument('addon-provider', { description: 'Add-on provider' }),
88
88
  alias: cliparse.argument('app-alias', { description: 'Application alias' }),
89
89
  appIdOrName: cliparse.argument('app-id', {
90
90
  description: 'Application ID (or name, if unambiguous)',
@@ -143,7 +143,7 @@ function run () {
143
143
  importAsJson: cliparse.flag('json', {
144
144
  description: 'Import variables as JSON (an array of { "name": "THE_NAME", "value": "THE_VALUE" } objects)',
145
145
  }),
146
- addonId: cliparse.option('addon', { metavar: 'addon_id', description: 'Addon ID' }),
146
+ addonId: cliparse.option('addon', { metavar: 'addon_id', description: 'Add-on ID' }),
147
147
  after: cliparse.option('after', {
148
148
  metavar: 'after',
149
149
  aliases: ['since'],
@@ -249,7 +249,7 @@ function run () {
249
249
  linkAddon: cliparse.option('link', {
250
250
  aliases: ['l'],
251
251
  metavar: 'alias',
252
- description: 'Link the created addon to the app with the specified alias',
252
+ description: 'Link the created add-on to the app with the specified alias',
253
253
  complete: Application('listAvailableAliases'),
254
254
  }),
255
255
  listAllNotifications: cliparse.flag('list-all', { description: 'List all notifications for your user or for an organisation with the \'--org\' option' }),
@@ -291,7 +291,7 @@ function run () {
291
291
  required: true,
292
292
  parser: Parsers.commaSeparated,
293
293
  }),
294
- onlyAddons: cliparse.flag('only-addons', { description: 'Only show addon dependencies' }),
294
+ onlyAddons: cliparse.flag('only-addons', { description: 'Only show add-on dependencies' }),
295
295
  onlyAliases: cliparse.flag('only-aliases', { description: 'List only application aliases' }),
296
296
  onlyApps: cliparse.flag('only-apps', { description: 'Only show app dependencies' }),
297
297
  orgaIdOrName: cliparse.option('org', {
@@ -306,13 +306,13 @@ function run () {
306
306
  drainPassword: cliparse.option('password', {
307
307
  aliases: ['p'],
308
308
  metavar: 'password',
309
- description: 'HTTP basic auth password',
309
+ description: '(HTTP drains) basic auth password',
310
310
  }),
311
311
  addonPlan: cliparse.option('plan', {
312
312
  aliases: ['p'],
313
313
  default: '',
314
314
  metavar: 'plan',
315
- description: 'Addon plan, depends on the provider',
315
+ description: 'Add-on plan, depends on the provider',
316
316
  complete: Addon('completePlan'),
317
317
  }),
318
318
  quiet: cliparse.flag('quiet', { aliases: ['q'], description: 'Don\'t show logs during deployment' }),
@@ -323,16 +323,16 @@ function run () {
323
323
  aliases: ['r'],
324
324
  default: 'par',
325
325
  metavar: 'region',
326
- description: 'Region to provision the addon in, depends on the provider',
326
+ description: 'Region to provision the add-on in, depends on the provider',
327
327
  complete: Addon('completeRegion'),
328
328
  }),
329
329
  addonVersion: cliparse.option('addon-version', {
330
330
  metavar: 'addon-version',
331
- description: 'The version to use for the addon',
331
+ description: 'The version to use for the add-on',
332
332
  }),
333
333
  addonOptions: cliparse.option('option', {
334
334
  metavar: 'option',
335
- description: 'Option to enable for the addon. Multiple --option argument can be passed to enable multiple options',
335
+ description: 'Option to enable for the add-on. Multiple --option argument can be passed to enable multiple options',
336
336
  }),
337
337
  region: cliparse.option('region', {
338
338
  aliases: ['r'],
@@ -351,7 +351,7 @@ function run () {
351
351
  }),
352
352
  notificationScope: cliparse.option('service', {
353
353
  metavar: 'service_id',
354
- description: 'Restrict notifications to specific applications and addons',
354
+ description: 'Restrict notifications to specific applications and add-ons',
355
355
  parser: Parsers.commaSeparated,
356
356
  }),
357
357
  showAllActivity: cliparse.flag('show-all', { description: 'Show all activity' }),
@@ -376,28 +376,33 @@ function run () {
376
376
  drainUsername: cliparse.option('username', {
377
377
  aliases: ['u'],
378
378
  metavar: 'username',
379
- description: 'HTTP basic auth username',
379
+ description: '(HTTP drains) basic auth username',
380
380
  }),
381
381
  drainAPIKey: cliparse.option('api-key', {
382
382
  aliases: ['k'],
383
383
  metavar: 'api_key',
384
- description: 'Drain custom key',
384
+ description: '(NewRelic drains) API key',
385
385
  }),
386
386
  drainIndexPrefix: cliparse.option('index-prefix', {
387
387
  aliases: ['i'],
388
388
  metavar: 'index_prefix',
389
- description: 'Optional drain index prefix for ElasticSearch: `<indexPrefix>-<YYYY-MM-DD>`',
389
+ description: '(ElasticSearch drains) Index prefix',
390
390
  default: 'logstash-<YYYY-MM-DD>',
391
391
  }),
392
+ drainSDParameters: cliparse.option('sd-params', {
393
+ aliases: ['s'],
394
+ metavar: 'sd_params',
395
+ description: '(TCP and UDP drains) sd-params string (e.g.: `X-OVH-TOKEN=\\"REDACTED\\"`)',
396
+ }),
392
397
  verbose: cliparse.flag('verbose', { aliases: ['v'], description: 'Verbose output' }),
393
398
  withoutCache: cliparse.flag('without-cache', { description: 'Restart the application without using cache' }),
394
399
  confirmAddonCreation: cliparse.flag('yes', {
395
400
  aliases: ['y'],
396
- description: 'Skip confirmation even if the addon is not free',
401
+ description: 'Skip confirmation even if the add-on is not free',
397
402
  }),
398
403
  confirmAddonDeletion: cliparse.flag('yes', {
399
404
  aliases: ['y'],
400
- description: 'Skip confirmation and delete the addon directly',
405
+ description: 'Skip confirmation and delete the add-on directly',
401
406
  }),
402
407
  confirmApplicationDeletion: cliparse.flag('yes', {
403
408
  aliases: ['y'],
@@ -428,7 +433,7 @@ function run () {
428
433
  aliases: ['m'],
429
434
  required: true,
430
435
  metavar: 'member_id',
431
- description: `The member ID: an app ID (e.g.: ${Formatter.formatCode('app_xxx')}), addon ID (e.g.: ${Formatter.formatCode('addon_xxx')}) or external node category ID`,
436
+ description: `The member ID: an app ID (e.g.: ${Formatter.formatCode('app_xxx')}), add-on ID (e.g.: ${Formatter.formatCode('addon_xxx')}) or external node category ID`,
432
437
  // complete: NetworkGroup('xxx'),
433
438
  }),
434
439
  ngMemberDomainName: cliparse.option('domain-name', {
@@ -579,37 +584,41 @@ function run () {
579
584
  // ADDON COMMANDS
580
585
  const addon = lazyRequirePromiseModule('../src/commands/addon.js');
581
586
  const addonCreateCommand = cliparse.command('create', {
582
- description: 'Create an addon',
587
+ description: 'Create an add-on',
583
588
  args: [args.addonProvider, args.addonName],
584
589
  options: [opts.linkAddon, opts.confirmAddonCreation, opts.addonPlan, opts.addonRegion, opts.addonVersion, opts.addonOptions, opts.humanJsonOutputFormat],
585
590
  }, addon('create'));
586
591
  const addonDeleteCommand = cliparse.command('delete', {
587
- description: 'Delete an addon',
592
+ description: 'Delete an add-on',
588
593
  args: [args.addonIdOrName],
589
594
  options: [opts.confirmAddonDeletion],
590
595
  }, addon('delete'));
591
596
  const addonRenameCommand = cliparse.command('rename', {
592
- description: 'Rename an addon',
597
+ description: 'Rename an add-on',
593
598
  args: [args.addonIdOrName, args.addonName],
594
599
  }, addon('rename'));
595
600
  const addonShowProviderCommand = cliparse.command('show', {
596
- description: 'Show information about an addon provider',
601
+ description: 'Show information about an add-on provider',
597
602
  args: [args.addonProvider],
598
603
  }, addon('showProvider'));
599
604
  const addonProvidersCommand = cliparse.command('providers', {
600
- description: 'List available addon providers',
605
+ description: 'List available add-on providers',
601
606
  commands: [addonShowProviderCommand],
602
607
  }, addon('listProviders'));
603
608
  const addonEnvCommand = cliparse.command('env', {
604
- description: 'List the environment variables for an addon',
609
+ description: 'List environment variables for an add-on',
605
610
  options: [opts.addonEnvFormat],
606
611
  args: [opts.addonId],
607
612
  }, addon('env'));
613
+ const addonListCommand = cliparse.command('list', {
614
+ description: 'List available add-ons',
615
+ options: [opts.humanJsonOutputFormat],
616
+ }, addon('list'));
608
617
 
609
618
  const addonCommands = cliparse.command('addon', {
610
- description: 'Manage addons',
611
- options: [opts.orgaIdOrName, opts.humanJsonOutputFormat],
612
- commands: [addonCreateCommand, addonDeleteCommand, addonRenameCommand, addonProvidersCommand, addonEnvCommand],
619
+ description: 'Manage add-ons',
620
+ options: [opts.orgaIdOrName],
621
+ commands: [addonCreateCommand, addonDeleteCommand, addonRenameCommand, addonListCommand, addonProvidersCommand, addonEnvCommand],
613
622
  }, addon('list'));
614
623
 
615
624
  // APPLICATIONS COMMAND
@@ -713,7 +722,7 @@ function run () {
713
722
  const drainCreateCommand = cliparse.command('create', {
714
723
  description: 'Create a drain',
715
724
  args: [args.drainType, args.drainUrl],
716
- options: [opts.addonId, opts.drainUsername, opts.drainPassword, opts.drainAPIKey, opts.drainIndexPrefix],
725
+ options: [opts.addonId, opts.drainUsername, opts.drainPassword, opts.drainAPIKey, opts.drainIndexPrefix, opts.drainSDParameters],
717
726
  }, drain('create'));
718
727
  const drainRemoveCommand = cliparse.command('remove', {
719
728
  description: 'Remove a drain',
@@ -821,11 +830,11 @@ function run () {
821
830
  options: [opts.ngIdOrLabel, opts.ngMemberId, opts.naturalName, opts.jsonFormat],
822
831
  }, networkgroups('getMember'));
823
832
  const networkGroupsMemberAddCommand = cliparse.command('add', {
824
- description: 'Add an app or addon as a Network Group member',
833
+ description: 'Add an app or add-on as a Network Group member',
825
834
  options: [opts.ngIdOrLabel, opts.ngMemberId, opts.ngMemberType, opts.ngMemberDomainName, opts.optNgMemberLabel],
826
835
  }, networkgroups('addMember'));
827
836
  const networkGroupsMemberRemoveCommand = cliparse.command('remove', {
828
- description: 'Remove an app or addon from a Network Group',
837
+ description: 'Remove an app or add-on from a Network Group',
829
838
  options: [opts.ngIdOrLabel, opts.ngMemberId],
830
839
  }, networkgroups('removeMember'));
831
840
 
@@ -954,11 +963,11 @@ function run () {
954
963
  args: [args.appIdOrName],
955
964
  }, service('unlinkApp'));
956
965
  const serviceLinkAddonCommand = cliparse.command('link-addon', {
957
- description: 'Link an existing addon to this application',
966
+ description: 'Link an existing add-on to this application',
958
967
  args: [args.addonIdOrName],
959
968
  }, service('linkAddon'));
960
969
  const serviceUnlinkAddonCommand = cliparse.command('unlink-addon', {
961
- description: 'Unlink an addon from this application',
970
+ description: 'Unlink an add-on from this application',
962
971
  args: [args.addonIdOrName],
963
972
  }, service('unlinkAddon'));
964
973
  const serviceCommands = cliparse.command('service', {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clever-tools",
3
- "version": "3.5.2",
3
+ "version": "3.6.0",
4
4
  "description": "Command Line Interface for Clever Cloud.",
5
5
  "main": "bin/clever.js",
6
6
  "keywords": [
@@ -146,7 +146,7 @@ function displayAddon (format, addon, providerName, message) {
146
146
  colors.yellow(`/!\\ The MateriaDB ${providerName.toUpperCase()} provider is in Alpha testing phase, don't store sensitive or production grade data`),
147
147
  'You can easily use MateriaDB KV with \'redis-cli\', with such commands:',
148
148
  colors.blue(`source <(clever addon env ${addon.id} -F shell)`),
149
- colors.blue('redis-cli -h $KV_HOST -p $KV_PORT'),
149
+ colors.blue('redis-cli -h $KV_HOST -p $KV_PORT --tls'),
150
150
  ].join('\n');
151
151
  Logger.println(materiaMessage);
152
152
  }
@@ -26,21 +26,24 @@ async function list (params) {
26
26
 
27
27
  drains.forEach((drain) => {
28
28
  const { id, state, target } = drain;
29
- const { url, drainType, indexPrefix } = target;
29
+ const { url, drainType, indexPrefix, structuredDataParameters } = target;
30
30
 
31
31
  let drainView = `${id} -> ${state} for ${url} as ${drainType}`;
32
32
  if (indexPrefix != null) {
33
33
  drainView += `, index: '${indexPrefix}-<YYYY-MM-DD>'`;
34
34
  }
35
+ if (structuredDataParameters != null) {
36
+ drainView += `, sd-parameters: '${structuredDataParameters}'`;
37
+ }
35
38
  Logger.println(drainView);
36
39
  });
37
40
  }
38
41
 
39
42
  async function create (params) {
40
43
  const [drainTargetType, drainTargetURL] = params.args;
41
- const { alias, addon: addonId, username, password, 'api-key': apiKey, 'index-prefix': indexPrefix } = params.options;
44
+ const { alias, addon: addonId, username, password, 'api-key': apiKey, 'index-prefix': indexPrefix, 'sd-parameters': structuredDataParameters } = params.options;
42
45
  const drainTargetCredentials = { username, password };
43
- const drainTargetConfig = { apiKey, indexPrefix };
46
+ const drainTargetConfig = { apiKey, indexPrefix, structuredDataParameters };
44
47
 
45
48
  const appIdOrAddonId = await getAppOrAddonId({ alias, addonId });
46
49
  const body = createDrainBody(appIdOrAddonId, drainTargetURL, drainTargetType, drainTargetCredentials, drainTargetConfig);
@@ -3,8 +3,8 @@
3
3
  const autocomplete = require('cliparse').autocomplete;
4
4
 
5
5
  const DRAIN_TYPES = [
6
- { id: 'TCPSyslog' },
7
- { id: 'UDPSyslog' },
6
+ { id: 'TCPSyslog', structuredDataParameters: 'OPTIONAL' },
7
+ { id: 'UDPSyslog', structuredDataParameters: 'OPTIONAL' },
8
8
  { id: 'HTTP', credentials: 'OPTIONAL' },
9
9
  { id: 'ElasticSearch', credentials: 'MANDATORY', indexPrefix: 'OPTIONAL' },
10
10
  { id: 'DatadogHTTP' },
@@ -33,6 +33,9 @@ function createDrainBody (appId, drainTargetURL, drainTargetType, drainTargetCre
33
33
  if (indexPrefixExist(drainTargetConfig)) {
34
34
  body.indexPrefix = drainTargetConfig.indexPrefix;
35
35
  }
36
+ if (structuredDataParametersExist(drainTargetConfig)) {
37
+ body.structuredDataParameters = drainTargetConfig.structuredDataParameters;
38
+ }
36
39
  return body;
37
40
  }
38
41
 
@@ -42,6 +45,7 @@ function authorizeDrainCreation (drainTargetType, drainTargetCredentials, drainT
42
45
  const credStatus = fieldStatus(drainTargetType).credentials;
43
46
  const keyStatus = fieldStatus(drainTargetType).apiKey;
44
47
  const indexPrefixStatus = fieldStatus(drainTargetType).indexPrefix;
48
+ const structuredDataParametersStatus = fieldStatus(drainTargetType).structuredDataParameters;
45
49
 
46
50
  if (credStatus === 'MANDATORY') {
47
51
  return credentialsExist(drainTargetCredentials);
@@ -72,6 +76,16 @@ function authorizeDrainCreation (drainTargetType, drainTargetCredentials, drainT
72
76
  if (!indexPrefixStatus) {
73
77
  return indexPrefixEmpty(drainTargetConfig);
74
78
  }
79
+
80
+ if (structuredDataParametersStatus === 'MANDATORY') {
81
+ return structuredDataParametersExist(drainTargetConfig);
82
+ }
83
+ if (structuredDataParametersStatus === 'OPTIONAL') {
84
+ return true;
85
+ }
86
+ if (!structuredDataParametersStatus) {
87
+ return structuredDataParametersEmpty(drainTargetConfig);
88
+ }
75
89
  }
76
90
  }
77
91
 
@@ -99,6 +113,14 @@ function indexPrefixEmpty ({ indexPrefix }) {
99
113
  return indexPrefix == null;
100
114
  }
101
115
 
116
+ function structuredDataParametersExist ({ structuredDataParameters }) {
117
+ return structuredDataParameters != null;
118
+ }
119
+
120
+ function structuredDataParametersEmpty ({ structuredDataParameters }) {
121
+ return structuredDataParameters == null;
122
+ }
123
+
102
124
  function keyExist ({ apiKey }) {
103
125
  return apiKey != null;
104
126
  }