Skip to content

Commit 80a94db

Browse files
juozasggitops-release-botKingdon Barrett
authored
merge WGE support into main (#496)
Support for Template, Canary, Pipeline and GitOpsSet resources. Integration with WGE web portal and documentation. TreeNode code improvements --------- Co-authored-by: gitops-release-bot <[email protected]> Co-authored-by: Kingdon Barrett <[email protected]>
1 parent 1f09314 commit 80a94db

File tree

77 files changed

+2019
-650
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+2019
-650
lines changed

README.md

+26-6
Original file line numberDiff line numberDiff line change
@@ -125,17 +125,37 @@ The GitOps Tools Extension depends on the [Kubernetes Tools](https://marketplace
125125
- 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.
126126
- 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.
127127

128-
## Weave GitOps Enterprise (WGE) Templates
128+
## Weave GitOps Enterprise (WGE) Integration
129129

130-
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.
130+
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.
131131

132-
Templates are an opt-in feature that must be enabled in setting:
132+
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.
133133

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

136-
After that they can be seen in a new 'Templates' view. Right-click a template to use it:
136+
![Enable WGE Features](docs/images/config-enable-wge.png)
137+
138+
![Weave GitOps Treeview](docs/images/weave-gitops-treeview.png)
139+
140+
![Create GitOps Template](docs/images/vscode-templates-view.png)
141+
142+
### WGE Configuration
143+
144+
For the integration to work, this extension needs a ConfigMap that provides WGE information and settings:
145+
146+
`kubectl create configmap weave-gitops-interop --from-literal=portalUrl='https://WGE-CLUSTER-HOST' --from-literal=wgeClusterName='WGE-CLUSTER-NAME' -n flux-system``
147+
148+
```
149+
apiVersion: v1
150+
kind: ConfigMap
151+
metadata:
152+
namespace: flux-system
153+
name: weave-gitops-interop
154+
data:
155+
portalUrl: https://mccp.howard.moomboo.space
156+
wgeClusterName: howard-moomboo-space
157+
```
137158

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

140160

141161

docs/images/config-enable-wge.png

24.8 KB
Loading
-50.7 KB
Binary file not shown.

docs/images/weave-gitops-treeview.png

217 KB
Loading

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+70-18
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "vscode-gitops-tools",
33
"displayName": "GitOps Tools for Flux",
44
"description": "GitOps automation tools for continuous delivery of Kubernetes and Cloud Native applications",
5-
"version": "0.25.4",
5+
"version": "0.25.5-edge.0",
66
"author": "Kingdon Barrett <[email protected]>",
77
"contributors": [
88
"Kingdon Barrett <[email protected]>",
@@ -98,6 +98,16 @@
9898
"title": "Resume",
9999
"category": "GitOps"
100100
},
101+
{
102+
"command": "gitops.manualPromotion",
103+
"title": "Disable Automatic Promotion",
104+
"category": "GitOps"
105+
},
106+
{
107+
"command": "gitops.autoPromotion",
108+
"title": "Enable Automatic Promotion",
109+
"category": "GitOps"
110+
},
101111
{
102112
"command": "gitops.flux.checkPrerequisites",
103113
"title": "Flux Check Prerequisites",
@@ -259,6 +269,16 @@
259269
"command": "gitops.views.createFromTemplate",
260270
"title": "Create from Template",
261271
"category": "GitOps"
272+
},
273+
{
274+
"command": "gitops.views.openInWgePortal",
275+
"title": "Open in Weave Gitops Enterprise...",
276+
"category": "GitOps"
277+
},
278+
{
279+
"command": "gitops.views.setContextToGitopsCluster",
280+
"title": "Set as Current Context",
281+
"category": "GitOps"
262282
}
263283
],
264284
"viewsContainers": {
@@ -285,8 +305,8 @@
285305
"name": "Workloads"
286306
},
287307
{
288-
"id": "gitops.views.templates",
289-
"name": "Templates",
308+
"id": "gitops.views.wge",
309+
"name": "Weave GitOps",
290310
"when": "config.gitops.weaveGitopsEnterprise"
291311
},
292312
{
@@ -311,7 +331,7 @@
311331
"gitops.weaveGitopsEnterprise": {
312332
"type": "boolean",
313333
"default": false,
314-
"description": "Enable WGE GitOpsTemplates feature"
334+
"description": "Enable WGE features"
315335
},
316336
"gitops.kubectlRequestTimeout": {
317337
"type": "string",
@@ -388,7 +408,7 @@
388408
{
389409
"command": "gitops.views.refreshResourcesTreeView",
390410
"group": "navigation@1",
391-
"when": "view == gitops.views.templates"
411+
"when": "view == gitops.views.wge"
392412
},
393413
{
394414
"command": "gitops.views.showWorkloadsHelpMessage",
@@ -434,57 +454,67 @@
434454
},
435455
{
436456
"command": "gitops.flux.reconcileSource",
437-
"when": "view == gitops.views.sources && viewItem =~ /(GitRepository;|OCIRepository;|HelmRepository;|Bucket;)/",
457+
"when": "viewItem =~ /(GitRepository;|OCIRepository;|HelmRepository;|Bucket;)/",
438458
"group": "navigation@0"
439459
},
440460
{
441461
"command": "gitops.flux.reconcileWorkloadWithSource",
442-
"when": "view == gitops.views.workloads && viewItem =~ /(Kustomization;|HelmRelease;)/",
462+
"when": "viewItem =~ /(Kustomization;|HelmRelease;)/",
443463
"group": "navigation@0"
444464
},
445465
{
446466
"command": "gitops.flux.reconcileWorkload",
447-
"when": "view == gitops.views.workloads && viewItem =~ /(Kustomization;|HelmRelease;)/",
467+
"when": "viewItem =~ /(Kustomization;|HelmRelease;)/",
448468
"group": "navigation@1"
449469
},
450470
{
451471
"command": "gitops.suspend",
452-
"when": "view =~ /(gitops.views.sources|gitops.views.workloads)/ && viewItem =~ /(GitRepository;|OCIRepository;|Kustomization;|HelmRelease;|HelmRepository;)/ && viewItem =~ /notSuspend;/",
472+
"when": "viewItem =~ /notSuspend;/",
453473
"group": "navigation@1"
454474
},
455475
{
456476
"command": "gitops.resume",
457-
"when": "view =~ /(gitops.views.sources|gitops.views.workloads)/ && viewItem =~ /(GitRepository;|OCIRepository;|Kustomization;|HelmRelease;|HelmRepository;)/ && viewItem =~ /suspend;/",
477+
"when": "viewItem =~ /suspend;/",
458478
"group": "navigation@1"
459479
},
480+
{
481+
"command": "gitops.manualPromotion",
482+
"when": "viewItem =~ /autoPromotion;/",
483+
"group": "1"
484+
},
485+
{
486+
"command": "gitops.autoPromotion",
487+
"when": "viewItem =~ /manualPromotion;/",
488+
"group": "1"
489+
},
460490
{
461491
"command": "gitops.views.deleteWorkload",
462-
"when": "view == gitops.views.workloads && viewItem =~ /(Kustomization;|HelmRelease;)/",
492+
"when": "viewItem =~ /(Kustomization;|HelmRelease;)/",
463493
"group": "navigation@2"
464494
},
465495
{
466496
"command": "gitops.views.deleteSource",
467-
"when": "view == gitops.views.sources && viewItem =~ /(GitRepository;|OCIRepository;|HelmRepository;|Bucket;)/",
497+
"when": "viewItem =~ /(GitRepository;|OCIRepository;|HelmRepository;|Bucket;)/",
468498
"group": "navigation@2"
469499
},
470500
{
471501
"command": "gitops.views.pullGitRepository",
472-
"when": "view == gitops.views.sources && viewItem =~ /GitRepository;/",
502+
"when": "viewItem =~ /GitRepository;/",
473503
"group": "navigation@3"
474504
},
475505
{
476506
"command": "gitops.addKustomization",
477-
"when": "view == gitops.views.sources && viewItem =~ /GitRepository;|OCIRepository;|Bucket;/",
507+
"when": "viewItem =~ /GitRepository;|OCIRepository;|Bucket;/",
478508
"group": "navigation@3"
479509
},
480510
{
481511
"command": "gitops.editor.showLogs",
482-
"when": "view =~ /^(gitops.views.clusters)$/ && viewItem =~ /(Deployment;)/"
512+
"when": "viewItem =~ /(Deployment;)/"
483513
},
484514
{
485515
"command": "gitops.copyResourceName",
486-
"when": "view =~ /^(gitops.views.sources|gitops.views.workloads)$/",
487-
"group": "navigation@9"
516+
"when": "view =~ /^(gitops.views.sources|gitops.views.workloads||gitops.views.wge)$/ && !(viewItem =~ /Container;/)",
517+
"group": "9"
488518
},
489519
{
490520
"command": "gitops.flux.trace",
@@ -494,7 +524,17 @@
494524
{
495525
"command": "gitops.views.createFromTemplate",
496526
"group": "1",
497-
"when": "view == gitops.views.templates"
527+
"when": "viewItem =~ /GitOpsTemplate;/"
528+
},
529+
{
530+
"command": "gitops.views.openInWgePortal",
531+
"group": "1",
532+
"when": "viewItem =~ /hasWgePortal;/"
533+
},
534+
{
535+
"command": "gitops.views.setContextToGitopsCluster",
536+
"group": "1",
537+
"when": "viewItem =~ /GitopsCluster;/"
498538
}
499539
],
500540
"gitops.explorer": [
@@ -543,6 +583,14 @@
543583
"command": "gitops.resume",
544584
"when": "never"
545585
},
586+
{
587+
"command": "gitops.manualPromotion",
588+
"when": "never"
589+
},
590+
{
591+
"command": "gitops.autoPromotion",
592+
"when": "never"
593+
},
546594
{
547595
"command": "gitops.flux.check",
548596
"when": "never"
@@ -614,6 +662,10 @@
614662
{
615663
"command": "gitops.dev.showGlobalState",
616664
"when": "gitops:isDev"
665+
},
666+
{
667+
"command": "gitops.views.setContextToGitopsCluster",
668+
"when": "never"
617669
}
618670
]
619671
}

src/cli/kubernetes/apiResources.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { redrawResourcesTreeViews, refreshResourcesTreeViews } from 'commands/refreshTreeViews';
2-
import { currentContextData } from 'data/contextData';
2+
import { currentContextData, loadContextData } from 'data/contextData';
33
import { setVSCodeContext, telemetry } from 'extension';
44
import { ContextId } from 'types/extensionIds';
55
import { Kind } from 'types/kubernetes/kubernetesTypes';
@@ -102,5 +102,6 @@ export async function loadAvailableResourceKinds() {
102102
// give proxy init callbacks time to fire
103103
setTimeout(() => {
104104
refreshResourcesTreeViews();
105+
loadContextData();
105106
}, 100);
106107
}

src/cli/kubernetes/kubectlGet.ts

+54-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import safesh from 'shell-escape-tag';
22

33
import { telemetry } from 'extension';
4-
import { k8sList } from 'k8s/list';
4+
import { k8sGet, k8sList } from 'k8s/list';
55
import { Bucket } from 'types/flux/bucket';
6+
import { Canary } from 'types/flux/canary';
67
import { GitOpsTemplate } from 'types/flux/gitOpsTemplate';
78
import { GitRepository } from 'types/flux/gitRepository';
9+
import { GitOpsSet } from 'types/flux/gitopsset';
810
import { HelmRelease } from 'types/flux/helmRelease';
911
import { HelmRepository } from 'types/flux/helmRepository';
1012
import { Kustomization } from 'types/flux/kustomization';
1113
import { OCIRepository } from 'types/flux/ociRepository';
14+
import { Pipeline } from 'types/flux/pipeline';
1215
import { Deployment, Kind, KubernetesObject, Pod, qualifyToolkitKind } from 'types/kubernetes/kubernetesTypes';
1316
import { TelemetryError } from 'types/telemetryEventNames';
1417
import { parseJson, parseJsonItems } from 'utils/jsonUtils';
@@ -29,14 +32,21 @@ export const notAnErrorServerNotRunning = /no connection could be made because t
2932
* @param namespace namespace of the target resource
3033
* @param kind kind of the target resource
3134
*/
32-
export async function getResource(name: string, namespace: string, kind: string): Promise<undefined | KubernetesObject> {
33-
const shellResult = await invokeKubectlCommand(`get ${kind}/${name} --namespace=${namespace} -o json`);
35+
export async function getResource<T extends KubernetesObject>(name: string, namespace: string, kind: Kind): Promise<undefined | T> {
36+
const item = await k8sGet(name, namespace, kind);
37+
if(item) {
38+
return item as T;
39+
}
40+
41+
let fqKind = qualifyToolkitKind(kind);
42+
43+
const shellResult = await invokeKubectlCommand(`get ${fqKind}/${name} --namespace=${namespace} -o json`);
3444
if (shellResult?.code !== 0) {
3545
telemetry.sendError(TelemetryError.FAILED_TO_GET_RESOURCE);
3646
return;
3747
}
3848

39-
return parseJson(shellResult.stdout);
49+
return parseJson(shellResult.stdout) as T;
4050
}
4151

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

102+
export async function getCanaries(): Promise<Canary[]> {
103+
return getResourcesAllNamespaces(Kind.Canary, TelemetryError.FAILED_TO_GET_HELM_RELEASES);
104+
}
105+
106+
export async function getPipelines(): Promise<Pipeline[]> {
107+
return getResourcesAllNamespaces(Kind.Pipeline, TelemetryError.FAILED_TO_GET_HELM_RELEASES);
108+
}
109+
110+
export async function getGitOpsSet(): Promise<GitOpsSet[]> {
111+
return getResourcesAllNamespaces(Kind.GitOpsSet, TelemetryError.FAILED_TO_GET_HELM_RELEASES);
112+
}
113+
92114

93115
/**
94116
* Get all flux system deployments.
@@ -118,8 +140,7 @@ export async function getFluxControllers(context?: string): Promise<Deployment[]
118140
* @param name name of the kustomize/helmRelease object
119141
* @param namespace namespace of the kustomize/helmRelease object
120142
*/
121-
export async function getChildrenOfWorkload(
122-
workload: 'kustomize' | 'helm',
143+
export async function getHelmReleaseChildren(
123144
name: string,
124145
namespace: string,
125146
): Promise<KubernetesObject[] | undefined> {
@@ -129,22 +150,46 @@ export async function getChildrenOfWorkload(
129150
return;
130151
}
131152

132-
const labelNameSelector = `-l ${workload}.toolkit.fluxcd.io/name=${name}`;
133-
const labelNamespaceSelector = `-l ${workload}.toolkit.fluxcd.io/namespace=${namespace}`;
153+
const labelNameSelector = `-l helm.toolkit.fluxcd.io/name=${name}`;
154+
const labelNamespaceSelector = `-l helm.toolkit.fluxcd.io/namespace=${namespace}`;
134155

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

138159
if (!shellResult || shellResult.code !== 0) {
139160
telemetry.sendError(TelemetryError.FAILED_TO_GET_CHILDREN_OF_A_WORKLOAD);
140-
window.showErrorMessage(`Failed to get ${workload} created resources: ${shellResult?.stderr}`);
161+
window.showErrorMessage(`Failed to get HelmRelease created resources: ${shellResult?.stderr}`);
141162
return;
142163
}
143164

144165
return parseJsonItems(shellResult.stdout);
145166
}
146167

147168

169+
export async function getCanaryChildren(
170+
name: string,
171+
): Promise<KubernetesObject[]> {
172+
// return [];
173+
const resourceKinds = getAvailableResourcePlurals();
174+
if (!resourceKinds) {
175+
return [];
176+
}
177+
178+
const labelNameSelector = `-l app=${name}`;
179+
180+
const query = `get ${resourceKinds.join(',')} ${labelNameSelector} -A -o json`;
181+
const shellResult = await invokeKubectlCommand(query);
182+
183+
if (!shellResult || shellResult.code !== 0) {
184+
telemetry.sendError(TelemetryError.FAILED_TO_GET_CHILDREN_OF_A_WORKLOAD);
185+
window.showErrorMessage(`Failed to get HelmRelease created resources: ${shellResult?.stderr}`);
186+
return [];
187+
}
188+
189+
return parseJsonItems(shellResult.stdout);
190+
}
191+
192+
148193
/**
149194
* Get pods by a deployment name.
150195
* @param name pod target name

0 commit comments

Comments
 (0)