1package taskenv
2
3import (
4	"github.com/hashicorp/nomad/nomad/structs"
5)
6
7// InterpolateServices returns an interpolated copy of services and checks with
8// values from the task's environment.
9func InterpolateServices(taskEnv *TaskEnv, services []*structs.Service) []*structs.Service {
10	// Guard against not having a valid taskEnv. This can be the case if the
11	// PreKilling or Exited hook is run before Poststart.
12	if taskEnv == nil || len(services) == 0 {
13		return nil
14	}
15
16	interpolated := make([]*structs.Service, len(services))
17
18	for i, origService := range services {
19		// Create a copy as we need to re-interpolate every time the
20		// environment changes.
21		service := origService.Copy()
22
23		for _, check := range service.Checks {
24			check.Name = taskEnv.ReplaceEnv(check.Name)
25			check.Type = taskEnv.ReplaceEnv(check.Type)
26			check.Command = taskEnv.ReplaceEnv(check.Command)
27			check.Args = taskEnv.ParseAndReplace(check.Args)
28			check.Path = taskEnv.ReplaceEnv(check.Path)
29			check.Protocol = taskEnv.ReplaceEnv(check.Protocol)
30			check.PortLabel = taskEnv.ReplaceEnv(check.PortLabel)
31			check.InitialStatus = taskEnv.ReplaceEnv(check.InitialStatus)
32			check.Method = taskEnv.ReplaceEnv(check.Method)
33			check.GRPCService = taskEnv.ReplaceEnv(check.GRPCService)
34			check.Header = interpolateMapStringSliceString(taskEnv, check.Header)
35		}
36
37		service.Name = taskEnv.ReplaceEnv(service.Name)
38		service.PortLabel = taskEnv.ReplaceEnv(service.PortLabel)
39		service.Tags = taskEnv.ParseAndReplace(service.Tags)
40		service.CanaryTags = taskEnv.ParseAndReplace(service.CanaryTags)
41		service.Meta = interpolateMapStringString(taskEnv, service.Meta)
42		service.CanaryMeta = interpolateMapStringString(taskEnv, service.CanaryMeta)
43		interpolateConnect(taskEnv, service.Connect)
44
45		interpolated[i] = service
46	}
47
48	return interpolated
49}
50
51func interpolateMapStringSliceString(taskEnv *TaskEnv, orig map[string][]string) map[string][]string {
52	if len(orig) == 0 {
53		return nil
54	}
55
56	m := make(map[string][]string, len(orig))
57	for k, vs := range orig {
58		m[taskEnv.ReplaceEnv(k)] = taskEnv.ParseAndReplace(vs)
59	}
60	return m
61}
62
63func interpolateMapStringString(taskEnv *TaskEnv, orig map[string]string) map[string]string {
64	if len(orig) == 0 {
65		return nil
66	}
67
68	m := make(map[string]string, len(orig))
69	for k, v := range orig {
70		m[taskEnv.ReplaceEnv(k)] = taskEnv.ReplaceEnv(v)
71	}
72	return m
73}
74
75func interpolateMapStringInterface(taskEnv *TaskEnv, orig map[string]interface{}) map[string]interface{} {
76	if len(orig) == 0 {
77		return nil
78	}
79
80	m := make(map[string]interface{}, len(orig))
81	for k, v := range orig {
82		m[taskEnv.ReplaceEnv(k)] = v
83	}
84	return m
85}
86
87func interpolateConnect(taskEnv *TaskEnv, connect *structs.ConsulConnect) {
88	if connect == nil {
89		return
90	}
91
92	interpolateConnectSidecarService(taskEnv, connect.SidecarService)
93	interpolateConnectSidecarTask(taskEnv, connect.SidecarTask)
94	if connect.Gateway != nil {
95		interpolateConnectGatewayProxy(taskEnv, connect.Gateway.Proxy)
96		interpolateConnectGatewayIngress(taskEnv, connect.Gateway.Ingress)
97	}
98}
99
100func interpolateConnectGatewayProxy(taskEnv *TaskEnv, proxy *structs.ConsulGatewayProxy) {
101	if proxy == nil {
102		return
103	}
104
105	m := make(map[string]*structs.ConsulGatewayBindAddress, len(proxy.EnvoyGatewayBindAddresses))
106	for k, v := range proxy.EnvoyGatewayBindAddresses {
107		m[taskEnv.ReplaceEnv(k)] = &structs.ConsulGatewayBindAddress{
108			Address: taskEnv.ReplaceEnv(v.Address),
109			Port:    v.Port,
110		}
111	}
112
113	proxy.EnvoyGatewayBindAddresses = m
114	proxy.Config = interpolateMapStringInterface(taskEnv, proxy.Config)
115}
116
117func interpolateConnectGatewayIngress(taskEnv *TaskEnv, ingress *structs.ConsulIngressConfigEntry) {
118	if ingress == nil {
119		return
120	}
121
122	for _, listener := range ingress.Listeners {
123		listener.Protocol = taskEnv.ReplaceEnv(listener.Protocol)
124		for _, service := range listener.Services {
125			service.Name = taskEnv.ReplaceEnv(service.Name)
126			service.Hosts = taskEnv.ParseAndReplace(service.Hosts)
127		}
128	}
129}
130
131func interpolateConnectSidecarService(taskEnv *TaskEnv, sidecar *structs.ConsulSidecarService) {
132	if sidecar == nil {
133		return
134	}
135
136	sidecar.Port = taskEnv.ReplaceEnv(sidecar.Port)
137	sidecar.Tags = taskEnv.ParseAndReplace(sidecar.Tags)
138	if sidecar.Proxy != nil {
139		sidecar.Proxy.LocalServiceAddress = taskEnv.ReplaceEnv(sidecar.Proxy.LocalServiceAddress)
140		if sidecar.Proxy.Expose != nil {
141			for i := 0; i < len(sidecar.Proxy.Expose.Paths); i++ {
142				sidecar.Proxy.Expose.Paths[i].Protocol = taskEnv.ReplaceEnv(sidecar.Proxy.Expose.Paths[i].Protocol)
143				sidecar.Proxy.Expose.Paths[i].ListenerPort = taskEnv.ReplaceEnv(sidecar.Proxy.Expose.Paths[i].ListenerPort)
144				sidecar.Proxy.Expose.Paths[i].Path = taskEnv.ReplaceEnv(sidecar.Proxy.Expose.Paths[i].Path)
145			}
146		}
147		for i := 0; i < len(sidecar.Proxy.Upstreams); i++ {
148			sidecar.Proxy.Upstreams[i].Datacenter = taskEnv.ReplaceEnv(sidecar.Proxy.Upstreams[i].Datacenter)
149			sidecar.Proxy.Upstreams[i].DestinationName = taskEnv.ReplaceEnv(sidecar.Proxy.Upstreams[i].DestinationName)
150			sidecar.Proxy.Upstreams[i].LocalBindAddress = taskEnv.ReplaceEnv(sidecar.Proxy.Upstreams[i].LocalBindAddress)
151		}
152		sidecar.Proxy.Config = interpolateMapStringInterface(taskEnv, sidecar.Proxy.Config)
153	}
154}
155
156func interpolateConnectSidecarTask(taskEnv *TaskEnv, task *structs.SidecarTask) {
157	if task == nil {
158		return
159	}
160
161	task.Driver = taskEnv.ReplaceEnv(task.Driver)
162	task.Config = interpolateMapStringInterface(taskEnv, task.Config)
163	task.Env = interpolateMapStringString(taskEnv, task.Env)
164	task.KillSignal = taskEnv.ReplaceEnv(task.KillSignal)
165	task.Meta = interpolateMapStringString(taskEnv, task.Meta)
166	interpolateTaskResources(taskEnv, task.Resources)
167	task.User = taskEnv.ReplaceEnv(task.User)
168}
169
170func interpolateTaskResources(taskEnv *TaskEnv, resources *structs.Resources) {
171	if resources == nil {
172		return
173	}
174
175	for i := 0; i < len(resources.Devices); i++ {
176		resources.Devices[i].Name = taskEnv.ReplaceEnv(resources.Devices[i].Name)
177		// do not interpolate constraints & affinities
178	}
179
180	for i := 0; i < len(resources.Networks); i++ {
181		resources.Networks[i].CIDR = taskEnv.ReplaceEnv(resources.Networks[i].CIDR)
182		resources.Networks[i].Device = taskEnv.ReplaceEnv(resources.Networks[i].Device)
183		resources.Networks[i].IP = taskEnv.ReplaceEnv(resources.Networks[i].IP)
184		resources.Networks[i].Mode = taskEnv.ReplaceEnv(resources.Networks[i].Mode)
185
186		if resources.Networks[i].DNS != nil {
187			resources.Networks[i].DNS.Options = taskEnv.ParseAndReplace(resources.Networks[i].DNS.Options)
188			resources.Networks[i].DNS.Searches = taskEnv.ParseAndReplace(resources.Networks[i].DNS.Searches)
189			resources.Networks[i].DNS.Servers = taskEnv.ParseAndReplace(resources.Networks[i].DNS.Servers)
190		}
191
192		for p := 0; p < len(resources.Networks[i].DynamicPorts); p++ {
193			resources.Networks[i].DynamicPorts[p].HostNetwork = taskEnv.ReplaceEnv(resources.Networks[i].DynamicPorts[p].HostNetwork)
194			resources.Networks[i].DynamicPorts[p].Label = taskEnv.ReplaceEnv(resources.Networks[i].DynamicPorts[p].Label)
195		}
196
197		for p := 0; p < len(resources.Networks[i].ReservedPorts); p++ {
198			resources.Networks[i].ReservedPorts[p].HostNetwork = taskEnv.ReplaceEnv(resources.Networks[i].ReservedPorts[p].HostNetwork)
199			resources.Networks[i].ReservedPorts[p].Label = taskEnv.ReplaceEnv(resources.Networks[i].ReservedPorts[p].Label)
200		}
201	}
202}
203