GitOps for k8s
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

registry_test.go 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. package registry
  2. import (
  3. "fmt"
  4. "testing"
  5. "time"
  6. "github.com/go-kit/kit/log"
  7. "github.com/pkg/errors"
  8. "encoding/base64"
  9. "github.com/docker/distribution/manifest/schema1"
  10. "github.com/weaveworks/flux"
  11. "github.com/weaveworks/flux/registry/middleware"
  12. "io/ioutil"
  13. "os"
  14. )
  15. var (
  16. testTags = []string{testTagStr, "anotherTag"}
  17. mClient = NewMockClient(
  18. func(repository Repository, tag string) (flux.Image, error) {
  19. return img, nil
  20. },
  21. func(repository Repository) ([]string, error) {
  22. return testTags, nil
  23. },
  24. )
  25. )
  26. func TestRegistry_GetRepository(t *testing.T) {
  27. fact := NewMockClientFactory(mClient, nil)
  28. reg := NewRegistry(fact, log.NewNopLogger())
  29. imgs, err := reg.GetRepository(testRepository)
  30. if err != nil {
  31. t.Fatal(err)
  32. }
  33. // Dev note, the tags will look the same because we are returning the same
  34. // Image from the mock remote. But they are distinct images.
  35. if len(testTags) != len(imgs) {
  36. t.Fatal("Expecting %v images, but got %v", len(testTags), len(imgs))
  37. }
  38. }
  39. func TestRegistry_GetRepositoryFactoryError(t *testing.T) {
  40. errFact := NewMockClientFactory(mClient, errors.New(""))
  41. reg := NewRegistry(errFact, nil)
  42. _, err := reg.GetRepository(testRepository)
  43. if err == nil {
  44. t.Fatal("Expecting error")
  45. }
  46. }
  47. func TestRegistry_GetRepositoryManifestError(t *testing.T) {
  48. errClient := NewMockClient(
  49. func(repository Repository, tag string) (flux.Image, error) {
  50. return flux.Image{}, errors.New("")
  51. },
  52. func(repository Repository) ([]string, error) {
  53. return testTags, nil
  54. },
  55. )
  56. errFact := NewMockClientFactory(errClient, nil)
  57. reg := NewRegistry(errFact, log.NewNopLogger())
  58. _, err := reg.GetRepository(testRepository)
  59. if err == nil {
  60. t.Fatal("Expecting error")
  61. }
  62. }
  63. // Note: This actually goes off to docker hub to find the Image.
  64. // It will fail if there is not internet connection
  65. func TestRemoteFactory_RawClient(t *testing.T) {
  66. // No credentials required for public Image
  67. fact := NewRemoteClientFactory(Credentials{}, log.NewNopLogger(), middleware.RateLimiterConfig{
  68. RPS: 200,
  69. Burst: 1,
  70. })
  71. img, err := flux.ParseImage("alpine:latest", time.Time{})
  72. if err != nil {
  73. t.Fatal(err)
  74. }
  75. testRepository = RepositoryFromImage(img)
  76. // Refresh tags first
  77. var tags []string
  78. client, err := fact.ClientFor(testRepository.Host())
  79. if err != nil {
  80. t.Fatal(err)
  81. }
  82. tags, err = client.Tags(testRepository)
  83. if err != nil {
  84. t.Fatal(err)
  85. }
  86. client.Cancel()
  87. if len(tags) == 0 {
  88. t.Fatal("Should have some tags")
  89. }
  90. client, err = fact.ClientFor(testRepository.Host())
  91. if err != nil {
  92. t.Fatal(err)
  93. }
  94. newImg, err := client.Manifest(testRepository, tags[0])
  95. if err != nil {
  96. t.Fatal(err)
  97. }
  98. if newImg.ID.String() == "" {
  99. t.Fatal("Should image ")
  100. }
  101. if newImg.CreatedAt.IsZero() {
  102. t.Fatal("CreatedAt time was 0")
  103. }
  104. client.Cancel()
  105. }
  106. func TestRemoteFactory_InvalidHost(t *testing.T) {
  107. fact := NewRemoteClientFactory(Credentials{}, log.NewNopLogger(), middleware.RateLimiterConfig{})
  108. img, err := flux.ParseImage("invalid.host/library/alpine:latest", time.Time{})
  109. if err != nil {
  110. t.Fatal(err)
  111. }
  112. testRepository = RepositoryFromImage(img)
  113. client, err := fact.ClientFor(testRepository.Host())
  114. if err != nil {
  115. return
  116. }
  117. _, err = client.Manifest(testRepository, img.ID.Tag)
  118. if err == nil {
  119. t.Fatal("Expected error due to invalid host but got none.")
  120. }
  121. }
  122. var (
  123. user string = "user"
  124. pass string = "pass"
  125. host string = "host"
  126. tmpl string = `
  127. {
  128. "auths": {
  129. %q: {"auth": %q}
  130. }
  131. }`
  132. okCreds string = base64.StdEncoding.EncodeToString([]byte(user + ":" + pass))
  133. )
  134. func writeCreds(t *testing.T, creds string) (string, func()) {
  135. file, err := ioutil.TempFile("", "testcreds")
  136. file.Write([]byte(creds))
  137. file.Close()
  138. if err != nil {
  139. t.Fatal(err)
  140. }
  141. return file.Name(), func() {
  142. os.Remove(file.Name())
  143. }
  144. }
  145. func TestRemoteFactory_CredentialsFromFile(t *testing.T) {
  146. file, cleanup := writeCreds(t, fmt.Sprintf(tmpl, host, okCreds))
  147. defer cleanup()
  148. creds, err := CredentialsFromFile(file)
  149. if err != nil {
  150. t.Fatal(err)
  151. }
  152. c := creds.credsFor(host)
  153. if user != c.username {
  154. t.Fatalf("Expected %q, got %q.", user, c.username)
  155. }
  156. if pass != c.password {
  157. t.Fatalf("Expected %q, got %q.", pass, c.password)
  158. }
  159. if len(creds.Hosts()) != 1 || host != creds.Hosts()[0] {
  160. t.Fatalf("Expected %q, got %q.", host, creds.Hosts()[0])
  161. }
  162. }
  163. func TestRemoteFactory_CredentialsFromConfigDecodeError(t *testing.T) {
  164. file, cleanup := writeCreds(t, `{
  165. "auths": {
  166. "host": {"auth": "credentials:notencoded"}
  167. }
  168. }`)
  169. defer cleanup()
  170. _, err := CredentialsFromFile(file)
  171. if err == nil {
  172. t.Fatal("Expected error")
  173. }
  174. }
  175. func TestRemoteFactory_CredentialsFromConfigHTTPSHosts(t *testing.T) {
  176. httpsHost := fmt.Sprintf("https://%s/v1/", host)
  177. file, cleanup := writeCreds(t, fmt.Sprintf(tmpl, httpsHost, okCreds))
  178. defer cleanup()
  179. creds, err := CredentialsFromFile(file)
  180. if err != nil {
  181. t.Fatal(err)
  182. }
  183. c := creds.credsFor(host)
  184. if user != c.username {
  185. t.Fatalf("Expected %q, got %q.", user, c.username)
  186. }
  187. if pass != c.password {
  188. t.Fatalf("Expected %q, got %q.", pass, c.password)
  189. }
  190. if len(creds.Hosts()) != 1 || host != creds.Hosts()[0] {
  191. t.Fatalf("Expected %q, got %q.", httpsHost, creds.Hosts()[0])
  192. }
  193. }
  194. func TestRemoteFactory_ParseHost(t *testing.T) {
  195. for _, v := range []struct {
  196. host string
  197. imagePrefix string
  198. error bool
  199. }{
  200. {
  201. host: "host",
  202. imagePrefix: "host",
  203. },
  204. {
  205. host: "gcr.io",
  206. imagePrefix: "gcr.io",
  207. },
  208. {
  209. host: "https://gcr.io",
  210. imagePrefix: "gcr.io",
  211. },
  212. {
  213. host: "https://gcr.io/v1",
  214. imagePrefix: "gcr.io",
  215. },
  216. {
  217. host: "https://gcr.io/v1/",
  218. imagePrefix: "gcr.io",
  219. },
  220. {
  221. host: "gcr.io/v1",
  222. imagePrefix: "gcr.io",
  223. },
  224. {
  225. host: "telnet://gcr.io/v1",
  226. imagePrefix: "gcr.io",
  227. },
  228. {
  229. host: "",
  230. imagePrefix: "gcr.io",
  231. error: true,
  232. },
  233. {
  234. host: "https://",
  235. imagePrefix: "gcr.io",
  236. error: true,
  237. },
  238. {
  239. host: "^#invalid.io/v1/",
  240. imagePrefix: "gcr.io",
  241. error: true,
  242. },
  243. {
  244. host: "/var/user",
  245. imagePrefix: "gcr.io",
  246. error: true,
  247. },
  248. } {
  249. file, cleanup := writeCreds(t, fmt.Sprintf(tmpl, v.host, okCreds))
  250. defer cleanup()
  251. creds, err := CredentialsFromFile(file)
  252. if (err != nil) != v.error {
  253. t.Fatalf("For test %q, expected error = %v but got %v", v.host, v.error, err != nil)
  254. }
  255. if v.error {
  256. continue
  257. }
  258. if u := creds.credsFor(v.imagePrefix).username; u != user {
  259. t.Fatalf("For test %q, expected %q but got %v", v.host, user, u)
  260. }
  261. }
  262. }
  263. const testTagStr = "tag"
  264. const testImageStr = "index.docker.io/test/Image:" + testTagStr
  265. const constTime = "2017-01-13T16:22:58.009923189Z"
  266. var (
  267. img, _ = flux.ParseImage(testImageStr, time.Time{})
  268. testRepository = RepositoryFromImage(img)
  269. man = schema1.SignedManifest{
  270. Manifest: schema1.Manifest{
  271. History: []schema1.History{
  272. {
  273. V1Compatibility: `{"created":"` + constTime + `"}`,
  274. },
  275. },
  276. },
  277. }
  278. )
  279. func TestRepository_ParseImage(t *testing.T) {
  280. for _, x := range []struct {
  281. test string
  282. expected string
  283. }{
  284. {"alpine", "index.docker.io/library/alpine"},
  285. {"library/alpine", "index.docker.io/library/alpine"},
  286. {"alpine:mytag", "index.docker.io/library/alpine"},
  287. {"quay.io/library/alpine", "quay.io/library/alpine"},
  288. {"quay.io/library/alpine:latest", "quay.io/library/alpine"},
  289. {"quay.io/library/alpine:mytag", "quay.io/library/alpine"},
  290. } {
  291. i, err := ParseRepository(x.test)
  292. if err != nil {
  293. t.Fatalf("Failed parsing %q, expected %q", x.test, x.expected)
  294. }
  295. if i.String() != x.expected {
  296. t.Fatalf("%q does not match expected %q", i.String(), x.expected)
  297. }
  298. }
  299. }