feat(provision): Phase-0 throwaway test VM via vendored @olsitec/pulumi-hetzner
- Vendor hetzner module (Stage-1, trimmed to @pulumi/hcloud+js-yaml; dropped unused types.ts + bcrypt/axios/tls/vault deps). GOTCHA documented: cloud-init moves SSH to port 222. - provision/: isolated stack (platformName foundation-test, no collision with olsicloud4-*) — one cx23 in nbg1-dc3 + firewall (222/80/443/2222) + Docker cloud-init. Dedicated throwaway ed25519 key (operator id_rsa already registered → uniqueness_error). - Provisioned + verified: foundation-test @ 91.98.117.152, Docker 29.6.1, docker-over-SSH OK. Token via ENV (pass), never committed; provision/state gitignored. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6a29db386f
commit
80a99c6f7e
13 changed files with 754 additions and 1 deletions
12
packages/pulumi-hetzner/VENDORED.md
Normal file
12
packages/pulumi-hetzner/VENDORED.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# VENDORED — @olsitec/pulumi-hetzner
|
||||
|
||||
Stage-1 vendor (000_TOPOLOGY.md §5; ADR-005), 2026-06-30.
|
||||
|
||||
- **Source**: `/Users/andiolsi/work/olsicloud4/pulumi/modules/hetzner/` (index.ts, cloudinit-config.ts, tsconfig.json).
|
||||
- **Trimmed**: dropped `types.ts` (standalone, unused by `HetznerDeployment`) and the unused deps it pulled
|
||||
(`bcrypt`, `axios`, `@pulumi/{tls,vault,random}`, `deepmerge`, `yaml`). Real import surface is only
|
||||
`@pulumi/hcloud`, `@pulumi/pulumi`, `fs`, `js-yaml`. Logic unchanged.
|
||||
- **GOTCHA — SSH port 222**: `getCloudInitConfig` writes an sshd drop-in that moves SSH to **port 222**
|
||||
and creates `root` + `andiolsi` users. Consumers (the Docker-over-SSH provider, ssh checks) MUST use
|
||||
`:222`. The module creates **no firewall** — the consumer adds one.
|
||||
- Stage-2 (publish to the foundation registry) is a later task.
|
||||
126
packages/pulumi-hetzner/cloudinit-config.ts
Normal file
126
packages/pulumi-hetzner/cloudinit-config.ts
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
import * as yaml from "js-yaml";
|
||||
|
||||
export interface GetCloudInitConfigArgs {
|
||||
sshAuthorizedKeys?: string[];
|
||||
extraPackages?: string[];
|
||||
additionalFiles?: {
|
||||
path: string;
|
||||
permissions: string;
|
||||
content: string;
|
||||
}[];
|
||||
lateCommands?: string[];
|
||||
}
|
||||
|
||||
export const getCloudInitConfig = (config: GetCloudInitConfigArgs) => {
|
||||
const defaultCloudInitConfig = {
|
||||
users: [
|
||||
{
|
||||
name: "root",
|
||||
sudo: "ALL=(ALL) NOPASSWD:ALL",
|
||||
"lock-passwd": false,
|
||||
passwd:
|
||||
"$6$hB5RrvU5$ll99.7zgATrkGEbInSRCF7o8t3TatZEYQK4QWqk6Ri.DT3LgG0l38Dz47CT.nBjLhhIUVgzIF6t2ZrWmDOcVl1",
|
||||
ssh_authorized_keys: config.sshAuthorizedKeys || [],
|
||||
},
|
||||
{
|
||||
name: "andiolsi",
|
||||
sudo: "ALL=(ALL) NOPASSWD:ALL",
|
||||
"lock-passwd": false,
|
||||
passwd:
|
||||
"$6$sKcNr2f8$AubrAJ..bZOPxnGVEWKQQrWShW/i7ClAZRvGLstrmeDcIS92/.BVCtYzgvg2tg6ub9rz8agfLRJg.ryDoM7jG/",
|
||||
ssh_authorized_keys: config.sshAuthorizedKeys || [],
|
||||
},
|
||||
],
|
||||
disable_root: false,
|
||||
ssh_pwauth: false,
|
||||
locale: "en_US.UTF-8",
|
||||
mounts: [["tmpfs", "/var/log", "tmpfs", "defaults,size=10%", "0", "0"]],
|
||||
package_update: true,
|
||||
package_upgrade: true,
|
||||
packages: [
|
||||
"curl",
|
||||
"apt-transport-https",
|
||||
"ca-certificates",
|
||||
"vim",
|
||||
"jq",
|
||||
"locales",
|
||||
...(config.extraPackages || []),
|
||||
],
|
||||
write_files: [
|
||||
{
|
||||
path: "/etc/ssh/sshd_config.d/99-cloud-init-manual.conf",
|
||||
permissions: "0600",
|
||||
content: `
|
||||
Port 222
|
||||
PermitRootLogin without-password
|
||||
PubkeyAuthentication yes
|
||||
PubkeyAcceptedKeyTypes=+ssh-rsa
|
||||
|
||||
`,
|
||||
},
|
||||
{
|
||||
path: "/etc/logrotate.d/var_log",
|
||||
permissions: "0644",
|
||||
content: `
|
||||
/var/log/*.log {
|
||||
daily
|
||||
missingok
|
||||
rotate 7
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
create 0640 root adm
|
||||
sharedscripts
|
||||
postrotate
|
||||
systemctl reload rsyslog > /dev/null 2>/dev/null || true
|
||||
endscript
|
||||
}`,
|
||||
},
|
||||
{
|
||||
path: "/etc/selinux/config",
|
||||
permissions: "0644",
|
||||
content: "SELINUX=disabled\nSELINUXTYPE=targeted\n",
|
||||
},
|
||||
{
|
||||
path: "/etc/security/limits.conf",
|
||||
permissions: "0644",
|
||||
content: "* soft nofile 32768\n* hard nofile 65536\n",
|
||||
},
|
||||
{
|
||||
path: "/etc/sysctl.d/99-custom.conf",
|
||||
permissions: "0644",
|
||||
content: `
|
||||
net.ipv4.ip_forward=1
|
||||
net.ipv4.conf.all.arp_ignore=1
|
||||
net.ipv4.conf.all.arp_announce=2
|
||||
net.ipv4.ip_nonlocal_bind=1
|
||||
vm.dirty_ratio=10
|
||||
vm.swappiness=0
|
||||
vm.dirty_background_ratio=5
|
||||
net.ipv6.conf.all.disable_ipv6=1
|
||||
fs.nr_open=3000000
|
||||
fs.file-max=3000000
|
||||
fs.inotify.max_user_instances=1048576
|
||||
fs.inotify.max_queued_events=1048576
|
||||
fs.inotify.max_user_watches=1048576
|
||||
vm.max_map_count=262144
|
||||
net.netfilter.nf_conntrack_max=262144
|
||||
net.netfilter.nf_conntrack_tcp_timeout_established=600
|
||||
net.netfilter.nf_conntrack_tcp_timeout_time_wait=30
|
||||
net.netfilter.nf_conntrack_tcp_timeout_close_wait=30
|
||||
net.netfilter.nf_conntrack_tcp_timeout_fin_wait=30
|
||||
`,
|
||||
},
|
||||
...(config.additionalFiles || []),
|
||||
],
|
||||
runcmd: [
|
||||
"echo never > /sys/kernel/mm/transparent_hugepage/enabled",
|
||||
"echo never > /sys/kernel/mm/transparent_hugepage/defrag",
|
||||
"systemctl stop apparmor || true",
|
||||
"systemctl disable apparmor || true",
|
||||
"sysctl --system",
|
||||
...(config.lateCommands || []),
|
||||
],
|
||||
};
|
||||
return `#cloud-config\n` + yaml.dump(defaultCloudInitConfig);
|
||||
};
|
||||
392
packages/pulumi-hetzner/index.ts
Normal file
392
packages/pulumi-hetzner/index.ts
Normal file
|
|
@ -0,0 +1,392 @@
|
|||
// hetzner/index.ts
|
||||
import * as pulumi from "@pulumi/pulumi";
|
||||
import * as hcloud from "@pulumi/hcloud";
|
||||
import * as fs from "fs";
|
||||
import {
|
||||
getCloudInitConfig,
|
||||
type GetCloudInitConfigArgs,
|
||||
} from "./cloudinit-config";
|
||||
import { LoadBalancerServiceHealthCheck } from "@pulumi/hcloud/types/input";
|
||||
|
||||
interface HetznerDeploymentConfig {
|
||||
platformName: string;
|
||||
datacenter: string;
|
||||
}
|
||||
|
||||
interface HetznerDeploymentArgs {
|
||||
platformName: string;
|
||||
hcloudToken: pulumi.Input<string>;
|
||||
vswitchId?: number;
|
||||
sshKeyPath: string;
|
||||
datacenter: string;
|
||||
servers: {
|
||||
name: string;
|
||||
hostname: string;
|
||||
image?: string;
|
||||
type: string;
|
||||
labels?: Record<string, string>;
|
||||
cloudInitConfig?: GetCloudInitConfigArgs;
|
||||
}[];
|
||||
loadbalancers?: {
|
||||
name: string;
|
||||
services: {
|
||||
name: string;
|
||||
port: number;
|
||||
targetPort: number;
|
||||
proxyprotocol?: boolean;
|
||||
healthCheck?: LoadBalancerServiceHealthCheck;
|
||||
}[];
|
||||
targets: Partial<hcloud.LoadBalancerTargetArgs>[];
|
||||
}[];
|
||||
}
|
||||
|
||||
class HetznerDeployment extends pulumi.ComponentResource {
|
||||
public readonly deploymentConfig: HetznerDeploymentConfig;
|
||||
public readonly servers: hcloud.Server[];
|
||||
public readonly platformNetwork: hcloud.Network;
|
||||
public readonly platformNetworkSubnetCloud: hcloud.NetworkSubnet;
|
||||
public readonly platformNetworkSubnetDedicated?:
|
||||
| hcloud.NetworkSubnet
|
||||
| undefined;
|
||||
public readonly platformNetworkSubnetLoadBalancers: hcloud.NetworkSubnet;
|
||||
public readonly publicIps: hcloud.PrimaryIp[];
|
||||
public readonly placementGroup: hcloud.PlacementGroup;
|
||||
public readonly networkAttachments: hcloud.ServerNetwork[];
|
||||
// public readonly loadBalancers: hcloud.LoadBalancer[];
|
||||
public readonly loadBalancerInstances: {
|
||||
loadBalancer: hcloud.LoadBalancer;
|
||||
networkAttachment: hcloud.LoadBalancerNetwork;
|
||||
services: hcloud.LoadBalancerService[];
|
||||
targets: hcloud.LoadBalancerTarget[];
|
||||
}[];
|
||||
public readonly serverInfo: {
|
||||
name: pulumi.Output<string>;
|
||||
publicIp: pulumi.Output<string>;
|
||||
privateIp: string;
|
||||
}[];
|
||||
|
||||
constructor(
|
||||
name: string,
|
||||
args: HetznerDeploymentArgs,
|
||||
opts?: pulumi.ComponentResourceOptions,
|
||||
) {
|
||||
super("custom:resource:HetznerDeployment", name, {}, opts);
|
||||
this.deploymentConfig = args;
|
||||
|
||||
// Hetzner Provider
|
||||
const hcloudProvider = new hcloud.Provider(
|
||||
`${this.deploymentConfig.platformName}-hcloud-provider`,
|
||||
{
|
||||
token: args.hcloudToken,
|
||||
},
|
||||
{ parent: this },
|
||||
);
|
||||
|
||||
/*
|
||||
Hetzner API has 2 functions to get datacenters:
|
||||
function getDatacenters(args: GetDatacentersArgs, opts?: InvokeOptions): Promise<GetDatacentersResult>
|
||||
function getDatacentersOutput(args: GetDatacentersOutputArgs, opts?: InvokeOptions): Output<GetDatacentersResult>
|
||||
|
||||
We need to check wether this.deploymentConfig.datacenter is a valid datacenter.
|
||||
*/
|
||||
hcloud
|
||||
.getDatacentersOutput({ provider: hcloudProvider, parent: this })
|
||||
.apply((datacenters) => {
|
||||
const isValid = datacenters.datacenters.some(
|
||||
(dc) => dc.name === this.deploymentConfig.datacenter,
|
||||
);
|
||||
if (!isValid) {
|
||||
throw new Error(
|
||||
`Invalid datacenter: ${this.deploymentConfig.datacenter}, valid datacenters are: ${datacenters.datacenters
|
||||
.map((dc) => dc.name)
|
||||
.join(", ")}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const sshPublicKey = fs.readFileSync(args.sshKeyPath, "utf-8");
|
||||
const sshKey = new hcloud.SshKey(
|
||||
`${this.deploymentConfig.platformName}-ssh-key`,
|
||||
{
|
||||
name: `${this.deploymentConfig.platformName}-ssh-key`,
|
||||
publicKey: sshPublicKey,
|
||||
},
|
||||
{
|
||||
parent: this,
|
||||
provider: hcloudProvider,
|
||||
dependsOn: [hcloudProvider],
|
||||
deleteBeforeReplace: true,
|
||||
},
|
||||
);
|
||||
|
||||
this.placementGroup = new hcloud.PlacementGroup(
|
||||
`${this.deploymentConfig.platformName}-hcloud-placement-group`,
|
||||
{
|
||||
name: `${this.deploymentConfig.platformName}-hcloud-placement-group`,
|
||||
type: "spread",
|
||||
labels: {
|
||||
platform: this.deploymentConfig.platformName,
|
||||
},
|
||||
},
|
||||
{ parent: this, provider: hcloudProvider, dependsOn: [hcloudProvider] },
|
||||
);
|
||||
|
||||
// Allocate public IPs
|
||||
this.publicIps = args.servers.map((server, serverIndex) => {
|
||||
return new hcloud.PrimaryIp(
|
||||
`${this.deploymentConfig.platformName}-hcloud-primary-ip-${(serverIndex + 1).toString().padStart(2, "0")}`,
|
||||
{
|
||||
type: "ipv4",
|
||||
assigneeType: "server",
|
||||
autoDelete: false,
|
||||
datacenter: this.deploymentConfig.datacenter,
|
||||
labels: {
|
||||
platform: this.deploymentConfig.platformName,
|
||||
role: "controlplane",
|
||||
node: server.hostname,
|
||||
},
|
||||
},
|
||||
{
|
||||
parent: this,
|
||||
provider: hcloudProvider,
|
||||
dependsOn: [hcloudProvider],
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
// Create a network
|
||||
this.platformNetwork = new hcloud.Network(
|
||||
`${this.deploymentConfig.platformName}-hcloud-network`,
|
||||
{
|
||||
name: `${this.deploymentConfig.platformName}-hcloud-network`,
|
||||
ipRange: "10.254.0.0/16",
|
||||
labels: {
|
||||
platform: this.deploymentConfig.platformName,
|
||||
},
|
||||
deleteProtection: false,
|
||||
},
|
||||
{ parent: this, provider: hcloudProvider, dependsOn: [hcloudProvider] },
|
||||
);
|
||||
|
||||
// Create a network subnet for cloud vms
|
||||
this.platformNetworkSubnetCloud = new hcloud.NetworkSubnet(
|
||||
`${this.deploymentConfig.platformName}-hcloud-network-subnet-cloud`,
|
||||
{
|
||||
type: "cloud",
|
||||
networkId: this.platformNetwork.id.apply((id) => Number(id)),
|
||||
networkZone: "eu-central",
|
||||
ipRange: "10.254.1.0/24",
|
||||
},
|
||||
{
|
||||
parent: this,
|
||||
provider: hcloudProvider,
|
||||
dependsOn: [hcloudProvider, this.platformNetwork],
|
||||
},
|
||||
);
|
||||
|
||||
if (args.vswitchId) {
|
||||
// Create a network subnet for dedicated servers
|
||||
this.platformNetworkSubnetDedicated = new hcloud.NetworkSubnet(
|
||||
`${this.deploymentConfig.platformName}-hcloud-network-subnet-dedicated`,
|
||||
{
|
||||
type: "vswitch",
|
||||
networkId: this.platformNetwork.id.apply((id) => Number(id)),
|
||||
networkZone: "eu-central",
|
||||
ipRange: "10.254.2.0/24",
|
||||
vswitchId: args.vswitchId,
|
||||
},
|
||||
{
|
||||
parent: this,
|
||||
provider: hcloudProvider,
|
||||
dependsOn: [hcloudProvider, this.platformNetwork],
|
||||
},
|
||||
);
|
||||
}
|
||||
this.servers = args.servers.map((server, serverIndex) => {
|
||||
return new hcloud.Server(
|
||||
`${this.deploymentConfig.platformName}-hcloud-server-${(serverIndex + 1).toString().padStart(2, "0")}-${server.name}`,
|
||||
{
|
||||
name: server.name,
|
||||
serverType: server.type,
|
||||
deleteProtection: false,
|
||||
image: server.image || "debian-12",
|
||||
datacenter: this.deploymentConfig.datacenter,
|
||||
placementGroupId: this.placementGroup.id.apply((id) => Number(id)),
|
||||
sshKeys: [sshKey.id],
|
||||
publicNets: [
|
||||
{
|
||||
ipv4Enabled: true,
|
||||
ipv6Enabled: false,
|
||||
ipv4: this.publicIps[serverIndex].id.apply((id) => Number(id)),
|
||||
},
|
||||
],
|
||||
labels: {
|
||||
platform: this.deploymentConfig.platformName,
|
||||
...server.labels,
|
||||
},
|
||||
userData: getCloudInitConfig({
|
||||
...(server.cloudInitConfig || {}),
|
||||
sshAuthorizedKeys: [sshPublicKey],
|
||||
}),
|
||||
},
|
||||
{
|
||||
parent: this,
|
||||
provider: hcloudProvider,
|
||||
ignoreChanges: ["userData"],
|
||||
dependsOn: [
|
||||
this.placementGroup,
|
||||
this.platformNetwork,
|
||||
this.platformNetworkSubnetCloud,
|
||||
this.publicIps[serverIndex],
|
||||
sshKey,
|
||||
],
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
this.networkAttachments = this.servers.map((server, serverIndex) => {
|
||||
return new hcloud.ServerNetwork(
|
||||
`${this.deploymentConfig.platformName}-hcloud-server-network-${(serverIndex + 1).toString().padStart(2, "0")}-${args.servers[serverIndex].name}`,
|
||||
{
|
||||
serverId: server.id.apply((id) => Number(id)),
|
||||
networkId: this.platformNetworkSubnetCloud.networkId.apply((id) =>
|
||||
Number(id),
|
||||
),
|
||||
ip: `10.254.1.${serverIndex + 1 + 10}`,
|
||||
},
|
||||
{
|
||||
parent: this,
|
||||
provider: hcloudProvider,
|
||||
dependsOn: [
|
||||
this.platformNetwork,
|
||||
this.publicIps[serverIndex],
|
||||
this.servers[serverIndex],
|
||||
],
|
||||
deletedWith: this.servers[serverIndex],
|
||||
},
|
||||
);
|
||||
});
|
||||
// Create a network subnet for dedicated servers
|
||||
this.platformNetworkSubnetLoadBalancers = new hcloud.NetworkSubnet(
|
||||
`${this.deploymentConfig.platformName}-hcloud-network-subnet-loadbalancers`,
|
||||
{
|
||||
type: "cloud",
|
||||
networkId: this.platformNetwork.id.apply((id) => Number(id)),
|
||||
networkZone: "eu-central",
|
||||
ipRange: "10.254.3.0/24",
|
||||
},
|
||||
{
|
||||
parent: this,
|
||||
provider: hcloudProvider,
|
||||
dependsOn: [hcloudProvider, this.platformNetwork],
|
||||
},
|
||||
);
|
||||
|
||||
this.loadBalancerInstances =
|
||||
args.loadbalancers?.map((loadBalancerArg, loadBalancerIndex) => {
|
||||
const loadBalancer = new hcloud.LoadBalancer(
|
||||
`${this.deploymentConfig.platformName}-hcloud-loadbalancer-${loadBalancerIndex}-${loadBalancerArg.name}`,
|
||||
{
|
||||
name: loadBalancerArg.name,
|
||||
loadBalancerType: "lb11",
|
||||
networkZone: "eu-central",
|
||||
},
|
||||
{
|
||||
parent: this,
|
||||
provider: hcloudProvider,
|
||||
dependsOn: [
|
||||
this.platformNetwork,
|
||||
this.platformNetworkSubnetLoadBalancers,
|
||||
],
|
||||
},
|
||||
);
|
||||
const networkAttachment = new hcloud.LoadBalancerNetwork(
|
||||
`${this.deploymentConfig.platformName}-hcloud-loadbalancer-network-${loadBalancerIndex}-${loadBalancerArg.name}`,
|
||||
{
|
||||
subnetId: this.platformNetworkSubnetLoadBalancers.id.apply(
|
||||
(id) => id,
|
||||
),
|
||||
// networkId: this.platformNetwork.id.apply((id) => Number(id)),
|
||||
loadBalancerId: loadBalancer.id.apply((id) => Number(id)),
|
||||
},
|
||||
{
|
||||
parent: this,
|
||||
provider: hcloudProvider,
|
||||
dependsOn: [
|
||||
loadBalancer,
|
||||
this.platformNetwork,
|
||||
this.platformNetworkSubnetLoadBalancers,
|
||||
],
|
||||
},
|
||||
);
|
||||
const loadBalancerTargets = loadBalancerArg.targets.map(
|
||||
(target, targetIndex) => {
|
||||
return new hcloud.LoadBalancerTarget(
|
||||
`${this.deploymentConfig.platformName}-hcloud-loadbalancer-target-${loadBalancerIndex}-${loadBalancerArg.name}-${targetIndex}`,
|
||||
{
|
||||
type: target.type || "ip",
|
||||
loadBalancerId: loadBalancer.id.apply((id) => Number(id)),
|
||||
...target,
|
||||
},
|
||||
{
|
||||
parent: this,
|
||||
provider: hcloudProvider,
|
||||
dependsOn: [loadBalancer],
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
const loadBalancerServices = loadBalancerArg.services.map(
|
||||
(service, serviceIndex) => {
|
||||
return new hcloud.LoadBalancerService(
|
||||
`${this.deploymentConfig.platformName}-hcloud-loadbalancer-service-${loadBalancerIndex}-${loadBalancerArg.name}-${serviceIndex}`,
|
||||
{
|
||||
loadBalancerId: loadBalancer.id.apply((id) => id),
|
||||
protocol: "tcp",
|
||||
listenPort: service.port,
|
||||
destinationPort: service.targetPort,
|
||||
proxyprotocol: service.proxyprotocol || false,
|
||||
healthCheck: service.healthCheck || {
|
||||
port: service.targetPort,
|
||||
protocol: "tcp",
|
||||
interval: 15,
|
||||
timeout: 10,
|
||||
retries: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
parent: this,
|
||||
provider: hcloudProvider,
|
||||
dependsOn: [loadBalancer],
|
||||
deleteBeforeReplace: true,
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
return {
|
||||
loadBalancer: loadBalancer,
|
||||
networkAttachment: networkAttachment,
|
||||
targets: loadBalancerTargets,
|
||||
services: loadBalancerServices,
|
||||
};
|
||||
}) || [];
|
||||
|
||||
this.serverInfo = this.servers.map((server, serverIndex) => {
|
||||
return {
|
||||
name: server.name,
|
||||
publicIp: this.publicIps[serverIndex].ipAddress,
|
||||
privateIp: `10.254.1.${serverIndex + 1 + 10}`,
|
||||
};
|
||||
});
|
||||
|
||||
this.registerOutputs({
|
||||
servers: this.servers,
|
||||
platformNetwork: this.platformNetwork,
|
||||
publicIps: this.publicIps,
|
||||
serverInfo: this.serverInfo,
|
||||
networkAttachments: this.networkAttachments,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { HetznerDeployment };
|
||||
17
packages/pulumi-hetzner/package.json
Normal file
17
packages/pulumi-hetzner/package.json
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "@olsitec/pulumi-hetzner",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"main": "index.ts",
|
||||
"description": "Vendored Hetzner Cloud provisioning module (Stage-1 vendor; see VENDORED.md). Trimmed to the deps HetznerDeployment actually uses.",
|
||||
"dependencies": {
|
||||
"@pulumi/hcloud": "^1.21.1",
|
||||
"@pulumi/pulumi": "^3.138.0",
|
||||
"js-yaml": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^18",
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
}
|
||||
27
packages/pulumi-hetzner/tsconfig.json
Normal file
27
packages/pulumi-hetzner/tsconfig.json
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"outDir": "bin",
|
||||
"target": "es2020",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"pretty": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitReturns": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"bin",
|
||||
"build",
|
||||
"out",
|
||||
"_test_"
|
||||
]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue