Initial version of command

This commit is contained in:
Dan Goodman 2021-10-13 13:27:45 -04:00 committed by ab77
parent f635f648da
commit e10a974a9f
No known key found for this signature in database
GPG Key ID: D094F44E5E29445A
6 changed files with 247 additions and 0 deletions

View 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() {
}
}

View 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!')
}
}

View File

@ -232,5 +232,6 @@ See: https://git.io/JRHUW#deprecation-policy`,
'join',
'leave',
'scan',
'instance',
];
}

20
npm-shrinkwrap.json generated
View File

@ -2583,6 +2583,12 @@
"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": {
"version": "4.0.2",
"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",
"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": {
"version": "0.1.2",
"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",
"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": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",

View File

@ -135,6 +135,7 @@
"@types/http-proxy": "^1.17.7",
"@types/intercept-stdout": "^0.1.0",
"@types/is-root": "^2.1.2",
"@types/isomorphic-fetch": "0.0.35",
"@types/js-yaml": "^4.0.2",
"@types/jsonwebtoken": "^8.5.4",
"@types/klaw": "^3.0.2",
@ -244,6 +245,7 @@
"inquirer": "^7.3.3",
"is-elevated": "^3.0.0",
"is-root": "^2.1.0",
"isomorphic-fetch": "^3.0.0",
"js-yaml": "^4.0.0",
"klaw": "^3.0.0",
"livepush": "^3.5.0",

0
test.json Normal file
View File