@highstate/k8s 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{helm-BV9UE-B8.js → helm-wPTgVV1N.js} +179 -48
- package/dist/index.d.ts +339 -74
- package/dist/index.js +531 -198
- package/dist/shared-Clzbl5K-.js +89 -0
- package/dist/units/cert-manager/index.js +6 -5
- package/dist/units/dns01-issuer/index.js +3 -3
- package/dist/units/existing-cluster/index.js +21 -5
- package/package.json +8 -7
- package/dist/shared-r5jBysdR.js +0 -51
@@ -1,46 +1,173 @@
|
|
1
1
|
import { resolve } from 'path';
|
2
2
|
import { mkdir, unlink, readFile } from 'node:fs/promises';
|
3
|
-
import { ComponentResource, output,
|
3
|
+
import { ComponentResource, output, interpolate, normalize, toPromise } from '@highstate/pulumi';
|
4
4
|
import { core, helm } from '@pulumi/kubernetes';
|
5
5
|
import { ComponentResource as ComponentResource$1, output as output$1 } from '@pulumi/pulumi';
|
6
6
|
import spawn from 'nano-spawn';
|
7
7
|
import { sha256 } from 'crypto-hash';
|
8
|
-
import {
|
8
|
+
import { omit, pipe, map } from 'remeda';
|
9
9
|
import { local } from '@pulumi/command';
|
10
10
|
import { glob } from 'glob';
|
11
|
-
import {
|
11
|
+
import { deepmerge } from 'deepmerge-ts';
|
12
|
+
import { v as verifyProvider, m as mapMetadata, c as commonExtraArgs, r as resourceIdToString, d as mapNamespaceLikeToNamespaceName } from './shared-Clzbl5K-.js';
|
12
13
|
import { gateway } from '@highstate/gateway-api';
|
13
14
|
|
14
|
-
const serviceExtraArgs = [...commonExtraArgs, "port", "ports"];
|
15
|
+
const serviceExtraArgs = [...commonExtraArgs, "port", "ports", "patch"];
|
15
16
|
class Service extends ComponentResource {
|
17
|
+
constructor(type, name, args, opts, clusterInfo, metadata, spec, status, resources) {
|
18
|
+
super(type, name, args, opts);
|
19
|
+
this.clusterInfo = clusterInfo;
|
20
|
+
this.metadata = metadata;
|
21
|
+
this.spec = spec;
|
22
|
+
this.status = status;
|
23
|
+
this.resources = resources;
|
24
|
+
}
|
25
|
+
/**
|
26
|
+
* The Highstate service entity.
|
27
|
+
*/
|
28
|
+
get entity() {
|
29
|
+
return output({
|
30
|
+
type: "k8s.service",
|
31
|
+
clusterInfo: this.clusterInfo,
|
32
|
+
metadata: this.metadata,
|
33
|
+
spec: this.spec
|
34
|
+
});
|
35
|
+
}
|
36
|
+
static create(name, args, opts) {
|
37
|
+
return new CreatedService(name, args, opts);
|
38
|
+
}
|
39
|
+
static wrap(name, service, clusterInfo, opts) {
|
40
|
+
return new WrappedService(name, service, clusterInfo, opts);
|
41
|
+
}
|
42
|
+
static external(name, id, clusterInfo, opts) {
|
43
|
+
return new ExternalService(name, id, clusterInfo, opts);
|
44
|
+
}
|
45
|
+
static of(name, entity, opts) {
|
46
|
+
return new ExternalService(name, output(entity).metadata, output(entity).clusterInfo, opts);
|
47
|
+
}
|
16
48
|
/**
|
17
|
-
*
|
49
|
+
* Returns the host of the service accessible from the cluster.
|
50
|
+
*
|
51
|
+
* The format is `name.namespace.svc`.
|
18
52
|
*/
|
19
|
-
|
53
|
+
get clusterHost() {
|
54
|
+
return interpolate`${this.metadata.name}.${this.metadata.namespace}.svc`;
|
55
|
+
}
|
56
|
+
/**
|
57
|
+
* Returns the external IP of the service.
|
58
|
+
*
|
59
|
+
* If the load balancer is available, the external IP is returned, otherwise the IP of the node.
|
60
|
+
*
|
61
|
+
* If the service has no external IP, `undefined` is returned.
|
62
|
+
*/
|
63
|
+
get externalIp() {
|
64
|
+
return output({ spec: this.spec, status: this.status }).apply(({ spec, status }) => {
|
65
|
+
const loadBalancerIp = status.loadBalancer?.ingress?.[0]?.ip;
|
66
|
+
if (loadBalancerIp) {
|
67
|
+
return loadBalancerIp;
|
68
|
+
}
|
69
|
+
const nodeIp = spec.externalIPs?.[0];
|
70
|
+
if (nodeIp) {
|
71
|
+
return nodeIp;
|
72
|
+
}
|
73
|
+
return void 0;
|
74
|
+
});
|
75
|
+
}
|
76
|
+
/**
|
77
|
+
* The "most public" IP of the service.
|
78
|
+
*
|
79
|
+
* Resolves to the external IP if available, otherwise the cluster IP.
|
80
|
+
*
|
81
|
+
* If the service is headless, an error is thrown.
|
82
|
+
*/
|
83
|
+
get ip() {
|
84
|
+
return output({ spec: this.spec, externalIp: this.externalIp }).apply(
|
85
|
+
({ spec, externalIp }) => {
|
86
|
+
if (externalIp) {
|
87
|
+
return externalIp;
|
88
|
+
}
|
89
|
+
const clusterIp = spec.clusterIP;
|
90
|
+
if (clusterIp && clusterIp !== "None") {
|
91
|
+
return clusterIp;
|
92
|
+
}
|
93
|
+
throw new Error("The service does not have neither an external IP nor a cluster IP.");
|
94
|
+
}
|
95
|
+
);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
class CreatedService extends Service {
|
20
99
|
constructor(name, args, opts) {
|
21
|
-
|
22
|
-
|
23
|
-
return new core.v1.Service(
|
100
|
+
const service = output(args).apply(async (args2) => {
|
101
|
+
await verifyProvider(opts.provider, args2.cluster.info);
|
102
|
+
return new (args2.patch ? core.v1.ServicePatch : core.v1.Service)(
|
24
103
|
name,
|
25
104
|
{
|
26
|
-
metadata: mapMetadata(args2, name),
|
27
|
-
spec:
|
105
|
+
metadata: mapMetadata(args2.patch?.metadata ?? args2, name),
|
106
|
+
spec: deepmerge(
|
28
107
|
{
|
29
108
|
ports: normalize(args2.port, args2.ports)
|
30
109
|
},
|
31
110
|
omit(args2, serviceExtraArgs)
|
32
111
|
)
|
33
112
|
},
|
34
|
-
{
|
113
|
+
{ parent: this, ...opts }
|
35
114
|
);
|
36
115
|
});
|
37
|
-
|
38
|
-
|
116
|
+
super(
|
117
|
+
"highstate:k8s:Service",
|
118
|
+
name,
|
119
|
+
args,
|
120
|
+
opts,
|
121
|
+
output(args.cluster).info,
|
122
|
+
service.metadata,
|
123
|
+
service.spec,
|
124
|
+
service.status,
|
125
|
+
[service]
|
126
|
+
);
|
127
|
+
}
|
128
|
+
}
|
129
|
+
class WrappedService extends Service {
|
130
|
+
constructor(name, service, clusterInfo, opts) {
|
131
|
+
super(
|
132
|
+
"highstate:k8s:WrappedService",
|
133
|
+
name,
|
134
|
+
{ service, clusterInfo },
|
135
|
+
opts,
|
136
|
+
output(clusterInfo),
|
137
|
+
output(service).metadata,
|
138
|
+
output(service).spec,
|
139
|
+
output(service).status,
|
140
|
+
[service]
|
141
|
+
);
|
142
|
+
}
|
143
|
+
}
|
144
|
+
class ExternalService extends Service {
|
145
|
+
constructor(name, id, clusterInfo, opts) {
|
146
|
+
const service = output(id).apply(async (id2) => {
|
147
|
+
await verifyProvider(opts.provider, this.clusterInfo);
|
148
|
+
return core.v1.Service.get(
|
149
|
+
//
|
150
|
+
name,
|
151
|
+
resourceIdToString(id2),
|
152
|
+
{ parent: this, provider: opts.provider }
|
153
|
+
);
|
39
154
|
});
|
155
|
+
super(
|
156
|
+
"highstate:k8s:ExternalService",
|
157
|
+
name,
|
158
|
+
{ id, clusterInfo },
|
159
|
+
opts,
|
160
|
+
output(clusterInfo),
|
161
|
+
service.metadata,
|
162
|
+
service.spec,
|
163
|
+
service.status,
|
164
|
+
[service]
|
165
|
+
);
|
40
166
|
}
|
41
167
|
}
|
42
168
|
function mapContainerPortToServicePort(port) {
|
43
169
|
return {
|
170
|
+
name: port.name,
|
44
171
|
port: port.containerPort,
|
45
172
|
targetPort: port.containerPort,
|
46
173
|
protocol: port.protocol
|
@@ -51,21 +178,9 @@ function mapServiceToLabelSelector(service) {
|
|
51
178
|
matchLabels: service.spec.selector
|
52
179
|
};
|
53
180
|
}
|
54
|
-
function getServiceHost(service) {
|
55
|
-
return interpolate`${service.metadata.name}.${service.metadata.namespace}.svc`;
|
56
|
-
}
|
57
|
-
function getRequiredServicePortByName(service, name) {
|
58
|
-
return service.spec.ports.apply((ports) => {
|
59
|
-
const port = ports.find((p) => p.name === name);
|
60
|
-
if (!port) {
|
61
|
-
throw new Error(`Service does not have a port with name ${name}`);
|
62
|
-
}
|
63
|
-
return port.port;
|
64
|
-
});
|
65
|
-
}
|
66
181
|
|
67
182
|
function resolveBackendRef(ref) {
|
68
|
-
if (
|
183
|
+
if (Service.isInstance(ref)) {
|
69
184
|
return output({
|
70
185
|
name: ref.metadata.name,
|
71
186
|
namespace: ref.metadata.namespace,
|
@@ -77,9 +192,7 @@ function resolveBackendRef(ref) {
|
|
77
192
|
return output({
|
78
193
|
name: service.metadata.name,
|
79
194
|
namespace: service.metadata.namespace,
|
80
|
-
port:
|
81
|
-
return typeof port === "number" ? output(port) : getRequiredServicePortByName(service2, port);
|
82
|
-
})
|
195
|
+
port: ref.port
|
83
196
|
});
|
84
197
|
}
|
85
198
|
return output({
|
@@ -149,16 +262,18 @@ class Chart extends ComponentResource$1 {
|
|
149
262
|
constructor(name, args, opts) {
|
150
263
|
super("highstate:k8s:Chart", name, args, opts);
|
151
264
|
this.name = name;
|
265
|
+
this.args = args;
|
266
|
+
this.opts = opts;
|
152
267
|
this.chart = output$1(args).apply((args2) => {
|
153
268
|
return new helm.v4.Chart(
|
154
269
|
name,
|
155
270
|
omit(
|
156
271
|
{
|
157
272
|
...args2,
|
158
|
-
chart: resolveHelmChart(args2.
|
273
|
+
chart: resolveHelmChart(args2.chart),
|
159
274
|
namespace: args2.namespace ? mapNamespaceLikeToNamespaceName(args2.namespace) : void 0
|
160
275
|
},
|
161
|
-
["
|
276
|
+
["httpRoute"]
|
162
277
|
),
|
163
278
|
{ parent: this, ...opts }
|
164
279
|
);
|
@@ -172,7 +287,7 @@ class Chart extends ComponentResource$1 {
|
|
172
287
|
{
|
173
288
|
...httpRoute,
|
174
289
|
rule: {
|
175
|
-
backend: this.
|
290
|
+
backend: this.service
|
176
291
|
}
|
177
292
|
},
|
178
293
|
{ ...opts, parent: this }
|
@@ -188,8 +303,28 @@ class Chart extends ComponentResource$1 {
|
|
188
303
|
* The HTTP route associated with the deployment.
|
189
304
|
*/
|
190
305
|
httpRoute;
|
306
|
+
get service() {
|
307
|
+
return this.getServiceOutput(void 0);
|
308
|
+
}
|
309
|
+
services = /* @__PURE__ */ new Map();
|
191
310
|
getServiceOutput(name) {
|
192
|
-
return this.chart.apply((
|
311
|
+
return output$1({ args: this.args, chart: this.chart }).apply(({ args, chart }) => {
|
312
|
+
const resolvedName = name ?? args.serviceName ?? this.name;
|
313
|
+
const existingService = this.services.get(resolvedName);
|
314
|
+
if (existingService) {
|
315
|
+
return existingService;
|
316
|
+
}
|
317
|
+
const service = getChartServiceOutput(chart, resolvedName);
|
318
|
+
const wrappedService = Service.wrap(
|
319
|
+
//
|
320
|
+
resolvedName,
|
321
|
+
service,
|
322
|
+
args.cluster.info,
|
323
|
+
{ parent: this, ...this.opts }
|
324
|
+
);
|
325
|
+
this.services.set(resolvedName, wrappedService);
|
326
|
+
return wrappedService;
|
327
|
+
});
|
193
328
|
}
|
194
329
|
getService(name) {
|
195
330
|
return toPromise(this.getServiceOutput(name));
|
@@ -214,7 +349,7 @@ class RenderedChart extends ComponentResource$1 {
|
|
214
349
|
create: output$1([
|
215
350
|
"helm",
|
216
351
|
"template",
|
217
|
-
resolveHelmChart(args2.
|
352
|
+
resolveHelmChart(args2.chart),
|
218
353
|
...args2.namespace ? ["--namespace", mapNamespaceLikeToNamespaceName(args2.namespace)] : [],
|
219
354
|
...values
|
220
355
|
]).apply((command) => command.join(" ")),
|
@@ -227,18 +362,14 @@ class RenderedChart extends ComponentResource$1 {
|
|
227
362
|
this.registerOutputs({ manifest: this.manifest, command: this.command });
|
228
363
|
}
|
229
364
|
}
|
230
|
-
async function resolveHelmChart(
|
231
|
-
const entry = charts[name];
|
232
|
-
if (!entry) {
|
233
|
-
throw new Error(`Chart '${name}' not found in the charts.json`);
|
234
|
-
}
|
365
|
+
async function resolveHelmChart(manifest) {
|
235
366
|
if (!process.env.HIGHSTATE_CACHE_DIR) {
|
236
367
|
throw new Error("Environment variable HIGHSTATE_CACHE_DIR is not set");
|
237
368
|
}
|
238
369
|
const chartsDir = resolve(process.env.HIGHSTATE_CACHE_DIR, "charts");
|
239
370
|
await mkdir(chartsDir, { recursive: true });
|
240
|
-
const globPattern = `${name}-*.tgz`;
|
241
|
-
const targetFileName = `${name}-${
|
371
|
+
const globPattern = `${manifest.name}-*.tgz`;
|
372
|
+
const targetFileName = `${manifest.name}-${manifest.version}.tgz`;
|
242
373
|
const files = await glob(globPattern, { cwd: chartsDir });
|
243
374
|
if (files.includes(targetFileName)) {
|
244
375
|
return resolve(chartsDir, targetFileName);
|
@@ -248,18 +379,18 @@ async function resolveHelmChart(charts, name) {
|
|
248
379
|
}
|
249
380
|
await spawn("helm", [
|
250
381
|
"pull",
|
251
|
-
|
382
|
+
manifest.name,
|
252
383
|
"--version",
|
253
|
-
|
384
|
+
manifest.version,
|
254
385
|
"--repo",
|
255
|
-
|
386
|
+
manifest.repo,
|
256
387
|
"--destination",
|
257
388
|
chartsDir
|
258
389
|
]);
|
259
390
|
const content = await readFile(resolve(chartsDir, targetFileName));
|
260
391
|
const actualSha256 = await sha256(content);
|
261
|
-
if (actualSha256 !==
|
262
|
-
throw new Error(`SHA256 mismatch for chart '${name}'`);
|
392
|
+
if (actualSha256 !== manifest.sha256) {
|
393
|
+
throw new Error(`SHA256 mismatch for chart '${manifest.name}'`);
|
263
394
|
}
|
264
395
|
return resolve(chartsDir, targetFileName);
|
265
396
|
}
|
@@ -279,4 +410,4 @@ function getChartService(chart, name) {
|
|
279
410
|
return toPromise(getChartServiceOutput(chart, name));
|
280
411
|
}
|
281
412
|
|
282
|
-
export { Chart as C, HttpRoute as H, RenderedChart as R, Service as S, mapServiceToLabelSelector as a,
|
413
|
+
export { Chart as C, HttpRoute as H, RenderedChart as R, Service as S, mapServiceToLabelSelector as a, getChartService as b, getChartServiceOutput as g, mapContainerPortToServicePort as m, resolveHelmChart as r };
|