platform/main.go

252 lines
6.4 KiB
Go
Raw Normal View History

2022-03-18 02:14:49 +00:00
package main
import (
"fmt"
"bytes"
2023-02-22 22:40:12 +00:00
"os"
"strconv"
"text/template"
2023-02-22 22:05:39 +00:00
"github.com/pulumi/pulumi-linode/sdk/v3/go/linode"
2022-03-18 02:14:49 +00:00
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
2022-03-18 02:14:49 +00:00
)
func main() {
pulumi.Run(infra)
}
func infra(ctx *pulumi.Context) error {
cfg := config.New(ctx, "")
platformConfigFile := cfg.Require("file")
p, err := newConfig(platformConfigFile)
if err != nil {
return fmt.Errorf("unable to load the platform configuration; %w", err)
2022-03-18 02:14:49 +00:00
}
if err := stackscript(ctx, p); err != nil {
return fmt.Errorf("unable to manage the StackScript; %w", err)
}
2022-03-18 02:14:49 +00:00
instanceDetails, err := instance(ctx, p);
2023-02-22 22:05:39 +00:00
if err != nil {
return fmt.Errorf("unable to manage the instance; %w", err)
2023-02-22 22:05:39 +00:00
}
if err := domain(ctx, p); err != nil {
return fmt.Errorf("unable to manage the domain; %w", err)
}
if err := records(ctx, p, instanceDetails.ipv4); err != nil {
return fmt.Errorf("unable to manage the domain records; %w", err)
}
if err := firewall(ctx, p, instanceDetails.id); err != nil {
2023-02-22 22:05:39 +00:00
return fmt.Errorf("unable to manage the firewall; %w", err)
}
if err := volumes(ctx, p, instanceDetails.id); err != nil {
return fmt.Errorf("unable to manage the volumes; %w", err)
}
2023-02-22 22:40:12 +00:00
if err := readme(ctx); err != nil {
return fmt.Errorf("unable to add the README to the Stack; %w", err)
}
return nil
}
func domain(ctx *pulumi.Context, cfg *platform) error {
domainArgs := linode.DomainArgs{
Description: pulumi.String(cfg.Domain.Description),
Domain: pulumi.String(cfg.Domain.Name),
SoaEmail: pulumi.String(cfg.Domain.Email),
Status: pulumi.String("active"),
2023-02-22 22:05:39 +00:00
Tags: pulumi.ToStringArray(cfg.Tags),
Type: pulumi.String(cfg.Domain.Type),
}
_, err := linode.NewDomain(ctx, cfg.Domain.Name, &domainArgs, pulumi.Protect(true))
if err != nil {
return err
}
return nil
}
func records(ctx *pulumi.Context, cfg *platform, instanceIPv4 pulumi.StringInput) error {
domainName := cfg.Domain.Name
domainArgs := linode.LookupDomainArgs{
Domain: &domainName,
}
domain, err := linode.LookupDomain(ctx, &domainArgs, nil)
if err != nil {
return fmt.Errorf("unable to lookup domain %s; %w", domainName, err)
}
domainID, err := strconv.Atoi(*domain.Id)
if err != nil {
return fmt.Errorf("unable to get the Domain ID; %w", err)
}
for _, r := range cfg.Domain.Records {
args := linode.DomainRecordArgs{
DomainId: pulumi.Int(domainID),
Name: pulumi.String(r.Name),
RecordType: pulumi.String(r.Type),
Target: instanceIPv4,
TtlSec: pulumi.Int(r.TtlSec),
}
_, err := linode.NewDomainRecord(ctx, r.Label, &args, nil)
if err != nil {
return fmt.Errorf("unable to update the domain record '%s'; %w", r.Name, err)
}
2022-03-18 02:14:49 +00:00
}
return nil
}
2023-02-22 22:05:39 +00:00
func firewall(ctx *pulumi.Context, cfg *platform, instanceID pulumi.IntInput) error {
2023-02-22 22:05:39 +00:00
inbounds := linode.FirewallInboundArray{}
for _, a := range cfg.Firewall.Inbound.Allow {
allow := linode.FirewallInboundArgs{
Label: pulumi.String(a.Label),
Action: pulumi.String("ACCEPT"),
Protocol: pulumi.String(a.Protocol),
Ports: pulumi.String(a.Ports),
Ipv4s: pulumi.ToStringArray(a.SourceIpv4s),
Ipv6s: pulumi.ToStringArray(a.SourceIpv6s),
}
inbounds = append(inbounds, allow)
}
firewallArgs := linode.FirewallArgs{
Label: pulumi.String(cfg.Firewall.Label),
Tags: pulumi.ToStringArray(cfg.Tags),
InboundPolicy: pulumi.String("DROP"),
Inbounds: inbounds,
OutboundPolicy: pulumi.String("ACCEPT"),
Linodes: pulumi.IntArray{
instanceID,
2023-02-22 22:05:39 +00:00
},
}
_, err := linode.NewFirewall(ctx, cfg.Firewall.Label, &firewallArgs)
if err != nil {
return fmt.Errorf("unable to update the firewall; %w", err)
}
return nil
}
func volumes(ctx *pulumi.Context, cfg *platform, instanceID pulumi.IntInput) error {
for _, v := range cfg.Volumes {
args := linode.VolumeArgs{
Label: pulumi.String(v.Label),
LinodeId: instanceID,
Region: pulumi.String(cfg.Region),
Size: pulumi.Int(v.Size),
Tags: pulumi.ToStringArray(cfg.Tags),
}
_, err := linode.NewVolume(ctx, v.Label, &args, pulumi.Protect(true))
if err != nil {
return fmt.Errorf("unable to update volume; %w", err)
}
}
return nil
}
2023-02-22 22:40:12 +00:00
type instanceOutput struct {
ipv4 pulumi.StringOutput
id pulumi.IntOutput
}
func instance(ctx *pulumi.Context, cfg *platform) (instanceOutput, error) {
instanceArgs := linode.InstanceArgs{
BackupsEnabled: pulumi.Bool(cfg.Instance.BackupsEnabled),
Label: pulumi.String(cfg.Instance.Label),
PrivateIp: pulumi.Bool(cfg.Instance.PrivateIp),
Region: pulumi.String(cfg.Region),
Tags: pulumi.ToStringArray(cfg.Tags),
SwapSize: pulumi.Int(cfg.Instance.SwapSize),
Type: pulumi.String(cfg.Instance.Type),
WatchdogEnabled: pulumi.Bool(cfg.Instance.WatchdogEnabled),
}
instance, err := linode.NewInstance(ctx, cfg.Instance.Label, &instanceArgs, pulumi.Protect(true))
if err != nil {
return instanceOutput{}, fmt.Errorf("unable to update instance; %w", err)
}
instanceID := instance.ID().ToStringOutput().ApplyT(func(id string) (int, error) {
return strconv.Atoi(id)
}).(pulumi.IntOutput)
output := instanceOutput{
id: instanceID,
ipv4: instance.IpAddress,
}
return output, nil
}
func stackscript(ctx *pulumi.Context, cfg *platform) error {
script, err := stackscriptText(cfg)
if err != nil {
return err
}
args := linode.StackScriptArgs{
Label: pulumi.String(cfg.StackScript.Label),
Description: pulumi.String(cfg.StackScript.Description),
Script: pulumi.String(script),
IsPublic: pulumi.Bool(cfg.StackScript.Public),
Images: pulumi.StringArray{
pulumi.String("linode/alpine3.17"),
pulumi.String("linode/alpine3.16"),
},
}
_, err = linode.NewStackScript(ctx, cfg.StackScript.Label, &args)
if err != nil {
return fmt.Errorf("unable to update StackScript; %w", err)
}
return nil
}
func stackscriptText(cfg *platform) (string, error) {
tmpl, err := template.New("stackscript.gotmpl").ParseFiles("./templates/stackscript.gotmpl")
if err != nil {
return "", fmt.Errorf("unable to get the StackScript template; %w", err)
}
var b bytes.Buffer
if err := tmpl.Execute(&b, cfg); err != nil {
return "", err
}
return b.String(), nil
}
2023-02-22 22:40:12 +00:00
func readme(ctx *pulumi.Context) error {
data, err := os.ReadFile("./README.md")
2023-02-22 22:40:12 +00:00
if err != nil {
return fmt.Errorf("unable to read README.md; %w", err)
}
ctx.Export("readme", pulumi.String(string(data)))
2023-02-22 22:40:12 +00:00
return nil
}