feature: support creating application with multiple deployment, service and expose it with multiple path in ingress configuration
This commit is contained in:
parent
53af9043fe
commit
2e7da70a48
12
main.go
12
main.go
@ -11,11 +11,19 @@ func main() {
|
||||
nginxApplication, err := application.NewApplication(ctx, &application.Configuration{
|
||||
Name: "nginx",
|
||||
Namespace: "pulumi-test",
|
||||
Image: "nginx",
|
||||
Images: []application.ImagesConfiguration{
|
||||
{
|
||||
Image: "nginx",
|
||||
},
|
||||
{
|
||||
Image: "nginx",
|
||||
Path: "/api",
|
||||
},
|
||||
},
|
||||
Env: map[string]string{
|
||||
"version": "1.0.0",
|
||||
},
|
||||
Dns: pulumi.StringRef("pulumi-test-nginx.localdomain"),
|
||||
Dns: pulumi.StringRef("pulumi-test-nginx.localdomain"),
|
||||
AllowAllOrigin: true,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
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"
|
||||
"slices"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -18,20 +19,25 @@ const (
|
||||
defaultRecordTTL = 180
|
||||
)
|
||||
|
||||
type ImagesConfiguration struct {
|
||||
Image string
|
||||
Path string
|
||||
}
|
||||
|
||||
type Configuration struct {
|
||||
Name string
|
||||
Namespace string
|
||||
Image string
|
||||
Images []ImagesConfiguration
|
||||
Dns *string
|
||||
Replicas *int
|
||||
Env map[string]string
|
||||
AllowAllOrigin bool
|
||||
}
|
||||
|
||||
type internalConfiguration struct {
|
||||
type configuration struct {
|
||||
Name string
|
||||
Namespace string
|
||||
Image string
|
||||
Images []ImagesConfiguration
|
||||
Dns string
|
||||
Replicas int
|
||||
Env map[string]string
|
||||
@ -42,6 +48,22 @@ type internalConfiguration struct {
|
||||
ResponseHeaders *traefikv1alpha1.MiddlewareSpecHeadersArgs
|
||||
}
|
||||
|
||||
type deploymentConfiguration struct {
|
||||
Env map[string]string
|
||||
Name string
|
||||
Replicas int
|
||||
ImageReference ImagesConfiguration
|
||||
}
|
||||
|
||||
type serviceConfiguration struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
type ingressConfiguration struct {
|
||||
Service *corev1.Service
|
||||
ImageReference ImagesConfiguration
|
||||
}
|
||||
|
||||
type Application struct {
|
||||
pulumi.ResourceState
|
||||
|
||||
@ -50,40 +72,55 @@ type Application struct {
|
||||
|
||||
func NewApplication(ctx *pulumi.Context, publicConfiguration *Configuration) (*Application, error) {
|
||||
|
||||
if publicConfiguration.Name != "" && publicConfiguration.Image != "" {
|
||||
configuration := &internalConfiguration{
|
||||
if publicConfiguration.Name != "" && len(publicConfiguration.Images) > 0 {
|
||||
localConfiguration := &configuration{
|
||||
Name: publicConfiguration.Name,
|
||||
Namespace: publicConfiguration.Namespace,
|
||||
Image: publicConfiguration.Image,
|
||||
Env: publicConfiguration.Env,
|
||||
}
|
||||
|
||||
var preventDuplicatePath []string
|
||||
for _, publicImageConfiguration := range publicConfiguration.Images {
|
||||
localImagesConfiguration := ImagesConfiguration{
|
||||
Image: publicImageConfiguration.Image,
|
||||
Path: publicImageConfiguration.Path,
|
||||
}
|
||||
if publicImageConfiguration.Path == "" {
|
||||
localImagesConfiguration.Path = "/"
|
||||
}
|
||||
if slices.Contains(preventDuplicatePath, localImagesConfiguration.Path) {
|
||||
return nil, errors.New("duplicate path in ingress configuration")
|
||||
}
|
||||
localConfiguration.Images = append(localConfiguration.Images, localImagesConfiguration)
|
||||
preventDuplicatePath = append(preventDuplicatePath, localImagesConfiguration.Path)
|
||||
}
|
||||
|
||||
if publicConfiguration.Replicas != nil {
|
||||
configuration.Replicas = *publicConfiguration.Replicas
|
||||
localConfiguration.Replicas = *publicConfiguration.Replicas
|
||||
} else {
|
||||
configuration.Replicas = 1
|
||||
localConfiguration.Replicas = 1
|
||||
}
|
||||
|
||||
if publicConfiguration.Dns != nil {
|
||||
configuration.Dns = *publicConfiguration.Dns
|
||||
configuration.ShouldCreateIngress = true
|
||||
configuration.ShouldCreateDns = true
|
||||
configuration.ShouldCreateCertificate = true
|
||||
localConfiguration.Dns = *publicConfiguration.Dns
|
||||
localConfiguration.ShouldCreateIngress = true
|
||||
localConfiguration.ShouldCreateDns = true
|
||||
localConfiguration.ShouldCreateCertificate = true
|
||||
}
|
||||
|
||||
if publicConfiguration.AllowAllOrigin {
|
||||
configuration.ResponseHeaders = &traefikv1alpha1.MiddlewareSpecHeadersArgs{
|
||||
localConfiguration.ResponseHeaders = &traefikv1alpha1.MiddlewareSpecHeadersArgs{
|
||||
AccessControlAllowOriginList: toPulumiStringArray([]string{"*"}),
|
||||
}
|
||||
}
|
||||
|
||||
return createResources(ctx, configuration)
|
||||
return createResources(ctx, localConfiguration)
|
||||
} else {
|
||||
return nil, errors.New("missing required value Name or Image during generic application construction")
|
||||
}
|
||||
}
|
||||
|
||||
func createResources(ctx *pulumi.Context, configuration *internalConfiguration, opts ...pulumi.ResourceOption) (*Application, error) {
|
||||
func createResources(ctx *pulumi.Context, configuration *configuration, opts ...pulumi.ResourceOption) (*Application, error) {
|
||||
application := &Application{}
|
||||
err := ctx.RegisterComponentResource("pkg:application:Application", configuration.Name, application)
|
||||
if err != nil {
|
||||
@ -95,18 +132,37 @@ func createResources(ctx *pulumi.Context, configuration *internalConfiguration,
|
||||
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()
|
||||
var ingressesParameter []ingressConfiguration
|
||||
var deployments []*appsv1.Deployment
|
||||
for index, image := range configuration.Images {
|
||||
appLabels := pulumi.StringMap{
|
||||
"app.kubernetes.io/name": pulumi.String(configuration.Name),
|
||||
}
|
||||
|
||||
service, err := createService(ctx, configuration, namespace, appLabels, application)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
deploymentParameter := &deploymentConfiguration{
|
||||
Name: fmt.Sprintf("%s-%d", configuration.Name, index),
|
||||
Env: configuration.Env,
|
||||
Replicas: configuration.Replicas,
|
||||
ImageReference: image,
|
||||
}
|
||||
deployment, err := createDeployment(ctx, deploymentParameter, namespace, appLabels, application)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
application.DeploymentName = deployment.Metadata.Name().Elem()
|
||||
|
||||
serviceParameter := serviceConfiguration{
|
||||
Name: fmt.Sprintf("%s-%d", configuration.Name, index),
|
||||
}
|
||||
service, err := createService(ctx, serviceParameter, namespace, appLabels, application)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ingressesParameter = append(ingressesParameter, ingressConfiguration{
|
||||
Service: service,
|
||||
ImageReference: image,
|
||||
})
|
||||
deployments = append(deployments, deployment)
|
||||
}
|
||||
|
||||
certificate, err := createCertificate(ctx, configuration, namespace, application)
|
||||
@ -119,7 +175,7 @@ func createResources(ctx *pulumi.Context, configuration *internalConfiguration,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = createIngress(ctx, configuration, namespace, certificate, service, application, headerMiddleware)
|
||||
_, err = createIngress(ctx, configuration, namespace, certificate, ingressesParameter, application, headerMiddleware)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -129,9 +185,12 @@ func createResources(ctx *pulumi.Context, configuration *internalConfiguration,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ctx.RegisterResourceOutputs(application, pulumi.Map{
|
||||
"deployment": deployment.Metadata.Name(),
|
||||
})
|
||||
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
|
||||
}
|
||||
@ -149,7 +208,7 @@ func toPulumiStringArray(values []string) pulumi.StringArray {
|
||||
|
||||
func createMiddlewareAddResponseHeader(
|
||||
ctx *pulumi.Context,
|
||||
configuration *internalConfiguration,
|
||||
configuration *configuration,
|
||||
namespace *corev1.Namespace,
|
||||
application *Application,
|
||||
) (*traefikv1alpha1.Middleware, error) {
|
||||
@ -168,19 +227,33 @@ func createMiddlewareAddResponseHeader(
|
||||
}, pulumi.Parent(application))
|
||||
}
|
||||
|
||||
func createIngress(ctx *pulumi.Context, configuration *internalConfiguration, namespace *corev1.Namespace, certificate *v1.Certificate, service *corev1.Service, application *Application, responseHeaderMiddleware *traefikv1alpha1.Middleware) (*netv1.Ingress, error) {
|
||||
func createIngress(ctx *pulumi.Context, configuration *configuration, namespace *corev1.Namespace, certificate *v1.Certificate, servicesConfiguration []ingressConfiguration, application *Application, responseHeaderMiddleware *traefikv1alpha1.Middleware) (*netv1.Ingress, error) {
|
||||
if configuration.ShouldCreateIngress {
|
||||
host := pulumi.String(configuration.Dns)
|
||||
middlewares := pulumi.All(namespace.Metadata.Name().Elem(), responseHeaderMiddleware.Metadata.Name().Elem()).ApplyT(func(args []interface{}) string {
|
||||
return fmt.Sprintf("kube-ingress-gzip-compress@kubernetescrd,%s-%s@kubernetescrd",
|
||||
args[0], args[1],
|
||||
)
|
||||
return fmt.Sprintf("kube-ingress-gzip-compress@kubernetescrd,%s-%s@kubernetescrd", args[0], args[1])
|
||||
}).(pulumi.StringOutput)
|
||||
ingressAnnotations := pulumi.StringMap{
|
||||
"traefik.ingress.kubernetes.io/router.middlewares": middlewares,
|
||||
"traefik.ingress.kubernetes.io/router.entrypoints": pulumi.String("websecure"),
|
||||
}
|
||||
|
||||
var ingressPaths netv1.HTTPIngressPathArray
|
||||
for _, service := range servicesConfiguration {
|
||||
ingressPaths = append(ingressPaths, netv1.HTTPIngressPathArgs{
|
||||
Path: pulumi.String(service.ImageReference.Path),
|
||||
PathType: pulumi.String("Prefix"),
|
||||
Backend: &netv1.IngressBackendArgs{
|
||||
Service: &netv1.IngressServiceBackendArgs{
|
||||
Name: service.Service.Metadata.Name().Elem(),
|
||||
Port: &netv1.ServiceBackendPortArgs{
|
||||
Name: pulumi.String("exposed-port"),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return netv1.NewIngress(ctx, configuration.Name, &netv1.IngressArgs{
|
||||
Metadata: &metav1.ObjectMetaArgs{
|
||||
Namespace: namespace.Metadata.Name(),
|
||||
@ -196,20 +269,7 @@ func createIngress(ctx *pulumi.Context, configuration *internalConfiguration, na
|
||||
netv1.IngressRuleArgs{
|
||||
Host: host,
|
||||
Http: &netv1.HTTPIngressRuleValueArgs{
|
||||
Paths: &netv1.HTTPIngressPathArray{
|
||||
netv1.HTTPIngressPathArgs{
|
||||
Path: pulumi.String("/"),
|
||||
PathType: pulumi.String("Prefix"),
|
||||
Backend: &netv1.IngressBackendArgs{
|
||||
Service: &netv1.IngressServiceBackendArgs{
|
||||
Name: service.Metadata.Name().Elem(),
|
||||
Port: &netv1.ServiceBackendPortArgs{
|
||||
Name: pulumi.String("exposed-port"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Paths: ingressPaths,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -226,7 +286,7 @@ func createIngress(ctx *pulumi.Context, configuration *internalConfiguration, na
|
||||
}
|
||||
|
||||
func createCertificate(
|
||||
ctx *pulumi.Context, configuration *internalConfiguration, namespace *corev1.Namespace, application *Application,
|
||||
ctx *pulumi.Context, configuration *configuration, namespace *corev1.Namespace, application *Application,
|
||||
) (*v1.Certificate, error) {
|
||||
if configuration.ShouldCreateCertificate {
|
||||
return v1.NewCertificate(ctx, configuration.Name, &v1.CertificateArgs{
|
||||
@ -253,7 +313,7 @@ func createCertificate(
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func createDNSRecord(ctx *pulumi.Context, configuration *internalConfiguration, namespace *corev1.Namespace, application *Application) (*dnsv1alpha1.DNSEndpoint, error) {
|
||||
func createDNSRecord(ctx *pulumi.Context, configuration *configuration, namespace *corev1.Namespace, application *Application) (*dnsv1alpha1.DNSEndpoint, error) {
|
||||
if configuration.ShouldCreateDns {
|
||||
return dnsv1alpha1.NewDNSEndpoint(ctx, fmt.Sprintf("%s-record", configuration.Name), &dnsv1alpha1.DNSEndpointArgs{
|
||||
Metadata: &metav1.ObjectMetaArgs{
|
||||
@ -280,9 +340,7 @@ func createDNSRecord(ctx *pulumi.Context, configuration *internalConfiguration,
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func createService(
|
||||
ctx *pulumi.Context, configuration *internalConfiguration, namespace *corev1.Namespace, appLabels pulumi.StringMap, application *Application,
|
||||
) (*corev1.Service, error) {
|
||||
func createService(ctx *pulumi.Context, configuration serviceConfiguration, 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(),
|
||||
@ -307,7 +365,7 @@ func createService(
|
||||
}
|
||||
|
||||
func createDeployment(
|
||||
ctx *pulumi.Context, configuration *internalConfiguration, namespace *corev1.Namespace, appLabels pulumi.StringMap, application *Application,
|
||||
ctx *pulumi.Context, configuration *deploymentConfiguration, namespace *corev1.Namespace, appLabels pulumi.StringMap, application *Application,
|
||||
) (*appsv1.Deployment, error) {
|
||||
env := corev1.EnvVarArray{}
|
||||
for key, value := range configuration.Env {
|
||||
@ -338,7 +396,7 @@ func createDeployment(
|
||||
Containers: corev1.ContainerArray{
|
||||
corev1.ContainerArgs{
|
||||
Name: pulumi.String(configuration.Name),
|
||||
Image: pulumi.String(configuration.Image),
|
||||
Image: pulumi.String(configuration.ImageReference.Image),
|
||||
Ports: corev1.ContainerPortArray{
|
||||
corev1.ContainerPortArgs{
|
||||
Name: pulumi.String("http"),
|
||||
@ -355,7 +413,7 @@ func createDeployment(
|
||||
}, pulumi.Parent(application))
|
||||
}
|
||||
|
||||
func createNamespace(ctx *pulumi.Context, configuration *internalConfiguration) (*corev1.Namespace, error) {
|
||||
func createNamespace(ctx *pulumi.Context, configuration *configuration) (*corev1.Namespace, error) {
|
||||
return corev1.NewNamespace(ctx, fmt.Sprintf("%s-namespace", configuration.Name), &corev1.NamespaceArgs{
|
||||
Metadata: &metav1.ObjectMetaArgs{
|
||||
Name: pulumi.String(configuration.Namespace),
|
||||
|
Loading…
Reference in New Issue
Block a user