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

merge WGE support into main #496

Merged
merged 7 commits into from
Dec 6, 2023
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
32 changes: 26 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,17 +125,37 @@ The GitOps Tools Extension depends on the [Kubernetes Tools](https://marketplace
- Make sure you have [successfully authenticated](https://docs.microsoft.com/en-us/cli/azure/authenticate-azure-cli) on your `az` CLI and have access to the [correct subscription](https://docs.microsoft.com/en-us/cli/azure/account?view=azure-cli-latest#az_account_set) for your AKS or ARC cluster.
- The easiest way to get your AKS or Arc cluster visible by the GitOps and Kubernetes Extensions, is to use the `az` CLI to merge the kubeconfig for accessing your cluster onto the default `kubectl` config. Use `get-credentials` as shown in the [official CLI documentation](https://docs.microsoft.com/en-us/cli/azure/aks?view=azure-cli-latest#az_aks_get_credentials). In order to enable GitOps in a cluster you will likely need the `--admin` credentials.

## Weave GitOps Enterprise (WGE) Templates
## Weave GitOps Enterprise (WGE) Integration

WGE users can access GitOpsTemplates directly from this extensions. Templates are provided by cluster administrators (Platform Teams) and can be used to quickly create cluster and configure applications with GitOps.
WGE users can access GitOpsTemplates directly from this extensions. WGE integration adds another treeview that shows `GitOpsTemplate`, `Canary`, `Pipeline`, and `GitOpsSet` resources and adds new interactions to each type of resource. All WGE resources have a right-click action to open them in WGE portal.

Templates are an opt-in feature that must be enabled in setting:
GitOpsTemplates are provided by cluster administrators (Platform Teams) and can be used to quickly create cluster and configure applications with GitOps. Flagger Canaries status can be visualized and their progress tracked. Pipelines are listed with their targets and each `GitopsCluster` attached to a Pipeline can be set as the current selected cluster for quick navigation between clusters.

![Enable GitOpsTemplates](docs/images/vscode-templates-config.png)
WGE integration is an opt-in feature that must be enabled in settings:

After that they can be seen in a new 'Templates' view. Right-click a template to use it:
![Enable WGE Features](docs/images/config-enable-wge.png)

![Weave GitOps Treeview](docs/images/weave-gitops-treeview.png)

![Create GitOps Template](docs/images/vscode-templates-view.png)

### WGE Configuration

For the integration to work, this extension needs a ConfigMap that provides WGE information and settings:

`kubectl create configmap weave-gitops-interop --from-literal=portalUrl='https://WGE-CLUSTER-HOST' --from-literal=wgeClusterName='WGE-CLUSTER-NAME' -n flux-system``

```
apiVersion: v1
kind: ConfigMap
metadata:
namespace: flux-system
name: weave-gitops-interop
data:
portalUrl: https://mccp.howard.moomboo.space
wgeClusterName: howard-moomboo-space
```

![Use GitOpsTemplates](docs/images/vscode-templates-view.png)



Expand Down
Binary file added docs/images/config-enable-wge.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/images/vscode-templates-config.png
Binary file not shown.
Binary file added docs/images/weave-gitops-treeview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

88 changes: 70 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "vscode-gitops-tools",
"displayName": "GitOps Tools for Flux",
"description": "GitOps automation tools for continuous delivery of Kubernetes and Cloud Native applications",
"version": "0.25.4",
"version": "0.25.5-edge.0",
"author": "Kingdon Barrett <[email protected]>",
"contributors": [
"Kingdon Barrett <[email protected]>",
Expand Down Expand Up @@ -98,6 +98,16 @@
"title": "Resume",
"category": "GitOps"
},
{
"command": "gitops.manualPromotion",
"title": "Disable Automatic Promotion",
"category": "GitOps"
},
{
"command": "gitops.autoPromotion",
"title": "Enable Automatic Promotion",
"category": "GitOps"
},
{
"command": "gitops.flux.checkPrerequisites",
"title": "Flux Check Prerequisites",
Expand Down Expand Up @@ -259,6 +269,16 @@
"command": "gitops.views.createFromTemplate",
"title": "Create from Template",
"category": "GitOps"
},
{
"command": "gitops.views.openInWgePortal",
"title": "Open in Weave Gitops Enterprise...",
"category": "GitOps"
},
{
"command": "gitops.views.setContextToGitopsCluster",
"title": "Set as Current Context",
"category": "GitOps"
}
],
"viewsContainers": {
Expand All @@ -285,8 +305,8 @@
"name": "Workloads"
},
{
"id": "gitops.views.templates",
"name": "Templates",
"id": "gitops.views.wge",
"name": "Weave GitOps",
"when": "config.gitops.weaveGitopsEnterprise"
},
{
Expand All @@ -311,7 +331,7 @@
"gitops.weaveGitopsEnterprise": {
"type": "boolean",
"default": false,
"description": "Enable WGE GitOpsTemplates feature"
"description": "Enable WGE features"
},
"gitops.kubectlRequestTimeout": {
"type": "string",
Expand Down Expand Up @@ -388,7 +408,7 @@
{
"command": "gitops.views.refreshResourcesTreeView",
"group": "navigation@1",
"when": "view == gitops.views.templates"
"when": "view == gitops.views.wge"
},
{
"command": "gitops.views.showWorkloadsHelpMessage",
Expand Down Expand Up @@ -434,57 +454,67 @@
},
{
"command": "gitops.flux.reconcileSource",
"when": "view == gitops.views.sources && viewItem =~ /(GitRepository;|OCIRepository;|HelmRepository;|Bucket;)/",
"when": "viewItem =~ /(GitRepository;|OCIRepository;|HelmRepository;|Bucket;)/",
"group": "navigation@0"
},
{
"command": "gitops.flux.reconcileWorkloadWithSource",
"when": "view == gitops.views.workloads && viewItem =~ /(Kustomization;|HelmRelease;)/",
"when": "viewItem =~ /(Kustomization;|HelmRelease;)/",
"group": "navigation@0"
},
{
"command": "gitops.flux.reconcileWorkload",
"when": "view == gitops.views.workloads && viewItem =~ /(Kustomization;|HelmRelease;)/",
"when": "viewItem =~ /(Kustomization;|HelmRelease;)/",
"group": "navigation@1"
},
{
"command": "gitops.suspend",
"when": "view =~ /(gitops.views.sources|gitops.views.workloads)/ && viewItem =~ /(GitRepository;|OCIRepository;|Kustomization;|HelmRelease;|HelmRepository;)/ && viewItem =~ /notSuspend;/",
"when": "viewItem =~ /notSuspend;/",
"group": "navigation@1"
},
{
"command": "gitops.resume",
"when": "view =~ /(gitops.views.sources|gitops.views.workloads)/ && viewItem =~ /(GitRepository;|OCIRepository;|Kustomization;|HelmRelease;|HelmRepository;)/ && viewItem =~ /suspend;/",
"when": "viewItem =~ /suspend;/",
"group": "navigation@1"
},
{
"command": "gitops.manualPromotion",
"when": "viewItem =~ /autoPromotion;/",
"group": "1"
},
{
"command": "gitops.autoPromotion",
"when": "viewItem =~ /manualPromotion;/",
"group": "1"
},
{
"command": "gitops.views.deleteWorkload",
"when": "view == gitops.views.workloads && viewItem =~ /(Kustomization;|HelmRelease;)/",
"when": "viewItem =~ /(Kustomization;|HelmRelease;)/",
"group": "navigation@2"
},
{
"command": "gitops.views.deleteSource",
"when": "view == gitops.views.sources && viewItem =~ /(GitRepository;|OCIRepository;|HelmRepository;|Bucket;)/",
"when": "viewItem =~ /(GitRepository;|OCIRepository;|HelmRepository;|Bucket;)/",
"group": "navigation@2"
},
{
"command": "gitops.views.pullGitRepository",
"when": "view == gitops.views.sources && viewItem =~ /GitRepository;/",
"when": "viewItem =~ /GitRepository;/",
"group": "navigation@3"
},
{
"command": "gitops.addKustomization",
"when": "view == gitops.views.sources && viewItem =~ /GitRepository;|OCIRepository;|Bucket;/",
"when": "viewItem =~ /GitRepository;|OCIRepository;|Bucket;/",
"group": "navigation@3"
},
{
"command": "gitops.editor.showLogs",
"when": "view =~ /^(gitops.views.clusters)$/ && viewItem =~ /(Deployment;)/"
"when": "viewItem =~ /(Deployment;)/"
},
{
"command": "gitops.copyResourceName",
"when": "view =~ /^(gitops.views.sources|gitops.views.workloads)$/",
"group": "navigation@9"
"when": "view =~ /^(gitops.views.sources|gitops.views.workloads||gitops.views.wge)$/ && !(viewItem =~ /Container;/)",
"group": "9"
},
{
"command": "gitops.flux.trace",
Expand All @@ -494,7 +524,17 @@
{
"command": "gitops.views.createFromTemplate",
"group": "1",
"when": "view == gitops.views.templates"
"when": "viewItem =~ /GitOpsTemplate;/"
},
{
"command": "gitops.views.openInWgePortal",
"group": "1",
"when": "viewItem =~ /hasWgePortal;/"
},
{
"command": "gitops.views.setContextToGitopsCluster",
"group": "1",
"when": "viewItem =~ /GitopsCluster;/"
}
],
"gitops.explorer": [
Expand Down Expand Up @@ -543,6 +583,14 @@
"command": "gitops.resume",
"when": "never"
},
{
"command": "gitops.manualPromotion",
"when": "never"
},
{
"command": "gitops.autoPromotion",
"when": "never"
},
{
"command": "gitops.flux.check",
"when": "never"
Expand Down Expand Up @@ -614,6 +662,10 @@
{
"command": "gitops.dev.showGlobalState",
"when": "gitops:isDev"
},
{
"command": "gitops.views.setContextToGitopsCluster",
"when": "never"
}
]
}
Expand Down
3 changes: 2 additions & 1 deletion src/cli/kubernetes/apiResources.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { redrawResourcesTreeViews, refreshResourcesTreeViews } from 'commands/refreshTreeViews';
import { currentContextData } from 'data/contextData';
import { currentContextData, loadContextData } from 'data/contextData';
import { setVSCodeContext, telemetry } from 'extension';
import { ContextId } from 'types/extensionIds';
import { Kind } from 'types/kubernetes/kubernetesTypes';
Expand Down Expand Up @@ -102,5 +102,6 @@ export async function loadAvailableResourceKinds() {
// give proxy init callbacks time to fire
setTimeout(() => {
refreshResourcesTreeViews();
loadContextData();
}, 100);
}
63 changes: 54 additions & 9 deletions src/cli/kubernetes/kubectlGet.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import safesh from 'shell-escape-tag';

import { telemetry } from 'extension';
import { k8sList } from 'k8s/list';
import { k8sGet, k8sList } from 'k8s/list';
import { Bucket } from 'types/flux/bucket';
import { Canary } from 'types/flux/canary';
import { GitOpsTemplate } from 'types/flux/gitOpsTemplate';
import { GitRepository } from 'types/flux/gitRepository';
import { GitOpsSet } from 'types/flux/gitopsset';
import { HelmRelease } from 'types/flux/helmRelease';
import { HelmRepository } from 'types/flux/helmRepository';
import { Kustomization } from 'types/flux/kustomization';
import { OCIRepository } from 'types/flux/ociRepository';
import { Pipeline } from 'types/flux/pipeline';
import { Deployment, Kind, KubernetesObject, Pod, qualifyToolkitKind } from 'types/kubernetes/kubernetesTypes';
import { TelemetryError } from 'types/telemetryEventNames';
import { parseJson, parseJsonItems } from 'utils/jsonUtils';
Expand All @@ -29,14 +32,21 @@ export const notAnErrorServerNotRunning = /no connection could be made because t
* @param namespace namespace of the target resource
* @param kind kind of the target resource
*/
export async function getResource(name: string, namespace: string, kind: string): Promise<undefined | KubernetesObject> {
const shellResult = await invokeKubectlCommand(`get ${kind}/${name} --namespace=${namespace} -o json`);
export async function getResource<T extends KubernetesObject>(name: string, namespace: string, kind: Kind): Promise<undefined | T> {
const item = await k8sGet(name, namespace, kind);
if(item) {
return item as T;
}

let fqKind = qualifyToolkitKind(kind);

const shellResult = await invokeKubectlCommand(`get ${fqKind}/${name} --namespace=${namespace} -o json`);
if (shellResult?.code !== 0) {
telemetry.sendError(TelemetryError.FAILED_TO_GET_RESOURCE);
return;
}

return parseJson(shellResult.stdout);
return parseJson(shellResult.stdout) as T;
}

export async function getResourcesAllNamespaces<T extends KubernetesObject>(kind: Kind, telemetryError: TelemetryError): Promise<T[]> {
Expand Down Expand Up @@ -89,6 +99,18 @@ export async function getGitOpsTemplates(): Promise<GitOpsTemplate[]> {
return getResourcesAllNamespaces(Kind.GitOpsTemplate, TelemetryError.FAILED_TO_GET_GITOPSTEMPLATES);
}

export async function getCanaries(): Promise<Canary[]> {
return getResourcesAllNamespaces(Kind.Canary, TelemetryError.FAILED_TO_GET_HELM_RELEASES);
}

export async function getPipelines(): Promise<Pipeline[]> {
return getResourcesAllNamespaces(Kind.Pipeline, TelemetryError.FAILED_TO_GET_HELM_RELEASES);
}

export async function getGitOpsSet(): Promise<GitOpsSet[]> {
return getResourcesAllNamespaces(Kind.GitOpsSet, TelemetryError.FAILED_TO_GET_HELM_RELEASES);
}


/**
* Get all flux system deployments.
Expand Down Expand Up @@ -118,8 +140,7 @@ export async function getFluxControllers(context?: string): Promise<Deployment[]
* @param name name of the kustomize/helmRelease object
* @param namespace namespace of the kustomize/helmRelease object
*/
export async function getChildrenOfWorkload(
workload: 'kustomize' | 'helm',
export async function getHelmReleaseChildren(
name: string,
namespace: string,
): Promise<KubernetesObject[] | undefined> {
Expand All @@ -129,22 +150,46 @@ export async function getChildrenOfWorkload(
return;
}

const labelNameSelector = `-l ${workload}.toolkit.fluxcd.io/name=${name}`;
const labelNamespaceSelector = `-l ${workload}.toolkit.fluxcd.io/namespace=${namespace}`;
const labelNameSelector = `-l helm.toolkit.fluxcd.io/name=${name}`;
const labelNamespaceSelector = `-l helm.toolkit.fluxcd.io/namespace=${namespace}`;

const query = `get ${resourceKinds.join(',')} ${labelNameSelector} ${labelNamespaceSelector} -A -o json`;
const shellResult = await invokeKubectlCommand(query);

if (!shellResult || shellResult.code !== 0) {
telemetry.sendError(TelemetryError.FAILED_TO_GET_CHILDREN_OF_A_WORKLOAD);
window.showErrorMessage(`Failed to get ${workload} created resources: ${shellResult?.stderr}`);
window.showErrorMessage(`Failed to get HelmRelease created resources: ${shellResult?.stderr}`);
return;
}

return parseJsonItems(shellResult.stdout);
}


export async function getCanaryChildren(
name: string,
): Promise<KubernetesObject[]> {
// return [];
const resourceKinds = getAvailableResourcePlurals();
if (!resourceKinds) {
return [];
}

const labelNameSelector = `-l app=${name}`;

const query = `get ${resourceKinds.join(',')} ${labelNameSelector} -A -o json`;
const shellResult = await invokeKubectlCommand(query);

if (!shellResult || shellResult.code !== 0) {
telemetry.sendError(TelemetryError.FAILED_TO_GET_CHILDREN_OF_A_WORKLOAD);
window.showErrorMessage(`Failed to get HelmRelease created resources: ${shellResult?.stderr}`);
return [];
}

return parseJsonItems(shellResult.stdout);
}


/**
* Get pods by a deployment name.
* @param name pod target name
Expand Down
Loading
Loading