Skip to content

Commit

Permalink
Configuration to produce Amazon Q server with IAM Credentials provide…
Browse files Browse the repository at this point in the history
…r service (aws#264)

* Add Amazon Q bundle configuration to build server using IAM credentials

* Update readme

* Fix packaging commands

* Move duplicated calls to CodeWhispererServiceBase class
  • Loading branch information
viktorsaws authored May 28, 2024
1 parent cbebdd5 commit dda40ea
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 36 deletions.
25 changes: 20 additions & 5 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,20 +62,35 @@
"preLaunchTask": "npm: compile"
},
{
"name": "CodeWhisperer Server",
"name": "CodeWhisperer Server Token",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceFolder}/client/vscode"],
"outFiles": ["${workspaceFolder}/client/vscode/out/**/*.js"],
"env": {
"LSP_SERVER": "${workspaceFolder}/app/aws-lsp-codewhisperer-binary/out/index.js",
"LSP_SERVER": "${workspaceFolder}/app/aws-lsp-codewhisperer-binary/out/token-standalone.js",
"ENABLE_INLINE_COMPLETION": "true",
"ENABLE_TOKEN_PROVIDER": "true",
"ENABLE_CHAT": "true"
// "HTTPS_PROXY": "http://127.0.0.1:8888",
},
"preLaunchTask": "npm: compile"
}
// "preLaunchTask": "npm: compile"
},
{
"name": "CodeWhisperer Server IAM",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceFolder}/client/vscode"],
"outFiles": ["${workspaceFolder}/client/vscode/out/**/*.js"],
"env": {
"LSP_SERVER": "${workspaceFolder}/app/aws-lsp-codewhisperer-binary/out/iam-standalone.js",
"ENABLE_INLINE_COMPLETION": "true",
"ENABLE_IAM_PROVIDER": "true"
// "HTTPS_PROXY": "http://127.0.0.1:8888",
}
// "preLaunchTask": "npm: compile"
},
{
"name": "S3 Server (with Credentials support)",
Expand Down Expand Up @@ -125,7 +140,7 @@
"compounds": [
{
"name": "Launch as VSCode Extension + Debugging",
"configurations": ["CodeWhisperer Server", "Attach to AWS Documents Language Server"]
"configurations": ["CodeWhisperer Server IAM", "Attach to AWS Documents Language Server"]
}
]
}
14 changes: 11 additions & 3 deletions app/aws-lsp-codewhisperer-binary/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,20 @@ To create bundle run:
npm run bundle
```

This command will compile package and produce bundled Javascript program at `./out/aws-lsp-codewhisperer-binary.js` path.
This command will compile package and produce 2 bundled Javascript programs in `./out/` directory:
- `./out/iam-standalone.js` - Amazon Q server using IAM Credentials provider
- `./out/token-standalone.js` - Amazon Q server using Bearer Token SSO Credentials provider

To test server you can use sample IDEs client in [`./client`](../../client) subpackages. In VSCode, you can use "Run and Debug" functionality with [sample client extension](../../CONTRIBUTING.md#with-minimal-vscode-client) and update `launch.json` configuration to point to [compiled bundle file](../../.vscode/launch.json#L60). Change value for `LSP_SERVER` valiable from `${workspaceFolder}/app/aws-lsp-codewhisperer-binary/out/index.js` to `${workspaceFolder}/app/aws-lsp-codewhisperer-binary/out/aws-lsp-codewhisperer-binary.js`.
To test server you can use sample IDEs client in [`./client`](../../client) subpackages. In VSCode, you can use "Run and Debug" functionality with [sample client extension](../../CONTRIBUTING.md#with-minimal-vscode-client) and update `launch.json` configuration to point to [compiled bundle file](../../.vscode/launch.json#L60). Change value for `LSP_SERVER` valiable from `${workspaceFolder}/app/aws-lsp-codewhisperer-binary/out/index.js` to `${workspaceFolder}/app/aws-lsp-codewhisperer-binary/out/token-standalone.js`.

To verify compiled bundle can run, you can start it in your shell with NodeJS:

```bash
node ./out/aws-lsp-codewhisperer-binary.js --stdio
node ./out/token-standalone.js --stdio
```

or

```bash
node ./out/iam-standalone.js --stdio
```
4 changes: 3 additions & 1 deletion app/aws-lsp-codewhisperer-binary/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
"bundle": "npm run compile && npm run webpack && npm run package-x64",
"clean": "rm -rf out/ bin/ tsconfig.tsbuildinfo",
"compile": "tsc --build",
"package-x64": "pkg --targets node18-linux-x64,node18-win-x64,node18-macos-x64 --out-path bin --compress GZip .",
"package-x64": "npm run package-x64:iam && npm run package-x64:token",
"package-x64:iam": "pkg --targets node18-linux-x64,node18-win-x64,node18-macos-x64 --out-path bin --compress GZip out/iam-standalone.js",
"package-x64:token": "pkg --targets node18-linux-x64,node18-win-x64,node18-macos-x64 --out-path bin --compress GZip out/token-standalone.js",
"webpack": "webpack"
},
"dependencies": {
Expand Down
10 changes: 10 additions & 0 deletions app/aws-lsp-codewhisperer-binary/src/iam-standalone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { standalone } from '@aws/language-server-runtimes/runtimes'
import { RuntimeProps } from '@aws/language-server-runtimes/runtimes/runtime'
import { CodeWhispererServerIAM } from '@aws/lsp-codewhisperer'

const props: RuntimeProps = {
version: '0.1.0',
servers: [CodeWhispererServerIAM],
name: 'AWS CodeWhisperer',
}
standalone(props)
14 changes: 11 additions & 3 deletions app/aws-lsp-codewhisperer-binary/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,20 @@ const baseConfig = {
},
}

const nodeJsBundleConfig = {
const nodeJsBearerTokenBundleConfig = {
...baseConfig,
entry: {
'aws-lsp-codewhisperer-binary': path.join(__dirname, 'src/index.ts'),
'aws-lsp-codewhisperer-token-binary': path.join(__dirname, 'src/token-standalone.ts'),
},
target: 'node',
}

module.exports = [nodeJsBundleConfig]
const nodeJsIamBundleConfig = {
...baseConfig,
entry: {
'aws-lsp-codewhisperer-iam-binary': path.join(__dirname, 'src/iam-standalone.ts'),
},
target: 'node',
}

module.exports = [nodeJsBearerTokenBundleConfig, nodeJsIamBundleConfig]
Original file line number Diff line number Diff line change
Expand Up @@ -567,5 +567,5 @@ export const CodeWhispererServerIAM = CodewhispererServerFactory(
credentialsProvider => new CodeWhispererServiceIAM(credentialsProvider)
)
export const CodeWhispererServerToken = CodewhispererServerFactory(
credentialsProvider => new CodeWhispererServiceToken(credentialsProvider, {})
credentialsProvider => new CodeWhispererServiceToken(credentialsProvider)
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CredentialsProvider, BearerCredentials } from '@aws/language-server-runtimes/server-interface'
import { BearerCredentials, CredentialsProvider } from '@aws/language-server-runtimes/server-interface'
import { AWSError, CredentialProviderChain, Credentials } from 'aws-sdk'
import { PromiseResult } from 'aws-sdk/lib/request'
import { v4 as uuidv4 } from 'uuid'
Expand Down Expand Up @@ -32,26 +32,44 @@ export interface GenerateSuggestionsResponse {
responseContext: ResponseContext
}

export interface AWSConfig {
proxy?: any
}

import CodeWhispererSigv4Client = require('../client/sigv4/codewhisperersigv4client')
import CodeWhispererTokenClient = require('../client/token/codewhispererbearertokenclient')
import AWS = require('aws-sdk')

// Right now the only difference between the token client and the IAM client for codewhsiperer is the difference in function name
// This abstract class can grow in the future to account for any additional changes across the clients
export abstract class CodeWhispererServiceBase {
protected readonly codeWhispererRegion = 'us-east-1'
protected readonly codeWhispererEndpoint = 'https://codewhisperer.us-east-1.amazonaws.com/'
public shareCodeWhispererContentWithAWS = false
abstract client: CodeWhispererSigv4Client | CodeWhispererTokenClient

abstract generateSuggestions(request: GenerateSuggestionsRequest): Promise<GenerateSuggestionsResponse>

constructor(credentialsProvider: CredentialsProvider, additionalAwsConfig: AWSConfig = {}) {
this.updateAwsConfiguration(additionalAwsConfig)
}

updateAwsConfiguration = (awsConfig: AWSConfig) => {
if (awsConfig?.proxy) {
AWS.config.update({
httpOptions: { agent: awsConfig.proxy },
})
}
}

generateItemId = () => uuidv4()
}

export class CodeWhispererServiceIAM extends CodeWhispererServiceBase {
client: CodeWhispererSigv4Client
private readonly codeWhispererRegion = 'us-east-1'
private readonly codeWhispererEndpoint = 'https://codewhisperer.us-east-1.amazonaws.com/'

constructor(credentialsProvider: CredentialsProvider) {
super()
constructor(credentialsProvider: CredentialsProvider, additionalAwsConfig: AWSConfig = {}) {
super(credentialsProvider, additionalAwsConfig)

const options: CodeWhispererTokenClientConfigurationOptions = {
region: this.codeWhispererRegion,
Expand Down Expand Up @@ -90,18 +108,13 @@ export class CodeWhispererServiceIAM extends CodeWhispererServiceBase {
responseContext,
}
}

generateItemId = () => uuidv4()
}

export class CodeWhispererServiceToken extends CodeWhispererServiceBase {
client: CodeWhispererTokenClient
private readonly codeWhispererRegion = 'us-east-1'
private readonly codeWhispererEndpoint = 'https://codewhisperer.us-east-1.amazonaws.com/'

constructor(credentialsProvider: CredentialsProvider, additionalAwsConfig: any) {
super()
this.updateAwsConfiguration(additionalAwsConfig)
constructor(credentialsProvider: CredentialsProvider, additionalAwsConfig: AWSConfig = {}) {
super(credentialsProvider, additionalAwsConfig)

const options: CodeWhispererTokenClientConfigurationOptions = {
region: this.codeWhispererRegion,
Expand Down Expand Up @@ -227,14 +240,4 @@ export class CodeWhispererServiceToken extends CodeWhispererServiceBase {
): Promise<PromiseResult<CodeWhispererTokenClient.ListCodeAnalysisFindingsResponse, AWSError>> {
return this.client.listCodeAnalysisFindings(request).promise()
}

updateAwsConfiguration = (awsConfig: any) => {
if (awsConfig.proxy) {
AWS.config.update({
httpOptions: { agent: awsConfig.proxy },
})
}
}

generateItemId = () => uuidv4()
}
18 changes: 17 additions & 1 deletion server/aws-lsp-codewhisperer/src/language-server/proxy-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ChatSessionManagementService } from './chat/chatSessionManagementServic
import { ChatSessionServiceConfig } from './chat/chatSessionService'
import { SecurityScanServerToken } from './codeWhispererSecurityScanServer'
import { CodewhispererServerFactory } from './codeWhispererServer'
import { CodeWhispererServiceToken } from './codeWhispererService'
import { CodeWhispererServiceIAM, CodeWhispererServiceToken } from './codeWhispererService'
import { QChatServer } from './qChatServer'

export const CodeWhispererServerTokenProxy = CodewhispererServerFactory(credentialsProvider => {
Expand All @@ -21,6 +21,22 @@ export const CodeWhispererServerTokenProxy = CodewhispererServerFactory(credenti
return new CodeWhispererServiceToken(credentialsProvider, additionalAwsConfig)
})

export const CodeWhispererServerIAMProxy = CodewhispererServerFactory(credentialsProvider => {
let additionalAwsConfig = {}
const proxyUrl = process.env.HTTPS_PROXY ?? process.env.https_proxy

if (proxyUrl) {
const { getProxyHttpAgent } = require('proxy-http-agent')
const proxyAgent = getProxyHttpAgent({
proxy: proxyUrl,
})
additionalAwsConfig = {
proxy: proxyAgent,
}
}
return new CodeWhispererServiceIAM(credentialsProvider, additionalAwsConfig)
})

export const CodeWhispererSecurityScanServerTokenProxy = SecurityScanServerToken(credentialsProvider => {
let additionalAwsConfig = {}
const proxyUrl = process.env.HTTPS_PROXY ?? process.env.https_proxy
Expand Down

0 comments on commit dda40ea

Please sign in to comment.