package main import ( "fmt" "os" "strconv" "github.com/pulumi/pulumi-linode/sdk/v3/go/linode" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumi/pulumi/sdk/v3/go/pulumi/config" ) 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) } instanceDetails, err := instance(ctx, p); if err != nil { return fmt.Errorf("unable to manage the instance; %w", err) } 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 { 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) } 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"), 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) } } return nil } func firewall(ctx *pulumi.Context, cfg *platform, instanceID pulumi.IntInput) error { 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, }, } _, 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 } 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 readme(ctx *pulumi.Context) error { data, err := os.ReadFile("./README.md") if err != nil { return fmt.Errorf("unable to read README.md; %w", err) } ctx.Export("readme", pulumi.String(string(data))) return nil }