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: solo deployment create should use the context and cluster provided for where to save the remote config #1142

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
11 changes: 11 additions & 0 deletions src/commands/cluster/configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {Flags as flags} from '../flags.js';
import * as constants from '../../core/constants.js';
import {ListrEnquirerPromptAdapter} from '@listr2/prompt-adapter-enquirer';
import {SoloError} from '../../core/errors.js';
import {type Namespace} from '../../core/config/remote/types.js';

export const CONNECT_CONFIGS_NAME = 'connectConfig';

Expand Down Expand Up @@ -123,3 +124,13 @@ export interface ClusterResetConfigClass {
clusterName: string;
clusterSetupNamespace: string;
}

export interface SelectClusterContextContext {
config: {
quiet: boolean;
namespace: Namespace;
clusterName: string;
context: string;
clusters: string[];
};
}
4 changes: 2 additions & 2 deletions src/commands/cluster/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@
this.tasks.initialize(argv, connectConfigBuilder.bind(this)),
this.parent.setupHomeDirectoryTask(),
this.parent.getLocalConfig().promptLocalConfigTask(this.parent.getK8()),
this.tasks.selectContext(argv),
this.tasks.selectContext(),

Check warning on line 48 in src/commands/cluster/handlers.ts

View check run for this annotation

Codecov / codecov/patch

src/commands/cluster/handlers.ts#L48

Added line #L48 was not covered by tests
RemoteConfigTasks.loadRemoteConfig.bind(this)(argv),
this.tasks.readClustersFromRemoteConfig(argv),
this.tasks.updateLocalConfig(argv),
this.tasks.updateLocalConfig(),

Check warning on line 51 in src/commands/cluster/handlers.ts

View check run for this annotation

Codecov / codecov/patch

src/commands/cluster/handlers.ts#L51

