From 4ffcb00958496c6cec75670cb3eabb52d171a475 Mon Sep 17 00:00:00 2001 From: Stephanie Date: Mon, 29 Mar 2021 19:05:16 -0400 Subject: [PATCH 1/9] complete implementation Signed-off-by: Stephanie --- go.mod | 8 +- go.sum | 225 ++++++++++++++++++++++++++++++++++++ pkg/devfile/parser/parse.go | 89 ++++++++++++-- 3 files changed, 307 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index e99155fe..dd7d4f30 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/fatih/color v1.7.0 github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 github.com/gobwas/glob v0.2.3 - github.com/google/go-cmp v0.4.0 + github.com/google/go-cmp v0.5.2 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 github.com/mattn/go-colorable v0.1.2 // indirect @@ -17,8 +17,10 @@ require ( github.com/spf13/afero v1.2.2 github.com/stretchr/testify v1.6.1 github.com/xeipuuv/gojsonschema v1.2.0 - k8s.io/api v0.19.0 - k8s.io/apimachinery v0.19.0 + k8s.io/api v0.19.2 + k8s.io/apimachinery v0.19.2 + k8s.io/client-go v0.19.2 k8s.io/klog v1.0.0 + sigs.k8s.io/controller-runtime v0.7.0 sigs.k8s.io/yaml v1.2.0 ) diff --git a/go.sum b/go.sum index 751a995c..bf80d7b3 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,33 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 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= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= 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/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 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= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -18,18 +35,30 @@ github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdko github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= 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= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 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/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 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/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= @@ -39,7 +68,9 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -47,6 +78,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/devfile/api/v2 v2.0.0-20210304212617-bfc3f501616b h1:r6Z2rXRA60eQQTdh1cJBBrj3vp9/c1rNbX42kyw2WpA= github.com/devfile/api/v2 v2.0.0-20210304212617-bfc3f501616b/go.mod h1:Cot4snybn3qhIh48oIFi9McocnIx7zY5fFbjfrIpPvg= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -61,6 +93,7 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -74,12 +107,20 @@ github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ER github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v0.3.0 h1:q4c+kbcR0d5rSurhBR8dIgieOaYpXtsdTYfx22Cu6rs= +github.com/go-logr/logr v0.3.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54= github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= +github.com/go-logr/zapr v0.2.0 h1:v6Ji8yBW77pva6NkJKQdHLAJKrIJKRHz0RXwPqCHSR4= +github.com/go-logr/zapr v0.2.0/go.mod h1:qhKdvif7YF5GI9NWEpyxTSSBdGmzkNguibrdCNVPunU= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= @@ -133,9 +174,12 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP 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/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/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= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -158,6 +202,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -165,22 +211,31 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/googleapis/gnostic v0.5.1 h1:A8Yhf6EtqTv9RMsU6MQTyrtV1TjWlR6xU9BsZIwuTCM= +github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 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= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -188,12 +243,17 @@ github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg= github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.10 h1:6q5mVkdH/vYmqngx7kZQTjJ5HRsx+ImorDIEQ+beJgc= +github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -202,11 +262,13 @@ github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 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.3/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= @@ -234,11 +296,15 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +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= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE= github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -251,6 +317,7 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 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= @@ -258,11 +325,15 @@ 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/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4= +github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs= +github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/openshift/api v0.0.0-20200930075302-db52bc4ef99f h1:/msM59v15x4DaAZeJnQwkVsCGTEa1mx+nSSMehZVAHs= github.com/openshift/api v0.0.0-20200930075302-db52bc4ef99f/go.mod h1:Si/I9UGeRR3qzg01YWPmtlr0GeGk2fnuggXJRmjAZ6U= github.com/openshift/build-machinery-go v0.0.0-20200819073603-48aa266c95f7/go.mod h1:b1BuldmJlbA/xYtdZvKi+7j5YGB44qJUJDZ9zwiNCfE= @@ -279,28 +350,49 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia5qI= github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -308,6 +400,8 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -315,10 +409,13 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= @@ -331,33 +428,73 @@ github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5/go.mod h1:skWido08r9w6Lq/w70DO5XYIKMu4QFu1+4VsqLQuJy8= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= 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/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -371,11 +508,16 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0= @@ -385,6 +527,8 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -401,18 +545,29 @@ golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8= golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -424,7 +579,12 @@ golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -433,28 +593,64 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200616133436-c1934b75d054 h1:HHeAlu5H9b71C+Fx0K+1dGgVFN1DM1/wz4aoGOA5qS8= golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/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= +gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0= gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= +gomodules.xyz/jsonpatch/v2 v2.1.0 h1:Phva6wqu+xR//Njw6iorylFFgn/z547tw5Ne3HZPQ+k= +gomodules.xyz/jsonpatch/v2 v2.1.0/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 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.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -476,6 +672,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/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/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= @@ -489,31 +686,51 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= k8s.io/api v0.18.6 h1:osqrAXbOQjkKIWDTjrqxWQ3w0GkKb1KA1XkUGHHYpeE= k8s.io/api v0.18.6/go.mod h1:eeyxr+cwCjMdLAmr2W3RyDI0VvTawSg/3RFFBEnmZGI= k8s.io/api v0.19.0 h1:XyrFIJqTYZJ2DU7FBE/bSPz7b1HvbVBuBf07oeo6eTc= k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw= +k8s.io/api v0.19.2 h1:q+/krnHWKsL7OBZg/rxnycsl9569Pud76UJ77MvKXms= +k8s.io/api v0.19.2/go.mod h1:IQpK0zFQ1xc5iNIQPqzgoOwuFugaYHK4iCknlAQP9nI= k8s.io/apiextensions-apiserver v0.18.6 h1:vDlk7cyFsDyfwn2rNAO2DbmUbvXy5yT5GE3rrqOzaMo= k8s.io/apiextensions-apiserver v0.18.6/go.mod h1:lv89S7fUysXjLZO7ke783xOwVTm6lKizADfvUM/SS/M= +k8s.io/apiextensions-apiserver v0.19.2 h1:oG84UwiDsVDu7dlsGQs5GySmQHCzMhknfhFExJMz9tA= +k8s.io/apiextensions-apiserver v0.19.2/go.mod h1:EYNjpqIAvNZe+svXVx9j4uBaVhTB4C94HkY3w058qcg= k8s.io/apimachinery v0.18.6 h1:RtFHnfGNfd1N0LeSrKCUznz5xtUP1elRGvHJbL3Ntag= k8s.io/apimachinery v0.18.6/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= k8s.io/apimachinery v0.19.0 h1:gjKnAda/HZp5k4xQYjL0K/Yb66IvNqjthCb03QlKpaQ= k8s.io/apimachinery v0.19.0/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= +k8s.io/apimachinery v0.19.2 h1:5Gy9vQpAGTKHPVOh5c4plE274X8D/6cuEiTO2zve7tc= +k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= k8s.io/apiserver v0.18.6/go.mod h1:Zt2XvTHuaZjBz6EFYzpp+X4hTmgWGy8AthNVnTdm3Wg= +k8s.io/apiserver v0.19.2/go.mod h1:FreAq0bJ2vtZFj9Ago/X0oNGC51GfubKK/ViOKfVAOA= +k8s.io/client-go v0.18.6 h1:I+oWqJbibLSGsZj8Xs8F0aWVXJVIoUHWaaJV3kUN/Zw= k8s.io/client-go v0.18.6/go.mod h1:/fwtGLjYMS1MaM5oi+eXhKwG+1UHidUEXRh6cNsdO0Q= +k8s.io/client-go v0.19.2 h1:gMJuU3xJZs86L1oQ99R4EViAADUPMHHtS9jFshasHSc= +k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA= k8s.io/code-generator v0.18.6/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= k8s.io/code-generator v0.19.0/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk= +k8s.io/code-generator v0.19.2/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk= k8s.io/component-base v0.18.6/go.mod h1:knSVsibPR5K6EW2XOjEHik6sdU5nCvKMrzMt2D4In14= +k8s.io/component-base v0.19.2 h1:jW5Y9RcZTb79liEhW3XDVTW7MuvEGP0tQZnfSX6/+gs= +k8s.io/component-base v0.19.2/go.mod h1:g5LrsiTiabMLZ40AR6Hl45f088DevyGY+cCE2agEIVo= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200114144118-36b2048a9120 h1:RPscN6KhmG54S33L+lr3GS+oD1jmchIU0ll519K6FA4= k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= @@ -533,9 +750,17 @@ k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20200603063816-c1c6865ac451 h1:v8ud2Up6QK1lNOKFgiIVrZdMg7MpmSnvtrOieolJKoE= k8s.io/utils v0.0.0-20200603063816-c1c6865ac451/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20200729134348-d5654de09c73 h1:uJmqzgNWG7XyClnU/mLPBWwfKKF1K8Hf8whTseBgJcg= +k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20200912215256-4140de9c8800 h1:9ZNvfPvVIEsp/T1ez4GQuzCcCTEQWhovSofhqR73A6g= +k8s.io/utils v0.0.0-20200912215256-4140de9c8800/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9/go.mod h1:dzAXnQbTRyDlZPJX2SUPEqvnB+j7AJjtlox7PEwigU0= sigs.k8s.io/controller-runtime v0.6.3 h1:SBbr+inLPEKhvlJtrvDcwIpm+uhDvp63Bl72xYJtoOE= sigs.k8s.io/controller-runtime v0.6.3/go.mod h1:WlZNXcM0++oyaQt4B7C2lEE5JYRs8vJUzRP4N4JpdAY= +sigs.k8s.io/controller-runtime v0.7.0 h1:bU20IBBEPccWz5+zXpLnpVsgBYxqclaHu1pVDl/gEt8= +sigs.k8s.io/controller-runtime v0.7.0/go.mod h1:pJ3YBrJiAqMAZKi6UVGuE98ZrroV1p+pIhoHsMm9wdU= sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E= sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= diff --git a/pkg/devfile/parser/parse.go b/pkg/devfile/parser/parse.go index c942de1f..7bda3b73 100644 --- a/pkg/devfile/parser/parse.go +++ b/pkg/devfile/parser/parse.go @@ -1,11 +1,16 @@ package parser import ( + "context" "encoding/json" "fmt" "github.com/devfile/library/pkg/util" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/clientcmd" "net/url" "path" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" "strings" devfileCtx "github.com/devfile/library/pkg/devfile/parser/context" @@ -165,17 +170,20 @@ func parseParentAndPlugin(d DevfileObj) (err error) { if !reflect.DeepEqual(parent, &v1.Parent{}) { var parentDevfileObj DevfileObj - if parent.Uri != "" { + switch { + case parent.Uri != "": parentDevfileObj, err = parseFromURI(parent.Uri, d.Ctx) if err != nil { return err } - } else if parent.Id != "" { + case parent.Id != "": parentDevfileObj, err = parseFromRegistry(parent.Id, parent.RegistryUrl, d.Ctx) if err != nil { return err } - } else { + case parent.Kubernetes != nil: + parentDevfileObj, err = parseFromKubeCRD(parent.Kubernetes.Namespace, parent.Kubernetes.Name, d.Ctx) + default: return fmt.Errorf("parent URI or parent Id undefined, currently only URI and Id are suppported") } @@ -202,13 +210,21 @@ func parseParentAndPlugin(d DevfileObj) (err error) { if component.Plugin != nil && !reflect.DeepEqual(component.Plugin, &v1.PluginComponent{}) { plugin := component.Plugin var pluginDevfileObj DevfileObj - if plugin.Uri != "" { + switch { + case plugin.Uri != "": pluginDevfileObj, err = parseFromURI(plugin.Uri, d.Ctx) if err != nil { return err } - } else { - return fmt.Errorf("plugin URI undefined, currently only URI is suppported") + case plugin.Id != "": + pluginDevfileObj, err = parseFromRegistry(plugin.Id, plugin.RegistryUrl, d.Ctx) + if err != nil { + return err + } + case plugin.Kubernetes != nil: + pluginDevfileObj, err = parseFromKubeCRD(plugin.Kubernetes.Namespace, plugin.Kubernetes.Name, d.Ctx) + default: + return fmt.Errorf("plugin URI or plugin Id undefined, currently only URI and Id are suppported") } pluginWorkspaceContent := pluginDevfileObj.Data.GetDevfileWorkspace() flattenedPlugin := pluginWorkspaceContent @@ -262,16 +278,16 @@ func parseFromURI(uri string, curDevfileCtx devfileCtx.DevfileCtx) (DevfileObj, return populateAndParseDevfile(d, true) } -func parseFromRegistry(parentId, registryURL string, curDevfileCtx devfileCtx.DevfileCtx) (DevfileObj, error) { +func parseFromRegistry(Id, registryURL string, curDevfileCtx devfileCtx.DevfileCtx) (DevfileObj, error) { if registryURL != "" { - devfileContent, err := getDevfileFromRegistry(parentId, registryURL) + devfileContent, err := getDevfileFromRegistry(Id, registryURL) if err != nil { return DevfileObj{}, err } return ParseDevfile(ParserArgs{Data: devfileContent, RegistryURLs: curDevfileCtx.GetRegistryURLs()}) } else if curDevfileCtx.GetRegistryURLs() != nil { for _, registry := range curDevfileCtx.GetRegistryURLs() { - devfileContent, err := getDevfileFromRegistry(parentId, registry) + devfileContent, err := getDevfileFromRegistry(Id, registry) if devfileContent != nil && err == nil { return ParseDevfile(ParserArgs{Data: devfileContent, RegistryURLs: curDevfileCtx.GetRegistryURLs()}) } @@ -280,15 +296,64 @@ func parseFromRegistry(parentId, registryURL string, curDevfileCtx devfileCtx.De return DevfileObj{}, fmt.Errorf("failed to fetch from registry, registry URL is not provided") } - return DevfileObj{}, fmt.Errorf("failed to get parent Id: %s from registry URLs provided", parentId) + return DevfileObj{}, fmt.Errorf("failed to get Id: %s from registry URLs provided", Id) } -func getDevfileFromRegistry(parentId, registryURL string) ([]byte, error) { +func getDevfileFromRegistry(Id, registryURL string) ([]byte, error) { if !strings.HasPrefix(registryURL, "http://") && !strings.HasPrefix(registryURL, "https://") { return nil, fmt.Errorf("the provided registryURL: %s is not a valid URL", registryURL) } param := util.HTTPRequestParams{ - URL: fmt.Sprintf("%s/devfiles/%s", registryURL, parentId), + URL: fmt.Sprintf("%s/devfiles/%s", registryURL, Id), } return util.HTTPGetRequest(param, 0) } + +func parseFromKubeCRD(providedNamespace, name string, curDevfileCtx devfileCtx.DevfileCtx) (DevfileObj, error) { + + kclient, namespace, err := getDevfileFromKubeCRD(providedNamespace) + if err != nil { + return DevfileObj{}, err + } + + var dwTemplate v1.DevWorkspaceTemplate + namespacedName := types.NamespacedName{ + Name: name, + Namespace: namespace, + } + err = kclient.Get(context.TODO(), namespacedName, &dwTemplate) + + if err != nil { + return DevfileObj{}, err + } + data, err := json.Marshal(dwTemplate) + if err != nil { + return DevfileObj{}, err + } + + return ParseDevfile(ParserArgs{Data: data, RegistryURLs: curDevfileCtx.GetRegistryURLs()}) +} + +func getDevfileFromKubeCRD(namespace string) (client.Client, string, error) { + cfg, err := config.GetConfig() + if err != nil { + return nil, "", err + } + // Instantiate an instance of conroller-runtime client + controllerClient, err := client.New(cfg, client.Options{}) + if err != nil { + return nil, "", err + } + + if namespace == "" { + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + configOverrides := &clientcmd.ConfigOverrides{} + config := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) + namespace, _, err = config.Namespace() + if err != nil { + return nil, "", fmt.Errorf("kubernetes namespace is not provided, and cannot get current running cluster's namespace: %v", err) + } + } + + return controllerClient, namespace, nil +} From 8d52b9b65bf206c9b4a2edf36a98f38629f8b42b Mon Sep 17 00:00:00 2001 From: Stephanie Date: Wed, 31 Mar 2021 17:59:58 -0400 Subject: [PATCH 2/9] before convert uriMap to contextresolver Signed-off-by: Stephanie --- pkg/devfile/parser/context/context.go | 41 +++++++++++++++++++ pkg/devfile/parser/parse.go | 50 ++++++++++++++++++++--- pkg/devfile/parser/resolutionContext.go | 54 +++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 pkg/devfile/parser/resolutionContext.go diff --git a/pkg/devfile/parser/context/context.go b/pkg/devfile/parser/context/context.go index 327fc433..c0e68b87 100644 --- a/pkg/devfile/parser/context/context.go +++ b/pkg/devfile/parser/context/context.go @@ -6,7 +6,9 @@ import ( "os" "path" "path/filepath" + "sigs.k8s.io/controller-runtime/pkg/client" "strings" + "context" "github.com/devfile/library/pkg/testingutil/filesystem" "github.com/devfile/library/pkg/util" @@ -42,6 +44,15 @@ type DevfileCtx struct { // registry URLs list registryURLs []string + + // default namespace to resolve kubernetes import reference + defaultNameSpace string + + // kubeContext is the context used for making Kubernetes requests + kubeContext context.Context + + // k8sClient is the Kubernetes client instance used for interacting with a cluster + k8sClient client.Client } // NewDevfileCtx returns a new DevfileCtx type object @@ -185,3 +196,33 @@ func (d *DevfileCtx) GetRegistryURLs() []string { func (d *DevfileCtx) SetRegistryURLs(registryURLs []string) { d.registryURLs = registryURLs } + +// GetDefaultNameSpace func returns current devfile default namespace +func (d *DevfileCtx) GetDefaultNameSpace() string { + return d.defaultNameSpace +} + +// SetDefaultNameSpace set default namespace in the devfile ctx +func (d *DevfileCtx) SetDefaultNameSpace(defaultNameSpace string) { + d.defaultNameSpace = defaultNameSpace +} + +// SetKubeContext set context in the devfile ctx +func (d *DevfileCtx) SetKubeContext(kubeContext context.Context) { + d.kubeContext = kubeContext +} + +// GetKubeContext func returns current devfile context to make Kubernetes request +func (d *DevfileCtx) GetKubeContext() context.Context { + return d.kubeContext +} + +// SetKubeContext set Kubernetes client instance in the devfile ctx +func (d *DevfileCtx) SetK8sClient(k8sClient client.Client) { + d.k8sClient = k8sClient +} + +// GetK8sClient func returns current devfile Kubernetes client instance to interact with a cluster +func (d *DevfileCtx) GetK8sClient() client.Client { + return d.k8sClient +} \ No newline at end of file diff --git a/pkg/devfile/parser/parse.go b/pkg/devfile/parser/parse.go index 7bda3b73..d20b0b27 100644 --- a/pkg/devfile/parser/parse.go +++ b/pkg/devfile/parser/parse.go @@ -74,11 +74,19 @@ type ParserArgs struct { // RegistryURLs is a list of registry hosts which parser should pull parent devfile from. // If registryUrl is defined in devfile, this list will be ignored. RegistryURLs []string + // DefaultNamespace is the default namespace to use + // If namespace is defined under ImportReferences.Kubernetes, this namespace will be ignored. + DefaultNameSpace string + // Context is the context used for making Kubernetes requests + Context context.Context + // K8sClient is the Kubernetes client instance used for interacting with a cluster + K8sClient client.Client } // ParseDevfile func populates the devfile data, parses and validates the devfile integrity. // Creates devfile context and runtime objects func ParseDevfile(args ParserArgs) (d DevfileObj, err error) { + resolutionCtx := &resolutionContextTree{} if args.Data != nil { d.Ctx = devfileCtx.DevfileCtx{} err = d.Ctx.SetDevfileContentFromBytes(args.Data) @@ -87,8 +95,10 @@ func ParseDevfile(args ParserArgs) (d DevfileObj, err error) { } } else if args.Path != "" { d.Ctx = devfileCtx.NewDevfileCtx(args.Path) + resolutionCtx.importReference.Uri = args.Path } else if args.URL != "" { d.Ctx = devfileCtx.NewURLDevfileCtx(args.URL) + resolutionCtx.importReference.Uri = args.URL } else { return d, errors.Wrap(err, "the devfile source is not provided") } @@ -96,16 +106,25 @@ func ParseDevfile(args ParserArgs) (d DevfileObj, err error) { if args.RegistryURLs != nil { d.Ctx.SetRegistryURLs(args.RegistryURLs) } + if args.DefaultNameSpace != "" { + d.Ctx.SetDefaultNameSpace(args.DefaultNameSpace) + } + if args.Context != nil { + d.Ctx.SetKubeContext(args.Context) + } + if args.K8sClient != nil { + d.Ctx.SetK8sClient(args.K8sClient) + } flattenedDevfile := true if args.FlattenedDevfile != nil { flattenedDevfile = *args.FlattenedDevfile } - return populateAndParseDevfile(d, flattenedDevfile) + return populateAndParseDevfile(d,resolutionCtx, flattenedDevfile) } -func populateAndParseDevfile(d DevfileObj, flattenedDevfile bool) (DevfileObj, error) { +func populateAndParseDevfile(d DevfileObj, resolveCtx *resolutionContextTree, flattenedDevfile bool) (DevfileObj, error) { var err error // Fill the fields of DevfileCtx struct @@ -275,6 +294,11 @@ func parseFromURI(uri string, curDevfileCtx devfileCtx.DevfileCtx) (DevfileObj, d.Ctx = devfileCtx.NewURLDevfileCtx(u.String()) } d.Ctx.SetURIMap(curDevfileCtx.GetURIMap()) + d.Ctx.SetKubeContext(curDevfileCtx.GetKubeContext()) + d.Ctx.SetRegistryURLs(curDevfileCtx.GetRegistryURLs()) + d.Ctx.SetK8sClient(curDevfileCtx.GetK8sClient()) + d.Ctx.SetDefaultNameSpace(curDevfileCtx.GetDefaultNameSpace()) + return populateAndParseDevfile(d, true) } @@ -284,12 +308,23 @@ func parseFromRegistry(Id, registryURL string, curDevfileCtx devfileCtx.DevfileC if err != nil { return DevfileObj{}, err } - return ParseDevfile(ParserArgs{Data: devfileContent, RegistryURLs: curDevfileCtx.GetRegistryURLs()}) + + return ParseDevfile( ParserArgs{ + Data: devfileContent, + RegistryURLs: curDevfileCtx.GetRegistryURLs(), + DefaultNameSpace: curDevfileCtx.GetDefaultNameSpace(), + Context: curDevfileCtx.GetKubeContext(), + K8sClient:curDevfileCtx.GetK8sClient()} ) } else if curDevfileCtx.GetRegistryURLs() != nil { for _, registry := range curDevfileCtx.GetRegistryURLs() { devfileContent, err := getDevfileFromRegistry(Id, registry) if devfileContent != nil && err == nil { - return ParseDevfile(ParserArgs{Data: devfileContent, RegistryURLs: curDevfileCtx.GetRegistryURLs()}) + return ParseDevfile( ParserArgs{ + Data: devfileContent, + RegistryURLs: curDevfileCtx.GetRegistryURLs(), + DefaultNameSpace: curDevfileCtx.GetDefaultNameSpace(), + Context: curDevfileCtx.GetKubeContext(), + K8sClient:curDevfileCtx.GetK8sClient()} ) } } } else { @@ -331,7 +366,12 @@ func parseFromKubeCRD(providedNamespace, name string, curDevfileCtx devfileCtx.D return DevfileObj{}, err } - return ParseDevfile(ParserArgs{Data: data, RegistryURLs: curDevfileCtx.GetRegistryURLs()}) + return ParseDevfile( ParserArgs{ + Data: data, + RegistryURLs: curDevfileCtx.GetRegistryURLs(), + DefaultNameSpace: curDevfileCtx.GetDefaultNameSpace(), + Context: curDevfileCtx.GetKubeContext(), + K8sClient:curDevfileCtx.GetK8sClient()} ) } func getDevfileFromKubeCRD(namespace string) (client.Client, string, error) { diff --git a/pkg/devfile/parser/resolutionContext.go b/pkg/devfile/parser/resolutionContext.go new file mode 100644 index 00000000..00084bae --- /dev/null +++ b/pkg/devfile/parser/resolutionContext.go @@ -0,0 +1,54 @@ +package parser + +import ( + "fmt" + "reflect" + + v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" +) + +// resolutionContextTree is a recursive structure representing information about the devfile that is +// lost when flattening (e.g. plugins, parents) +type resolutionContextTree struct { + importReference v1.ImportReference + parentNode *resolutionContextTree +} + +// addPlugin adds a plugin component to the resolution context. +func (t *resolutionContextTree) addPlugin(name string, importReference v1.ImportReference) *resolutionContextTree { + newNode := &resolutionContextTree{ + importReference: importReference, + parentNode: t, + } + return newNode +} + +// hasCycle checks if the current resolutionContextTree has a cycle +func (t *resolutionContextTree) hasCycle() error { + var seenRefs []v1.ImportReference + currNode := t + for currNode.parentNode != nil { + for _, seenRef := range seenRefs { + if reflect.DeepEqual(seenRef, currNode.importReference) { + return fmt.Errorf("devfile has an cycle in references: %v", currNode.importReference) + } + } + seenRefs = append(seenRefs, currNode.importReference) + currNode = currNode.parentNode + } + return nil +} + +//// formatImportCycle is a utility method for formatting a cycle that has been detected. Output is formatted as +//// plugin1 -> plugin2 -> plugin3 -> plugin1, where pluginX are component names. +//func formatImportCycle(end *resolutionContextTree) string { +// cycle := fmt.Sprintf("%v", end.importReference) +// for end.parentNode != nil { +// end = end.parentNode +// if end.parentNode == nil { +// end.componentName = "devfile" +// } +// cycle = fmt.Sprintf("%s -> %s", end.componentName, cycle) +// } +// return cycle +//} \ No newline at end of file From 35f703b3b960d1ec40ac963740552f4236f6691a Mon Sep 17 00:00:00 2001 From: Stephanie Date: Wed, 31 Mar 2021 20:00:58 -0400 Subject: [PATCH 3/9] delete values provided by consumers from devfileCtx, and use resolverTool struct to save instead Signed-off-by: Stephanie --- pkg/devfile/parser/context/context.go | 81 ----------- pkg/devfile/parser/parse.go | 182 ++++++++++++------------ pkg/devfile/parser/parse_test.go | 161 +++++++++++++-------- pkg/devfile/parser/resolutionContext.go | 45 +++--- 4 files changed, 221 insertions(+), 248 deletions(-) diff --git a/pkg/devfile/parser/context/context.go b/pkg/devfile/parser/context/context.go index c0e68b87..7d5959b2 100644 --- a/pkg/devfile/parser/context/context.go +++ b/pkg/devfile/parser/context/context.go @@ -6,9 +6,7 @@ import ( "os" "path" "path/filepath" - "sigs.k8s.io/controller-runtime/pkg/client" "strings" - "context" "github.com/devfile/library/pkg/testingutil/filesystem" "github.com/devfile/library/pkg/util" @@ -38,21 +36,6 @@ type DevfileCtx struct { // filesystem for devfile fs filesystem.Filesystem - - // trace of all url referenced - uriMap map[string]bool - - // registry URLs list - registryURLs []string - - // default namespace to resolve kubernetes import reference - defaultNameSpace string - - // kubeContext is the context used for making Kubernetes requests - kubeContext context.Context - - // k8sClient is the Kubernetes client instance used for interacting with a cluster - k8sClient client.Client } // NewDevfileCtx returns a new DevfileCtx type object @@ -99,13 +82,6 @@ func (d *DevfileCtx) Populate() (err error) { return err } klog.V(4).Infof("absolute devfile path: '%s'", d.absPath) - if d.uriMap == nil { - d.uriMap = make(map[string]bool) - } - if d.uriMap[d.absPath] { - return fmt.Errorf("URI %v is recursively referenced", d.absPath) - } - d.uriMap[d.absPath] = true // Read and save devfile content if err := d.SetDevfileContent(); err != nil { return err @@ -129,13 +105,6 @@ func (d *DevfileCtx) PopulateFromURL() (err error) { } d.url = u.String() } - if d.uriMap == nil { - d.uriMap = make(map[string]bool) - } - if d.uriMap[d.url] { - return fmt.Errorf("URI %v is recursively referenced", d.url) - } - d.uriMap[d.url] = true // Read and save devfile content if err := d.SetDevfileContent(); err != nil { return err @@ -176,53 +145,3 @@ func (d *DevfileCtx) SetAbsPath() (err error) { return nil } - -// GetURIMap func returns current devfile uri map -func (d *DevfileCtx) GetURIMap() map[string]bool { - return d.uriMap -} - -// SetURIMap set uri map in the devfile ctx -func (d *DevfileCtx) SetURIMap(uriMap map[string]bool) { - d.uriMap = uriMap -} - -// GetRegistryURLs func returns current devfile registry URLs -func (d *DevfileCtx) GetRegistryURLs() []string { - return d.registryURLs -} - -// SetRegistryURLs set registry URLs in the devfile ctx -func (d *DevfileCtx) SetRegistryURLs(registryURLs []string) { - d.registryURLs = registryURLs -} - -// GetDefaultNameSpace func returns current devfile default namespace -func (d *DevfileCtx) GetDefaultNameSpace() string { - return d.defaultNameSpace -} - -// SetDefaultNameSpace set default namespace in the devfile ctx -func (d *DevfileCtx) SetDefaultNameSpace(defaultNameSpace string) { - d.defaultNameSpace = defaultNameSpace -} - -// SetKubeContext set context in the devfile ctx -func (d *DevfileCtx) SetKubeContext(kubeContext context.Context) { - d.kubeContext = kubeContext -} - -// GetKubeContext func returns current devfile context to make Kubernetes request -func (d *DevfileCtx) GetKubeContext() context.Context { - return d.kubeContext -} - -// SetKubeContext set Kubernetes client instance in the devfile ctx -func (d *DevfileCtx) SetK8sClient(k8sClient client.Client) { - d.k8sClient = k8sClient -} - -// GetK8sClient func returns current devfile Kubernetes client instance to interact with a cluster -func (d *DevfileCtx) GetK8sClient() client.Client { - return d.k8sClient -} \ No newline at end of file diff --git a/pkg/devfile/parser/parse.go b/pkg/devfile/parser/parse.go index d20b0b27..a6c0d441 100644 --- a/pkg/devfile/parser/parse.go +++ b/pkg/devfile/parser/parse.go @@ -10,7 +10,6 @@ import ( "net/url" "path" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/config" "strings" devfileCtx "github.com/devfile/library/pkg/devfile/parser/context" @@ -28,7 +27,7 @@ import ( // ParseDevfile func validates the devfile integrity. // Creates devfile context and runtime objects -func parseDevfile(d DevfileObj, flattenedDevfile bool) (DevfileObj, error) { +func parseDevfile(d DevfileObj, resolveCtx *resolutionContextTree, tool resolverTools, flattenedDevfile bool) (DevfileObj, error) { // Validate devfile err := d.Ctx.Validate() @@ -49,7 +48,7 @@ func parseDevfile(d DevfileObj, flattenedDevfile bool) (DevfileObj, error) { } if flattenedDevfile { - err = parseParentAndPlugin(d) + err = parseParentAndPlugin(d, resolveCtx, tool) if err != nil { return DevfileObj{}, err } @@ -86,7 +85,6 @@ type ParserArgs struct { // ParseDevfile func populates the devfile data, parses and validates the devfile integrity. // Creates devfile context and runtime objects func ParseDevfile(args ParserArgs) (d DevfileObj, err error) { - resolutionCtx := &resolutionContextTree{} if args.Data != nil { d.Ctx = devfileCtx.DevfileCtx{} err = d.Ctx.SetDevfileContentFromBytes(args.Data) @@ -95,25 +93,17 @@ func ParseDevfile(args ParserArgs) (d DevfileObj, err error) { } } else if args.Path != "" { d.Ctx = devfileCtx.NewDevfileCtx(args.Path) - resolutionCtx.importReference.Uri = args.Path } else if args.URL != "" { d.Ctx = devfileCtx.NewURLDevfileCtx(args.URL) - resolutionCtx.importReference.Uri = args.URL } else { return d, errors.Wrap(err, "the devfile source is not provided") } - if args.RegistryURLs != nil { - d.Ctx.SetRegistryURLs(args.RegistryURLs) - } - if args.DefaultNameSpace != "" { - d.Ctx.SetDefaultNameSpace(args.DefaultNameSpace) - } - if args.Context != nil { - d.Ctx.SetKubeContext(args.Context) - } - if args.K8sClient != nil { - d.Ctx.SetK8sClient(args.K8sClient) + tool := resolverTools{ + defaultNamespace: args.DefaultNameSpace, + registryURLs: args.RegistryURLs, + context: args.Context, + k8sClient: args.K8sClient, } flattenedDevfile := true @@ -121,12 +111,27 @@ func ParseDevfile(args ParserArgs) (d DevfileObj, err error) { flattenedDevfile = *args.FlattenedDevfile } - return populateAndParseDevfile(d,resolutionCtx, flattenedDevfile) + return populateAndParseDevfile(d, &resolutionContextTree{}, tool, flattenedDevfile) } -func populateAndParseDevfile(d DevfileObj, resolveCtx *resolutionContextTree, flattenedDevfile bool) (DevfileObj, error) { - var err error +// resolverTools contains required structs and data for resolving remote components of a devfile (plugins and parents) +type resolverTools struct { + // DefaultNamespace is the default namespace to use for resolving Kubernetes ImportReferences that do not include one + defaultNamespace string + // RegistryURLs is a list of registry hosts which parser should pull parent devfile from. + // If registryUrl is defined in devfile, this list will be ignored. + registryURLs []string + // Context is the context used for making Kubernetes or HTTP requests + context context.Context + // K8sClient is the Kubernetes client instance used for interacting with a cluster + k8sClient client.Client +} +func populateAndParseDevfile(d DevfileObj, resolveCtx *resolutionContextTree, tool resolverTools, flattenedDevfile bool) (DevfileObj, error) { + var err error + if err = resolveCtx.hasCycle(); err != nil { + return DevfileObj{}, err + } // Fill the fields of DevfileCtx struct if d.Ctx.GetURL() != "" { err = d.Ctx.PopulateFromURL() @@ -139,7 +144,7 @@ func populateAndParseDevfile(d DevfileObj, resolveCtx *resolutionContextTree, fl return d, err } - return parseDevfile(d, flattenedDevfile) + return parseDevfile(d, resolveCtx, tool, flattenedDevfile) } // Parse func populates the flattened devfile data, parses and validates the devfile integrity. @@ -149,8 +154,7 @@ func Parse(path string) (d DevfileObj, err error) { // NewDevfileCtx d.Ctx = devfileCtx.NewDevfileCtx(path) - - return populateAndParseDevfile(d, true) + return populateAndParseDevfile(d, &resolutionContextTree{}, resolverTools{}, true) } // ParseRawDevfile populates the raw devfile data without overriding and merging @@ -158,8 +162,7 @@ func Parse(path string) (d DevfileObj, err error) { func ParseRawDevfile(path string) (d DevfileObj, err error) { // NewDevfileCtx d.Ctx = devfileCtx.NewDevfileCtx(path) - - return populateAndParseDevfile(d, false) + return populateAndParseDevfile(d, &resolutionContextTree{}, resolverTools{}, false) } // ParseFromURL func parses and validates the devfile integrity. @@ -167,7 +170,7 @@ func ParseRawDevfile(path string) (d DevfileObj, err error) { // Deprecated, use ParseDevfile() instead func ParseFromURL(url string) (d DevfileObj, err error) { d.Ctx = devfileCtx.NewURLDevfileCtx(url) - return populateAndParseDevfile(d, true) + return populateAndParseDevfile(d, &resolutionContextTree{}, resolverTools{}, true) } // ParseFromData func parses and validates the devfile integrity. @@ -179,10 +182,10 @@ func ParseFromData(data []byte) (d DevfileObj, err error) { if err != nil { return d, errors.Wrap(err, "failed to set devfile content from bytes") } - return populateAndParseDevfile(d, true) + return populateAndParseDevfile(d, &resolutionContextTree{}, resolverTools{}, true) } -func parseParentAndPlugin(d DevfileObj) (err error) { +func parseParentAndPlugin(d DevfileObj, resolveCtx *resolutionContextTree, tool resolverTools) (err error) { flattenedParent := &v1.DevWorkspaceTemplateSpecContent{} parent := d.Data.GetParent() if parent != nil { @@ -191,17 +194,17 @@ func parseParentAndPlugin(d DevfileObj) (err error) { var parentDevfileObj DevfileObj switch { case parent.Uri != "": - parentDevfileObj, err = parseFromURI(parent.Uri, d.Ctx) + parentDevfileObj, err = parseFromURI(parent.ImportReference, d.Ctx, resolveCtx, tool) if err != nil { return err } case parent.Id != "": - parentDevfileObj, err = parseFromRegistry(parent.Id, parent.RegistryUrl, d.Ctx) + parentDevfileObj, err = parseFromRegistry(parent.ImportReference, resolveCtx, tool) if err != nil { return err } case parent.Kubernetes != nil: - parentDevfileObj, err = parseFromKubeCRD(parent.Kubernetes.Namespace, parent.Kubernetes.Name, d.Ctx) + parentDevfileObj, err = parseFromKubeCRD(parent.ImportReference, resolveCtx, tool) default: return fmt.Errorf("parent URI or parent Id undefined, currently only URI and Id are suppported") } @@ -231,17 +234,17 @@ func parseParentAndPlugin(d DevfileObj) (err error) { var pluginDevfileObj DevfileObj switch { case plugin.Uri != "": - pluginDevfileObj, err = parseFromURI(plugin.Uri, d.Ctx) + pluginDevfileObj, err = parseFromURI(plugin.ImportReference, d.Ctx, resolveCtx, tool) if err != nil { return err } case plugin.Id != "": - pluginDevfileObj, err = parseFromRegistry(plugin.Id, plugin.RegistryUrl, d.Ctx) + pluginDevfileObj, err = parseFromRegistry(plugin.ImportReference, resolveCtx, tool) if err != nil { return err } case plugin.Kubernetes != nil: - pluginDevfileObj, err = parseFromKubeCRD(plugin.Kubernetes.Namespace, plugin.Kubernetes.Name, d.Ctx) + pluginDevfileObj, err = parseFromKubeCRD(plugin.ImportReference, resolveCtx, tool) default: return fmt.Errorf("plugin URI or plugin Id undefined, currently only URI and Id are suppported") } @@ -268,7 +271,8 @@ func parseParentAndPlugin(d DevfileObj) (err error) { return nil } -func parseFromURI(uri string, curDevfileCtx devfileCtx.DevfileCtx) (DevfileObj, error) { +func parseFromURI(importReference v1.ImportReference, curDevfileCtx devfileCtx.DevfileCtx, resolveCtx *resolutionContextTree, tool resolverTools) (DevfileObj, error) { + uri := importReference.Uri // validate URI err := validation.ValidateURI(uri) if err != nil { @@ -277,13 +281,16 @@ func parseFromURI(uri string, curDevfileCtx devfileCtx.DevfileCtx) (DevfileObj, // NewDevfileCtx var d DevfileObj absoluteURL := strings.HasPrefix(uri, "http://") || strings.HasPrefix(uri, "https://") + var newUri string // relative path on disk if !absoluteURL && curDevfileCtx.GetAbsPath() != "" { d.Ctx = devfileCtx.NewDevfileCtx(path.Join(path.Dir(curDevfileCtx.GetAbsPath()), uri)) + newUri = path.Join(path.Dir(curDevfileCtx.GetAbsPath()), uri) } else if absoluteURL { // absolute URL address d.Ctx = devfileCtx.NewURLDevfileCtx(uri) + newUri = uri } else if curDevfileCtx.GetURL() != "" { // relative path to a URL u, err := url.Parse(curDevfileCtx.GetURL()) @@ -292,39 +299,43 @@ func parseFromURI(uri string, curDevfileCtx devfileCtx.DevfileCtx) (DevfileObj, } u.Path = path.Join(path.Dir(u.Path), uri) d.Ctx = devfileCtx.NewURLDevfileCtx(u.String()) + newUri = u.String() } - d.Ctx.SetURIMap(curDevfileCtx.GetURIMap()) - d.Ctx.SetKubeContext(curDevfileCtx.GetKubeContext()) - d.Ctx.SetRegistryURLs(curDevfileCtx.GetRegistryURLs()) - d.Ctx.SetK8sClient(curDevfileCtx.GetK8sClient()) - d.Ctx.SetDefaultNameSpace(curDevfileCtx.GetDefaultNameSpace()) + importReference.Uri = newUri + newCtx := resolveCtx.appendNode(importReference) - return populateAndParseDevfile(d, true) + return populateAndParseDevfile(d, newCtx, tool, true) } -func parseFromRegistry(Id, registryURL string, curDevfileCtx devfileCtx.DevfileCtx) (DevfileObj, error) { +func parseFromRegistry(importReference v1.ImportReference, resolveCtx *resolutionContextTree, tool resolverTools) (d DevfileObj, err error) { + d.Ctx = devfileCtx.DevfileCtx{} + Id := importReference.Id + registryURL := importReference.RegistryUrl if registryURL != "" { devfileContent, err := getDevfileFromRegistry(Id, registryURL) if err != nil { return DevfileObj{}, err } + err = d.Ctx.SetDevfileContentFromBytes(devfileContent) + if err != nil { + return d, errors.Wrap(err, "failed to set devfile content from bytes") + } + newCtx := resolveCtx.appendNode(importReference) + + return populateAndParseDevfile(d, newCtx, tool, true) - return ParseDevfile( ParserArgs{ - Data: devfileContent, - RegistryURLs: curDevfileCtx.GetRegistryURLs(), - DefaultNameSpace: curDevfileCtx.GetDefaultNameSpace(), - Context: curDevfileCtx.GetKubeContext(), - K8sClient:curDevfileCtx.GetK8sClient()} ) - } else if curDevfileCtx.GetRegistryURLs() != nil { - for _, registry := range curDevfileCtx.GetRegistryURLs() { + } else if tool.registryURLs != nil { + for _, registry := range tool.registryURLs { devfileContent, err := getDevfileFromRegistry(Id, registry) if devfileContent != nil && err == nil { - return ParseDevfile( ParserArgs{ - Data: devfileContent, - RegistryURLs: curDevfileCtx.GetRegistryURLs(), - DefaultNameSpace: curDevfileCtx.GetDefaultNameSpace(), - Context: curDevfileCtx.GetKubeContext(), - K8sClient:curDevfileCtx.GetK8sClient()} ) + err = d.Ctx.SetDevfileContentFromBytes(devfileContent) + if err != nil { + return d, errors.Wrap(err, "failed to set devfile content from bytes") + } + importReference.RegistryUrl = registry + newCtx := resolveCtx.appendNode(importReference) + + return populateAndParseDevfile(d, newCtx, tool, true) } } } else { @@ -344,19 +355,32 @@ func getDevfileFromRegistry(Id, registryURL string) ([]byte, error) { return util.HTTPGetRequest(param, 0) } -func parseFromKubeCRD(providedNamespace, name string, curDevfileCtx devfileCtx.DevfileCtx) (DevfileObj, error) { +func parseFromKubeCRD(importReference v1.ImportReference, resolveCtx *resolutionContextTree, tool resolverTools) (d DevfileObj, err error) { - kclient, namespace, err := getDevfileFromKubeCRD(providedNamespace) - if err != nil { - return DevfileObj{}, err + if tool.k8sClient == nil || tool.context == nil { + return DevfileObj{}, fmt.Errorf("Kubernetes client and context are required to parse from Kubernetes CRD") + } + namespace := importReference.Kubernetes.Namespace + // if namespace is not set in devfile, use default namespace provided in by consumer + if namespace == "" { + namespace = tool.defaultNamespace + } + // use current namespace if namespace is not set in devfile and not provided by consumer + if namespace == "" { + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + configOverrides := &clientcmd.ConfigOverrides{} + config := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) + namespace, _, err = config.Namespace() + if err != nil { + return DevfileObj{}, fmt.Errorf("kubernetes namespace is not provided, and cannot get current running cluster's namespace: %v", err) + } } - var dwTemplate v1.DevWorkspaceTemplate namespacedName := types.NamespacedName{ - Name: name, + Name: importReference.Kubernetes.Name, Namespace: namespace, } - err = kclient.Get(context.TODO(), namespacedName, &dwTemplate) + err = tool.k8sClient.Get(tool.context, namespacedName, &dwTemplate) if err != nil { return DevfileObj{}, err @@ -365,35 +389,13 @@ func parseFromKubeCRD(providedNamespace, name string, curDevfileCtx devfileCtx.D if err != nil { return DevfileObj{}, err } - - return ParseDevfile( ParserArgs{ - Data: data, - RegistryURLs: curDevfileCtx.GetRegistryURLs(), - DefaultNameSpace: curDevfileCtx.GetDefaultNameSpace(), - Context: curDevfileCtx.GetKubeContext(), - K8sClient:curDevfileCtx.GetK8sClient()} ) -} - -func getDevfileFromKubeCRD(namespace string) (client.Client, string, error) { - cfg, err := config.GetConfig() - if err != nil { - return nil, "", err - } - // Instantiate an instance of conroller-runtime client - controllerClient, err := client.New(cfg, client.Options{}) + err = d.Ctx.SetDevfileContentFromBytes(data) if err != nil { - return nil, "", err + return d, errors.Wrap(err, "failed to set devfile content from bytes") } + importReference.Kubernetes.Namespace = namespace + newCtx := resolveCtx.appendNode(importReference) - if namespace == "" { - loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() - configOverrides := &clientcmd.ConfigOverrides{} - config := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) - namespace, _, err = config.Namespace() - if err != nil { - return nil, "", fmt.Errorf("kubernetes namespace is not provided, and cannot get current running cluster's namespace: %v", err) - } - } + return populateAndParseDevfile(d, newCtx, tool, true) - return controllerClient, namespace, nil } diff --git a/pkg/devfile/parser/parse_test.go b/pkg/devfile/parser/parse_test.go index 164b9ea7..d2159ccb 100644 --- a/pkg/devfile/parser/parse_test.go +++ b/pkg/devfile/parser/parse_test.go @@ -2164,7 +2164,7 @@ func Test_parseParentAndPluginFromURI(t *testing.T) { tt.args.devFileObj.Data.AddComponents(plugincomp) } - err := parseParentAndPlugin(tt.args.devFileObj) + err := parseParentAndPlugin(tt.args.devFileObj, &resolutionContextTree{}, resolverTools{}) // Unexpected error if (err != nil) != tt.wantErr { @@ -2389,8 +2389,10 @@ func Test_parseParentAndPlugin_RecursivelyReference_withMultipleURI(t *testing.T testServer3.Start() defer testServer3.Close() t.Run("it should error out if URI is recursively referenced with multiple references", func(t *testing.T) { - err := parseParentAndPlugin(devFileObj) - expectedErr := fmt.Sprintf("URI %v%v/devfile.yaml is recursively referenced", httpPrefix, uri1) + err := parseParentAndPlugin(devFileObj, &resolutionContextTree{}, resolverTools{}) + // devfile has an cycle in references: main devfile -> uri: http://127.0.0.1:8080 -> uri: http://127.0.0.1:9090 -> uri: http://127.0.0.1:8090 -> uri: http://127.0.0.1:8080 + expectedErr := fmt.Sprintf("devfile has an cycle in references: main devfile -> uri: %s%s -> uri: %s%s -> uri: %s%s -> uri: %s%s", httpPrefix, uri1, + httpPrefix, uri2, httpPrefix, uri3, httpPrefix, uri1) // Unexpected error if err == nil || !reflect.DeepEqual(expectedErr, err.Error()) { t.Errorf("Test_parseParentAndPlugin_RecursivelyReference_withMultipleURI() unexpected error = %v", err) @@ -2403,6 +2405,9 @@ func Test_parseParentAndPlugin_RecursivelyReference_withMultipleURI(t *testing.T func Test_parseParentFromRegistry(t *testing.T) { const validRegistry = "127.0.0.1:8080" const invalidRegistry = "invalid-registry.io" + tool := resolverTools{ + registryURLs: []string{"http://" + validRegistry}, + } parentDevfile := DevfileObj{ Data: &v2.DevfileV2{ Devfile: v1.Devfile{ @@ -2576,9 +2581,6 @@ func Test_parseParentFromRegistry(t *testing.T) { }, } - ctxWithRegistry := devfileCtx.NewDevfileCtx(OutputDevfileYamlPath) - ctxWithRegistry.SetRegistryURLs([]string{"http://" + validRegistry}) - tests := []struct { name string mainDevfile DevfileObj @@ -2604,7 +2606,6 @@ func Test_parseParentFromRegistry(t *testing.T) { { name: "it should override the requested parent's data from registryURLs set in context and add the local devfile's data", mainDevfile: DevfileObj{ - Ctx: ctxWithRegistry, Data: &v2.DevfileV2{ Devfile: mainDevfileContent, }, @@ -2662,7 +2663,7 @@ func Test_parseParentFromRegistry(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err := parseParentAndPlugin(tt.mainDevfile) + err := parseParentAndPlugin(tt.mainDevfile, &resolutionContextTree{}, tool) // Unexpected error if (err != nil) != tt.wantErr { @@ -2833,23 +2834,31 @@ func Test_parseFromURI(t *testing.T) { defer testServer.Close() tests := []struct { - name string - curDevfileCtx devfileCtx.DevfileCtx - uri string - wantDevFile DevfileObj - wantErr bool + name string + curDevfileCtx devfileCtx.DevfileCtx + importReference v1.ImportReference + wantDevFile DevfileObj + wantErr bool }{ { name: "should be able to parse from relative uri on local disk", curDevfileCtx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), wantDevFile: localDevfile, - uri: localRelativeURI, + importReference: v1.ImportReference{ + ImportReferenceUnion: v1.ImportReferenceUnion{ + Uri: localRelativeURI, + }, + }, }, { name: "should be able to parse relative uri from URL", curDevfileCtx: parentDevfile.Ctx, wantDevFile: relativeParentDevfile, - uri: localRelativeURI, + importReference: v1.ImportReference{ + ImportReferenceUnion: v1.ImportReferenceUnion{ + Uri: localRelativeURI, + }, + }, }, { name: "should fail if no path or url has been set for devfile ctx", @@ -2859,20 +2868,32 @@ func Test_parseFromURI(t *testing.T) { { name: "should fail if file not exist", curDevfileCtx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), - uri: notExistURI, - wantErr: true, + importReference: v1.ImportReference{ + ImportReferenceUnion: v1.ImportReferenceUnion{ + Uri: notExistURI, + }, + }, + wantErr: true, }, { name: "should fail if url not exist", curDevfileCtx: devfileCtx.NewURLDevfileCtx(httpPrefix + uri1), - uri: notExistURI, - wantErr: true, + importReference: v1.ImportReference{ + ImportReferenceUnion: v1.ImportReferenceUnion{ + Uri: notExistURI, + }, + }, + wantErr: true, }, { name: "should fail if with invalid URI format", curDevfileCtx: devfileCtx.NewURLDevfileCtx(OutputDevfileYamlPath), - uri: invalidURL, - wantErr: true, + importReference: v1.ImportReference{ + ImportReferenceUnion: v1.ImportReferenceUnion{ + Uri: invalidURL, + }, + }, + wantErr: true, }, } for _, tt := range tests { @@ -2885,7 +2906,7 @@ func Test_parseFromURI(t *testing.T) { return } } - got, err := parseFromURI(tt.uri, tt.curDevfileCtx) + got, err := parseFromURI(tt.importReference, tt.curDevfileCtx, &resolutionContextTree{}, resolverTools{}) if tt.wantErr == (err == nil) { t.Errorf("Test_parseFromURI() error = %v, wantErr %v", err, tt.wantErr) return @@ -2907,9 +2928,6 @@ func Test_parseFromRegistry(t *testing.T) { registryId = "nodejs" ) - ctxWithRegistry := devfileCtx.NewDevfileCtx(OutputDevfileYamlPath) - ctxWithRegistry.SetRegistryURLs([]string{"http://" + registry}) - parentDevfile := DevfileObj{ Data: &v2.DevfileV2{ Devfile: v1.Devfile{ @@ -2970,58 +2988,79 @@ func Test_parseFromRegistry(t *testing.T) { defer testServer.Close() tests := []struct { - name string - curDevfileCtx devfileCtx.DevfileCtx - registryUrl string - registryId string - wantDevFile DevfileObj - wantErr bool + name string + curDevfileCtx devfileCtx.DevfileCtx + importReference v1.ImportReference + tool resolverTools + wantDevFile DevfileObj + wantErr bool }{ { - name: "should fail if provided registryUrl does not have protocol prefix", - curDevfileCtx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), - wantDevFile: parentDevfile, - registryUrl: registry, - registryId: registryId, - wantErr: true, + name: "should fail if provided registryUrl does not have protocol prefix", + wantDevFile: parentDevfile, + importReference: v1.ImportReference{ + ImportReferenceUnion: v1.ImportReferenceUnion{ + Id: registryId, + }, + RegistryUrl: registry, + }, + wantErr: true, }, { - name: "should be able to parse from provided registryUrl with prefix", - curDevfileCtx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), - wantDevFile: parentDevfile, - registryUrl: httpPrefix + registry, - registryId: registryId, + name: "should be able to parse from provided registryUrl with prefix", + wantDevFile: parentDevfile, + importReference: v1.ImportReference{ + ImportReferenceUnion: v1.ImportReferenceUnion{ + Id: registryId, + }, + RegistryUrl: httpPrefix + registry, + }, }, { - name: "should be able to parse from registry URL defined in ctx", - curDevfileCtx: ctxWithRegistry, - wantDevFile: parentDevfile, - registryId: registryId, + name: "should be able to parse from registry URL defined in tool", + wantDevFile: parentDevfile, + importReference: v1.ImportReference{ + ImportReferenceUnion: v1.ImportReferenceUnion{ + Id: registryId, + }, + }, + tool: resolverTools{ + registryURLs: []string{"http://" + registry}, + }, }, { - name: "should fail if registryId does not exist", - curDevfileCtx: devfileCtx.NewURLDevfileCtx(OutputDevfileYamlPath), - registryUrl: registry, - registryId: notExistId, - wantErr: true, + name: "should fail if registryId does not exist", + importReference: v1.ImportReference{ + ImportReferenceUnion: v1.ImportReferenceUnion{ + Id: notExistId, + }, + RegistryUrl: httpPrefix + registry, + }, + wantErr: true, }, { - name: "should fail if registryUrl is not provided, and no registry URLs has been set in ctx", - curDevfileCtx: devfileCtx.NewURLDevfileCtx(OutputDevfileYamlPath), - registryId: registryId, - wantErr: true, + name: "should fail if registryUrl is not provided, and no registry URLs has been set in tool", + importReference: v1.ImportReference{ + ImportReferenceUnion: v1.ImportReferenceUnion{ + Id: registryId, + }, + }, + wantErr: true, }, { - name: "should fail if registryUrl is invalid", - curDevfileCtx: devfileCtx.NewURLDevfileCtx(OutputDevfileYamlPath), - registryUrl: invalidRegistry, - registryId: registryId, - wantErr: true, + name: "should fail if registryUrl is invalid", + importReference: v1.ImportReference{ + ImportReferenceUnion: v1.ImportReferenceUnion{ + Id: notExistId, + }, + RegistryUrl: httpPrefix + invalidRegistry, + }, + wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := parseFromRegistry(tt.registryId, tt.registryUrl, tt.curDevfileCtx) + got, err := parseFromRegistry(tt.importReference, &resolutionContextTree{}, tt.tool) if tt.wantErr == (err == nil) { t.Errorf("Test_parseFromRegistry() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/pkg/devfile/parser/resolutionContext.go b/pkg/devfile/parser/resolutionContext.go index 00084bae..1237ecdb 100644 --- a/pkg/devfile/parser/resolutionContext.go +++ b/pkg/devfile/parser/resolutionContext.go @@ -14,8 +14,8 @@ type resolutionContextTree struct { parentNode *resolutionContextTree } -// addPlugin adds a plugin component to the resolution context. -func (t *resolutionContextTree) addPlugin(name string, importReference v1.ImportReference) *resolutionContextTree { +// appendNode adds a new node to the resolution context. +func (t *resolutionContextTree) appendNode(importReference v1.ImportReference) *resolutionContextTree { newNode := &resolutionContextTree{ importReference: importReference, parentNode: t, @@ -30,7 +30,7 @@ func (t *resolutionContextTree) hasCycle() error { for currNode.parentNode != nil { for _, seenRef := range seenRefs { if reflect.DeepEqual(seenRef, currNode.importReference) { - return fmt.Errorf("devfile has an cycle in references: %v", currNode.importReference) + return fmt.Errorf("devfile has an cycle in references: %v", formatImportCycle(t)) } } seenRefs = append(seenRefs, currNode.importReference) @@ -39,16 +39,29 @@ func (t *resolutionContextTree) hasCycle() error { return nil } -//// formatImportCycle is a utility method for formatting a cycle that has been detected. Output is formatted as -//// plugin1 -> plugin2 -> plugin3 -> plugin1, where pluginX are component names. -//func formatImportCycle(end *resolutionContextTree) string { -// cycle := fmt.Sprintf("%v", end.importReference) -// for end.parentNode != nil { -// end = end.parentNode -// if end.parentNode == nil { -// end.componentName = "devfile" -// } -// cycle = fmt.Sprintf("%s -> %s", end.componentName, cycle) -// } -// return cycle -//} \ No newline at end of file +// formatImportCycle is a utility method for formatting a cycle that has been detected. Output is formatted as +// plugin1 -> plugin2 -> plugin3 -> plugin1, where pluginX are component names. +func formatImportCycle(end *resolutionContextTree) string { + cycle := resolveImportReference(end.importReference) + for end.parentNode != nil { + end = end.parentNode + cycle = fmt.Sprintf("%s -> %s", resolveImportReference(end.importReference), cycle) + } + return cycle +} + +func resolveImportReference(importReference v1.ImportReference) string { + if !reflect.DeepEqual(importReference, v1.ImportReference{}) { + switch { + case importReference.Uri != "": + return fmt.Sprintf("uri: %s", importReference.Uri) + case importReference.Id != "": + return fmt.Sprintf("id: %s, registryURL: %s", importReference.Id, importReference.RegistryUrl) + case importReference.Kubernetes != nil: + return fmt.Sprintf("name: %s, namespace: %s", importReference.Kubernetes.Name, importReference.Kubernetes.Namespace) + } + + } + // the first node + return "main devfile" +} From d87add6061864b77c3a43bf170964035c6723f89 Mon Sep 17 00:00:00 2001 From: Stephanie Date: Tue, 6 Apr 2021 14:58:05 -0400 Subject: [PATCH 4/9] add fake k8sclient Signed-off-by: Stephanie --- pkg/devfile/parser/parse_test.go | 79 ++++++++++++++++++++++++++++++++ pkg/testingutil/k8sClient.go | 31 +++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 pkg/testingutil/k8sClient.go diff --git a/pkg/devfile/parser/parse_test.go b/pkg/devfile/parser/parse_test.go index d2159ccb..7e83dea0 100644 --- a/pkg/devfile/parser/parse_test.go +++ b/pkg/devfile/parser/parse_test.go @@ -16,6 +16,7 @@ import ( devfilepkg "github.com/devfile/api/v2/pkg/devfile" devfileCtx "github.com/devfile/library/pkg/devfile/parser/context" v2 "github.com/devfile/library/pkg/devfile/parser/data/v2" + "github.com/devfile/library/pkg/testingutil" "github.com/ghodss/yaml" "github.com/kylelemons/godebug/pretty" ) @@ -3072,3 +3073,81 @@ func Test_parseFromRegistry(t *testing.T) { }) } } + +func Test_parseFromKubeCRD(t *testing.T) { + const ( + registry = "127.0.0.1:8080" + httpPrefix = "http://" + notExistId = "notexist" + invalidRegistry = "http//invalid.com" + registryId = "nodejs" + ) + + parentDevfile := DevfileObj{ + Data: &v2.DevfileV2{ + Devfile: v1.Devfile{ + DevfileHeader: devfilepkg.DevfileHeader{ + SchemaVersion: schemaV200, + }, + DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: v1.DevWorkspaceTemplateSpecContent{ + Components: []v1.Component{ + { + Name: "runtime2", + ComponentUnion: v1.ComponentUnion{ + Volume: &v1.VolumeComponent{ + Volume: v1.Volume{ + Size: "500Mi", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + tests := []struct { + name string + curDevfileCtx devfileCtx.DevfileCtx + importReference v1.ImportReference + devWorkspaceResources map[string]v1.DevWorkspaceTemplate + errors map[string]string + wantDevFile DevfileObj + wantErr bool + }{ + { + name: "should fail if provided registryUrl does not have protocol prefix", + wantDevFile: parentDevfile, + importReference: v1.ImportReference{ + ImportReferenceUnion: v1.ImportReferenceUnion{ + Id: registryId, + }, + RegistryUrl: registry, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testK8sClient := &testingutil.FakeK8sClient{ + DevWorkspaceResources: tt.devWorkspaceResources, + Errors: tt.errors, + } + tool := resolverTools{ + k8sClient: testK8sClient, + } + got, err := parseFromKubeCRD(tt.importReference, &resolutionContextTree{}, tool) + if tt.wantErr == (err == nil) { + t.Errorf("Test_parseFromRegistry() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if err == nil && !reflect.DeepEqual(got.Data, tt.wantDevFile.Data) { + t.Errorf("wanted: %v, got: %v, difference at %v", tt.wantDevFile, got, pretty.Compare(tt.wantDevFile, got)) + } + }) + } +} diff --git a/pkg/testingutil/k8sClient.go b/pkg/testingutil/k8sClient.go new file mode 100644 index 00000000..60993adf --- /dev/null +++ b/pkg/testingutil/k8sClient.go @@ -0,0 +1,31 @@ +package testingutil + +import ( + "context" + "errors" + "fmt" + + "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type FakeK8sClient struct { + client.Client // To satisfy interface; override all used methods + DevWorkspaceResources map[string]v1alpha2.DevWorkspaceTemplate + Errors map[string]string +} + +func (client *FakeK8sClient) Get(_ context.Context, namespacedName client.ObjectKey, obj client.Object) error { + template, ok := obj.(*v1alpha2.DevWorkspaceTemplate) + if !ok { + return fmt.Errorf("called Get() in fake client with non-DevWorkspaceTemplate") + } + if element, ok := client.DevWorkspaceResources[namespacedName.Name]; ok { + *template = element + return nil + } + if err, ok := client.Errors[namespacedName.Name]; ok { + return errors.New(err) + } + return fmt.Errorf("test does not define an entry for %s", namespacedName.Name) +} From a194da8c1522de9fa0c12ec54fc99ae6b3121aa3 Mon Sep 17 00:00:00 2001 From: Stephanie Date: Tue, 6 Apr 2021 19:04:14 -0400 Subject: [PATCH 5/9] add unit test Signed-off-by: Stephanie --- pkg/devfile/parser/data/versions.go | 4 ++ pkg/devfile/parser/parse.go | 28 +++++++--- pkg/devfile/parser/parse_test.go | 82 +++++++++++++++++++---------- pkg/testingutil/k8sClient.go | 1 + 4 files changed, 80 insertions(+), 35 deletions(-) diff --git a/pkg/devfile/parser/data/versions.go b/pkg/devfile/parser/data/versions.go index c6cecc63..a331a12f 100644 --- a/pkg/devfile/parser/data/versions.go +++ b/pkg/devfile/parser/data/versions.go @@ -15,6 +15,7 @@ type supportedApiVersion string const ( APIVersion200 supportedApiVersion = "2.0.0" APIVersion210 supportedApiVersion = "2.1.0" + V2APIVersion supportedApiVersion = "v1alpha2" ) // ------------- Init functions ------------- // @@ -27,6 +28,7 @@ func init() { apiVersionToDevfileStruct = make(map[supportedApiVersion]reflect.Type) apiVersionToDevfileStruct[APIVersion200] = reflect.TypeOf(v2.DevfileV2{}) apiVersionToDevfileStruct[APIVersion210] = reflect.TypeOf(v2.DevfileV2{}) + apiVersionToDevfileStruct[V2APIVersion] = reflect.TypeOf(v2.DevfileV2{}) } // Map to store mappings between supported devfile API versions and respective devfile JSON schemas @@ -37,4 +39,6 @@ func init() { devfileApiVersionToJSONSchema = make(map[supportedApiVersion]string) devfileApiVersionToJSONSchema[APIVersion200] = v200.JsonSchema200 devfileApiVersionToJSONSchema[APIVersion210] = v210.JsonSchema210 + // should use hightest v2 schema version since it is expected to be backward compatible with the same api version + devfileApiVersionToJSONSchema[V2APIVersion] = v210.JsonSchema210 } diff --git a/pkg/devfile/parser/parse.go b/pkg/devfile/parser/parse.go index a6c0d441..92a4a1e2 100644 --- a/pkg/devfile/parser/parse.go +++ b/pkg/devfile/parser/parse.go @@ -381,21 +381,35 @@ func parseFromKubeCRD(importReference v1.ImportReference, resolveCtx *resolution Namespace: namespace, } err = tool.k8sClient.Get(tool.context, namespacedName, &dwTemplate) - if err != nil { return DevfileObj{}, err } - data, err := json.Marshal(dwTemplate) + + d, err = convertDevWorskapceTemplateToDevObj(dwTemplate) if err != nil { return DevfileObj{}, err } - err = d.Ctx.SetDevfileContentFromBytes(data) - if err != nil { - return d, errors.Wrap(err, "failed to set devfile content from bytes") - } + importReference.Kubernetes.Namespace = namespace newCtx := resolveCtx.appendNode(importReference) - return populateAndParseDevfile(d, newCtx, tool, true) + err = parseParentAndPlugin(d, newCtx, tool) + return d, err + +} + +func convertDevWorskapceTemplateToDevObj(dwTemplate v1.DevWorkspaceTemplate) (d DevfileObj, err error) { + // Todo: use dwTemplate.APIVersion to determine devfile schemaversion + // APIVersion: group/version + // for example: APIVersion: "workspace.devfile.io/v1alpha2" uses api version v1alpha2, and match to v2 schemas + tempList := strings.Split(dwTemplate.APIVersion, "/") + apiversion := tempList[len(tempList)-1] + d.Data, err = data.NewDevfileData(apiversion) + if err != nil { + return DevfileObj{}, err + } + d.Data.SetDevfileWorkspace(dwTemplate.Spec.DevWorkspaceTemplateSpecContent) + + return d, nil } diff --git a/pkg/devfile/parser/parse_test.go b/pkg/devfile/parser/parse_test.go index 7e83dea0..464c90fc 100644 --- a/pkg/devfile/parser/parse_test.go +++ b/pkg/devfile/parser/parse_test.go @@ -1,6 +1,7 @@ package parser import ( + "context" "fmt" "io/ioutil" "net" @@ -19,6 +20,7 @@ import ( "github.com/devfile/library/pkg/testingutil" "github.com/ghodss/yaml" "github.com/kylelemons/godebug/pretty" + kubev1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const schemaV200 = "2.0.0" @@ -3076,31 +3078,19 @@ func Test_parseFromRegistry(t *testing.T) { func Test_parseFromKubeCRD(t *testing.T) { const ( - registry = "127.0.0.1:8080" - httpPrefix = "http://" - notExistId = "notexist" - invalidRegistry = "http//invalid.com" - registryId = "nodejs" + namespace = "default" + name = "test-parent-k8s" + apiVersion = "testgroup/v1alpha2" ) - - parentDevfile := DevfileObj{ - Data: &v2.DevfileV2{ - Devfile: v1.Devfile{ - DevfileHeader: devfilepkg.DevfileHeader{ - SchemaVersion: schemaV200, - }, - DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ - DevWorkspaceTemplateSpecContent: v1.DevWorkspaceTemplateSpecContent{ - Components: []v1.Component{ - { - Name: "runtime2", - ComponentUnion: v1.ComponentUnion{ - Volume: &v1.VolumeComponent{ - Volume: v1.Volume{ - Size: "500Mi", - }, - }, - }, + parentSpec := v1.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: v1.DevWorkspaceTemplateSpecContent{ + Components: []v1.Component{ + { + Name: "runtime", + ComponentUnion: v1.ComponentUnion{ + Volume: &v1.VolumeComponent{ + Volume: v1.Volume{ + Size: "500Mi", }, }, }, @@ -3108,6 +3098,13 @@ func Test_parseFromKubeCRD(t *testing.T) { }, }, } + parentDevfile := DevfileObj{ + Data: &v2.DevfileV2{ + Devfile: v1.Devfile{ + DevWorkspaceTemplateSpec: parentSpec, + }, + }, + } tests := []struct { name string @@ -3119,13 +3116,41 @@ func Test_parseFromKubeCRD(t *testing.T) { wantErr bool }{ { - name: "should fail if provided registryUrl does not have protocol prefix", + name: "should successfully parse the parent with namespace specified in devfile", wantDevFile: parentDevfile, importReference: v1.ImportReference{ ImportReferenceUnion: v1.ImportReferenceUnion{ - Id: registryId, + Kubernetes: &v1.KubernetesCustomResourceImportReference{ + Name: name, + Namespace: namespace, + }, }, - RegistryUrl: registry, + }, + devWorkspaceResources: map[string]v1.DevWorkspaceTemplate{ + name: { + TypeMeta: kubev1.TypeMeta{ + Kind: "DevWorkspaceTemplate", + APIVersion: apiVersion, + }, + Spec: parentSpec, + }, + }, + wantErr: false, + }, + { + name: "should fail if kclient get returns error", + wantDevFile: parentDevfile, + importReference: v1.ImportReference{ + ImportReferenceUnion: v1.ImportReferenceUnion{ + Kubernetes: &v1.KubernetesCustomResourceImportReference{ + Name: name, + Namespace: namespace, + }, + }, + }, + devWorkspaceResources: map[string]v1.DevWorkspaceTemplate{}, + errors: map[string]string{ + name: "not found", }, wantErr: true, }, @@ -3138,10 +3163,11 @@ func Test_parseFromKubeCRD(t *testing.T) { } tool := resolverTools{ k8sClient: testK8sClient, + context: context.Background(), } got, err := parseFromKubeCRD(tt.importReference, &resolutionContextTree{}, tool) if tt.wantErr == (err == nil) { - t.Errorf("Test_parseFromRegistry() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("Test_parseFromKubeCRD() error = %v, wantErr %v", err, tt.wantErr) return } diff --git a/pkg/testingutil/k8sClient.go b/pkg/testingutil/k8sClient.go index 60993adf..93394fd3 100644 --- a/pkg/testingutil/k8sClient.go +++ b/pkg/testingutil/k8sClient.go @@ -24,6 +24,7 @@ func (client *FakeK8sClient) Get(_ context.Context, namespacedName client.Object *template = element return nil } + if err, ok := client.Errors[namespacedName.Name]; ok { return errors.New(err) } From ce734446825a2d010a54d424f64990e784ed9ba7 Mon Sep 17 00:00:00 2001 From: Stephanie Date: Thu, 8 Apr 2021 18:37:19 -0400 Subject: [PATCH 6/9] address some review comments Signed-off-by: Stephanie --- pkg/devfile/parser/parse.go | 41 ++++++++++++------------- pkg/devfile/parser/resolutionContext.go | 2 +- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/pkg/devfile/parser/parse.go b/pkg/devfile/parser/parse.go index 92a4a1e2..8dba33e8 100644 --- a/pkg/devfile/parser/parse.go +++ b/pkg/devfile/parser/parse.go @@ -74,8 +74,8 @@ type ParserArgs struct { // If registryUrl is defined in devfile, this list will be ignored. RegistryURLs []string // DefaultNamespace is the default namespace to use - // If namespace is defined under ImportReferences.Kubernetes, this namespace will be ignored. - DefaultNameSpace string + // If namespace is defined under devfile's parent kubernetes object, this namespace will be ignored. + DefaultNamespace string // Context is the context used for making Kubernetes requests Context context.Context // K8sClient is the Kubernetes client instance used for interacting with a cluster @@ -100,7 +100,7 @@ func ParseDevfile(args ParserArgs) (d DevfileObj, err error) { } tool := resolverTools{ - defaultNamespace: args.DefaultNameSpace, + defaultNamespace: args.DefaultNamespace, registryURLs: args.RegistryURLs, context: args.Context, k8sClient: args.K8sClient, @@ -206,7 +206,7 @@ func parseParentAndPlugin(d DevfileObj, resolveCtx *resolutionContextTree, tool case parent.Kubernetes != nil: parentDevfileObj, err = parseFromKubeCRD(parent.ImportReference, resolveCtx, tool) default: - return fmt.Errorf("parent URI or parent Id undefined, currently only URI and Id are suppported") + return fmt.Errorf("devfile parent does not define any resources") } parentWorkspaceContent := parentDevfileObj.Data.GetDevfileWorkspace() @@ -246,7 +246,7 @@ func parseParentAndPlugin(d DevfileObj, resolveCtx *resolutionContextTree, tool case plugin.Kubernetes != nil: pluginDevfileObj, err = parseFromKubeCRD(plugin.ImportReference, resolveCtx, tool) default: - return fmt.Errorf("plugin URI or plugin Id undefined, currently only URI and Id are suppported") + return fmt.Errorf("plugin %s does not define any resources", component.Name) } pluginWorkspaceContent := pluginDevfileObj.Data.GetDevfileWorkspace() flattenedPlugin := pluginWorkspaceContent @@ -285,12 +285,12 @@ func parseFromURI(importReference v1.ImportReference, curDevfileCtx devfileCtx.D // relative path on disk if !absoluteURL && curDevfileCtx.GetAbsPath() != "" { - d.Ctx = devfileCtx.NewDevfileCtx(path.Join(path.Dir(curDevfileCtx.GetAbsPath()), uri)) newUri = path.Join(path.Dir(curDevfileCtx.GetAbsPath()), uri) + d.Ctx = devfileCtx.NewDevfileCtx(newUri) } else if absoluteURL { // absolute URL address - d.Ctx = devfileCtx.NewURLDevfileCtx(uri) newUri = uri + d.Ctx = devfileCtx.NewURLDevfileCtx(newUri) } else if curDevfileCtx.GetURL() != "" { // relative path to a URL u, err := url.Parse(curDevfileCtx.GetURL()) @@ -298,8 +298,8 @@ func parseFromURI(importReference v1.ImportReference, curDevfileCtx devfileCtx.D return DevfileObj{}, err } u.Path = path.Join(path.Dir(u.Path), uri) - d.Ctx = devfileCtx.NewURLDevfileCtx(u.String()) newUri = u.String() + d.Ctx = devfileCtx.NewURLDevfileCtx(newUri) } importReference.Uri = newUri newCtx := resolveCtx.appendNode(importReference) @@ -309,10 +309,10 @@ func parseFromURI(importReference v1.ImportReference, curDevfileCtx devfileCtx.D func parseFromRegistry(importReference v1.ImportReference, resolveCtx *resolutionContextTree, tool resolverTools) (d DevfileObj, err error) { d.Ctx = devfileCtx.DevfileCtx{} - Id := importReference.Id + id := importReference.Id registryURL := importReference.RegistryUrl if registryURL != "" { - devfileContent, err := getDevfileFromRegistry(Id, registryURL) + devfileContent, err := getDevfileFromRegistry(id, registryURL) if err != nil { return DevfileObj{}, err } @@ -320,37 +320,37 @@ func parseFromRegistry(importReference v1.ImportReference, resolveCtx *resolutio if err != nil { return d, errors.Wrap(err, "failed to set devfile content from bytes") } - newCtx := resolveCtx.appendNode(importReference) + newResolveCtx := resolveCtx.appendNode(importReference) - return populateAndParseDevfile(d, newCtx, tool, true) + return populateAndParseDevfile(d, newResolveCtx, tool, true) } else if tool.registryURLs != nil { - for _, registry := range tool.registryURLs { - devfileContent, err := getDevfileFromRegistry(Id, registry) + for _, registryURL := range tool.registryURLs { + devfileContent, err := getDevfileFromRegistry(id, registryURL) if devfileContent != nil && err == nil { err = d.Ctx.SetDevfileContentFromBytes(devfileContent) if err != nil { return d, errors.Wrap(err, "failed to set devfile content from bytes") } - importReference.RegistryUrl = registry - newCtx := resolveCtx.appendNode(importReference) + importReference.RegistryUrl = registryURL + newResolveCtx := resolveCtx.appendNode(importReference) - return populateAndParseDevfile(d, newCtx, tool, true) + return populateAndParseDevfile(d, newResolveCtx, tool, true) } } } else { return DevfileObj{}, fmt.Errorf("failed to fetch from registry, registry URL is not provided") } - return DevfileObj{}, fmt.Errorf("failed to get Id: %s from registry URLs provided", Id) + return DevfileObj{}, fmt.Errorf("failed to get id: %s from registry URLs provided", id) } -func getDevfileFromRegistry(Id, registryURL string) ([]byte, error) { +func getDevfileFromRegistry(id, registryURL string) ([]byte, error) { if !strings.HasPrefix(registryURL, "http://") && !strings.HasPrefix(registryURL, "https://") { return nil, fmt.Errorf("the provided registryURL: %s is not a valid URL", registryURL) } param := util.HTTPRequestParams{ - URL: fmt.Sprintf("%s/devfiles/%s", registryURL, Id), + URL: fmt.Sprintf("%s/devfiles/%s", registryURL, id), } return util.HTTPGetRequest(param, 0) } @@ -399,7 +399,6 @@ func parseFromKubeCRD(importReference v1.ImportReference, resolveCtx *resolution } func convertDevWorskapceTemplateToDevObj(dwTemplate v1.DevWorkspaceTemplate) (d DevfileObj, err error) { - // Todo: use dwTemplate.APIVersion to determine devfile schemaversion // APIVersion: group/version // for example: APIVersion: "workspace.devfile.io/v1alpha2" uses api version v1alpha2, and match to v2 schemas tempList := strings.Split(dwTemplate.APIVersion, "/") diff --git a/pkg/devfile/parser/resolutionContext.go b/pkg/devfile/parser/resolutionContext.go index 1237ecdb..ee5737b2 100644 --- a/pkg/devfile/parser/resolutionContext.go +++ b/pkg/devfile/parser/resolutionContext.go @@ -40,7 +40,7 @@ func (t *resolutionContextTree) hasCycle() error { } // formatImportCycle is a utility method for formatting a cycle that has been detected. Output is formatted as -// plugin1 -> plugin2 -> plugin3 -> plugin1, where pluginX are component names. +// {importReference1} -> {importReference2} -> {importReference3} -> {importReference1}, where {importReference1} are importReference detailed info func formatImportCycle(end *resolutionContextTree) string { cycle := resolveImportReference(end.importReference) for end.parentNode != nil { From aa85a4a983df8fa3337edbfe99558868d5b7db92 Mon Sep 17 00:00:00 2001 From: Stephanie Date: Fri, 9 Apr 2021 14:51:17 -0400 Subject: [PATCH 7/9] address review comments Signed-off-by: Stephanie --- pkg/devfile/parser/context/context.go | 9 ++++ pkg/devfile/parser/data/helper_test.go | 6 +-- pkg/devfile/parser/data/interface.go | 5 +- pkg/devfile/parser/data/v2/workspace.go | 13 +++-- pkg/devfile/parser/data/v2/workspace_test.go | 6 +-- pkg/devfile/parser/data/versions.go | 18 +++---- pkg/devfile/parser/parse.go | 54 ++++++++++---------- 7 files changed, 63 insertions(+), 48 deletions(-) diff --git a/pkg/devfile/parser/context/context.go b/pkg/devfile/parser/context/context.go index b3c93211..64e3b5d4 100644 --- a/pkg/devfile/parser/context/context.go +++ b/pkg/devfile/parser/context/context.go @@ -52,6 +52,15 @@ func NewURLDevfileCtx(url string) DevfileCtx { } } +// NewByteContentDevfileCtx set devfile content from byte data and returns a new DevfileCtx type object and error +func NewByteContentDevfileCtx(data []byte) (d DevfileCtx, err error) { + err = d.SetDevfileContentFromBytes(data) + if err != nil { + return DevfileCtx{}, err + } + return d, nil +} + // populateDevfile checks the API version is supported and returns the JSON schema for the given devfile API Version func (d *DevfileCtx) populateDevfile() (err error) { diff --git a/pkg/devfile/parser/data/helper_test.go b/pkg/devfile/parser/data/helper_test.go index 1c9b3140..e35de0be 100644 --- a/pkg/devfile/parser/data/helper_test.go +++ b/pkg/devfile/parser/data/helper_test.go @@ -14,7 +14,7 @@ func TestNewDevfileData(t *testing.T) { t.Run("valid devfile apiVersion", func(t *testing.T) { var ( - version = APIVersion200 + version = APISchemaVersion200 want = reflect.TypeOf(&v2.DevfileV2{}) obj, err = NewDevfileData(string(version)) got = reflect.TypeOf(obj) @@ -50,7 +50,7 @@ func TestGetDevfileJSONSchema(t *testing.T) { t.Run("valid devfile apiVersion", func(t *testing.T) { var ( - version = APIVersion200 + version = APISchemaVersion200 want = v200.JsonSchema200 got, err = GetDevfileJSONSchema(string(version)) ) @@ -82,7 +82,7 @@ func TestIsApiVersionSupported(t *testing.T) { t.Run("valid devfile apiVersion", func(t *testing.T) { var ( - version = APIVersion200 + version = APISchemaVersion200 want = true got = IsApiVersionSupported(string(version)) ) diff --git a/pkg/devfile/parser/data/interface.go b/pkg/devfile/parser/data/interface.go index ecbeb90a..ff6049c3 100644 --- a/pkg/devfile/parser/data/interface.go +++ b/pkg/devfile/parser/data/interface.go @@ -52,8 +52,9 @@ type DevfileData interface { GetVolumeMountPaths(mountName, containerName string) ([]string, error) // workspace related methods - GetDevfileWorkspace() *v1.DevWorkspaceTemplateSpecContent - SetDevfileWorkspace(content v1.DevWorkspaceTemplateSpecContent) + GetDevfileWorkspaceSpecContent() *v1.DevWorkspaceTemplateSpecContent + SetDevfileWorkspaceSpecContent(content v1.DevWorkspaceTemplateSpecContent) + SetDevfileWorkspaceSpec(spec v1.DevWorkspaceTemplateSpec) // utils GetDevfileContainerComponents(common.DevfileOptions) ([]v1.Component, error) diff --git a/pkg/devfile/parser/data/v2/workspace.go b/pkg/devfile/parser/data/v2/workspace.go index e73b8d47..53f0d1fd 100644 --- a/pkg/devfile/parser/data/v2/workspace.go +++ b/pkg/devfile/parser/data/v2/workspace.go @@ -4,13 +4,18 @@ import ( v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" ) -// GetDevfileWorkspace returns the workspace content for the devfile -func (d *DevfileV2) GetDevfileWorkspace() *v1.DevWorkspaceTemplateSpecContent { +// GetDevfileWorkspaceSpecContent returns the workspace spec content for the devfile +func (d *DevfileV2) GetDevfileWorkspaceSpecContent() *v1.DevWorkspaceTemplateSpecContent { return &d.DevWorkspaceTemplateSpecContent } -// SetDevfileWorkspace sets the workspace content -func (d *DevfileV2) SetDevfileWorkspace(content v1.DevWorkspaceTemplateSpecContent) { +// SetDevfileWorkspaceSpecContent sets the workspace spec content +func (d *DevfileV2) SetDevfileWorkspaceSpecContent(content v1.DevWorkspaceTemplateSpecContent) { d.DevWorkspaceTemplateSpecContent = content } + +// SetDevfileWorkspaceSpec sets the workspace spec +func (d *DevfileV2) SetDevfileWorkspaceSpec(spec v1.DevWorkspaceTemplateSpec) { + d.DevWorkspaceTemplateSpec = spec +} diff --git a/pkg/devfile/parser/data/v2/workspace_test.go b/pkg/devfile/parser/data/v2/workspace_test.go index 5ecbd5ee..2eb026ee 100644 --- a/pkg/devfile/parser/data/v2/workspace_test.go +++ b/pkg/devfile/parser/data/v2/workspace_test.go @@ -7,7 +7,7 @@ import ( v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" ) -func TestDevfile200_SetDevfileWorkspace(t *testing.T) { +func TestDevfile200_SetDevfileWorkspaceSpecContent(t *testing.T) { type args struct { name string @@ -65,9 +65,9 @@ func TestDevfile200_SetDevfileWorkspace(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - tt.devfilev2.SetDevfileWorkspace(tt.workspace) + tt.devfilev2.SetDevfileWorkspaceSpecContent(tt.workspace) if !reflect.DeepEqual(tt.devfilev2, tt.expectedDevfilev2) { - t.Errorf("TestDevfile200_SetDevfileWorkspace() expected %v, got %v", tt.expectedDevfilev2, tt.devfilev2) + t.Errorf("TestDevfile200_SetDevfileWorkspaceSpecContent() expected %v, got %v", tt.expectedDevfilev2, tt.devfilev2) } }) } diff --git a/pkg/devfile/parser/data/versions.go b/pkg/devfile/parser/data/versions.go index a331a12f..91ec756e 100644 --- a/pkg/devfile/parser/data/versions.go +++ b/pkg/devfile/parser/data/versions.go @@ -13,9 +13,9 @@ type supportedApiVersion string // Supported devfile API versions const ( - APIVersion200 supportedApiVersion = "2.0.0" - APIVersion210 supportedApiVersion = "2.1.0" - V2APIVersion supportedApiVersion = "v1alpha2" + APISchemaVersion200 supportedApiVersion = "2.0.0" + APISchemaVersion210 supportedApiVersion = "2.1.0" + APIVersionAlpha2 supportedApiVersion = "v1alpha2" ) // ------------- Init functions ------------- // @@ -26,9 +26,9 @@ var apiVersionToDevfileStruct map[supportedApiVersion]reflect.Type // Initializes a map of supported devfile api versions and devfile structs func init() { apiVersionToDevfileStruct = make(map[supportedApiVersion]reflect.Type) - apiVersionToDevfileStruct[APIVersion200] = reflect.TypeOf(v2.DevfileV2{}) - apiVersionToDevfileStruct[APIVersion210] = reflect.TypeOf(v2.DevfileV2{}) - apiVersionToDevfileStruct[V2APIVersion] = reflect.TypeOf(v2.DevfileV2{}) + apiVersionToDevfileStruct[APISchemaVersion200] = reflect.TypeOf(v2.DevfileV2{}) + apiVersionToDevfileStruct[APISchemaVersion210] = reflect.TypeOf(v2.DevfileV2{}) + apiVersionToDevfileStruct[APIVersionAlpha2] = reflect.TypeOf(v2.DevfileV2{}) } // Map to store mappings between supported devfile API versions and respective devfile JSON schemas @@ -37,8 +37,8 @@ var devfileApiVersionToJSONSchema map[supportedApiVersion]string // init initializes a map of supported devfile apiVersions with it's respective devfile JSON schema func init() { devfileApiVersionToJSONSchema = make(map[supportedApiVersion]string) - devfileApiVersionToJSONSchema[APIVersion200] = v200.JsonSchema200 - devfileApiVersionToJSONSchema[APIVersion210] = v210.JsonSchema210 + devfileApiVersionToJSONSchema[APISchemaVersion200] = v200.JsonSchema200 + devfileApiVersionToJSONSchema[APISchemaVersion210] = v210.JsonSchema210 // should use hightest v2 schema version since it is expected to be backward compatible with the same api version - devfileApiVersionToJSONSchema[V2APIVersion] = v210.JsonSchema210 + devfileApiVersionToJSONSchema[APIVersionAlpha2] = v210.JsonSchema210 } diff --git a/pkg/devfile/parser/parse.go b/pkg/devfile/parser/parse.go index 8dba33e8..2e2d595d 100644 --- a/pkg/devfile/parser/parse.go +++ b/pkg/devfile/parser/parse.go @@ -86,8 +86,7 @@ type ParserArgs struct { // Creates devfile context and runtime objects func ParseDevfile(args ParserArgs) (d DevfileObj, err error) { if args.Data != nil { - d.Ctx = devfileCtx.DevfileCtx{} - err = d.Ctx.SetDevfileContentFromBytes(args.Data) + d.Ctx, err = devfileCtx.NewByteContentDevfileCtx(args.Data) if err != nil { return d, errors.Wrap(err, "failed to set devfile content from bytes") } @@ -177,8 +176,7 @@ func ParseFromURL(url string) (d DevfileObj, err error) { // Creates devfile context and runtime objects // Deprecated, use ParseDevfile() instead func ParseFromData(data []byte) (d DevfileObj, err error) { - d.Ctx = devfileCtx.DevfileCtx{} - err = d.Ctx.SetDevfileContentFromBytes(data) + d.Ctx, err = devfileCtx.NewByteContentDevfileCtx(data) if err != nil { return d, errors.Wrap(err, "failed to set devfile content from bytes") } @@ -209,7 +207,7 @@ func parseParentAndPlugin(d DevfileObj, resolveCtx *resolutionContextTree, tool return fmt.Errorf("devfile parent does not define any resources") } - parentWorkspaceContent := parentDevfileObj.Data.GetDevfileWorkspace() + parentWorkspaceContent := parentDevfileObj.Data.GetDevfileWorkspaceSpecContent() if !reflect.DeepEqual(parent.ParentOverrides, v1.ParentOverrides{}) { flattenedParent, err = apiOverride.OverrideDevWorkspaceTemplateSpec(parentWorkspaceContent, parent.ParentOverrides) if err != nil { @@ -248,7 +246,7 @@ func parseParentAndPlugin(d DevfileObj, resolveCtx *resolutionContextTree, tool default: return fmt.Errorf("plugin %s does not define any resources", component.Name) } - pluginWorkspaceContent := pluginDevfileObj.Data.GetDevfileWorkspace() + pluginWorkspaceContent := pluginDevfileObj.Data.GetDevfileWorkspaceSpecContent() flattenedPlugin := pluginWorkspaceContent if !reflect.DeepEqual(plugin.PluginOverrides, v1.PluginOverrides{}) { flattenedPlugin, err = apiOverride.OverrideDevWorkspaceTemplateSpec(pluginWorkspaceContent, plugin.PluginOverrides) @@ -260,11 +258,11 @@ func parseParentAndPlugin(d DevfileObj, resolveCtx *resolutionContextTree, tool } } - mergedContent, err := apiOverride.MergeDevWorkspaceTemplateSpec(d.Data.GetDevfileWorkspace(), flattenedParent, flattenedPlugins...) + mergedContent, err := apiOverride.MergeDevWorkspaceTemplateSpec(d.Data.GetDevfileWorkspaceSpecContent(), flattenedParent, flattenedPlugins...) if err != nil { return err } - d.Data.SetDevfileWorkspace(*mergedContent) + d.Data.SetDevfileWorkspaceSpecContent(*mergedContent) // remove parent from flatterned devfile d.Data.SetParent(nil) @@ -302,13 +300,12 @@ func parseFromURI(importReference v1.ImportReference, curDevfileCtx devfileCtx.D d.Ctx = devfileCtx.NewURLDevfileCtx(newUri) } importReference.Uri = newUri - newCtx := resolveCtx.appendNode(importReference) + newResolveCtx := resolveCtx.appendNode(importReference) - return populateAndParseDevfile(d, newCtx, tool, true) + return populateAndParseDevfile(d, newResolveCtx, tool, true) } func parseFromRegistry(importReference v1.ImportReference, resolveCtx *resolutionContextTree, tool resolverTools) (d DevfileObj, err error) { - d.Ctx = devfileCtx.DevfileCtx{} id := importReference.Id registryURL := importReference.RegistryUrl if registryURL != "" { @@ -316,7 +313,7 @@ func parseFromRegistry(importReference v1.ImportReference, resolveCtx *resolutio if err != nil { return DevfileObj{}, err } - err = d.Ctx.SetDevfileContentFromBytes(devfileContent) + d.Ctx, err = devfileCtx.NewByteContentDevfileCtx(devfileContent) if err != nil { return d, errors.Wrap(err, "failed to set devfile content from bytes") } @@ -328,7 +325,7 @@ func parseFromRegistry(importReference v1.ImportReference, resolveCtx *resolutio for _, registryURL := range tool.registryURLs { devfileContent, err := getDevfileFromRegistry(id, registryURL) if devfileContent != nil && err == nil { - err = d.Ctx.SetDevfileContentFromBytes(devfileContent) + d.Ctx, err = devfileCtx.NewByteContentDevfileCtx(devfileContent) if err != nil { return d, errors.Wrap(err, "failed to set devfile content from bytes") } @@ -361,20 +358,23 @@ func parseFromKubeCRD(importReference v1.ImportReference, resolveCtx *resolution return DevfileObj{}, fmt.Errorf("Kubernetes client and context are required to parse from Kubernetes CRD") } namespace := importReference.Kubernetes.Namespace - // if namespace is not set in devfile, use default namespace provided in by consumer - if namespace == "" { - namespace = tool.defaultNamespace - } - // use current namespace if namespace is not set in devfile and not provided by consumer + if namespace == "" { - loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() - configOverrides := &clientcmd.ConfigOverrides{} - config := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) - namespace, _, err = config.Namespace() - if err != nil { - return DevfileObj{}, fmt.Errorf("kubernetes namespace is not provided, and cannot get current running cluster's namespace: %v", err) + // if namespace is not set in devfile, use default namespace provided in by consumer + if tool.defaultNamespace != "" { + namespace = tool.defaultNamespace + } else { + // use current namespace if namespace is not set in devfile and not provided by consumer + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + configOverrides := &clientcmd.ConfigOverrides{} + config := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) + namespace, _, err = config.Namespace() + if err != nil { + return DevfileObj{}, fmt.Errorf("kubernetes namespace is not provided, and cannot get current running cluster's namespace: %v", err) + } } } + var dwTemplate v1.DevWorkspaceTemplate namespacedName := types.NamespacedName{ Name: importReference.Kubernetes.Name, @@ -391,9 +391,9 @@ func parseFromKubeCRD(importReference v1.ImportReference, resolveCtx *resolution } importReference.Kubernetes.Namespace = namespace - newCtx := resolveCtx.appendNode(importReference) + newResolveCtx := resolveCtx.appendNode(importReference) - err = parseParentAndPlugin(d, newCtx, tool) + err = parseParentAndPlugin(d, newResolveCtx, tool) return d, err } @@ -407,7 +407,7 @@ func convertDevWorskapceTemplateToDevObj(dwTemplate v1.DevWorkspaceTemplate) (d if err != nil { return DevfileObj{}, err } - d.Data.SetDevfileWorkspace(dwTemplate.Spec.DevWorkspaceTemplateSpecContent) + d.Data.SetDevfileWorkspaceSpec(dwTemplate.Spec) return d, nil From 1c3b45de0a51e950a2ef1db86763435bd266cb03 Mon Sep 17 00:00:00 2001 From: Stephanie Date: Fri, 9 Apr 2021 15:04:35 -0400 Subject: [PATCH 8/9] modify existing test, check multiple types of reference in import cycle Signed-off-by: Stephanie --- pkg/devfile/parser/parse.go | 3 + pkg/devfile/parser/parse_test.go | 125 ++++++++++++++++--------------- 2 files changed, 67 insertions(+), 61 deletions(-) diff --git a/pkg/devfile/parser/parse.go b/pkg/devfile/parser/parse.go index 2e2d595d..ad3c70c0 100644 --- a/pkg/devfile/parser/parse.go +++ b/pkg/devfile/parser/parse.go @@ -203,6 +203,9 @@ func parseParentAndPlugin(d DevfileObj, resolveCtx *resolutionContextTree, tool } case parent.Kubernetes != nil: parentDevfileObj, err = parseFromKubeCRD(parent.ImportReference, resolveCtx, tool) + if err != nil { + return err + } default: return fmt.Errorf("devfile parent does not define any resources") } diff --git a/pkg/devfile/parser/parse_test.go b/pkg/devfile/parser/parse_test.go index 464c90fc..512f2088 100644 --- a/pkg/devfile/parser/parse_test.go +++ b/pkg/devfile/parser/parse_test.go @@ -2188,11 +2188,12 @@ func Test_parseParentAndPluginFromURI(t *testing.T) { } } -func Test_parseParentAndPlugin_RecursivelyReference_withMultipleURI(t *testing.T) { +func Test_parseParentAndPlugin_RecursivelyReference(t *testing.T) { const uri1 = "127.0.0.1:8080" - const uri2 = "127.0.0.1:9090" - const uri3 = "127.0.0.1:8090" + const uri2 = "127.0.0.1:8090" const httpPrefix = "http://" + const name = "testcrd" + const namespace = "defaultnamespace" devFileObj := DevfileObj{ Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), @@ -2236,7 +2237,10 @@ func Test_parseParentAndPlugin_RecursivelyReference_withMultipleURI(t *testing.T Parent: &v1.Parent{ ImportReference: v1.ImportReference{ ImportReferenceUnion: v1.ImportReferenceUnion{ - Uri: httpPrefix + uri2, + Kubernetes: &v1.KubernetesCustomResourceImportReference{ + Name: name, + Namespace: namespace, + }, }, }, }, @@ -2258,35 +2262,8 @@ func Test_parseParentAndPlugin_RecursivelyReference_withMultipleURI(t *testing.T }, }, } + parentDevfile2 := DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), - Data: &v2.DevfileV2{ - Devfile: v1.Devfile{ - DevfileHeader: devfilepkg.DevfileHeader{ - SchemaVersion: schemaV200, - }, - DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ - DevWorkspaceTemplateSpecContent: v1.DevWorkspaceTemplateSpecContent{ - Components: []v1.Component{ - { - Name: "plugin", - ComponentUnion: v1.ComponentUnion{ - Plugin: &v1.PluginComponent{ - ImportReference: v1.ImportReference{ - ImportReferenceUnion: v1.ImportReferenceUnion{ - Uri: httpPrefix + uri3, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - parentDevfile3 := DevfileObj{ Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ @@ -2345,7 +2322,13 @@ func Test_parseParentAndPlugin_RecursivelyReference_withMultipleURI(t *testing.T defer testServer1.Close() testServer2 := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - data, err := yaml.Marshal(parentDevfile2.Data) + var data []byte + if strings.Contains(r.URL.Path, "/devfiles/nodejs") { + data, err = yaml.Marshal(parentDevfile2.Data) + } else { + w.WriteHeader(http.StatusNotFound) + return + } if err != nil { t.Errorf("unexpected error: %v", err) } @@ -2355,7 +2338,7 @@ func Test_parseParentAndPlugin_RecursivelyReference_withMultipleURI(t *testing.T } })) // create a listener with the desired port. - l2, err := net.Listen("tcp", uri2) + l3, err := net.Listen("tcp", uri2) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -2363,42 +2346,62 @@ func Test_parseParentAndPlugin_RecursivelyReference_withMultipleURI(t *testing.T // NewUnstartedServer creates a listener. Close that listener and replace // with the one we created. testServer2.Listener.Close() - testServer2.Listener = l2 + testServer2.Listener = l3 testServer2.Start() defer testServer2.Close() - testServer3 := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - data, err := yaml.Marshal(parentDevfile3.Data) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - _, err = w.Write(data) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - })) - // create a listener with the desired port. - l3, err := net.Listen("tcp", uri3) - if err != nil { - t.Errorf("unexpected error: %v", err) + parentSpec := v1.DevWorkspaceTemplateSpec{ + Parent: &v1.Parent{ + ImportReference: v1.ImportReference{ + ImportReferenceUnion: v1.ImportReferenceUnion{ + Id: "nodejs", + }, + RegistryUrl: httpPrefix + uri2, + }, + }, + DevWorkspaceTemplateSpecContent: v1.DevWorkspaceTemplateSpecContent{ + Components: []v1.Component{ + { + Name: "crdcomponent", + ComponentUnion: v1.ComponentUnion{ + Volume: &v1.VolumeComponent{ + Volume: v1.Volume{ + Size: "500Mi", + }, + }, + }, + }, + }, + }, + } + devWorkspaceResources := map[string]v1.DevWorkspaceTemplate{ + name: { + TypeMeta: kubev1.TypeMeta{ + Kind: "DevWorkspaceTemplate", + APIVersion: "testgroup/v1alpha2", + }, + Spec: parentSpec, + }, } - // NewUnstartedServer creates a listener. Close that listener and replace - // with the one we created. - testServer3.Listener.Close() - testServer3.Listener = l3 + t.Run("it should error out if import reference has a cycle", func(t *testing.T) { + testK8sClient := &testingutil.FakeK8sClient{ + DevWorkspaceResources: devWorkspaceResources, + } + tool := resolverTools{ + k8sClient: testK8sClient, + context: context.Background(), + } - testServer3.Start() - defer testServer3.Close() - t.Run("it should error out if URI is recursively referenced with multiple references", func(t *testing.T) { - err := parseParentAndPlugin(devFileObj, &resolutionContextTree{}, resolverTools{}) - // devfile has an cycle in references: main devfile -> uri: http://127.0.0.1:8080 -> uri: http://127.0.0.1:9090 -> uri: http://127.0.0.1:8090 -> uri: http://127.0.0.1:8080 - expectedErr := fmt.Sprintf("devfile has an cycle in references: main devfile -> uri: %s%s -> uri: %s%s -> uri: %s%s -> uri: %s%s", httpPrefix, uri1, - httpPrefix, uri2, httpPrefix, uri3, httpPrefix, uri1) + err := parseParentAndPlugin(devFileObj, &resolutionContextTree{}, tool) + // devfile has a cycle in references: main devfile -> uri: http://127.0.0.1:8080 -> uri: http://127.0.0.1:9090 -> id: nodejs, registryURL: http://127.0.0.1:8090 -> uri: http://127.0.0.1:8080 + expectedErr := fmt.Sprintf("devfile has an cycle in references: main devfile -> uri: %s%s -> name: %s, namespace: %s -> id: nodejs, registryURL: %s%s -> uri: %s%s", httpPrefix, uri1, name, namespace, + httpPrefix, uri2, httpPrefix, uri1) // Unexpected error if err == nil || !reflect.DeepEqual(expectedErr, err.Error()) { - t.Errorf("Test_parseParentAndPlugin_RecursivelyReference_withMultipleURI() unexpected error = %v", err) + t.Errorf("Test_parseParentAndPlugin_RecursivelyReference unexpected error = %v", err) + return } From d135abfaa4343355e2e4631e515755630da82019 Mon Sep 17 00:00:00 2001 From: Stephanie Date: Tue, 13 Apr 2021 13:12:08 -0400 Subject: [PATCH 9/9] update comment Signed-off-by: Stephanie --- pkg/devfile/parser/parse_test.go | 2 +- pkg/devfile/parser/resolutionContext.go | 19 ++++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/pkg/devfile/parser/parse_test.go b/pkg/devfile/parser/parse_test.go index 5e1bc0f8..b0df6c4a 100644 --- a/pkg/devfile/parser/parse_test.go +++ b/pkg/devfile/parser/parse_test.go @@ -2395,7 +2395,7 @@ func Test_parseParentAndPlugin_RecursivelyReference(t *testing.T) { } err := parseParentAndPlugin(devFileObj, &resolutionContextTree{}, tool) - // devfile has a cycle in references: main devfile -> uri: http://127.0.0.1:8080 -> uri: http://127.0.0.1:9090 -> id: nodejs, registryURL: http://127.0.0.1:8090 -> uri: http://127.0.0.1:8080 + // devfile has a cycle in references: main devfile -> uri: http://127.0.0.1:8080 -> name: testcrd, namespace: defaultnamespace -> id: nodejs, registryURL: http://127.0.0.1:8090 -> uri: http://127.0.0.1:8080 expectedErr := fmt.Sprintf("devfile has an cycle in references: main devfile -> uri: %s%s -> name: %s, namespace: %s -> id: nodejs, registryURL: %s%s -> uri: %s%s", httpPrefix, uri1, name, namespace, httpPrefix, uri2, httpPrefix, uri1) // Unexpected error diff --git a/pkg/devfile/parser/resolutionContext.go b/pkg/devfile/parser/resolutionContext.go index ee5737b2..5bb0d4bf 100644 --- a/pkg/devfile/parser/resolutionContext.go +++ b/pkg/devfile/parser/resolutionContext.go @@ -27,27 +27,24 @@ func (t *resolutionContextTree) appendNode(importReference v1.ImportReference) * func (t *resolutionContextTree) hasCycle() error { var seenRefs []v1.ImportReference currNode := t + hasCycle := false + cycle := resolveImportReference(t.importReference) + for currNode.parentNode != nil { for _, seenRef := range seenRefs { if reflect.DeepEqual(seenRef, currNode.importReference) { - return fmt.Errorf("devfile has an cycle in references: %v", formatImportCycle(t)) + hasCycle = true } } seenRefs = append(seenRefs, currNode.importReference) currNode = currNode.parentNode + cycle = fmt.Sprintf("%s -> %s", resolveImportReference(currNode.importReference), cycle) } - return nil -} -// formatImportCycle is a utility method for formatting a cycle that has been detected. Output is formatted as -// {importReference1} -> {importReference2} -> {importReference3} -> {importReference1}, where {importReference1} are importReference detailed info -func formatImportCycle(end *resolutionContextTree) string { - cycle := resolveImportReference(end.importReference) - for end.parentNode != nil { - end = end.parentNode - cycle = fmt.Sprintf("%s -> %s", resolveImportReference(end.importReference), cycle) + if hasCycle { + return fmt.Errorf("devfile has an cycle in references: %v", cycle) } - return cycle + return nil } func resolveImportReference(importReference v1.ImportReference) string {