plugin_test.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /*
  2. Copyright 2017 The Kubernetes 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. http://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 plugin
  14. import (
  15. "bytes"
  16. "fmt"
  17. "io/ioutil"
  18. "os"
  19. "strings"
  20. "testing"
  21. "k8s.io/cli-runtime/pkg/genericclioptions"
  22. )
  23. func TestPluginPathsAreUnaltered(t *testing.T) {
  24. tempDir, err := ioutil.TempDir(os.TempDir(), "test-cmd-plugins")
  25. if err != nil {
  26. t.Fatalf("unexpected error: %v", err)
  27. }
  28. tempDir2, err := ioutil.TempDir(os.TempDir(), "test-cmd-plugins2")
  29. if err != nil {
  30. t.Fatalf("unexpected error: %v", err)
  31. }
  32. // cleanup
  33. defer func() {
  34. if err := os.RemoveAll(tempDir); err != nil {
  35. panic(fmt.Errorf("unexpected cleanup error: %v", err))
  36. }
  37. if err := os.RemoveAll(tempDir2); err != nil {
  38. panic(fmt.Errorf("unexpected cleanup error: %v", err))
  39. }
  40. }()
  41. ioStreams, _, _, errOut := genericclioptions.NewTestIOStreams()
  42. verifier := newFakePluginPathVerifier()
  43. pluginPaths := []string{tempDir, tempDir2}
  44. o := &PluginListOptions{
  45. Verifier: verifier,
  46. IOStreams: ioStreams,
  47. PluginPaths: pluginPaths,
  48. }
  49. // write at least one valid plugin file
  50. if _, err := ioutil.TempFile(tempDir, "kubectl-"); err != nil {
  51. t.Fatalf("unexpected error %v", err)
  52. }
  53. if _, err := ioutil.TempFile(tempDir2, "kubectl-"); err != nil {
  54. t.Fatalf("unexpected error %v", err)
  55. }
  56. if err := o.Run(); err != nil {
  57. t.Fatalf("unexpected error %v - %v", err, errOut.String())
  58. }
  59. // ensure original paths remain unaltered
  60. if len(verifier.seenUnsorted) != len(pluginPaths) {
  61. t.Fatalf("saw unexpected plugin paths. Expecting %v, got %v", pluginPaths, verifier.seenUnsorted)
  62. }
  63. for actual := range verifier.seenUnsorted {
  64. if !strings.HasPrefix(verifier.seenUnsorted[actual], pluginPaths[actual]) {
  65. t.Fatalf("expected PATH slice to be unaltered. Expecting %v, but got %v", pluginPaths[actual], verifier.seenUnsorted[actual])
  66. }
  67. }
  68. }
  69. func TestPluginPathsAreValid(t *testing.T) {
  70. tempDir, err := ioutil.TempDir(os.TempDir(), "test-cmd-plugins")
  71. if err != nil {
  72. t.Fatalf("unexpected error: %v", err)
  73. }
  74. // cleanup
  75. defer func() {
  76. if err := os.RemoveAll(tempDir); err != nil {
  77. panic(fmt.Errorf("unexpected cleanup error: %v", err))
  78. }
  79. }()
  80. tc := []struct {
  81. name string
  82. pluginPaths []string
  83. pluginFile func() (*os.File, error)
  84. verifier *fakePluginPathVerifier
  85. expectVerifyErrors []error
  86. expectErr string
  87. }{
  88. {
  89. name: "ensure no plugins found if no files begin with kubectl- prefix",
  90. pluginPaths: []string{tempDir},
  91. verifier: newFakePluginPathVerifier(),
  92. pluginFile: func() (*os.File, error) {
  93. return ioutil.TempFile(tempDir, "notkubectl-")
  94. },
  95. expectErr: "unable to find any kubectl plugins in your PATH",
  96. },
  97. {
  98. name: "ensure de-duplicated plugin-paths slice",
  99. pluginPaths: []string{tempDir, tempDir},
  100. verifier: newFakePluginPathVerifier(),
  101. pluginFile: func() (*os.File, error) {
  102. return ioutil.TempFile(tempDir, "kubectl-")
  103. },
  104. },
  105. }
  106. for _, test := range tc {
  107. t.Run(test.name, func(t *testing.T) {
  108. ioStreams, _, _, errOut := genericclioptions.NewTestIOStreams()
  109. o := &PluginListOptions{
  110. Verifier: test.verifier,
  111. IOStreams: ioStreams,
  112. PluginPaths: test.pluginPaths,
  113. }
  114. // create files
  115. if test.pluginFile != nil {
  116. if _, err := test.pluginFile(); err != nil {
  117. t.Fatalf("unexpected error creating plugin file: %v", err)
  118. }
  119. }
  120. for _, expected := range test.expectVerifyErrors {
  121. for _, actual := range test.verifier.errors {
  122. if expected != actual {
  123. t.Fatalf("unexpected error: expected %v, but got %v", expected, actual)
  124. }
  125. }
  126. }
  127. err := o.Run()
  128. if err == nil && len(test.expectErr) > 0 {
  129. t.Fatalf("unexpected non-error: expecting %v", test.expectErr)
  130. }
  131. if err != nil && len(test.expectErr) == 0 {
  132. t.Fatalf("unexpected error: %v - %v", err, errOut.String())
  133. }
  134. if err == nil {
  135. return
  136. }
  137. allErrs := bytes.NewBuffer(errOut.Bytes())
  138. if _, err := allErrs.WriteString(err.Error()); err != nil {
  139. t.Fatalf("unexpected error: %v", err)
  140. }
  141. if len(test.expectErr) > 0 {
  142. if !strings.Contains(allErrs.String(), test.expectErr) {
  143. t.Fatalf("unexpected error: expected %v, but got %v", test.expectErr, allErrs.String())
  144. }
  145. }
  146. })
  147. }
  148. }
  149. type duplicatePathError struct {
  150. path string
  151. }
  152. func (d *duplicatePathError) Error() string {
  153. return fmt.Sprintf("path %q already visited", d.path)
  154. }
  155. type fakePluginPathVerifier struct {
  156. errors []error
  157. seen map[string]bool
  158. seenUnsorted []string
  159. }
  160. func (f *fakePluginPathVerifier) Verify(path string) []error {
  161. if f.seen[path] {
  162. err := &duplicatePathError{path}
  163. f.errors = append(f.errors, err)
  164. return []error{err}
  165. }
  166. f.seen[path] = true
  167. f.seenUnsorted = append(f.seenUnsorted, path)
  168. return nil
  169. }
  170. func newFakePluginPathVerifier() *fakePluginPathVerifier {
  171. return &fakePluginPathVerifier{seen: make(map[string]bool)}
  172. }