Skip to content

Commit

Permalink
Kubernetes resource object types (vscode-kubernetes-tools#223)
Browse files Browse the repository at this point in the history
  • Loading branch information
itowlson authored Jun 1, 2018
1 parent b0b83e2 commit d3cc5a0
Show file tree
Hide file tree
Showing 16 changed files with 227 additions and 156 deletions.
3 changes: 2 additions & 1 deletion src/components/clusterprovider/azure/azure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import * as vscode from 'vscode';
import { Shell } from '../../../shell';
import { FS } from '../../../fs';
import { Errorable, ActionResult, fromShellJson, fromShellExitCodeAndStandardError, fromShellExitCodeOnly, succeeded, failed, Diagnostic } from '../../../wizard';
import { ActionResult, fromShellJson, fromShellExitCodeAndStandardError, fromShellExitCodeOnly, Diagnostic } from '../../../wizard';
import { Errorable, succeeded, failed } from '../../../errorable';
import * as compareVersions from 'compare-versions';
import { sleep } from '../../../sleep';

Expand Down
3 changes: 2 additions & 1 deletion src/components/clusterprovider/azure/azureclusterprovider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import * as restify from 'restify';
import * as portfinder from 'portfinder';
import * as clusterproviderregistry from '../clusterproviderregistry';
import * as azure from './azure';
import { Errorable, script, styles, formStyles, waitScript, ActionResult, succeeded, failed, Failed, Succeeded, Diagnostic } from '../../../wizard';
import { script, styles, formStyles, waitScript, ActionResult, Diagnostic } from '../../../wizard';
import { Errorable, succeeded, failed, Failed, Succeeded } from '../../../errorable';

// HTTP request dispatch

Expand Down
2 changes: 1 addition & 1 deletion src/components/git/git.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Shell, ShellResult } from '../../shell';
import { Errorable } from '../../wizard';
import { Errorable } from '../../errorable';

