github.com/hernad/nomad@v1.6.112/e2e/workload_id/workload_id_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package workload_id 5 6 import ( 7 "fmt" 8 "io" 9 "strconv" 10 "strings" 11 "testing" 12 13 "github.com/hernad/nomad/e2e/e2eutil" 14 "github.com/hernad/nomad/helper/uuid" 15 "github.com/shoenig/test/must" 16 ) 17 18 // TestWorkloadIdentity runs subtests exercising workload identity related 19 // functionality. 20 func TestWorkloadIdentity(t *testing.T) { 21 nomad := e2eutil.NomadClient(t) 22 23 e2eutil.WaitForLeader(t, nomad) 24 e2eutil.WaitForNodesReady(t, nomad, 1) 25 26 t.Run("testIdentity", testIdentity) 27 t.Run("testNobody", testNobody) 28 } 29 30 // testIdentity asserts that the various combinations of identity block 31 // parameteres produce the expected results. 32 func testIdentity(t *testing.T) { 33 nomad := e2eutil.NomadClient(t) 34 35 jobID := "identity-" + uuid.Short() 36 jobIDs := []string{jobID} 37 t.Cleanup(e2eutil.CleanupJobsAndGC(t, &jobIDs)) 38 39 // start job 40 allocs := e2eutil.RegisterAndWaitForAllocs(t, nomad, "./input/identity.nomad", jobID, "") 41 must.Len(t, 1, allocs) 42 allocID := allocs[0].ID 43 44 // wait for batch alloc to complete 45 alloc := e2eutil.WaitForAllocStopped(t, nomad, allocID) 46 must.Eq(t, alloc.ClientStatus, "complete") 47 48 assertions := []struct { 49 task string 50 env bool 51 file bool 52 }{ 53 { 54 task: "none", 55 env: false, 56 file: false, 57 }, 58 { 59 task: "empty", 60 env: false, 61 file: false, 62 }, 63 { 64 task: "env", 65 env: true, 66 file: false, 67 }, 68 { 69 task: "file", 70 env: false, 71 file: true, 72 }, 73 { 74 task: "falsey", 75 env: false, 76 file: false, 77 }, 78 } 79 80 // Ensure the assertions and input file match 81 must.Len(t, len(assertions), alloc.Job.TaskGroups[0].Tasks, 82 must.Sprintf("test and jobspec mismatch")) 83 84 for _, tc := range assertions { 85 logFile := fmt.Sprintf("alloc/logs/%s.stdout.0", tc.task) 86 fd, err := nomad.AllocFS().Cat(alloc, logFile, nil) 87 must.NoError(t, err) 88 logBytes, err := io.ReadAll(fd) 89 must.NoError(t, err) 90 logs := string(logBytes) 91 92 ps := must.Sprintf("Task: %s Logs: <<EOF\n%sEOF", tc.task, logs) 93 94 must.StrHasSuffix(t, "done\n", logs, ps) 95 96 lines := strings.Split(logs, "\n") 97 switch { 98 case tc.env && tc.file: 99 must.Len(t, 4, lines, ps) 100 101 // Parse the env first 102 token := parseEnv(t, lines[1], ps) 103 104 // Assert the file length matches 105 n, err := strconv.Atoi(lines[0]) 106 must.NoError(t, err, ps) 107 must.Eq(t, n, len(token), ps) 108 109 case !tc.env && tc.file: 110 must.Len(t, 3, lines, ps) 111 112 // Assert the length is > 10 113 n, err := strconv.Atoi(lines[0]) 114 must.NoError(t, err, ps) 115 must.Greater(t, 10, n, ps) 116 117 case tc.env && !tc.file: 118 must.Len(t, 3, lines, ps) 119 120 parseEnv(t, lines[0], ps) 121 122 case !tc.env && !tc.file: 123 must.Len(t, 2, lines, ps) 124 } 125 } 126 127 } 128 129 func parseEnv(t *testing.T, line string, ps must.Setting) string { 130 must.StrHasPrefix(t, "NOMAD_TOKEN=", line, ps) 131 token := strings.Split(line, "=")[1] 132 must.Positive(t, len(token), ps) 133 return token 134 } 135 136 // testNobody asserts that when task.user is set, the nomad_token file is owned 137 // by that user and has minimal permissions. 138 // 139 // Test assumes Client is running as root! 140 func testNobody(t *testing.T) { 141 nomad := e2eutil.NomadClient(t) 142 143 jobID := "nobodyid-" + uuid.Short() 144 jobIDs := []string{jobID} 145 t.Cleanup(e2eutil.CleanupJobsAndGC(t, &jobIDs)) 146 147 // start job 148 allocs := e2eutil.RegisterAndWaitForAllocs(t, nomad, "./input/nobody.nomad", jobID, "") 149 must.Len(t, 1, allocs) 150 allocID := allocs[0].ID 151 152 // wait for batch alloc to complete 153 alloc := e2eutil.WaitForAllocStopped(t, nomad, allocID) 154 must.Eq(t, alloc.ClientStatus, "complete") 155 156 logFile := "alloc/logs/nobody.stdout.0" 157 fd, err := nomad.AllocFS().Cat(alloc, logFile, nil) 158 must.NoError(t, err) 159 logBytes, err := io.ReadAll(fd) 160 must.NoError(t, err) 161 logs := string(logBytes) 162 163 must.StrHasSuffix(t, "done\n", logs) 164 165 lines := strings.Split(logs, "\n") 166 must.Len(t, 3, lines) 167 parts := strings.Split(lines[0], " ") 168 stats := map[string]string{} 169 for _, p := range parts { 170 kvparts := strings.Split(p, "=") 171 stats[kvparts[0]] = kvparts[1] 172 } 173 174 must.Eq(t, "0600", stats["perms"], must.Sprintf("is the client running as root?")) 175 must.Eq(t, "nobody", stats["username"], must.Sprintf("is the client running as root?")) 176 }