@highstate/k8s 0.9.10 → 0.9.12
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/{chunk-7R2VAXVL.js → chunk-5S4JPM4M.js} +4 -3
- package/dist/chunk-5S4JPM4M.js.map +1 -0
- package/dist/{chunk-3B5DTLGG.js → chunk-6L67WIZW.js} +3 -3
- package/dist/{chunk-R43VRICF.js → chunk-SARVLQZY.js} +63 -25
- package/dist/chunk-SARVLQZY.js.map +1 -0
- package/dist/{chunk-FF3GFWG3.js → chunk-VL7Z5FJQ.js} +3 -3
- package/dist/{chunk-OP75IMU7.js → chunk-WEKIQRCZ.js} +43 -17
- package/dist/chunk-WEKIQRCZ.js.map +1 -0
- package/dist/{chunk-HTQP2NB4.js → chunk-Y3LZSX7I.js} +4 -17
- package/dist/chunk-Y3LZSX7I.js.map +1 -0
- package/dist/deployment-QTPBNKO5.js +10 -0
- package/dist/highstate.manifest.json +8 -8
- package/dist/index.js +8 -36
- package/dist/index.js.map +1 -1
- package/dist/stateful-set-K4GV7ZTK.js +10 -0
- package/dist/units/cert-manager/index.js +3 -3
- package/dist/units/dns01-issuer/index.js +1 -1
- package/dist/units/gateway-api/index.js +1 -1
- package/package.json +9 -9
- package/src/container.ts +36 -1
- package/src/custom.ts +104 -0
- package/src/helm.ts +2 -1
- package/src/index.ts +0 -2
- package/src/network-policy.ts +23 -21
- package/src/network.ts +6 -6
- package/src/service.ts +8 -8
- package/src/shared.ts +7 -19
- package/src/workload.ts +50 -28
- package/dist/chunk-7R2VAXVL.js.map +0 -1
- package/dist/chunk-HTQP2NB4.js.map +0 -1
- package/dist/chunk-OP75IMU7.js.map +0 -1
- package/dist/chunk-R43VRICF.js.map +0 -1
- package/dist/deployment-E3ZTF2IS.js +0 -10
- package/dist/stateful-set-NTU7QKC7.js +0 -10
- /package/dist/{chunk-3B5DTLGG.js.map → chunk-6L67WIZW.js.map} +0 -0
- /package/dist/{chunk-FF3GFWG3.js.map → chunk-VL7Z5FJQ.js.map} +0 -0
- /package/dist/{deployment-E3ZTF2IS.js.map → deployment-QTPBNKO5.js.map} +0 -0
- /package/dist/{stateful-set-NTU7QKC7.js.map → stateful-set-K4GV7ZTK.js.map} +0 -0
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@highstate/k8s",
|
3
|
-
"version": "0.9.
|
3
|
+
"version": "0.9.12",
|
4
4
|
"type": "module",
|
5
5
|
"files": [
|
6
6
|
"dist",
|
@@ -28,12 +28,12 @@
|
|
28
28
|
"generate-crds": "./scripts/generate-crds.sh"
|
29
29
|
},
|
30
30
|
"dependencies": {
|
31
|
-
"@highstate/cert-manager": "^0.9.
|
32
|
-
"@highstate/common": "^0.9.
|
33
|
-
"@highstate/contract": "^0.9.
|
34
|
-
"@highstate/gateway-api": "^0.9.
|
35
|
-
"@highstate/library": "^0.9.
|
36
|
-
"@highstate/pulumi": "^0.9.
|
31
|
+
"@highstate/cert-manager": "^0.9.12",
|
32
|
+
"@highstate/common": "^0.9.12",
|
33
|
+
"@highstate/contract": "^0.9.12",
|
34
|
+
"@highstate/gateway-api": "^0.9.12",
|
35
|
+
"@highstate/library": "^0.9.12",
|
36
|
+
"@highstate/pulumi": "^0.9.12",
|
37
37
|
"@kubernetes/client-node": "^1.1.0",
|
38
38
|
"@pulumi/command": "^1.0.2",
|
39
39
|
"@pulumi/kubernetes": "^4.18.0",
|
@@ -46,7 +46,7 @@
|
|
46
46
|
"remeda": "^2.21.0"
|
47
47
|
},
|
48
48
|
"devDependencies": {
|
49
|
-
"@highstate/cli": "^0.9.
|
49
|
+
"@highstate/cli": "^0.9.12"
|
50
50
|
},
|
51
|
-
"gitHead": "
|
51
|
+
"gitHead": "071ace10161f3fcc4e21b75a502e47cfd6e738a6"
|
52
52
|
}
|
package/src/container.ts
CHANGED
@@ -1,7 +1,14 @@
|
|
1
1
|
import type { PartialKeys } from "@highstate/contract"
|
2
2
|
import type { k8s, network } from "@highstate/library"
|
3
3
|
import { core, type types } from "@pulumi/kubernetes"
|
4
|
-
import {
|
4
|
+
import {
|
5
|
+
normalize,
|
6
|
+
Output,
|
7
|
+
output,
|
8
|
+
type Input,
|
9
|
+
type InputArray,
|
10
|
+
type Unwrap,
|
11
|
+
} from "@highstate/pulumi"
|
5
12
|
import { concat, map, omit } from "remeda"
|
6
13
|
import { PersistentVolumeClaim } from "./pvc"
|
7
14
|
import { Secret } from "./secret"
|
@@ -329,3 +336,31 @@ export function mapWorkloadVolume(volume: WorkloadVolume) {
|
|
329
336
|
|
330
337
|
return volume
|
331
338
|
}
|
339
|
+
|
340
|
+
export function getWorkloadVolumeResourceUuid(volume: WorkloadVolume): Output<string | undefined> {
|
341
|
+
if (volume instanceof PersistentVolumeClaim) {
|
342
|
+
return volume.metadata.uid
|
343
|
+
}
|
344
|
+
|
345
|
+
if (volume instanceof Secret) {
|
346
|
+
return volume.metadata.uid
|
347
|
+
}
|
348
|
+
|
349
|
+
if (volume instanceof ConfigMap) {
|
350
|
+
return volume.metadata.uid
|
351
|
+
}
|
352
|
+
|
353
|
+
if (core.v1.PersistentVolumeClaim.isInstance(volume)) {
|
354
|
+
return volume.metadata.uid
|
355
|
+
}
|
356
|
+
|
357
|
+
if (core.v1.ConfigMap.isInstance(volume)) {
|
358
|
+
return volume.metadata.uid
|
359
|
+
}
|
360
|
+
|
361
|
+
if (core.v1.Secret.isInstance(volume)) {
|
362
|
+
return volume.metadata.uid
|
363
|
+
}
|
364
|
+
|
365
|
+
return output(undefined)
|
366
|
+
}
|
package/src/custom.ts
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
import type { k8s } from "@highstate/library"
|
2
|
+
import { dynamic, interpolate, Output, type Input } from "@pulumi/pulumi"
|
3
|
+
import { KubeConfig } from "@kubernetes/client-node"
|
4
|
+
import { toPromise } from "@highstate/pulumi"
|
5
|
+
|
6
|
+
type CustomOperationInputs = {
|
7
|
+
/**
|
8
|
+
* The cluster to perform the operation on.
|
9
|
+
*/
|
10
|
+
cluster: Input<k8s.Cluster>
|
11
|
+
}
|
12
|
+
|
13
|
+
type WorkloadReloadInputs = CustomOperationInputs & {
|
14
|
+
/**
|
15
|
+
* The type of workload to reload.
|
16
|
+
*/
|
17
|
+
workloadType: Input<"Deployment" | "StatefulSet" | "DaemonSet">
|
18
|
+
|
19
|
+
/**
|
20
|
+
* The namespace of the workload to reload.
|
21
|
+
*/
|
22
|
+
namespace: Input<string>
|
23
|
+
|
24
|
+
/**
|
25
|
+
* The name of the workload to reload.
|
26
|
+
*/
|
27
|
+
name: Input<string>
|
28
|
+
}
|
29
|
+
|
30
|
+
abstract class CustomOperation<
|
31
|
+
TInputs extends CustomOperationInputs = CustomOperationInputs,
|
32
|
+
TOutputs extends CustomOperationInputs = CustomOperationInputs,
|
33
|
+
>
|
34
|
+
extends dynamic.Resource
|
35
|
+
implements dynamic.ResourceProvider<TInputs, TOutputs>
|
36
|
+
{
|
37
|
+
async create(inputs: TInputs): Promise<dynamic.CreateResult<TOutputs>> {
|
38
|
+
const kubeconfig = await this.createKubeConfig(inputs.cluster)
|
39
|
+
|
40
|
+
return {
|
41
|
+
id: this.getId(inputs),
|
42
|
+
outs: await this.onCreate(kubeconfig, inputs),
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
async update(
|
47
|
+
_id: string,
|
48
|
+
outputs: TOutputs,
|
49
|
+
inputs: TInputs,
|
50
|
+
): Promise<dynamic.UpdateResult<TOutputs>> {
|
51
|
+
const kubeconfig = await this.createKubeConfig(inputs.cluster)
|
52
|
+
|
53
|
+
if (this.onUpdate) {
|
54
|
+
return {
|
55
|
+
outs: await this.onUpdate(kubeconfig, inputs, outputs),
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
return { outs: outputs }
|
60
|
+
}
|
61
|
+
|
62
|
+
async delete(_id: string, outputs: TOutputs): Promise<void> {
|
63
|
+
const kubeconfig = await this.createKubeConfig(outputs.cluster)
|
64
|
+
|
65
|
+
if (this.onDelete) {
|
66
|
+
await this.onDelete(kubeconfig, outputs)
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
private async createKubeConfig(cluster: Input<k8s.Cluster>): Promise<KubeConfig> {
|
71
|
+
const clusterConfig = await toPromise(cluster)
|
72
|
+
|
73
|
+
const kubeconfig = new KubeConfig()
|
74
|
+
kubeconfig.loadFromString(clusterConfig.kubeconfig)
|
75
|
+
|
76
|
+
return kubeconfig
|
77
|
+
}
|
78
|
+
|
79
|
+
abstract getId(inputs: TInputs): Input<string>
|
80
|
+
|
81
|
+
abstract onCreate(kubeconfig: KubeConfig, inputs: TInputs): Promise<Omit<TOutputs, "cluster">>
|
82
|
+
|
83
|
+
onUpdate?(
|
84
|
+
kubeconfig: KubeConfig,
|
85
|
+
inputs: TInputs,
|
86
|
+
outputs: TOutputs,
|
87
|
+
): Promise<Omit<TOutputs, "cluster">>
|
88
|
+
|
89
|
+
onDelete?(kubeconfig: KubeConfig, outputs: TOutputs): Promise<void>
|
90
|
+
}
|
91
|
+
|
92
|
+
export class WorkloadReload extends CustomOperation<WorkloadReloadInputs> {
|
93
|
+
override getId(inputs: WorkloadReloadInputs): Output<string> {
|
94
|
+
return interpolate`highstate:k8s:workload-reload:${inputs.workloadType}:${inputs.namespace}:${inputs.name}`
|
95
|
+
}
|
96
|
+
|
97
|
+
override async onCreate(kubeconfig: KubeConfig, inputs: WorkloadReloadInputs) {
|
98
|
+
const apps = kubeconfig.makeApiClient(SchedulingV1Api)
|
99
|
+
|
100
|
+
apps.res
|
101
|
+
|
102
|
+
return {}
|
103
|
+
}
|
104
|
+
}
|
package/src/helm.ts
CHANGED
@@ -56,7 +56,7 @@ export type ChartArgs = Omit<
|
|
56
56
|
/**
|
57
57
|
* The http route args to bind the service to.
|
58
58
|
*/
|
59
|
-
httpRoute?: Input<HttpRouteArgs
|
59
|
+
httpRoute?: Input<Omit<HttpRouteArgs, "cluster">>
|
60
60
|
|
61
61
|
/**
|
62
62
|
* The network policy to apply to the chart.
|
@@ -158,6 +158,7 @@ export class Chart extends ComponentResource {
|
|
158
158
|
name,
|
159
159
|
{
|
160
160
|
...httpRoute,
|
161
|
+
cluster: args.cluster,
|
161
162
|
rule: {
|
162
163
|
backend: this.service,
|
163
164
|
},
|
package/src/index.ts
CHANGED
package/src/network-policy.ts
CHANGED
@@ -30,6 +30,7 @@ import {
|
|
30
30
|
type SelectorLike,
|
31
31
|
} from "./shared"
|
32
32
|
import { getServiceMetadata, isFromCluster, mapServiceToLabelSelector } from "./service"
|
33
|
+
import { requireBestEndpoint } from "./network"
|
33
34
|
|
34
35
|
export type NetworkPolicyPort = {
|
35
36
|
/**
|
@@ -439,34 +440,22 @@ export abstract class NetworkPolicy extends ComponentResource {
|
|
439
440
|
)
|
440
441
|
const parsedEndpoints = endpoints.map(parseL34Endpoint)
|
441
442
|
|
442
|
-
const
|
443
|
+
const endpointsNamespaces = groupBy(parsedEndpoints, endpoint => {
|
443
444
|
const namespace = isFromCluster(endpoint, args.cluster)
|
444
445
|
? endpoint.metadata.k8sService.namespace
|
445
446
|
: ""
|
446
447
|
|
447
|
-
|
448
|
-
? endpoint.metadata.k8sService.targetPort
|
449
|
-
: endpoint.port
|
450
|
-
|
451
|
-
return `${port ?? "0"}:${namespace}`
|
448
|
+
return namespace
|
452
449
|
})
|
453
450
|
|
454
|
-
const l3OnlyRule =
|
455
|
-
? NetworkPolicy.getRuleFromEndpoint(
|
456
|
-
undefined,
|
457
|
-
endpointsByPortsAndNamespaces["0:"],
|
458
|
-
args.cluster,
|
459
|
-
)
|
451
|
+
const l3OnlyRule = endpointsNamespaces[""]
|
452
|
+
? NetworkPolicy.getRuleFromEndpoint(undefined, endpointsNamespaces[""], args.cluster)
|
460
453
|
: undefined
|
461
454
|
|
462
|
-
const otherRules = Object.entries(
|
463
|
-
.filter(([key]) => key !== "
|
464
|
-
.map(([
|
465
|
-
|
466
|
-
const portNumber = parseInt(port, 10)
|
467
|
-
const portValue = isNaN(portNumber) ? port : portNumber
|
468
|
-
|
469
|
-
return NetworkPolicy.getRuleFromEndpoint(portValue, endpoints, args.cluster)
|
455
|
+
const otherRules = Object.entries(endpointsNamespaces)
|
456
|
+
.filter(([key]) => key !== "")
|
457
|
+
.map(([, endpoints]) => {
|
458
|
+
return NetworkPolicy.getRuleFromEndpoint(undefined, endpoints, args.cluster)
|
470
459
|
})
|
471
460
|
|
472
461
|
return [
|
@@ -772,7 +761,7 @@ export abstract class NetworkPolicy extends ComponentResource {
|
|
772
761
|
const parsedEndpoint = parseL34Endpoint(endpoint)
|
773
762
|
|
774
763
|
return NetworkPolicy.create(
|
775
|
-
`allow-egress-to-${l34EndpointToString(parsedEndpoint)}`,
|
764
|
+
`allow-egress-to-${l34EndpointToString(parsedEndpoint).replace(":", "-")}`,
|
776
765
|
{
|
777
766
|
namespace,
|
778
767
|
cluster,
|
@@ -785,6 +774,19 @@ export abstract class NetworkPolicy extends ComponentResource {
|
|
785
774
|
)
|
786
775
|
}
|
787
776
|
|
777
|
+
static allowEgressToBestEndpoint(
|
778
|
+
endpoints: InputArray<InputL34Endpoint>,
|
779
|
+
namespace: Input<NamespaceLike>,
|
780
|
+
cluster: Input<k8s.Cluster>,
|
781
|
+
opts?: ResourceOptions,
|
782
|
+
): Output<NetworkPolicy> {
|
783
|
+
return output({ endpoints, cluster }).apply(({ endpoints, cluster }) => {
|
784
|
+
const bestEndpoint = requireBestEndpoint(endpoints.map(parseL34Endpoint), cluster)
|
785
|
+
|
786
|
+
return NetworkPolicy.allowEgressToEndpoint(bestEndpoint, namespace, cluster, opts)
|
787
|
+
})
|
788
|
+
}
|
789
|
+
|
788
790
|
static allowIngressFromEndpoint(
|
789
791
|
endpoint: InputL34Endpoint,
|
790
792
|
namespace: Input<NamespaceLike>,
|
package/src/network.ts
CHANGED
@@ -2,10 +2,10 @@ import type { k8s, network } from "@highstate/library"
|
|
2
2
|
import { filterEndpoints } from "@highstate/common"
|
3
3
|
import { isFromCluster } from "./service"
|
4
4
|
|
5
|
-
export function getBestEndpoint(
|
6
|
-
endpoints:
|
5
|
+
export function getBestEndpoint<TEndpoint extends network.L34Endpoint>(
|
6
|
+
endpoints: TEndpoint[],
|
7
7
|
cluster?: k8s.Cluster,
|
8
|
-
):
|
8
|
+
): TEndpoint | undefined {
|
9
9
|
if (!endpoints.length) {
|
10
10
|
return undefined
|
11
11
|
}
|
@@ -27,10 +27,10 @@ export function getBestEndpoint(
|
|
27
27
|
return filterEndpoints(endpoints)[0]
|
28
28
|
}
|
29
29
|
|
30
|
-
export function requireBestEndpoint(
|
31
|
-
endpoints:
|
30
|
+
export function requireBestEndpoint<TEndpoint extends network.L34Endpoint>(
|
31
|
+
endpoints: TEndpoint[],
|
32
32
|
cluster: k8s.Cluster,
|
33
|
-
):
|
33
|
+
): TEndpoint {
|
34
34
|
const endpoint = getBestEndpoint(endpoints, cluster)
|
35
35
|
|
36
36
|
if (!endpoint) {
|
package/src/service.ts
CHANGED
@@ -110,7 +110,7 @@ export abstract class Service extends ComponentResource {
|
|
110
110
|
type: string,
|
111
111
|
name: string,
|
112
112
|
args: Inputs,
|
113
|
-
opts: ComponentResourceOptions,
|
113
|
+
opts: ComponentResourceOptions | undefined,
|
114
114
|
|
115
115
|
/**
|
116
116
|
* The cluster info associated with the service.
|
@@ -155,16 +155,16 @@ export abstract class Service extends ComponentResource {
|
|
155
155
|
name: string,
|
156
156
|
service: Input<core.v1.Service>,
|
157
157
|
cluster: Input<k8s.Cluster>,
|
158
|
-
opts
|
158
|
+
opts?: ComponentResourceOptions,
|
159
159
|
): Service {
|
160
160
|
return new WrappedService(name, service, cluster, opts)
|
161
161
|
}
|
162
162
|
|
163
|
-
static
|
163
|
+
static get(
|
164
164
|
name: string,
|
165
165
|
id: ResourceId,
|
166
166
|
cluster: Input<k8s.Cluster>,
|
167
|
-
opts
|
167
|
+
opts?: ComponentResourceOptions,
|
168
168
|
): Service {
|
169
169
|
return new ExternalService(name, id, cluster, opts)
|
170
170
|
}
|
@@ -173,7 +173,7 @@ export abstract class Service extends ComponentResource {
|
|
173
173
|
name: string,
|
174
174
|
entity: Input<k8s.Service>,
|
175
175
|
cluster: Input<k8s.Cluster>,
|
176
|
-
opts
|
176
|
+
opts?: ComponentResourceOptions,
|
177
177
|
): Service {
|
178
178
|
return new ExternalService(
|
179
179
|
name,
|
@@ -277,7 +277,7 @@ export abstract class Service extends ComponentResource {
|
|
277
277
|
}
|
278
278
|
|
279
279
|
class CreatedService extends Service {
|
280
|
-
constructor(name: string, args: ServiceArgs, opts
|
280
|
+
constructor(name: string, args: ServiceArgs, opts?: ComponentResourceOptions) {
|
281
281
|
const service = output(args).apply(args => {
|
282
282
|
return new core.v1.Service(
|
283
283
|
name,
|
@@ -319,7 +319,7 @@ class WrappedService extends Service {
|
|
319
319
|
name: string,
|
320
320
|
service: Input<core.v1.Service>,
|
321
321
|
cluster: Input<k8s.Cluster>,
|
322
|
-
opts
|
322
|
+
opts?: ComponentResourceOptions,
|
323
323
|
) {
|
324
324
|
super(
|
325
325
|
"highstate:k8s:WrappedService",
|
@@ -340,7 +340,7 @@ class ExternalService extends Service {
|
|
340
340
|
name: string,
|
341
341
|
id: Input<ResourceId>,
|
342
342
|
cluster: Input<k8s.Cluster>,
|
343
|
-
opts
|
343
|
+
opts?: ComponentResourceOptions,
|
344
344
|
) {
|
345
345
|
const service = output(id).apply(id => {
|
346
346
|
return core.v1.Service.get(
|
package/src/shared.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import type { PartialKeys } from "@highstate/contract"
|
2
2
|
import type { k8s } from "@highstate/library"
|
3
|
-
import { Output, output, toPromise, type Input, type Unwrap } from "@highstate/pulumi"
|
3
|
+
import { interpolate, Output, output, toPromise, type Input, type Unwrap } from "@highstate/pulumi"
|
4
4
|
import { core, Provider, types } from "@pulumi/kubernetes"
|
5
5
|
import { Namespace } from "./namespace"
|
6
6
|
|
@@ -99,29 +99,17 @@ export function mapNamespaceNameToSelector(
|
|
99
99
|
|
100
100
|
export type ResourceId = {
|
101
101
|
name: Input<string>
|
102
|
-
namespace?: Input<
|
102
|
+
namespace?: Input<NamespaceLike | undefined>
|
103
103
|
}
|
104
104
|
|
105
105
|
export function resourceIdToString(id: Input<ResourceId>): Output<string> {
|
106
106
|
return output(id).apply(metadata => {
|
107
|
-
|
108
|
-
|
109
|
-
|
107
|
+
const namespaceName = metadata.namespace
|
108
|
+
? mapNamespaceLikeToNamespaceName(metadata.namespace)
|
109
|
+
: undefined
|
110
110
|
|
111
|
-
|
112
|
-
|
113
|
-
return `${resourceId.namespace ?? "default"}-${resourceId.name}`
|
114
|
-
}
|
115
|
-
|
116
|
-
return resourceId.name
|
117
|
-
}
|
118
|
-
|
119
|
-
export function getAppDisplayName(resourceId: Unwrap<ResourceId>): string {
|
120
|
-
if (resourceId.namespace !== resourceId.name) {
|
121
|
-
return `${resourceId.namespace ?? "default"}/${resourceId.name}`
|
122
|
-
}
|
123
|
-
|
124
|
-
return resourceId.name
|
111
|
+
return output(namespaceName ? interpolate`${namespaceName}/${metadata.name}` : metadata.name)
|
112
|
+
})
|
125
113
|
}
|
126
114
|
|
127
115
|
export function withPatchName(
|
package/src/workload.ts
CHANGED
@@ -16,18 +16,20 @@ import {
|
|
16
16
|
type CustomResourceOptions,
|
17
17
|
type Input,
|
18
18
|
} from "@pulumi/pulumi"
|
19
|
-
import { uniqueBy } from "remeda"
|
19
|
+
import { filter, isNonNullish, unique, uniqueBy } from "remeda"
|
20
20
|
import { deepmerge } from "deepmerge-ts"
|
21
|
+
import { sha256 } from "crypto-hash"
|
21
22
|
import { commonExtraArgs, getProvider, type CommonArgs } from "./shared"
|
22
23
|
import { mapContainerPortToServicePort, Service, type ServiceArgs } from "./service"
|
23
24
|
import { HttpRoute, type HttpRouteArgs } from "./gateway"
|
24
25
|
import {
|
26
|
+
getWorkloadVolumeResourceUuid,
|
25
27
|
mapContainerToRaw,
|
26
28
|
mapWorkloadVolume,
|
27
29
|
type Container,
|
28
30
|
type WorkloadVolume,
|
29
31
|
} from "./container"
|
30
|
-
import { NetworkPolicy } from "./network-policy"
|
32
|
+
import { NetworkPolicy, type NetworkPolicyArgs } from "./network-policy"
|
31
33
|
import { podSpecDefaults } from "./pod"
|
32
34
|
|
33
35
|
export type WorkloadArgs = CommonArgs & {
|
@@ -40,6 +42,11 @@ export type WorkloadArgs = CommonArgs & {
|
|
40
42
|
* By default, `bash` is used.
|
41
43
|
*/
|
42
44
|
terminalShell?: string
|
45
|
+
|
46
|
+
/**
|
47
|
+
* The network policy to apply to the deployment.
|
48
|
+
*/
|
49
|
+
networkPolicy?: Input<Omit<NetworkPolicyArgs, "selector" | "cluster" | "namespace">>
|
43
50
|
}
|
44
51
|
|
45
52
|
export const workloadExtraArgs = [...commonExtraArgs, "container", "containers"] as const
|
@@ -98,24 +105,24 @@ export function getWorkloadComponents(
|
|
98
105
|
|
99
106
|
const containers = output(args).apply(args => normalize(args.container, args.containers))
|
100
107
|
|
101
|
-
const
|
102
|
-
const containerVolumes = containers
|
103
|
-
|
104
|
-
.map(mapWorkloadVolume)
|
105
|
-
|
106
|
-
const containerVolumeMounts = containers
|
107
|
-
.flatMap(container => {
|
108
|
-
return normalize(container.volumeMount, container.volumeMounts)
|
109
|
-
.map(volumeMount => {
|
110
|
-
return "volume" in volumeMount ? volumeMount.volume : undefined
|
111
|
-
})
|
112
|
-
.filter(Boolean) as WorkloadVolume[]
|
113
|
-
})
|
114
|
-
.map(mapWorkloadVolume)
|
115
|
-
|
116
|
-
return output([...containerVolumes, ...containerVolumeMounts]).apply(
|
117
|
-
uniqueBy(volume => volume.name),
|
108
|
+
const rawVolumes = containers.apply(containers => {
|
109
|
+
const containerVolumes = containers.flatMap(container =>
|
110
|
+
normalize(container.volume, container.volumes),
|
118
111
|
)
|
112
|
+
|
113
|
+
const containerVolumeMounts = containers.flatMap(container => {
|
114
|
+
return normalize(container.volumeMount, container.volumeMounts)
|
115
|
+
.map(volumeMount => {
|
116
|
+
return "volume" in volumeMount ? volumeMount.volume : undefined
|
117
|
+
})
|
118
|
+
.filter(Boolean) as WorkloadVolume[]
|
119
|
+
})
|
120
|
+
|
121
|
+
return output([...containerVolumes, ...containerVolumeMounts])
|
122
|
+
})
|
123
|
+
|
124
|
+
const volumes = rawVolumes.apply(rawVolumes => {
|
125
|
+
return output(rawVolumes.map(mapWorkloadVolume)).apply(uniqueBy(volume => volume.name))
|
119
126
|
})
|
120
127
|
|
121
128
|
const podSpec = output({ args, containers, volumes }).apply(({ args, containers, volumes }) => {
|
@@ -143,18 +150,30 @@ export function getWorkloadComponents(
|
|
143
150
|
return spec
|
144
151
|
})
|
145
152
|
|
146
|
-
const
|
153
|
+
const dependencyHash = rawVolumes.apply(rawVolumes => {
|
154
|
+
return output(rawVolumes.map(getWorkloadVolumeResourceUuid))
|
155
|
+
.apply(filter(isNonNullish))
|
156
|
+
.apply(unique())
|
157
|
+
.apply(ids => sha256(ids.join(",")))
|
158
|
+
})
|
159
|
+
|
160
|
+
const podTemplate = output({ podSpec, dependencyHash }).apply(({ podSpec, dependencyHash }) => {
|
147
161
|
return {
|
148
|
-
metadata: {
|
162
|
+
metadata: {
|
163
|
+
labels,
|
164
|
+
annotations: {
|
165
|
+
"highstate.io/dependency-hash": dependencyHash,
|
166
|
+
},
|
167
|
+
},
|
149
168
|
spec: podSpec,
|
150
169
|
} satisfies types.input.core.v1.PodTemplateSpec
|
151
170
|
})
|
152
171
|
|
153
|
-
const networkPolicy = containers.apply(containers => {
|
172
|
+
const networkPolicy = output({ args, containers }).apply(({ args, containers }) => {
|
154
173
|
const allowedEndpoints = containers.flatMap(container => container.allowedEndpoints ?? [])
|
155
174
|
|
156
|
-
if (allowedEndpoints.length === 0) {
|
157
|
-
return undefined
|
175
|
+
if (allowedEndpoints.length === 0 && !args.networkPolicy) {
|
176
|
+
return output(undefined)
|
158
177
|
}
|
159
178
|
|
160
179
|
return NetworkPolicy.create(
|
@@ -164,13 +183,16 @@ export function getWorkloadComponents(
|
|
164
183
|
namespace: args.namespace,
|
165
184
|
selector: labels,
|
166
185
|
|
167
|
-
|
168
|
-
|
169
|
-
|
186
|
+
...args.networkPolicy,
|
187
|
+
|
188
|
+
egressRules: [
|
189
|
+
...(args.networkPolicy?.egressRules ?? []),
|
190
|
+
...(allowedEndpoints.length > 0 ? [{ toEndpoints: allowedEndpoints }] : []),
|
191
|
+
],
|
170
192
|
},
|
171
193
|
{ ...opts, parent: parent() },
|
172
194
|
)
|
173
|
-
})
|
195
|
+
})
|
174
196
|
|
175
197
|
return { labels, containers, volumes, podSpec, podTemplate, networkPolicy }
|
176
198
|
}
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"sources":["../src/helm.ts"],"sourcesContent":["import type { k8s } from \"@highstate/library\"\nimport { resolve } from \"node:path\"\nimport { mkdir, readFile, unlink } from \"node:fs/promises\"\nimport { normalize, toPromise, type InputMap } from \"@highstate/pulumi\"\nimport { core, helm, types } from \"@pulumi/kubernetes\"\nimport {\n ComponentResource,\n output,\n type ComponentResourceOptions,\n type Input,\n type Output,\n} from \"@pulumi/pulumi\"\nimport spawn from \"nano-spawn\"\nimport { sha256 } from \"crypto-hash\"\nimport { omit } from \"remeda\"\nimport { local } from \"@pulumi/command\"\nimport { glob } from \"glob\"\nimport { NetworkPolicy, type NetworkPolicyArgs } from \"./network-policy\"\nimport { HttpRoute, type HttpRouteArgs } from \"./gateway\"\nimport { getProvider, mapNamespaceLikeToNamespaceName, type NamespaceLike } from \"./shared\"\nimport { getServiceType, Service, type ServiceArgs } from \"./service\"\n\nexport type ChartArgs = Omit<\n helm.v4.ChartArgs,\n \"chart\" | \"version\" | \"repositoryOpts\" | \"namespace\"\n> & {\n /**\n * The namespace to deploy the chart into.\n */\n namespace?: Input<NamespaceLike>\n\n /**\n * The custom name of the primary service exposed by the chart.\n *\n * By default, it is the same as the chart name.\n */\n serviceName?: string\n\n /**\n * The extra args to pass to the main service of the chart.\n *\n * Will be patched via transformations.\n */\n service?: Partial<ServiceArgs>\n\n /**\n * The manifest of the chart to resolve.\n */\n chart: ChartManifest\n\n /**\n * The cluster to create the resource in.\n */\n cluster: Input<k8s.Cluster>\n\n /**\n * The http route args to bind the service to.\n */\n httpRoute?: Input<HttpRouteArgs>\n\n /**\n * The network policy to apply to the chart.\n */\n networkPolicy?: Input<Omit<NetworkPolicyArgs, \"selector\" | \"cluster\" | \"namespace\">>\n\n /**\n * The network policies to apply to the chart.\n */\n networkPolicies?: Input<NetworkPolicyArgs[]>\n}\n\nexport class Chart extends ComponentResource {\n /**\n * The underlying Helm chart.\n */\n public readonly chart: Output<helm.v4.Chart>\n\n /**\n * The HTTP route associated with the deployment.\n */\n public readonly httpRoute: Output<HttpRoute | undefined>\n\n /**\n * The network policies applied to the chart.\n */\n public readonly networkPolicies: Output<NetworkPolicy[]>\n\n constructor(\n private readonly name: string,\n private readonly args: ChartArgs,\n private readonly opts?: ComponentResourceOptions,\n ) {\n super(\"highstate:k8s:Chart\", name, args, opts)\n\n const namespace = output(args.namespace).apply(namespace =>\n output(namespace ? mapNamespaceLikeToNamespaceName(namespace) : \"default\"),\n )\n\n this.chart = output({ args, namespace }).apply(async ({ args, namespace }) => {\n return new helm.v4.Chart(\n name,\n omit(\n {\n ...args,\n chart: resolveHelmChart(args.chart),\n namespace,\n },\n [\"httpRoute\"],\n ),\n {\n ...opts,\n parent: this,\n provider: await getProvider(args.cluster),\n\n transforms: [\n ...(opts?.transforms ?? []),\n\n resourceArgs => {\n const serviceName = args.serviceName ?? name\n const expectedName = `${name}:${namespace}/${serviceName}`\n\n if (\n resourceArgs.type === \"kubernetes:core/v1:Service\" &&\n resourceArgs.name === expectedName\n ) {\n const spec = resourceArgs.props.spec as types.input.core.v1.ServiceSpec\n\n return {\n props: {\n ...resourceArgs.props,\n spec: {\n ...spec,\n ...(args.service ?? {}),\n\n type: getServiceType(args.service, args.cluster),\n\n externalIPs:\n args.service?.externalIPs ?? args.cluster.externalIps ?? spec.externalIPs,\n },\n },\n opts: resourceArgs.opts,\n }\n }\n\n return undefined\n },\n ],\n },\n )\n })\n\n this.httpRoute = output(args.httpRoute).apply(httpRoute => {\n if (!httpRoute) {\n return undefined\n }\n\n return new HttpRoute(\n name,\n {\n ...httpRoute,\n rule: {\n backend: this.service,\n },\n },\n { ...opts, parent: this },\n )\n })\n\n this.networkPolicies = output(args).apply(args => {\n const policies = normalize(args.networkPolicy, args.networkPolicies)\n\n return output(\n policies.map(policy => {\n return NetworkPolicy.create(\n name,\n {\n ...policy,\n\n cluster: args.cluster,\n namespace: args.namespace,\n },\n { ...opts, parent: this },\n )\n }),\n )\n })\n }\n\n get service(): Output<Service> {\n return this.getServiceOutput(undefined)\n }\n\n private readonly services = new Map<string, Service>()\n\n getServiceOutput(name: string | undefined): Output<Service> {\n return output({ args: this.args, chart: this.chart }).apply(({ args, chart }) => {\n const resolvedName = name ?? args.serviceName ?? this.name\n const existingService = this.services.get(resolvedName)\n\n if (existingService) {\n return existingService\n }\n\n const service = getChartServiceOutput(chart, resolvedName)\n\n const wrappedService = Service.wrap(\n //\n resolvedName,\n service,\n args.cluster,\n { ...this.opts, parent: this },\n )\n\n this.services.set(resolvedName, wrappedService)\n return wrappedService\n })\n }\n\n getService(name?: string): Promise<Service> {\n return toPromise(this.getServiceOutput(name))\n }\n}\n\nexport type RenderedChartArgs = {\n /**\n * The namespace to deploy the chart into.\n */\n namespace?: Input<NamespaceLike>\n\n /**\n * The manifest of the chart to resolve.\n */\n chart: ChartManifest\n\n /**\n * The values to pass to the chart.\n */\n values?: InputMap<string>\n}\n\nexport class RenderedChart extends ComponentResource {\n /**\n * The rendered manifest of the Helm chart.\n */\n public readonly manifest: Output<string>\n\n /**\n * The underlying command used to render the chart.\n */\n public readonly command: Output<local.Command>\n\n constructor(name: string, args: RenderedChartArgs, opts?: ComponentResourceOptions) {\n super(\"highstate:k8s:RenderedChart\", name, args, opts)\n\n this.command = output(args).apply(args => {\n const values = args.values\n ? Object.entries(args.values).flatMap(([key, value]) => [\"--set\", `${key}=\"${value}\"`])\n : []\n\n return new local.Command(\n name,\n {\n create: output([\n \"helm\",\n \"template\",\n resolveHelmChart(args.chart),\n\n ...(args.namespace\n ? [\"--namespace\", mapNamespaceLikeToNamespaceName(args.namespace)]\n : []),\n\n ...values,\n ]).apply(command => command.join(\" \")),\n\n logging: \"stderr\",\n },\n { parent: this, ...opts },\n )\n })\n\n this.manifest = this.command.stdout\n\n this.registerOutputs({ manifest: this.manifest, command: this.command })\n }\n}\n\nexport type ChartManifest = {\n repo: string\n name: string\n version: string\n sha256: string\n}\n\n/**\n * Downloads or reuses the Helm chart according to the charts.json file.\n * Returns the full path to the chart's .tgz file.\n *\n * @param manifest The manifest of the Helm chart.\n */\nexport async function resolveHelmChart(manifest: ChartManifest): Promise<string> {\n if (!process.env.HIGHSTATE_CACHE_DIR) {\n throw new Error(\"Environment variable HIGHSTATE_CACHE_DIR is not set\")\n }\n\n const chartsDir = resolve(process.env.HIGHSTATE_CACHE_DIR, \"charts\")\n await mkdir(chartsDir, { recursive: true })\n\n const globPattern = `${manifest.name}-*.tgz`\n const targetFileName = `${manifest.name}-${manifest.version}.tgz`\n\n // find all matching files\n const files = await glob(globPattern, { cwd: chartsDir })\n\n if (files.includes(targetFileName)) {\n return resolve(chartsDir, targetFileName)\n }\n\n // delete old versions\n for (const file of files) {\n await unlink(resolve(chartsDir, file))\n }\n\n // download the chart\n await spawn(\"helm\", [\n \"pull\",\n manifest.name,\n \"--version\",\n manifest.version,\n \"--repo\",\n manifest.repo,\n \"--destination\",\n chartsDir,\n ])\n\n // check the SHA256\n const content = await readFile(resolve(chartsDir, targetFileName))\n const actualSha256 = await sha256(content)\n\n if (actualSha256 !== manifest.sha256) {\n throw new Error(`SHA256 mismatch for chart '${manifest.name}'`)\n }\n\n return resolve(chartsDir, targetFileName)\n}\n\n/**\n * Extracts the service with the given name from the chart resources.\n * Throws an error if the service is not found.\n *\n * @param chart The Helm chart.\n * @param name The name of the service.\n */\nexport function getChartServiceOutput(chart: helm.v4.Chart, name: string): Output<core.v1.Service> {\n const services = chart.resources.apply(resources => {\n return resources\n .filter(r => core.v1.Service.isInstance(r))\n .map(service => ({ name: service.metadata.name, service }))\n })\n\n return output(services).apply(services => {\n const service = services.find(s => s.name === name)?.service\n\n if (!service) {\n throw new Error(`Service with name '${name}' not found in the chart resources`)\n }\n\n return service\n })\n}\n\n/**\n * Extracts the service with the given name from the chart resources.\n * Throws an error if the service is not found.\n *\n * @param chart The Helm chart.\n * @param name The name of the service.\n */\nexport function getChartService(chart: helm.v4.Chart, name: string): Promise<core.v1.Service> {\n return toPromise(getChartServiceOutput(chart, name))\n}\n"],"mappings":"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","names":["namespace","args","services"]}
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"sources":["../src/namespace.ts","../src/shared.ts"],"sourcesContent":["import type { k8s } from \"@highstate/library\"\nimport { core, type types } from \"@pulumi/kubernetes\"\nimport {\n ComponentResource,\n output,\n Output,\n type ComponentResourceOptions,\n type Input,\n type Inputs,\n type Unwrap,\n} from \"@pulumi/pulumi\"\nimport { getProvider, mapMetadata, type CommonArgs } from \"./shared\"\n\nexport type NamespaceArgs = Omit<CommonArgs, \"namespace\"> & {\n /**\n * Whether to apply \"pod-security.kubernetes.io/enforce=privileged\" label to the namespace.\n */\n privileged?: boolean\n}\n\nexport type CreateOrPatchNamespaceArgs = NamespaceArgs & {\n /**\n * The resource to use to determine the name of the namespace.\n *\n * If not provided, the namespace will be created, otherwise it will be retrieved/patched.\n */\n resource: Input<k8s.Resource> | undefined\n}\n\nexport abstract class Namespace extends ComponentResource {\n protected constructor(\n type: string,\n name: string,\n args: Inputs,\n opts: ComponentResourceOptions | undefined,\n\n /**\n * The cluster where the namespace is created.\n */\n readonly cluster: Output<k8s.Cluster>,\n\n /*\n * The metadata of the underlying Kubernetes namespace.\n */\n readonly metadata: Output<types.output.meta.v1.ObjectMeta>,\n\n /**\n * The spec of the underlying Kubernetes namespace.\n */\n readonly spec: Output<types.output.core.v1.NamespaceSpec>,\n\n /**\n * The status of the underlying Kubernetes namespace.\n */\n readonly status: Output<types.output.core.v1.NamespaceStatus>,\n ) {\n super(type, name, args, opts)\n }\n\n /**\n * Creates a new namespace.\n */\n static create(name: string, args: NamespaceArgs, opts?: ComponentResourceOptions): Namespace {\n return new CreatedNamespace(name, args, opts)\n }\n\n /**\n * Creates a new namespace or patches an existing one.\n *\n * Will throw an error if the namespace does not exist when `args.resource` is provided.\n */\n static createOrPatch(\n name: string,\n args: CreateOrPatchNamespaceArgs,\n opts?: ComponentResourceOptions,\n ): Namespace {\n if (!args.resource) {\n return new CreatedNamespace(name, args, opts)\n }\n\n return new NamespacePatch(\n name,\n {\n ...args,\n name: output(args).apply(args => {\n if (args.resource!.clusterId !== args.cluster.id) {\n throw new Error(\n `Cluster mismatch when patching namespace \"${name}\": \"${args.resource!.clusterId}\" != \"${args.cluster.id}\"`,\n )\n }\n\n return args.resource!.metadata.namespace\n }),\n },\n opts,\n )\n }\n\n /**\n * Creates a new namespace or gets an existing one.\n *\n * Will throw an error if the namespace does not exist when `args.resource` is provided.\n */\n static createOrGet(\n name: string,\n args: CreateOrPatchNamespaceArgs,\n opts?: ComponentResourceOptions,\n ): Namespace {\n if (!args.resource) {\n return new CreatedNamespace(name, args, opts)\n }\n\n return new ExternalNamespace(\n name,\n output(args).apply(args => {\n if (args.resource!.clusterId !== args.cluster.id) {\n throw new Error(\n `Cluster mismatch when receiving namespace \"${name}\": \"${args.resource!.clusterId}\" != \"${args.cluster.id}\"`,\n )\n }\n\n return args.resource!.metadata.namespace\n }),\n args.cluster,\n opts,\n )\n }\n\n /**\n * Patches an existing namespace.\n *\n * Will throw an error if the namespace does not exist.\n */\n static patch(name: string, args: NamespaceArgs, opts?: ComponentResourceOptions): Namespace {\n return new NamespacePatch(name, args, opts)\n }\n\n /**\n * Gets an existing namespace.\n *\n * Will throw an error if the namespace does not exist.\n */\n static get(\n name: string,\n id: Input<string>,\n cluster: Input<k8s.Cluster>,\n opts?: ComponentResourceOptions,\n ): Namespace {\n return new ExternalNamespace(name, id, cluster, opts)\n }\n}\n\nfunction mapNamespaceMetadata(\n args: Unwrap<NamespaceArgs>,\n name: string,\n): types.input.meta.v1.ObjectMeta {\n const labels: Record<string, string> = args.metadata?.labels ?? {}\n\n if (args.privileged) {\n labels[\"pod-security.kubernetes.io/enforce\"] = \"privileged\"\n }\n\n return { ...mapMetadata(args, name), labels }\n}\n\nclass CreatedNamespace extends Namespace {\n constructor(name: string, args: NamespaceArgs, opts?: ComponentResourceOptions) {\n const namespace = output(args).apply(async args => {\n return new core.v1.Namespace(\n name,\n {\n metadata: mapNamespaceMetadata(args, name),\n },\n {\n ...opts,\n parent: this,\n provider: await getProvider(args.cluster),\n },\n )\n })\n\n super(\n \"highstate:k8s:Namespace\",\n name,\n args,\n opts,\n output(args.cluster),\n namespace.metadata,\n namespace.spec,\n namespace.status,\n )\n }\n}\n\nclass NamespacePatch extends Namespace {\n constructor(name: string, args: NamespaceArgs, opts?: ComponentResourceOptions) {\n const namespace = output(args).apply(async args => {\n return new core.v1.NamespacePatch(\n name,\n {\n metadata: mapNamespaceMetadata(args, name),\n },\n {\n ...opts,\n parent: this,\n provider: await getProvider(args.cluster),\n },\n )\n })\n\n super(\n \"highstate:k8s:NamespacePatch\",\n name,\n args,\n opts,\n output(args.cluster),\n namespace.metadata,\n namespace.spec,\n namespace.status,\n )\n }\n}\n\nclass ExternalNamespace extends Namespace {\n constructor(\n name: string,\n id: Input<string>,\n cluster: Input<k8s.Cluster>,\n opts?: ComponentResourceOptions,\n ) {\n const namespace = output(id).apply(async realName => {\n return core.v1.Namespace.get(\n //\n name,\n realName,\n {\n ...opts,\n parent: this,\n provider: await getProvider(cluster),\n },\n )\n })\n\n super(\n \"highstate:k8s:ExternalNamespace\",\n name,\n { id, cluster },\n opts,\n output(cluster),\n namespace.metadata,\n namespace.spec,\n namespace.status,\n )\n }\n}\n","import type { PartialKeys } from \"@highstate/contract\"\nimport type { k8s } from \"@highstate/library\"\nimport { Output, output, toPromise, type Input, type Unwrap } from \"@highstate/pulumi\"\nimport { core, Provider, types } from \"@pulumi/kubernetes\"\nimport { Namespace } from \"./namespace\"\n\nconst providers = new Map<string, Provider>()\n\nexport function getProvider(cluster: Input<k8s.Cluster>): Promise<Provider> {\n const provider = output(cluster).apply(cluster => {\n const existingProvider = providers.get(cluster.id)\n if (existingProvider) {\n return existingProvider\n }\n\n const provider = new Provider(`${cluster.name}-${cluster.id}`, {\n kubeconfig: cluster.kubeconfig,\n })\n providers.set(cluster.id, provider)\n\n return provider\n })\n\n return toPromise(provider)\n}\n\nexport type NamespaceLike = core.v1.Namespace | Namespace | string\n\nexport type CommonArgs = {\n /**\n * The name of the resource.\n */\n name?: Input<string>\n\n /**\n * The namespace to create the resource in.\n */\n namespace: Input<NamespaceLike | undefined>\n\n /**\n * The cluster to create the resource in.\n */\n cluster: Input<k8s.Cluster>\n\n /**\n * The metadata to apply to the resource.\n */\n metadata?: Input<types.input.meta.v1.ObjectMeta>\n}\n\nexport const commonExtraArgs = [\"name\", \"namespace\", \"cluster\", \"metadata\"] as const\n\nexport function mapMetadata(\n args: PartialKeys<Unwrap<CommonArgs>, \"namespace\" | \"cluster\">,\n fallbackName?: string,\n): types.input.meta.v1.ObjectMeta {\n return {\n ...args.metadata,\n name: args.name ?? args.metadata?.name ?? fallbackName,\n namespace: args.namespace ? mapNamespaceLikeToNamespaceName(args.namespace) : undefined,\n }\n}\n\nexport type SelectorLike = types.input.meta.v1.LabelSelector | Record<string, Input<string>>\n\nexport function mapSelectorLikeToSelector(\n selector: SelectorLike,\n): types.input.meta.v1.LabelSelector {\n if (\"matchLabels\" in selector || \"matchExpressions\" in selector) {\n return selector\n }\n\n return {\n matchLabels: selector as Record<string, Input<string>>,\n }\n}\n\nexport function mapNamespaceLikeToNamespaceName(namespace: NamespaceLike): Output<string> {\n if (Namespace.isInstance(namespace)) {\n return namespace.metadata.name\n }\n\n if (core.v1.Namespace.isInstance(namespace)) {\n return namespace.metadata.name\n }\n\n return output(namespace)\n}\n\nexport function mapNamespaceNameToSelector(\n namespace: Input<string>,\n): types.input.meta.v1.LabelSelector {\n return {\n matchLabels: {\n \"kubernetes.io/metadata.name\": namespace,\n },\n }\n}\n\nexport type ResourceId = {\n name: Input<string>\n namespace?: Input<string | undefined>\n}\n\nexport function resourceIdToString(id: Input<ResourceId>): Output<string> {\n return output(id).apply(metadata => {\n return metadata.namespace ? `${metadata.namespace}/${metadata.name}` : metadata.name\n })\n}\n\nexport function getAppName(resourceId: Unwrap<ResourceId>): string {\n if (resourceId.namespace !== resourceId.name) {\n return `${resourceId.namespace ?? \"default\"}-${resourceId.name}`\n }\n\n return resourceId.name\n}\n\nexport function getAppDisplayName(resourceId: Unwrap<ResourceId>): string {\n if (resourceId.namespace !== resourceId.name) {\n return `${resourceId.namespace ?? \"default\"}/${resourceId.name}`\n }\n\n return resourceId.name\n}\n\nexport function withPatchName(\n resourceType: string,\n resource: Input<k8s.Resource>,\n cluster: Input<k8s.Cluster>,\n): Output<string> {\n return output({ resource, cluster }).apply(({ resource, cluster }) => {\n if (resource.clusterId !== cluster.id) {\n throw new Error(\n `Cluster mismatch when patching ${resourceType} \"${resource.metadata.name}\": \"${resource.clusterId}\" != \"${cluster.id}\"`,\n )\n }\n\n return resource.metadata.name\n })\n}\n"],"mappings":"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","names":["core","output","cluster","provider","resource","output","args","core"]}
|