Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
580 changes: 505 additions & 75 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,7 @@
"@aws-sdk/client-ssm": "<3.696.0",
"@aws-sdk/client-sso": "<3.696.0",
"@aws-sdk/client-sso-oidc": "<3.696.0",
"@aws-sdk/client-apprunner": "<3.696.0",
"@aws-sdk/client-cloudcontrol": "<3.696.0",
"@aws-sdk/credential-provider-env": "<3.696.0",
"@aws-sdk/credential-provider-process": "<3.696.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import { EcrRepositoryNode } from '../../../awsService/ecr/explorer/ecrRepositor
import { EcrTagNode } from '../../../awsService/ecr/explorer/ecrTagNode'

import { CreateAppRunnerServiceWizard } from '../wizards/apprunnerCreateServiceWizard'
import { DefaultAppRunnerClient } from '../../../shared/clients/apprunnerClient'
import { AppRunnerClient } from '../../../shared/clients/apprunner'
import { telemetry } from '../../../shared/telemetry/telemetry'
import { Result } from '../../../shared/telemetry/telemetry'

export async function createFromEcr(
node: EcrTagNode | EcrRepositoryNode,
client = new DefaultAppRunnerClient(node.regionCode)
client = new AppRunnerClient(node.regionCode)
): Promise<void> {
let telemetryResult: Result = 'Failed'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@ import { AWSTreeNodeBase } from '../../../shared/treeview/nodes/awsTreeNodeBase'
import { AppRunnerServiceNode } from './apprunnerServiceNode'
import { PlaceholderNode } from '../../../shared/treeview/nodes/placeholderNode'
import * as nls from 'vscode-nls'
import { AppRunnerClient } from '../../../shared/clients/apprunnerClient'
import { getPaginatedAwsCallIter } from '../../../shared/utilities/collectionUtils'
import { AppRunner } from 'aws-sdk'
import { AppRunnerClient, CreateServiceRequest, ServiceSummary } from '../../../shared/clients/apprunner'
import { PollingSet } from '../../../shared/utilities/pollingSet'

const localize = nls.loadMessageBundle()

export class AppRunnerNode extends AWSTreeNodeBase {
private readonly serviceNodes: Map<AppRunner.ServiceId, AppRunnerServiceNode> = new Map()
private readonly serviceNodes: Map<string, AppRunnerServiceNode> = new Map()
private readonly pollingSet: PollingSet<string> = new PollingSet(20000, this.refresh.bind(this))

public constructor(
Expand All @@ -43,30 +41,10 @@ export class AppRunnerNode extends AWSTreeNodeBase {
})
}

private async getServiceSummaries(request: AppRunner.ListServicesRequest = {}): Promise<AppRunner.Service[]> {
const iterator = getPaginatedAwsCallIter({
awsCall: async (request) => await this.client.listServices(request),
nextTokenNames: {
request: 'NextToken',
response: 'NextToken',
},
request,
})

const services: AppRunner.Service[] = []

while (true) {
const next = await iterator.next()

// eslint-disable-next-line unicorn/no-array-for-each
next.value.ServiceSummaryList.forEach((summary: AppRunner.Service) => services.push(summary))

if (next.done) {
break
}
}

return services
private async getServiceSummaries(): Promise<ServiceSummary[]> {
// TODO: avoid resolving all services at once.
const serviceCollection = this.client.paginateServices({})
return await serviceCollection.flatten().promise()
}

public async updateChildren(): Promise<void> {
Expand Down Expand Up @@ -107,7 +85,7 @@ export class AppRunnerNode extends AWSTreeNodeBase {
this.pollingSet.delete(id)
}

public async createService(request: AppRunner.CreateServiceRequest): Promise<void> {
public async createService(request: CreateServiceRequest): Promise<void> {
await this.client.createService(request)
this.refresh()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
*/

import AsyncLock from 'async-lock'
import { AppRunnerClient } from '../../../shared/clients/apprunnerClient'
import { AppRunner } from 'aws-sdk'
import { AppRunnerClient, ServiceSummary } from '../../../shared/clients/apprunner'
import { AppRunnerNode } from './apprunnerNode'

import { toArrayAsync, toMap } from '../../../shared/utilities/collectionUtils'
Expand All @@ -15,6 +14,7 @@ import { AWSResourceNode } from '../../../shared/treeview/nodes/awsResourceNode'
import * as nls from 'vscode-nls'
import { getLogger } from '../../../shared/logger/logger'
import { getIcon } from '../../../shared/icons'
import * as AppRunner from '@aws-sdk/client-apprunner'
import { CloudWatchLogsClient } from '../../../shared/clients/cloudWatchLogs'
import { LogGroup } from '@aws-sdk/client-cloudwatch-logs'
const localize = nls.loadMessageBundle()
Expand All @@ -41,7 +41,7 @@ export class AppRunnerServiceNode extends CloudWatchLogsBase implements AWSResou
constructor(
public readonly parent: AppRunnerNode,
private readonly client: AppRunnerClient,
private _info: AppRunner.Service,
private _info: ServiceSummary,
private currentOperation: AppRunner.OperationSummary & { Type?: ServiceOperation } = {},
cloudwatchClient = new CloudWatchLogsClient(client.regionCode)
) {
Expand All @@ -55,7 +55,7 @@ export class AppRunnerServiceNode extends CloudWatchLogsBase implements AWSResou
this.update(_info)
}

public get info(): Readonly<AppRunner.Service> {
public get info(): Readonly<ServiceSummary> {
return this._info
}

Expand All @@ -81,7 +81,7 @@ export class AppRunnerServiceNode extends CloudWatchLogsBase implements AWSResou
this.label = `${this._info.ServiceName} [${displayStatus}]`
}

public update(info: AppRunner.ServiceSummary | AppRunner.Service): void {
public update(info: ServiceSummary): void {
// update can be called multiple times during an event loop
// this would rarely cause the node's status to appear as 'Operation in progress'
this.lock
Expand Down Expand Up @@ -135,7 +135,7 @@ export class AppRunnerServiceNode extends CloudWatchLogsBase implements AWSResou
})
}

private updateInfo(info: AppRunner.ServiceSummary | AppRunner.Service): void {
private updateInfo(info: ServiceSummary): void {
if (info.Status === 'OPERATION_IN_PROGRESS' && this.currentOperation.Type === undefined) {
// Asynchronous since it is not currently possible for race-conditions to occur with updating operations
void this.updateOperation()
Expand Down Expand Up @@ -171,13 +171,14 @@ export class AppRunnerServiceNode extends CloudWatchLogsBase implements AWSResou
this.setOperation(this._info, resp.OperationId, 'START_DEPLOYMENT')
}

public setOperation(info: AppRunner.Service, id?: string, type?: ServiceOperation): void {
// eslint-disable-next-line @typescript-eslint/no-duplicate-type-constituents
public setOperation(info: ServiceSummary, id?: string, type?: ServiceOperation): void {
this.currentOperation.Id = id
this.currentOperation.Type = type
this.update(info)
}

public async describe(): Promise<AppRunner.Service> {
public async describe(): Promise<ServiceSummary> {
const resp = await this.client.describeService({ ServiceArn: this.arn })
this.update(resp.Service)
return this._info
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { AppRunner } from 'aws-sdk'
import * as nls from 'vscode-nls'
import { createCommonButtons, QuickInputToggleButton } from '../../../shared/ui/buttons'
import * as input from '../../../shared/ui/inputPrompter'
Expand All @@ -17,8 +16,9 @@ import { makeDeploymentButton } from './deploymentButton'
import { createExitPrompter } from '../../../shared/ui/common/exitPrompter'
import { IamClient } from '../../../shared/clients/iam'
import { DefaultEcrClient } from '../../../shared/clients/ecrClient'
import { DefaultAppRunnerClient } from '../../../shared/clients/apprunnerClient'
import { AppRunnerClient, CreateServiceRequest, SourceConfiguration } from '../../../shared/clients/apprunner'
import { getAppRunnerCreateServiceDocUrl } from '../../../shared/extensionUtilities'
import * as AppRunner from '@aws-sdk/client-apprunner'

const localize = nls.loadMessageBundle()

Expand Down Expand Up @@ -74,10 +74,10 @@ function createInstanceStep(): Prompter<AppRunner.InstanceConfiguration> {

function createSourcePrompter(
autoDeployButton: QuickInputToggleButton
): Prompter<AppRunner.CreateServiceRequest['SourceConfiguration']> {
): Prompter<CreateServiceRequest['SourceConfiguration']> {
const ecrPath = {
label: 'ECR',
data: { ImageRepository: {} } as AppRunner.SourceConfiguration,
data: { ImageRepository: {} } as SourceConfiguration,
detail: localize(
'AWS.apprunner.createService.ecr.detail',
'Create a service from a public or private Elastic Container Registry repository'
Expand All @@ -86,7 +86,7 @@ function createSourcePrompter(

const repositoryPath = {
label: 'Repository',
data: { CodeRepository: {} } as AppRunner.SourceConfiguration,
data: { CodeRepository: {} } as SourceConfiguration,
detail: localize('AWS.apprunner.createService.repository.detail', 'Create a service from a GitHub repository'),
}

Expand All @@ -96,15 +96,15 @@ function createSourcePrompter(
})
}

export class CreateAppRunnerServiceWizard extends Wizard<AppRunner.CreateServiceRequest> {
export class CreateAppRunnerServiceWizard extends Wizard<CreateServiceRequest> {
public constructor(
region: string,
initState: WizardState<AppRunner.CreateServiceRequest> = {},
implicitState: WizardState<AppRunner.CreateServiceRequest> = {},
initState: WizardState<CreateServiceRequest> = {},
implicitState: WizardState<CreateServiceRequest> = {},
clients = {
iam: new IamClient(region),
ecr: new DefaultEcrClient(region),
apprunner: new DefaultAppRunnerClient(region),
apprunner: new AppRunnerClient(region),
}
) {
super({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,32 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { AppRunner } from 'aws-sdk'
import * as nls from 'vscode-nls'
import { createCommonButtons, createRefreshButton, QuickInputToggleButton } from '../../../shared/ui/buttons'
import { Remote } from '../../../../types/git.d'
import { GitExtension } from '../../../shared/extensions/git'
import * as vscode from 'vscode'
import { WizardForm } from '../../../shared/wizards/wizardForm'
import { createVariablesPrompter } from '../../../shared/ui/common/variablesPrompter'
import { AppRunnerClient } from '../../../shared/clients/apprunnerClient'
import {
AppRunnerClient,
SourceConfiguration,
CodeRepository,
CodeConfigurationValues,
} from '../../../shared/clients/apprunner'
import { makeDeploymentButton } from './deploymentButton'
import { createLabelQuickPick, createQuickPick, QuickPickPrompter } from '../../../shared/ui/pickerPrompter'
import {
createLabelQuickPick,
createQuickPick,
DataQuickPickItem,
QuickPickPrompter,
} from '../../../shared/ui/pickerPrompter'
import { createInputBox, InputBoxPrompter } from '../../../shared/ui/inputPrompter'
import { apprunnerConnectionHelpUrl, apprunnerConfigHelpUrl, apprunnerRuntimeHelpUrl } from '../../../shared/constants'
import { apprunnerConfigHelpUrl, apprunnerConnectionHelpUrl, apprunnerRuntimeHelpUrl } from '../../../shared/constants'
import { Wizard, WIZARD_BACK } from '../../../shared/wizards/wizard'
import { openUrl } from '../../../shared/utilities/vsCodeUtils'
import { getAppRunnerCreateServiceDocUrl } from '../../../shared/extensionUtilities'
import * as AppRunner from '@aws-sdk/client-apprunner'

const localize = nls.loadMessageBundle()

Expand Down Expand Up @@ -82,9 +92,9 @@ function createBranchPrompter(
}

function createRuntimePrompter(): QuickPickPrompter<AppRunner.Runtime> {
const items = [
{ label: 'python3', data: 'PYTHON_3' },
{ label: 'nodejs12', data: 'NODEJS_16' },
const items: DataQuickPickItem<AppRunner.Runtime>[] = [
{ label: 'python3', data: AppRunner.Runtime.PYTHON_3 },
{ label: 'nodejs16', data: AppRunner.Runtime.NODEJS_16 },
]

return createQuickPick(items, {
Expand Down Expand Up @@ -158,10 +168,12 @@ export function createConnectionPrompter(client: AppRunnerClient) {
const getItems = async () => {
const resp = await client.listConnections()

return resp.ConnectionSummaryList.filter((conn) => conn.Status === 'AVAILABLE').map((conn) => ({
label: conn.ConnectionName!,
data: conn,
}))
return resp
.filter((conn) => conn.Status === 'AVAILABLE')
.map((conn) => ({
label: conn.ConnectionName!,
data: conn,
}))
}

const refreshButton = createRefreshButton()
Expand All @@ -184,21 +196,24 @@ function createSourcePrompter(): QuickPickPrompter<AppRunner.ConfigurationSource
)
const apiLabel = localize('AWS.apprunner.createService.configSource.apiLabel', 'Configure all settings here')
const repoLabel = localize('AWS.apprunner.createService.configSource.repoLabel', 'Use configuration file')
const apiItem: DataQuickPickItem<AppRunner.ConfigurationSource> = {
label: apiLabel,
data: AppRunner.ConfigurationSource.API,
}
const repoItem: DataQuickPickItem<AppRunner.ConfigurationSource> = {
label: repoLabel,
data: AppRunner.ConfigurationSource.REPOSITORY,
detail: configDetail,
}

return createQuickPick(
[
{ label: apiLabel, data: 'API' },
{ label: repoLabel, data: 'REPOSITORY', detail: configDetail },
],
{
title: localize('AWS.apprunner.createService.configSource.title', 'Choose configuration source'),
buttons: createCommonButtons(apprunnerConfigHelpUrl),
}
)
return createQuickPick([apiItem, repoItem], {
title: localize('AWS.apprunner.createService.configSource.title', 'Choose configuration source'),
buttons: createCommonButtons(apprunnerConfigHelpUrl),
})
}

function createCodeRepositorySubForm(git: GitExtension): WizardForm<AppRunner.CodeRepository> {
const subform = new WizardForm<AppRunner.CodeRepository>()
function createCodeRepositorySubForm(git: GitExtension): WizardForm<CodeRepository> {
const subform = new WizardForm<CodeRepository>()
const form = subform.body

form.RepositoryUrl.bindPrompter(() => createRepoPrompter(git).transform((r) => r.fetchUrl!))
Expand All @@ -210,7 +225,7 @@ function createCodeRepositorySubForm(git: GitExtension): WizardForm<AppRunner.Co
form.CodeConfiguration.ConfigurationSource.bindPrompter(createSourcePrompter)
form.SourceCodeVersion.Type.setDefault(() => 'BRANCH')

const codeConfigForm = new WizardForm<AppRunner.CodeConfigurationValues>()
const codeConfigForm = new WizardForm<CodeConfigurationValues>()
codeConfigForm.body.Runtime.bindPrompter(createRuntimePrompter)
codeConfigForm.body.BuildCommand.bindPrompter((state) => createBuildCommandPrompter(state.Runtime!))
codeConfigForm.body.StartCommand.bindPrompter((state) => createStartCommandPrompter(state.Runtime!))
Expand All @@ -227,7 +242,7 @@ function createCodeRepositorySubForm(git: GitExtension): WizardForm<AppRunner.Co
return subform
}

export class AppRunnerCodeRepositoryWizard extends Wizard<AppRunner.SourceConfiguration> {
export class AppRunnerCodeRepositoryWizard extends Wizard<SourceConfiguration> {
constructor(
client: AppRunnerClient,
git: GitExtension,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { AppRunner } from 'aws-sdk'
import { createCommonButtons, QuickInputButton, QuickInputToggleButton } from '../../../shared/ui/buttons'
import { toArrayAsync } from '../../../shared/utilities/collectionUtils'
import { EcrClient, EcrRepository } from '../../../shared/clients/ecrClient'
Expand All @@ -22,6 +21,7 @@ import { createRolePrompter } from '../../../shared/ui/common/roles'
import { getLogger } from '../../../shared/logger/logger'
import { getAppRunnerCreateServiceDocUrl, isCloud9 } from '../../../shared/extensionUtilities'
import { createExitPrompter } from '../../../shared/ui/common/exitPrompter'
import { ImageRepository, SourceConfiguration } from '../../../shared/clients/apprunner'

const localize = nls.loadMessageBundle()

Expand Down Expand Up @@ -223,8 +223,8 @@ export class ImageIdentifierForm extends WizardForm<{ repo: TaggedEcrRepository
function createImageRepositorySubForm(
ecrClient: EcrClient,
autoDeployButton: QuickInputToggleButton
): WizardForm<AppRunner.ImageRepository> {
const subform = new WizardForm<AppRunner.ImageRepository>()
): WizardForm<ImageRepository> {
const subform = new WizardForm<ImageRepository>()
const form = subform.body

// note: this is intentionally initialized only once to preserve caches
Expand Down Expand Up @@ -253,7 +253,7 @@ function createImageRepositorySubForm(
return subform
}

export class AppRunnerImageRepositoryWizard extends Wizard<AppRunner.SourceConfiguration> {
export class AppRunnerImageRepositoryWizard extends Wizard<SourceConfiguration> {
constructor(ecrClient: EcrClient, iamClient: IamClient, autoDeployButton = makeDeploymentButton()) {
super()
const form = this.form
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/awsexplorer/regionNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { ResourcesNode } from '../dynamicResources/explorer/nodes/resourcesNode'
import { AppRunnerNode } from '../awsService/apprunner/explorer/apprunnerNode'
import { DocumentDBNode } from '../docdb/explorer/docdbNode'
import { DefaultDocumentDBClient } from '../shared/clients/docdbClient'
import { DefaultAppRunnerClient } from '../shared/clients/apprunnerClient'
import { AppRunnerClient } from '../shared/clients/apprunner'
import { DefaultEcrClient } from '../shared/clients/ecrClient'
import { DefaultRedshiftClient } from '../shared/clients/redshiftClient'
import { DefaultIotClient } from '../shared/clients/iotClient'
Expand Down Expand Up @@ -53,7 +53,7 @@ const serviceCandidates: ServiceNode[] = [
},
{
serviceId: 'apprunner',
createFn: (regionCode: string) => new AppRunnerNode(regionCode, new DefaultAppRunnerClient(regionCode)),
createFn: (regionCode: string) => new AppRunnerNode(regionCode, new AppRunnerClient(regionCode)),
},
{
serviceId: 'cloudformation',
Expand Down
Loading
Loading