Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
63 changes: 63 additions & 0 deletions cluster/pulumi/canton-network/src/clusterVersion.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import * as pulumi from '@pulumi/pulumi';
import { expect, jest, test } from '@jest/globals';

import { installClusterVersion } from './clusterVersion';

jest.mock('@lfdecentralizedtrust/splice-pulumi-common', () => ({
__esModule: true,
activeVersion: {
type: 'remote',
version: '1.0.0',
},
CLUSTER_HOSTNAME: 'cluster.global',
config: {},
}));

test('version service should return the correct version', async () => {
await pulumi.runtime.setMocks({
newResource(args) {
return {
id: `mock:${args.name}`,
state: args.inputs,
};
},
call(args) {
switch (args.token) {
default:
return args.inputs;
}
},
});

const versionService = installClusterVersion();

const spec =
'spec' in versionService
? (versionService.spec as pulumi.Output<{
hosts: Array<string>;
http: Array<{
match: Array<{
uri: { exact: string };
port: number;
}>;
directResponse: {
status: number;
body: string;
};
}>;
}>)
: undefined;
expect(spec).toBeDefined();
spec?.apply(spec => {
const route = spec.http.find(route =>
route.match.some(match => match.port === 443 && match.uri.exact === '/version')
);
expect(route).toBeDefined();
expect(route?.directResponse.status).toEqual(200);
expect(route?.directResponse.body).toEqual({ string: '1.0.0' });
});

await pulumi.runtime.disconnect();
});
2 changes: 1 addition & 1 deletion cluster/pulumi/canton-network/src/clusterVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import {
activeVersion,
CLUSTER_HOSTNAME,
config,
exactNamespace,
} from '@lfdecentralizedtrust/splice-pulumi-common';
import { exactNamespace } from '@lfdecentralizedtrust/splice-pulumi-common/src/namespace';
import exec from 'node:child_process';

