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

URL-encode the cluster name #45

Merged
merged 1 commit into from
Aug 7, 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
40 changes: 40 additions & 0 deletions plugins/backstage-plugin-flux/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,46 @@ gitops:
baseUrl: https://wego.example.com
```

**NOTE:** The URL generated will include the name of the cluster that is configured in Backstage's cluster location mechanism.

```yaml
kubernetes:
serviceLocatorMethod:
type: 'multiTenant'
clusterLocatorMethods:
- type: 'config'
clusters:
- url: https://192.168.0.1:8000
name: Default
authProvider: 'serviceAccount'
skipTLSVerify: true
skipMetricsLookup: true
serviceAccountToken: ABC123
caData: LS0tLS1CRUdJTiBDRVJUSUZJQ0...
```

For example, this would generate a link to the `Default` cluster.

If you want to link to Weave GitOps Enterprise, you will need to provide the
name and namespace of the cluster you want to query, for example:

```yaml
kubernetes:
serviceLocatorMethod:
type: 'multiTenant'
clusterLocatorMethods:
- type: 'config'
clusters:
- url: https://192.168.0.1:8000
name: test-ns/name
authProvider: 'serviceAccount'
skipTLSVerify: true
skipMetricsLookup: true
serviceAccountToken: ABC123
caData: LS0tLS1CRUdJTiBDRVJUSUZJQ0...

