kube_test.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. //go:build linux
  4. package main
  5. import (
  6. "context"
  7. "errors"
  8. "testing"
  9. "time"
  10. "github.com/google/go-cmp/cmp"
  11. "tailscale.com/ipn"
  12. "tailscale.com/kube/kubeapi"
  13. "tailscale.com/kube/kubeclient"
  14. )
  15. func TestSetupKube(t *testing.T) {
  16. tests := []struct {
  17. name string
  18. cfg *settings
  19. wantErr bool
  20. wantCfg *settings
  21. kc *kubeClient
  22. }{
  23. {
  24. name: "TS_AUTHKEY set, state Secret exists",
  25. cfg: &settings{
  26. AuthKey: "foo",
  27. KubeSecret: "foo",
  28. },
  29. kc: &kubeClient{stateSecret: "foo", Client: &kubeclient.FakeClient{
  30. CheckSecretPermissionsImpl: func(context.Context, string) (bool, bool, error) {
  31. return false, false, nil
  32. },
  33. GetSecretImpl: func(context.Context, string) (*kubeapi.Secret, error) {
  34. return nil, nil
  35. },
  36. }},
  37. wantCfg: &settings{
  38. AuthKey: "foo",
  39. KubeSecret: "foo",
  40. },
  41. },
  42. {
  43. name: "TS_AUTHKEY set, state Secret does not exist, we have permissions to create it",
  44. cfg: &settings{
  45. AuthKey: "foo",
  46. KubeSecret: "foo",
  47. },
  48. kc: &kubeClient{stateSecret: "foo", Client: &kubeclient.FakeClient{
  49. CheckSecretPermissionsImpl: func(context.Context, string) (bool, bool, error) {
  50. return false, true, nil
  51. },
  52. GetSecretImpl: func(context.Context, string) (*kubeapi.Secret, error) {
  53. return nil, &kubeapi.Status{Code: 404}
  54. },
  55. }},
  56. wantCfg: &settings{
  57. AuthKey: "foo",
  58. KubeSecret: "foo",
  59. },
  60. },
  61. {
  62. name: "TS_AUTHKEY set, state Secret does not exist, we do not have permissions to create it",
  63. cfg: &settings{
  64. AuthKey: "foo",
  65. KubeSecret: "foo",
  66. },
  67. kc: &kubeClient{stateSecret: "foo", Client: &kubeclient.FakeClient{
  68. CheckSecretPermissionsImpl: func(context.Context, string) (bool, bool, error) {
  69. return false, false, nil
  70. },
  71. GetSecretImpl: func(context.Context, string) (*kubeapi.Secret, error) {
  72. return nil, &kubeapi.Status{Code: 404}
  73. },
  74. }},
  75. wantCfg: &settings{
  76. AuthKey: "foo",
  77. KubeSecret: "foo",
  78. },
  79. wantErr: true,
  80. },
  81. {
  82. name: "TS_AUTHKEY set, we encounter a non-404 error when trying to retrieve the state Secret",
  83. cfg: &settings{
  84. AuthKey: "foo",
  85. KubeSecret: "foo",
  86. },
  87. kc: &kubeClient{stateSecret: "foo", Client: &kubeclient.FakeClient{
  88. CheckSecretPermissionsImpl: func(context.Context, string) (bool, bool, error) {
  89. return false, false, nil
  90. },
  91. GetSecretImpl: func(context.Context, string) (*kubeapi.Secret, error) {
  92. return nil, &kubeapi.Status{Code: 403}
  93. },
  94. }},
  95. wantCfg: &settings{
  96. AuthKey: "foo",
  97. KubeSecret: "foo",
  98. },
  99. wantErr: true,
  100. },
  101. {
  102. name: "TS_AUTHKEY set, we encounter a non-404 error when trying to check Secret permissions",
  103. cfg: &settings{
  104. AuthKey: "foo",
  105. KubeSecret: "foo",
  106. },
  107. wantCfg: &settings{
  108. AuthKey: "foo",
  109. KubeSecret: "foo",
  110. },
  111. kc: &kubeClient{stateSecret: "foo", Client: &kubeclient.FakeClient{
  112. CheckSecretPermissionsImpl: func(context.Context, string) (bool, bool, error) {
  113. return false, false, errors.New("broken")
  114. },
  115. }},
  116. wantErr: true,
  117. },
  118. {
  119. // Interactive login using URL in Pod logs
  120. name: "TS_AUTHKEY not set, state Secret does not exist, we have permissions to create it",
  121. cfg: &settings{
  122. KubeSecret: "foo",
  123. },
  124. wantCfg: &settings{
  125. KubeSecret: "foo",
  126. },
  127. kc: &kubeClient{stateSecret: "foo", Client: &kubeclient.FakeClient{
  128. CheckSecretPermissionsImpl: func(context.Context, string) (bool, bool, error) {
  129. return false, true, nil
  130. },
  131. GetSecretImpl: func(context.Context, string) (*kubeapi.Secret, error) {
  132. return nil, &kubeapi.Status{Code: 404}
  133. },
  134. }},
  135. },
  136. {
  137. // Interactive login using URL in Pod logs
  138. name: "TS_AUTHKEY not set, state Secret exists, but does not contain auth key",
  139. cfg: &settings{
  140. KubeSecret: "foo",
  141. },
  142. wantCfg: &settings{
  143. KubeSecret: "foo",
  144. },
  145. kc: &kubeClient{stateSecret: "foo", Client: &kubeclient.FakeClient{
  146. CheckSecretPermissionsImpl: func(context.Context, string) (bool, bool, error) {
  147. return false, false, nil
  148. },
  149. GetSecretImpl: func(context.Context, string) (*kubeapi.Secret, error) {
  150. return &kubeapi.Secret{}, nil
  151. },
  152. }},
  153. },
  154. {
  155. name: "TS_AUTHKEY not set, state Secret contains auth key, we do not have RBAC to patch it",
  156. cfg: &settings{
  157. KubeSecret: "foo",
  158. },
  159. kc: &kubeClient{stateSecret: "foo", Client: &kubeclient.FakeClient{
  160. CheckSecretPermissionsImpl: func(context.Context, string) (bool, bool, error) {
  161. return false, false, nil
  162. },
  163. GetSecretImpl: func(context.Context, string) (*kubeapi.Secret, error) {
  164. return &kubeapi.Secret{Data: map[string][]byte{"authkey": []byte("foo")}}, nil
  165. },
  166. }},
  167. wantCfg: &settings{
  168. KubeSecret: "foo",
  169. },
  170. wantErr: true,
  171. },
  172. {
  173. name: "TS_AUTHKEY not set, state Secret contains auth key, we have RBAC to patch it",
  174. cfg: &settings{
  175. KubeSecret: "foo",
  176. },
  177. kc: &kubeClient{stateSecret: "foo", Client: &kubeclient.FakeClient{
  178. CheckSecretPermissionsImpl: func(context.Context, string) (bool, bool, error) {
  179. return true, false, nil
  180. },
  181. GetSecretImpl: func(context.Context, string) (*kubeapi.Secret, error) {
  182. return &kubeapi.Secret{Data: map[string][]byte{"authkey": []byte("foo")}}, nil
  183. },
  184. }},
  185. wantCfg: &settings{
  186. KubeSecret: "foo",
  187. AuthKey: "foo",
  188. KubernetesCanPatch: true,
  189. },
  190. },
  191. }
  192. for _, tt := range tests {
  193. kc := tt.kc
  194. t.Run(tt.name, func(t *testing.T) {
  195. if err := tt.cfg.setupKube(context.Background(), kc); (err != nil) != tt.wantErr {
  196. t.Errorf("settings.setupKube() error = %v, wantErr %v", err, tt.wantErr)
  197. }
  198. if diff := cmp.Diff(*tt.cfg, *tt.wantCfg); diff != "" {
  199. t.Errorf("unexpected contents of settings after running settings.setupKube()\n(-got +want):\n%s", diff)
  200. }
  201. })
  202. }
  203. }
  204. func TestWaitForConsistentState(t *testing.T) {
  205. data := map[string][]byte{
  206. // Missing _current-profile.
  207. string(ipn.KnownProfilesStateKey): []byte(""),
  208. string(ipn.MachineKeyStateKey): []byte(""),
  209. "profile-foo": []byte(""),
  210. }
  211. kc := &kubeClient{
  212. Client: &kubeclient.FakeClient{
  213. GetSecretImpl: func(context.Context, string) (*kubeapi.Secret, error) {
  214. return &kubeapi.Secret{
  215. Data: data,
  216. }, nil
  217. },
  218. },
  219. }
  220. ctx, cancel := context.WithTimeout(context.Background(), time.Second)
  221. defer cancel()
  222. if err := kc.waitForConsistentState(ctx); err != context.DeadlineExceeded {
  223. t.Fatalf("expected DeadlineExceeded, got %v", err)
  224. }
  225. ctx, cancel = context.WithTimeout(context.Background(), time.Second)
  226. defer cancel()
  227. data[string(ipn.CurrentProfileStateKey)] = []byte("")
  228. if err := kc.waitForConsistentState(ctx); err != nil {
  229. t.Fatalf("expected nil, got %v", err)
  230. }
  231. }