export function installClusterVersion(): k8s.apiextensions.CustomResource {
Expand Down
38 changes: 38 additions & 0 deletions cluster/pulumi/common/src/namespace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import * as k8s from '@pulumi/kubernetes';

// There is no way to read the logical name off a Namespace. Exactly
// specified namespaces are therefore returned as a tuple with the
// logical name, to allow it to be used to ensure distinct Pulumi
// logical names when creating objects of the same name in different
// Kubernetes namespaces.
//
// See: https://github.com/pulumi/pulumi/issues/5234
export interface ExactNamespace {
ns: k8s.core.v1.Namespace;
logicalName: string;
}

export function exactNamespace(
name: string,
withIstioInjection = false,
retainOnDelete?: boolean
): ExactNamespace {
// Namespace with a fully specified name, exactly as it will
// appear within Kubernetes. (No Pulumi suffix.)
const ns = new k8s.core.v1.Namespace(
name,
{
metadata: {
name,
labels: withIstioInjection ? { 'istio-injection': 'enabled' } : {},
},
},
{
retainOnDelete,
}
);

return { ns, logicalName: name };
}
3 changes: 3 additions & 0 deletions cluster/pulumi/common/src/test/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
export * from './pulumi';
24 changes: 24 additions & 0 deletions cluster/pulumi/common/src/test/pulumi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import * as pulumi from '@pulumi/pulumi';

export function collectResources<Result>(
initializeResources: () => Result
): Promise<[Awaited<Result>, Array<pulumi.Resource>]> {
const resources: Array<pulumi.Resource> = [];
return new Promise((resolve, reject) => {
try {
new pulumi.runtime.Stack(async () => {
pulumi.runtime.registerStackTransformation(args => {
resources.push(args.resource);
return undefined;
});
const result = await initializeResources();
resolve([result, resources]);
return {};
});
} catch (error) {
reject(error);
}
});
}
38 changes: 1 addition & 37 deletions cluster/pulumi/common/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import * as k8s from '@pulumi/kubernetes';
import * as fs from 'fs';
import * as nodePath from 'path';
import { PathLike } from 'fs';
Expand Down Expand Up @@ -67,42 +66,7 @@ export const domainLivenessProbeInitialDelaySeconds: string | undefined = config

export const svOnboardingPollingInterval = config.optionalEnv('SV_ONBOARDING_POLLING_INTERVAL');

/// Kubernetes Namespace

// There is no way to read the logical name off a Namespace. Exactly
// specified namespaces are therefore returned as a tuple with the
// logical name, to allow it to be used to ensure distinct Pulumi
// logical names when creating objects of the same name in different
// Kubernetes namespaces.
//
// See: https://github.com/pulumi/pulumi/issues/5234
export interface ExactNamespace {
ns: k8s.core.v1.Namespace;
logicalName: string;
}

export function exactNamespace(
name: string,
withIstioInjection = false,
retainOnDelete?: boolean
): ExactNamespace {
// Namespace with a fully specified name, exactly as it will
// appear within Kubernetes. (No Pulumi suffix.)
const ns = new k8s.core.v1.Namespace(
name,
{
metadata: {
name,
labels: withIstioInjection ? { 'istio-injection': 'enabled' } : {},
},
},
{
retainOnDelete,
}
);

return { ns, logicalName: name };
}
export * from './namespace';

/// Chart Values

Expand Down
90 changes: 90 additions & 0 deletions cluster/pulumi/gha/src/runners.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import * as k8s from '@pulumi/kubernetes';
import * as pulumi from '@pulumi/pulumi';
import { expect, jest, test } from '@jest/globals';
import { collectResources } from '@lfdecentralizedtrust/splice-pulumi-common/src/test';

import { installRunnerScaleSets } from './runners';

jest.mock('./config', () => ({
__esModule: true,
ghaConfig: {
githubRepo: 'dummy',
runnerVersion: 'dummy',
runnerHookVersion: 'dummy',
},
}));
jest.mock('@lfdecentralizedtrust/splice-pulumi-common', () => ({
__esModule: true,
appsAffinityAndTolerations: {},
DOCKER_REPO: 'dummy',
HELM_MAX_HISTORY_SIZE: 42,
imagePullSecretByNamespaceNameForServiceAccount: () => [],
infraAffinityAndTolerations: {},
}));
jest.mock('@lfdecentralizedtrust/splice-pulumi-common/src/config/envConfig', () => ({
__esModule: true,
spliceEnvConfig: {
requireEnv() {
return 'dummy';
},
},
}));

test('GHA runner k8s resources are in the gha-runners namespace', async () => {
await pulumi.runtime.setMocks({
newResource(args) {
return {
id: `mock:${args.name}`,
state: args.inputs,
};
},
call(args) {
switch (args.token) {
case 'gcp:secretmanager/getSecretVersion:getSecretVersion':
return {
...args.inputs,
secretData: '{}',
};
default:
return args.inputs;
}
},
});
const mockController = {
__pulumiResource: true,
name: 'mock-controller',
} as unknown as k8s.helm.v3.Release;

const [, resources] = await collectResources(() => {
installRunnerScaleSets(mockController);
});

for (const resource of resources) {
if (k8s.core.v1.Namespace.isInstance(resource)) {
continue;
}
let namespace: pulumi.Output<string>;
if ('namespace' in resource) {
namespace = resource.namespace as pulumi.Output<string>;
} else if ('metadata' in resource) {
namespace = (resource.metadata as pulumi.Output<k8s.types.output.meta.v1.ObjectMeta>).apply(
meta => meta.namespace
);
} else {
continue;
}
pulumi.all([resource.urn, namespace]).apply(([urn, namespace]) => {
try {
expect(namespace).toEqual('gha-runners');
} catch (error) {
throw new Error(
`Resource [${urn}] should be created in namespace [gha-runners] but is in [${namespace}].`
);
}
});
}

await pulumi.runtime.disconnect();
});