Skip to content

Commit

Permalink
Add device start-service and stop-service commands
Browse files Browse the repository at this point in the history
Change-type: minor
  • Loading branch information
mlveggo committed Oct 29, 2023
1 parent c785d01 commit 0169a1a
Show file tree
Hide file tree
Showing 6 changed files with 366 additions and 2 deletions.
2 changes: 2 additions & 0 deletions automation/capitanodoc/capitanodoc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ const capitanoDoc = {
'build/commands/device/restart.js',
'build/commands/device/rm.js',
'build/commands/device/shutdown.js',
'build/commands/device/start-service.js',
'build/commands/device/stop-service.js',
],
},
{
Expand Down
2 changes: 1 addition & 1 deletion completion/_balena
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ _balena() {
app_cmds=( create )
block_cmds=( create )
config_cmds=( generate inject read reconfigure write )
device_cmds=( deactivate identify init local-mode move os-update pin public-url purge reboot register rename restart rm shutdown track-fleet )
device_cmds=( deactivate identify init local-mode move os-update pin public-url purge reboot register rename restart rm shutdown start-service stop-service track-fleet )
devices_cmds=( supported )
env_cmds=( add rename rm )
fleet_cmds=( create pin purge rename restart rm track-latest )
Expand Down
2 changes: 1 addition & 1 deletion completion/balena-completion.bash
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ _balena_complete()
app_cmds="create"
block_cmds="create"
config_cmds="generate inject read reconfigure write"
device_cmds="deactivate identify init local-mode move os-update pin public-url purge reboot register rename restart rm shutdown track-fleet"
device_cmds="deactivate identify init local-mode move os-update pin public-url purge reboot register rename restart rm shutdown start-service stop-service track-fleet"
devices_cmds="supported"
env_cmds="add rename rm"
fleet_cmds="create pin purge rename restart rm track-latest"
Expand Down
52 changes: 52 additions & 0 deletions docs/balena-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ are encouraged to regularly update the balena CLI to the latest version.
- [device restart <uuid>](#device-restart-uuid)
- [device rm <uuid(s)>](#device-rm-uuid-s)
- [device shutdown <uuid>](#device-shutdown-uuid)
- [device start-service <uuid>](#device-start-service-uuid)
- [device stop-service <uuid>](#device-stop-service-uuid)

- Releases

Expand Down Expand Up @@ -1157,6 +1159,56 @@ the uuid of the device to shutdown

force action if the update lock is set

## device start-service <uuid>

Start containers on a device.
The --service flag should be provided to start services' containers.

Multiple devices and services may be specified with a comma-separated list
of values (no spaces).

Examples:

$ balena device start-service 23c73a1 --service myService
$ balena device start-service 23c73a1 -s myService1,myService2

### Arguments

#### UUID

comma-separated list (no blank spaces) of device UUIDs

### Options

#### -s, --service SERVICE

comma-separated list (no blank spaces) of service names

## device stop-service <uuid>

Stop containers on a device.
The --service flag should be provided to stop services' containers.

Multiple devices and services may be specified with a comma-separated list
of values (no spaces).

Examples:

$ balena device stop-service 23c73a1 --service myService
$ balena device stop-service 23c73a1 -s myService1,myService2

### Arguments

#### UUID

comma-separated list (no blank spaces) of device UUIDs

### Options

#### -s, --service SERVICE

comma-separated list (no blank spaces) of service names

# Releases

## releases <fleet>
Expand Down
155 changes: 155 additions & 0 deletions lib/commands/device/start-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/**
* @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, Args } from '@oclif/core';
import Command from '../../command';
import * as cf from '../../utils/common-flags';
import { getBalenaSdk, getCliUx, stripIndent } from '../../utils/lazy';
import type {
BalenaSDK,
DeviceWithServiceDetails,
CurrentServiceWithCommit,
} from 'balena-sdk';

export default class DeviceStartServiceCmd extends Command {
public static description = stripIndent`
Start containers on a device.
Start containers on a device.
The --service flag should be provided to start services' containers.
Multiple devices and services may be specified with a comma-separated list
of values (no spaces).
`;
public static examples = [
'$ balena device start-service 23c73a1 --service myService',
'$ balena device start-service 23c73a1 -s myService1,myService2',
];

public static args = {
uuid: Args.string({
description: 'comma-separated list (no blank spaces) of device UUIDs',
required: true,
}),
};

public static usage = 'device start-service <uuid>';

public static flags = {
service: Flags.string({
description: 'comma-separated list (no blank spaces) of service names',
char: 's',
}),
help: cf.help,
};

public static authenticated = true;

public async run() {
const { args: params, flags: options } = await this.parse(
DeviceStartServiceCmd,
);

const balena = getBalenaSdk();
const ux = getCliUx();

const deviceUuids = params.uuid.split(',');
const serviceNames = options.service?.split(',');

// Iterate sequentially through deviceUuids.
// We may later want to add a batching feature,
// so that n devices are processed in parallel
for (const uuid of deviceUuids) {
ux.action.start(`Starting services on device ${uuid}`);
if (serviceNames) {
await this.startServices(balena, uuid, serviceNames);
}
ux.action.stop();
}
}

async startServices(
balena: BalenaSDK,
deviceUuid: string,
serviceNames: string[],
) {
const { ExpectedError, instanceOf } = await import('../../errors');
const { getExpandedProp } = await import('../../utils/pine');

// Get device
let device: DeviceWithServiceDetails<CurrentServiceWithCommit>;
try {
device = await balena.models.device.getWithServiceDetails(deviceUuid, {
$expand: {
is_running__release: { $select: 'commit' },
},
});
} catch (e) {
const { BalenaDeviceNotFound } = await import('balena-errors');
if (instanceOf(e, BalenaDeviceNotFound)) {
throw new ExpectedError(`Device ${deviceUuid} not found.`);
} else {
throw e;
}
}

const activeReleaseCommit = getExpandedProp(
device.is_running__release,
'commit',
);

// Check specified services exist on this device before startinganything
serviceNames.forEach((service) => {
if (!device.current_services[service]) {
throw new ExpectedError(
`Service ${service} not found on device ${deviceUuid}.`,
);
}
});

// Start services
const startPromises: Array<Promise<void>> = [];
for (const serviceName of serviceNames) {
const service = device.current_services[serviceName];
// Each service is an array of `CurrentServiceWithCommit`
// because when service is updating, it will actually hold 2 services
// Target commit matching `device.is_running__release`
const serviceContainer = service.find((s) => {
return s.commit === activeReleaseCommit;
});

if (serviceContainer) {
startPromises.push(
balena.models.device.startService(
deviceUuid,
serviceContainer.image_id,
),
);
}
}

try {
await Promise.all(startPromises);
} catch (e) {
if (e.message.toLowerCase().includes('no online device')) {
throw new ExpectedError(`Device ${deviceUuid} is not online.`);
} else {
throw e;
}
}
}
}
Loading

0 comments on commit 0169a1a

Please sign in to comment.