Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
apiVersion: v1alpha1
kind: Infrastructure
metadata:
name: foundry
spec:
deployment:
flavor: terraform
mode: ec2
platform: ecs
patches:
- operations:
- op: replace
path: /variable/aws_region/default
value: us-east-1
target: infrastructure/variables.tf.json
type: jsonpatch
resource:
spec:
cluster: {}
config:
data:
resource.yaml: |
networking:
networkCIDR: 10.0.0.0/16
subnets:
private-a:
type: private
zone: us-east-1a
cidr: 10.0.0.0/19
public-a:
type: public
zone: us-east-1a
cidr: 10.0.96.0/22
instanceGroups:
persistent:
minSize: 3
maxSize: 3
machineType: t3.medium
dataVolume:
size: 5
ephemeral:
machineType: t3.small
status:
config:
data:
resource.yaml: |
instanceGroups:
ephemeral:
machineType: t3.small
maxSize: 1
minSize: 1
rootVolume:
size: 30
type: gp3
storage: ephemeral
persistent:
dataVolume:
size: 5
type: gp3
machineType: t3.medium
maxSize: 3
minSize: 3
rootVolume:
size: 30
type: gp3
storage: persistent
networking:
networkCIDR: 10.0.0.0/16
subnets:
private-a:
cidr: 10.0.0.0/19
type: private
zone: us-east-1a
public-a:
cidr: 10.0.96.0/22
type: public
zone: us-east-1a
41 changes: 41 additions & 0 deletions docs/examples/ecs/ec2/terraform/infrastructure/infra.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
apiVersion: v1alpha1
kind: Infrastructure
metadata:
name: foundry
spec:
deployment:
platform: ecs
mode: ec2
flavor: terraform
resource:
spec:
config:
data:
resource.yaml: |
networking:
networkCIDR: 10.0.0.0/16
subnets:
private-a:
type: private
zone: us-east-1a
cidr: 10.0.0.0/19
public-a:
type: public
zone: us-east-1a
cidr: 10.0.96.0/22
instanceGroups:
persistent:
minSize: 3
maxSize: 3
machineType: t3.medium
dataVolume:
size: 5
ephemeral:
machineType: t3.small
patches:
- target: "infrastructure/variables.tf.json"
type: jsonpatch
operations:
- op: replace
path: /variable/aws_region/default
value: us-east-1
140 changes: 140 additions & 0 deletions internal/casting/infrastructure/ecsec2terraformcasting/casting.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package ecsec2terraformcasting

import (
"bytes"
"context"
"log/slog"
"path/filepath"

"github.com/signoz/foundry/api/v1alpha1/infrastructure"
"github.com/signoz/foundry/internal/contract"
ecscontract "github.com/signoz/foundry/internal/contract/aws/ecs"
"github.com/signoz/foundry/internal/domain"
foundryerrors "github.com/signoz/foundry/internal/errors"
infrastructuremolding "github.com/signoz/foundry/internal/molding/infrastructure"
"github.com/signoz/foundry/internal/molding/infrastructure/resourcemolding"
"github.com/signoz/foundry/internal/pourer"
"github.com/signoz/foundry/internal/tooler"
"github.com/signoz/foundry/internal/tooler/terraformtooler"
)

type cloudInitData struct {
Cluster string
Selector map[string]string
DataVolume bool
}

type ecsEc2TerraformCasting struct {
logger *slog.Logger
}

func New(logger *slog.Logger) *ecsEc2TerraformCasting {
return &ecsEc2TerraformCasting{logger: logger}
}

func (c *ecsEc2TerraformCasting) Enricher(ctx context.Context, config *infrastructure.Casting) (infrastructuremolding.MoldingEnricher, error) {
return newEcsEc2TerraformMoldingEnricher(), nil
}

