mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,256 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const { test } = require('node:test');
|
||||
const {
|
||||
WorkerCredentialDeliveryConflictError,
|
||||
workerCredentialDeliveryTokenDigest,
|
||||
} = require('@qinglong/runtime-core/worker-credential-delivery');
|
||||
const {
|
||||
formatWorkerCredentialToken,
|
||||
} = require('@qinglong/runtime-core/worker-credential-token');
|
||||
const {
|
||||
WorkerCredentialKubernetesDeliveryAdapter,
|
||||
} = require('@qinglong/cluster-admin/worker-credential-kubernetes-delivery');
|
||||
|
||||
const enabled = Boolean(
|
||||
process.env.QL3_TEST_KUBECONFIG &&
|
||||
process.env.QL3_TEST_KUBERNETES_NAMESPACE,
|
||||
);
|
||||
const integrationTest = enabled ? test : test.skip;
|
||||
|
||||
const IDS = [
|
||||
'223e4567-e89b-42d3-a456-426614174901',
|
||||
'223e4567-e89b-42d3-a456-426614174902',
|
||||
'223e4567-e89b-42d3-a456-426614174903',
|
||||
'223e4567-e89b-42d3-a456-426614174904',
|
||||
];
|
||||
|
||||
function token(credentialId, fill) {
|
||||
return Buffer.from(formatWorkerCredentialToken(
|
||||
credentialId,
|
||||
Buffer.alloc(32, fill).toString('base64url'),
|
||||
));
|
||||
}
|
||||
|
||||
function intent(adapter, deliveryId, credentialId, material, overrides = {}) {
|
||||
return {
|
||||
deliveryId,
|
||||
workerId: 'integration-worker-1',
|
||||
credentialId,
|
||||
credentialVersion: 1,
|
||||
previousCredentialId: null,
|
||||
secretDigest: 'b'.repeat(64),
|
||||
tokenDigest: workerCredentialDeliveryTokenDigest(material),
|
||||
deploymentTargetDigest: adapter.deploymentTargetDigest,
|
||||
deploymentGeneration: `generation-${credentialId}`,
|
||||
stagedAtMs: 1_000,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function committed(candidate) {
|
||||
return {
|
||||
...candidate,
|
||||
version: 1,
|
||||
state: 'credential_committed',
|
||||
credentialCommittedAtMs: candidate.stagedAtMs,
|
||||
publishedAtMs: null,
|
||||
publicationDigest: null,
|
||||
observedAtMs: null,
|
||||
observedSessionId: null,
|
||||
observedSessionVersion: null,
|
||||
previousRevokedAtMs: null,
|
||||
};
|
||||
}
|
||||
|
||||
integrationTest(
|
||||
'real Kubernetes API enforces resourceVersion single winner and delete preconditions',
|
||||
async () => {
|
||||
const k8s = await import('@kubernetes/client-node');
|
||||
const config = new k8s.KubeConfig();
|
||||
config.loadFromFile(process.env.QL3_TEST_KUBECONFIG);
|
||||
const api = config.makeApiClient(k8s.CoreV1Api);
|
||||
const deployments = config.makeApiClient(k8s.AppsV1Api);
|
||||
const namespace = process.env.QL3_TEST_KUBERNETES_NAMESPACE;
|
||||
const stageNamespace =
|
||||
process.env.QL3_TEST_KUBERNETES_STAGE_NAMESPACE ??
|
||||
`${namespace.slice(0, 57)}-stage`;
|
||||
await api.createNamespace({
|
||||
body: {
|
||||
apiVersion: 'v1',
|
||||
kind: 'Namespace',
|
||||
metadata: { name: stageNamespace },
|
||||
},
|
||||
}).catch((error) => {
|
||||
if (error?.code !== 409) throw error;
|
||||
});
|
||||
await api.createNamespacedSecret({
|
||||
namespace,
|
||||
body: {
|
||||
apiVersion: 'v1',
|
||||
kind: 'Secret',
|
||||
type: 'Opaque',
|
||||
metadata: {
|
||||
name: 'integration-worker-credential',
|
||||
labels: {
|
||||
'app.kubernetes.io/managed-by': 'qinglong3',
|
||||
'qinglong.io/worker-credential-target': 'prepared-v3',
|
||||
},
|
||||
},
|
||||
data: {},
|
||||
},
|
||||
});
|
||||
await deployments.createNamespacedDeployment({
|
||||
namespace,
|
||||
body: {
|
||||
apiVersion: 'apps/v1',
|
||||
kind: 'Deployment',
|
||||
metadata: {
|
||||
name: 'integration-worker',
|
||||
namespace,
|
||||
labels: { 'app.kubernetes.io/component': 'worker' },
|
||||
},
|
||||
spec: {
|
||||
replicas: 1,
|
||||
strategy: { type: 'Recreate' },
|
||||
selector: {
|
||||
matchLabels: {
|
||||
app: 'integration-worker',
|
||||
'app.kubernetes.io/component': 'worker',
|
||||
},
|
||||
},
|
||||
template: {
|
||||
metadata: {
|
||||
labels: {
|
||||
app: 'integration-worker',
|
||||
'app.kubernetes.io/component': 'worker',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
containers: [{
|
||||
name: 'worker',
|
||||
image: 'registry.k8s.io/pause:3.10.1',
|
||||
volumeMounts: [{
|
||||
name: 'credential',
|
||||
mountPath: '/credential',
|
||||
readOnly: true,
|
||||
}],
|
||||
}],
|
||||
volumes: [{
|
||||
name: 'credential',
|
||||
projected: {
|
||||
sources: [{
|
||||
secret: {
|
||||
name: 'integration-worker-credential',
|
||||
items: [{
|
||||
key: 'credential.token',
|
||||
path: 'credential-token',
|
||||
}],
|
||||
},
|
||||
}],
|
||||
},
|
||||
}],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const adapter = new WorkerCredentialKubernetesDeliveryAdapter(
|
||||
api,
|
||||
deployments,
|
||||
{
|
||||
clusterIdentity: 'ql3-k3s-integration',
|
||||
namespace,
|
||||
stageNamespace,
|
||||
targetSecretName: 'integration-worker-credential',
|
||||
targetDeploymentName: 'integration-worker',
|
||||
targetDataKey: 'credential.token',
|
||||
},
|
||||
);
|
||||
|
||||
const first = token('integration_generation_1', 1);
|
||||
const firstIntent = intent(
|
||||
adapter,
|
||||
IDS[0],
|
||||
'integration_generation_1',
|
||||
first,
|
||||
);
|
||||
await adapter.stage(firstIntent, first);
|
||||
const initial = await adapter.publish(committed(firstIntent));
|
||||
assert.match(initial.publicationDigest, /^[0-9a-f]{64}$/);
|
||||
const initialTarget = await api.readNamespacedSecret({
|
||||
namespace,
|
||||
name: 'integration-worker-credential',
|
||||
});
|
||||
|
||||
const second = token('integration_generation_2', 2);
|
||||
const third = token('integration_generation_3', 3);
|
||||
const secondIntent = intent(
|
||||
adapter,
|
||||
IDS[1],
|
||||
'integration_generation_2',
|
||||
second,
|
||||
{ previousCredentialId: 'integration_generation_1' },
|
||||
);
|
||||
const thirdIntent = intent(
|
||||
adapter,
|
||||
IDS[2],
|
||||
'integration_generation_3',
|
||||
third,
|
||||
{ previousCredentialId: 'integration_generation_1' },
|
||||
);
|
||||
await adapter.stage(secondIntent, second);
|
||||
await adapter.stage(thirdIntent, third);
|
||||
const results = await Promise.allSettled([
|
||||
adapter.publish(committed(secondIntent)),
|
||||
adapter.publish(committed(thirdIntent)),
|
||||
]);
|
||||
assert.equal(
|
||||
results.filter((result) => result.status === 'fulfilled').length,
|
||||
1,
|
||||
);
|
||||
const rejected = results.find((result) => result.status === 'rejected');
|
||||
assert.ok(rejected.reason instanceof WorkerCredentialDeliveryConflictError);
|
||||
const winner = results[0].status === 'fulfilled' ? secondIntent : thirdIntent;
|
||||
const target = await api.readNamespacedSecret({
|
||||
namespace,
|
||||
name: 'integration-worker-credential',
|
||||
});
|
||||
assert.notEqual(
|
||||
target.metadata.resourceVersion,
|
||||
initialTarget.metadata.resourceVersion,
|
||||
);
|
||||
assert.equal(
|
||||
target.metadata.annotations['qinglong.io/worker-credential-delivery-id'],
|
||||
winner.deliveryId,
|
||||
);
|
||||
const deployment = await deployments.readNamespacedDeployment({
|
||||
namespace,
|
||||
name: 'integration-worker',
|
||||
});
|
||||
assert.equal(
|
||||
deployment.spec.template.metadata.annotations[
|
||||
'qinglong.io/worker-credential-generation'
|
||||
],
|
||||
winner.deploymentGeneration,
|
||||
);
|
||||
|
||||
const orphan = token('integration_orphan', 4);
|
||||
const orphanIntent = intent(
|
||||
adapter,
|
||||
IDS[3],
|
||||
'integration_orphan',
|
||||
orphan,
|
||||
);
|
||||
await adapter.stage(orphanIntent, orphan);
|
||||
await adapter.discard(orphanIntent);
|
||||
assert.equal(await adapter.inspect(orphanIntent.deliveryId), null);
|
||||
const page = await adapter.listStaged({ limit: 4 });
|
||||
assert.equal(page.stages.some((item) =>
|
||||
item.deliveryId === orphanIntent.deliveryId), false);
|
||||
|
||||
first.fill(0);
|
||||
second.fill(0);
|
||||
third.fill(0);
|
||||
orphan.fill(0);
|
||||
},
|
||||
);
|
||||
Reference in New Issue
Block a user