sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/config/secret/agent_test.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package secret 18 19 import ( 20 "errors" 21 "fmt" 22 "os" 23 "path/filepath" 24 "strconv" 25 "testing" 26 "time" 27 28 "github.com/sirupsen/logrus" 29 30 "sigs.k8s.io/prow/pkg/logrusutil" 31 ) 32 33 func TestCensoringFormatter(t *testing.T) { 34 var err error 35 secret1, err := os.CreateTemp("", "") 36 if err != nil { 37 t.Fatalf("failed to set up a temporary file: %v", err) 38 } 39 if _, err := secret1.WriteString("SECRET"); err != nil { 40 t.Fatalf("failed to write a fake secret to a file: %v", err) 41 } 42 defer secret1.Close() 43 defer os.Remove(secret1.Name()) 44 secret2, err := os.CreateTemp("", "") 45 if err != nil { 46 t.Fatalf("failed to set up a temporary file: %v", err) 47 } 48 if _, err := secret2.WriteString("MYSTERY"); err != nil { 49 t.Fatalf("failed to write a fake secret to a file: %v", err) 50 } 51 defer secret2.Close() 52 defer os.Remove(secret2.Name()) 53 54 agent := agent{} 55 if err = agent.Start([]string{secret1.Name(), secret2.Name()}); err != nil { 56 t.Fatalf("failed to start a secret agent: %v", err) 57 } 58 59 testCases := []struct { 60 description string 61 entry *logrus.Entry 62 expected string 63 }{ 64 { 65 description: "all occurrences of a single secret in a message are censored", 66 entry: &logrus.Entry{Message: "A SECRET is a SECRET if it is secret"}, 67 expected: "level=panic msg=\"A XXXXXX is a XXXXXX if it is secret\"\n", 68 }, 69 { 70 description: "occurrences of a multiple secrets in a message are censored", 71 entry: &logrus.Entry{Message: "A SECRET is a MYSTERY"}, 72 expected: "level=panic msg=\"A XXXXXX is a XXXXXXX\"\n", 73 }, 74 { 75 description: "occurrences of multiple secrets in a field", 76 entry: &logrus.Entry{Message: "message", Data: logrus.Fields{"key": "A SECRET is a MYSTERY"}}, 77 expected: "level=panic msg=message key=\"A XXXXXX is a XXXXXXX\"\n", 78 }, 79 { 80 description: "occurrences of a secret in a non-string field", 81 entry: &logrus.Entry{Message: "message", Data: logrus.Fields{"key": fmt.Errorf("A SECRET is a MYSTERY")}}, 82 expected: "level=panic msg=message key=\"A XXXXXX is a XXXXXXX\"\n", 83 }, 84 } 85 86 baseFormatter := &logrus.TextFormatter{ 87 DisableColors: true, 88 DisableTimestamp: true, 89 } 90 formatter := logrusutil.NewCensoringFormatter(baseFormatter, agent.getSecrets) 91 92 for _, tc := range testCases { 93 t.Run(tc.description, func(t *testing.T) { 94 censored, err := formatter.Format(tc.entry) 95 if err != nil { 96 t.Errorf("Unexpected error: %v", err) 97 } 98 if string(censored) != tc.expected { 99 t.Errorf("Expected '%s', got '%s'", tc.expected, string(censored)) 100 } 101 }) 102 } 103 } 104 105 func TestAddWithParser(t *testing.T) { 106 t.Parallel() 107 // Go never runs a test in parallel with itself, so run 108 // the test twice to make sure the race detector checks 109 // the thread safety. 110 for idx := range []int{0, 1} { 111 t.Run(strconv.Itoa(idx), testAddWithParser) 112 } 113 } 114 115 func testAddWithParser(t *testing.T) { 116 t.Parallel() 117 118 tmpDir := t.TempDir() 119 120 secretPath := filepath.Join(tmpDir, "secret") 121 if err := os.WriteFile(secretPath, []byte("1"), 0644); err != nil { 122 t.Fatalf("failed to write initial content of secret: %v", err) 123 } 124 125 vals := make(chan int, 3) 126 errs := make(chan error, 3) 127 generator, err := AddWithParser( 128 secretPath, 129 func(raw []byte) (int, error) { 130 val, err := strconv.Atoi(string(raw)) 131 if err != nil { 132 errs <- err 133 return val, err 134 } 135 vals <- val 136 return val, err 137 }, 138 ) 139 if err != nil { 140 t.Fatalf("AddWithParser failed: %v", err) 141 } 142 143 checkValueAndErr := func(expected int, e error) { 144 t.Helper() 145 select { 146 case v := <-vals: 147 if v != expected { 148 t.Errorf("expected value to get updated to %d but got updated to %d", expected, v) 149 } 150 break 151 case err := <-errs: 152 if e == nil || e.Error() != err.Error() { 153 t.Fatalf("expected error %v, got %v", e, err) 154 } 155 // the agent reloads every second, so ten seconds should be plenty 156 case <-time.After(10 * time.Second): 157 t.Fatalf("timed out waiting for value %d and error %d", expected, e) 158 } 159 if actual := generator(); actual != expected { 160 t.Errorf("expected value %d from generator, got %d", expected, actual) 161 } 162 } 163 checkValueAndErr(1, nil) 164 165 if err := os.WriteFile(secretPath, []byte("2"), 0644); err != nil { 166 t.Fatalf("failed to update secret on disk: %v", err) 167 } 168 // expect secret to get updated 169 checkValueAndErr(2, nil) 170 171 if err := os.WriteFile(secretPath, []byte("not-a-number"), 0644); err != nil { 172 t.Fatalf("failed to update secret on disk: %v", err) 173 } 174 // expect secret to remain unchanged and an error in the parsing func 175 checkValueAndErr(2, errors.New(`strconv.Atoi: parsing "not-a-number": invalid syntax`)) 176 }