@highstate/k8s 0.9.16 → 0.9.19
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-2EEHJZPD.js +13 -0
- package/dist/chunk-2EEHJZPD.js.map +1 -0
- package/dist/{chunk-OFFSHGC6.js → chunk-4JGXGN2L.js} +66 -48
- package/dist/chunk-4JGXGN2L.js.map +1 -0
- package/dist/chunk-A3XGSDIW.js +306 -0
- package/dist/chunk-A3XGSDIW.js.map +1 -0
- package/dist/chunk-IMTXUK2U.js +244 -0
- package/dist/chunk-IMTXUK2U.js.map +1 -0
- package/dist/chunk-JYNXQ3I3.js +287 -0
- package/dist/chunk-JYNXQ3I3.js.map +1 -0
- package/dist/{chunk-5C2BJGES.js → chunk-KDD6XUWM.js} +30 -23
- package/dist/chunk-KDD6XUWM.js.map +1 -0
- package/dist/chunk-NOFJC3EM.js +236 -0
- package/dist/chunk-NOFJC3EM.js.map +1 -0
- package/dist/chunk-NXSYCA3V.js +337 -0
- package/dist/chunk-NXSYCA3V.js.map +1 -0
- package/dist/chunk-SBC3TUIN.js +1513 -0
- package/dist/chunk-SBC3TUIN.js.map +1 -0
- package/dist/chunk-SI7X6N46.js +338 -0
- package/dist/chunk-SI7X6N46.js.map +1 -0
- package/dist/chunk-WGMJCZSK.js +360 -0
- package/dist/chunk-WGMJCZSK.js.map +1 -0
- package/dist/deployment-752P6JIT.js +8 -0
- package/dist/{deployment-XK3CDJOE.js.map → deployment-752P6JIT.js.map} +1 -1
- package/dist/highstate.manifest.json +8 -7
- package/dist/impl/gateway-route.js +123 -0
- package/dist/impl/gateway-route.js.map +1 -0
- package/dist/impl/tls-certificate.js +32 -0
- package/dist/impl/tls-certificate.js.map +1 -0
- package/dist/index.js +736 -208
- package/dist/index.js.map +1 -1
- package/dist/stateful-set-N64YVKR7.js +8 -0
- package/dist/{stateful-set-7CAQWTV2.js.map → stateful-set-N64YVKR7.js.map} +1 -1
- package/dist/units/cert-manager/index.js +11 -10
- package/dist/units/cert-manager/index.js.map +1 -1
- package/dist/units/cluster-dns/index.js.map +1 -1
- package/dist/units/cluster-patch/index.js.map +1 -1
- package/dist/units/dns01-issuer/index.js +27 -23
- package/dist/units/dns01-issuer/index.js.map +1 -1
- package/dist/units/existing-cluster/index.js +11 -8
- package/dist/units/existing-cluster/index.js.map +1 -1
- package/dist/units/gateway-api/index.js +2 -2
- package/dist/units/gateway-api/index.js.map +1 -1
- package/package.json +40 -14
- package/src/cluster.ts +30 -22
- package/src/config-map.ts +195 -57
- package/src/container.ts +5 -5
- package/src/cron-job.ts +403 -31
- package/src/deployment.ts +260 -120
- package/src/dns01-solver.ts +10 -0
- package/src/gateway/backend.ts +2 -2
- package/src/gateway/gateway.ts +383 -0
- package/src/gateway/http-route.ts +17 -24
- package/src/gateway/index.ts +1 -0
- package/src/helm.ts +83 -53
- package/src/impl/gateway-route.ts +155 -0
- package/src/impl/tls-certificate.ts +33 -0
- package/src/index.ts +22 -67
- package/src/job.ts +393 -28
- package/src/namespace.ts +236 -99
- package/src/network-policy.ts +216 -165
- package/src/network.ts +2 -2
- package/src/pvc.ts +266 -65
- package/src/rbac.ts +218 -0
- package/src/scripting/bundle.ts +9 -20
- package/src/scripting/container.ts +1 -1
- package/src/scripting/environment.ts +5 -5
- package/src/secret.ts +200 -62
- package/src/service.ts +288 -158
- package/src/shared.ts +94 -67
- package/src/stateful-set.ts +270 -117
- package/src/tls.ts +344 -0
- package/src/units/cert-manager/index.ts +2 -3
- package/src/units/dns01-issuer/index.ts +30 -14
- package/src/units/existing-cluster/index.ts +10 -7
- package/src/units/gateway-api/index.ts +2 -2
- package/src/worker.ts +26 -0
- package/src/workload.ts +275 -171
- package/dist/chunk-5C2BJGES.js.map +0 -1
- package/dist/chunk-5TLC5BXR.js +0 -256
- package/dist/chunk-5TLC5BXR.js.map +0 -1
- package/dist/chunk-BBIY3KUN.js +0 -1557
- package/dist/chunk-BBIY3KUN.js.map +0 -1
- package/dist/chunk-OFFSHGC6.js.map +0 -1
- package/dist/chunk-TZHOUJRC.js +0 -202
- package/dist/chunk-TZHOUJRC.js.map +0 -1
- package/dist/chunk-YWRJ4EZM.js +0 -192
- package/dist/chunk-YWRJ4EZM.js.map +0 -1
- package/dist/deployment-XK3CDJOE.js +0 -6
- package/dist/stateful-set-7CAQWTV2.js +0 -6
- package/dist/units/access-point/index.js +0 -21
- package/dist/units/access-point/index.js.map +0 -1
- package/src/access-point.ts +0 -191
- package/src/units/access-point/index.ts +0 -19
- package/src/units/dns01-issuer/solver.ts +0 -23
package/src/pvc.ts
CHANGED
@@ -1,26 +1,27 @@
|
|
1
1
|
import type { k8s } from "@highstate/library"
|
2
2
|
import { core, type types } from "@pulumi/kubernetes"
|
3
3
|
import {
|
4
|
-
|
5
|
-
Output,
|
4
|
+
interpolate,
|
5
|
+
type Output,
|
6
6
|
output,
|
7
7
|
type ComponentResourceOptions,
|
8
|
-
type CustomResourceOptions,
|
9
8
|
type Input,
|
10
9
|
type Inputs,
|
11
10
|
} from "@highstate/pulumi"
|
11
|
+
import { toPromise } from "@highstate/pulumi"
|
12
12
|
import { deepmerge } from "deepmerge-ts"
|
13
13
|
import { omit } from "remeda"
|
14
|
+
import { getOrCreate } from "@highstate/contract"
|
14
15
|
import {
|
15
16
|
commonExtraArgs,
|
16
17
|
getProvider,
|
17
18
|
mapMetadata,
|
18
|
-
|
19
|
-
|
20
|
-
type ResourceId,
|
19
|
+
type ScopedResourceArgs,
|
20
|
+
ScopedResource,
|
21
21
|
} from "./shared"
|
22
|
+
import { Namespace } from "./namespace"
|
22
23
|
|
23
|
-
export type PersistentVolumeClaimArgs =
|
24
|
+
export type PersistentVolumeClaimArgs = ScopedResourceArgs &
|
24
25
|
types.input.core.v1.PersistentVolumeClaimSpec & {
|
25
26
|
/**
|
26
27
|
* The size of the volume to request.
|
@@ -31,27 +32,28 @@ export type PersistentVolumeClaimArgs = CommonArgs &
|
|
31
32
|
}
|
32
33
|
|
33
34
|
export type CreateOrGetPersistentVolumeClaimArgs = PersistentVolumeClaimArgs & {
|
35
|
+
/**
|
36
|
+
* The PVC entity to patch/retrieve.
|
37
|
+
*/
|
34
38
|
existing: Input<k8s.PersistentVolumeClaim> | undefined
|
35
39
|
}
|
36
40
|
|
37
41
|
const extraPersistentVolumeClaimArgs = [...commonExtraArgs, "size"] as const
|
38
42
|
|
39
|
-
|
43
|
+
/**
|
44
|
+
* Represents a Kubernetes PersistentVolumeClaim resource with metadata and spec.
|
45
|
+
*/
|
46
|
+
export abstract class PersistentVolumeClaim extends ScopedResource {
|
40
47
|
protected constructor(
|
41
48
|
type: string,
|
42
49
|
name: string,
|
43
50
|
args: Inputs,
|
44
|
-
opts: ComponentResourceOptions,
|
51
|
+
opts: ComponentResourceOptions | undefined,
|
45
52
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
/**
|
52
|
-
* The metadata of the underlying Kubernetes PVC.
|
53
|
-
*/
|
54
|
-
readonly metadata: Output<types.output.meta.v1.ObjectMeta>,
|
53
|
+
apiVersion: Output<string>,
|
54
|
+
kind: Output<string>,
|
55
|
+
namespace: Output<Namespace>,
|
56
|
+
metadata: Output<types.output.meta.v1.ObjectMeta>,
|
55
57
|
|
56
58
|
/**
|
57
59
|
* The spec of the underlying Kubernetes PVC.
|
@@ -63,7 +65,7 @@ export abstract class PersistentVolumeClaim extends ComponentResource {
|
|
63
65
|
*/
|
64
66
|
readonly status: Output<types.output.core.v1.PersistentVolumeClaimStatus>,
|
65
67
|
) {
|
66
|
-
super(type, name, args, opts)
|
68
|
+
super(type, name, args, opts, apiVersion, kind, namespace, metadata)
|
67
69
|
}
|
68
70
|
|
69
71
|
/**
|
@@ -71,81 +73,238 @@ export abstract class PersistentVolumeClaim extends ComponentResource {
|
|
71
73
|
*/
|
72
74
|
get entity(): Output<k8s.PersistentVolumeClaim> {
|
73
75
|
return output({
|
74
|
-
type: "
|
76
|
+
type: "persistent-volume-claim",
|
75
77
|
clusterId: this.cluster.id,
|
78
|
+
clusterName: this.cluster.name,
|
76
79
|
metadata: this.metadata,
|
77
80
|
})
|
78
81
|
}
|
79
82
|
|
83
|
+
/**
|
84
|
+
* Creates a new PVC.
|
85
|
+
*/
|
80
86
|
static create(
|
81
87
|
name: string,
|
82
88
|
args: PersistentVolumeClaimArgs,
|
83
|
-
opts
|
89
|
+
opts?: ComponentResourceOptions,
|
84
90
|
): PersistentVolumeClaim {
|
85
91
|
return new CreatedPersistentVolumeClaim(name, args, opts)
|
86
92
|
}
|
87
93
|
|
88
|
-
|
94
|
+
/**
|
95
|
+
* Creates a new PVC or patches an existing one.
|
96
|
+
*
|
97
|
+
* @param name The name of the resource. May not be the same as the PVC name.
|
98
|
+
* @param args The arguments to create or patch the PVC with.
|
99
|
+
* @param opts Optional resource options.
|
100
|
+
*/
|
101
|
+
static createOrPatch(
|
89
102
|
name: string,
|
90
|
-
|
91
|
-
|
92
|
-
opts: ComponentResourceOptions,
|
103
|
+
args: CreateOrGetPersistentVolumeClaimArgs,
|
104
|
+
opts?: ComponentResourceOptions,
|
93
105
|
): PersistentVolumeClaim {
|
94
|
-
|
106
|
+
if (args.existing) {
|
107
|
+
return new PersistentVolumeClaimPatch(name, {
|
108
|
+
...args,
|
109
|
+
name: output(args.existing).metadata.name,
|
110
|
+
})
|
111
|
+
}
|
112
|
+
|
113
|
+
return new CreatedPersistentVolumeClaim(name, args, opts)
|
95
114
|
}
|
96
115
|
|
97
|
-
|
116
|
+
/**
|
117
|
+
* Creates a new PVC or gets an existing one.
|
118
|
+
*
|
119
|
+
* @param name The name of the resource. May not be the same as the PVC name. Will not be used when existing PVC is retrieved.
|
120
|
+
* @param args The arguments to create or get the PVC with.
|
121
|
+
* @param opts Optional resource options.
|
122
|
+
*/
|
123
|
+
static async createOrGet(
|
98
124
|
name: string,
|
99
125
|
args: CreateOrGetPersistentVolumeClaimArgs,
|
100
|
-
opts
|
101
|
-
): PersistentVolumeClaim {
|
102
|
-
if (
|
103
|
-
return
|
126
|
+
opts?: ComponentResourceOptions,
|
127
|
+
): Promise<PersistentVolumeClaim> {
|
128
|
+
if (args.existing) {
|
129
|
+
return await PersistentVolumeClaim.forAsync(args.existing, output(args.namespace).cluster)
|
104
130
|
}
|
105
131
|
|
106
|
-
return new
|
132
|
+
return new CreatedPersistentVolumeClaim(name, args, opts)
|
133
|
+
}
|
134
|
+
|
135
|
+
/**
|
136
|
+
* Patches an existing PVC.
|
137
|
+
*
|
138
|
+
* Will throw an error if the PVC does not exist.
|
139
|
+
*
|
140
|
+
* @param name The name of the resource. May not be the same as the PVC name.
|
141
|
+
* @param args The arguments to patch the PVC with.
|
142
|
+
* @param opts Optional resource options.
|
143
|
+
*/
|
144
|
+
static patch(
|
145
|
+
name: string,
|
146
|
+
args: PersistentVolumeClaimArgs,
|
147
|
+
opts?: ComponentResourceOptions,
|
148
|
+
): PersistentVolumeClaim {
|
149
|
+
return new PersistentVolumeClaimPatch(name, args, opts)
|
150
|
+
}
|
151
|
+
|
152
|
+
/**
|
153
|
+
* Wraps an existing Kubernetes PVC.
|
154
|
+
*/
|
155
|
+
static wrap(
|
156
|
+
name: string,
|
157
|
+
args: WrappedPersistentVolumeClaimArgs,
|
158
|
+
opts?: ComponentResourceOptions,
|
159
|
+
): PersistentVolumeClaim {
|
160
|
+
return new WrappedPersistentVolumeClaim(name, args, opts)
|
161
|
+
}
|
162
|
+
|
163
|
+
/**
|
164
|
+
* Gets an existing PVC.
|
165
|
+
*
|
166
|
+
* Will throw an error if the PVC does not exist.
|
167
|
+
*/
|
168
|
+
static get(
|
169
|
+
name: string,
|
170
|
+
args: ExternalPersistentVolumeClaimArgs,
|
171
|
+
opts?: ComponentResourceOptions,
|
172
|
+
): PersistentVolumeClaim {
|
173
|
+
return new ExternalPersistentVolumeClaim(name, args, opts)
|
174
|
+
}
|
175
|
+
|
176
|
+
private static readonly pvcCache = new Map<string, PersistentVolumeClaim>()
|
177
|
+
|
178
|
+
/**
|
179
|
+
* Gets an existing PVC for a given entity.
|
180
|
+
* Prefer this method over `get` when possible.
|
181
|
+
*
|
182
|
+
* It automatically names the resource with the following format: `{clusterName}.{namespace}.{name}.{clusterId}`.
|
183
|
+
*
|
184
|
+
* This method is idempotent and will return the same instance for the same entity.
|
185
|
+
*
|
186
|
+
* @param entity The entity to get the PVC for.
|
187
|
+
* @param cluster The cluster where the PVC is located.
|
188
|
+
*/
|
189
|
+
static for(
|
190
|
+
entity: k8s.PersistentVolumeClaim,
|
191
|
+
cluster: Input<k8s.Cluster>,
|
192
|
+
): PersistentVolumeClaim {
|
193
|
+
return getOrCreate(
|
194
|
+
PersistentVolumeClaim.pvcCache,
|
195
|
+
`${entity.clusterName}.${entity.metadata.namespace}.${entity.metadata.name}.${entity.clusterId}`,
|
196
|
+
name => {
|
197
|
+
return PersistentVolumeClaim.get(name, {
|
198
|
+
name: entity.metadata.name,
|
199
|
+
namespace: Namespace.forResource(entity, cluster),
|
200
|
+
})
|
201
|
+
},
|
202
|
+
)
|
203
|
+
}
|
204
|
+
|
205
|
+
/**
|
206
|
+
* Gets an existing PVC for a given entity.
|
207
|
+
* Prefer this method over `get` when possible.
|
208
|
+
*
|
209
|
+
* It automatically names the resource with the following format: `{clusterName}.{namespace}.{name}.{clusterId}`.
|
210
|
+
*
|
211
|
+
* This method is idempotent and will return the same instance for the same entity.
|
212
|
+
*
|
213
|
+
* @param entity The entity to get the PVC for.
|
214
|
+
* @param cluster The cluster where the PVC is located.
|
215
|
+
*/
|
216
|
+
static async forAsync(
|
217
|
+
entity: Input<k8s.PersistentVolumeClaim>,
|
218
|
+
cluster: Input<k8s.Cluster>,
|
219
|
+
): Promise<PersistentVolumeClaim> {
|
220
|
+
const resolvedEntity = await toPromise(entity)
|
221
|
+
return PersistentVolumeClaim.for(resolvedEntity, cluster)
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
225
|
+
class CreatedPersistentVolumeClaim extends PersistentVolumeClaim {
|
226
|
+
constructor(name: string, args: PersistentVolumeClaimArgs, opts?: ComponentResourceOptions) {
|
227
|
+
const pvc = output(args.namespace).cluster.apply(cluster => {
|
228
|
+
return new core.v1.PersistentVolumeClaim(
|
229
|
+
name,
|
230
|
+
{
|
231
|
+
metadata: mapMetadata(args, name),
|
232
|
+
spec: output(args).apply(args => {
|
233
|
+
return deepmerge(
|
234
|
+
{
|
235
|
+
accessModes: ["ReadWriteOnce"],
|
236
|
+
resources: {
|
237
|
+
requests: {
|
238
|
+
storage: args.size ?? "100Mi",
|
239
|
+
},
|
240
|
+
},
|
241
|
+
} satisfies types.input.core.v1.PersistentVolumeClaimSpec,
|
242
|
+
omit(args, extraPersistentVolumeClaimArgs),
|
243
|
+
)
|
244
|
+
}),
|
245
|
+
},
|
246
|
+
{
|
247
|
+
...opts,
|
248
|
+
parent: this,
|
249
|
+
provider: getProvider(cluster),
|
250
|
+
},
|
251
|
+
)
|
252
|
+
})
|
253
|
+
|
254
|
+
super(
|
255
|
+
"highstate:k8s:PersistentVolumeClaim",
|
107
256
|
name,
|
108
|
-
|
109
|
-
args.cluster,
|
257
|
+
args,
|
110
258
|
opts,
|
259
|
+
|
260
|
+
pvc.apiVersion,
|
261
|
+
pvc.kind,
|
262
|
+
output(args.namespace),
|
263
|
+
pvc.metadata,
|
264
|
+
pvc.spec,
|
265
|
+
pvc.status,
|
111
266
|
)
|
112
267
|
}
|
113
268
|
}
|
114
269
|
|
115
|
-
|
116
|
-
constructor(name: string, args: PersistentVolumeClaimArgs, opts
|
117
|
-
const pvc = output(args).apply(
|
118
|
-
return new core.v1.
|
270
|
+
class PersistentVolumeClaimPatch extends PersistentVolumeClaim {
|
271
|
+
constructor(name: string, args: PersistentVolumeClaimArgs, opts?: ComponentResourceOptions) {
|
272
|
+
const pvc = output(args.namespace).cluster.apply(cluster => {
|
273
|
+
return new core.v1.PersistentVolumeClaimPatch(
|
119
274
|
name,
|
120
275
|
{
|
121
276
|
metadata: mapMetadata(args, name),
|
122
|
-
spec:
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
277
|
+
spec: output(args).apply(args => {
|
278
|
+
return deepmerge(
|
279
|
+
{
|
280
|
+
accessModes: ["ReadWriteOnce"],
|
281
|
+
resources: {
|
282
|
+
requests: {
|
283
|
+
storage: args.size ?? "100Mi",
|
284
|
+
},
|
128
285
|
},
|
129
|
-
},
|
130
|
-
|
131
|
-
|
132
|
-
),
|
286
|
+
} satisfies types.input.core.v1.PersistentVolumeClaimSpec,
|
287
|
+
omit(args, extraPersistentVolumeClaimArgs),
|
288
|
+
)
|
289
|
+
}),
|
133
290
|
},
|
134
291
|
{
|
135
292
|
...opts,
|
136
293
|
parent: this,
|
137
|
-
provider:
|
294
|
+
provider: getProvider(cluster),
|
138
295
|
},
|
139
296
|
)
|
140
297
|
})
|
141
298
|
|
142
299
|
super(
|
143
|
-
"k8s:
|
300
|
+
"highstate:k8s:PersistentVolumeClaimPatch",
|
144
301
|
name,
|
145
302
|
args,
|
146
303
|
opts,
|
147
304
|
|
148
|
-
|
305
|
+
pvc.apiVersion,
|
306
|
+
pvc.kind,
|
307
|
+
output(args.namespace),
|
149
308
|
pvc.metadata,
|
150
309
|
pvc.spec,
|
151
310
|
pvc.status,
|
@@ -153,33 +312,75 @@ export class CreatedPersistentVolumeClaim extends PersistentVolumeClaim {
|
|
153
312
|
}
|
154
313
|
}
|
155
314
|
|
156
|
-
export
|
315
|
+
export type WrappedPersistentVolumeClaimArgs = {
|
316
|
+
/**
|
317
|
+
* The underlying Kubernetes PVC to wrap.
|
318
|
+
*/
|
319
|
+
pvc: Input<core.v1.PersistentVolumeClaim>
|
320
|
+
|
321
|
+
/**
|
322
|
+
* The namespace where the PVC is located.
|
323
|
+
*/
|
324
|
+
namespace: Input<Namespace>
|
325
|
+
}
|
326
|
+
|
327
|
+
class WrappedPersistentVolumeClaim extends PersistentVolumeClaim {
|
328
|
+
constructor(
|
329
|
+
name: string,
|
330
|
+
args: WrappedPersistentVolumeClaimArgs,
|
331
|
+
opts?: ComponentResourceOptions,
|
332
|
+
) {
|
333
|
+
super(
|
334
|
+
"highstate:k8s:WrappedPersistentVolumeClaim",
|
335
|
+
name,
|
336
|
+
args,
|
337
|
+
opts,
|
338
|
+
|
339
|
+
output(args.pvc).apiVersion,
|
340
|
+
output(args.pvc).kind,
|
341
|
+
output(args.namespace),
|
342
|
+
output(args.pvc).metadata,
|
343
|
+
output(args.pvc).spec,
|
344
|
+
output(args.pvc).status,
|
345
|
+
)
|
346
|
+
}
|
347
|
+
}
|
348
|
+
|
349
|
+
export type ExternalPersistentVolumeClaimArgs = {
|
350
|
+
/**
|
351
|
+
* The name of the PVC to get.
|
352
|
+
*/
|
353
|
+
name: Input<string>
|
354
|
+
|
355
|
+
/**
|
356
|
+
* The namespace where the PVC is located.
|
357
|
+
*/
|
358
|
+
namespace: Input<Namespace>
|
359
|
+
}
|
360
|
+
|
361
|
+
class ExternalPersistentVolumeClaim extends PersistentVolumeClaim {
|
157
362
|
constructor(
|
158
363
|
name: string,
|
159
|
-
|
160
|
-
|
161
|
-
opts: ComponentResourceOptions,
|
364
|
+
args: ExternalPersistentVolumeClaimArgs,
|
365
|
+
opts?: ComponentResourceOptions,
|
162
366
|
) {
|
163
|
-
const pvc = output(
|
367
|
+
const pvc = output(args.namespace).cluster.apply(cluster => {
|
164
368
|
return core.v1.PersistentVolumeClaim.get(
|
165
|
-
//
|
166
369
|
name,
|
167
|
-
|
168
|
-
{
|
169
|
-
...opts,
|
170
|
-
parent: this,
|
171
|
-
provider: await getProvider(cluster),
|
172
|
-
},
|
370
|
+
interpolate`${output(args.namespace).metadata.name}/${args.name}`,
|
371
|
+
{ ...opts, parent: this, provider: getProvider(cluster) },
|
173
372
|
)
|
174
373
|
})
|
175
374
|
|
176
375
|
super(
|
177
376
|
"highstate:k8s:ExternalPersistentVolumeClaim",
|
178
377
|
name,
|
179
|
-
|
378
|
+
args,
|
180
379
|
opts,
|
181
380
|
|
182
|
-
|
381
|
+
pvc.apiVersion,
|
382
|
+
pvc.kind,
|
383
|
+
output(args.namespace),
|
183
384
|
pvc.metadata,
|
184
385
|
pvc.spec,
|
185
386
|
pvc.status,
|
package/src/rbac.ts
ADDED
@@ -0,0 +1,218 @@
|
|
1
|
+
import type { k8s } from "@highstate/library"
|
2
|
+
import type { Namespace } from "./namespace"
|
3
|
+
import {
|
4
|
+
ComponentResource,
|
5
|
+
type ComponentResourceOptions,
|
6
|
+
type Input,
|
7
|
+
type InputArray,
|
8
|
+
interpolate,
|
9
|
+
normalizeInputs,
|
10
|
+
type Output,
|
11
|
+
output,
|
12
|
+
toPromise,
|
13
|
+
} from "@highstate/pulumi"
|
14
|
+
import { KubeConfig } from "@kubernetes/client-node"
|
15
|
+
import { core, rbac, type types } from "@pulumi/kubernetes"
|
16
|
+
import { map, unique } from "remeda"
|
17
|
+
import { Secret } from "./secret"
|
18
|
+
import { getNamespaceName, getProvider, type NamespaceLike, type ScopedResource } from "./shared"
|
19
|
+
|
20
|
+
export type ClusterAccessScopeArgs = {
|
21
|
+
/**
|
22
|
+
* The namespace to locate the ServiceAccount in.
|
23
|
+
*/
|
24
|
+
namespace: Input<Namespace>
|
25
|
+
|
26
|
+
/**
|
27
|
+
* The RBAC rule to apply to the `ServiceAccount`.
|
28
|
+
*
|
29
|
+
* It will be used to create ClusterRole.
|
30
|
+
*/
|
31
|
+
rule?: Input<types.input.rbac.v1.PolicyRule>
|
32
|
+
|
33
|
+
/**
|
34
|
+
* The RBAC rules to apply to the `ServiceAccount`.
|
35
|
+
*
|
36
|
+
* It will be used to create `ClusterRole`.
|
37
|
+
*/
|
38
|
+
rules?: InputArray<types.input.rbac.v1.PolicyRule>
|
39
|
+
|
40
|
+
/**
|
41
|
+
* Whether to allow the `ServiceAccount` to access resources in the namespace where it is created.
|
42
|
+
*
|
43
|
+
* By default, it is set to `true`.
|
44
|
+
*/
|
45
|
+
allowOriginNamespace?: boolean
|
46
|
+
|
47
|
+
/**
|
48
|
+
* The extra namespaces to bind to the `ClusterRole` and allow `ServiceAccount` to access them
|
49
|
+
* with specified `rules`.
|
50
|
+
*/
|
51
|
+
extraNamespaces?: InputArray<NamespaceLike>
|
52
|
+
|
53
|
+
/**
|
54
|
+
* Whether to create `ClusterRoleBinding` to bind the `ServiceAccount` to the `ClusterRole`.
|
55
|
+
*
|
56
|
+
* This will allow the `ServiceAccount` to access all namespaces and cluster resources.
|
57
|
+
*/
|
58
|
+
clusterWide?: boolean
|
59
|
+
}
|
60
|
+
|
61
|
+
export class ClusterAccessScope extends ComponentResource {
|
62
|
+
/**
|
63
|
+
* The cluster entity with the reduced access.
|
64
|
+
*/
|
65
|
+
readonly cluster: Output<k8s.Cluster>
|
66
|
+
|
67
|
+
constructor(name: string, args: ClusterAccessScopeArgs, opts?: ComponentResourceOptions) {
|
68
|
+
super("highstate:k8s:ClusterAccessScope", name, args, opts)
|
69
|
+
|
70
|
+
const { serviceAccount, kubeconfig } = output(args.namespace).cluster.apply(cluster => {
|
71
|
+
const provider = getProvider(cluster)
|
72
|
+
const namespaceName = output(args.namespace).metadata.name
|
73
|
+
|
74
|
+
const serviceAccount = new core.v1.ServiceAccount(
|
75
|
+
name,
|
76
|
+
{
|
77
|
+
metadata: {
|
78
|
+
name,
|
79
|
+
namespace: namespaceName,
|
80
|
+
},
|
81
|
+
},
|
82
|
+
{ provider },
|
83
|
+
)
|
84
|
+
|
85
|
+
const clusterRole = new rbac.v1.ClusterRole(
|
86
|
+
name,
|
87
|
+
{
|
88
|
+
metadata: {
|
89
|
+
name: interpolate`highstate.${namespaceName}.${name}`,
|
90
|
+
annotations: {
|
91
|
+
"kubernetes.io/description": interpolate`Created by Highstate for the ServiceAccount "${name}" in the namespace "${namespaceName}".`,
|
92
|
+
},
|
93
|
+
},
|
94
|
+
rules: normalizeInputs(args.rule, args.rules),
|
95
|
+
},
|
96
|
+
{ provider },
|
97
|
+
)
|
98
|
+
|
99
|
+
const createRoleBinding = (namespace: Input<string>) => {
|
100
|
+
return new rbac.v1.RoleBinding(
|
101
|
+
name,
|
102
|
+
{
|
103
|
+
metadata: { name, namespace },
|
104
|
+
roleRef: {
|
105
|
+
kind: "ClusterRole",
|
106
|
+
name: clusterRole.metadata.name,
|
107
|
+
apiGroup: "rbac.authorization.k8s.io",
|
108
|
+
},
|
109
|
+
subjects: [
|
110
|
+
{
|
111
|
+
kind: "ServiceAccount",
|
112
|
+
name: serviceAccount.metadata.name,
|
113
|
+
namespace: namespaceName,
|
114
|
+
},
|
115
|
+
],
|
116
|
+
},
|
117
|
+
{ provider },
|
118
|
+
)
|
119
|
+
}
|
120
|
+
|
121
|
+
if (args.allowOriginNamespace ?? true) {
|
122
|
+
createRoleBinding(namespaceName)
|
123
|
+
}
|
124
|
+
|
125
|
+
output(args.extraNamespaces ?? [])
|
126
|
+
.apply(map(getNamespaceName))
|
127
|
+
.apply(map(createRoleBinding))
|
128
|
+
|
129
|
+
return { serviceAccount, kubeconfig: cluster.kubeconfig }
|
130
|
+
})
|
131
|
+
|
132
|
+
const accessTokenSecret = Secret.create(`${name}-token`, {
|
133
|
+
namespace: args.namespace,
|
134
|
+
type: "kubernetes.io/service-account-token",
|
135
|
+
metadata: {
|
136
|
+
annotations: {
|
137
|
+
"kubernetes.io/service-account.name": serviceAccount.metadata.name,
|
138
|
+
},
|
139
|
+
},
|
140
|
+
})
|
141
|
+
|
142
|
+
this.cluster = output({
|
143
|
+
cluster: output(args.namespace).cluster,
|
144
|
+
kubeconfig,
|
145
|
+
newToken: accessTokenSecret.getValue("token"),
|
146
|
+
serviceAccount: serviceAccount.metadata.name,
|
147
|
+
}).apply(({ cluster, kubeconfig, newToken, serviceAccount }) => {
|
148
|
+
const config = new KubeConfig()
|
149
|
+
config.loadFromString(kubeconfig)
|
150
|
+
|
151
|
+
// clear all existing contexts and users
|
152
|
+
config.users = []
|
153
|
+
config.contexts = []
|
154
|
+
|
155
|
+
config.addUser({ name: serviceAccount, token: newToken })
|
156
|
+
|
157
|
+
config.addContext({
|
158
|
+
name: config.clusters[0].name,
|
159
|
+
cluster: config.clusters[0].name,
|
160
|
+
user: serviceAccount,
|
161
|
+
})
|
162
|
+
|
163
|
+
config.setCurrentContext(config.clusters[0].name)
|
164
|
+
|
165
|
+
return {
|
166
|
+
...cluster,
|
167
|
+
kubeconfig: config.exportConfig(),
|
168
|
+
}
|
169
|
+
})
|
170
|
+
}
|
171
|
+
|
172
|
+
/**
|
173
|
+
* Creates `ClusterAccessScope` for the given resources with the specified verbs.
|
174
|
+
*
|
175
|
+
* All resources must belong to the same namespace in the same cluster.
|
176
|
+
*
|
177
|
+
* @param name The name of the resource and the ServiceAccount.
|
178
|
+
* @param resources The resources to create access scope for.
|
179
|
+
* @param verbs The verbs to allow on the resources.
|
180
|
+
*/
|
181
|
+
static async forResources(
|
182
|
+
name: string,
|
183
|
+
resources: InputArray<ScopedResource>,
|
184
|
+
verbs: string[],
|
185
|
+
): Promise<ClusterAccessScope> {
|
186
|
+
const resolved = await toPromise(
|
187
|
+
output(resources).apply(resources =>
|
188
|
+
resources.map(r => ({
|
189
|
+
namespaceId: r.namespace.metadata.uid,
|
190
|
+
namespace: r.namespace,
|
191
|
+
metadata: r.metadata,
|
192
|
+
apiVersion: r.apiVersion,
|
193
|
+
kind: r.kind,
|
194
|
+
})),
|
195
|
+
),
|
196
|
+
)
|
197
|
+
|
198
|
+
if (resolved.length === 0) {
|
199
|
+
throw new Error("No resources provided to forResources.")
|
200
|
+
}
|
201
|
+
|
202
|
+
// verify all resources belong to the same namespace
|
203
|
+
if (unique(resolved.map(r => r.namespaceId)).length !== 1) {
|
204
|
+
throw new Error("All resources must belong to the same namespace.")
|
205
|
+
}
|
206
|
+
|
207
|
+
return new ClusterAccessScope(name, {
|
208
|
+
namespace: resolved[0].namespace,
|
209
|
+
rules: resolved.map(r => ({
|
210
|
+
apiGroups: r.apiVersion === "v1" ? [""] : [r.apiVersion.split("/")[0]],
|
211
|
+
resources: [r.kind.toLowerCase() + (r.metadata?.name ? "s" : "")],
|
212
|
+
// TODO: critical
|
213
|
+
// resourceNames: r.metadata?.name ? [r.metadata.name] : undefined,
|
214
|
+
verbs,
|
215
|
+
})),
|
216
|
+
})
|
217
|
+
}
|
218
|
+
}
|