Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement solo context connect #863

Merged
Show file tree
Hide file tree
Changes from 6 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
13 changes: 11 additions & 2 deletions docs/content/User/SDK.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Instructions for using Solo with Hedera JavaScript SDK

First, please follow solo repository README to install solo and Docker Desktop.
You also need to install the Taskfile tool following the instructions here:
https://taskfile.dev/installation/
Expand All @@ -9,10 +10,13 @@ Then we start with launching a local Solo network with the following commands:
# launch a local Solo network with mirror node and hedera explorer
task default-with-mirror-node
```

Then create a new test account with the following command:

```
npm run solo-test -- account create -n solo-e2e --hbar-amount 100
```

The output would be similar to the following:

```bash
Expand All @@ -26,10 +30,13 @@ The output would be similar to the following:
```

Then use the following commmand to get private key of the account `0.0.1007`:

```bash
npm run solo-test -- account get --account-id 0.0.1007 -n solo-e2e --private-key
```

The output would be similar to the following:

```bash
{
"accountId": "0.0.1007",
Expand All @@ -52,7 +59,8 @@ OPERATOR_KEY="302a300506032b65700321001d8978e647aca1195c54a4d3d5dc469b95666de14e
# Hedera Network
HEDERA_NETWORK="local-node"
```
Make sure to assign the value of accountId to OPERATOR_ID and the value of privateKey to OPERATOR_KEY.

Make sure to assign the value of accountId to OPERATOR\_ID and the value of privateKey to OPERATOR\_KEY.

Then try the following command to run the test

Expand All @@ -69,9 +77,11 @@ account id = 0.0.1009
```

Or try the topic creation example:

```bash
node examples/create-topic.js
```

The output should be similar to the following:

```bash
Expand All @@ -89,4 +99,3 @@ Finally, after done with using solo, using the following command to tear down th
```bash
task clean
```

2 changes: 2 additions & 0 deletions examples/sdk-network-connection/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# Solo Network Connection Example

## pre-requirements:

1. fork or download the solo repository: https://github.com/hashgraph/solo
2. have NodeJS 20+ and NPM installed: https://nodejs.org/en/download/package-manager
3. have Taskfile installed: https://taskfile.dev/installation/

## running the Solo connection example:

1. open a terminal and cd into the root of the solo repo directory
2. run: `task default-with-mirror`
3. run: `cd examples/sdk-network-connection`
Expand Down
8 changes: 8 additions & 0 deletions src/commands/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,12 @@ export class BaseCommand extends ShellRunner {
getUnusedConfigs (configName: string): string[] {
return this._configMaps.get(configName).getUnusedConfigs()
}

getK8 () {
return this.k8
}

getLocalConfig () {
return this.localConfig
}
}
30 changes: 30 additions & 0 deletions src/commands/context/flags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Copyright (C) 2024 Hedera Hashgraph, LLC
*
* 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 * as flags from '../flags.js'

export const DEFAULT_FLAGS = {
requiredFlags: [],
requiredFlagsWithDisabledPrompt: [flags.namespace, flags.cacheDir, flags.releaseTag],
jeromy-cannon marked this conversation as resolved.
Show resolved Hide resolved
optionalFlags: [flags.devMode]
}

export const USE_FLAGS = {
requiredFlags: [],
requiredFlagsWithDisabledPrompt: [],
optionalFlags: [flags.devMode, flags.quiet, flags.clusterName, flags.context, flags.force, flags.namespace]
}
49 changes: 49 additions & 0 deletions src/commands/context/handlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Copyright (C) 2024 Hedera Hashgraph, LLC
*
* 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 { type BaseCommand } from '../base.js'
import { type ContextCommandTasks } from './tasks.js'
import * as helpers from '../../core/helpers.js'
import { constants } from '../../core/index.js'
import { type CommandHandlers } from '../../types/index.js'
import * as ContextFlags from './flags.js'

export class ContextCommandHandlers implements CommandHandlers {
readonly parent: BaseCommand
readonly tasks: ContextCommandTasks

constructor (parent: BaseCommand, tasks: ContextCommandTasks) {
this.parent = parent
this.tasks = tasks
}

async connect (argv: any) {
argv = helpers.addFlagsToArgv(argv, ContextFlags.USE_FLAGS)

const action = helpers.commandActionBuilder([
this.tasks.initialize(argv),
this.parent.getLocalConfig().promptLocalConfigTask(this.parent.getK8(), argv),
this.tasks.updateLocalConfig(argv),
], {
concurrent: false,
rendererOptions: constants.LISTR_DEFAULT_RENDERER_OPTION
}, 'context use', null)

await action(argv, this)
return true
}

}

Check warning on line 49 in src/commands/context/handlers.ts

View check run for this annotation

Codecov / codecov/patch

src/commands/context/handlers.ts#L25-L49

Added lines #L25 - L49 were not covered by tests
54 changes: 54 additions & 0 deletions src/commands/context/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Copyright (C) 2024 Hedera Hashgraph, LLC
*
* 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 { YargsCommand } from '../../core/index.js'
import { BaseCommand } from './../base.js'
import type { Opts } from '../../types/index.js'
import { ContextCommandTasks } from './tasks.js'
import { ContextCommandHandlers } from './handlers.js'
import * as ContextFlags from './flags.js'
import { getPromptMap } from '../prompts.js'

/**
* Defines the core functionalities of 'node' command
*/
export class ContextCommand extends BaseCommand {
private handlers: ContextCommandHandlers

constructor (opts: Opts) {
super(opts)

this.handlers = new ContextCommandHandlers(this, new ContextCommandTasks(this, getPromptMap()))
}

getCommandDefinition () {
return {
command: 'context',
desc: 'Manage local and remote configurations',
builder: (yargs: any) => {
return yargs
.command(new YargsCommand({
command: 'connect',
description: 'updates the local configuration by connecting a deployment to a k8s context',
commandDef: this,
handler: 'connect'
}, ContextFlags.USE_FLAGS))
.demandCommand(1, 'Select a context command')
}
}
}
}

Check warning on line 54 in src/commands/context/index.ts

View check run for this annotation

Codecov / codecov/patch

src/commands/context/index.ts#L30-L54

Added lines #L30 - L54 were not covered by tests
104 changes: 104 additions & 0 deletions src/commands/context/tasks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* Copyright (C) 2024 Hedera Hashgraph, LLC
*
* 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 {
Task, Templates
} from '../../core/index.js'
import * as flags from '../flags.js'
import type { ListrTaskWrapper } from 'listr2'
import { type BaseCommand } from '../base.js'

export class ContextCommandTasks {

private readonly parent: BaseCommand
private readonly promptMap: Map<string, Function>

constructor (parent, promptMap) {
this.parent = parent
this.promptMap = promptMap
}

updateLocalConfig (argv) {
return new Task('Update local configuration', async (ctx: any, task: ListrTaskWrapper<any, any, any>) => {
this.parent.logger.info('Updating local configuration...')

const isQuiet = !!argv[flags.quiet.name]

let currentDeploymentName = argv[flags.namespace.name]
let clusterAliases = Templates.parseClusterAliases(argv[flags.clusterName.name])
let contextName = argv[flags.context.name]

const kubeContexts = await this.parent.getK8().getKubeConfig().getContexts()

if (isQuiet) {
const currentCluster = (await this.parent.getK8().getKubeConfig().getCurrentCluster())
if (!clusterAliases.length) clusterAliases = [currentCluster.name]
if (!contextName) contextName = await this.parent.getK8().getKubeConfig().getCurrentContext()

if (!currentDeploymentName) {
const selectedContext = kubeContexts.find(e => e.name === contextName)
currentDeploymentName = selectedContext && selectedContext.namespace ? selectedContext.namespace : 'default'
}
}
else {
if (!clusterAliases.length) {
const prompt = this.promptMap.get(flags.clusterName.name)
const unparsedClusterAliases = await prompt(task, clusterAliases)
clusterAliases = Templates.parseClusterAliases(unparsedClusterAliases)
}
if (!contextName) {
const prompt = this.promptMap.get(flags.context.name)
contextName = await prompt(task, kubeContexts.map(c => c.name), contextName)
}
if (!currentDeploymentName) {
const prompt = this.promptMap.get(flags.namespace.name)
currentDeploymentName = await prompt(task, currentDeploymentName)
}
}

// Select current deployment
this.parent.getLocalConfig().setCurrentDeployment(currentDeploymentName)

// Set clusters for active deployment
const deployments = this.parent.getLocalConfig().deployments
deployments[currentDeploymentName].clusterAliases = clusterAliases
this.parent.getLocalConfig().setDeployments(deployments)

this.parent.getK8().getKubeConfig().setCurrentContext(contextName)

this.parent.logger.info(`currentDeploymentName: ${currentDeploymentName}`)
this.parent.logger.info(`contextName: ${contextName}`)
this.parent.logger.info(`clusterAliases: ${clusterAliases.join(' ')}`)
this.parent.logger.info('Save LocalConfig file')
jeromy-cannon marked this conversation as resolved.
Show resolved Hide resolved
await this.parent.getLocalConfig().write()
})
}

initialize (argv: any) {
const { requiredFlags, optionalFlags } = argv

argv.flags = [
...requiredFlags,
...optionalFlags
]

return new Task('Initialize', async (ctx: any, task: ListrTaskWrapper<any, any, any>) => {
if (argv[flags.devMode.name]) {
this.parent.logger.setDevMode(true)
}
})
}

Check warning on line 103 in src/commands/context/tasks.ts

View check run for this annotation

Codecov / codecov/patch

src/commands/context/tasks.ts#L91-L103

Added lines #L91 - L103 were not covered by tests
}
34 changes: 22 additions & 12 deletions src/commands/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@

/**
* Set flag from the flag option
* @param y instance of yargs

Check warning on line 25 in src/commands/flags.ts

View workflow job for this annotation

GitHub Actions / Code Style / Standard

tsdoc-param-tag-missing-hyphen: The @param block should be followed by a parameter name and then a hyphen
* @param commandFlags a set of command flags

Check warning on line 26 in src/commands/flags.ts

View workflow job for this annotation

GitHub Actions / Code Style / Standard

tsdoc-param-tag-missing-hyphen: The @param block should be followed by a parameter name and then a hyphen
*
*/
export function setCommandFlags (y: any, ...commandFlags: CommandFlag[]) {
Expand Down Expand Up @@ -748,6 +748,16 @@
}
}

export const context: CommandFlag = {
constName: 'contextName',
name: 'context',
definition: {
describe: 'The kind context name to be used',
jeromy-cannon marked this conversation as resolved.
Show resolved Hide resolved
defaultValue: '',
type: 'string'
}
}

export const deploymentName: CommandFlag = {
constName: 'deploymentName',
name: 'deployment-name',
Expand Down Expand Up @@ -775,9 +785,9 @@
name: 'grpc-tls-cert',
definition: {
describe:
'TLS Certificate path for the gRPC ' +
'(e.g. "node1=/Users/username/node1-grpc.cert" ' +
'with multiple nodes comma seperated)',
'TLS Certificate path for the gRPC ' +
'(e.g. "node1=/Users/username/node1-grpc.cert" ' +
'with multiple nodes comma seperated)',
defaultValue: '',
type: 'string'
}
Expand All @@ -788,9 +798,9 @@
name: 'grpc-web-tls-cert',
definition: {
describe:
'TLS Certificate path for gRPC Web ' +
'(e.g. "node1=/Users/username/node1-grpc-web.cert" ' +
'with multiple nodes comma seperated)',
'TLS Certificate path for gRPC Web ' +
'(e.g. "node1=/Users/username/node1-grpc-web.cert" ' +
'with multiple nodes comma seperated)',
defaultValue: '',
type: 'string'
}
Expand All @@ -801,9 +811,9 @@
name: 'grpc-tls-key',
definition: {
describe:
'TLS Certificate key path for the gRPC ' +
'(e.g. "node1=/Users/username/node1-grpc.key" ' +
'with multiple nodes comma seperated)',
'TLS Certificate key path for the gRPC ' +
'(e.g. "node1=/Users/username/node1-grpc.key" ' +
'with multiple nodes comma seperated)',
defaultValue: '',
type: 'string'
}
Expand All @@ -814,9 +824,9 @@
name: 'grpc-web-tls-key',
definition: {
describe:
'TLC Certificate key path for gRPC Web ' +
'(e.g. "node1=/Users/username/node1-grpc-web.key" ' +
'with multiple nodes comma seperated)',
'TLC Certificate key path for gRPC Web ' +
'(e.g. "node1=/Users/username/node1-grpc-web.key" ' +
'with multiple nodes comma seperated)',
defaultValue: '',
type: 'string'
}
Expand Down
Loading
Loading