client: enable specifying user/group permissions in the template stanza (#13755)
* Adds Uid/Gid parameters to template. * Updated diff_test * fixed order * update jobspec and api * removed obsolete code * helper functions for jobspec parse test * updated documentation * adjusted API jobs test. * propagate uid/gid setting to job_endpoint * adjusted job_endpoint tests * making uid/gid into pointers * refactor * updated documentation * updated documentation * Update client/allocrunner/taskrunner/template/template_test.go Co-authored-by: Luiz Aoqui <luiz@hashicorp.com> * Update website/content/api-docs/json-jobs.mdx Co-authored-by: Luiz Aoqui <luiz@hashicorp.com> * propagating documentation change from Luiz * formatting * changelog entry * changed changelog entry Co-authored-by: Luiz Aoqui <luiz@hashicorp.com>
This commit is contained in:
parent
13bf88fdf7
commit
530280505f
|
@ -0,0 +1,3 @@
|
|||
```release-note:improvement
|
||||
template: Templates support new uid/gid parameter pair
|
||||
```
|
|
@ -765,6 +765,8 @@ func TestJobs_Canonicalize(t *testing.T) {
|
|||
ChangeSignal: stringToPtr(""),
|
||||
Splay: timeToPtr(5 * time.Second),
|
||||
Perms: stringToPtr("0644"),
|
||||
Uid: intToPtr(0),
|
||||
Gid: intToPtr(0),
|
||||
LeftDelim: stringToPtr("{{"),
|
||||
RightDelim: stringToPtr("}}"),
|
||||
Envvars: boolToPtr(false),
|
||||
|
@ -778,6 +780,8 @@ func TestJobs_Canonicalize(t *testing.T) {
|
|||
ChangeSignal: stringToPtr(""),
|
||||
Splay: timeToPtr(5 * time.Second),
|
||||
Perms: stringToPtr("0644"),
|
||||
Uid: intToPtr(0),
|
||||
Gid: intToPtr(0),
|
||||
LeftDelim: stringToPtr("{{"),
|
||||
RightDelim: stringToPtr("}}"),
|
||||
Envvars: boolToPtr(true),
|
||||
|
|
|
@ -799,6 +799,8 @@ type Template struct {
|
|||
ChangeSignal *string `mapstructure:"change_signal" hcl:"change_signal,optional"`
|
||||
Splay *time.Duration `mapstructure:"splay" hcl:"splay,optional"`
|
||||
Perms *string `mapstructure:"perms" hcl:"perms,optional"`
|
||||
Uid *int `mapstructure:"uid" hcl:"uid,optional"`
|
||||
Gid *int `mapstructure:"gid" hcl:"gid,optional"`
|
||||
LeftDelim *string `mapstructure:"left_delimiter" hcl:"left_delimiter,optional"`
|
||||
RightDelim *string `mapstructure:"right_delimiter" hcl:"right_delimiter,optional"`
|
||||
Envvars *bool `mapstructure:"env" hcl:"env,optional"`
|
||||
|
@ -835,6 +837,12 @@ func (tmpl *Template) Canonicalize() {
|
|||
if tmpl.Perms == nil {
|
||||
tmpl.Perms = stringToPtr("0644")
|
||||
}
|
||||
if tmpl.Uid == nil {
|
||||
tmpl.Uid = intToPtr(0)
|
||||
}
|
||||
if tmpl.Gid == nil {
|
||||
tmpl.Gid = intToPtr(0)
|
||||
}
|
||||
if tmpl.LeftDelim == nil {
|
||||
tmpl.LeftDelim = stringToPtr("{{")
|
||||
}
|
||||
|
|
|
@ -626,6 +626,12 @@ func parseTemplateConfigs(config *TaskTemplateManagerConfig) (map[*ctconf.Templa
|
|||
m := os.FileMode(v)
|
||||
ct.Perms = &m
|
||||
}
|
||||
// Set ownership
|
||||
if tmpl.Uid >= 0 && tmpl.Gid >= 0 {
|
||||
ct.Uid = &tmpl.Uid
|
||||
ct.Gid = &tmpl.Gid
|
||||
}
|
||||
|
||||
ct.Finalize()
|
||||
|
||||
ctmpls[ct] = tmpl
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -33,6 +34,7 @@ import (
|
|||
sconfig "github.com/hashicorp/nomad/nomad/structs/config"
|
||||
"github.com/hashicorp/nomad/testutil"
|
||||
"github.com/kr/pretty"
|
||||
"github.com/shoenig/test/must"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -512,6 +514,8 @@ func TestTaskTemplateManager_Permissions(t *testing.T) {
|
|||
DestPath: file,
|
||||
ChangeMode: structs.TemplateChangeModeNoop,
|
||||
Perms: "777",
|
||||
Uid: 503,
|
||||
Gid: 20,
|
||||
}
|
||||
|
||||
harness := newTestHarness(t, []*structs.Template{template}, false, false)
|
||||
|
@ -535,6 +539,13 @@ func TestTaskTemplateManager_Permissions(t *testing.T) {
|
|||
if m := fi.Mode(); m != os.ModePerm {
|
||||
t.Fatalf("Got mode %v; want %v", m, os.ModePerm)
|
||||
}
|
||||
|
||||
sys := fi.Sys()
|
||||
uid := int(sys.(*syscall.Stat_t).Uid)
|
||||
gid := int(sys.(*syscall.Stat_t).Gid)
|
||||
|
||||
must.Eq(t, template.Uid, uid)
|
||||
must.Eq(t, template.Gid, gid)
|
||||
}
|
||||
|
||||
func TestTaskTemplateManager_Unblock_Static_NomadEnv(t *testing.T) {
|
||||
|
|
|
@ -1209,6 +1209,14 @@ func ApiTaskToStructsTask(job *structs.Job, group *structs.TaskGroup,
|
|||
if len(apiTask.Templates) > 0 {
|
||||
structsTask.Templates = []*structs.Template{}
|
||||
for _, template := range apiTask.Templates {
|
||||
uid := -1
|
||||
if template.Uid != nil {
|
||||
uid = *template.Uid
|
||||
}
|
||||
gid := -1
|
||||
if template.Gid != nil {
|
||||
gid = *template.Gid
|
||||
}
|
||||
structsTask.Templates = append(structsTask.Templates,
|
||||
&structs.Template{
|
||||
SourcePath: *template.SourcePath,
|
||||
|
@ -1218,6 +1226,8 @@ func ApiTaskToStructsTask(job *structs.Job, group *structs.TaskGroup,
|
|||
ChangeSignal: *template.ChangeSignal,
|
||||
Splay: *template.Splay,
|
||||
Perms: *template.Perms,
|
||||
Uid: uid,
|
||||
Gid: gid,
|
||||
LeftDelim: *template.LeftDelim,
|
||||
RightDelim: *template.RightDelim,
|
||||
Envvars: *template.Envvars,
|
||||
|
|
|
@ -2733,6 +2733,8 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) {
|
|||
ChangeSignal: helper.StringToPtr("signal"),
|
||||
Splay: helper.TimeToPtr(1 * time.Minute),
|
||||
Perms: helper.StringToPtr("666"),
|
||||
Uid: helper.IntToPtr(1000),
|
||||
Gid: helper.IntToPtr(1000),
|
||||
LeftDelim: helper.StringToPtr("abc"),
|
||||
RightDelim: helper.StringToPtr("def"),
|
||||
Envvars: helper.BoolToPtr(true),
|
||||
|
@ -3138,6 +3140,8 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) {
|
|||
ChangeSignal: "SIGNAL",
|
||||
Splay: 1 * time.Minute,
|
||||
Perms: "666",
|
||||
Uid: 1000,
|
||||
Gid: 1000,
|
||||
LeftDelim: "abc",
|
||||
RightDelim: "def",
|
||||
Envvars: true,
|
||||
|
|
|
@ -18,6 +18,11 @@ func stringToPtr(str string) *string {
|
|||
return &str
|
||||
}
|
||||
|
||||
// intToPtr returns the pointer to an int
|
||||
func intToPtr(i int) *int {
|
||||
return &i
|
||||
}
|
||||
|
||||
// timeToPtr returns the pointer to a time.Duration.
|
||||
func timeToPtr(t time.Duration) *time.Duration {
|
||||
return &t
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
package jobspec
|
||||
|
||||
// These functions are copied from helper/funcs.go
|
||||
// added here to avoid jobspec depending on any other package
|
||||
|
||||
// intToPtr returns the pointer to an int
|
||||
func intToPtr(i int) *int {
|
||||
return &i
|
||||
}
|
||||
|
||||
// int8ToPtr returns the pointer to an int8
|
||||
func int8ToPtr(i int8) *int8 {
|
||||
return &i
|
||||
}
|
||||
|
||||
// int64ToPtr returns the pointer to an int
|
||||
func int64ToPtr(i int64) *int64 {
|
||||
return &i
|
||||
}
|
||||
|
||||
// Uint64ToPtr returns the pointer to an uint64
|
||||
func uint64ToPtr(u uint64) *uint64 {
|
||||
return &u
|
||||
}
|
|
@ -441,6 +441,8 @@ func parseTemplates(result *[]*api.Template, list *ast.ObjectList) error {
|
|||
"destination",
|
||||
"left_delimiter",
|
||||
"perms",
|
||||
"uid",
|
||||
"gid",
|
||||
"right_delimiter",
|
||||
"source",
|
||||
"splay",
|
||||
|
@ -460,6 +462,8 @@ func parseTemplates(result *[]*api.Template, list *ast.ObjectList) error {
|
|||
ChangeMode: stringToPtr("restart"),
|
||||
Splay: timeToPtr(5 * time.Second),
|
||||
Perms: stringToPtr("0644"),
|
||||
Uid: intToPtr(0),
|
||||
Gid: intToPtr(0),
|
||||
}
|
||||
|
||||
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
|
|
|
@ -24,6 +24,17 @@ const (
|
|||
templateChangeModeRestart = "restart"
|
||||
)
|
||||
|
||||
// Helper functions below are only used by this test suite
|
||||
func int8ToPtr(i int8) *int8 {
|
||||
return &i
|
||||
}
|
||||
func uint64ToPtr(u uint64) *uint64 {
|
||||
return &u
|
||||
}
|
||||
func int64ToPtr(i int64) *int64 {
|
||||
return &i
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
ci.Parallel(t)
|
||||
|
||||
|
@ -363,6 +374,8 @@ func TestParse(t *testing.T) {
|
|||
ChangeSignal: stringToPtr("foo"),
|
||||
Splay: timeToPtr(10 * time.Second),
|
||||
Perms: stringToPtr("0644"),
|
||||
Uid: intToPtr(0),
|
||||
Gid: intToPtr(0),
|
||||
Envvars: boolToPtr(true),
|
||||
VaultGrace: timeToPtr(33 * time.Second),
|
||||
},
|
||||
|
@ -372,6 +385,8 @@ func TestParse(t *testing.T) {
|
|||
ChangeMode: stringToPtr(templateChangeModeRestart),
|
||||
Splay: timeToPtr(5 * time.Second),
|
||||
Perms: stringToPtr("777"),
|
||||
Uid: intToPtr(1001),
|
||||
Gid: intToPtr(20),
|
||||
LeftDelim: stringToPtr("--"),
|
||||
RightDelim: stringToPtr("__"),
|
||||
},
|
||||
|
|
|
@ -318,6 +318,8 @@ job "binstore-storagelocker" {
|
|||
source = "bar"
|
||||
destination = "bar"
|
||||
perms = "777"
|
||||
uid = 1001
|
||||
gid = 20
|
||||
left_delimiter = "--"
|
||||
right_delimiter = "__"
|
||||
}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
package jobspec2
|
||||
|
||||
func intToPtr(v int) *int {
|
||||
return &v
|
||||
}
|
|
@ -107,6 +107,12 @@ func normalizeTemplates(templates []*api.Template) {
|
|||
if t.Perms == nil {
|
||||
t.Perms = stringToPtr("0644")
|
||||
}
|
||||
if t.Uid == nil {
|
||||
t.Uid = intToPtr(0)
|
||||
}
|
||||
if t.Gid == nil {
|
||||
t.Gid = intToPtr(0)
|
||||
}
|
||||
if t.Splay == nil {
|
||||
t.Splay = durationToPtr(5 * time.Second)
|
||||
}
|
||||
|
@ -121,6 +127,10 @@ func boolToPtr(v bool) *bool {
|
|||
return &v
|
||||
}
|
||||
|
||||
func intToPtr(v int) *int {
|
||||
return &v
|
||||
}
|
||||
|
||||
func stringToPtr(v string) *string {
|
||||
return &v
|
||||
}
|
||||
|
|
|
@ -7044,6 +7044,8 @@ func TestTaskDiff(t *testing.T) {
|
|||
ChangeSignal: "SIGHUP",
|
||||
Splay: 1,
|
||||
Perms: "0644",
|
||||
Uid: 1001,
|
||||
Gid: 21,
|
||||
Wait: &WaitConfig{
|
||||
Min: helper.TimeToPtr(5 * time.Second),
|
||||
Max: helper.TimeToPtr(5 * time.Second),
|
||||
|
@ -7057,6 +7059,8 @@ func TestTaskDiff(t *testing.T) {
|
|||
ChangeSignal: "SIGHUP2",
|
||||
Splay: 2,
|
||||
Perms: "0666",
|
||||
Uid: 1000,
|
||||
Gid: 20,
|
||||
Envvars: true,
|
||||
},
|
||||
},
|
||||
|
@ -7071,6 +7075,8 @@ func TestTaskDiff(t *testing.T) {
|
|||
ChangeSignal: "SIGHUP",
|
||||
Splay: 1,
|
||||
Perms: "0644",
|
||||
Uid: 1001,
|
||||
Gid: 21,
|
||||
Wait: &WaitConfig{
|
||||
Min: helper.TimeToPtr(5 * time.Second),
|
||||
Max: helper.TimeToPtr(10 * time.Second),
|
||||
|
@ -7084,6 +7090,8 @@ func TestTaskDiff(t *testing.T) {
|
|||
ChangeSignal: "SIGHUP3",
|
||||
Splay: 3,
|
||||
Perms: "0776",
|
||||
Uid: 1002,
|
||||
Gid: 22,
|
||||
Wait: &WaitConfig{
|
||||
Min: helper.TimeToPtr(5 * time.Second),
|
||||
Max: helper.TimeToPtr(10 * time.Second),
|
||||
|
@ -7154,6 +7162,12 @@ func TestTaskDiff(t *testing.T) {
|
|||
Old: "",
|
||||
New: "false",
|
||||
},
|
||||
{
|
||||
Type: DiffTypeAdded,
|
||||
Name: "Gid",
|
||||
Old: "",
|
||||
New: "22",
|
||||
},
|
||||
{
|
||||
Type: DiffTypeAdded,
|
||||
Name: "Perms",
|
||||
|
@ -7172,6 +7186,12 @@ func TestTaskDiff(t *testing.T) {
|
|||
Old: "",
|
||||
New: "3",
|
||||
},
|
||||
{
|
||||
Type: DiffTypeAdded,
|
||||
Name: "Uid",
|
||||
Old: "",
|
||||
New: "1002",
|
||||
},
|
||||
{
|
||||
Type: DiffTypeAdded,
|
||||
Name: "VaultGrace",
|
||||
|
@ -7234,6 +7254,12 @@ func TestTaskDiff(t *testing.T) {
|
|||
Old: "true",
|
||||
New: "",
|
||||
},
|
||||
{
|
||||
Type: DiffTypeDeleted,
|
||||
Name: "Gid",
|
||||
Old: "20",
|
||||
New: "",
|
||||
},
|
||||
{
|
||||
Type: DiffTypeDeleted,
|
||||
Name: "Perms",
|
||||
|
@ -7252,6 +7278,12 @@ func TestTaskDiff(t *testing.T) {
|
|||
Old: "2",
|
||||
New: "",
|
||||
},
|
||||
{
|
||||
Type: DiffTypeDeleted,
|
||||
Name: "Uid",
|
||||
Old: "1000",
|
||||
New: "",
|
||||
},
|
||||
{
|
||||
Type: DiffTypeDeleted,
|
||||
Name: "VaultGrace",
|
||||
|
|
|
@ -7711,6 +7711,9 @@ type Template struct {
|
|||
|
||||
// Perms is the permission the file should be written out with.
|
||||
Perms string
|
||||
// User and group that should own the file.
|
||||
Uid int
|
||||
Gid int
|
||||
|
||||
// LeftDelim and RightDelim are optional configurations to control what
|
||||
// delimiter is utilized when parsing the template.
|
||||
|
|
|
@ -1077,6 +1077,20 @@ README][ct].
|
|||
- `Perms` - Specifies the rendered template's permissions. File permissions are
|
||||
given as octal of the Unix file permissions `rwxrwxrwx`.
|
||||
|
||||
- `Uid` - Specifies the rendered template owner's user ID.
|
||||
|
||||
~> **Caveat:** Works only on Unix-based systems. Be careful when using
|
||||
containerized drivers, suck as `docker` or `podman`, as groups and users
|
||||
inside the container may have different IDs than on the host system. This
|
||||
feature will also **not** work with Docker Desktop.
|
||||
|
||||
- `Gid` - Specifies the rendered template owner's group ID.
|
||||
|
||||
~> **Caveat:** Works only on Unix-based systems. Be careful when using
|
||||
containerized drivers, suck as `docker` or `podman`, as groups and users
|
||||
inside the container may have different IDs than on the host system. This
|
||||
feature will also **not** work with Docker Desktop.
|
||||
|
||||
- `RightDelim` - Specifies the right delimiter to use in the template. The default
|
||||
is "}}" for some templates, it may be easier to use a different delimiter that
|
||||
does not conflict with the output file itself.
|
||||
|
|
|
@ -84,6 +84,20 @@ refer to the [Learn Go Template Syntax][gt_learn] Learn guide.
|
|||
- `perms` `(string: "644")` - Specifies the rendered template's permissions.
|
||||
File permissions are given as octal of the Unix file permissions `rwxrwxrwx`.
|
||||
|
||||
- `uid` `(int: 0)` - Specifies the rendered template owner's user ID.
|
||||
|
||||
~> **Caveat:** Works only on Unix-based systems. Be careful when using
|
||||
containerized drivers, suck as `docker` or `podman`, as groups and users
|
||||
inside the container may have different IDs than on the host system. This
|
||||
feature will also **not** work with Docker Desktop.
|
||||
|
||||
- `gid` `(int: 0)` - Specifies the rendered template owner's group ID.
|
||||
|
||||
~> **Caveat:** Works only on Unix-based systems. Be careful when using
|
||||
containerized drivers, suck as `docker` or `podman`, as groups and users
|
||||
inside the container may have different IDs than on the host system. This
|
||||
feature will also **not** work with Docker Desktop.
|
||||
|
||||
- `right_delimiter` `(string: "}}")` - Specifies the right delimiter to use in the
|
||||
template. The default is "}}" for some templates, it may be easier to use a
|
||||
different delimiter that does not conflict with the output file itself.
|
||||
|
@ -561,4 +575,4 @@ options](/docs/configuration/client#options):
|
|||
[task working directory]: /docs/runtime/environment#task-directories 'Task Directories'
|
||||
[filesystem internals]: /docs/concepts/filesystem#templates-artifacts-and-dispatch-payloads
|
||||
[`client.template.wait_bounds`]: /docs/configuration/client#wait_bounds
|
||||
[rhash]: https://en.wikipedia.org/wiki/Rendezvous_hashing
|
||||
[rhash]: https://en.wikipedia.org/wiki/Rendezvous_hashing
|
||||
|
|
Loading…
Reference in New Issue