diff --git a/go.mod b/go.mod index 5cc5039983a..bf2cc2afe9e 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( k8s.io/client-go v0.18.0 k8s.io/klog v1.0.0 rsc.io/letsencrypt v0.0.3 // indirect + sigs.k8s.io/yaml v1.2.0 ) replace ( diff --git a/go.sum b/go.sum index 441903820af..175d089f8b1 100644 --- a/go.sum +++ b/go.sum @@ -3,7 +3,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= @@ -15,7 +14,6 @@ github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6L github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1MRDJM= github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= @@ -28,9 +26,7 @@ github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZ github.com/Masterminds/squirrel v1.2.0 h1:K1NhbTO21BWG47IVR0OnIZuE0LZcXAYqywrC3Ko53KI= github.com/Masterminds/squirrel v1.2.0/go.mod h1:yaPeOnPG5ZRwL9oKdTsO/prlkPbXWZlRVMQ/gGlzIuA= github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/hcsshim v0.8.7 h1:ptnOoufxGSzauVTsdE+wMYnCWA301PdoN4xg5oRdZpg= github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -41,7 +37,6 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -59,44 +54,34 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/bshuster-repo/logrus-logstash-hook v0.4.1 h1:pgAtgj+A31JBVtEHu2uHuEx0n+2ukqUJnS2vVe5pQNA= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f h1:tSNMc+rJDfmYntojat8lljbt1mgKNpTxUZJsSzJ9Y1s= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.2 h1:ForxmXkA6tPIvffbrDAcPUIB32QgXkt2XFj+F0UxetA= github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41 h1:kIFnQBO7rQ0XkMe6xEwbybYHBEaWmh/f++laI6Emt7M= github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/dex v2.3.0+incompatible h1:qm4sUMHVz/Kf2ZAwcfJhpd91r8M+3sI917y2GJ5cTgo= github.com/coreos/dex v2.3.0+incompatible/go.mod h1:eWiVFa+I1sIRiwXi4bJMZvd90+H7EhRm/J1Y6Y18AOM= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -138,13 +123,11 @@ github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= @@ -159,9 +142,7 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZM github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 h1:LofdAjjjqCSXMwLGgOgnE+rdPuvX9DxCqaHwKy7i/ko= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= @@ -221,17 +202,12 @@ github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+ github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.7.1 h1:OQl5ys5MBea7OGCdvPbBJWRgnhC/fGona6QKfvFeau8= github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= -github.com/gobuffalo/logger v1.0.1 h1:ZEgyRGgAm4ZAhAO45YXMs5Fp+bzGLESFewzAVBMKuTg= github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= -github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4= github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= -github.com/gobuffalo/packr/v2 v2.7.1 h1:n3CIW5T17T8v4GGK5sWXLVWJhCz7b5aNLSxW6gYim4o= github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= @@ -242,10 +218,8 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -278,16 +252,13 @@ github.com/googleapis/gnostic v0.1.0 h1:rVsPeBmXbYv4If/cumu1AzZPwV58q433hvONV1UE github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33 h1:893HsJqtxp9z1SF76gg6hY70hRY1wVlTSnC/h1yUDCo= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= -github.com/graph-gophers/graphql-go v0.0.0-20200309224638-dae41bde9ef9 h1:kLnsdud6Fl1/7ZX/5oD23cqYAzBfuZBhNkGr2NvuEsU= github.com/graph-gophers/graphql-go v0.0.0-20200309224638-dae41bde9ef9/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= @@ -304,20 +275,17 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -331,14 +299,11 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= @@ -370,7 +335,6 @@ github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/ github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.12.0 h1:u/x3mp++qUxvYfulZ4HKOvVO0JWhk7HtE8lWhbGz/Do= github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -381,7 +345,6 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f h1:2+myh5ml7lgEU/51gbeLHfKGNfgEQQIWrlbdaOsidbQ= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -407,10 +370,8 @@ github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2f github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -424,24 +385,20 @@ github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJ github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU= github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -466,13 +423,11 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rawagner/graphql-transport-ws v0.0.0-20200506141829-b08019af7525 h1:R5nr9yWTJQRCpHPcS7FgcVK9sFtoub5o5XFVHqhPsac= github.com/rawagner/graphql-transport-ws v0.0.0-20200506141829-b08019af7525/go.mod h1:FqBcj08OiZxzD2yyPAMfCYWU2NDfmgffW2MXk0uXtUI= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.4.0 h1:LUa41nrWTQNGhzdsZ5lTnkwbNjj6rXTdazA1cSdjkOY= github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rubenv/sql-migrate v0.0.0-20200212082348-64f95ea68aa3 h1:xkBtI5JktwbW/vf4vopBbhYsRFTGfQWHYXzC0/qYwxI= github.com/rubenv/sql-migrate v0.0.0-20200212082348-64f95ea68aa3/go.mod h1:rtQlpHw+eR6UrqaS3kX1VYeaCxzCVdimDS7g5Ln4pPc= @@ -516,7 +471,6 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= @@ -537,13 +491,9 @@ github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4m github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= -github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -552,7 +502,6 @@ go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qL go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -658,14 +607,12 @@ golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -687,11 +634,9 @@ gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/gorp.v1 v1.7.2 h1:j3DWlAyGVv8whO7AcIWznQ2Yj7yJkn34B8s63GViAAw= @@ -701,9 +646,7 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y= gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -712,7 +655,6 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= helm.sh/helm/v3 v3.2.1 h1:nLkUyZ5NWe8lYwyKbO5ZlqjwIm/nf35k8ab5uRxCwBY= helm.sh/helm/v3 v3.2.1/go.mod h1:ZaXz/vzktgwjyGGFbUWtIQkscfE7WYoRGP2szqAFHR0= @@ -747,7 +689,6 @@ k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/metrics v0.18.0/go.mod h1:8aYTW18koXqjLVKL7Ds05RPMX9ipJZI3mywYvBOxXd4= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -rsc.io/letsencrypt v0.0.3 h1:H7xDfhkaFFSYEJlKeq38RwX2jYcnTeHuDQyT+mMNMwM= rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= diff --git a/pkg/helm/actions/fake/helmcrconfigs.go b/pkg/helm/actions/fake/helmcrconfigs.go new file mode 100644 index 00000000000..d5a4d370f3d --- /dev/null +++ b/pkg/helm/actions/fake/helmcrconfigs.go @@ -0,0 +1,55 @@ +package fake + +import ( + "fmt" + "net/http" + "net/http/httptest" + "strconv" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/dynamic/fake" +) + +func newUnstructured(apiVersion, kind, namespace, name string) *unstructured.Unstructured { + return &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": apiVersion, + "kind": kind, + "metadata": map[string]interface{}{ + "namespace": namespace, + "name": name, + }, + }, + } +} + +func fakeHelmCR(fakeIndexFile string, name string) *unstructured.Unstructured { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/yaml") + fmt.Fprintln(w, fakeIndexFile) + })) + sampleRepoCR := newUnstructured("helm.openshift.io/v1beta1", "HelmChartRepository", "", name) + connectionConfig := map[string]interface{}{ + "url": ts.URL, + } + sampleRepoCR.Object["spec"] = map[string]interface{}{ + "connectionConfig": connectionConfig, + "name": name, + } + return sampleRepoCR +} + +func SetupClusterWithHelmCrs(indexFiles []string) dynamic.Interface { + scheme := runtime.NewScheme() + var objs []runtime.Object + + for i, indexFile := range indexFiles { + fakeCr := fakeHelmCR(indexFile, "sample-repo-"+strconv.Itoa(i+1)) + objs = append(objs, fakeCr) + } + + client := fake.NewSimpleDynamicClient(scheme, objs...) + return client +} diff --git a/pkg/helm/actions/get_dynamic_client.go b/pkg/helm/actions/get_dynamic_client.go new file mode 100644 index 00000000000..1d187cbe689 --- /dev/null +++ b/pkg/helm/actions/get_dynamic_client.go @@ -0,0 +1,14 @@ +package actions + +import ( + "k8s.io/client-go/dynamic" + "k8s.io/client-go/rest" +) + +func DynamicClient(conf *rest.Config) (dynamic.Interface, error) { + client, err := dynamic.NewForConfig(conf) + if err != nil { + return nil, err + } + return client, nil +} diff --git a/pkg/helm/actions/get_repos.go b/pkg/helm/actions/get_repos.go new file mode 100644 index 00000000000..f466ad099a3 --- /dev/null +++ b/pkg/helm/actions/get_repos.go @@ -0,0 +1,274 @@ +package actions + +import ( + "context" + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "io/ioutil" + "net/http" + "strings" + + "helm.sh/helm/v3/pkg/repo" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" + corev1 "k8s.io/client-go/kubernetes/typed/core/v1" + "sigs.k8s.io/yaml" + + "github.com/openshift/console/pkg/crypto" +) + +var ( + helmChartRepositoryGVK = schema.GroupVersionResource{ + Group: "helm.openshift.io", + Version: "v1beta1", + Resource: "helmchartrepositories", + } + + defaultRepo = unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "helm.openshift.io/v1beta1", + "kind": "HelmChartRepository", + "metadata": map[string]interface{}{ + "namespace": "", + "name": "redhat-helm-chart", + }, + "spec": map[string]interface{}{ + "connectionConfig": map[string]interface{}{ + "url": "https://redhat-developer.github.io/redhat-helm-charts", + }, + }, + }, + } +) + +const ( + configNamespace = "openshift-config" +) + +type TLSConfigGetter interface { + Get() (*tls.Config, error) +} + +type HelmConfigGetter struct { + Client dynamic.Interface + CoreClient corev1.CoreV1Interface + + DefaultRepoCACertificate []byte +} + +func (b HelmConfigGetter) IndexFiles(helmConfigs []*HelmConfig) []*repo.IndexFile { + var indexFiles []*repo.IndexFile + for _, helmConfig := range helmConfigs { + indexFile, err := b.IndexFile(helmConfig) + if err != nil { + plog.Errorf("Failed to load helm IndexFile %s", err.Error()) + } + indexFiles = append(indexFiles, indexFile) + } + return indexFiles +} + +func (b HelmConfigGetter) parseHelmConfig(repo unstructured.Unstructured) (*HelmConfig, error) { + h := &HelmConfig{} + url, _, err := unstructured.NestedString(repo.Object, "spec", "connectionConfig", "url") + if err != nil { + return nil, err + } + + name, _, err := unstructured.NestedString(repo.Object, "metadata", "name") + if err != nil { + return nil, err + } + + caReference, _, err := unstructured.NestedString(repo.Object, "spec", "connectionConfig", "ca", "name") + if err != nil { + return nil, err + } + + tlsReference, _, err := unstructured.NestedString(repo.Object, "spec", "connectionConfig", "tlsconfig", "name") + if err != nil { + return nil, err + } + + h.Name = name + if caReference != "" { + h.CACert, err = b.ConfigMapValue(caReference, "ca-bundle.crt") + if err != nil { + return nil, err + } + } + if tlsReference != "" { + h.TLSCert, err = b.SecretValue(tlsReference, "tls.crt") + if err != nil { + return nil, err + } + h.TLSKey, err = b.SecretValue(tlsReference, "tls.key") + if err != nil { + return nil, err + } + } + h.Url = url + return h, nil +} + +func (b HelmConfigGetter) MergeIndexFiles(files ...*repo.IndexFile) *repo.IndexFile { + indexFile := repo.NewIndexFile() + for _, file := range files { + for key, entry := range file.Entries { + indexFile.Entries[key] = entry + } + } + return indexFile +} + +func (b HelmConfigGetter) ConfigMapValue(name string, dataField string) ([]byte, error) { + configMap, err := b.CoreClient.ConfigMaps(configNamespace).Get(context.TODO(), name, v1.GetOptions{}) + if err != nil { + return nil, errors.New(fmt.Sprintf("Failed to find %s key in configmap %s", dataField, name)) + } + if val, ok := configMap.Data[dataField]; ok { + return []byte(val), nil + } + return nil, errors.New(fmt.Sprintf("Failed to find %s key in configmap %s", dataField, name)) +} + +func (b HelmConfigGetter) SecretValue(name string, dataField string) ([]byte, error) { + secret, err := b.CoreClient.Secrets(configNamespace).Get(context.TODO(), name, v1.GetOptions{}) + if err != nil { + return nil, errors.New(fmt.Sprintf("Failed to find %s key in secret %s", dataField, name)) + + } + if val, ok := secret.Data[dataField]; ok { + return val, nil + } + return nil, errors.New(fmt.Sprintf("Failed to find %s key in secret %s", dataField, name)) +} + +type HelmConfig struct { + Name string + Url string + CACert []byte + TLSCert []byte + TLSKey []byte +} + +func (b HelmConfigGetter) Get(caCert, tlsCert, tlsKey []byte) (*tls.Config, error) { + mTLSConfig := &tls.Config{ + CipherSuites: crypto.DefaultCiphers(), + } + var rootCAs *x509.CertPool + var err error + + if caCert != nil { + rootCAs = x509.NewCertPool() + if ok := rootCAs.AppendCertsFromPEM(caCert); !ok { + return nil, errors.New("Failed to append caCert") + } + } else { + rootCAs, err = x509.SystemCertPool() + if err != nil { + return nil, err + } + } + mTLSConfig.ClientCAs = rootCAs + + if tlsKey != nil && tlsCert != nil { + cert, err := tls.X509KeyPair(tlsCert, tlsKey) + if err != nil { + return nil, err + } + mTLSConfig.Certificates = []tls.Certificate{cert} + } + return mTLSConfig, nil +} + +func (b HelmConfigGetter) HttpClient(helmConfig *HelmConfig) (*http.Client, error) { + mTLSConfig, err := b.Get(helmConfig.CACert, helmConfig.TLSCert, helmConfig.TLSKey) + if err != nil { + return nil, err + } + tr := &http.Transport{ + TLSClientConfig: mTLSConfig, + } + + client := &http.Client{Transport: tr} + return client, nil +} + +func (b HelmConfigGetter) IndexFile(helmConfig *HelmConfig) (*repo.IndexFile, error) { + plog.Println("HelmConfig", helmConfig) + var indexFile repo.IndexFile + httpClient, err := b.HttpClient(helmConfig) + if err != nil { + return nil, err + } + if !strings.HasSuffix(helmConfig.Url, "/index.yaml") { + helmConfig.Url += "/index.yaml" + } + resp, err := httpClient.Get(helmConfig.Url) + if err != nil { + return nil, err + } + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + err = yaml.Unmarshal(body, &indexFile) + if err != nil { + return nil, err + } + return &indexFile, nil +} + +func (b *HelmConfigGetter) List() ([]*HelmConfig, error) { + var helmConfigs []*HelmConfig + repos, err := b.Client.Resource(helmChartRepositoryGVK).List(context.TODO(), v1.ListOptions{}) + if err != nil || len(repos.Items) == 0 { + // In case no HelmRepoCRs configured, use default redhat helm chart repo + helmConfig, err := b.parseHelmConfig(defaultRepo) + if err != nil { + return helmConfigs, nil + } + helmConfig.CACert = b.DefaultRepoCACertificate + helmConfigs = append(helmConfigs, helmConfig) + return helmConfigs, nil + } + for _, item := range repos.Items { + helmConfig, err := b.parseHelmConfig(item) + if err != nil { + return nil, err + } + helmConfigs = append(helmConfigs, helmConfig) + } + return helmConfigs, nil +} + +func newHelmConfigGetter(client dynamic.Interface, corev1Client corev1.CoreV1Interface, defaultRepoCACerts []byte) (*HelmConfigGetter, error) { + helmConfigGetter := &HelmConfigGetter{ + Client: client, + CoreClient: corev1Client, + DefaultRepoCACertificate: defaultRepoCACerts, + } + return helmConfigGetter, nil +} + +func FetchIndexFile(client dynamic.Interface, corev1Client corev1.CoreV1Interface, defaultRepoCACerts []byte) (*repo.IndexFile, error) { + helmConfigBuilder, err := newHelmConfigGetter(client, corev1Client, defaultRepoCACerts) + if err != nil { + return nil, err + } + + helmConfigs, err := helmConfigBuilder.List() + if err != nil { + return nil, err + } + + indexFiles := helmConfigBuilder.IndexFiles(helmConfigs) + indexFile := helmConfigBuilder.MergeIndexFiles(indexFiles...) + + return indexFile, nil +} diff --git a/pkg/helm/actions/get_repos_test.go b/pkg/helm/actions/get_repos_test.go new file mode 100644 index 00000000000..5d7dea3f7ef --- /dev/null +++ b/pkg/helm/actions/get_repos_test.go @@ -0,0 +1,464 @@ +package actions + +import ( + "fmt" + "net/http" + "net/http/httptest" + "strconv" + "testing" + + "github.com/openshift/console/pkg/helm/actions/fake" + "github.com/openshift/console/pkg/helm/testdata" +) + +var sampleRepoYaml = ` +apiVersion: v1 +entries: + ibm-cpq-prod: + - apiVersion: v2 + appVersion: 10.0.0.6 + created: "2020-07-15T16:32:34.607411698+05:30" + description: |- + IBM Sterling Configure, Price, Quote (CPQ) solution enables you to quickly configure, price, quote and order the right products and services.\n + To sell competitively in today’s multichannel environment, organizations need a way to easily manage product and service configuration and pricing rules that allow prospects, customers, sales staff, call center representatives and Business Partners to quickly find, configure and order the right products and services.\n + IBM® Sterling Configure, Price, Quote solution automates every step of the configure, price and quote process to help organizations easily create Web storefronts, offer dynamic catalog and pricing information, and direct customers and partners to find, configure and order the right products and services.\n + This enables you to transform how you sell complex products and services by removing the internal complexity of multi-tiered selling within your organization and with your partners.\n + + Documentation\n + Additional information about installation can be found at https://ibm.biz/BdqqUS. + + License\n + Sterling Configure Price Quote Software is licensed under https://ibm.biz/Bdqqgd which must be accepted during the install of the Product. + digest: 2d93c570ff47b27858901dacafc22fe64d868e9a8ecb4742867396f2912eded8 + icon: https://raw.githubusercontent.com/IBM/charts/master/logo/ibm-oms-logo.png + keywords: + - cpq + - ifs + - sterling + - yantra + - order + - fulfillment + - om + - amd64 + - framework + - Commercial + - RHOCP + - All items + - Languages + - Databases + - Middleware + - CI/CD + - Other + kubeVersion: '>=1.11.0' + maintainers: + - name: IBM + name: ibm-cpq-prod + response: + - https://redhat-developer.github.com/redhat-helm-charts/charts/ibm-cpq-prod-2.0.0.tgz + version: 2.0.0 + ibm-object-storage-plugin: + - apiVersion: v2 + appVersion: 2.0.0 + created: "2020-07-15T16:32:34.612556197+05:30" + description: | + Chart for deploying ibmcloud object storage plugin. + IBM Cloud Object Storage is persistent, highly available storage + that you can mount to apps that run in a Kubernetes cluster by using + the IBM Cloud Object Storage plug-in. The plug-in is a Kubernetes Flex-Volume plug-in + that connects Cloud Object Storage buckets to pods in your cluster. + + Documentation + For additional details regarding install parameters see here + ibm.biz/BdqPR6 + + License + By installing this product you accept the license terms. + https://www.apache.org/licenses/LICENSE-2.0 + digest: 469e5e4093386c9b2a2f50cf55197db1c2489832bd3bc72018a17f0df9553474 + icon: https://cache.globalcatalog.cloud.ibm.com/static/cache/2461-acb0d10a1725d783/images/uploaded/icons/object-storage.png + keywords: + - IKS + - amd64 + - Storage + - Commercial + - Limited + - Other + - ROKS + maintainers: + - email: contsto2@in.ibm.com + name: IBM + name: ibm-object-storage-plugin + response: + - https://redhat-developer.github.com/redhat-helm-charts/charts/ibm-object-storage-plugin-2.0.0.tgz + version: 2.0.0 + ibm-oms-ent-prod: + - apiVersion: v2 + appVersion: 10.0.0 + created: "2020-07-15T16:32:34.663118186+05:30" + dependencies: + - alias: sch + name: ibm-sch + repository: '@sch' + version: 1.2.15 + description: |- + IBM Order management Software Enterprise Edition v10 provides cross-channel order orchestration capabilities to enable intelligent brokering of orders across many disparate systems. It also provides a global view of inventory across the supply chain and enables business users to make changes to order process. + + For additional details regarding install parameters check: http://ibm.biz/oms-helm-readme. + + By installing this product you accept the license terms http://ibm.biz/oms-license and http://ibm.biz/oms-apps-license. + digest: f01226a974704ea84685d95ad48feae8666ea154254cdca888859430d5517961 + icon: https://raw.githubusercontent.com/IBM/charts/master/logo/ibm-oms-logo.png + keywords: + - oms + - sterling + - yantra + - order + - fulfillment + - om + - amd64 + - ppc64le + - framework + - Commercial + - RHOCP + - Other + kubeVersion: '>=1.11.0' + maintainers: + - name: IBM + name: ibm-oms-ent-prod + type: application + response: + - https://redhat-developer.github.com/redhat-helm-charts/charts/ibm-oms-ent-prod-5.0.0.tgz + version: 5.0.0 + ibm-oms-pro-prod: + - apiVersion: v2 + appVersion: 10.0.0 + created: "2020-07-15T16:32:34.695882333+05:30" + dependencies: + - alias: sch + name: ibm-sch + repository: '@sch' + version: 1.2.15 + description: |- + IBM Order management Software Professional Edition v10 provides cross-channel order orchestration capabilities to enable intelligent brokering of orders across many disparate systems. It also provides a global view of inventory across the supply chain and enables business users to make changes to order process. + + For additional details regarding install parameters check: http://ibm.biz/oms-helm-readme. + + By installing this product you accept the license terms http://ibm.biz/oms-license and http://ibm.biz/oms-apps-license. + digest: e1bd6eedeedbd07d38a610bb79550d2815555f444493f6d8331d395212813772 + icon: https://raw.githubusercontent.com/IBM/charts/master/logo/ibm-oms-logo.png + keywords: + - oms + - sterling + - yantra + - order + - fulfillment + - om + - amd64 + - ppc64le + - framework + - Commercial + - RHOCP + - Other + kubeVersion: '>=1.11.0' + maintainers: + - name: IBM + name: ibm-oms-pro-prod + type: application + response: + - https://redhat-developer.github.com/redhat-helm-charts/charts/ibm-oms-pro-prod-5.0.0.tgz + version: 5.0.0 + ibm-operator-catalog-enablement: + - apiVersion: v2 + appVersion: 1.0.0 + created: "2020-07-15T16:32:34.697730479+05:30" + description: | + IBM Operator Catalog enablement deploys custom CatalogSources for operators + available to deploy and maintain IBM products. The operators are publically + available, but products they install may require purchase and entitlement keys. + + Documentation + For additional details regarding installation see https://ibm.biz/operator-catalog-readme + + License + By installing this catalog you accept the license terms https://www.apache.org/licenses/LICENSE-2.0 + digest: 5a765d2f3f36f1b35c403a546df3590e3d6014cbf86bd36775f8febb54baa58c + home: http://ibm.biz/oprcatalog + icon: data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBxITEhUQEhAVEhEVFRUQFxgVGBUQEBgSFREXFhcXFxUYHiggGBolHRUWITEhJSkrLi4uFx8zODMsNygtLisBCgoKDg0OGxAQGi0mHyYtLS0wLy4tKy8tMistLS0tLS8vLS0tLS0rLS0uLS0tLS0tLS0vLS0tLS0tLS0tLS0tLf/AABEIANMA7wMBEQACEQEDEQH/xAAbAAEAAgMBAQAAAAAAAAAAAAAABQYCAwQBB//EAEYQAAIBAQQFBwgHBgUFAAAAAAABAgMEBREhBhIxQVEiYXGBkaGxEyMyUlSjwdIzQmJygsLRFBaisuHwB0NTc5IVJDREs//EABoBAQACAwEAAAAAAAAAAAAAAAAEBQECAwb/xAA5EQACAQICBwYFAwMFAQEAAAAAAQIDBBEhBRIxQVGh0RMVUnGBsSIyYeHwQpHBFCMzNENygvHCYv/aAAwDAQACEQMRAD8A+4gAAAAAAAAAAAAAAAAAAAArmk1eWvGnjhHV1sOLcms+Ow70lliea03Wn2ipp5YY830OjRmvKSnFvFR1Wsc8MccujIxVSWDJGhK05xnCTxSww9cehNnEvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVbSh+eX+2v5pEqj8p5XTf+oX/Fe7OnRR/Sfg/OaV9xJ0Dtqf8AX+SwHA9CAAAAAAAAAAAAAAAAAAAAAAAAAAAACD0sttSnTioNx1pNOSyeSxwT3Y/AlWsIyk8Sp0tcVKVNKDwxe0jdGb5lr+SqTcoyyi5NyaluWLzwf6HW4orV1oog6Mv5qp2VSWKezHj5/UtxAPSAAAAAAAAqmlT88v8Abj/PMl0Pl9Ty2m/9Qv8AivdnTok/pfwfnNbjcSNA7an/AF/+ixEY9CAAAAAAACs3neUpTahNxgslqtrHnxR2jHBHkr/SNSpVapyaitmDwx+pMXPXlOmnLN4tY8UjnNYMvdGV51rdSntzWPE7jUsAAAAAAAAAAAAAAAAAct5WJVqcqb3rJ8JLYzenNwkpI4XNBV6Tpvf7nzqpCUJOMspReD4pplwmpLFHipwlCTi8mi/XDePlqSk/TjyZdK39e0qq9Ps5Ybj19hdf1FFSe1ZPz+5InEmgAAAAAFU0sXnovc6aXWpSx8V2ky3+U8vptPt4v/8AP8s6dEF9K93IXWtbHxRpcbiRoJP+4/L+SxEY9AAAAAAARd/W3UhqJ8qeXRHe/gdKccXiVOlrvsaWpH5pe2/p/wCEBZKLnJQW19y3s6vJYnmbehKtUVOO8uFGkoxUVsSwI7eJ7mnTjTgoR2IzMG4AAAAAAAAAAAAAAAAABVNMrt2WiK4Rn4Rl8Own2dX9D9Cg0xa/78fJ/wAP+P2IjR+8vI1U2+RLkz6N0urwxJFelrxy2ldYXX9PVxfyvJ9fQ+hIqD2AAAAAABptVlhUWE4qS59q6HuNoycdhxrUKdZYVFiZUKEYLVhFRjwRhybeLNqdKFOOrBYI2GDoAAAADGpNRTk3gksX0IyliazmoRcpbEUu22p1Jub37FwjuRKjHBYHiLq4dxVdR+nkT+j1j1Y+Ua5UtnNH+u3sONSWeB6DQ9p2dPtZbZe332/sS5yLkAAAAAAAAAAAAAAAAAAAGFekpxcJLGMk4tczMxbi8UazgpxcZbGfPbbctaFR01SnNY8mUYtxa3PFZJ8cdhcQrwlHHFI8hWsa1Oo4KLfBpbfzeXu7KMoUqcJvGUYRi9+aWzHeVNSSlNtHqraEqdGMZbUkdRodwAAAAAAAAAAAAAcN9UJToyjDN5PDik8cDpTaUsyDpGjOrbyjDbl65leuy7JzmlKEowWcnJOPUsTvOaSyPO2Wj6lWqlOLUVtxWHpmW5IiHsD0AAAAAAAAAAAAAAAAAAAAAAAAA8bAIm26R2enlr+Ulwhyl/y2d5Iha1JbsPMrq2lLenlji/pnz2EPaNLpv0KcYrjJuT7FgSI2cVtZW1NNVH8kUvPPocFXSC0y/wA3D7qivhidVbU1uIc9J3Uv14eSRhTvavj9NPtDow4Gsb24b+dnbQvGt/qy7TnKlDgSY3dfxskaF51d8selI4ulHgTKd5WW8kKN5v60V1ZdxydLgTIXj/UjtpWqMt+D58jm4tEuFaEjcanUAAAAAAAAAAAAAAAAHPZ7fSm3GFWE5LaoyjJrpSZ0lSnFYyi16HKFanNtRkm1wZ0HM6gAAAAAAAAELfGkdKjjBecqr6qeSf2nu6NpKo2s6mbyRXXekqdD4VnLhw82U68b4rVny58n1VyYdm/rLKnQhT2I87cXlav87y4bvzzNdisVWq8KdOU92K9FdMnkjM5xh8zOdKhUqvCnFv8AOOwnbJojUedSpGHNFOb7cku8iTvY/pRZ0tC1H/kkl5Z9P5JSjopQXpSnLpaS7kR3dzezAnQ0NQXzNv16HVDR6zL/AC+2U38TR3NR7zvHRltH9PN9TZ/0Whuhh0OX6mvbz4m3d9Dw82Yu54bnJdjRntnvMOwp7mzB3bJbGn3Mz2iZo7SUdjxMVTayawGOJjUayZ1UajXQaNJkiEmjqhUTNGsCRGSZmYNgAAAAAAAADU7TBS1HOOtwxWt2GcHtOTr0lPUclrcMVj+xtMHUjtIaFSdmqwpem4tLDJvil0rFdZ3tpRjVi5bMSNdwnOhKMNuB8njKUJfWhOL54Ti/FM9M0pLimeQWtCXBr0aLbcumso4QtC147NeK5a+9H63Vn0lZcaNTzp5fQuLbS0o/DWzXFbfVdP2LrY7XTqxU6c1OL3rwfB8zKidOUHqyWDLynVhUjrQeKN5odAAADyTSWLeCWfNgA3gUrSHSlyxpWd4Q2Oayk+aPBc/9u1t7LV+Kpt4Hnb7SjnjCi8uPHy6/sV2yWedSShTi5Se5eLe5c7Js5RgsZPIqadKdSWrBYsuV0aJwjhKu/KS9VfRr4y8OYq617KWUMlzL+10RCPxVs3w3ff8AMiyQgkkkkksklkl1EJtvNlxGKisEsjIwZAAAAAAAB41jtBhpPaaZ0OBspcTm6fAwijYwkb4SNGjomZmDYAAAAGi12uFNa05JcOL6FvNoxcthwr3FOhHWqPAr1vv2c8qfIjx+u/0O8aSW087d6YqVPhpfCuf2/MyMpwcnhFOUnwzZu8tpUwhOpLVisWy8UU1GKk8ZJJN8+GZEe095TUlBKW3BY+ZmYNyNvi46NoXnI8rYpxymuveuZ5EihdVKL+F5cNxFuLSlXXxrPjvKFfei1ehjJLytL1orlJfajtXSsV0F1b31Ork8n+bGefudHVaOazjxX8oi7Bb6lGWvSm4S5tjXBrY0SalKNRas1iRKVadKWtTeB9E0V0g/aVKM4qNSCTeHoyT3pbtmwoby07Bpp5M9JYX39QmpLCSJ8hFiACg6WaReVboUn5pPCUl9drcvsrv6NtzZ2motee32+55vSV/2rdKn8u98ft7+W2Jua66lonqQySzlJ+jFfF8ESa9aNKOLINrazuJ6sfV8D6PdV2U6ENSmumT9KT4t/AoqtaVWWMj1VtbU7eOrBeu9naciQAAAAAAAAAAAAADyUcTOJhoxwBgyRgyegyACFvy+XSfk4LlYYtvNLHgt7O9KlrZsp9I6SlQl2dNZ4beBWataUnrSk5Se95slKKWw8zUqTqS1pvFkrd1xznyp+bj/ABvq3dZxnVS2FnaaJq1fiqfCuf29f2LJZLHCmsIRw4vbJ9LI0pOW09Jb21KhHCmsPc3mp3AAAABXr60SoV25x8zUeeMVjFv7UN/SsCdQv6lLJ5r83ldc6NpVviXwv6fyjo0c0fjZVLluc5YYyw1VgtiSzw28TS6u3XaywSOlnZRtk88W95MkQmlV03vvycf2em/OTWMmtsYPd0vwx5iysLbXfaS2L3+xUaUvOzj2UNr2/Rfcpl12GdepGlDa9r3Ritsn/fAta1SNKDlIoaFCVaahD/xcT6ldl3woU1Sgslte+Ut8nznnKtWVWWtI9fb0IUIKEDrOZ2AAAAAAAAAAAAAAAAB45LiDDaPNdcUZwGsuJkYMgAir2uVVpKanqSwweWsmujFZnanWcFgVl9o2NzJTUsH5Y9Dbd10U6WaWtP1nt6luNZ1ZSOlro6jb5pYy4v8AjgSBzJ4AAAAAAAAAAOe32uNKnKrL0Ypy6eCXO3l1m9Om6klFbznVqKnBzlsR8kttolUnKrN4yk3J/BdCWC6j1FOChFRjsR46rOVSbnLaz6Nojc/kKOtJedqYSlxS+rDq387ZQ3tx2s8FsX5iel0da9hTxl8z29PzeTpCLAAAAAAAAAAAAAAGMppGUsTDeBplWe7I21TnKb3GmcnxNkcZNs0yNjkzVIyjmzHWa2NroyM4Gus1sNkLdNb8en9TV00zeNzOP1OyheEJZPkvn2dppKm0Sqd1CWTyZ1nMkgAAAAAAAAAAAFS08tb1YUFv85LoWUV24vqRaaNp5uo/IqdKVMlTXm/4IPRS6/K2hOS5FPzj4Np8ldufUyZe1uzpZbXkQLC37Ssm9iz6H0k8+elAAAAAAAAAAAAABjJmTDZqaNjQKjxDkNQ2KkuBrizKhEyUVwMYm2CDiuAxGCNU7NB7Yrqy8DZSaNJUYPajlrXb6sup/qbqrxI07PwsjLRSlF4SWHgdotPYQKkJQeEkbLJeMoZPlR4b10GJU1I3o3UqeTzRN0K0ZrWi8URmmngy2p1IzjrRNhg3AAAAAAAAAKDfkvKVpy3Y6q6I5fDHrLy3WpTSKC5evVb/ADIsOiFk1KLnvnJv8Mcl349pAvqmtUw4FjYU9WnjxJ0hE4AAAAAAAAAAAA8ZkwzzAGD1IwbHoAAAAAAAAMalNSWDWKMptbDWUVJYNEDeV3uHKjnDvXTzc5Jp1NbJ7SourV0/ijs9jksdtdOWKzW9cV+pvOCkiNQuJUZYrZvRZ6FVTipReKZDaaeDL+nOM4qUdhsMG4AAB42DGITMgxrTwi5cE32IRWLSEngsSjypF1iUrgXWx0dSEYerFLrwzKepLWk2XFOOrFI3GhuAAAAAAAAAAAAAAAAAAAAAAAAAAAeNbgGsSsXxYfJyxXoS2cz4E2lPWWe0oL227GWK+V/mBlcVv1J6knyZPDolu7dnYYrU8VijNhc9nPUex+5ZiGXwANVWrhlvNlHE1lLAxhIyzVM3JGhvgarb9HL7r8Den8yNanysr1Oz4tLnXiT3PIhKOZZytLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA02uzqpBwexrse5m0ZOLxRzrUlVg4PeUivFxk4vJptPpRZRzWKPKTi4ScXtRcLntflaUZP0lyZdK/XJ9ZX1YaksD0tnX7akpPbsfmdNeqoxcnu8TWMdZ4EiUtVYkUq7bxe1kpxwI+tiSlCGCz2kWTxZIisDaamxrtK5Mug2h8yNZbGRlKnmuleJJlLIjpZkuRCUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqaV2fVqRqLZNYP70f6NdhPtZYxw4Hn9K0tWoprf7oz0RtPLnT4rXXSng/Fdhi6jkpG+iauE5Q45nbftq5Spp7FrPpez++cxbU8nIsbieeqeXNHXlrPZHPrewXD1VhxFBazxJwhEwAGM1kzK2mHsOdQN8TngdRzOoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITS6njQUvVmn1PFfFEq0fx4FZpaONDHg10K5cNfVrwe7lJ9Go/6EyvHGmynsZuFeL8/Zi9LbrVqjx+u49UeSvAk0KWFKK+haVZ4zbLVo5DChGW+Tcu/BdyKq8f91rgT7ZYU8eJKEUkAAAGvVNsTXAzRqbHoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFaUf+NU/B/8ASJItf8q9fYg6S/00vT3RRaVRpprb/QtWsUeXi2nijjqV223xbfa8SxUElgWzli8T6dci/wC3o/7UH2wTPL3P+afm/cu6P+OPkjtOJ1AAAPMAYPQZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCaX1cLM160ox79b8pKs1jVK7SssLdri11/gqNz0tetCGGOOt3Qk/gWVZ6sGygtIa9aMfP2ZFWxatScfVnKPZJr4FnT+KCfFInzyk0fTdHaqlZqLX+nGPXFar8DzF3HVrzX1fMu7d40o+RIkc7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqGnVrzp0VuxqP+WP5iysIZOXoUWmKucafr/C/k5tCaGtXc90IfxSyXcpHS+lhTS4nHRNPWrOXBe/4yE0vs3k7XUW6bVVfjzf8WsWmj6mvbx+mX7fbA73UNWq/rmWn/D23a1CVJvOnLFfcnmu/WKrS1LVqqfFc1+InWFTGDjwLUVROAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMatRRTlJ4RScm3sSSxbMpNvBGJSUVi9h8uvS3+Wqzq7pPJcIrJLsPQUqXZwUTx9xWdao58fbcXbQ+xeToKT9Ko9f8OyPdn1lTe1Napgt2R6DRlHs6Os9ss/TcRv+Id261ONois6fJl9yT29T8WTdEV9Wbpvfs8zN/Sxiprd7FQ0bvf8AZq8aj9B8if3Hvw4p4Pq5y4vLbt6Tjv2rzK+3rdlNS3bz65CaaTTTTWKazTT2NM8g008GX6eOaMjBkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFL03vv/1ab56jXdD4vqXEtrC2/wB2Xp1KLSt5/sw9enUgtHrtdorKH1FypvhBbul7O17iZc1lShjv3FdZ27r1VHdtfl9z6hFYZLJLI86euSwMa1KM4uEljGScWnsaawaMxk4tSW1GGk1gz5BpHdErLWdN4uD5VOXGH6rY+3eexs7mNxT1lt3r6/mw8/cUXSnqvZuJ3QjSpU8LNXlhT2U5vZHH6sn6vB7ujZA0lo9z/u01nvXH6+ZKs7vV/tz2bmfRTzhbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFS0p0rVPGjQknV2Sms4w5lxl4FnZ2Ln8dTZw4/Yp7/SKp406T+Le+H39ij2WjOpNQgnOcngltbbzbb72y4nKMI60skihhCVSSjHNs+pXBdEbNSUFnN8qcuMubmW485c13Wnju3HrLO1VvT1d+9/UkyOSgAR9+XRTtVJ0qmW+Ml6UZbmv03ki2uZ289ePquKOVajGrHVkfIb6umrZqjp1Y/dkvQmuMX8NqPYW1zTuIa0H6b0efrUZUpasiV0b0zq2ZKnUTrUFklj5yC+y3tX2X1NES80XTr/ABRylyfn19zvb3s6XwvNcz6LdOkFmtC81VTl6j5NRfhefWsjzlxZVqHzxy47v3LelcU6vyvqShFO4AAAAAAAAAAAAAAAAAAAAAAAAAABE3npHZqGU6qcvUhy59i2deBKo2dar8scuLyRFr3tGj80s+CzZSL80xrV8YU/M0nlk/OSXPLcuZdrLi30dTpZyzfIo7rSdSr8MPhXP9+n7kLd1hqVpqnSg5S7Elxk9yJdWpCnHWmyBRozqy1ILFn03RzR+Fljj6daS5U/yx4Lx8PO3V3Ku+C4HqLOyjbx4ye19PoTREJoAAAAOW8bvpV4OlVgpwe57U+Ke1PnR1o1p0Za8HgzSpTjUjqyWKPnV/aAVqeM7O/LQ26rwjWS8Jdz5j0drpmnP4avwvju+xT19HzjnDNc/uUu00pQlqzjKE1uknGS6nmXcJRmsYvFFdJOLweTO2y6SWyllC1VEuDl5Rdk8ThOxtqnzQXt7HWNzWjsk/zzOp6dXj7T7uj8hy7os/Bzl1N/6+48XJdB+/l4+0+7o/IO57Pwc5dTHeFx4uS6D9/Lx9p93R+Qd0Wfg5y6jvC48XJdB+/l4+0+7o/IY7os/Bzl1Md4XHi5LoFp3eHtPu6PyDuiz8HOXUx3hc+LkuhktOrw9p93R+Qx3RZ+DnLqO8Lnxcl0Pf36vD2n3dH5DHdNp4Ocuo7wufFyXQ9WnN4e0+7pfKY7ptPBzfUx3hc+LkuhktOLw9o93S+Ux3TaeDm+o7xufFyXQ9/fi3+0e7pfKY7qtPBzfUd43Pi5LoerTe3+0e7pfKY7qtfBzfUd43Pi5LoZLTa3+0e7pfKY7rtfBzfUd43Pi5LoerTW3+0e7pfKY7rtfBzfUd43Pi5LoZLTW3+0e7pfKY7rtfBzfUx3jc+Lkuh6tNLd7R7ul8pjuy18PN9R3jc+Lkuh6tM7d7R/BS+Ux3ZbeHm+o7xufFyXQS0wtzy/aH1Qpr8oWjbZfo5vqYekbl/r5LocNqve0VPpK9SS4OTUf+KyO0LalD5YpehwncVZ/NJv1NFks06j1acJTlwinJ9eGw2nOMFjN4eZyhTlN4QWPkW65tBas8JWiXko+rHCVR9fox7yrr6UhHKmsXx3dXyLW30TOWdV4LgtvRcy9Xdd1KhDUpQUI78NrfFva30lJVrTqy1pvEvKNGFKOrBYI6jmdQAAAAAAAADntlipVVq1aUKkeE4qa7zpTqzpvGEmvJms4RmsJLEp+kWiVigtaFDVbx2TqJZLhrYIuLPSVzJ4Slj6LoV9ezorNR9z5fb6ajNpLBJvn8T1NKTlFNlJUSUsEczOpzBgwegwAD1GGDJGpg9RgGaMMHqMGDJGoPUYBkjBgyRhgyRqDJGATFwWKFSSU44rPe1sx4Mh3VWUFjFkq1pRqSwkj6BdGi1j1FN2eMpfac5r/jJtFDXv7jWw1v2wXsXlGwt8MdX98X7lhoUIwWrCMYRW6KUV2IgSlKTxk8SdGKisIrA2GpsAAAAAAf/Z + keywords: + - amd64 + - deploy + - Catalog + - Commercial + - Limited + - Other + - RHOCP + kubeVersion: '>=1.17.1-0' + maintainers: + - name: IBM + name: ibm-operator-catalog-enablement + type: application + response: + - https://redhat-developer.github.com/redhat-helm-charts/charts/ibm-operator-catalog-enablement-1.0.0.tgz + version: 1.0.0 + nodejs-ex-k: + - apiVersion: v2 + appVersion: 1.16.0 + created: "2020-07-15T16:32:34.700484147+05:30" + description: A Helm chart for Kubernetes + digest: 9f8eca455f8379baddd6ab61bd5517373514bb99bedbbddffb98ad88ac9841cc + name: nodejs-ex-k + type: application + response: + - https://redhat-developer.github.com/redhat-helm-charts/charts/nodejs-ex-k-0.2.0.tgz + version: 0.2.0 + - apiVersion: v2 + appVersion: 1.16.0 + created: "2020-07-15T16:32:34.698385506+05:30" + description: A Helm chart for Kubernetes + digest: 64b36fee719567e2aae8cb6186ba02a56dc62a90df71af7f90cc1d9d2cd1fb25 + name: nodejs-ex-k + type: application + response: + - https://redhat-developer.github.com/redhat-helm-charts/charts/nodejs-ex-k-0.1.1.tgz + version: 0.1.1 +generated: "2020-07-15T16:32:34.538582491+05:30" +` + +var azureRepoYaml = ` +apiVersion: v1 +entries: + aks-helloworld: + - apiVersion: v1 + created: "2020-03-30T16:27:13.4350608-05:00" + description: A Helm chart for Kubernetes + digest: a9bc374461e31cb1429021a898ef83d0a650783033f3bc121ed536ad99301bbc + name: aks-helloworld + response: + - https://azure-samples.github.io/helm-charts/aks-helloworld-0.1.1.tgz + version: 0.1.1 + - apiVersion: v1 + created: "2020-03-30T16:27:13.433497-05:00" + description: A Helm chart for Kubernetes + digest: f4eb20e7116b24c4825861e6ff6f4303bbe83bcee4a7b2b6ea9fe6929852f34d + name: aks-helloworld + response: + - https://azure-samples.github.io/helm-charts/aks-helloworld-0.1.0.tgz + version: 0.1.0 + azure-vote: + - apiVersion: v1 + created: "2020-03-30T16:27:13.4454224-05:00" + description: A Helm chart for Kubernetes + digest: ac7698878230b11c766c0fcbfce36e7a1c8ab4ab18d55eae9e6551217042c164 + name: azure-vote + response: + - https://azure-samples.github.io/helm-charts/azure-vote-0.1.1.tgz + version: 0.1.1 + - apiVersion: v1 + created: "2020-03-30T16:27:13.4436157-05:00" + description: A Helm chart for Kubernetes + digest: da054ba832f52ff3e8da26ffe705d6ad6c2ed0818c04588c832c716e8e8d84a7 + name: azure-vote + response: + - https://azure-samples.github.io/helm-charts/azure-vote-0.1.0.tgz + version: 0.1.0 + azure-vote-osba: + - apiVersion: v1 + created: "2020-03-30T16:27:13.4518442-05:00" + description: A Helm chart for Kubernetes + digest: 64f7688099208066cef552afc560bd4d0ff04a297365c4260d3116fd1af061da + name: azure-vote-osba + response: + - https://azure-samples.github.io/helm-charts/azure-vote-osba-0.1.0.tgz + version: 0.1.0 + burst-scheduler: + - apiVersion: v1 + created: "2020-03-30T16:27:13.455658-05:00" + description: A Helm chart for Kubernetes + digest: 326910e7174b98c337e508c1b2baa1e430720758e013256aa8fcbcf1551c2c13 + name: burst-scheduler + response: + - https://azure-samples.github.io/helm-charts/burst-scheduler-0.1.0.tgz + version: 0.1.0 + image-pull-secret: + - apiVersion: v1 + created: "2020-03-30T16:27:13.4592817-05:00" + description: A Helm chart for Kubernetes + digest: ee937e26d72c420a9eb5af8eb0ef23206260858c830b06e7b5ec37c5752fa6a3 + name: image-pull-secret + response: + - https://azure-samples.github.io/helm-charts/image-pull-secret-0.1.0.tgz + version: 0.1.0 + open-service-broker-azure: + - apiVersion: v1 + appVersion: v0.0.1 + created: "2020-03-30T16:27:13.4706997-05:00" + dependencies: + - condition: redis.embedded + name: redis + repository: https://kubernetes-charts.storage.googleapis.com/ + version: 0.10.0 + description: A Helm chart for Open Service Broker For Azure + digest: 407ba56844a0e8ed67014ea3d7ef32d2b2e15f7f34bf691b237da6f10818e265 + home: https://github.com/azure/open-service-broker-azure + keywords: + - azure + - services + - service broker + maintainers: + - email: kent.rancourt@microsoft.com + name: Kent Rancourt + - email: jeremy.rickard@microsoft.com + name: Jeremy Rickard + name: open-service-broker-azure + sources: + - https://github.com/azure/open-service-broker-azure + - https://hub.docker.com/r/microsoft/azure-service-broker/ + response: + - https://azure-samples.github.io/helm-charts/open-service-broker-azure-v0.0.1.tgz + version: v0.0.1 + osba-container-instances-demo: + - apiVersion: v1 + created: "2020-03-30T16:27:13.4798738-05:00" + description: A Helm chart for Kubernetes + digest: b5f25ecf64d72644166835e48afda396b83acaff341bcb7c1e60efc12e547df6 + name: osba-container-instances-demo + response: + - https://azure-samples.github.io/helm-charts/osba-container-instances-demo-0.1.0.tgz + version: 0.1.0 + osba-cosmos-mongodb-demo: + - apiVersion: v1 + created: "2020-03-30T16:27:13.489062-05:00" + description: A Helm chart for Kubernetes + digest: b581f67458cdf999ef0308786dc29f603bab6aadf5ad0a127f5fb1a816aef091 + name: osba-cosmos-mongodb-demo + response: + - https://azure-samples.github.io/helm-charts/osba-cosmos-mongodb-demo-0.1.0.tgz + version: 0.1.0 + osba-mysql-demo: + - apiVersion: v1 + created: "2020-03-30T16:27:13.4958437-05:00" + description: A Helm chart for Kubernetes + digest: 034fe8960dcc4948ae28110622c6ea5b5439eb5ee35750899446da2455acf6ea + name: osba-mysql-demo + response: + - https://azure-samples.github.io/helm-charts/osba-mysql-demo-0.1.0.tgz + version: 0.1.0 + osba-storage-demo: + - apiVersion: v1 + created: "2020-03-30T16:27:13.5013751-05:00" + description: A Helm chart for Kubernetes + digest: d74b4c6a348b988e4dfa8fb2ad6873f533aeb0390ee5a1a88fedcee9fc2abc3a + name: osba-storage-demo + response: + - https://azure-samples.github.io/helm-charts/osba-storage-demo-0.1.0.tgz + version: 0.1.0 + osba-text-analytics-demo: + - apiVersion: v1 + created: "2020-03-30T16:27:13.5060898-05:00" + description: A Helm chart for Kubernetes + digest: d249a3f0926c24f448e36bc40211faee4b320927919e27c1aabe337065496db0 + name: osba-text-analytics-demo + response: + - https://azure-samples.github.io/helm-charts/osba-text-analytics-demo-0.1.0.tgz + version: 0.1.0 + tweet-factory-operator: + - apiVersion: v1 + created: "2020-03-30T16:27:13.5104606-05:00" + description: A Helm chart for Kubernetes + digest: 8186cd27426d20d607f093f90ee05fe65be0e27fda7ecaeb6b98c1ee6204f11a + name: tweet-factory-operator + response: + - https://azure-samples.github.io/helm-charts/tweet-factory-operator-0.1.0.tgz + version: 0.1.0 + twitter-sentiment: + - apiVersion: v1 + created: "2020-03-30T16:27:13.5145249-05:00" + description: A Helm chart for Kubernetes + digest: 0a71fceec9bc133c4d2e7a9475e19478bb997cd10898803eaf68f3ce33ff271a + name: twitter-sentiment + response: + - https://azure-samples.github.io/helm-charts/twitter-sentiment-0.1.0.tgz + version: 0.1.0 + twitter-sentiment-cnab: + - apiVersion: v1 + created: "2020-03-30T16:27:13.5192786-05:00" + description: A Helm chart for Kubernetes + digest: f6446a210a13c6f8a3b5d5feb7cd70115e3b00c8b445dc14c5186e5d2a56de2a + name: twitter-sentiment-cnab + response: + - https://azure-samples.github.io/helm-charts/twitter-sentiment-cnab-0.1.0.tgz + version: 0.1.0 +generated: "2020-03-30T16:27:13.4275217-05:00" +` + +func TestHelmConfigBuilder_IndexFiles(t *testing.T) { + tests := []struct { + testName string + indexFile string + expectedEntries int + }{{ + testName: "correct index.yaml should report correct no of chart entries", + indexFile: testdata.AzureRepoYaml, + expectedEntries: 14, + }} + + for i, tt := range tests { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/yaml") + fmt.Fprintln(w, tt.indexFile) + })) + helmConfigBuilder := HelmConfigGetter{} + + helmConfigs := []*HelmConfig{ + { + Name: "sample-" + strconv.Itoa(i), + Url: ts.URL, + }, + } + + indexFiles := helmConfigBuilder.IndexFiles(helmConfigs) + indexFile := helmConfigBuilder.MergeIndexFiles(indexFiles...) + + if len(indexFile.Entries) != tt.expectedEntries { + t.Errorf("index length mismatch expected is %d received %d", len(indexFile.Entries), tt.expectedEntries) + } + } +} + +func TestHelmConfigBuilder_ClusterConfigs(t *testing.T) { + tests := []struct { + testName string + response []string + expectedEntries int + repoName []string + }{ + { + testName: "with both repos", + response: []string{sampleRepoYaml, azureRepoYaml}, + expectedEntries: 20, + repoName: []string{"redhat-repo", "azure-repo"}, + }, + { + testName: "with single repos", + response: []string{azureRepoYaml}, + expectedEntries: 14, + repoName: []string{"azure-repo"}, + }, + { + testName: "with no repo configured", + response: []string{}, + expectedEntries: 6, + repoName: []string{}, + }, + } + for _, test := range tests { + client := fake.SetupClusterWithHelmCrs(test.response) + file, err := FetchIndexFile(client, nil, nil) + if err != nil { + t.Error(err) + } + if len(file.Entries) != test.expectedEntries { + t.Errorf("Total chart entries are not matching expected is %d and received %d", test.expectedEntries, len(file.Entries)) + } + } +} diff --git a/pkg/helm/chartproxy/config.go b/pkg/helm/chartproxy/config.go index 57bc1899ce1..b5bfbe4477e 100644 --- a/pkg/helm/chartproxy/config.go +++ b/pkg/helm/chartproxy/config.go @@ -40,6 +40,7 @@ func (cfg *config) Configure(srv *server.Server) { if cfg.repoCaFile != "" { rootCAs = x509.NewCertPool() certPEM, err := ioutil.ReadFile(cfg.repoCaFile) + srv.HelmDefaultRepoCACert = certPEM if err != nil { log.Fatalf("failed to read helm chart repo ca file %v : %v", cfg.repoCaFile, err) } diff --git a/pkg/helm/handlers/handler_test.go b/pkg/helm/handlers/handler_test.go index 0908cdf4f7c..3f4af14e8f2 100644 --- a/pkg/helm/handlers/handler_test.go +++ b/pkg/helm/handlers/handler_test.go @@ -11,6 +11,13 @@ import ( "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/release" + "helm.sh/helm/v3/pkg/repo" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/dynamic/fake" + corev1 "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/rest" + "sigs.k8s.io/yaml" "github.com/openshift/console/pkg/auth" "github.com/openshift/console/pkg/helm/actions" @@ -122,8 +129,30 @@ func fakeRollbackRelease(name string, t *testing.T, rel *release.Release, err er } } +func fakeHelmGetChartRepos(indexFile *repo.IndexFile, err error) func(c dynamic.Interface, coreClient corev1.CoreV1Interface, caCert []byte) (*repo.IndexFile, error) { + return func(c dynamic.Interface, coreClient corev1.CoreV1Interface, caCerts []byte) (*repo.IndexFile, error) { + return indexFile, err + } +} + +func fakeDynamicClient(err error) func(conf *rest.Config) (dynamic.Interface, error) { + return func(conf *rest.Config) (dynamic.Interface, error) { + return fake.NewSimpleDynamicClient(runtime.NewScheme()), err + } +} + +type FakeConfig struct { + action.RESTClientGetter +} + +func (f FakeConfig) ToRESTConfig() (config *rest.Config, err error) { + return &rest.Config{}, nil +} + func getFakeActionConfigurations(string, string, string, *http.RoundTripper) *action.Configuration { - return &action.Configuration{} + return &action.Configuration{ + RESTClientGetter: FakeConfig{}, + } } func TestHelmHandlers_HandleHelmList(t *testing.T) { @@ -632,3 +661,73 @@ func TestHelmHandlers_HandleHelmUpgradeRelease(t *testing.T) { }) } } + +func TestHelmHandlers_HandleGetRepos(t *testing.T) { + tests := []struct { + name string + indexFile *repo.IndexFile + httpStatusCode int + fakeError error + expectedResponse string + }{ + { + name: "valid repo index file should return correct response", + httpStatusCode: http.StatusOK, + fakeError: nil, + indexFile: &repo.IndexFile{ + APIVersion: "v1", + Entries: map[string]repo.ChartVersions{ + "redhat-chart": { + { + Metadata: &chart.Metadata{ + Name: "redhat-chart", + Version: "v1.0.0", + APIVersion: "v1", + }, + URLs: []string{"https://redhat-chart.url.com"}, + }, + }, + }, + }, + }, + { + name: "error case should return correct http header", + indexFile: nil, + httpStatusCode: http.StatusInternalServerError, + fakeError: errors.New("Fake error"), + expectedResponse: "{\"error\":\"Failed to get k8s dynamic client: Fake error\"}", + }, + } + + for _, tt := range tests { + handlers := fakeHelmHandler() + var request *http.Request + + handlers.getHelmIndexFile = fakeHelmGetChartRepos(tt.indexFile, tt.fakeError) + handlers.getDynamicClient = fakeDynamicClient(tt.fakeError) + + request = httptest.NewRequest(http.MethodGet, "/api/helm/charts/index.yaml", strings.NewReader("")) + response := httptest.NewRecorder() + handlers.HandleGetRepos(&auth.User{}, response, request) + + if tt.expectedResponse == "" && tt.indexFile != nil { + expectedResponse, err := yaml.Marshal(tt.indexFile) + tt.expectedResponse = string(expectedResponse) + if err != nil { + t.Error(err) + } + } + + if tt.expectedResponse != response.Body.String() { + t.Errorf("Expected response isn't matching expected \n %s received \n %s", tt.expectedResponse, response.Body.String()) + } + + if response.Code != tt.httpStatusCode { + t.Errorf("Response status code isn't matching expected %d recieved %d", tt.httpStatusCode, response.Code) + } + + if tt.expectedResponse != "" && tt.expectedResponse != response.Body.String() { + t.Errorf("Response not matching expected is %s and received %s", tt.expectedResponse, response.Body.String()) + } + } +} diff --git a/pkg/helm/handlers/handlers.go b/pkg/helm/handlers/handlers.go index 71572f6568a..129a125a564 100644 --- a/pkg/helm/handlers/handlers.go +++ b/pkg/helm/handlers/handlers.go @@ -9,6 +9,12 @@ import ( "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/release" + "helm.sh/helm/v3/pkg/repo" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" + v1 "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/rest" + "sigs.k8s.io/yaml" "github.com/openshift/console/pkg/auth" "github.com/openshift/console/pkg/helm/actions" @@ -19,10 +25,11 @@ var ( plog = capnslog.NewPackageLogger("github.com/openshift/console", "helm") ) -func New(apiUrl string, transport http.RoundTripper) *helmHandlers { +func New(apiUrl string, transport http.RoundTripper, defaultRepoCACert []byte) *helmHandlers { return &helmHandlers{ ApiServerHost: apiUrl, Transport: transport, + DefaultRepoCACert: defaultRepoCACert, getActionConfigurations: actions.GetActionConfigurations, renderManifests: actions.RenderManifests, installChart: actions.InstallChart, @@ -33,13 +40,16 @@ func New(apiUrl string, transport http.RoundTripper) *helmHandlers { uninstallRelease: actions.UninstallRelease, rollbackRelease: actions.RollbackRelease, getReleaseHistory: actions.GetReleaseHistory, + getHelmIndexFile: actions.FetchIndexFile, + getDynamicClient: actions.DynamicClient, } } // helmHandlers provides handlers to handle helm related requests type helmHandlers struct { - ApiServerHost string - Transport http.RoundTripper + ApiServerHost string + Transport http.RoundTripper + DefaultRepoCACert []byte // helm action configurator getActionConfigurations func(string, string, string, *http.RoundTripper) *action.Configuration @@ -54,6 +64,8 @@ type helmHandlers struct { getRelease func(string, *action.Configuration) (*release.Release, error) getChart func(chartUrl string, conf *action.Configuration) (*chart.Chart, error) getReleaseHistory func(releaseName string, conf *action.Configuration) ([]*release.Release, error) + getHelmIndexFile func(client dynamic.Interface, corev1Client v1.CoreV1Interface, defaultRepoCACerts []byte) (*repo.IndexFile, error) + getDynamicClient func(conf *rest.Config) (dynamic.Interface, error) } func (h *helmHandlers) HandleHelmRenderManifests(user *auth.User, w http.ResponseWriter, r *http.Request) { @@ -240,3 +252,31 @@ func (h *helmHandlers) HandleGetReleaseHistory(user *auth.User, w http.ResponseW w.Header().Set("Content-Type", "application/json") w.Write(res) } + +func (h *helmHandlers) HandleGetRepos(user *auth.User, w http.ResponseWriter, r *http.Request) { + conf := h.getActionConfigurations(h.ApiServerHost, "", user.Token, &h.Transport) + config, err := conf.RESTClientGetter.ToRESTConfig() + if err != nil { + serverutils.SendResponse(w, http.StatusInternalServerError, serverutils.ApiError{Err: fmt.Sprintf("Failed to get k8s config: %v", err)}) + return + } + client, err := h.getDynamicClient(config) + if err != nil { + serverutils.SendResponse(w, http.StatusInternalServerError, serverutils.ApiError{Err: fmt.Sprintf("Failed to get k8s dynamic client: %v", err)}) + return + } + + kubeClient, err := kubernetes.NewForConfig(config) + if err != nil { + serverutils.SendResponse(w, http.StatusInternalServerError, serverutils.ApiError{Err: fmt.Sprintf("Failed to get k8s core client: %v", err)}) + return + } + + w.Header().Set("Content-Type", "application/yaml") + w.Header().Set("Cache-Control", "no-store, must-revalidate") + + indexFile, err := h.getHelmIndexFile(client, kubeClient.CoreV1(), h.DefaultRepoCACert) + + out, _ := yaml.Marshal(indexFile) + w.Write(out) +} diff --git a/pkg/helm/testdata/testdata.go b/pkg/helm/testdata/testdata.go new file mode 100644 index 00000000000..f036857341e --- /dev/null +++ b/pkg/helm/testdata/testdata.go @@ -0,0 +1,168 @@ +package testdata + +var AzureRepoYaml = ` +apiVersion: v1 +entries: + aks-helloworld: + - apiVersion: v1 + created: "2020-03-30T16:27:13.4350608-05:00" + description: A Helm chart for Kubernetes + digest: a9bc374461e31cb1429021a898ef83d0a650783033f3bc121ed536ad99301bbc + name: aks-helloworld + response: + - https://azure-samples.github.io/helm-charts/aks-helloworld-0.1.1.tgz + version: 0.1.1 + - apiVersion: v1 + created: "2020-03-30T16:27:13.433497-05:00" + description: A Helm chart for Kubernetes + digest: f4eb20e7116b24c4825861e6ff6f4303bbe83bcee4a7b2b6ea9fe6929852f34d + name: aks-helloworld + response: + - https://azure-samples.github.io/helm-charts/aks-helloworld-0.1.0.tgz + version: 0.1.0 + azure-vote: + - apiVersion: v1 + created: "2020-03-30T16:27:13.4454224-05:00" + description: A Helm chart for Kubernetes + digest: ac7698878230b11c766c0fcbfce36e7a1c8ab4ab18d55eae9e6551217042c164 + name: azure-vote + response: + - https://azure-samples.github.io/helm-charts/azure-vote-0.1.1.tgz + version: 0.1.1 + - apiVersion: v1 + created: "2020-03-30T16:27:13.4436157-05:00" + description: A Helm chart for Kubernetes + digest: da054ba832f52ff3e8da26ffe705d6ad6c2ed0818c04588c832c716e8e8d84a7 + name: azure-vote + response: + - https://azure-samples.github.io/helm-charts/azure-vote-0.1.0.tgz + version: 0.1.0 + azure-vote-osba: + - apiVersion: v1 + created: "2020-03-30T16:27:13.4518442-05:00" + description: A Helm chart for Kubernetes + digest: 64f7688099208066cef552afc560bd4d0ff04a297365c4260d3116fd1af061da + name: azure-vote-osba + response: + - https://azure-samples.github.io/helm-charts/azure-vote-osba-0.1.0.tgz + version: 0.1.0 + burst-scheduler: + - apiVersion: v1 + created: "2020-03-30T16:27:13.455658-05:00" + description: A Helm chart for Kubernetes + digest: 326910e7174b98c337e508c1b2baa1e430720758e013256aa8fcbcf1551c2c13 + name: burst-scheduler + response: + - https://azure-samples.github.io/helm-charts/burst-scheduler-0.1.0.tgz + version: 0.1.0 + image-pull-secret: + - apiVersion: v1 + created: "2020-03-30T16:27:13.4592817-05:00" + description: A Helm chart for Kubernetes + digest: ee937e26d72c420a9eb5af8eb0ef23206260858c830b06e7b5ec37c5752fa6a3 + name: image-pull-secret + response: + - https://azure-samples.github.io/helm-charts/image-pull-secret-0.1.0.tgz + version: 0.1.0 + open-service-broker-azure: + - apiVersion: v1 + appVersion: v0.0.1 + created: "2020-03-30T16:27:13.4706997-05:00" + dependencies: + - condition: redis.embedded + name: redis + repository: https://kubernetes-charts.storage.googleapis.com/ + version: 0.10.0 + description: A Helm chart for Open Service Broker For Azure + digest: 407ba56844a0e8ed67014ea3d7ef32d2b2e15f7f34bf691b237da6f10818e265 + home: https://github.com/azure/open-service-broker-azure + keywords: + - azure + - services + - service broker + maintainers: + - email: kent.rancourt@microsoft.com + name: Kent Rancourt + - email: jeremy.rickard@microsoft.com + name: Jeremy Rickard + name: open-service-broker-azure + sources: + - https://github.com/azure/open-service-broker-azure + - https://hub.docker.com/r/microsoft/azure-service-broker/ + response: + - https://azure-samples.github.io/helm-charts/open-service-broker-azure-v0.0.1.tgz + version: v0.0.1 + osba-container-instances-demo: + - apiVersion: v1 + created: "2020-03-30T16:27:13.4798738-05:00" + description: A Helm chart for Kubernetes + digest: b5f25ecf64d72644166835e48afda396b83acaff341bcb7c1e60efc12e547df6 + name: osba-container-instances-demo + response: + - https://azure-samples.github.io/helm-charts/osba-container-instances-demo-0.1.0.tgz + version: 0.1.0 + osba-cosmos-mongodb-demo: + - apiVersion: v1 + created: "2020-03-30T16:27:13.489062-05:00" + description: A Helm chart for Kubernetes + digest: b581f67458cdf999ef0308786dc29f603bab6aadf5ad0a127f5fb1a816aef091 + name: osba-cosmos-mongodb-demo + response: + - https://azure-samples.github.io/helm-charts/osba-cosmos-mongodb-demo-0.1.0.tgz + version: 0.1.0 + osba-mysql-demo: + - apiVersion: v1 + created: "2020-03-30T16:27:13.4958437-05:00" + description: A Helm chart for Kubernetes + digest: 034fe8960dcc4948ae28110622c6ea5b5439eb5ee35750899446da2455acf6ea + name: osba-mysql-demo + response: + - https://azure-samples.github.io/helm-charts/osba-mysql-demo-0.1.0.tgz + version: 0.1.0 + osba-storage-demo: + - apiVersion: v1 + created: "2020-03-30T16:27:13.5013751-05:00" + description: A Helm chart for Kubernetes + digest: d74b4c6a348b988e4dfa8fb2ad6873f533aeb0390ee5a1a88fedcee9fc2abc3a + name: osba-storage-demo + response: + - https://azure-samples.github.io/helm-charts/osba-storage-demo-0.1.0.tgz + version: 0.1.0 + osba-text-analytics-demo: + - apiVersion: v1 + created: "2020-03-30T16:27:13.5060898-05:00" + description: A Helm chart for Kubernetes + digest: d249a3f0926c24f448e36bc40211faee4b320927919e27c1aabe337065496db0 + name: osba-text-analytics-demo + response: + - https://azure-samples.github.io/helm-charts/osba-text-analytics-demo-0.1.0.tgz + version: 0.1.0 + tweet-factory-operator: + - apiVersion: v1 + created: "2020-03-30T16:27:13.5104606-05:00" + description: A Helm chart for Kubernetes + digest: 8186cd27426d20d607f093f90ee05fe65be0e27fda7ecaeb6b98c1ee6204f11a + name: tweet-factory-operator + response: + - https://azure-samples.github.io/helm-charts/tweet-factory-operator-0.1.0.tgz + version: 0.1.0 + twitter-sentiment: + - apiVersion: v1 + created: "2020-03-30T16:27:13.5145249-05:00" + description: A Helm chart for Kubernetes + digest: 0a71fceec9bc133c4d2e7a9475e19478bb997cd10898803eaf68f3ce33ff271a + name: twitter-sentiment + response: + - https://azure-samples.github.io/helm-charts/twitter-sentiment-0.1.0.tgz + version: 0.1.0 + twitter-sentiment-cnab: + - apiVersion: v1 + created: "2020-03-30T16:27:13.5192786-05:00" + description: A Helm chart for Kubernetes + digest: f6446a210a13c6f8a3b5d5feb7cd70115e3b00c8b445dc14c5186e5d2a56de2a + name: twitter-sentiment-cnab + response: + - https://azure-samples.github.io/helm-charts/twitter-sentiment-cnab-0.1.0.tgz + version: 0.1.0 +generated: "2020-03-30T16:27:13.4275217-05:00" +` diff --git a/pkg/server/server.go b/pkg/server/server.go index 1a9f5808185..79dce705a36 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -119,6 +119,7 @@ type Server struct { KnativeEventSourceCRDLister ResourceLister KnativeChannelCRDLister ResourceLister HelmChartRepoProxyConfig *proxy.Config + HelmDefaultRepoCACert []byte GOARCH string GOOS string // Monitoring and Logging related URLs @@ -375,11 +376,12 @@ func (s *Server) HTTPHandler() http.Handler { handle("/api/console/version", authHandler(s.versionHandler)) // Helm Endpoints - helmHandlers := helmhandlerspkg.New(s.KubeAPIServerURL, s.K8sClient.Transport) + helmHandlers := helmhandlerspkg.New(s.KubeAPIServerURL, s.K8sClient.Transport, s.HelmDefaultRepoCACert) handle("/api/helm/template", authHandlerWithUser(helmHandlers.HandleHelmRenderManifests)) handle("/api/helm/releases", authHandlerWithUser(helmHandlers.HandleHelmList)) handle("/api/helm/chart", authHandlerWithUser(helmHandlers.HandleChartGet)) handle("/api/helm/release/history", authHandlerWithUser(helmHandlers.HandleGetReleaseHistory)) + handle("/api/helm/charts/index.yaml", authHandlerWithUser(helmHandlers.HandleGetRepos)) handle("/api/helm/release", authHandlerWithUser(func(user *auth.User, w http.ResponseWriter, r *http.Request) { switch r.Method { @@ -399,13 +401,6 @@ func (s *Server) HTTPHandler() http.Handler { } })) - helmChartRepoProxy := proxy.NewProxy(s.HelmChartRepoProxyConfig) - - // Only proxy requests to chart repo index file - handle(helmChartRepoProxyEndpoint+"index.yaml", http.StripPrefix( - proxy.SingleJoiningSlash(s.BaseURL.Path, helmChartRepoProxyEndpoint), - http.HandlerFunc(helmChartRepoProxy.ServeHTTP))) - // GitOps proxy endpoints if s.gitopsProxyEnabled() { gitopsProxy := proxy.NewProxy(s.GitOpsProxyConfig) diff --git a/vendor/k8s.io/client-go/dynamic/fake/simple.go b/vendor/k8s.io/client-go/dynamic/fake/simple.go new file mode 100644 index 00000000000..b2c5f6f3436 --- /dev/null +++ b/vendor/k8s.io/client-go/dynamic/fake/simple.go @@ -0,0 +1,371 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package fake + +import ( + "context" + "strings" + + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/testing" +) + +func NewSimpleDynamicClient(scheme *runtime.Scheme, objects ...runtime.Object) *FakeDynamicClient { + // In order to use List with this client, you have to have the v1.List registered in your scheme. Neat thing though + // it does NOT have to be the *same* list + scheme.AddKnownTypeWithName(schema.GroupVersionKind{Group: "fake-dynamic-client-group", Version: "v1", Kind: "List"}, &unstructured.UnstructuredList{}) + + codecs := serializer.NewCodecFactory(scheme) + o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) + for _, obj := range objects { + if err := o.Add(obj); err != nil { + panic(err) + } + } + + cs := &FakeDynamicClient{scheme: scheme} + cs.AddReactor("*", "*", testing.ObjectReaction(o)) + cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + gvr := action.GetResource() + ns := action.GetNamespace() + watch, err := o.Watch(gvr, ns) + if err != nil { + return false, nil, err + } + return true, watch, nil + }) + + return cs +} + +// Clientset implements clientset.Interface. Meant to be embedded into a +// struct to get a default implementation. This makes faking out just the method +// you want to test easier. +type FakeDynamicClient struct { + testing.Fake + scheme *runtime.Scheme +} + +type dynamicResourceClient struct { + client *FakeDynamicClient + namespace string + resource schema.GroupVersionResource +} + +var _ dynamic.Interface = &FakeDynamicClient{} + +func (c *FakeDynamicClient) Resource(resource schema.GroupVersionResource) dynamic.NamespaceableResourceInterface { + return &dynamicResourceClient{client: c, resource: resource} +} + +func (c *dynamicResourceClient) Namespace(ns string) dynamic.ResourceInterface { + ret := *c + ret.namespace = ns + return &ret +} + +func (c *dynamicResourceClient) Create(ctx context.Context, obj *unstructured.Unstructured, opts metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) { + var uncastRet runtime.Object + var err error + switch { + case len(c.namespace) == 0 && len(subresources) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootCreateAction(c.resource, obj), obj) + + case len(c.namespace) == 0 && len(subresources) > 0: + accessor, err := meta.Accessor(obj) + if err != nil { + return nil, err + } + name := accessor.GetName() + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootCreateSubresourceAction(c.resource, name, strings.Join(subresources, "/"), obj), obj) + + case len(c.namespace) > 0 && len(subresources) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewCreateAction(c.resource, c.namespace, obj), obj) + + case len(c.namespace) > 0 && len(subresources) > 0: + accessor, err := meta.Accessor(obj) + if err != nil { + return nil, err + } + name := accessor.GetName() + uncastRet, err = c.client.Fake. + Invokes(testing.NewCreateSubresourceAction(c.resource, name, strings.Join(subresources, "/"), c.namespace, obj), obj) + + } + + if err != nil { + return nil, err + } + if uncastRet == nil { + return nil, err + } + + ret := &unstructured.Unstructured{} + if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil { + return nil, err + } + return ret, err +} + +func (c *dynamicResourceClient) Update(ctx context.Context, obj *unstructured.Unstructured, opts metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error) { + var uncastRet runtime.Object + var err error + switch { + case len(c.namespace) == 0 && len(subresources) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootUpdateAction(c.resource, obj), obj) + + case len(c.namespace) == 0 && len(subresources) > 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(c.resource, strings.Join(subresources, "/"), obj), obj) + + case len(c.namespace) > 0 && len(subresources) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewUpdateAction(c.resource, c.namespace, obj), obj) + + case len(c.namespace) > 0 && len(subresources) > 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewUpdateSubresourceAction(c.resource, strings.Join(subresources, "/"), c.namespace, obj), obj) + + } + + if err != nil { + return nil, err + } + if uncastRet == nil { + return nil, err + } + + ret := &unstructured.Unstructured{} + if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil { + return nil, err + } + return ret, err +} + +func (c *dynamicResourceClient) UpdateStatus(ctx context.Context, obj *unstructured.Unstructured, opts metav1.UpdateOptions) (*unstructured.Unstructured, error) { + var uncastRet runtime.Object + var err error + switch { + case len(c.namespace) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(c.resource, "status", obj), obj) + + case len(c.namespace) > 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewUpdateSubresourceAction(c.resource, "status", c.namespace, obj), obj) + + } + + if err != nil { + return nil, err + } + if uncastRet == nil { + return nil, err + } + + ret := &unstructured.Unstructured{} + if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil { + return nil, err + } + return ret, err +} + +func (c *dynamicResourceClient) Delete(ctx context.Context, name string, opts metav1.DeleteOptions, subresources ...string) error { + var err error + switch { + case len(c.namespace) == 0 && len(subresources) == 0: + _, err = c.client.Fake. + Invokes(testing.NewRootDeleteAction(c.resource, name), &metav1.Status{Status: "dynamic delete fail"}) + + case len(c.namespace) == 0 && len(subresources) > 0: + _, err = c.client.Fake. + Invokes(testing.NewRootDeleteSubresourceAction(c.resource, strings.Join(subresources, "/"), name), &metav1.Status{Status: "dynamic delete fail"}) + + case len(c.namespace) > 0 && len(subresources) == 0: + _, err = c.client.Fake. + Invokes(testing.NewDeleteAction(c.resource, c.namespace, name), &metav1.Status{Status: "dynamic delete fail"}) + + case len(c.namespace) > 0 && len(subresources) > 0: + _, err = c.client.Fake. + Invokes(testing.NewDeleteSubresourceAction(c.resource, strings.Join(subresources, "/"), c.namespace, name), &metav1.Status{Status: "dynamic delete fail"}) + } + + return err +} + +func (c *dynamicResourceClient) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOptions metav1.ListOptions) error { + var err error + switch { + case len(c.namespace) == 0: + action := testing.NewRootDeleteCollectionAction(c.resource, listOptions) + _, err = c.client.Fake.Invokes(action, &metav1.Status{Status: "dynamic deletecollection fail"}) + + case len(c.namespace) > 0: + action := testing.NewDeleteCollectionAction(c.resource, c.namespace, listOptions) + _, err = c.client.Fake.Invokes(action, &metav1.Status{Status: "dynamic deletecollection fail"}) + + } + + return err +} + +func (c *dynamicResourceClient) Get(ctx context.Context, name string, opts metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) { + var uncastRet runtime.Object + var err error + switch { + case len(c.namespace) == 0 && len(subresources) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootGetAction(c.resource, name), &metav1.Status{Status: "dynamic get fail"}) + + case len(c.namespace) == 0 && len(subresources) > 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootGetSubresourceAction(c.resource, strings.Join(subresources, "/"), name), &metav1.Status{Status: "dynamic get fail"}) + + case len(c.namespace) > 0 && len(subresources) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewGetAction(c.resource, c.namespace, name), &metav1.Status{Status: "dynamic get fail"}) + + case len(c.namespace) > 0 && len(subresources) > 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewGetSubresourceAction(c.resource, c.namespace, strings.Join(subresources, "/"), name), &metav1.Status{Status: "dynamic get fail"}) + } + + if err != nil { + return nil, err + } + if uncastRet == nil { + return nil, err + } + + ret := &unstructured.Unstructured{} + if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil { + return nil, err + } + return ret, err +} + +func (c *dynamicResourceClient) List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error) { + var obj runtime.Object + var err error + switch { + case len(c.namespace) == 0: + obj, err = c.client.Fake. + Invokes(testing.NewRootListAction(c.resource, schema.GroupVersionKind{Group: "fake-dynamic-client-group", Version: "v1", Kind: "" /*List is appended by the tracker automatically*/}, opts), &metav1.Status{Status: "dynamic list fail"}) + + case len(c.namespace) > 0: + obj, err = c.client.Fake. + Invokes(testing.NewListAction(c.resource, schema.GroupVersionKind{Group: "fake-dynamic-client-group", Version: "v1", Kind: "" /*List is appended by the tracker automatically*/}, c.namespace, opts), &metav1.Status{Status: "dynamic list fail"}) + + } + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + + retUnstructured := &unstructured.Unstructured{} + if err := c.client.scheme.Convert(obj, retUnstructured, nil); err != nil { + return nil, err + } + entireList, err := retUnstructured.ToList() + if err != nil { + return nil, err + } + + list := &unstructured.UnstructuredList{} + list.SetResourceVersion(entireList.GetResourceVersion()) + for i := range entireList.Items { + item := &entireList.Items[i] + metadata, err := meta.Accessor(item) + if err != nil { + return nil, err + } + if label.Matches(labels.Set(metadata.GetLabels())) { + list.Items = append(list.Items, *item) + } + } + return list, nil +} + +func (c *dynamicResourceClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { + switch { + case len(c.namespace) == 0: + return c.client.Fake. + InvokesWatch(testing.NewRootWatchAction(c.resource, opts)) + + case len(c.namespace) > 0: + return c.client.Fake. + InvokesWatch(testing.NewWatchAction(c.resource, c.namespace, opts)) + + } + + panic("math broke") +} + +// TODO: opts are currently ignored. +func (c *dynamicResourceClient) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (*unstructured.Unstructured, error) { + var uncastRet runtime.Object + var err error + switch { + case len(c.namespace) == 0 && len(subresources) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootPatchAction(c.resource, name, pt, data), &metav1.Status{Status: "dynamic patch fail"}) + + case len(c.namespace) == 0 && len(subresources) > 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewRootPatchSubresourceAction(c.resource, name, pt, data, subresources...), &metav1.Status{Status: "dynamic patch fail"}) + + case len(c.namespace) > 0 && len(subresources) == 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewPatchAction(c.resource, c.namespace, name, pt, data), &metav1.Status{Status: "dynamic patch fail"}) + + case len(c.namespace) > 0 && len(subresources) > 0: + uncastRet, err = c.client.Fake. + Invokes(testing.NewPatchSubresourceAction(c.resource, c.namespace, name, pt, data, subresources...), &metav1.Status{Status: "dynamic patch fail"}) + + } + + if err != nil { + return nil, err + } + if uncastRet == nil { + return nil, err + } + + ret := &unstructured.Unstructured{} + if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil { + return nil, err + } + return ret, err +} diff --git a/vendor/k8s.io/client-go/testing/actions.go b/vendor/k8s.io/client-go/testing/actions.go new file mode 100644 index 00000000000..b6b2c1f222c --- /dev/null +++ b/vendor/k8s.io/client-go/testing/actions.go @@ -0,0 +1,681 @@ +/* +Copyright 2015 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package testing + +import ( + "fmt" + "path" + "strings" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" +) + +func NewRootGetAction(resource schema.GroupVersionResource, name string) GetActionImpl { + action := GetActionImpl{} + action.Verb = "get" + action.Resource = resource + action.Name = name + + return action +} + +func NewGetAction(resource schema.GroupVersionResource, namespace, name string) GetActionImpl { + action := GetActionImpl{} + action.Verb = "get" + action.Resource = resource + action.Namespace = namespace + action.Name = name + + return action +} + +func NewGetSubresourceAction(resource schema.GroupVersionResource, namespace, subresource, name string) GetActionImpl { + action := GetActionImpl{} + action.Verb = "get" + action.Resource = resource + action.Subresource = subresource + action.Namespace = namespace + action.Name = name + + return action +} + +func NewRootGetSubresourceAction(resource schema.GroupVersionResource, subresource, name string) GetActionImpl { + action := GetActionImpl{} + action.Verb = "get" + action.Resource = resource + action.Subresource = subresource + action.Name = name + + return action +} + +func NewRootListAction(resource schema.GroupVersionResource, kind schema.GroupVersionKind, opts interface{}) ListActionImpl { + action := ListActionImpl{} + action.Verb = "list" + action.Resource = resource + action.Kind = kind + labelSelector, fieldSelector, _ := ExtractFromListOptions(opts) + action.ListRestrictions = ListRestrictions{labelSelector, fieldSelector} + + return action +} + +func NewListAction(resource schema.GroupVersionResource, kind schema.GroupVersionKind, namespace string, opts interface{}) ListActionImpl { + action := ListActionImpl{} + action.Verb = "list" + action.Resource = resource + action.Kind = kind + action.Namespace = namespace + labelSelector, fieldSelector, _ := ExtractFromListOptions(opts) + action.ListRestrictions = ListRestrictions{labelSelector, fieldSelector} + + return action +} + +func NewRootCreateAction(resource schema.GroupVersionResource, object runtime.Object) CreateActionImpl { + action := CreateActionImpl{} + action.Verb = "create" + action.Resource = resource + action.Object = object + + return action +} + +func NewCreateAction(resource schema.GroupVersionResource, namespace string, object runtime.Object) CreateActionImpl { + action := CreateActionImpl{} + action.Verb = "create" + action.Resource = resource + action.Namespace = namespace + action.Object = object + + return action +} + +func NewRootCreateSubresourceAction(resource schema.GroupVersionResource, name, subresource string, object runtime.Object) CreateActionImpl { + action := CreateActionImpl{} + action.Verb = "create" + action.Resource = resource + action.Subresource = subresource + action.Name = name + action.Object = object + + return action +} + +func NewCreateSubresourceAction(resource schema.GroupVersionResource, name, subresource, namespace string, object runtime.Object) CreateActionImpl { + action := CreateActionImpl{} + action.Verb = "create" + action.Resource = resource + action.Namespace = namespace + action.Subresource = subresource + action.Name = name + action.Object = object + + return action +} + +func NewRootUpdateAction(resource schema.GroupVersionResource, object runtime.Object) UpdateActionImpl { + action := UpdateActionImpl{} + action.Verb = "update" + action.Resource = resource + action.Object = object + + return action +} + +func NewUpdateAction(resource schema.GroupVersionResource, namespace string, object runtime.Object) UpdateActionImpl { + action := UpdateActionImpl{} + action.Verb = "update" + action.Resource = resource + action.Namespace = namespace + action.Object = object + + return action +} + +func NewRootPatchAction(resource schema.GroupVersionResource, name string, pt types.PatchType, patch []byte) PatchActionImpl { + action := PatchActionImpl{} + action.Verb = "patch" + action.Resource = resource + action.Name = name + action.PatchType = pt + action.Patch = patch + + return action +} + +func NewPatchAction(resource schema.GroupVersionResource, namespace string, name string, pt types.PatchType, patch []byte) PatchActionImpl { + action := PatchActionImpl{} + action.Verb = "patch" + action.Resource = resource + action.Namespace = namespace + action.Name = name + action.PatchType = pt + action.Patch = patch + + return action +} + +func NewRootPatchSubresourceAction(resource schema.GroupVersionResource, name string, pt types.PatchType, patch []byte, subresources ...string) PatchActionImpl { + action := PatchActionImpl{} + action.Verb = "patch" + action.Resource = resource + action.Subresource = path.Join(subresources...) + action.Name = name + action.PatchType = pt + action.Patch = patch + + return action +} + +func NewPatchSubresourceAction(resource schema.GroupVersionResource, namespace, name string, pt types.PatchType, patch []byte, subresources ...string) PatchActionImpl { + action := PatchActionImpl{} + action.Verb = "patch" + action.Resource = resource + action.Subresource = path.Join(subresources...) + action.Namespace = namespace + action.Name = name + action.PatchType = pt + action.Patch = patch + + return action +} + +func NewRootUpdateSubresourceAction(resource schema.GroupVersionResource, subresource string, object runtime.Object) UpdateActionImpl { + action := UpdateActionImpl{} + action.Verb = "update" + action.Resource = resource + action.Subresource = subresource + action.Object = object + + return action +} +func NewUpdateSubresourceAction(resource schema.GroupVersionResource, subresource string, namespace string, object runtime.Object) UpdateActionImpl { + action := UpdateActionImpl{} + action.Verb = "update" + action.Resource = resource + action.Subresource = subresource + action.Namespace = namespace + action.Object = object + + return action +} + +func NewRootDeleteAction(resource schema.GroupVersionResource, name string) DeleteActionImpl { + action := DeleteActionImpl{} + action.Verb = "delete" + action.Resource = resource + action.Name = name + + return action +} + +func NewRootDeleteSubresourceAction(resource schema.GroupVersionResource, subresource string, name string) DeleteActionImpl { + action := DeleteActionImpl{} + action.Verb = "delete" + action.Resource = resource + action.Subresource = subresource + action.Name = name + + return action +} + +func NewDeleteAction(resource schema.GroupVersionResource, namespace, name string) DeleteActionImpl { + action := DeleteActionImpl{} + action.Verb = "delete" + action.Resource = resource + action.Namespace = namespace + action.Name = name + + return action +} + +func NewDeleteSubresourceAction(resource schema.GroupVersionResource, subresource, namespace, name string) DeleteActionImpl { + action := DeleteActionImpl{} + action.Verb = "delete" + action.Resource = resource + action.Subresource = subresource + action.Namespace = namespace + action.Name = name + + return action +} + +func NewRootDeleteCollectionAction(resource schema.GroupVersionResource, opts interface{}) DeleteCollectionActionImpl { + action := DeleteCollectionActionImpl{} + action.Verb = "delete-collection" + action.Resource = resource + labelSelector, fieldSelector, _ := ExtractFromListOptions(opts) + action.ListRestrictions = ListRestrictions{labelSelector, fieldSelector} + + return action +} + +func NewDeleteCollectionAction(resource schema.GroupVersionResource, namespace string, opts interface{}) DeleteCollectionActionImpl { + action := DeleteCollectionActionImpl{} + action.Verb = "delete-collection" + action.Resource = resource + action.Namespace = namespace + labelSelector, fieldSelector, _ := ExtractFromListOptions(opts) + action.ListRestrictions = ListRestrictions{labelSelector, fieldSelector} + + return action +} + +func NewRootWatchAction(resource schema.GroupVersionResource, opts interface{}) WatchActionImpl { + action := WatchActionImpl{} + action.Verb = "watch" + action.Resource = resource + labelSelector, fieldSelector, resourceVersion := ExtractFromListOptions(opts) + action.WatchRestrictions = WatchRestrictions{labelSelector, fieldSelector, resourceVersion} + + return action +} + +func ExtractFromListOptions(opts interface{}) (labelSelector labels.Selector, fieldSelector fields.Selector, resourceVersion string) { + var err error + switch t := opts.(type) { + case metav1.ListOptions: + labelSelector, err = labels.Parse(t.LabelSelector) + if err != nil { + panic(fmt.Errorf("invalid selector %q: %v", t.LabelSelector, err)) + } + fieldSelector, err = fields.ParseSelector(t.FieldSelector) + if err != nil { + panic(fmt.Errorf("invalid selector %q: %v", t.FieldSelector, err)) + } + resourceVersion = t.ResourceVersion + default: + panic(fmt.Errorf("expect a ListOptions %T", opts)) + } + if labelSelector == nil { + labelSelector = labels.Everything() + } + if fieldSelector == nil { + fieldSelector = fields.Everything() + } + return labelSelector, fieldSelector, resourceVersion +} + +func NewWatchAction(resource schema.GroupVersionResource, namespace string, opts interface{}) WatchActionImpl { + action := WatchActionImpl{} + action.Verb = "watch" + action.Resource = resource + action.Namespace = namespace + labelSelector, fieldSelector, resourceVersion := ExtractFromListOptions(opts) + action.WatchRestrictions = WatchRestrictions{labelSelector, fieldSelector, resourceVersion} + + return action +} + +func NewProxyGetAction(resource schema.GroupVersionResource, namespace, scheme, name, port, path string, params map[string]string) ProxyGetActionImpl { + action := ProxyGetActionImpl{} + action.Verb = "get" + action.Resource = resource + action.Namespace = namespace + action.Scheme = scheme + action.Name = name + action.Port = port + action.Path = path + action.Params = params + return action +} + +type ListRestrictions struct { + Labels labels.Selector + Fields fields.Selector +} +type WatchRestrictions struct { + Labels labels.Selector + Fields fields.Selector + ResourceVersion string +} + +type Action interface { + GetNamespace() string + GetVerb() string + GetResource() schema.GroupVersionResource + GetSubresource() string + Matches(verb, resource string) bool + + // DeepCopy is used to copy an action to avoid any risk of accidental mutation. Most people never need to call this + // because the invocation logic deep copies before calls to storage and reactors. + DeepCopy() Action +} + +type GenericAction interface { + Action + GetValue() interface{} +} + +type GetAction interface { + Action + GetName() string +} + +type ListAction interface { + Action + GetListRestrictions() ListRestrictions +} + +type CreateAction interface { + Action + GetObject() runtime.Object +} + +type UpdateAction interface { + Action + GetObject() runtime.Object +} + +type DeleteAction interface { + Action + GetName() string +} + +type DeleteCollectionAction interface { + Action + GetListRestrictions() ListRestrictions +} + +type PatchAction interface { + Action + GetName() string + GetPatchType() types.PatchType + GetPatch() []byte +} + +type WatchAction interface { + Action + GetWatchRestrictions() WatchRestrictions +} + +type ProxyGetAction interface { + Action + GetScheme() string + GetName() string + GetPort() string + GetPath() string + GetParams() map[string]string +} + +type ActionImpl struct { + Namespace string + Verb string + Resource schema.GroupVersionResource + Subresource string +} + +func (a ActionImpl) GetNamespace() string { + return a.Namespace +} +func (a ActionImpl) GetVerb() string { + return a.Verb +} +func (a ActionImpl) GetResource() schema.GroupVersionResource { + return a.Resource +} +func (a ActionImpl) GetSubresource() string { + return a.Subresource +} +func (a ActionImpl) Matches(verb, resource string) bool { + // Stay backwards compatible. + if !strings.Contains(resource, "/") { + return strings.EqualFold(verb, a.Verb) && + strings.EqualFold(resource, a.Resource.Resource) + } + + parts := strings.SplitN(resource, "/", 2) + topresource, subresource := parts[0], parts[1] + + return strings.EqualFold(verb, a.Verb) && + strings.EqualFold(topresource, a.Resource.Resource) && + strings.EqualFold(subresource, a.Subresource) +} +func (a ActionImpl) DeepCopy() Action { + ret := a + return ret +} + +type GenericActionImpl struct { + ActionImpl + Value interface{} +} + +func (a GenericActionImpl) GetValue() interface{} { + return a.Value +} + +func (a GenericActionImpl) DeepCopy() Action { + return GenericActionImpl{ + ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl), + // TODO this is wrong, but no worse than before + Value: a.Value, + } +} + +type GetActionImpl struct { + ActionImpl + Name string +} + +func (a GetActionImpl) GetName() string { + return a.Name +} + +func (a GetActionImpl) DeepCopy() Action { + return GetActionImpl{ + ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl), + Name: a.Name, + } +} + +type ListActionImpl struct { + ActionImpl + Kind schema.GroupVersionKind + Name string + ListRestrictions ListRestrictions +} + +func (a ListActionImpl) GetKind() schema.GroupVersionKind { + return a.Kind +} + +func (a ListActionImpl) GetListRestrictions() ListRestrictions { + return a.ListRestrictions +} + +func (a ListActionImpl) DeepCopy() Action { + return ListActionImpl{ + ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl), + Kind: a.Kind, + Name: a.Name, + ListRestrictions: ListRestrictions{ + Labels: a.ListRestrictions.Labels.DeepCopySelector(), + Fields: a.ListRestrictions.Fields.DeepCopySelector(), + }, + } +} + +type CreateActionImpl struct { + ActionImpl + Name string + Object runtime.Object +} + +func (a CreateActionImpl) GetObject() runtime.Object { + return a.Object +} + +func (a CreateActionImpl) DeepCopy() Action { + return CreateActionImpl{ + ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl), + Name: a.Name, + Object: a.Object.DeepCopyObject(), + } +} + +type UpdateActionImpl struct { + ActionImpl + Object runtime.Object +} + +func (a UpdateActionImpl) GetObject() runtime.Object { + return a.Object +} + +func (a UpdateActionImpl) DeepCopy() Action { + return UpdateActionImpl{ + ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl), + Object: a.Object.DeepCopyObject(), + } +} + +type PatchActionImpl struct { + ActionImpl + Name string + PatchType types.PatchType + Patch []byte +} + +func (a PatchActionImpl) GetName() string { + return a.Name +} + +func (a PatchActionImpl) GetPatch() []byte { + return a.Patch +} + +func (a PatchActionImpl) GetPatchType() types.PatchType { + return a.PatchType +} + +func (a PatchActionImpl) DeepCopy() Action { + patch := make([]byte, len(a.Patch)) + copy(patch, a.Patch) + return PatchActionImpl{ + ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl), + Name: a.Name, + PatchType: a.PatchType, + Patch: patch, + } +} + +type DeleteActionImpl struct { + ActionImpl + Name string +} + +func (a DeleteActionImpl) GetName() string { + return a.Name +} + +func (a DeleteActionImpl) DeepCopy() Action { + return DeleteActionImpl{ + ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl), + Name: a.Name, + } +} + +type DeleteCollectionActionImpl struct { + ActionImpl + ListRestrictions ListRestrictions +} + +func (a DeleteCollectionActionImpl) GetListRestrictions() ListRestrictions { + return a.ListRestrictions +} + +func (a DeleteCollectionActionImpl) DeepCopy() Action { + return DeleteCollectionActionImpl{ + ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl), + ListRestrictions: ListRestrictions{ + Labels: a.ListRestrictions.Labels.DeepCopySelector(), + Fields: a.ListRestrictions.Fields.DeepCopySelector(), + }, + } +} + +type WatchActionImpl struct { + ActionImpl + WatchRestrictions WatchRestrictions +} + +func (a WatchActionImpl) GetWatchRestrictions() WatchRestrictions { + return a.WatchRestrictions +} + +func (a WatchActionImpl) DeepCopy() Action { + return WatchActionImpl{ + ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl), + WatchRestrictions: WatchRestrictions{ + Labels: a.WatchRestrictions.Labels.DeepCopySelector(), + Fields: a.WatchRestrictions.Fields.DeepCopySelector(), + ResourceVersion: a.WatchRestrictions.ResourceVersion, + }, + } +} + +type ProxyGetActionImpl struct { + ActionImpl + Scheme string + Name string + Port string + Path string + Params map[string]string +} + +func (a ProxyGetActionImpl) GetScheme() string { + return a.Scheme +} + +func (a ProxyGetActionImpl) GetName() string { + return a.Name +} + +func (a ProxyGetActionImpl) GetPort() string { + return a.Port +} + +func (a ProxyGetActionImpl) GetPath() string { + return a.Path +} + +func (a ProxyGetActionImpl) GetParams() map[string]string { + return a.Params +} + +func (a ProxyGetActionImpl) DeepCopy() Action { + params := map[string]string{} + for k, v := range a.Params { + params[k] = v + } + return ProxyGetActionImpl{ + ActionImpl: a.ActionImpl.DeepCopy().(ActionImpl), + Scheme: a.Scheme, + Name: a.Name, + Port: a.Port, + Path: a.Path, + Params: params, + } +} diff --git a/vendor/k8s.io/client-go/testing/fake.go b/vendor/k8s.io/client-go/testing/fake.go new file mode 100644 index 00000000000..8b9ee149c83 --- /dev/null +++ b/vendor/k8s.io/client-go/testing/fake.go @@ -0,0 +1,216 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package testing + +import ( + "fmt" + "sync" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + restclient "k8s.io/client-go/rest" +) + +// Fake implements client.Interface. Meant to be embedded into a struct to get +// a default implementation. This makes faking out just the method you want to +// test easier. +type Fake struct { + sync.RWMutex + actions []Action // these may be castable to other types, but "Action" is the minimum + + // ReactionChain is the list of reactors that will be attempted for every + // request in the order they are tried. + ReactionChain []Reactor + // WatchReactionChain is the list of watch reactors that will be attempted + // for every request in the order they are tried. + WatchReactionChain []WatchReactor + // ProxyReactionChain is the list of proxy reactors that will be attempted + // for every request in the order they are tried. + ProxyReactionChain []ProxyReactor + + Resources []*metav1.APIResourceList +} + +// Reactor is an interface to allow the composition of reaction functions. +type Reactor interface { + // Handles indicates whether or not this Reactor deals with a given + // action. + Handles(action Action) bool + // React handles the action and returns results. It may choose to + // delegate by indicated handled=false. + React(action Action) (handled bool, ret runtime.Object, err error) +} + +// WatchReactor is an interface to allow the composition of watch functions. +type WatchReactor interface { + // Handles indicates whether or not this Reactor deals with a given + // action. + Handles(action Action) bool + // React handles a watch action and returns results. It may choose to + // delegate by indicating handled=false. + React(action Action) (handled bool, ret watch.Interface, err error) +} + +// ProxyReactor is an interface to allow the composition of proxy get +// functions. +type ProxyReactor interface { + // Handles indicates whether or not this Reactor deals with a given + // action. + Handles(action Action) bool + // React handles a watch action and returns results. It may choose to + // delegate by indicating handled=false. + React(action Action) (handled bool, ret restclient.ResponseWrapper, err error) +} + +// ReactionFunc is a function that returns an object or error for a given +// Action. If "handled" is false, then the test client will ignore the +// results and continue to the next ReactionFunc. A ReactionFunc can describe +// reactions on subresources by testing the result of the action's +// GetSubresource() method. +type ReactionFunc func(action Action) (handled bool, ret runtime.Object, err error) + +// WatchReactionFunc is a function that returns a watch interface. If +// "handled" is false, then the test client will ignore the results and +// continue to the next ReactionFunc. +type WatchReactionFunc func(action Action) (handled bool, ret watch.Interface, err error) + +// ProxyReactionFunc is a function that returns a ResponseWrapper interface +// for a given Action. If "handled" is false, then the test client will +// ignore the results and continue to the next ProxyReactionFunc. +type ProxyReactionFunc func(action Action) (handled bool, ret restclient.ResponseWrapper, err error) + +// AddReactor appends a reactor to the end of the chain. +func (c *Fake) AddReactor(verb, resource string, reaction ReactionFunc) { + c.ReactionChain = append(c.ReactionChain, &SimpleReactor{verb, resource, reaction}) +} + +// PrependReactor adds a reactor to the beginning of the chain. +func (c *Fake) PrependReactor(verb, resource string, reaction ReactionFunc) { + c.ReactionChain = append([]Reactor{&SimpleReactor{verb, resource, reaction}}, c.ReactionChain...) +} + +// AddWatchReactor appends a reactor to the end of the chain. +func (c *Fake) AddWatchReactor(resource string, reaction WatchReactionFunc) { + c.WatchReactionChain = append(c.WatchReactionChain, &SimpleWatchReactor{resource, reaction}) +} + +// PrependWatchReactor adds a reactor to the beginning of the chain. +func (c *Fake) PrependWatchReactor(resource string, reaction WatchReactionFunc) { + c.WatchReactionChain = append([]WatchReactor{&SimpleWatchReactor{resource, reaction}}, c.WatchReactionChain...) +} + +// AddProxyReactor appends a reactor to the end of the chain. +func (c *Fake) AddProxyReactor(resource string, reaction ProxyReactionFunc) { + c.ProxyReactionChain = append(c.ProxyReactionChain, &SimpleProxyReactor{resource, reaction}) +} + +// PrependProxyReactor adds a reactor to the beginning of the chain. +func (c *Fake) PrependProxyReactor(resource string, reaction ProxyReactionFunc) { + c.ProxyReactionChain = append([]ProxyReactor{&SimpleProxyReactor{resource, reaction}}, c.ProxyReactionChain...) +} + +// Invokes records the provided Action and then invokes the ReactionFunc that +// handles the action if one exists. defaultReturnObj is expected to be of the +// same type a normal call would return. +func (c *Fake) Invokes(action Action, defaultReturnObj runtime.Object) (runtime.Object, error) { + c.Lock() + defer c.Unlock() + + actionCopy := action.DeepCopy() + c.actions = append(c.actions, action.DeepCopy()) + for _, reactor := range c.ReactionChain { + if !reactor.Handles(actionCopy) { + continue + } + + handled, ret, err := reactor.React(actionCopy) + if !handled { + continue + } + + return ret, err + } + + return defaultReturnObj, nil +} + +// InvokesWatch records the provided Action and then invokes the ReactionFunc +// that handles the action if one exists. +func (c *Fake) InvokesWatch(action Action) (watch.Interface, error) { + c.Lock() + defer c.Unlock() + + actionCopy := action.DeepCopy() + c.actions = append(c.actions, action.DeepCopy()) + for _, reactor := range c.WatchReactionChain { + if !reactor.Handles(actionCopy) { + continue + } + + handled, ret, err := reactor.React(actionCopy) + if !handled { + continue + } + + return ret, err + } + + return nil, fmt.Errorf("unhandled watch: %#v", action) +} + +// InvokesProxy records the provided Action and then invokes the ReactionFunc +// that handles the action if one exists. +func (c *Fake) InvokesProxy(action Action) restclient.ResponseWrapper { + c.Lock() + defer c.Unlock() + + actionCopy := action.DeepCopy() + c.actions = append(c.actions, action.DeepCopy()) + for _, reactor := range c.ProxyReactionChain { + if !reactor.Handles(actionCopy) { + continue + } + + handled, ret, err := reactor.React(actionCopy) + if !handled || err != nil { + continue + } + + return ret + } + + return nil +} + +// ClearActions clears the history of actions called on the fake client. +func (c *Fake) ClearActions() { + c.Lock() + defer c.Unlock() + + c.actions = make([]Action, 0) +} + +// Actions returns a chronologically ordered slice fake actions called on the +// fake client. +func (c *Fake) Actions() []Action { + c.RLock() + defer c.RUnlock() + fa := make([]Action, len(c.actions)) + copy(fa, c.actions) + return fa +} diff --git a/vendor/k8s.io/client-go/testing/fixture.go b/vendor/k8s.io/client-go/testing/fixture.go new file mode 100644 index 00000000000..54f600ad3f7 --- /dev/null +++ b/vendor/k8s.io/client-go/testing/fixture.go @@ -0,0 +1,577 @@ +/* +Copyright 2015 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package testing + +import ( + "fmt" + "reflect" + "sync" + + jsonpatch "github.com/evanphx/json-patch" + + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/json" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/watch" + restclient "k8s.io/client-go/rest" +) + +// ObjectTracker keeps track of objects. It is intended to be used to +// fake calls to a server by returning objects based on their kind, +// namespace and name. +type ObjectTracker interface { + // Add adds an object to the tracker. If object being added + // is a list, its items are added separately. + Add(obj runtime.Object) error + + // Get retrieves the object by its kind, namespace and name. + Get(gvr schema.GroupVersionResource, ns, name string) (runtime.Object, error) + + // Create adds an object to the tracker in the specified namespace. + Create(gvr schema.GroupVersionResource, obj runtime.Object, ns string) error + + // Update updates an existing object in the tracker in the specified namespace. + Update(gvr schema.GroupVersionResource, obj runtime.Object, ns string) error + + // List retrieves all objects of a given kind in the given + // namespace. Only non-List kinds are accepted. + List(gvr schema.GroupVersionResource, gvk schema.GroupVersionKind, ns string) (runtime.Object, error) + + // Delete deletes an existing object from the tracker. If object + // didn't exist in the tracker prior to deletion, Delete returns + // no error. + Delete(gvr schema.GroupVersionResource, ns, name string) error + + // Watch watches objects from the tracker. Watch returns a channel + // which will push added / modified / deleted object. + Watch(gvr schema.GroupVersionResource, ns string) (watch.Interface, error) +} + +// ObjectScheme abstracts the implementation of common operations on objects. +type ObjectScheme interface { + runtime.ObjectCreater + runtime.ObjectTyper +} + +// ObjectReaction returns a ReactionFunc that applies core.Action to +// the given tracker. +func ObjectReaction(tracker ObjectTracker) ReactionFunc { + return func(action Action) (bool, runtime.Object, error) { + ns := action.GetNamespace() + gvr := action.GetResource() + // Here and below we need to switch on implementation types, + // not on interfaces, as some interfaces are identical + // (e.g. UpdateAction and CreateAction), so if we use them, + // updates and creates end up matching the same case branch. + switch action := action.(type) { + + case ListActionImpl: + obj, err := tracker.List(gvr, action.GetKind(), ns) + return true, obj, err + + case GetActionImpl: + obj, err := tracker.Get(gvr, ns, action.GetName()) + return true, obj, err + + case CreateActionImpl: + objMeta, err := meta.Accessor(action.GetObject()) + if err != nil { + return true, nil, err + } + if action.GetSubresource() == "" { + err = tracker.Create(gvr, action.GetObject(), ns) + } else { + // TODO: Currently we're handling subresource creation as an update + // on the enclosing resource. This works for some subresources but + // might not be generic enough. + err = tracker.Update(gvr, action.GetObject(), ns) + } + if err != nil { + return true, nil, err + } + obj, err := tracker.Get(gvr, ns, objMeta.GetName()) + return true, obj, err + + case UpdateActionImpl: + objMeta, err := meta.Accessor(action.GetObject()) + if err != nil { + return true, nil, err + } + err = tracker.Update(gvr, action.GetObject(), ns) + if err != nil { + return true, nil, err + } + obj, err := tracker.Get(gvr, ns, objMeta.GetName()) + return true, obj, err + + case DeleteActionImpl: + err := tracker.Delete(gvr, ns, action.GetName()) + if err != nil { + return true, nil, err + } + return true, nil, nil + + case PatchActionImpl: + obj, err := tracker.Get(gvr, ns, action.GetName()) + if err != nil { + return true, nil, err + } + + old, err := json.Marshal(obj) + if err != nil { + return true, nil, err + } + + // reset the object in preparation to unmarshal, since unmarshal does not guarantee that fields + // in obj that are removed by patch are cleared + value := reflect.ValueOf(obj) + value.Elem().Set(reflect.New(value.Type().Elem()).Elem()) + + switch action.GetPatchType() { + case types.JSONPatchType: + patch, err := jsonpatch.DecodePatch(action.GetPatch()) + if err != nil { + return true, nil, err + } + modified, err := patch.Apply(old) + if err != nil { + return true, nil, err + } + + if err = json.Unmarshal(modified, obj); err != nil { + return true, nil, err + } + case types.MergePatchType: + modified, err := jsonpatch.MergePatch(old, action.GetPatch()) + if err != nil { + return true, nil, err + } + + if err := json.Unmarshal(modified, obj); err != nil { + return true, nil, err + } + case types.StrategicMergePatchType: + mergedByte, err := strategicpatch.StrategicMergePatch(old, action.GetPatch(), obj) + if err != nil { + return true, nil, err + } + if err = json.Unmarshal(mergedByte, obj); err != nil { + return true, nil, err + } + default: + return true, nil, fmt.Errorf("PatchType is not supported") + } + + if err = tracker.Update(gvr, obj, ns); err != nil { + return true, nil, err + } + + return true, obj, nil + + default: + return false, nil, fmt.Errorf("no reaction implemented for %s", action) + } + } +} + +type tracker struct { + scheme ObjectScheme + decoder runtime.Decoder + lock sync.RWMutex + objects map[schema.GroupVersionResource][]runtime.Object + // The value type of watchers is a map of which the key is either a namespace or + // all/non namespace aka "" and its value is list of fake watchers. + // Manipulations on resources will broadcast the notification events into the + // watchers' channel. Note that too many unhandled events (currently 100, + // see apimachinery/pkg/watch.DefaultChanSize) will cause a panic. + watchers map[schema.GroupVersionResource]map[string][]*watch.RaceFreeFakeWatcher +} + +var _ ObjectTracker = &tracker{} + +// NewObjectTracker returns an ObjectTracker that can be used to keep track +// of objects for the fake clientset. Mostly useful for unit tests. +func NewObjectTracker(scheme ObjectScheme, decoder runtime.Decoder) ObjectTracker { + return &tracker{ + scheme: scheme, + decoder: decoder, + objects: make(map[schema.GroupVersionResource][]runtime.Object), + watchers: make(map[schema.GroupVersionResource]map[string][]*watch.RaceFreeFakeWatcher), + } +} + +func (t *tracker) List(gvr schema.GroupVersionResource, gvk schema.GroupVersionKind, ns string) (runtime.Object, error) { + // Heuristic for list kind: original kind + List suffix. Might + // not always be true but this tracker has a pretty limited + // understanding of the actual API model. + listGVK := gvk + listGVK.Kind = listGVK.Kind + "List" + // GVK does have the concept of "internal version". The scheme recognizes + // the runtime.APIVersionInternal, but not the empty string. + if listGVK.Version == "" { + listGVK.Version = runtime.APIVersionInternal + } + + list, err := t.scheme.New(listGVK) + if err != nil { + return nil, err + } + + if !meta.IsListType(list) { + return nil, fmt.Errorf("%q is not a list type", listGVK.Kind) + } + + t.lock.RLock() + defer t.lock.RUnlock() + + objs, ok := t.objects[gvr] + if !ok { + return list, nil + } + + matchingObjs, err := filterByNamespace(objs, ns) + if err != nil { + return nil, err + } + if err := meta.SetList(list, matchingObjs); err != nil { + return nil, err + } + return list.DeepCopyObject(), nil +} + +func (t *tracker) Watch(gvr schema.GroupVersionResource, ns string) (watch.Interface, error) { + t.lock.Lock() + defer t.lock.Unlock() + + fakewatcher := watch.NewRaceFreeFake() + + if _, exists := t.watchers[gvr]; !exists { + t.watchers[gvr] = make(map[string][]*watch.RaceFreeFakeWatcher) + } + t.watchers[gvr][ns] = append(t.watchers[gvr][ns], fakewatcher) + return fakewatcher, nil +} + +func (t *tracker) Get(gvr schema.GroupVersionResource, ns, name string) (runtime.Object, error) { + errNotFound := errors.NewNotFound(gvr.GroupResource(), name) + + t.lock.RLock() + defer t.lock.RUnlock() + + objs, ok := t.objects[gvr] + if !ok { + return nil, errNotFound + } + + var matchingObjs []runtime.Object + for _, obj := range objs { + acc, err := meta.Accessor(obj) + if err != nil { + return nil, err + } + if acc.GetNamespace() != ns { + continue + } + if acc.GetName() != name { + continue + } + matchingObjs = append(matchingObjs, obj) + } + if len(matchingObjs) == 0 { + return nil, errNotFound + } + if len(matchingObjs) > 1 { + return nil, fmt.Errorf("more than one object matched gvr %s, ns: %q name: %q", gvr, ns, name) + } + + // Only one object should match in the tracker if it works + // correctly, as Add/Update methods enforce kind/namespace/name + // uniqueness. + obj := matchingObjs[0].DeepCopyObject() + if status, ok := obj.(*metav1.Status); ok { + if status.Status != metav1.StatusSuccess { + return nil, &errors.StatusError{ErrStatus: *status} + } + } + + return obj, nil +} + +func (t *tracker) Add(obj runtime.Object) error { + if meta.IsListType(obj) { + return t.addList(obj, false) + } + objMeta, err := meta.Accessor(obj) + if err != nil { + return err + } + gvks, _, err := t.scheme.ObjectKinds(obj) + if err != nil { + return err + } + + if partial, ok := obj.(*metav1.PartialObjectMetadata); ok && len(partial.TypeMeta.APIVersion) > 0 { + gvks = []schema.GroupVersionKind{partial.TypeMeta.GroupVersionKind()} + } + + if len(gvks) == 0 { + return fmt.Errorf("no registered kinds for %v", obj) + } + for _, gvk := range gvks { + // NOTE: UnsafeGuessKindToResource is a heuristic and default match. The + // actual registration in apiserver can specify arbitrary route for a + // gvk. If a test uses such objects, it cannot preset the tracker with + // objects via Add(). Instead, it should trigger the Create() function + // of the tracker, where an arbitrary gvr can be specified. + gvr, _ := meta.UnsafeGuessKindToResource(gvk) + // Resource doesn't have the concept of "__internal" version, just set it to "". + if gvr.Version == runtime.APIVersionInternal { + gvr.Version = "" + } + + err := t.add(gvr, obj, objMeta.GetNamespace(), false) + if err != nil { + return err + } + } + return nil +} + +func (t *tracker) Create(gvr schema.GroupVersionResource, obj runtime.Object, ns string) error { + return t.add(gvr, obj, ns, false) +} + +func (t *tracker) Update(gvr schema.GroupVersionResource, obj runtime.Object, ns string) error { + return t.add(gvr, obj, ns, true) +} + +func (t *tracker) getWatches(gvr schema.GroupVersionResource, ns string) []*watch.RaceFreeFakeWatcher { + watches := []*watch.RaceFreeFakeWatcher{} + if t.watchers[gvr] != nil { + if w := t.watchers[gvr][ns]; w != nil { + watches = append(watches, w...) + } + if ns != metav1.NamespaceAll { + if w := t.watchers[gvr][metav1.NamespaceAll]; w != nil { + watches = append(watches, w...) + } + } + } + return watches +} + +func (t *tracker) add(gvr schema.GroupVersionResource, obj runtime.Object, ns string, replaceExisting bool) error { + t.lock.Lock() + defer t.lock.Unlock() + + gr := gvr.GroupResource() + + // To avoid the object from being accidentally modified by caller + // after it's been added to the tracker, we always store the deep + // copy. + obj = obj.DeepCopyObject() + + newMeta, err := meta.Accessor(obj) + if err != nil { + return err + } + + // Propagate namespace to the new object if hasn't already been set. + if len(newMeta.GetNamespace()) == 0 { + newMeta.SetNamespace(ns) + } + + if ns != newMeta.GetNamespace() { + msg := fmt.Sprintf("request namespace does not match object namespace, request: %q object: %q", ns, newMeta.GetNamespace()) + return errors.NewBadRequest(msg) + } + + for i, existingObj := range t.objects[gvr] { + oldMeta, err := meta.Accessor(existingObj) + if err != nil { + return err + } + if oldMeta.GetNamespace() == newMeta.GetNamespace() && oldMeta.GetName() == newMeta.GetName() { + if replaceExisting { + for _, w := range t.getWatches(gvr, ns) { + w.Modify(obj) + } + t.objects[gvr][i] = obj + return nil + } + return errors.NewAlreadyExists(gr, newMeta.GetName()) + } + } + + if replaceExisting { + // Tried to update but no matching object was found. + return errors.NewNotFound(gr, newMeta.GetName()) + } + + t.objects[gvr] = append(t.objects[gvr], obj) + + for _, w := range t.getWatches(gvr, ns) { + w.Add(obj) + } + + return nil +} + +func (t *tracker) addList(obj runtime.Object, replaceExisting bool) error { + list, err := meta.ExtractList(obj) + if err != nil { + return err + } + errs := runtime.DecodeList(list, t.decoder) + if len(errs) > 0 { + return errs[0] + } + for _, obj := range list { + if err := t.Add(obj); err != nil { + return err + } + } + return nil +} + +func (t *tracker) Delete(gvr schema.GroupVersionResource, ns, name string) error { + t.lock.Lock() + defer t.lock.Unlock() + + found := false + + for i, existingObj := range t.objects[gvr] { + objMeta, err := meta.Accessor(existingObj) + if err != nil { + return err + } + if objMeta.GetNamespace() == ns && objMeta.GetName() == name { + obj := t.objects[gvr][i] + t.objects[gvr] = append(t.objects[gvr][:i], t.objects[gvr][i+1:]...) + for _, w := range t.getWatches(gvr, ns) { + w.Delete(obj) + } + found = true + break + } + } + + if found { + return nil + } + + return errors.NewNotFound(gvr.GroupResource(), name) +} + +// filterByNamespace returns all objects in the collection that +// match provided namespace. Empty namespace matches +// non-namespaced objects. +func filterByNamespace(objs []runtime.Object, ns string) ([]runtime.Object, error) { + var res []runtime.Object + + for _, obj := range objs { + acc, err := meta.Accessor(obj) + if err != nil { + return nil, err + } + if ns != "" && acc.GetNamespace() != ns { + continue + } + res = append(res, obj) + } + + return res, nil +} + +func DefaultWatchReactor(watchInterface watch.Interface, err error) WatchReactionFunc { + return func(action Action) (bool, watch.Interface, error) { + return true, watchInterface, err + } +} + +// SimpleReactor is a Reactor. Each reaction function is attached to a given verb,resource tuple. "*" in either field matches everything for that value. +// For instance, *,pods matches all verbs on pods. This allows for easier composition of reaction functions +type SimpleReactor struct { + Verb string + Resource string + + Reaction ReactionFunc +} + +func (r *SimpleReactor) Handles(action Action) bool { + verbCovers := r.Verb == "*" || r.Verb == action.GetVerb() + if !verbCovers { + return false + } + resourceCovers := r.Resource == "*" || r.Resource == action.GetResource().Resource + if !resourceCovers { + return false + } + + return true +} + +func (r *SimpleReactor) React(action Action) (bool, runtime.Object, error) { + return r.Reaction(action) +} + +// SimpleWatchReactor is a WatchReactor. Each reaction function is attached to a given resource. "*" matches everything for that value. +// For instance, *,pods matches all verbs on pods. This allows for easier composition of reaction functions +type SimpleWatchReactor struct { + Resource string + + Reaction WatchReactionFunc +} + +func (r *SimpleWatchReactor) Handles(action Action) bool { + resourceCovers := r.Resource == "*" || r.Resource == action.GetResource().Resource + if !resourceCovers { + return false + } + + return true +} + +func (r *SimpleWatchReactor) React(action Action) (bool, watch.Interface, error) { + return r.Reaction(action) +} + +// SimpleProxyReactor is a ProxyReactor. Each reaction function is attached to a given resource. "*" matches everything for that value. +// For instance, *,pods matches all verbs on pods. This allows for easier composition of reaction functions. +type SimpleProxyReactor struct { + Resource string + + Reaction ProxyReactionFunc +} + +func (r *SimpleProxyReactor) Handles(action Action) bool { + resourceCovers := r.Resource == "*" || r.Resource == action.GetResource().Resource + if !resourceCovers { + return false + } + + return true +} + +func (r *SimpleProxyReactor) React(action Action) (bool, restclient.ResponseWrapper, error) { + return r.Reaction(action) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 52f48ec050a..24bd52afa71 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -594,6 +594,7 @@ k8s.io/cli-runtime/pkg/resource k8s.io/client-go/discovery k8s.io/client-go/discovery/cached/disk k8s.io/client-go/dynamic +k8s.io/client-go/dynamic/fake k8s.io/client-go/kubernetes k8s.io/client-go/kubernetes/scheme k8s.io/client-go/kubernetes/typed/admissionregistration/v1 @@ -652,6 +653,7 @@ k8s.io/client-go/scale/scheme/appsv1beta2 k8s.io/client-go/scale/scheme/autoscalingv1 k8s.io/client-go/scale/scheme/extensionsint k8s.io/client-go/scale/scheme/extensionsv1beta1 +k8s.io/client-go/testing k8s.io/client-go/third_party/forked/golang/template k8s.io/client-go/tools/auth k8s.io/client-go/tools/cache