Skip to content

Commit

Permalink
CLI: add driver override in compose file (#330)
Browse files Browse the repository at this point in the history
  • Loading branch information
Roy Razon authored Nov 2, 2023
1 parent 31076c5 commit 1bda1e0
Show file tree
Hide file tree
Showing 11 changed files with 189 additions and 53 deletions.
45 changes: 43 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ Visit The full documentation here: https://preevy.dev/
- [Notice on preview environments exposure](#notice-on-preview-environments-exposure)
- [Configuration files](#configuration-files)
- [Preevy-specific configuration](#preevy-specific-configuration)
- [Plugins](#plugins)
- [`driver`](#driver)
- [`drivers`](#drivers)
- [`plugins`](#plugins)
- [Plugins](#plugins-1)
- [Docs and support](#docs-and-support)
- [Telemetry](#telemetry)
<!--lint enable double-link-->
Expand Down Expand Up @@ -185,16 +188,54 @@ An additional option `--system-compose-file` can be used to specify paths to Com

### Preevy-specific configuration

Additional Preevy-specific configuration, if needed, can be specified by adding a `x-preevy` top-level element to the Compose file(s). Currently only the `plugins` section is supported:
Additional Preevy-specific configuration, if needed, can be specified by adding a `x-preevy` top-level element to the Compose file(s).

```yaml
services:
...
x-preevy:
driver: lightsail
drivers:
lightsail:
region: eu-central-1
kube-pod:
context: dev-cluster
plugins:
...
```
The following optional properties are supported:
### `driver`

<!--lint disable double-link-->
Override the default [driver](https://preevy.dev/category/drivers) to use for this Compose project.
Available values: `lightsail`, `gce`, `azure`, `kube-pod`.
<!--lint enable double-link-->

This value can be overridden per command execution using the `--driver` CLI flag.

### `drivers`

<!--lint disable double-link-->
Override the default the default options per driver for this Compose project. See the [specific driver documentation](https://preevy.dev/category/drivers).
<!--lint enable double-link-->

These values can be overridden per command execution using the specific driver CLI flags, e.g, `--lightsail-bundle-id=2xlarge_2_0`

Example:

```yaml
x-preevy:
drivers:
lightsail:
bundle-id: large_2_0
kube-pod:
context: dev-cluster
```

### `plugins`

<!--lint disable double-link-->
See [Plugins](#plugins) below.
<!--lint enable double-link-->
Expand Down
33 changes: 21 additions & 12 deletions packages/cli/src/driver-command.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Command, Flags, Interfaces } from '@oclif/core'
import { MachineConnection, MachineDriver, isPartialMachine, profileStore } from '@preevy/core'
import { pickBy } from 'lodash'
import { DriverFlags, DriverName, flagsForAllDrivers, machineDrivers, removeDriverPrefix } from './drivers'
import { DriverFlags, DriverName, FlagType, flagsForAllDrivers, machineDrivers, removeDriverPrefix } from './drivers'
import ProfileCommand from './profile-command'

// eslint-disable-next-line no-use-before-define
Expand All @@ -25,7 +25,7 @@ abstract class DriverCommand<T extends typeof Command> extends ProfileCommand<T>

public async init(): Promise<void> {
await super.init()
this.#driverName = this.flags.driver ?? this.profile.driver as DriverName
this.#driverName = this.flags.driver ?? this.preevyConfig?.driver as DriverName ?? this.profile.driver as DriverName
}

#driverName: DriverName | undefined
Expand All @@ -36,23 +36,32 @@ abstract class DriverCommand<T extends typeof Command> extends ProfileCommand<T>
return this.#driverName
}

protected async driverFlags<Name extends DriverName, Type extends FlagType>(
driver: Name,
type: Type
): Promise<DriverFlags<DriverName, Type>> {
const driverFlagNames = Object.keys(machineDrivers[driver][type])
const flagDefaults = pickBy(
{
...await profileStore(this.store).defaultFlags(driver),
...this.preevyConfig.drivers?.[driver] ?? {},
},
(_v, k) => driverFlagNames.includes(k),
) as DriverFlags<DriverName, Type>
return {
...flagDefaults,
...removeDriverPrefix<DriverFlags<DriverName, Type>>(driver, this.flags),
}
}

#driver: MachineDriver | undefined
async driver(): Promise<MachineDriver> {
if (this.#driver) {
return this.#driver
}
const { profile, driverName } = this
const driverFlagNames = Object.keys(machineDrivers[driverName].flags)
const defaultFlags = pickBy(
await profileStore(this.store).defaultFlags(driverName),
(_v, k) => driverFlagNames.includes(k),
)
const driverFlags = {
...defaultFlags,
...removeDriverPrefix<DriverFlags<DriverName, 'flags'>>(driverName, this.flags),
}
this.#driver = machineDrivers[driverName].factory({
flags: driverFlags as never,
flags: await this.driverFlags(driverName, 'flags') as never,
profile,
store: this.store,
log: this.logger,
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/drivers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type MachineDrivers = typeof machineDrivers

export type DriverName = keyof MachineDrivers

type FlagType = 'flags' | 'machineCreationFlags'
export type FlagType = 'flags' | 'machineCreationFlags'

export type DriverFlagName<
Name extends DriverName,
Expand Down
9 changes: 3 additions & 6 deletions packages/cli/src/machine-creation-driver-command.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Command, Flags, Interfaces } from '@oclif/core'
import { MachineCreationDriver, profileStore } from '@preevy/core'
import { MachineCreationDriver } from '@preevy/core'
import { BaseCommand } from '@preevy/cli-common'
import DriverCommand from './driver-command'
import { DriverFlags, DriverName, machineCreationflagsForAllDrivers, machineDrivers, removeDriverPrefix } from './drivers'
import { machineCreationflagsForAllDrivers, machineDrivers } from './drivers'

// eslint-disable-next-line no-use-before-define
export type Flags<T extends typeof Command> = Interfaces.InferredFlags<typeof MachineCreationDriverCommand['baseFlags'] & T['flags']>
Expand All @@ -24,11 +24,8 @@ abstract class MachineCreationDriverCommand<T extends typeof Command> extends Dr
return this.#machineCreationDriver
}
const { profile, driverName } = this
const defaultFlags = await profileStore(this.store).defaultFlags(driverName)
const specifiedFlags = removeDriverPrefix<DriverFlags<DriverName, 'machineCreationFlags'>>(this.driverName, this.flags)
const driverFlags = { ...defaultFlags, ...specifiedFlags }
this.#machineCreationDriver = machineDrivers[driverName].machineCreationFactory({
flags: driverFlags as never,
flags: await this.driverFlags(driverName, 'machineCreationFlags') as never,
profile,
store: this.store,
log: this.logger,
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ export type PreevyPluginConfig = {

export type PreevyConfig = {
plugins?: PreevyPluginConfig[]
driver?: string
drivers?: Record<string, Record<string, unknown>>
}
12 changes: 1 addition & 11 deletions packages/driver-azure/src/driver/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,26 +179,17 @@ const DEFAULT_VM_SIZE = 'Standard_B2s'

const machineCreationFlags = {
...flags,
region: Flags.string({
description: 'Microsoft Azure region in which resources will be provisioned',
required: true,
}),
'vm-size': Flags.string({
description: 'Machine type to be provisioned',
default: DEFAULT_VM_SIZE,
required: false,
}),
'resource-group-name': Flags.string({
description: 'Microsoft Azure resource group name',
required: true,
}),
} as const

type MachineCreationFlagTypes = Omit<InferredFlags<typeof machineCreationFlags>, 'json'>

type MachineCreationContext = DriverContext & {
vmSize?: string
resourceGroupId: string
metadata: MachineCreationFlagTypes
}

Expand Down Expand Up @@ -273,10 +264,9 @@ const factory: MachineDriverFactory<

const machineCreationContextFromFlags = (
f: MachineCreationFlagTypes,
): ReturnType<typeof contextFromFlags> & { vmSize: string; resourceGroupId: string } => ({
): ReturnType<typeof contextFromFlags> & { vmSize: string } => ({
...contextFromFlags(f),
vmSize: f['vm-size'],
resourceGroupId: f['resource-group-name'],
})

const machineCreationFactory: MachineCreationDriverFactory<
Expand Down
2 changes: 1 addition & 1 deletion packages/driver-kube-pod/src/driver/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
import { Package } from './common'
import { logError } from './log-error'

export const loadKubeConfig = (kubeconfig?: string, context?:string) => {
export const loadKubeConfig = (kubeconfig?: string, context?: string) => {
const kc = new k8s.KubeConfig()
if (kubeconfig) {
kc.loadFromFile(kubeconfig)
Expand Down
34 changes: 31 additions & 3 deletions site/docs/drivers/aws-lightsail.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,41 @@ title: AWS Lightsail Driver

# AWS Lightsail Driver

Preevy can provision virtual machines on AWS Lightsail using the `aws-lightsail` driver.
Preevy can provision virtual machines on AWS Lightsail using the `lightsail` driver.
[AWS lightsail](https://aws.amazon.com/lightsail) is Amazon's cost-effective solution for VMs in the cloud.
AWS lightsail provisioning time for a VM is usually around 2 minutes, and its cost can be as low as $3.50 per month making them suitable for preview environments at scale.


### Supported flags
- `--aws-region` - The AWS region to use.
### Supported options

| option | flag | description | required | default |
| ------ | ---- | ----------- | -------- | ------- |
| `region` | `--lightsail-region` | AWS region in which resources will be provisioned | required | (none) |
| `bundle-id` | `--lightsail-bundle-id` | Lightsail bundle ID (size of instance) to provision | optional | `medium_2_0` |
| `availability-zone` | `--lightsail-availability-zone` | AWS zone to provision resources in region | optional | (first AZ in zone) |

### Overriding options

Similar to other drivers, options are saved in the Preevy profile to be used as default values for all operations.

Options can be overridden for a specific compose file by adding them to the `x-preevy` section:

```yaml
services:
...
x-preevy:
driver: lightsail
drivers:
lightsail:
# use a larger instance for this project
bundle-id: xlarge_2_0
```
Options can also be overridden using a CLI flag per command execution:
```bash
preevy up ---lightsail-bundle-id=xlarge_2_0
```

### Credentials Configuration
Preevy uses the AWS JS SDK which supports multiple ways of configuring credentials, according to the [credentials provider chain](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/setting-credentials-node.html).
Expand Down
34 changes: 29 additions & 5 deletions site/docs/drivers/azure.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,35 @@ title: Microsoft Azure Driver
Preevy can provision virtual machines on Microsoft Azure using the `azure` driver.
Microsoft Azure also offers free 12 months for new users which is suited for trying out preevy.

### Supported flags
- `--azure-region` - Microsoft Azure region in which resources will be provisioned
- `--azure-resource-group-name` - Microsoft Azure resource group name
- `--azure-subscription-id` - Microsoft Azure subscription id
- `--azure-vm-size` - Machine type to be provisioned, defaults to `Standard_B2s`
### Supported options

| option | flag | description | required | default |
| ------ | ---- | ----------- | -------- | ------- |
| `region` | `--azure-region` | Microsoft Azure region in which resources will be provisioned | required | (none) |
| `subscription-id` | `--azure-subscription-id` | Microsoft Azure subsription ID | required | (none) |
| `vm-size` | `--azure-vm-size` | Machine type to be provisioned | optional | `Standard_B2s` |

### Overriding options

Similar to other drivers, options are saved in the Preevy profile to be used as default values for all operations.

Options can be overridden for a specific compose file by adding them to the `x-preevy` section:

```yaml
services:
...
x-preevy:
driver: azure
drivers:
azure:
vm-size: DS3_v2
```
Options can also be overridden using a CLI flag per command execution:
```bash
preevy up --azure-vm-size=DS3_v2
```

### Credentials Configuration
Preevy uses the Microsoft Azure SDK which can obtain the application [default credentials](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/identity/identity#defaultazurecredential).
Expand Down
33 changes: 29 additions & 4 deletions site/docs/drivers/gcp-gce.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,35 @@ Preevy can provision virtual machines on GCP using the `gce` driver.
Google compute engine provisioning time for a VM is usually less than a minute, the default machine size in use is e2-small (2GB, 2vCpu) which costs around $12 per month.
Google compute also offer $300 free credit for new users which is suited for trying out preevy.

### Supported flags
- `--gce-machine-type` - Machine type to be provisioned.
- `--gce-profile-id` - Google Cloud project ID.
- `--gce-zone` - Google Cloud zone in which resources will be provisioned.
### Supported options

| option | flag | description | required | default |
| ------ | ---- | ----------- | -------- | ------- |
| `project-id` | `--gce-project-id` | Google Cloud project ID | required | (none) |
| `zone` | `--gce-zone` | Google Cloud zone in which resources will be provisioned | required | (none) |
| `machine-type` | `--gce-machine-type` | Machine type to be provisioned | optional | `e2-small` |

### Overriding options

Similar to other drivers, options are saved in the Preevy profile to be used as default values for all operations.

Options can be overridden for a specific compose file by adding them to the `x-preevy` section:

```yaml
services:
...
x-preevy:
driver: gce
drivers:
gce:
machine-type: e2-medium
```
Options can also be overridden using a CLI flag per command execution:
```bash
preevy up --gce-machine-type=e2-medium
```

### Credentials Configuration
Preevy uses the Google SDK which uses application default credentials (https://cloud.google.com/docs/authentication/application-default-credentials).
Expand Down
36 changes: 28 additions & 8 deletions site/docs/drivers/kube-pod.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,36 @@ Your services are still exposed using the Preevy Tunnel Service - there's no nee
- The [`kubectl`](https://kubernetes.io/docs/tasks/tools/#kubectl) tool needs to be installed and available in the PATH.
- By default, the driver runs a Pod with [`privileged: true` security context](https://kubernetes.io/docs/concepts/security/pod-security-standards/#privileged). In some cases, this requirement may be lifted by customizing the deployment template, see [below](#configuring-rootless-unprivileged-docker-in-docker).

## Supported flags
## Supported options

|flag|default|env var|description|
|---|--------|-------|-----------|
|`--kube-pod-namespace`|`default`| |Kubernetes namespace to provision resources in|
|`--kube-pod-kubeconfig`|`$HOME/.kube`| `KUBECONFIG` | path to a [`kubeconfig`](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/) file|
|`--kube-pod-template`|[default template](https://github.com/livecycle/preevy/blob/main/packages/driver-kube-pod/static/default-template.yaml.njk)| |path to a [nunjacks template](https://mozilla.github.io/nunjucks/templating.html) used to provision Kubernetes resources per environment. See [below](#customizing-the-provisioned-kubernetes-resources) for details|
|`--no-kube-pod-server-side-apply`| | | provision resources using client-side apply (CREATE/PATCH) instead of [server-side apply](https://kubernetes.io/docs/reference/using-api/server-side-apply/). Applies to `preevy up` only|
| option | flag | default | env var | description |
| ---- | --- | -------- | ------- | ----------- |
|`namespace`|`--kube-pod-namespace`|`default`| |Kubernetes namespace to provision resources in|
|`kubeconfig`|`--kube-pod-kubeconfig`|`$HOME/.kube`| `KUBECONFIG` | path to a [`kubeconfig`](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/) file|
|`pod-template`|`--kube-pod-template`|[default template](https://github.com/livecycle/preevy/blob/main/packages/driver-kube-pod/static/default-template.yaml.njk)| |path to a [nunjacks template](https://mozilla.github.io/nunjucks/templating.html) used to provision Kubernetes resources per environment. See [below](#customizing-the-provisioned-kubernetes-resources) for details|
|`server-side-apply`|`--[no-]kube-pod-server-side-apply`| true | | if true, provision resources using [server-side apply](https://kubernetes.io/docs/reference/using-api/server-side-apply/), else using client-side apply (CREATE/PATCH). Applies to `preevy up` only|

Similar to other drivers, flags are saved in the Preevy profile to be used as default values for all operations. They can be specified per command if the defaults need to be changed. For example, if a specific environment needs to be provisioned in a different Kubernetes namespace, specify `--kube-pod-namespace=other-namespace` when running the `preevy up` command.
### Overriding options

Similar to other drivers, options are saved in the Preevy profile to be used as default values for all operations.

Options can be overridden for a specific compose file by adding them to the `x-preevy` section:

```yaml
services:
...
x-preevy:
driver: kube-pod
drivers:
kube-pod:
namespace: other-namespace
```
Options can also be overridden using a CLI flag per command execution:
```bash
preevy up --kube-pod-namespace=other-namespace
```

## Customizing the provisioned Kubernetes resources

Expand Down

0 comments on commit 1bda1e0

Please sign in to comment.