func (c *ecsEc2TerraformCasting) Forge(ctx context.Context, config infrastructure.Casting, p *pourer.Pourer) error {
data, err := newResources(config)
if err != nil {
return err
}

items := []struct {
template *domain.Template
path string
}{
{versionsTFTemplate, "versions.tf.json"},
{backendTFTemplate, "backend.tf.json"},
{providersTFTemplate, "providers.tf.json"},
{mainTFTemplate, "main.tf.json"},
{variablesTFTemplate, "variables.tf.json"},
{outputsTFTemplate, "outputs.tf.json"},
}

for _, item := range items {
buf := bytes.NewBuffer(nil)
if err := item.template.Execute(buf, data); err != nil {
return foundryerrors.Wrapf(err, foundryerrors.TypeInternal, "failed to execute %s template", item.path)
}

p.AddJSON(buf.Bytes(), item.path)
}

// Blobs stay byte-exact, preserving the #cloud-config header.
boots := map[string]cloudInitData{}

for key, group := range data.Pinned {
boots[key] = cloudInitData{
Cluster: data.Cluster.Name,
Selector: group.Selector,
DataVolume: group.Storage.RequiresDataVolume(),
}
}

for key, group := range data.Pools {
boots[key] = cloudInitData{
Cluster: data.Cluster.Name,
Selector: group.Selector,
DataVolume: group.Storage.RequiresDataVolume(),
}
}

for key, boot := range boots {
buf := bytes.NewBuffer(nil)
if err := cloudInitTemplate.Execute(buf, boot); err != nil {
return foundryerrors.Wrapf(err, foundryerrors.TypeInternal, "failed to execute cloud-init template")
}

p.AddBlob(buf.Bytes(), "cloud-init", key+".yaml")
}

return nil
}

func (c *ecsEc2TerraformCasting) Cast(ctx context.Context, config infrastructure.Casting, outputPath string, p *pourer.Pourer, toolers []tooler.Tooler) error {
terraform, err := terraformtooler.Lookup(toolers)
if err != nil {
return err
}

return terraform.Apply(ctx, terraformtooler.Release{
Release: domain.Release{Name: config.Metadata.Name, Owner: config.Labels()},
Root: filepath.Join(outputPath, p.Dir()),
})
}

// Melt destroys the substrate, and the volumes it holds go with it.
func (c *ecsEc2TerraformCasting) Melt(ctx context.Context, config infrastructure.Casting, outputPath string, p *pourer.Pourer, toolers []tooler.Tooler) error {
terraform, err := terraformtooler.Lookup(toolers)
if err != nil {
return err
}

return terraform.Destroy(ctx, terraformtooler.Release{
Release: domain.Release{Name: config.Metadata.Name, Owner: config.Labels()},
Root: filepath.Join(outputPath, p.Dir()),
})
}

func newResources(config infrastructure.Casting) (*ecscontract.Resources, error) {
doc := config.Spec.Resource.Status.Config.Data[resourcemolding.ResourceConfigName]

if doc == "" {
return nil, foundryerrors.Newf(foundryerrors.TypeInternal, "resource config %q is missing from the resource status", resourcemolding.ResourceConfigName)
}

declaration := &infrastructure.ResourceConfig{}
if err := domain.UnmarshalYAML([]byte(doc), declaration); err != nil {
return nil, foundryerrors.Wrapf(err, foundryerrors.TypeInternal, "failed to unmarshal resource config")
}

substrate, err := contract.NewSubstrate(config.Metadata.Name)
if err != nil {
return nil, foundryerrors.Wrapf(err, foundryerrors.TypeInvalidInput, "failed to resolve the substrate being provisioned")
}

return ecscontract.Derive(substrate, declaration, config.Labels())
}
20 changes: 20 additions & 0 deletions internal/casting/infrastructure/ecsec2terraformcasting/embed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ecsec2terraformcasting

import (
"embed"

"github.com/signoz/foundry/internal/domain"
)

//go:embed templates/*.gotmpl
var templates embed.FS

var (
versionsTFTemplate *domain.Template = domain.MustNewTemplateFromFS(templates, "templates/versions.tf.json.gotmpl", domain.FormatJSON)
backendTFTemplate *domain.Template = domain.MustNewTemplateFromFS(templates, "templates/backend.tf.json.gotmpl", domain.FormatJSON)
providersTFTemplate *domain.Template = domain.MustNewTemplateFromFS(templates, "templates/providers.tf.json.gotmpl", domain.FormatJSON)
mainTFTemplate *domain.Template = domain.MustNewTemplateFromFS(templates, "templates/main.tf.json.gotmpl", domain.FormatJSON)
variablesTFTemplate *domain.Template = domain.MustNewTemplateFromFS(templates, "templates/variables.tf.json.gotmpl", domain.FormatJSON)
outputsTFTemplate *domain.Template = domain.MustNewTemplateFromFS(templates, "templates/outputs.tf.json.gotmpl", domain.FormatJSON)
cloudInitTemplate *domain.Template = domain.MustNewTemplateFromFS(templates, "templates/cloudinit.yaml.gotmpl", domain.FormatText)
)
Loading