This commit is contained in:
Moustafa Baiou
2020-11-09 14:00:13 -05:00
committed by moustafab
parent 75d63ee0a0
commit 728767e3d3
6 changed files with 71 additions and 71 deletions
@@ -16,6 +16,7 @@
import { getVoidLogger } from '@backstage/backend-common';
import { KubernetesClientBasedFetcher } from './KubernetesFetcher';
import { ObjectFetchParams } from '..';
describe('KubernetesClientProvider', () => {
let clientMock: any;
@@ -57,17 +58,17 @@ describe('KubernetesClientProvider', () => {
clientMock.listServiceForAllNamespaces.mockRejectedValue(errorResponse);
const result = await sut.fetchObjectsForService(
'some-service',
{
const result = await sut.fetchObjectsForService(<ObjectFetchParams>{
serviceId: 'some-service',
clusterDetails: {
name: 'cluster1',
url: 'http://localhost:9999',
serviceAccountToken: 'token',
authProvider: 'serviceAccount',
},
new Set(['pods', 'services']),
'',
);
objectTypesToFetch: new Set(['pods', 'services']),
labelSelector: '',
});
expect(result).toStrictEqual({
errors: [expectedResult],
@@ -121,17 +122,17 @@ describe('KubernetesClientProvider', () => {
},
});
const result = await sut.fetchObjectsForService(
'some-service',
{
const result = await sut.fetchObjectsForService(<ObjectFetchParams>{
serviceId: 'some-service',
clusterDetails: {
name: 'cluster1',
url: 'http://localhost:9999',
serviceAccountToken: 'token',
authProvider: 'serviceAccount',
},
new Set(['pods', 'services']),
'',
);
objectTypesToFetch: new Set(['pods', 'services']),
labelSelector: '',
});
expect(result).toStrictEqual({
errors: [],
@@ -171,17 +172,17 @@ describe('KubernetesClientProvider', () => {
});
it('should throw error on unknown type', () => {
expect(() =>
sut.fetchObjectsForService(
'some-service',
{
sut.fetchObjectsForService(<ObjectFetchParams>{
serviceId: 'some-service',
clusterDetails: {
name: 'cluster1',
url: 'http://localhost:9999',
serviceAccountToken: 'token',
authProvider: 'serviceAccount',
},
new Set<any>(['foo']),
'',
),
objectTypesToFetch: new Set<any>(['foo']),
labelSelector: '',
}),
).toThrow('unrecognised type=foo');
expect(clientMock.listPodForAllNamespaces.mock.calls.length).toBe(0);
@@ -282,17 +283,17 @@ describe('KubernetesClientProvider', () => {
},
});
await sut.fetchObjectsForService(
'some-service',
{
await sut.fetchObjectsForService(<ObjectFetchParams>{
serviceId: 'some-service',
clusterDetails: {
name: 'cluster1',
url: 'http://localhost:9999',
serviceAccountToken: 'token',
authProvider: 'serviceAccount',
},
new Set(['pods', 'services']),
'',
);
objectTypesToFetch: new Set(['pods', 'services']),
labelSelector: '',
});
const mockCall = clientMock.listPodForAllNamespaces.mock.calls[0];
const actualSelector = mockCall[mockCall.length - 1];
@@ -38,6 +38,7 @@ import {
FetchResponseWrapper,
KubernetesFetchError,
KubernetesErrorTypes,
ObjectFetchParams,
} from '..';
import lodash, { Dictionary } from 'lodash';
@@ -106,18 +107,14 @@ export class KubernetesClientBasedFetcher implements KubernetesFetcher {
}
fetchObjectsForService(
serviceId: string,
clusterDetails: ClusterDetails,
objectTypesToFetch: Set<KubernetesObjectTypes>,
labelSelector: string,
params: ObjectFetchParams,
): Promise<FetchResponseWrapper> {
const fetchResults = Array.from(objectTypesToFetch).map(type => {
const fetchResults = Array.from(params.objectTypesToFetch).map(type => {
return this.fetchByObjectType(
clusterDetails,
params.clusterDetails,
type,
labelSelector.length !== 0
? labelSelector
: `backstage.io/kubernetes-id=${serviceId}`,
params.labelSelector ||
`backstage.io/kubernetes-id=${params.serviceId}`,
).catch(captureKubernetesErrorsRethrowOthers);
});
@@ -25,7 +25,7 @@ const fetchObjectsForService = jest.fn();
const getClustersByServiceId = jest.fn();
const goodEntity: ComponentEntityV1alpha1 = <ComponentEntityV1alpha1>{
const goodEntity: ComponentEntityV1alpha1 = {
apiVersion: 'backstage.io/v1beta1',
kind: 'Component',
metadata: {
@@ -15,6 +15,7 @@
*/
import { Logger } from 'winston';
import { ComponentEntityV1alpha1 } from '@backstage/catalog-model';
import {
KubernetesRequestBody,
ClusterDetails,
@@ -22,10 +23,10 @@ import {
KubernetesFetcher,
KubernetesObjectTypes,
ObjectsByEntityResponse,
ObjectFetchParams,
} from '../types/types';
import { KubernetesAuthTranslator } from '../kubernetes-auth-translator/types';
import { KubernetesAuthTranslatorGenerator } from '../kubernetes-auth-translator/KubernetesAuthTranslatorGenerator';
import { ComponentEntityV1alpha1 } from '@backstage/catalog-model';
export type GetKubernetesObjectsForServiceHandler = (
serviceId: string,
@@ -47,18 +48,10 @@ const DEFAULT_OBJECTS = new Set<KubernetesObjectTypes>([
]);
function parseLabelSelector(entity: ComponentEntityV1alpha1): string {
if (
entity &&
entity.spec &&
entity.spec.kubernetes &&
entity.spec.kubernetes.selector
) {
const matchLabels = entity?.spec?.kubernetes?.selector?.matchLabels;
if (matchLabels) {
// TODO: figure out how to convert the selector to the full query param from the yaml
// (as shown here https://github.com/kubernetes/apimachinery/blob/master/pkg/labels/selector.go)
const { matchLabels } = entity.spec.kubernetes.selector;
if (!matchLabels) {
return '';
}
return Object.keys(matchLabels)
.map(key => `${key}=${matchLabels[key.toString()]}`)
.join(',');
@@ -73,7 +66,7 @@ export const handleGetKubernetesObjectsForService: GetKubernetesObjectsForServic
serviceLocator,
logger,
requestBody,
objectsToFetch = DEFAULT_OBJECTS,
objectTypesToFetch = DEFAULT_OBJECTS,
) => {
const clusterDetails: ClusterDetails[] = await serviceLocator.getClustersByServiceId(
serviceId,
@@ -102,13 +95,18 @@ export const handleGetKubernetesObjectsForService: GetKubernetesObjectsForServic
const labelSelector = parseLabelSelector(requestBody.entity);
return Promise.all(
clusterDetailsDecoratedForAuth.map(cd => {
clusterDetailsDecoratedForAuth.map(clusterDetails => {
return fetcher
.fetchObjectsForService(serviceId, cd, objectsToFetch, labelSelector)
.fetchObjectsForService(<ObjectFetchParams>{
serviceId,
clusterDetails,
objectTypesToFetch,
labelSelector,
})
.then(result => {
return {
cluster: {
name: cd.name,
name: clusterDetails.name,
},
resources: result.responses,
errors: result.errors,
@@ -109,14 +109,18 @@ export interface IngressesFetchResponse {
resources: Array<ExtensionsV1beta1Ingress>;
}
export interface ObjectFetchParams {
serviceId: string;
clusterDetails: ClusterDetails;
objectTypesToFetch: Set<KubernetesObjectTypes>;
labelSelector: string;
}
// Fetches information from a kubernetes cluster using the cluster details object
// to target a specific cluster
export interface KubernetesFetcher {
fetchObjectsForService(
serviceId: string,
clusterDetails: ClusterDetails,
objectTypesToFetch: Set<KubernetesObjectTypes>,
labelSelector: string,
params: ObjectFetchParams,
): Promise<FetchResponseWrapper>;
}
+18 -18
View File
@@ -14,10 +14,26 @@ It is only meant for local development, and the setup for it can be found inside
## Surfacing your Kubernetes components as part of an entity
There are 2 ways to surface your kubernetes components as part of an entity.
There are two ways to surface your kubernetes components as part of an entity.
The label selector takes precedence over the annotation/service id.
### Common `backstage.io/kubernetes-id` label
### Full label selector
#### Adding the entity label selector to the spec
In order for Backstage to detect that an entity has kubernetes components the `kubernetes.selector` must have a valid selector. (Currently only matchLabels is supported)
```yaml
spec:
kubernetes:
selector:
matchLabels:
someKey: someValue
other-key: other-value
app.kubernetes.io/name: dice-roller
```
### Common `backstage.io/kubernetes-id` label on objects
#### Adding the entity annotation
@@ -37,19 +53,3 @@ as a part of an entity, Kubernetes components must be labeled with the following
```yaml
'backstage.io/kubernetes-id': <ENTITY_NAME>
```
### Full label selector
#### Adding the entity label selector to the spec
In order for Backstage to detect that an entity has kubernetes components the `kubernetes.selector` must have a valid selector. (Currently only matchLabels is supported)
```yaml
spec:
kubernetes:
selector:
matchLabels:
someKey: someValue
other-key: other-value
app.kubernetes.io/name: dice-roller
```