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  }