304 lines
9.2 KiB
Go
304 lines
9.2 KiB
Go
|
package application
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
appsv1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/apps/v1"
|
||
|
corev1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/core/v1"
|
||
|
metav1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/meta/v1"
|
||
|
netv1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/networking/v1"
|
||
|
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
|
||
|
v1 "infra/crds/kubernetes/certmanager/v1"
|
||
|
"infra/crds/kubernetes/externaldns/v1alpha1"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
loadBalancerAddressName = "internal-lb.localdomain"
|
||
|
defaultRecordTTL = 180
|
||
|
)
|
||
|
|
||
|
type Configuration struct {
|
||
|
Name string
|
||
|
Namespace string
|
||
|
Image string
|
||
|
Dns *string
|
||
|
Replicas *int
|
||
|
Env map[string]string
|
||
|
}
|
||
|
|
||
|
type internalConfiguration struct {
|
||
|
Name string
|
||
|
Namespace string
|
||
|
Image string
|
||
|
Dns string
|
||
|
Replicas int
|
||
|
Env map[string]string
|
||
|
|
||
|
ShouldCreateDns bool
|
||
|
ShouldCreateCertificate bool
|
||
|
ShouldCreateIngress bool
|
||
|
}
|
||
|
|
||
|
type Application struct {
|
||
|
pulumi.ResourceState
|
||
|
|
||
|
DeploymentName pulumi.StringOutput `pulumi:"deployment"`
|
||
|
}
|
||
|
|
||
|
func NewApplication(ctx *pulumi.Context, publicConfiguration *Configuration) (*Application, error) {
|
||
|
|
||
|
if publicConfiguration.Name != "" && publicConfiguration.Image != "" {
|
||
|
configuration := &internalConfiguration{
|
||
|
Name: publicConfiguration.Name,
|
||
|
Namespace: publicConfiguration.Namespace,
|
||
|
Image: publicConfiguration.Image,
|
||
|
Env: publicConfiguration.Env,
|
||
|
}
|
||
|
|
||
|
if publicConfiguration.Replicas != nil {
|
||
|
configuration.Replicas = *publicConfiguration.Replicas
|
||
|
} else {
|
||
|
configuration.Replicas = 1
|
||
|
}
|
||
|
|
||
|
if publicConfiguration.Dns != nil {
|
||
|
configuration.Dns = *publicConfiguration.Dns
|
||
|
configuration.ShouldCreateIngress = true
|
||
|
configuration.ShouldCreateDns = true
|
||
|
configuration.ShouldCreateCertificate = true
|
||
|
}
|
||
|
|
||
|
return createResource(ctx, configuration)
|
||
|
} else {
|
||
|
return nil, errors.New("missing required value Name or Image during generic application construction")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func createResource(ctx *pulumi.Context, configuration *internalConfiguration, opts ...pulumi.ResourceOption) (*Application, error) {
|
||
|
application := &Application{}
|
||
|
err := ctx.RegisterComponentResource("pkg:application:Application", configuration.Name, application)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
namespace, err := createNamespace(ctx, configuration)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
appLabels := pulumi.StringMap{
|
||
|
"app.kubernetes.io/name": pulumi.String(configuration.Name),
|
||
|
}
|
||
|
deployment, err := createDeployment(ctx, configuration, namespace, appLabels, application)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
application.DeploymentName = deployment.Metadata.Name().Elem()
|
||
|
|
||
|
_, err = createService(ctx, configuration, namespace, appLabels, application)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
certificate, err := createCertificate(ctx, configuration, namespace, application)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
_, err = createIngress(ctx, configuration, namespace, certificate, application)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
_, err = createDNSRecord(ctx, configuration, namespace, application)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
err = ctx.RegisterResourceOutputs(application, pulumi.Map{
|
||
|
"deployment": deployment.Metadata.Name(),
|
||
|
})
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return application, nil
|
||
|
}
|
||
|
|
||
|
func createIngress(
|
||
|
ctx *pulumi.Context, configuration *internalConfiguration, namespace *corev1.Namespace, certificate *v1.Certificate, application *Application,
|
||
|
) (*netv1.Ingress, error) {
|
||
|
if configuration.ShouldCreateIngress {
|
||
|
host := pulumi.String(configuration.Dns)
|
||
|
return netv1.NewIngress(ctx, configuration.Name, &netv1.IngressArgs{
|
||
|
Metadata: &metav1.ObjectMetaArgs{
|
||
|
Namespace: namespace.Metadata.Name(),
|
||
|
Labels: pulumi.StringMap{
|
||
|
"app.kubernetes.io/part-of": pulumi.String(configuration.Name),
|
||
|
"app.kubernetes.io/managed-by": pulumi.String("pulumi"),
|
||
|
},
|
||
|
Annotations: pulumi.StringMap{
|
||
|
"traefik.ingress.kubernetes.io/router.middlewares": pulumi.String("kube-ingress-gzip-compress@kubernetescrd"),
|
||
|
"traefik.ingress.kubernetes.io/router.entrypoints": pulumi.String("websecure"),
|
||
|
},
|
||
|
},
|
||
|
Spec: &netv1.IngressSpecArgs{
|
||
|
IngressClassName: nil,
|
||
|
Rules: &netv1.IngressRuleArray{
|
||
|
netv1.IngressRuleArgs{
|
||
|
Host: host,
|
||
|
Http: &netv1.HTTPIngressRuleValueArgs{},
|
||
|
},
|
||
|
},
|
||
|
Tls: &netv1.IngressTLSArray{
|
||
|
netv1.IngressTLSArgs{
|
||
|
Hosts: pulumi.StringArray{host},
|
||
|
SecretName: certificate.Spec.SecretName(),
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}, pulumi.Parent(application))
|
||
|
}
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
func createCertificate(
|
||
|
ctx *pulumi.Context, configuration *internalConfiguration, namespace *corev1.Namespace, application *Application,
|
||
|
) (*v1.Certificate, error) {
|
||
|
if configuration.ShouldCreateCertificate {
|
||
|
return v1.NewCertificate(ctx, configuration.Name, &v1.CertificateArgs{
|
||
|
Metadata: &metav1.ObjectMetaArgs{
|
||
|
Namespace: namespace.Metadata.Name(),
|
||
|
Labels: pulumi.StringMap{
|
||
|
"app.kubernetes.io/part-of": pulumi.String(configuration.Name),
|
||
|
"app.kubernetes.io/managed-by": pulumi.String("pulumi"),
|
||
|
},
|
||
|
},
|
||
|
Spec: &v1.CertificateSpecArgs{
|
||
|
SecretName: pulumi.String(fmt.Sprintf("%s-certificate", configuration.Name)),
|
||
|
DnsNames: pulumi.StringArray{
|
||
|
pulumi.String(configuration.Dns),
|
||
|
},
|
||
|
IssuerRef: &v1.CertificateSpecIssuerRefArgs{
|
||
|
Name: pulumi.String("localdomain-issuer"),
|
||
|
Kind: pulumi.String("ClusterIssuer"),
|
||
|
Group: pulumi.String("cfssl-issuer.wikimedia.org"),
|
||
|
},
|
||
|
},
|
||
|
}, pulumi.Parent(application))
|
||
|
}
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
func createDNSRecord(ctx *pulumi.Context, configuration *internalConfiguration, namespace *corev1.Namespace, application *Application) (*v1alpha1.DNSEndpoint, error) {
|
||
|
if configuration.ShouldCreateDns {
|
||
|
return v1alpha1.NewDNSEndpoint(ctx, fmt.Sprintf("%s-record", configuration.Name), &v1alpha1.DNSEndpointArgs{
|
||
|
Metadata: &metav1.ObjectMetaArgs{
|
||
|
Namespace: namespace.Metadata.Name(),
|
||
|
Labels: pulumi.StringMap{
|
||
|
"app.kubernetes.io/part-of": pulumi.String(configuration.Name),
|
||
|
"app.kubernetes.io/managed-by": pulumi.String("pulumi"),
|
||
|
},
|
||
|
},
|
||
|
Spec: &v1alpha1.DNSEndpointSpecArgs{
|
||
|
Endpoints: &v1alpha1.DNSEndpointSpecEndpointsArray{
|
||
|
&v1alpha1.DNSEndpointSpecEndpointsArgs{
|
||
|
DnsName: pulumi.String(configuration.Dns),
|
||
|
RecordTTL: pulumi.Int(defaultRecordTTL),
|
||
|
RecordType: pulumi.String("CNAME"),
|
||
|
Targets: pulumi.StringArray{
|
||
|
pulumi.String(loadBalancerAddressName),
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}, pulumi.Parent(application))
|
||
|
}
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
func createService(
|
||
|
ctx *pulumi.Context, configuration *internalConfiguration, namespace *corev1.Namespace, appLabels pulumi.StringMap, application *Application,
|
||
|
) (*corev1.Service, error) {
|
||
|
return corev1.NewService(ctx, fmt.Sprintf("%s-service", configuration.Name), &corev1.ServiceArgs{
|
||
|
Metadata: &metav1.ObjectMetaArgs{
|
||
|
Namespace: namespace.Metadata.Name(),
|
||
|
Labels: pulumi.StringMap{
|
||
|
"app.kubernetes.io/part-of": pulumi.String(configuration.Name),
|
||
|
"app.kubernetes.io/managed-by": pulumi.String("pulumi"),
|
||
|
},
|
||
|
},
|
||
|
Spec: &corev1.ServiceSpecArgs{
|
||
|
Type: pulumi.String("ClusterIP"),
|
||
|
Selector: appLabels,
|
||
|
Ports: corev1.ServicePortArray{
|
||
|
corev1.ServicePortArgs{
|
||
|
Port: pulumi.Int(8090),
|
||
|
TargetPort: pulumi.String("http"),
|
||
|
Protocol: pulumi.String("TCP"),
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}, pulumi.Parent(application))
|
||
|
}
|
||
|
|
||
|
func createDeployment(
|
||
|
ctx *pulumi.Context, configuration *internalConfiguration, namespace *corev1.Namespace, appLabels pulumi.StringMap, application *Application,
|
||
|
) (*appsv1.Deployment, error) {
|
||
|
env := corev1.EnvVarArray{}
|
||
|
for key, value := range configuration.Env {
|
||
|
env = append(env, &corev1.EnvVarArgs{
|
||
|
Name: pulumi.String(key),
|
||
|
Value: pulumi.String(value),
|
||
|
})
|
||
|
}
|
||
|
|
||
|
return appsv1.NewDeployment(ctx, fmt.Sprintf("%s-deployment", configuration.Name), &appsv1.DeploymentArgs{
|
||
|
Metadata: &metav1.ObjectMetaArgs{
|
||
|
Namespace: namespace.Metadata.Name(),
|
||
|
Labels: pulumi.StringMap{
|
||
|
"app.kubernetes.io/part-of": pulumi.String(configuration.Name),
|
||
|
"app.kubernetes.io/managed-by": pulumi.String("pulumi"),
|
||
|
},
|
||
|
},
|
||
|
Spec: appsv1.DeploymentSpecArgs{
|
||
|
Selector: &metav1.LabelSelectorArgs{
|
||
|
MatchLabels: appLabels,
|
||
|
},
|
||
|
Replicas: pulumi.Int(configuration.Replicas),
|
||
|
Template: &corev1.PodTemplateSpecArgs{
|
||
|
Metadata: &metav1.ObjectMetaArgs{
|
||
|
Labels: appLabels,
|
||
|
},
|
||
|
Spec: &corev1.PodSpecArgs{
|
||
|
Containers: corev1.ContainerArray{
|
||
|
corev1.ContainerArgs{
|
||
|
Name: pulumi.String(configuration.Name),
|
||
|
Image: pulumi.String(configuration.Image),
|
||
|
Ports: corev1.ContainerPortArray{
|
||
|
corev1.ContainerPortArgs{
|
||
|
Name: pulumi.String("http"),
|
||
|
ContainerPort: pulumi.Int(80),
|
||
|
Protocol: pulumi.String("TCP"),
|
||
|
},
|
||
|
},
|
||
|
Env: env,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}, pulumi.Parent(application))
|
||
|
}
|
||
|
|
||
|
func createNamespace(ctx *pulumi.Context, configuration *internalConfiguration) (*corev1.Namespace, error) {
|
||
|
return corev1.NewNamespace(ctx, fmt.Sprintf("%s-namespace", configuration.Name), &corev1.NamespaceArgs{
|
||
|
Metadata: &metav1.ObjectMetaArgs{
|
||
|
Name: pulumi.String(configuration.Namespace),
|
||
|
Labels: pulumi.StringMap{
|
||
|
"app.kubernetes.io/managed-by": pulumi.String("pulumi"),
|
||
|
},
|
||
|
},
|
||
|
})
|
||
|
}
|