Added line #L51 was not covered by tests
],
{
concurrent: false,
Expand Down
165 changes: 90 additions & 75 deletions src/commands/cluster/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@ import chalk from 'chalk';
import {ListrLease} from '../../core/lease/listr_lease.js';
import {ErrorMessages} from '../../core/error_messages.js';
import {SoloError} from '../../core/errors.js';
import {type Context} from '@kubernetes/client-node';
import {RemoteConfigManager} from '../../core/config/remote/remote_config_manager.js';
import {type RemoteConfigDataWrapper} from '../../core/config/remote/remote_config_data_wrapper.js';
import {type K8} from '../../core/k8.js';
import type {RemoteConfigDataWrapper} from '../../core/config/remote/remote_config_data_wrapper.js';
import type {K8} from '../../core/k8.js';
import type {Cluster} from '@kubernetes/client-node/dist/config_types.js';
import type {SoloListrTask, SoloListrTaskWrapper} from '../../types/index.js';
import type {SelectClusterContextContext} from './configs.js';
import type {Namespace} from '../../core/config/remote/types.js';
import type {LocalConfig} from '../../core/config/local_config.js';
import {ListrEnquirerPromptAdapter} from '@listr2/prompt-adapter-enquirer';
import {type LocalConfig} from '../../core/config/local_config.js';
import {type Cluster} from '@kubernetes/client-node/dist/config_types.js';

export class ClusterCommandTasks {
private readonly parent: BaseCommand;
Expand All @@ -44,11 +46,11 @@ export class ClusterCommandTasks {
this.parent = parent;
}

testConnectionToCluster(cluster: string, localConfig: LocalConfig, parentTask: ListrTaskWrapper<Context, any, any>) {
testConnectionToCluster(cluster: string, localConfig: LocalConfig, parentTask: ListrTaskWrapper<any, any, any>) {
const self = this;
return {
title: `Test connection to cluster: ${chalk.cyan(cluster)}`,
task: async (_, subTask: ListrTaskWrapper<Context, any, any>) => {
task: async (_, subTask: ListrTaskWrapper<any, any, any>) => {
let context = localConfig.clusterContextMapping[cluster];
if (!context) {
const isQuiet = self.parent.getConfigManager().getFlag(flags.quiet);
Expand Down Expand Up @@ -77,7 +79,7 @@ export class ClusterCommandTasks {
const self = this;
return {
title: `Pull and validate remote configuration for cluster: ${chalk.cyan(cluster)}`,
task: async (_, subTask: ListrTaskWrapper<Context, any, any>) => {
task: async (_, subTask: ListrTaskWrapper<any, any, any>) => {
const context = localConfig.clusterContextMapping[cluster];
self.parent.getK8().setCurrentContext(context);
const remoteConfigFromOtherCluster = await self.parent.getRemoteConfigManager().get();
Expand Down Expand Up @@ -118,7 +120,7 @@ export class ClusterCommandTasks {
};
}

updateLocalConfig(argv) {
updateLocalConfig(): SoloListrTask<SelectClusterContextContext> {
return new Task('Update local configuration', async (ctx: any, task: ListrTaskWrapper<any, any, any>) => {
this.parent.logger.info('Compare local and remote configuration...');
const configManager = this.parent.getConfigManager();
Expand All @@ -128,7 +130,7 @@ export class ClusterCommandTasks {
// Update current deployment with cluster list from remoteConfig
const localConfig = this.parent.getLocalConfig();
const localDeployments = localConfig.deployments;
const remoteClusterList = [];
const remoteClusterList: string[] = [];

const namespace = remoteConfig.metadata.name;
localConfig.currentDeploymentName = remoteConfig.metadata.name;
Expand All @@ -155,11 +157,11 @@ export class ClusterCommandTasks {
const cluster = ctx.config.clusters[i];
const context = contexts[i];

// If a context is provided use it to update the mapping
// If a context is provided, use it to update the mapping
if (context) {
localConfig.clusterContextMapping[cluster] = context;
} else if (!localConfig.clusterContextMapping[cluster]) {
// In quiet mode use the currently selected context to update the mapping
// In quiet mode, use the currently selected context to update the mapping
if (isQuiet) {
localConfig.clusterContextMapping[cluster] = this.parent.getK8().getKubeConfig().getCurrentContext();
}
Expand All @@ -176,7 +178,12 @@ export class ClusterCommandTasks {
});
}

private async getSelectedContext(task, selectedCluster, localConfig, isQuiet) {
private async getSelectedContext(
task: SoloListrTaskWrapper<SelectClusterContextContext>,
selectedCluster: string,
localConfig: LocalConfig,
isQuiet: boolean,
) {
let selectedContext;
if (isQuiet) {
selectedContext = this.parent.getK8().getKubeConfig().getCurrentContext();
Expand All @@ -187,7 +194,7 @@ export class ClusterCommandTasks {
return selectedContext;
}

private async promptForContext(task, cluster) {
private async promptForContext(task: SoloListrTaskWrapper<SelectClusterContextContext>, cluster: string) {
const kubeContexts = this.parent.getK8().getContexts();
return flags.context.prompt(
task,
Expand All @@ -196,14 +203,19 @@ export class ClusterCommandTasks {
);
}

private async selectContextForFirstCluster(task, clusters, localConfig, isQuiet) {
private async selectContextForFirstCluster(
task: SoloListrTaskWrapper<SelectClusterContextContext>,
clusters: string[],
localConfig: LocalConfig,
isQuiet: boolean,
) {
const selectedCluster = clusters[0];

if (localConfig.clusterContextMapping[selectedCluster]) {
return localConfig.clusterContextMapping[selectedCluster];
}

// If cluster does not exist in LocalConfig mapping prompt the user to select a context or use the current one
// If a cluster does not exist in LocalConfig mapping prompt the user to select a context or use the current one
else {
return this.getSelectedContext(task, selectedCluster, localConfig, isQuiet);
}
Expand Down Expand Up @@ -252,78 +264,81 @@ export class ClusterCommandTasks {
);
}

selectContext(argv) {
return new Task('Read local configuration settings', async (ctx: any, task: ListrTaskWrapper<any, any, any>) => {
this.parent.logger.info('Read local configuration settings...');
const configManager = this.parent.getConfigManager();
const isQuiet = configManager.getFlag(flags.quiet);
const deploymentName: string = configManager.getFlag(flags.namespace);
let clusters = splitFlagInput(configManager.getFlag(flags.clusterName));
const contexts = splitFlagInput(configManager.getFlag(flags.context));
const localConfig = this.parent.getLocalConfig();
let selectedContext;
let selectedCluster;

// If one or more contexts are provided use the first one
if (contexts.length) {
selectedContext = contexts[0];
}

// If one or more clusters are provided use the first one to determine the context
// from the mapping in the LocalConfig
else if (clusters.length) {
selectedCluster = clusters[0];
selectedContext = await this.selectContextForFirstCluster(task, clusters, localConfig, isQuiet);
}
selectContext(): SoloListrTask<SelectClusterContextContext> {
return {
title: 'Read local configuration settings',
task: async (_, task) => {
this.parent.logger.info('Read local configuration settings...');
const configManager = this.parent.getConfigManager();
const isQuiet = configManager.getFlag<boolean>(flags.quiet);
const deploymentName: string = configManager.getFlag<Namespace>(flags.namespace);
let clusters = splitFlagInput(configManager.getFlag<string>(flags.clusterName));
const contexts = splitFlagInput(configManager.getFlag<string>(flags.context));
const localConfig = this.parent.getLocalConfig();
let selectedContext: string;
let selectedCluster: string;

// If a deployment name is provided get the clusters associated with the deployment from the LocalConfig
// and select the context from the mapping, corresponding to the first deployment cluster
else if (deploymentName) {
const deployment = localConfig.deployments[deploymentName];
// If one or more contexts are provided, use the first one
if (contexts.length) {
selectedContext = contexts[0];
}

if (deployment && deployment.clusters.length) {
selectedCluster = deployment.clusters[0];
selectedContext = await this.selectContextForFirstCluster(task, deployment.clusters, localConfig, isQuiet);
// If one or more clusters are provided, use the first one to determine the context
// from the mapping in the LocalConfig
else if (clusters.length) {
selectedCluster = clusters[0];
selectedContext = await this.selectContextForFirstCluster(task, clusters, localConfig, isQuiet);
}

// The provided deployment does not exist in the LocalConfig
else {
// Add the deployment to the LocalConfig with the currently selected cluster and context in KubeConfig
if (isQuiet) {
selectedContext = this.parent.getK8().getKubeConfig().getCurrentContext();
selectedCluster = this.parent.getK8().getKubeConfig().getCurrentCluster().name;
localConfig.deployments[deploymentName] = {
clusters: [selectedCluster],
};

if (!localConfig.clusterContextMapping[selectedCluster]) {
localConfig.clusterContextMapping[selectedCluster] = selectedContext;
}
// If a deployment name is provided, get the clusters associated with the deployment from the LocalConfig
// and select the context from the mapping, corresponding to the first deployment cluster
else if (deploymentName) {
const deployment = localConfig.deployments[deploymentName];

if (deployment && deployment.clusters.length) {
selectedCluster = deployment.clusters[0];
selectedContext = await this.selectContextForFirstCluster(task, deployment.clusters, localConfig, isQuiet);
}

// Prompt user for clusters and contexts
// The provided deployment does not exist in the LocalConfig
else {
const promptedClusters = await flags.clusterName.prompt(task, '');
clusters = splitFlagInput(promptedClusters);

for (const cluster of clusters) {
if (!localConfig.clusterContextMapping[cluster]) {
localConfig.clusterContextMapping[cluster] = await this.promptForContext(task, cluster);
// Add the deployment to the LocalConfig with the currently selected cluster and context in KubeConfig
if (isQuiet) {
selectedContext = this.parent.getK8().getKubeConfig().getCurrentContext();
selectedCluster = this.parent.getK8().getKubeConfig().getCurrentCluster().name;
localConfig.deployments[deploymentName] = {
clusters: [selectedCluster],
};

if (!localConfig.clusterContextMapping[selectedCluster]) {
localConfig.clusterContextMapping[selectedCluster] = selectedContext;
}
}

selectedCluster = clusters[0];
selectedContext = localConfig.clusterContextMapping[clusters[0]];
// Prompt user for clusters and contexts
else {
const promptedClusters = await flags.clusterName.prompt(task, '');
clusters = splitFlagInput(promptedClusters);

for (const cluster of clusters) {
if (!localConfig.clusterContextMapping[cluster]) {
localConfig.clusterContextMapping[cluster] = await this.promptForContext(task, cluster);
}
}

selectedCluster = clusters[0];
selectedContext = localConfig.clusterContextMapping[clusters[0]];
}
}
}
}

const connectionValid = await this.parent.getK8().testClusterConnection(selectedContext, selectedCluster);
if (!connectionValid) {
throw new SoloError(ErrorMessages.INVALID_CONTEXT_FOR_CLUSTER(selectedContext));
}
this.parent.getK8().setCurrentContext(selectedContext);
});
const connectionValid = await this.parent.getK8().testClusterConnection(selectedContext, selectedCluster);
if (!connectionValid) {
throw new SoloError(ErrorMessages.INVALID_CONTEXT_FOR_CLUSTER(selectedContext));
}
this.parent.getK8().setCurrentContext(selectedContext);
},
};
}

initialize(argv: any, configInit: ConfigBuilder) {
Expand Down
Loading
Loading