Skip to content

Commit

Permalink
improve region choice (#342)
Browse files Browse the repository at this point in the history
- add autocomplete
- allow free input in addition to preset choices
- separate S3 region from Lightsail region. could not find a way to dynamically retrieve available regions for S3 or Lightsail.
  • Loading branch information
Roy Razon authored Nov 15, 2023
1 parent f573a78 commit cb66c36
Show file tree
Hide file tree
Showing 13 changed files with 147 additions and 42 deletions.
2 changes: 2 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@preevy/driver-lightsail": "0.0.56",
"@preevy/plugin-github-pr-link": "0.0.56",
"inquirer": "^8.0.0",
"inquirer-autocomplete-prompt": "^2.0.0",
"iter-tools-es": "^7.5.3",
"lodash": "^4.17.21",
"shell-escape": "^0.2.0",
Expand All @@ -39,6 +40,7 @@
"devDependencies": {
"@oclif/test": "^2.3.4",
"@types/inquirer": "^8.0.0",
"@types/inquirer-autocomplete-prompt": "^3.0.3",
"@types/lodash": "^4.14.192",
"@types/node": "18",
"@types/shell-escape": "^0.2.1",
Expand Down
17 changes: 12 additions & 5 deletions packages/cli/src/fs.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { fsTypeFromUrl, localFsFromUrl } from '@preevy/core'
import { googleCloudStorageFs, defaultBucketName as gsDefaultBucketName, defaultProjectId as defaultGceProjectId } from '@preevy/driver-gce'
import { s3fs, defaultBucketName as s3DefaultBucketName, AWS_REGIONS, awsUtils } from '@preevy/driver-lightsail'
import { s3fs, defaultBucketName as s3DefaultBucketName, awsUtils, S3_REGIONS } from '@preevy/driver-lightsail'
import inquirer from 'inquirer'
import inquirerAutoComplete from 'inquirer-autocomplete-prompt'
import { DriverName } from './drivers'
import ambientAwsAccountId = awsUtils.ambientAccountId

inquirer.registerPrompt('autocomplete', inquirerAutoComplete)

export const fsFromUrl = async (url: string, localBaseDir: string) => {
const fsType = fsTypeFromUrl(url)
if (fsType === 'local') {
Expand Down Expand Up @@ -57,12 +60,16 @@ export const chooseFs: Record<FsType, FsChooser> = {
// eslint-disable-next-line no-use-before-define
const { region, bucket } = await inquirer.prompt<{ region: string; bucket: string }>([
{
type: 'list',
type: 'autocomplete',
name: 'region',
message: 'S3 bucket region',
choices: AWS_REGIONS,
default: driver?.name === 'lightsail' ? driver.flags.region as string : 'us-east-1',
},
source: async (_opts, input) => S3_REGIONS.filter(r => !input || r.includes(input.toLowerCase())),
default: driver?.name === 'lightsail' && S3_REGIONS.includes(driver.flags.region as string)
? driver.flags.region as string
: 'us-east-1',
suggestOnly: true,
filter: i => i.toLowerCase(),
} as inquirerAutoComplete.AutocompleteQuestionOptions,
{
type: 'input',
name: 'bucket',
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
"@preevy/compose-tunnel-agent": "0.0.56",
"chalk": "^4.1.2",
"fast-safe-stringify": "^2.1.1",
"inquirer": "^8.0.0",
"is-stream": "^2.0.1",
"iter-tools-es": "^7.5.3",
"jose": "^4.14.4",
Expand Down Expand Up @@ -41,6 +40,7 @@
"devDependencies": {
"@jest/globals": "29.7.0",
"@types/inquirer": "^8.0.0",
"@types/inquirer-autocomplete-prompt": "^3.0.3",
"@types/is-stream": "^2.0.0",
"@types/lodash": "^4.14.192",
"@types/node": "18",
Expand Down
3 changes: 3 additions & 0 deletions packages/driver-azure/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@
"@oclif/core": "^2",
"@preevy/core": "0.0.56",
"inquirer": "^8.0.0",
"inquirer-autocomplete-prompt": "^2.0.0",
"iter-tools-es": "^7.5.3",
"lodash": "^4.17.21"
},
"devDependencies": {
"@types/azure": "^0.9.20",
"@types/inquirer": "^8.0.0",
"@types/inquirer-autocomplete-prompt": "^3.0.3",
"@types/lodash": "^4.14.192",
"@types/node": "18",
"@typescript-eslint/eslint-plugin": "6.7.4",
Expand Down
1 change: 1 addition & 0 deletions packages/driver-azure/src/driver/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export const REGIONS = [
'centraluseuap',
'eastus2euap',
'qatarcentral',
'israelcentral',
]

type VMInstance = {
Expand Down
15 changes: 10 additions & 5 deletions packages/driver-azure/src/driver/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Flags, Interfaces } from '@oclif/core'
import { asyncFirst, asyncMap } from 'iter-tools-es'
import { ListQuestion, Question } from 'inquirer'
import inquirer, { Question } from 'inquirer'
import inquirerAutoComplete from 'inquirer-autocomplete-prompt'
import { InferredFlags } from '@oclif/core/lib/interfaces'
import { Resource, VirtualMachine } from '@azure/arm-compute'
import { inspect } from 'util'
Expand All @@ -26,6 +27,8 @@ import { Client, client as createClient, REGIONS } from './client'
import { CUSTOMIZE_BARE_MACHINE } from './scripts'
import { AzureCustomTags, extractResourceGroupNameFromId } from './vm-creation-utils'

inquirer.registerPrompt('autocomplete', inquirerAutoComplete)

type RootObjectDetailsError = {
code: string
message: string
Expand Down Expand Up @@ -142,13 +145,15 @@ const flags = {

type FlagTypes = Omit<Interfaces.InferredFlags<typeof flags>, 'json'>

const questions = async (): Promise<(Question | ListQuestion)[]> => [
const questions = async (): Promise<Question[]> => [
{
type: 'list',
type: 'autocomplete',
name: 'region',
message: flags.region.description,
choices: REGIONS,
},
source: async (_opts, input) => !input || REGIONS.filter(r => r.includes(input.toLowerCase())),
suggestOnly: true,
filter: i => i.toLowerCase(),
} as inquirerAutoComplete.AutocompleteQuestionOptions,
{
type: 'input',
name: 'subscription-id',
Expand Down
3 changes: 3 additions & 0 deletions packages/driver-gce/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@
"google-auth-library": "^8.7.0",
"google-gax": "^4.0.4",
"inquirer": "^8.0.0",
"inquirer-autocomplete-prompt": "^2.0.0",
"iter-tools-es": "^7.5.3",
"lodash": "^4.17.21"
},
"devDependencies": {
"@types/inquirer": "^8.0.0",
"@types/inquirer-autocomplete-prompt": "^3.0.3",
"@types/lodash": "^4.14.192",
"@types/node": "18",
"@typescript-eslint/eslint-plugin": "6.7.4",
Expand Down
53 changes: 31 additions & 22 deletions packages/driver-gce/src/driver/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Flags, Interfaces } from '@oclif/core'
import { asyncMap } from 'iter-tools-es'
import { InputQuestion, ListQuestion } from 'inquirer'
import inquirer, { ListQuestion, Question } from 'inquirer'
import inquirerAutoComplete from 'inquirer-autocomplete-prompt'
import {
MachineDriver,
SshMachine, MachineCreationDriver, MachineCreationDriverFactory, MachineDriverFactory,
Expand All @@ -15,11 +16,13 @@ import {
extractDefined,
PartialMachine,
} from '@preevy/core'
import { pick } from 'lodash'
import { memoize, pick } from 'lodash'
import createClient, { Client, Instance, availableRegions, defaultProjectId, instanceError, shortResourceName } from './client'
import { deserializeMetadata, metadataKey } from './metadata'
import { LABELS } from './labels'

inquirer.registerPrompt('autocomplete', inquirerAutoComplete)

type DriverContext = {
log: Logger
debug: boolean
Expand Down Expand Up @@ -108,26 +111,32 @@ const contextFromFlags = ({
zone,
})

const questions = async (): Promise<(InputQuestion | ListQuestion)[]> => [
{
type: 'input',
name: 'project',
default: defaultProjectId,
message: flags['project-id'].description,
},
{
type: 'list',
name: 'region',
choices: async ({ project }) => (await availableRegions(project)).map(r => r.name),
},
{
type: 'list',
name: 'zone',
choices: async (
{ project, region },
) => (await availableRegions(project)).find(r => r.name === region)?.zones ?? [],
},
]
const questions = async (): Promise<Question[]> => {
const memoizedAvailableRegions = memoize(availableRegions)
return [
{
type: 'input',
name: 'project',
default: defaultProjectId,
message: flags['project-id'].description,
},
{
type: 'autocomplete',
name: 'region',
source: async ({ project }, input) => (await memoizedAvailableRegions(project))
.filter(({ name }) => !input || name.includes(input.toLowerCase()))
.map(r => r.name),
filter: i => i.toLowerCase(),
} as inquirerAutoComplete.AutocompleteQuestionOptions,
{
type: 'list',
name: 'zone',
choices: memoize(
async ({ project, region }) => (await availableRegions(project)).find(r => r.name === region)?.zones ?? [],
),
} as ListQuestion,
]
}

const flagsFromAnswers = async (answers: Record<string, unknown>): Promise<FlagTypes> => ({
'project-id': answers.project as string,
Expand Down
3 changes: 3 additions & 0 deletions packages/driver-lightsail/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@
"@oclif/core": "^2",
"@preevy/core": "0.0.56",
"inquirer": "^8.0.0",
"inquirer-autocomplete-prompt": "^2.0.0",
"iter-tools-es": "^7.5.3",
"lodash": "^4.17.21"
},
"devDependencies": {
"@types/inquirer": "^8.0.0",
"@types/inquirer-autocomplete-prompt": "^3.0.3",
"@types/lodash": "^4.14.192",
"@types/node": "18",
"@typescript-eslint/eslint-plugin": "6.7.4",
Expand Down
16 changes: 10 additions & 6 deletions packages/driver-lightsail/src/driver/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { asyncConcat, asyncMap } from 'iter-tools-es'
import { Flags } from '@oclif/core'
import { randomBytes } from 'crypto'
import { InferredFlags } from '@oclif/core/lib/interfaces'
import { ListQuestion, Question } from 'inquirer'
import inquirer, { Question } from 'inquirer'
import inquirerAutoComplete from 'inquirer-autocomplete-prompt'
import {
telemetryEmitter,
SshMachine, MachineDriver, MachineCreationDriver, MachineCreationDriverFactory, machineResourceType,
Expand All @@ -19,6 +20,8 @@ import { CURRENT_MACHINE_VERSION, TAGS, requiredTag } from './tags'

export { BundleId, BUNDLE_IDS, bundleIdFromString as bundleId }

inquirer.registerPrompt('autocomplete', inquirerAutoComplete)

type ResourceType = typeof machineResourceType | 'snapshot' | 'keypair'

const machineFromInstance = (
Expand Down Expand Up @@ -110,7 +113,6 @@ const flags = {
description: 'AWS region in which resources will be provisioned',
required: true,
env: 'AWS_REGION',
options: REGIONS.map(r => r),
}),
} as const

Expand All @@ -120,14 +122,16 @@ const contextFromFlags = ({ region }: FlagTypes): { region: string } => ({
region: region as string,
})

const questions = async (): Promise<(Question | ListQuestion)[]> => [
const questions = async (): Promise<Question[]> => [
{
type: 'list',
type: 'autocomplete',
name: 'region',
default: process.env.AWS_REGION ?? 'us-east-1',
message: flags.region.description,
choices: flags.region.options,
},
source: async (_opts, input) => REGIONS.filter(r => !input || r.includes(input.toLowerCase())),
suggestOnly: true,
filter: i => i.toLowerCase(),
} as inquirerAutoComplete.AutocompleteQuestionOptions,
]

const flagsFromAnswers = async (answers: Record<string, unknown>): Promise<FlagTypes> => ({
Expand Down
35 changes: 35 additions & 0 deletions packages/driver-lightsail/src/fs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,38 @@ export const s3fs = async (s3Url: string): Promise<VirtualFS> => {
},
}
}

export const S3_REGIONS = [
'us-east-2',
'us-east-1',
'us-west-1',
'us-west-2',
'af-south-1',
'ap-east-1',
'ap-south-2',
'ap-southeast-3',
'ap-southeast-4',
'ap-south-1',
'ap-northeast-3',
'ap-northeast-2',
'ap-southeast-1',
'ap-southeast-2',
'ap-northeast-1',
'ca-central-1',
'cn-north-1',
'cn-northwest-1',
'eu-central-1',
'eu-west-1',
'eu-west-2',
'eu-south-1',
'eu-west-3',
'eu-north-1',
'eu-south-2',
'eu-central-2',
'me-south-1',
'me-central-1',
'il-central-1',
'sa-east-1',
'us-gov-east-1',
'us-gov-west-1',
]
4 changes: 2 additions & 2 deletions packages/driver-lightsail/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ import lightsail from './driver'

export default lightsail
export * as awsUtils from './aws-utils'
export { REGIONS as AWS_REGIONS } from './driver/client'
export { s3fs, defaultBucketName } from './fs'
export { REGIONS as LIGHTSAIL_REGIONS } from './driver/client'
export { s3fs, defaultBucketName, S3_REGIONS } from './fs'
35 changes: 34 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3773,6 +3773,21 @@
dependencies:
"@types/node" "*"

"@types/inquirer-autocomplete-prompt@^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/inquirer-autocomplete-prompt/-/inquirer-autocomplete-prompt-3.0.3.tgz#8bbb3095454cb2ac9a26865c694e32fc317d7640"
integrity sha512-OQCW09mEECgvhcppbQRgZSmWskWv58l+WwyUvWB1oxTu3CZj8keYSDZR9U8owUzJ5Zeux5kacN9iVPJLXcoLXg==
dependencies:
"@types/inquirer" "*"

"@types/inquirer@*":
version "9.0.7"
resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-9.0.7.tgz#61bb8d0e42f038b9a1738b08fba7fa98ad9b4b24"
integrity sha512-Q0zyBupO6NxGRZut/JdmqYKOnN95Eg5V8Csg3PGKkP+FnvsUZx1jAyK7fztIszxxMuoBA6E3KXWvdZVXIpx60g==
dependencies:
"@types/through" "*"
rxjs "^7.2.0"

"@types/inquirer@^8.0.0":
version "8.2.9"
resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.9.tgz#bb29e7d2358e3af7d9f4d6c6410320498b428d48"
Expand Down Expand Up @@ -8045,6 +8060,17 @@ [email protected], init-package-json@^3.0.2:
validate-npm-package-license "^3.0.4"
validate-npm-package-name "^4.0.0"

inquirer-autocomplete-prompt@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/inquirer-autocomplete-prompt/-/inquirer-autocomplete-prompt-2.0.1.tgz#72868aada4d9d36814a6054cbd1ececc63aab0c6"
integrity sha512-jUHrH0btO7j5r8DTQgANf2CBkTZChoVySD8zF/wp5fZCOLIuUbleXhf4ZY5jNBOc1owA3gdfWtfZuppfYBhcUg==
dependencies:
ansi-escapes "^4.3.2"
figures "^3.2.0"
picocolors "^1.0.0"
run-async "^2.4.1"
rxjs "^7.5.4"

[email protected]:
version "8.2.4"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.4.tgz#ddbfe86ca2f67649a67daa6f1051c128f684f0b4"
Expand Down Expand Up @@ -12038,7 +12064,7 @@ rimraf@^5.0.0:
dependencies:
glob "^10.3.7"

run-async@^2.0.0, run-async@^2.4.0:
run-async@^2.0.0, run-async@^2.4.0, run-async@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==
Expand All @@ -12062,6 +12088,13 @@ rxjs@^7.0.0, rxjs@^7.2.0, rxjs@^7.5.5:
dependencies:
tslib "^2.1.0"

rxjs@^7.5.4:
version "7.8.1"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543"
integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==
dependencies:
tslib "^2.1.0"

safe-array-concat@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c"
Expand Down

0 comments on commit cb66c36

Please sign in to comment.