localenv/services/service.go
2023-08-05 22:10:48 +02:00

158 lines
3.3 KiB
Go

package services
import (
"context"
"errors"
"fmt"
"time"
"localenv/utils"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/client"
)
const NetworkName = "localenv"
var (
RetryServiceAttempts uint64 = 0
SwarmServiceReplicas uint64 = 1
RetryServiceDelay = 20 * time.Second
ErrNoRunningContainer = errors.New("no running container")
)
type ServiceOption func(spec *swarm.ServiceSpec)
func WithIONetwork() ServiceOption {
return WithNetwork(NetworkName)
}
func WithNetwork(name string) ServiceOption {
return func(spec *swarm.ServiceSpec) {
spec.Networks = append(spec.Networks,
swarm.NetworkAttachmentConfig{
Target: name,
Aliases: []string{name},
},
)
}
}
func WithHostEndpoint(port uint32) ServiceOption {
return func(spec *swarm.ServiceSpec) {
spec.EndpointSpec = &swarm.EndpointSpec{
Mode: "vip",
Ports: []swarm.PortConfig{
{
Protocol: "tcp",
TargetPort: port,
PublishedPort: port,
PublishMode: "host",
},
},
}
}
}
func WithRestartPolicy() ServiceOption {
return func(spec *swarm.ServiceSpec) {
spec.TaskTemplate.RestartPolicy = &swarm.RestartPolicy{
Condition: swarm.RestartPolicyConditionOnFailure,
MaxAttempts: &RetryServiceAttempts,
Delay: &RetryServiceDelay,
}
}
}
func WithPostgres(name string) ServiceOption {
return func(spec *swarm.ServiceSpec) {
if spec.TaskTemplate.ContainerSpec == nil {
return
}
spec.TaskTemplate.ContainerSpec.Env = append(
spec.TaskTemplate.ContainerSpec.Env,
"DB_TYPE=postgres",
fmt.Sprintf("POSTGRES_HOSTNAME=pg-%s", name),
fmt.Sprintf("POSTGRES_PORT=%d", PostgresServicePort),
fmt.Sprintf("POSTGRES_DB=%s", name),
"POSTGRES_USER=test",
"POSTGRES_PASSWORD=test",
)
}
}
func WithRabbitMQ() ServiceOption {
return func(spec *swarm.ServiceSpec) {
if spec.TaskTemplate.ContainerSpec == nil {
return
}
spec.TaskTemplate.ContainerSpec.Env = append(
spec.TaskTemplate.ContainerSpec.Env,
fmt.Sprintf("RABBITMQ_ENDPOINT=amqp://%s:%d", RabbitMQServiceName, RabbitMQServicePort),
"RABBITMQ_USERNAME=intercloud",
"RABBITMQ_PASSWORD=intercloud",
)
}
}
type Servicer interface {
Deploy(ctx context.Context, cli *client.Client) error
}
type Service struct {
spec swarm.ServiceSpec
name string
}
func (p *Service) GetBaseServiceSpec(serviceName, hostname, imageName, port string, command []string) swarm.ServiceSpec {
spec := swarm.ServiceSpec{
Annotations: swarm.Annotations{
Name: serviceName,
},
TaskTemplate: swarm.TaskSpec{
ContainerSpec: &swarm.ContainerSpec{
Hostname: hostname,
Image: imageName,
Env: []string{
fmt.Sprintf("PORT=%s", port),
},
Command: command,
},
},
Mode: swarm.ServiceMode{
Replicated: &swarm.ReplicatedService{
Replicas: &SwarmServiceReplicas,
},
},
}
return spec
}
func Deploy(ctx context.Context, cli *client.Client, spec *swarm.ServiceSpec, dependencies []Servicer) error {
err := utils.CheckServiceHealth(ctx, cli, spec.Annotations.Name)
if err == nil {
return nil
}
if !errors.Is(err, utils.ErrServiceNotFound) {
return err
}
for _, deps := range dependencies {
if err := deps.Deploy(ctx, cli); err != nil {
return err
}
}
if err := utils.CreateService(ctx, cli, spec); err != nil {
return err
}
return nil
}