pulumi-library/pkg/application/generic.go

215 lines
5.7 KiB
Go

package application
import (
v1 "antoine-roux.tk/projects/go/pulumi-library/crds/kubernetes/certmanager/v1"
"antoine-roux.tk/projects/go/pulumi-library/pkg/exposition"
"antoine-roux.tk/projects/go/pulumi-library/pkg/meta"
"antoine-roux.tk/projects/go/pulumi-library/pkg/workload"
"errors"
"fmt"
appsv1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/apps/v1"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
"slices"
)
type Configuration struct {
Name string
Namespace string
Images []ImagesConfiguration
Dns *string
Replicas *int
Env map[string]string
AllowAllOrigin bool
}
type ImagesConfiguration struct {
Image string
Path string
Health string
}
type CreatedApplication struct {
ApplicationName pulumi.StringOutput `pulumi:"application"`
DeploymentName []pulumi.StringOutput `pulumi:"deployment"`
}
type application struct {
pulumi.ResourceState
Name string
Namespace string
Services []service
Dns string
Replicas int
Env map[string]string
AllowAllOrigin bool
shouldCreateDns bool
shouldCreateCertificate bool
shouldCreateIngress bool
}
type service struct {
Image string
Path string
Health string
}
func NewApplication(ctx *pulumi.Context, configuration *Configuration) (*CreatedApplication, error) {
if configuration.Name != "" && len(configuration.Images) > 0 {
application := &application{
Name: configuration.Name,
Namespace: configuration.Namespace,
Env: configuration.Env,
AllowAllOrigin: configuration.AllowAllOrigin,
}
var preventDuplicatePath []string
for _, image := range configuration.Images {
serviceConfiguration := service{
Image: image.Image,
Path: image.Path,
Health: image.Health,
}
if image.Path == "" {
serviceConfiguration.Path = "/"
}
if image.Health == "" {
serviceConfiguration.Health = "/"
}
if slices.Contains(preventDuplicatePath, serviceConfiguration.Path) {
return nil, errors.New("duplicate path in ingress applicationConfiguration")
}
application.Services = append(application.Services, serviceConfiguration)
preventDuplicatePath = append(preventDuplicatePath, serviceConfiguration.Path)
}
if configuration.Replicas != nil {
application.Replicas = *configuration.Replicas
} else {
application.Replicas = 1
}
if configuration.Dns != nil {
application.Dns = *configuration.Dns
application.shouldCreateDns = true
application.shouldCreateCertificate = true
application.shouldCreateIngress = true
}
err := ctx.RegisterComponentResource("pkg:application:CreatedApplication", configuration.Name, application)
if err != nil {
return nil, err
}
return application.createResources(ctx)
} else {
return nil, errors.New("missing required value ApplicationName or Image during generic application construction")
}
}
func (application *application) createResources(ctx *pulumi.Context) (*CreatedApplication, error) {
createdApplication := &CreatedApplication{
ApplicationName: pulumi.String(application.Name).ToStringOutput(),
}
namespaceConfiguration := &meta.NamespaceConfiguration{
Name: application.Name,
Namespace: application.Namespace,
}
namespace, err := namespaceConfiguration.CreateNamespace(ctx)
if err != nil {
return nil, err
}
var deployments []*appsv1.Deployment
var ingressServices []exposition.IngressServices
for index, service := range application.Services {
indexedName := fmt.Sprintf("%s-%d", application.Name, index)
appLabels := pulumi.StringMap{
"app.kubernetes.io/name": pulumi.String(indexedName),
}
deploymentConfiguration := &workload.DeploymentConfiguration{
Name: indexedName,
Env: application.Env,
Replicas: application.Replicas,
ImageReference: &workload.ImageReference{
Image: service.Image,
Health: service.Health,
},
}
deployment, err := deploymentConfiguration.CreateDeployment(ctx, namespace, application, appLabels)
if err != nil {
return nil, err
}
createdApplication.DeploymentName = append(createdApplication.DeploymentName, deployment.Metadata.Name().Elem())
serviceConfiguration := exposition.ServiceConfiguration{
Name: indexedName,
}
createdService, err := serviceConfiguration.CreateService(ctx, namespace, application, appLabels)
if err != nil {
return nil, err
}
ingressServices = append(ingressServices,
exposition.IngressServices{
Service: createdService,
Path: service.Path,
})
deployments = append(deployments, deployment)
}
if application.shouldCreateDns {
dnsConfiguration := &exposition.DnsConfiguration{
Name: application.Name,
Dns: application.Dns,
}
_, err = dnsConfiguration.CreateDNSRecord(ctx, namespace, application)
if err != nil {
return nil, err
}
}
var certificate *v1.Certificate
if application.shouldCreateCertificate {
certificateConfiguration := &meta.CertificateConfiguration{
Name: application.Name,
Dns: application.Dns,
}
certificate, err = certificateConfiguration.CreateCertificate(ctx, namespace, application)
if err != nil {
return nil, err
}
}
if application.shouldCreateIngress {
ingressConfiguration := exposition.NewIngressConfiguration(
application.Name,
application.Dns,
application.AllowAllOrigin,
ingressServices,
)
err = ingressConfiguration.CreateIngress(ctx, namespace, application, certificate)
if err != nil {
return nil, err
}
}
outs := pulumi.Map{}
for i, deployment := range deployments {
outs[fmt.Sprintf("deployment-%d", i)] = deployment.Metadata.Name()
}
err = ctx.RegisterResourceOutputs(application, outs)
if err != nil {
return nil, err
}
return createdApplication, nil
}