mirror of
https://github.com/balena-io/balena-cli.git
synced 2025-01-25 22:00:30 +00:00
Initial version of command
This commit is contained in:
parent
f635f648da
commit
e10a974a9f
49
lib/commands/instance/index.ts
Normal file
49
lib/commands/instance/index.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2016-2020 Balena Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { flags } from '@oclif/command';
|
||||||
|
import Command from '../../command';
|
||||||
|
import * as cf from '../../utils/common-flags';
|
||||||
|
import { stripIndent } from '../../utils/lazy';
|
||||||
|
|
||||||
|
interface FlagsDef {
|
||||||
|
help: void;
|
||||||
|
v13: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class InstanceCmd extends Command {
|
||||||
|
public static description = stripIndent`
|
||||||
|
Initialize a new cloud instance running balenaOS
|
||||||
|
|
||||||
|
A config.json must first be generated using the 'balena config generate' command
|
||||||
|
`;
|
||||||
|
public static examples = ['$ balena instance init'];
|
||||||
|
|
||||||
|
public static usage = 'instance [COMMAND]';
|
||||||
|
|
||||||
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
|
help: cf.help,
|
||||||
|
v13: cf.v13,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static authenticated = true;
|
||||||
|
public static primary = true;
|
||||||
|
|
||||||
|
public async run() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
175
lib/commands/instance/init.ts
Normal file
175
lib/commands/instance/init.ts
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2016-2020 Balena Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { IArg } from '@oclif/parser/lib/args';
|
||||||
|
import Command from '../../command';
|
||||||
|
import { stripIndent } from '../../utils/lazy';
|
||||||
|
import {
|
||||||
|
applicationIdInfo,
|
||||||
|
} from '../../utils/messages';
|
||||||
|
|
||||||
|
import * as fs from 'fs'
|
||||||
|
import * as fetch from 'isomorphic-fetch'
|
||||||
|
import * as cf from '../../utils/common-flags';
|
||||||
|
import { flags } from '@oclif/command';
|
||||||
|
import { uniqueId } from 'lodash';
|
||||||
|
import { json } from 'body-parser';
|
||||||
|
|
||||||
|
interface FlagsDef {
|
||||||
|
help: void;
|
||||||
|
v13: boolean;
|
||||||
|
apiKey?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class InstanceInitCmd extends Command {
|
||||||
|
public static description = stripIndent`
|
||||||
|
Initialize an instance with balenaOS.
|
||||||
|
|
||||||
|
Initialize a device by downloading the OS image of the specified fleet
|
||||||
|
and writing it to an SD Card.
|
||||||
|
|
||||||
|
If the --fleet option is omitted, it will be prompted for interactively.
|
||||||
|
|
||||||
|
${applicationIdInfo.split('\n').join('\n\t\t')}
|
||||||
|
`;
|
||||||
|
|
||||||
|
public static examples = [
|
||||||
|
'$ balena instance init',
|
||||||
|
'$ balena instance init --fleet MyFleet',
|
||||||
|
'$ balena instance init -f myorg/myfleet',
|
||||||
|
];
|
||||||
|
|
||||||
|
public static usage = 'instance init';
|
||||||
|
|
||||||
|
public static args: Array<IArg<any>> = [
|
||||||
|
{
|
||||||
|
name: 'configFile',
|
||||||
|
description: 'the config.json file path',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
|
help: cf.help,
|
||||||
|
v13: cf.v13,
|
||||||
|
apiKey: flags.string({
|
||||||
|
description: 'digitalocean api key',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
public static authenticated = true;
|
||||||
|
|
||||||
|
public async run() {
|
||||||
|
const { args: params, flags: options } = this.parse<FlagsDef, { configFile: string }>(InstanceInitCmd);
|
||||||
|
|
||||||
|
// Check if the config file exists
|
||||||
|
console.log('Reading config file')
|
||||||
|
const exists = fs.existsSync(params.configFile)
|
||||||
|
if (!exists) {
|
||||||
|
console.log('Config file does not exist, exiting...')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const configFile = JSON.parse(fs.readFileSync(params.configFile).toString())
|
||||||
|
|
||||||
|
console.log('Creating digitalocean image')
|
||||||
|
|
||||||
|
if (!options.apiKey) {
|
||||||
|
console.log('Missing digitalocean api key, please provide with --apiKey <api_key>')
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Uploading image...')
|
||||||
|
let res = await fetch('https://api.digitalocean.com/v2/images', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json',
|
||||||
|
authorization: `Bearer ${options.apiKey}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: 'balenaOS',
|
||||||
|
url: `https://api.balena-cloud.com/download?fileType=.gz&appId=${configFile.applicationID}&deviceType=qemux86-64`,
|
||||||
|
distribution: 'Unknown',
|
||||||
|
region: 'nyc1',
|
||||||
|
description: 'balenaOS',
|
||||||
|
tags: [
|
||||||
|
'balenaOS'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
console.log('Image sent.')
|
||||||
|
|
||||||
|
let responseBody = await res.json()
|
||||||
|
const imageID = responseBody.image.id
|
||||||
|
do {
|
||||||
|
console.log('Checking image status...')
|
||||||
|
await new Promise((r) => setTimeout(() => r(null), 2000)) // Sleep for 2 seconds
|
||||||
|
res = await fetch(`https://api.digitalocean.com/v2/images/${imageID}`, {
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${options.apiKey}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
responseBody = await res.json()
|
||||||
|
} while (responseBody.image.status !== 'available')
|
||||||
|
console.log('Image available.')
|
||||||
|
|
||||||
|
console.log('Getting ssh key info')
|
||||||
|
res = await fetch('https://api.digitalocean.com/v2/account/keys', {
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${options.apiKey}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
responseBody = await res.json()
|
||||||
|
|
||||||
|
const sshKeyID = responseBody.ssh_keys[0].id
|
||||||
|
|
||||||
|
console.log('Creating droplet...')
|
||||||
|
res = await fetch('https://api.digitalocean.com/v2/droplets', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: uniqueId(),
|
||||||
|
region: 'nyc1',
|
||||||
|
size: 's-2vcpu-4gb',
|
||||||
|
image: imageID,
|
||||||
|
ssh_keys: [sshKeyID],
|
||||||
|
user_data: JSON.stringify(configFile),
|
||||||
|
tags: [
|
||||||
|
'balenaOS'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
responseBody = await res.json()
|
||||||
|
const createURL = responseBody.links.actions.filter((link: any) => link.rel === 'created')[0]
|
||||||
|
if (!createURL) {
|
||||||
|
console.error('Failed to get a create url!')
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
console.log('Checking droplet creation status...')
|
||||||
|
await new Promise((r) => setTimeout(() => r(null), 2000)) // Sleep for 2 seconds
|
||||||
|
res = await fetch(createURL, {
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${options.apiKey}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
responseBody = await res.json()
|
||||||
|
} while (responseBody.action.status !== 'completed')
|
||||||
|
|
||||||
|
console.log('Done! the device should show soon!')
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -232,5 +232,6 @@ See: https://git.io/JRHUW#deprecation-policy`,
|
|||||||
'join',
|
'join',
|
||||||
'leave',
|
'leave',
|
||||||
'scan',
|
'scan',
|
||||||
|
'instance',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
20
npm-shrinkwrap.json
generated
20
npm-shrinkwrap.json
generated
@ -2583,6 +2583,12 @@
|
|||||||
"is-root": "*"
|
"is-root": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/isomorphic-fetch": {
|
||||||
|
"version": "0.0.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/isomorphic-fetch/-/isomorphic-fetch-0.0.35.tgz",
|
||||||
|
"integrity": "sha512-DaZNUvLDCAnCTjgwxgiL1eQdxIKEpNLOlTNtAgnZc50bG2copGhRrFN9/PxPBuJe+tZVLCbQ7ls0xveXVRPkvw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/js-yaml": {
|
"@types/js-yaml": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.2.tgz",
|
||||||
@ -10365,6 +10371,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
|
||||||
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
|
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
|
||||||
},
|
},
|
||||||
|
"isomorphic-fetch": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==",
|
||||||
|
"requires": {
|
||||||
|
"node-fetch": "^2.6.1",
|
||||||
|
"whatwg-fetch": "^3.4.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"isstream": {
|
"isstream": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||||
@ -18492,6 +18507,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.1.1.tgz",
|
||||||
"integrity": "sha512-Czi3fG883e96T4DLEPRvufrF2ydhOOW1+1a6c3gNjH2aIh50DNFBdfwh2AKoOf1rXvpvavAoA11Qdq9+BKjE0Q=="
|
"integrity": "sha512-Czi3fG883e96T4DLEPRvufrF2ydhOOW1+1a6c3gNjH2aIh50DNFBdfwh2AKoOf1rXvpvavAoA11Qdq9+BKjE0Q=="
|
||||||
},
|
},
|
||||||
|
"whatwg-fetch": {
|
||||||
|
"version": "3.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz",
|
||||||
|
"integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA=="
|
||||||
|
},
|
||||||
"which": {
|
"which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
@ -135,6 +135,7 @@
|
|||||||
"@types/http-proxy": "^1.17.7",
|
"@types/http-proxy": "^1.17.7",
|
||||||
"@types/intercept-stdout": "^0.1.0",
|
"@types/intercept-stdout": "^0.1.0",
|
||||||
"@types/is-root": "^2.1.2",
|
"@types/is-root": "^2.1.2",
|
||||||
|
"@types/isomorphic-fetch": "0.0.35",
|
||||||
"@types/js-yaml": "^4.0.2",
|
"@types/js-yaml": "^4.0.2",
|
||||||
"@types/jsonwebtoken": "^8.5.4",
|
"@types/jsonwebtoken": "^8.5.4",
|
||||||
"@types/klaw": "^3.0.2",
|
"@types/klaw": "^3.0.2",
|
||||||
@ -244,6 +245,7 @@
|
|||||||
"inquirer": "^7.3.3",
|
"inquirer": "^7.3.3",
|
||||||
"is-elevated": "^3.0.0",
|
"is-elevated": "^3.0.0",
|
||||||
"is-root": "^2.1.0",
|
"is-root": "^2.1.0",
|
||||||
|
"isomorphic-fetch": "^3.0.0",
|
||||||
"js-yaml": "^4.0.0",
|
"js-yaml": "^4.0.0",
|
||||||
"klaw": "^3.0.0",
|
"klaw": "^3.0.0",
|
||||||
"livepush": "^3.5.0",
|
"livepush": "^3.5.0",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user