github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/allocrunner/taskrunner/template_hook.go (about) 1 package taskrunner 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 8 log "github.com/hashicorp/go-hclog" 9 "github.com/hashicorp/nomad/client/allocrunner/interfaces" 10 ti "github.com/hashicorp/nomad/client/allocrunner/taskrunner/interfaces" 11 "github.com/hashicorp/nomad/client/allocrunner/taskrunner/template" 12 "github.com/hashicorp/nomad/client/config" 13 "github.com/hashicorp/nomad/client/taskenv" 14 "github.com/hashicorp/nomad/nomad/structs" 15 ) 16 17 const ( 18 templateHookName = "template" 19 ) 20 21 type templateHookConfig struct { 22 // logger is used to log 23 logger log.Logger 24 25 // lifecycle is used to interact with the task's lifecycle 26 lifecycle ti.TaskLifecycle 27 28 // events is used to emit events 29 events ti.EventEmitter 30 31 // templates is the set of templates we are managing 32 templates []*structs.Template 33 34 // clientConfig is the Nomad Client configuration 35 clientConfig *config.Config 36 37 // envBuilder is the environment variable builder for the task. 38 envBuilder *taskenv.Builder 39 40 // consulNamespace is the current Consul namespace 41 consulNamespace string 42 43 // nomadNamespace is the job's Nomad namespace 44 nomadNamespace string 45 } 46 47 type templateHook struct { 48 config *templateHookConfig 49 50 // logger is used to log 51 logger log.Logger 52 53 // templateManager is used to manage any consul-templates this task may have 54 templateManager *template.TaskTemplateManager 55 managerLock sync.Mutex 56 57 // consulNamespace is the current Consul namespace 58 consulNamespace string 59 60 // vaultToken is the current Vault token 61 vaultToken string 62 63 // vaultNamespace is the current Vault namespace 64 vaultNamespace string 65 66 // nomadToken is the current Nomad token 67 nomadToken string 68 69 // taskDir is the task directory 70 taskDir string 71 } 72 73 func newTemplateHook(config *templateHookConfig) *templateHook { 74 return &templateHook{ 75 config: config, 76 consulNamespace: config.consulNamespace, 77 logger: config.logger.Named(templateHookName), 78 } 79 } 80 81 func (*templateHook) Name() string { 82 return templateHookName 83 } 84 85 func (h *templateHook) Prestart(ctx context.Context, req *interfaces.TaskPrestartRequest, resp *interfaces.TaskPrestartResponse) error { 86 h.managerLock.Lock() 87 defer h.managerLock.Unlock() 88 89 // If we have already run prerun before exit early. 90 if h.templateManager != nil { 91 return nil 92 } 93 94 // Store the current Vault token and the task directory 95 h.taskDir = req.TaskDir.Dir 96 h.vaultToken = req.VaultToken 97 h.nomadToken = req.NomadToken 98 99 // Set vault namespace if specified 100 if req.Task.Vault != nil { 101 h.vaultNamespace = req.Task.Vault.Namespace 102 } 103 104 unblockCh, err := h.newManager() 105 if err != nil { 106 return err 107 } 108 109 // Wait for the template to render 110 select { 111 case <-ctx.Done(): 112 case <-unblockCh: 113 } 114 115 return nil 116 } 117 118 func (h *templateHook) Poststart(ctx context.Context, req *interfaces.TaskPoststartRequest, resp *interfaces.TaskPoststartResponse) error { 119 h.managerLock.Lock() 120 defer h.managerLock.Unlock() 121 122 if h.templateManager == nil { 123 return nil 124 } 125 126 if req.DriverExec != nil { 127 h.templateManager.SetDriverHandle(req.DriverExec) 128 } else { 129 for _, tmpl := range h.config.templates { 130 if tmpl.ChangeMode == structs.TemplateChangeModeScript { 131 return fmt.Errorf("template has change mode set to 'script' but the driver it uses does not provide exec capability") 132 } 133 } 134 } 135 return nil 136 } 137 138 func (h *templateHook) newManager() (unblock chan struct{}, err error) { 139 unblock = make(chan struct{}) 140 m, err := template.NewTaskTemplateManager(&template.TaskTemplateManagerConfig{ 141 UnblockCh: unblock, 142 Lifecycle: h.config.lifecycle, 143 Events: h.config.events, 144 Templates: h.config.templates, 145 ClientConfig: h.config.clientConfig, 146 ConsulNamespace: h.config.consulNamespace, 147 VaultToken: h.vaultToken, 148 VaultNamespace: h.vaultNamespace, 149 TaskDir: h.taskDir, 150 EnvBuilder: h.config.envBuilder, 151 MaxTemplateEventRate: template.DefaultMaxTemplateEventRate, 152 NomadNamespace: h.config.nomadNamespace, 153 NomadToken: h.nomadToken, 154 }) 155 if err != nil { 156 h.logger.Error("failed to create template manager", "error", err) 157 return nil, err 158 } 159 160 h.templateManager = m 161 return unblock, nil 162 } 163 164 func (h *templateHook) Stop(ctx context.Context, req *interfaces.TaskStopRequest, resp *interfaces.TaskStopResponse) error { 165 h.managerLock.Lock() 166 defer h.managerLock.Unlock() 167 168 // Shutdown any created template 169 if h.templateManager != nil { 170 h.templateManager.Stop() 171 } 172 173 return nil 174 } 175 176 // Update is used to handle updates to vault and/or nomad tokens. 177 func (h *templateHook) Update(ctx context.Context, req *interfaces.TaskUpdateRequest, resp *interfaces.TaskUpdateResponse) error { 178 h.managerLock.Lock() 179 defer h.managerLock.Unlock() 180 181 // no template manager to manage 182 if h.templateManager == nil { 183 return nil 184 } 185 186 // neither vault or nomad token has been updated, nothing to do 187 if req.VaultToken == h.vaultToken && req.NomadToken == h.nomadToken { 188 return nil 189 } else { 190 h.vaultToken = req.VaultToken 191 h.nomadToken = req.NomadToken 192 } 193 194 // shutdown the old template 195 h.templateManager.Stop() 196 h.templateManager = nil 197 198 // create the new template 199 if _, err := h.newManager(); err != nil { 200 err = fmt.Errorf("failed to build template manager: %v", err) 201 h.logger.Error("failed to build template manager", "error", err) 202 _ = h.config.lifecycle.Kill(context.Background(), 203 structs.NewTaskEvent(structs.TaskKilling). 204 SetFailsTask(). 205 SetDisplayMessage(fmt.Sprintf("Template update %v", err))) 206 } 207 208 return nil 209 }