stack_options.go

129 lines
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
package platform

// This file gives Stack the same declarative With… Options API used across
// Congo, as an alternative to the struct literal. Both produce the same Stack:
//
//	stack := platform.NewStack(
//		platform.WithVolume("data"),
//		platform.WithService("app",
//			platform.WithImage("${APP_IMAGE}"),
//			platform.WithPort(80, 5000),
//			platform.WithMount("data", "/data"),
//			platform.WithHealthcheck("curl -sf http://localhost:5000/health"),
//		),
//	)

// StackOption configures a Stack.
type StackOption func(*Stack)

// NewStack builds a Stack from options.
func NewStack(opts ...StackOption) *Stack {
	s := &Stack{}
	for _, opt := range opts {
		opt(s)
	}
	return s
}

// WithService adds a service built from service options.
func WithService(name string, opts ...ServiceOption) StackOption {
	return func(s *Stack) {
		svc := Service{Name: name}
		for _, opt := range opts {
			opt(&svc)
		}
		s.Services = append(s.Services, svc)
	}
}

// WithVolume declares one or more named docker volumes.
func WithVolume(names ...string) StackOption {
	return func(s *Stack) { s.Volumes = append(s.Volumes, names...) }
}

// WithNetwork declares one or more named docker networks.
func WithNetwork(names ...string) StackOption {
	return func(s *Stack) { s.Networks = append(s.Networks, names...) }
}

// ServiceOption configures a Service within a Stack.
type ServiceOption func(*Service)

// WithImage sets the service image.
func WithImage(image string) ServiceOption {
	return func(svc *Service) { svc.Image = image }
}

// WithContainerName fixes the container name (needed when another service targets
// it by name via docker exec).
func WithContainerName(name string) ServiceOption {
	return func(svc *Service) { svc.ContainerName = name }
}

// WithCommand overrides the image's default command.
func WithCommand(cmd ...string) ServiceOption {
	return func(svc *Service) { svc.Command = cmd }
}

// WithPort maps a host port to a container port.
func WithPort(host, container int) ServiceOption {
	return func(svc *Service) { svc.Ports = append(svc.Ports, Port{Host: host, Container: container}) }
}

// WithBoundPort maps a host port on a specific bind address to a container port.
func WithBoundPort(bind string, host, container int) ServiceOption {
	return func(svc *Service) {
		svc.Ports = append(svc.Ports, Port{Bind: bind, Host: host, Container: container})
	}
}

// WithEnv sets an environment variable on the service.
func WithEnv(key, value string) ServiceOption {
	return func(svc *Service) {
		if svc.Env == nil {
			svc.Env = map[string]string{}
		}
		svc.Env[key] = value
	}
}

// WithMount adds a volume or bind mount: a bare Source names a Stack volume, a
// Source with a slash is a host bind.
func WithMount(source, target string) ServiceOption {
	return func(svc *Service) { svc.Volumes = append(svc.Volumes, Mount{Source: source, Target: target}) }
}

// WithDependsOn declares services that must start before this one.
func WithDependsOn(deps ...string) ServiceOption {
	return func(svc *Service) { svc.DependsOn = append(svc.DependsOn, deps...) }
}

// WithServiceNetwork attaches the service to a named network.
func WithServiceNetwork(network string) ServiceOption {
	return func(svc *Service) { svc.Network = network }
}

// WithRestart sets the restart policy (default "unless-stopped" at render time).
func WithRestart(policy string) ServiceOption {
	return func(svc *Service) { svc.Restart = policy }
}

// WithPrivileged runs the container privileged.
func WithPrivileged() ServiceOption {
	return func(svc *Service) { svc.Privileged = true }
}

// WithHealthcheck sets a shell health check command.
func WithHealthcheck(cmd string) ServiceOption {
	return func(svc *Service) {
		if svc.Healthcheck == nil {
			svc.Healthcheck = &Healthcheck{}
		}
		svc.Healthcheck.Cmd = cmd
	}
}

// WithResources sets CPU and memory limits (e.g. "0.5", "512m").
func WithResources(cpus, memory string) ServiceOption {
	return func(svc *Service) { svc.Resources = &Resources{CPUs: cpus, Memory: memory} }
}