```

## Verification

For the resources where we display a Verification status, if the Flux resource
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,30 @@ import { renderHook } from '@testing-library/react-hooks';
import React, { PropsWithChildren } from 'react';
import { useWeaveFluxDeepLink } from './useWeaveFluxDeepLink';
import { GitRepository, HelmRelease, OCIRepository } from '../objects';
import * as unverifiedGitRepository from '../__fixtures__/unverified_git_repository.json';
import * as unverifiedOCIRepository from '../__fixtures__/unverified_oci_repository.json';


const testHelmRelease = new HelmRelease({
clusterName: 'Default',
payload:
'{"apiVersion":"helm.toolkit.fluxcd.io/v2beta1","kind":"HelmRelease","metadata":{"annotations":{"metadata.weave.works/test":"value"},"creationTimestamp":"2023-05-25T14:14:46Z","finalizers":["finalizers.fluxcd.io"],"generation":5,"name":"normal","namespace":"default","resourceVersion":"1","uid":"82231842-2224-4f22-8576-5babf08d746d"}}',
});

const testGitRepository = new GitRepository({
clusterName: 'Default',
payload:
'{"apiVersion":"source.toolkit.fluxcd.io/v1","kind":"GitRepository","metadata":{"creationTimestamp":"2023-06-22T17:58:23Z","finalizers":["finalizers.fluxcd.io"],"generation":1,"name":"podinfo","namespace":"default","resourceVersion":"137468","uid":"068ec137-b2a0-4b35-90ea-4e9a8a2fe5f6"},"spec":{"interval":"1m","ref":{"branch":"master"},"timeout":"60s","url":"https://github.com/stefanprodan/podinfo"},"status":{"artifact":{"digest":"sha256:f1e2d4a8244772c47d5e10b38768acec57dc404d6409464c15d2eb8c84b28b51","lastUpdateTime":"2023-06-22T17:58:24Z","path":"gitrepository/default/podinfo/e06a5517daf5ac8c5ba74a97135499e40624885a.tar.gz","revision":"master@sha1:e06a5517daf5ac8c5ba74a97135499e40624885a","size":80053,"url":"http://source-controller.flux-system.svc.cluster.local./gitrepository/default/podinfo/e06a5517daf5ac8c5ba74a97135499e40624885a.tar.gz"},"conditions":[{"lastTransitionTime":"2023-06-23T06:58:24Z","message":"stored artifact for revision \'master@sha1:e06a5517daf5ac8c5ba74a97135499e40624885a\'","observedGeneration":1,"reason":"Succeeded","status":"True","type":"Ready"},{"lastTransitionTime":"2023-06-22T17:58:24Z","message":"stored artifact for revision \'master@sha1:e06a5517daf5ac8c5ba74a97135499e40624885a\'","observedGeneration":1,"reason":"Succeeded","status":"True","type":"ArtifactInStorage"}],"observedGeneration":1}}',
JSON.stringify(unverifiedGitRepository),
});

const testOCIRepository = new OCIRepository({
payload: `{"apiVersion":"source.toolkit.fluxcd.io/v1beta2","kind":"OCIRepository","metadata":{"creationTimestamp":"2023-06-23T07:50:47Z","finalizers":["finalizers.fluxcd.io"],"generation":1,"name":"podinfo","namespace":"default","resourceVersion":"143955","uid":"1ec54278-ed2d-4f31-9bb0-39dc7163730e"},"spec":{"interval":"5m","provider":"generic","timeout":"60s","url":"oci://ghcr.io/stefanprodan/manifests/podinfo","verify":{"provider":"cosign"}},"status":{"artifact":{"digest":"sha256:62df151eb3714d9dfa943c7d88192d72466bffa268b25595f85530b793f77524","lastUpdateTime":"2023-06-23T07:50:53Z","metadata":{"org.opencontainers.image.created":"2023-05-03T14:30:58Z","org.opencontainers.image.revision":"6.3.6/073f1ec5aff930bd3411d33534e91cbe23302324","org.opencontainers.image.source":"https://github.com/stefanprodan/podinfo"},"path":"ocirepository/default/podinfo/sha256:2982c337af6ba98c0e9224a5d7149a19baa9cbedea09b16ae44253682050b6a4.tar.gz","revision":"latest@sha256:2982c337af6ba98c0e9224a5d7149a19baa9cbedea09b16ae44253682050b6a4","size":1071,"url":"http://source-controller.flux-system.svc.cluster.local./ocirepository/default/podinfo/sha256:2982c337af6ba98c0e9224a5d7149a19baa9cbedea09b16ae44253682050b6a4.tar.gz"},"conditions":[{"lastTransitionTime":"2023-06-23T07:50:53Z","message":"stored artifact for digest 'latest@sha256:2982c337af6ba98c0e9224a5d7149a19baa9cbedea09b16ae44253682050b6a4'","observedGeneration":1,"reason":"Succeeded","status":"True","type":"Ready"},{"lastTransitionTime":"2023-06-23T07:50:53Z","message":"stored artifact for digest 'latest@sha256:2982c337af6ba98c0e9224a5d7149a19baa9cbedea09b16ae44253682050b6a4'","observedGeneration":1,"reason":"Succeeded","status":"True","type":"ArtifactInStorage"}],"observedGeneration":1,"url":"http://source-controller.flux-system.svc.cluster.local./ocirepository/default/podinfo/latest.tar.gz"}}`,
clusterName: 'demo-cluster',
payload:
JSON.stringify(unverifiedOCIRepository),
});

let gitOpsUrl: string | undefined;

const mockConfigApi = {
getOptionalString: jest.fn(() => gitOpsUrl),
} as Partial<ConfigApi>;
Expand Down Expand Up @@ -47,7 +57,7 @@ describe('useWeaveFluxDeepLink', () => {
},
);
expect(result.current).toBe(
'https://example.com/helm_release/details?clusterName=&name=normal&namespace=default',
'https://example.com/helm_release/details?clusterName=Default&name=normal&namespace=default',
);
});

Expand All @@ -61,7 +71,7 @@ describe('useWeaveFluxDeepLink', () => {
},
);
expect(result.current).toBe(
'https://example.com/git_repo/details?clusterName=&name=podinfo&namespace=default',
'https://example.com/git_repo/details?clusterName=Default&name=podinfo&namespace=backstage',
);
});

Expand All @@ -75,7 +85,7 @@ describe('useWeaveFluxDeepLink', () => {
},
);
expect(result.current).toBe(
'https://example.com/oci/details?clusterName=&name=podinfo&namespace=default',
'https://example.com/oci/details?clusterName=demo-cluster&name=testing&namespace=default',
);
});
});
Expand All @@ -91,7 +101,7 @@ describe('useWeaveFluxDeepLink', () => {
},
);
expect(result.current).toBe(
'https://example.com/helm_release/details?clusterName=&name=normal&namespace=default',
'https://example.com/helm_release/details?clusterName=Default&name=normal&namespace=default',
);
});
});
Expand All @@ -107,4 +117,26 @@ describe('useWeaveFluxDeepLink', () => {
expect(result.current).toBeUndefined();
});
});

describe('when the cluster name has a namespace name/test-ns', () => {
it('is correctly URL encoded into the request URL', async () => {
gitOpsUrl = 'https://example.com/';

const helmRelease = new HelmRelease({
clusterName: 'demo-ns/test-cluster',
payload:
'{"apiVersion":"helm.toolkit.fluxcd.io/v2beta1","kind":"HelmRelease","metadata":{"annotations":{"metadata.weave.works/test":"value"},"creationTimestamp":"2023-05-25T14:14:46Z","finalizers":["finalizers.fluxcd.io"],"generation":5,"name":"normal","namespace":"default","resourceVersion":"1","uid":"82231842-2224-4f22-8576-5babf08d746d"}}',
});


const { result } = renderHook(
() => useWeaveFluxDeepLink(helmRelease),
{
wrapper,
},
);
expect(result.current).toBe(
'https://example.com/helm_release/details?clusterName=demo-ns%2Ftest-cluster&name=normal&namespace=default',
); });
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { configApiRef, useApi } from '@backstage/core-plugin-api';

import {
FluxObject,
GitRepository,
Expand All @@ -8,18 +9,17 @@ import {
OCIRepository,
} from '../objects';


const typedUrl = (baseUrl: string, a: FluxObject, type: string): string => {
const queryStringData = {
clusterName: a.clusterName,
name: a.name,
namespace: a.namespace,
};

const queryString = Object.entries(queryStringData)
.map(([key, value]) => `${key}=${value}`)
.join('&');
const searchParams = new URLSearchParams(queryStringData);

return `${baseUrl.replace(/\/$/, '')}/${type}/details?${queryString}`;
return `${baseUrl.replace(/\/$/, '')}/${type}/details?${searchParams.toString()}`;
};

export const useWeaveFluxDeepLink = (
Expand Down