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 Ready 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 Ready 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, Ready: image.Ready, } if image.Path == "" { serviceConfiguration.Path = "/" } if image.Health == "" { serviceConfiguration.Health = "/" } if image.Ready == "" { serviceConfiguration.Ready = "/" } 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, Ready: service.Ready, }, } 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 }