export class Git {
constructor(private readonly shell: Shell) {}
Expand Down
2 changes: 1 addition & 1 deletion src/components/installer/installer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as tar from 'tar';
import * as tmp from 'tmp';
import * as vscode from 'vscode';
import { Shell, Platform } from '../../shell';
import { Errorable, failed, succeeded } from '../../wizard';
import { Errorable, failed, succeeded } from '../../errorable';
import { exec } from 'child_process';

export async function installKubectl(shell: Shell): Promise<Errorable<void>> {
Expand Down
20 changes: 13 additions & 7 deletions src/components/kubectl/dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { host } from '../../host';
import { shell } from '../../shell';
import { create as kubectlCreate, Kubectl } from '../../kubectl';
import { installDependencies } from '../../extension';
import { Node, KubernetesCollection, Pod } from '../../kuberesources.objectmodel';
import { failed } from '../../errorable';


const KUBE_DASHBOARD_URL = "http://localhost:8001/ui/";
Expand All @@ -30,9 +32,11 @@ let terminal: vscode.Terminal;
* @returns Boolean identifying if we think this is an AKS cluster.
*/
async function isAKSCluster (): Promise<boolean> {
const nodes = await kubectl.invokeAsync('get nodes -o json');
const nodesJson = JSON.parse(nodes.stdout);
const nodeItems = nodesJson.items;
const nodes = await kubectl.asJson<KubernetesCollection<Node>>('get nodes -o json');
if (failed(nodes)) {
return false;
}
const nodeItems = nodes.result.items;
const nodeCount = nodeItems.length;

for (let nodeItem of nodeItems) {
Expand All @@ -46,7 +50,7 @@ async function isAKSCluster (): Promise<boolean> {
return true;
}

function _isNodeAKS(node): boolean {
function _isNodeAKS(node: Node): boolean {
const name: string = node.metadata.name;
const roleLabel: string = node.metadata.labels["kubernetes.io/role"];

Expand All @@ -64,11 +68,13 @@ function _isNodeAKS(node): boolean {
* @returns The name of the dashboard pod.
*/
async function findDashboardPod (): Promise<string> {
const dashboardResults = await kubectl.invokeAsync(
const dashboardPod = await kubectl.asJson<KubernetesCollection<Pod>>(
"get pod -n kube-system -l k8s-app=kubernetes-dashboard -o json"
);
const resultsJson = JSON.parse(dashboardResults.stdout);
return resultsJson.items[0].metadata.name;
if (failed(dashboardPod)) {
return undefined;
}
return dashboardPod.result.items[0].metadata.name;
}

/**
Expand Down
33 changes: 21 additions & 12 deletions src/components/kubectl/port-forward.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { host } from '../../host';
import { findAllPods, tryFindKindNameFromEditor, FindPodsResult, installDependencies } from '../../extension';
import { QuickPickOptions } from 'vscode';
import * as portFinder from 'portfinder';
import { Pod } from '../../kuberesources.objectmodel';
import { succeeded } from '../../errorable';

const kubectl = kubectlCreate(host, fs, shell, installDependencies);
const PORT_FORWARD_TERMINAL = 'kubectl port-forward';
Expand All @@ -18,8 +20,16 @@ export interface PortMapping {
targetPort: number;
}

interface PortForwardFindPodsResult extends FindPodsResult {
readonly fromOpenDocument?: boolean;
interface PodFromDocument {
readonly succeeded: true;
readonly pod: string;
readonly fromOpenDocument: true;
}

type PortForwardFindPodsResult = PodFromDocument | FindPodsResult;

function isFindResultFromDocument(obj: PortForwardFindPodsResult): obj is PodFromDocument {
return (obj as PodFromDocument).fromOpenDocument;
}

/**
Expand Down Expand Up @@ -48,17 +58,16 @@ export async function portForwardKubernetes (explorerNode?: any): Promise<void>
host.showInformationMessage("Error while fetching pods for port-forward");
}

let pods = portForwardablePods.pods;

if (portForwardablePods.fromOpenDocument && pods.length === 1) {
if (isFindResultFromDocument(portForwardablePods)) {
// The pod is described by the open document. Skip asking which pod to use and go straight to port-forward.
const podSelection = portForwardablePods[0];
const podSelection = portForwardablePods.pod;
const portMapping = await promptForPort(podSelection);
portForwardToPod(podSelection, portMapping);
return;
}

let podSelection;
let podSelection: string | undefined;
const pods = portForwardablePods.pods;

try {
const podNames: string[] = pods.map((podObj) => podObj.metadata.name);
Expand All @@ -71,7 +80,7 @@ export async function portForwardKubernetes (explorerNode?: any): Promise<void>
throw e;
}

if (podSelection === undefined) {
if (!podSelection) {
host.showErrorMessage("Error while selecting pod for port-forward");
return;
}
Expand Down Expand Up @@ -168,7 +177,7 @@ export function buildPortMapping (portString: string): PortMapping {
*/
async function findPortForwardablePods (): Promise<PortForwardFindPodsResult> {
let kindFromEditor = tryFindKindNameFromEditor();
let kind, podName;
let kind: string | undefined, podName: string | undefined;

// Find the pod type from the open editor.
if (kindFromEditor !== null) {
Expand All @@ -178,13 +187,13 @@ async function findPortForwardablePods (): Promise<PortForwardFindPodsResult> {

// Not a pod type, so not port-forwardable, fallback to looking
// up all pods.
if (kind !== 'pods') {
if (kind !== 'pods' && kind !== 'pod') {
return await findAllPods();
}

return <PortForwardFindPodsResult>{
return {
succeeded: true,
pods: [podName],
pod: podName,
fromOpenDocument: true
};
}
Expand Down
17 changes: 12 additions & 5 deletions src/configMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { fs } from './fs';
import { shell } from './shell';
import { host } from './host';
import { create as kubectlCreate, Kubectl } from './kubectl';
import { currentNamespace } from './kubectlUtils';
import { currentNamespace, DataHolder } from './kubectlUtils';
import { deleteMessageItems, overwriteMessageItems } from './extension';
import { KubernetesFileObject, KubernetesDataHolderResource, KubernetesExplorer } from './explorer';
import { allKinds } from './kuberesources';
import { failed } from './errorable';

export const uriScheme: string = 'k8sviewfiledata';

Expand Down Expand Up @@ -53,8 +54,11 @@ export async function deleteKubernetesConfigFile(kubectl: Kubectl, obj: Kubernet
return;
}
const currentNS = await currentNamespace(kubectl);
const json = await kubectl.invokeAsync(`get ${obj.resource} ${obj.parentName} --namespace=${currentNS} -o json`);
const dataHolder = JSON.parse(json.stdout);
const json = await kubectl.asJson<any>(`get ${obj.resource} ${obj.parentName} --namespace=${currentNS} -o json`);
if (failed(json)) {
return;
}
const dataHolder = json.result;
dataHolder.data = removeKey(dataHolder.data, obj.id);
const out = JSON.stringify(dataHolder);
const shellRes = await kubectl.invokeAsync(`replace -f - --namespace=${currentNS}`, out);
Expand All @@ -74,8 +78,11 @@ export async function addKubernetesConfigFile(kubectl: Kubectl, obj: KubernetesD
if (fileUris) {
console.log(fileUris);
const currentNS = await currentNamespace(kubectl);
const json = await kubectl.invokeAsync(`get ${obj.resource} ${obj.id} --namespace=${currentNS} -o json`);
const dataHolder = JSON.parse(json.stdout);
const dataHolderJson = await kubectl.asJson<DataHolder>(`get ${obj.resource} ${obj.id} --namespace=${currentNS} -o json`);
if (failed(dataHolderJson)) {
return;
}
const dataHolder = dataHolderJson.result;
fileUris.map(async (uri) => {
const filePath = uri.fsPath;
const fileName = basename(filePath);
Expand Down
22 changes: 14 additions & 8 deletions src/debug/debugSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { shell } from "../shell";

import { DockerfileParser } from "../docker/dockerfileParser";
import * as dockerUtils from "../docker/dockerUtils";
import { isPod, Pod, KubernetesCollection, Container } from "../kuberesources.objectmodel";

const debugCommandDocumentationUrl = "https://github.com/Azure/vscode-kubernetes-tools/blob/master/debug-on-kubernetes.md";

Expand Down Expand Up @@ -130,16 +131,21 @@ export class DebugSession implements IDebugSession {
}

// Select the target pod to attach.
let targetPod = pod, targetContainer, containers = [];
const resource = await kubectlUtils.getResourceAsJson(this.kubectl, pod ? `pod/${pod}` : "pods");
let targetPod = pod,
targetContainer: string | undefined = undefined,
containers: Container[] = [];

const resource = pod ?
await kubectlUtils.getResourceAsJson<Pod>(this.kubectl, `pod/${pod}`) :
await kubectlUtils.getResourceAsJson<KubernetesCollection<Pod>>(this.kubectl, "pods");
if (!resource) {
return;
}

if (pod) {
if (isPod(resource)) {
containers = resource.spec.containers;
} else {
const podPickItems: vscode.QuickPickItem[] = resource.items.map((pod) => {
const podPickItems = resource.items.map((pod) => {
return {
label: `${pod.metadata.name} (${pod.spec.nodeName})`,
description: "pod",
Expand All @@ -153,14 +159,14 @@ export class DebugSession implements IDebugSession {
return;
}

targetPod = (<any> selectedPod).name;
containers = (<any> selectedPod).containers;
targetPod = selectedPod.name;
containers = selectedPod.containers;
}

// Select the target container to attach.
// TODO: consolidate with container selection in extension.ts.
if (containers.length > 1) {
const containerPickItems: vscode.QuickPickItem[] = containers.map((container) => {
const containerPickItems = containers.map((container) => {
return {
label: container.name,
description: '',
Expand All @@ -174,7 +180,7 @@ export class DebugSession implements IDebugSession {
return;
}

targetContainer = (<any> selectedContainer).name;
targetContainer = selectedContainer.name;
}

// Find the debug port to attach.
Expand Down
19 changes: 19 additions & 0 deletions src/errorable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export interface Succeeded<T> {
readonly succeeded: true;
readonly result: T;
}

export interface Failed {
readonly succeeded: false;
readonly error: string[];
}

export type Errorable<T> = Succeeded<T> | Failed;

export function succeeded<T>(e: Errorable<T>): e is Succeeded<T> {
return e.succeeded;
}

export function failed<T>(e: Errorable<T>): e is Failed {
return !e.succeeded;
}
7 changes: 4 additions & 3 deletions src/explorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Kubectl } from './kubectl';
import * as kubectlUtils from './kubectlUtils';
import { Host } from './host';
import * as kuberesources from './kuberesources';
import { failed } from './errorable';

export function create(kubectl: Kubectl, host: Host): KubernetesExplorer {
return new KubernetesExplorer(kubectl, host);
Expand Down Expand Up @@ -155,11 +156,11 @@ class KubernetesResourceFolder extends KubernetesFolder {

async getChildren(kubectl: Kubectl, host: Host): Promise<KubernetesObject[]> {
const childrenLines = await kubectl.asLines("get " + this.kind.abbreviation);
if (shell.isShellResult(childrenLines)) {
host.showErrorMessage(childrenLines.stderr);
if (failed(childrenLines)) {
host.showErrorMessage(childrenLines.error[0]);
return [new DummyObject("Error")];
}
return childrenLines.map((line) => {
return childrenLines.result.map((line) => {
const bits = line.split(' ');
return new KubernetesResource(this.kind, bits[0]);
});
Expand Down
Loading

0 comments on commit d3cc5a0

Please sign in to comment.