markasfailed_test.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. /*
  2. Copyright © The ESO Authors
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. https://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package externalsecret
  14. import (
  15. "errors"
  16. "fmt"
  17. "strings"
  18. "testing"
  19. "github.com/prometheus/client_golang/prometheus"
  20. v1 "k8s.io/api/core/v1"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. "k8s.io/client-go/tools/record"
  23. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  24. ctrlutil "github.com/external-secrets/external-secrets/pkg/controllers/util"
  25. )
  26. // secretValue is the shape that leaked through a PushSecret condition in
  27. // external-secrets#5884: json.Unmarshal echoes the offending value.
  28. const secretValue = "8019210420527506405"
  29. func markAsFailedFixture() (*Reconciler, *esv1.ExternalSecret, prometheus.Counter) {
  30. r := &Reconciler{recorder: record.NewFakeRecorder(10)}
  31. es := &esv1.ExternalSecret{
  32. ObjectMeta: metav1.ObjectMeta{Name: "es", Namespace: "default"},
  33. }
  34. return r, es, prometheus.NewCounter(prometheus.CounterOpts{Name: "test_sync_errors"})
  35. }
  36. func readyCondition(t *testing.T, es *esv1.ExternalSecret) *esv1.ExternalSecretStatusCondition {
  37. t.Helper()
  38. cond := esv1.GetExternalSecretCondition(es.Status, esv1.ExternalSecretReady)
  39. if cond == nil {
  40. t.Fatal("no Ready condition was set")
  41. }
  42. return cond
  43. }
  44. func TestMarkAsFailedKeepsUnsafeErrorsOutOfCondition(t *testing.T) {
  45. tests := []struct {
  46. name string
  47. err error
  48. }{
  49. {
  50. name: "provider unmarshal error",
  51. err: fmt.Errorf("json: cannot unmarshal number %s into Go value of type float64", secretValue),
  52. },
  53. {
  54. name: "template render error",
  55. err: fmt.Errorf(errApplyTemplate, fmt.Errorf("executing template: bad value %s", secretValue)),
  56. },
  57. {
  58. name: "provider error wrapping a marked error",
  59. err: fmt.Errorf("provider said %s: %w", secretValue, ctrlutil.Safe(errors.New("connection refused"))),
  60. },
  61. }
  62. for _, tt := range tests {
  63. t.Run(tt.name, func(t *testing.T) {
  64. r, es, counter := markAsFailedFixture()
  65. r.markAsFailed(msgErrorGetSecretData, tt.err, es, counter, esv1.ConditionReasonSecretSyncedError)
  66. cond := readyCondition(t, es)
  67. if strings.Contains(cond.Message, secretValue) {
  68. t.Fatalf("condition message leaked provider text: %q", cond.Message)
  69. }
  70. if cond.Status != v1.ConditionFalse {
  71. t.Errorf("status = %v, want False", cond.Status)
  72. }
  73. })
  74. }
  75. }
  76. func TestMarkAsFailedDetailsSafeErrors(t *testing.T) {
  77. r, es, counter := markAsFailedFixture()
  78. err := ctrlutil.Safe(fmt.Errorf(errUpdate, "my-secret", ErrSecretImmutable))
  79. r.markAsFailed(msgErrorUpdateImmutable, err, es, counter, esv1.ConditionReasonSecretImmutable)
  80. cond := readyCondition(t, es)
  81. if cond.Reason != esv1.ConditionReasonSecretImmutable {
  82. t.Errorf("reason = %q, want %q", cond.Reason, esv1.ConditionReasonSecretImmutable)
  83. }
  84. if !strings.HasPrefix(cond.Message, msgErrorUpdateImmutable) {
  85. t.Errorf("message %q lost the base text", cond.Message)
  86. }
  87. if !strings.Contains(cond.Message, "my-secret") {
  88. t.Errorf("message %q dropped the safe detail", cond.Message)
  89. }
  90. }
  91. // A long safe error must not grow the condition message without bound: there is
  92. // no maxLength on the Message field in the CRDs.
  93. func TestMarkAsFailedCapsMessageLength(t *testing.T) {
  94. r, es, counter := markAsFailedFixture()
  95. err := ctrlutil.Safe(errors.New(strings.Repeat("a", 4096)))
  96. r.markAsFailed(msgErrorUpdateSecret, err, es, counter, esv1.ConditionReasonSecretSyncedError)
  97. cond := readyCondition(t, es)
  98. limit := len(msgErrorUpdateSecret) + len(": ") + ctrlutil.MaxConditionMessageLength
  99. if len(cond.Message) > limit {
  100. t.Errorf("message length = %d, want at most %d", len(cond.Message), limit)
  101. }
  102. }