diff --git a/kstatus/go.mod b/kstatus/go.mod index 74a6cb5b5..c331a021a 100644 --- a/kstatus/go.mod +++ b/kstatus/go.mod @@ -4,13 +4,22 @@ go 1.12 require ( github.com/ghodss/yaml v1.0.0 - github.com/kr/pretty v0.1.0 // indirect + github.com/onsi/ginkgo v1.10.1 // indirect + github.com/onsi/gomega v1.7.0 // indirect github.com/pkg/errors v0.8.1 github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/testify v1.4.0 + go.uber.org/atomic v1.4.0 // indirect + go.uber.org/zap v1.10.0 // indirect + golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 // indirect golang.org/x/net v0.0.0-20190909003024-a7b16738d86b // indirect + golang.org/x/sys v0.0.0-20190911201528-7ad0cfa0b7b5 // indirect golang.org/x/text v0.3.2 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + k8s.io/api v0.0.0-20190918195907-bd6ac527cfd2 + k8s.io/apimachinery v0.0.0-20190817020851-f2f3a405f61d + k8s.io/client-go v0.0.0-20190918200256-06eb1244587a + sigs.k8s.io/controller-runtime v0.3.0 sigs.k8s.io/kustomize/pseudo/k8s v0.1.0 sigs.k8s.io/yaml v1.1.0 ) diff --git a/kstatus/go.sum b/kstatus/go.sum index 94a51155c..23718f5ae 100644 --- a/kstatus/go.sum +++ b/kstatus/go.sum @@ -1,4 +1,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest v0.9.2/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= @@ -10,57 +12,147 @@ github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxB 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.0/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/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/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/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/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-oidc v0.0.0-20180117170138-065b426bd416/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.0.0-20180108230905-e214231b295a/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +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/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680 h1:ZktWZesgun21uEDrwW7iEV1zPCGQldM2atlJZ3TdvVM= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +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-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.17.2/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.17.2/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.17.2/go.mod h1:QO936ZXeisByFmZEO1IS1Dqhtf4QV1sYYFtIq6Ld86Q= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.17.2/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.17.2/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +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-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 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= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/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/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= github.com/gophercloud/gophercloud v0.6.0/go.mod h1:GICNByuaEBibcjmjvI7QvYJSZEbGkcYwAR7EZK2WMqM= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v0.0.0-20170330212424-2500245aa611/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.3.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jonboulle/clockwork v0.0.0-20141017032234-72f9bd7c4e0c/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= 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/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 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= @@ -69,69 +161,201 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.4.2 h1:3mYCb7aPxS/RU7TI1y4rkEn1oKmPRjNJLNEXgw7MH2I= +github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/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.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +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/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 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/cobra v0.0.0-20180319062004-c439c4fa0937/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff h1:VARhShG49tiji6mdRNp7JTNDtJ0FhuprF93GBQ37xGU= 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= +github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 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/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/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 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/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= +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/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o= +go.uber.org/zap v1.9.1/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= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRisrHJAN57yOiPRQItI20fU= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 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= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190909003024-a7b16738d86b h1:XfVGCX+0T4WOStkaOsJRllbsiImhB2jgVBGc9L0lPGc= golang.org/x/net v0.0.0-20190909003024-a7b16738d86b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/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= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20190312061237-fead79001313 h1:pczuHS43Cp2ktBEEmLwScxgjWsBSzdaQiKzUyf3DTTc= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190911201528-7ad0cfa0b7b5 h1:SW/0nsKCUaozCUtZTakri5laocGx/5bkDSSLrFUsa5s= +golang.org/x/sys v0.0.0-20190911201528-7ad0cfa0b7b5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/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/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= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= +gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20170731182057-09f6ed296fc6/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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/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.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0-20150622162204-20b71e5b60d7/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/square/go-jose.v2 v2.0.0-20180411045311-89060dee6a84/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= 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 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +k8s.io/api v0.0.0-20190918195907-bd6ac527cfd2 h1:bkwe5LsuANqyOwsBng5Qc4S91D2Tv0JHctAztt3YTQs= +k8s.io/api v0.0.0-20190918195907-bd6ac527cfd2/go.mod h1:AOxZTnaXR/xiarlQL0JUfwQPxjmKDvVYoRp58cA7lUo= +k8s.io/apiextensions-apiserver v0.0.0-20190918201827-3de75813f604 h1:Kl/sh+wWzYK2hWFZtwvuFECup1SbE2kXfMnhGZsoO5M= +k8s.io/apiextensions-apiserver v0.0.0-20190918201827-3de75813f604/go.mod h1:7H8sjDlWQu89yWB3FhZfsLyRCRLuoXoCoY5qtwW1q6I= +k8s.io/apimachinery v0.0.0-20190817020851-f2f3a405f61d h1:7Kns6qqhMAQWvGkxYOLSLRZ5hJO0/5pcE5lPGP2fxUw= +k8s.io/apimachinery v0.0.0-20190817020851-f2f3a405f61d/go.mod h1:3jediapYqJ2w1BFw7lAZPCx7scubsTfosqHkhXCWJKw= +k8s.io/apiserver v0.0.0-20190918200908-1e17798da8c1/go.mod h1:4FuDU+iKPjdsdQSN3GsEKZLB/feQsj1y9dhhBDVV2Ns= +k8s.io/client-go v0.0.0-20190918200256-06eb1244587a h1:huOvPq1vO7dkuw9rZPYsLGpFmyGvy6L8q6mDItgkdQ4= +k8s.io/client-go v0.0.0-20190918200256-06eb1244587a/go.mod h1:3YAcTbI2ArBRmhHns5vlHRX8YQqvkVYpz+U/N5i1mVU= +k8s.io/code-generator v0.0.0-20190612205613-18da4a14b22b/go.mod h1:G8bQwmHm2eafm5bgtX67XDZQ8CWKSGu9DekI+yN4Y5I= +k8s.io/component-base v0.0.0-20190918200425-ed2f0867c778/go.mod h1:DFWQCXgXVLiWtzFaS17KxHdlUeUymP7FLxZSkmL9/jU= +k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.3/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= +k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= +k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d h1:1P0iBJsBzxRmR+dIFnM+Iu4aLxnoa7lBqozW/0uHbT8= k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= +modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= +modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= +modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= +modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= +sigs.k8s.io/controller-runtime v0.3.0 h1:ZtdgqJXVHsIytjdmDuk0QjagnzyLq9FjojXRqIp+dU4= +sigs.k8s.io/controller-runtime v0.3.0/go.mod h1:Cw6PkEg0Sa7dAYovGT4R0tRkGhHXpYijwNxYhAnAZZk= sigs.k8s.io/kustomize/pseudo/k8s v0.1.0 h1:otg4dLFc03c3gzl+2CV8GPGcd1kk8wjXwD+UhhcCn5I= sigs.k8s.io/kustomize/pseudo/k8s v0.1.0/go.mod h1:bl/gVJgYYhJZCZdYU2BfnaKYAlqFkgbJEkpl302jEss= +sigs.k8s.io/structured-merge-diff v0.0.0-20190302045857-e85c7b244fd2/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/testing_frameworks v0.1.1 h1:cP2l8fkA3O9vekpy5Ks8mmA0NW/F7yBdXf8brkWhVrs= +sigs.k8s.io/testing_frameworks v0.1.1/go.mod h1:VVBKrHmJ6Ekkfz284YKhQePcdycOzNH9qL6ht1zEr/U= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/kstatus/status/core.go b/kstatus/status/core.go index fdc951d79..6c002d5fb 100644 --- a/kstatus/status/core.go +++ b/kstatus/status/core.go @@ -6,8 +6,8 @@ package status import ( "fmt" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" corev1 "sigs.k8s.io/kustomize/pseudo/k8s/api/core/v1" - "sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1/unstructured" ) // GetConditionsFn defines the signature for functions to compute the @@ -36,6 +36,8 @@ const ( tooFewAvailable = "LessAvailable" tooFewUpdated = "LessUpdated" tooFewReplicas = "LessReplicas" + + onDeleteUpdateStrategy = "OnDelete" ) // GetLegacyConditionsFn returns a function that can compute the status for the @@ -71,7 +73,7 @@ func stsConditions(u *unstructured.Unstructured) (*Result, error) { // updateStrategy==ondelete is a user managed statefulset. updateStrategy := GetStringField(obj, ".spec.updateStrategy.type", "") - if updateStrategy == "ondelete" { + if updateStrategy == onDeleteUpdateStrategy { return &Result{ Status: CurrentStatus, Message: "StatefulSet is using the ondelete update strategy", diff --git a/kstatus/status/doc.go b/kstatus/status/doc.go index 1cec69e49..19ded9218 100644 --- a/kstatus/status/doc.go +++ b/kstatus/status/doc.go @@ -14,7 +14,7 @@ // Computing the status of a resources can be done by calling the // Compute function in the status package. // import ( -// "sigs.k8s.io/kustomize/kstatus/status +// "sigs.k8s.io/kustomize/kstatus/status" // ) // res, err := status.Compute(resource) // diff --git a/kstatus/status/example_test.go b/kstatus/status/example_test.go index 5973dcc17..367c96e9b 100644 --- a/kstatus/status/example_test.go +++ b/kstatus/status/example_test.go @@ -7,8 +7,8 @@ import ( "fmt" "log" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" . "sigs.k8s.io/kustomize/kstatus/status" - "sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1/unstructured" "sigs.k8s.io/yaml" ) diff --git a/kstatus/status/generic.go b/kstatus/status/generic.go index 52cee50f8..ec44e6a43 100644 --- a/kstatus/status/generic.go +++ b/kstatus/status/generic.go @@ -7,8 +7,8 @@ import ( "fmt" "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" corev1 "sigs.k8s.io/kustomize/pseudo/k8s/api/core/v1" - "sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1/unstructured" ) // checkGenericProperties looks at the properties that are available on diff --git a/kstatus/status/status.go b/kstatus/status/status.go index b2401764f..67bd6f22b 100644 --- a/kstatus/status/status.go +++ b/kstatus/status/status.go @@ -7,8 +7,8 @@ import ( "time" "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" corev1 "sigs.k8s.io/kustomize/pseudo/k8s/api/core/v1" - "sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1/unstructured" ) const ( @@ -23,6 +23,7 @@ const ( FailedStatus Status = "Failed" CurrentStatus Status = "Current" TerminatingStatus Status = "Terminating" + UnknownStatus Status = "Unknown" ) // ConditionType defines the set of condition types allowed inside a Condition struct. @@ -81,8 +82,15 @@ type Condition struct { // a list of standard resources that would belong on the given resource. func Compute(u *unstructured.Unstructured) (*Result, error) { res, err := checkGenericProperties(u) - if err != nil || res != nil { - return res, err + if err != nil { + return nil, err + } + + // If res is not nil, it means the generic checks was able to determine + // the status of the resource. We don't need to check the type-specific + // rules. + if res != nil { + return res, nil } fn := GetLegacyConditionsFn(u) diff --git a/kstatus/status/status_augment_test.go b/kstatus/status/status_augment_test.go index 9f7145bb2..9d12f12c0 100644 --- a/kstatus/status/status_augment_test.go +++ b/kstatus/status/status_augment_test.go @@ -8,8 +8,8 @@ import ( "time" "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" corev1 "sigs.k8s.io/kustomize/pseudo/k8s/api/core/v1" - "sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1/unstructured" ) var pod = ` diff --git a/kstatus/status/status_compute_test.go b/kstatus/status/status_compute_test.go index 0bc80d8c0..936c06b88 100644 --- a/kstatus/status/status_compute_test.go +++ b/kstatus/status/status_compute_test.go @@ -8,8 +8,8 @@ import ( "github.com/ghodss/yaml" "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" corev1 "sigs.k8s.io/kustomize/pseudo/k8s/api/core/v1" - "sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1/unstructured" ) func y2u(t *testing.T, spec string) *unstructured.Unstructured { diff --git a/kstatus/status/util.go b/kstatus/status/util.go index 75ef8473d..75e4c511f 100644 --- a/kstatus/status/util.go +++ b/kstatus/status/util.go @@ -6,9 +6,9 @@ package status import ( "strings" + apiunstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" corev1 "sigs.k8s.io/kustomize/pseudo/k8s/api/core/v1" - apiunstructured "sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1/unstructured" - "sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/runtime" ) // newInProgressCondition creates an inProgress condition with the given diff --git a/kstatus/wait/doc.go b/kstatus/wait/doc.go new file mode 100644 index 000000000..40f7b1370 --- /dev/null +++ b/kstatus/wait/doc.go @@ -0,0 +1,57 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package wait contains functionality for getting the statuses +// of a list of kubernetes resources. Unlike the status package, +// the functions exposed in the wait package will talk to a +// live kubernetes cluster to get the latest state of resources +// and provides functionality for polling the cluster until the +// resources reach the Current status. +// +// FetchAndResolve will fetch resources from a cluster, compute the +// status for each of them and then return the results. The list of +// resources is defined as a slice of ResourceIdentifier, which is +// an interface that is implemented by the Unstructured type. It +// only requires functions for getting the apiVersion, kind, name +// and namespace of a resource. +// +// import ( +// "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +// "k8s.io/apimachinery/pkg/types" +// "sigs.k8s.io/kustomize/kstatus/wait" +// ) +// +// key := types.NamespacedName{Name: "name", Namespace: "namespace"} +// deployment := &unstructured.Unstructured{ +// Object: map[string]interface{}{ +// "apiVersion": "apps/v1", +// "kind": "Deployment", +// }, +// } +// client.Get(context.Background(), key, deployment) +// resourceIdentifiers := []wait.ResourceIdentifier{deployment} +// +// resolver := wait.NewResolver(client) +// results := resolver.FetchAndResolve(context.Background(), resourceIdentifiers) +// +// WaitForStatus also looks up status for a list of resources, but it will +// block until all the provided resources has reached the Current status or +// the wait is cancelled through the passed-in context. The function returns +// a channel that will provide updates as the status of the different +// resources change. +// +// import ( +// "sigs.k8s.io/kustomize/kstatus/wait" +// ) +// resolver := wait.NewResolver(client) +// eventsChan := resolver.WaitForStatus(context.Background(), resourceIdentifiers, 2 * time.Second) +// for { +// select { +// case event, ok := <-eventsChan: +// if !ok { +// return +// } +// fmt.Printf(event) // do something useful here. +// } +// } +package wait diff --git a/kstatus/wait/util.go b/kstatus/wait/util.go new file mode 100644 index 000000000..531df4b17 --- /dev/null +++ b/kstatus/wait/util.go @@ -0,0 +1,28 @@ +package wait + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// keyFromResourceIdentifier creates a resourceKey from a ResourceIdentifier. +func keyFromResourceIdentifier(i ResourceIdentifier) resourceKey { + return resourceKey{ + apiVersion: i.GetAPIVersion(), + kind: i.GetKind(), + name: i.GetName(), + namespace: i.GetNamespace(), + } +} + +// keyFromObject creates a resourceKey from an Object. +func keyFromObject(obj runtime.Object) resourceKey { + gvk := obj.GetObjectKind().GroupVersionKind() + r := obj.(metav1.Object) + return resourceKey{ + apiVersion: gvk.GroupVersion().String(), + kind: gvk.Kind, + name: r.GetName(), + namespace: r.GetNamespace(), + } +} diff --git a/kstatus/wait/wait.go b/kstatus/wait/wait.go new file mode 100644 index 000000000..5e1c467c6 --- /dev/null +++ b/kstatus/wait/wait.go @@ -0,0 +1,258 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package wait + +import ( + "context" + "fmt" + "time" + + "github.com/pkg/errors" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/kustomize/kstatus/status" +) + +// ResourceIdentifier defines the functions needed to identify +// a resource in a cluster. This interface is implemented by +// both unstructured.Unstructured and the standard Kubernetes types. +type ResourceIdentifier interface { + GetName() string + GetNamespace() string + GetAPIVersion() string + GetKind() string +} + +// Resolver provides the functions for resolving status of a list of resources. +type Resolver struct { + // DynamicClient is the client used to talk + // with the cluster + client client.Reader + + // statusComputeFunc defines which function should be used for computing + // the status of a resource. This is available for testing purposes. + statusComputeFunc func(u *unstructured.Unstructured) (*status.Result, error) + + // pollInterval defines the frequency with which the resolver should poll + // the cluster for the state of resources. More frequent polling will + // lead to more load on the cluster. + pollInterval time.Duration +} + +// NewResolver creates a new resolver with the provided client. Fetching +// and polling of resources will be done using the provided client. +func NewResolver(client client.Reader, pollInterval time.Duration) *Resolver { + return &Resolver{ + client: client, + statusComputeFunc: status.Compute, + pollInterval: pollInterval, + } +} + +// ResourceResult is the status result for a given resource. It provides +// information about the resource if the request was successful and an +// error if something went wrong. +type ResourceResult struct { + Result *status.Result + + Resource ResourceIdentifier + + Error error +} + +// FetchAndResolve returns the status for a list of resources. It will return +// the status for each of them individually. The slice of ResourceIdentifiers will +// only be used to get the information needed to fetch the updated state of +// the resources from the cluster. +func (r *Resolver) FetchAndResolve(ctx context.Context, resources []ResourceIdentifier) []ResourceResult { + var results []ResourceResult + + for _, resource := range resources { + u, err := r.fetchResource(ctx, resource) + if err != nil { + if k8serrors.IsNotFound(errors.Cause(err)) { + results = append(results, ResourceResult{ + Resource: resource, + Result: &status.Result{ + Status: status.CurrentStatus, + Message: "Resource does not exist", + }, + }) + } else { + results = append(results, ResourceResult{ + Result: &status.Result{ + Status: status.UnknownStatus, + Message: fmt.Sprintf("Error fetching resource from cluster: %v", err), + }, + Resource: resource, + Error: err, + }) + } + continue + } + res, err := r.statusComputeFunc(u) + results = append(results, ResourceResult{ + Result: res, + Resource: resource, + Error: err, + }) + } + + return results +} + +// Event is returned through the channel returned after a call +// to WaitForStatus. It contains an update to either an individual +// resource or to the aggregate status for the set of resources. +type Event struct { + // Type defines which type of event this is. + Type EventType + + // AggregateStatus is the aggregated status for all the provided resources. + AggregateStatus status.Status + + // EventResource is information about the event to which this event pertains. + // This is only populated for ResourceUpdate events. + EventResource *EventResource +} + +type EventType string + +const ( + // The status/message for a resource has changed. This also means the + // aggregate status might have changed. + ResourceUpdate EventType = "ResourceUpdate" + + // All resources have reached the current status. + Completed EventType = "Completed" + + // The wait was stopped before all resources could reach the + // Current status. + Aborted EventType = "Aborted" +) + +// EventResource contains information about the resource for which +// a specific Event pertains. +type EventResource struct { + // Identifier contains information that identifies which resource + // this information is about. + Identifier ResourceIdentifier + + // Status is the latest status for the given resource. + Status status.Status + + // Message is more details about the status. + Message string + + // Error is set if there was a problem identifying the status + // of the resource. For example, if polling the cluster for information + // about the resource failed. + Error error +} + +// WaitForStatus polls all the provided resources until all of them has +// reached the Current status. Updates the channel as resources change their status and +// when the wait is either completed or aborted. +func (r *Resolver) WaitForStatus(ctx context.Context, resources []ResourceIdentifier) <-chan Event { + eventChan := make(chan Event) + + go func() { + ticker := time.NewTicker(r.pollInterval) + + defer func() { + ticker.Stop() + // Make sure the channel is closed so consumers can detect that + // we have completed. + close(eventChan) + }() + + // No need to wait if we have no resources. We consider + // this a situation where the status is Current. + if len(resources) == 0 { + eventChan <- Event{ + Type: Completed, + AggregateStatus: status.CurrentStatus, + EventResource: nil, + } + return + } + + // Initiate a new waitStatus object to keep track of the + // resources while polling the state. + waitState := newWaitState(resources, r.statusComputeFunc) + + // Loop until either all resources have reached the Current status + // or until the wait is cancelled through the context. In both cases + // we will break out of the loop by returning from the function. + for { + select { + case <-ctx.Done(): + // The context has been cancelled, so report the most recent + // aggregate status, report it through the channel and then + // break out of the loop (which will close the channel). + eventChan <- Event{ + Type: Aborted, + AggregateStatus: waitState.AggregateStatus(), + } + return + case <-ticker.C: + // Every time the ticker fires, fetch all resources from the cluster, + // check if their status has changed and send an event for each resource + // with a new status. In each event, we also include the latest aggregate + // status. Finally, if the aggregate status becomes Current, send a final + // Completed type event and then return. + for id := range waitState.ResourceWaitStates { + // Make sure we have a local copy since we are passing + // pointers to this variable as parameters to functions + identifier := id + u, err := r.fetchResource(ctx, &identifier) + eventResource, updateObserved := waitState.ResourceObserved(&identifier, u, err) + // Find the aggregate status based on the new state for this resource. + aggStatus := waitState.AggregateStatus() + // We want events for changes in status for each resource, so send + // an event for this resource before checking if the aggregate status + // has become Current. + if updateObserved { + eventChan <- Event{ + Type: ResourceUpdate, + AggregateStatus: aggStatus, + EventResource: &eventResource, + } + } + // If aggregate status is Current, we are done! + if aggStatus == status.CurrentStatus { + eventChan <- Event{ + Type: Completed, + AggregateStatus: status.CurrentStatus, + } + return + } + } + } + } + }() + + return eventChan +} + +// fetchResource gets the resource given by the identifier from the cluster +// through the client available in the Resolver. It returns the resource +// as an Unstructured. +func (r *Resolver) fetchResource(ctx context.Context, identifier ResourceIdentifier) (*unstructured.Unstructured, error) { + key := types.NamespacedName{Name: identifier.GetName(), Namespace: identifier.GetNamespace()} + u := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": identifier.GetAPIVersion(), + "kind": identifier.GetKind(), + }, + } + err := r.client.Get(ctx, key, u) + //return u, err + if err != nil { + return nil, errors.Wrap(err, "error fetching resource from cluster") + } + return u, nil +} diff --git a/kstatus/wait/wait_test.go b/kstatus/wait/wait_test.go new file mode 100644 index 000000000..5e22bebd1 --- /dev/null +++ b/kstatus/wait/wait_test.go @@ -0,0 +1,561 @@ +package wait + +import ( + "context" + "fmt" + "reflect" + "testing" + "time" + + "github.com/pkg/errors" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/kustomize/kstatus/status" +) + +const ( + testTimeout = 1 * time.Minute + testPollInterval = 1 * time.Second +) + +func TestFetchAndResolve(t *testing.T) { + type result struct { + status status.Status + error bool + } + + testCases := map[string]struct { + resources []runtime.Object + expectedResults []result + }{ + "no resources": { + resources: []runtime.Object{}, + expectedResults: []result{}, + }, + "single resource": { + resources: []runtime.Object{ + &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Generation: 1, + Name: "myDeployment", + Namespace: "default", + }, + }, + }, + expectedResults: []result{ + { + status: status.InProgressStatus, + error: false, + }, + }, + }, + "multiple resources": { + resources: []runtime.Object{ + &appsv1.StatefulSet{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "StatefulSet", + }, + ObjectMeta: metav1.ObjectMeta{ + Generation: 1, + Name: "myStatefulSet", + Namespace: "default", + }, + Spec: appsv1.StatefulSetSpec{ + UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ + Type: appsv1.OnDeleteStatefulSetStrategyType, + }, + }, + Status: appsv1.StatefulSetStatus{ + ObservedGeneration: 1, + }, + }, + &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Generation: 1, + Name: "mySecret", + Namespace: "default", + }, + }, + }, + expectedResults: []result{ + { + status: status.CurrentStatus, + error: false, + }, + { + status: status.CurrentStatus, + error: false, + }, + }, + }, + } + + for tn, tc := range testCases { + tc := tc + t.Run(tn, func(t *testing.T) { + fakeClient := fake.NewFakeClientWithScheme(scheme.Scheme, tc.resources...) + resolver := NewResolver(fakeClient, testPollInterval) + resolver.statusComputeFunc = status.Compute + + var identifiers []ResourceIdentifier + for _, resource := range tc.resources { + gvk := resource.GetObjectKind().GroupVersionKind() + r := resource.(metav1.Object) + identifiers = append(identifiers, &resourceKey{ + name: r.GetName(), + namespace: r.GetNamespace(), + apiVersion: gvk.GroupVersion().String(), + kind: gvk.Kind, + }) + } + + results := resolver.FetchAndResolve(context.TODO(), identifiers) + for i, res := range results { + id := identifiers[i] + expectedRes := tc.expectedResults[i] + rid := fmt.Sprintf("%s/%s", id.GetNamespace(), id.GetName()) + if expectedRes.error { + if res.Error == nil { + t.Errorf("expected error for resource %s, but didn't get one", rid) + } + continue + } + + if res.Error != nil { + t.Errorf("didn't expected error for resource %s, but got %v", rid, res.Error) + } + + if got, want := res.Result.Status, expectedRes.status; got != want { + t.Errorf("expected status %s for resources %s, but got %s", want, rid, got) + } + } + }) + } +} + +func TestFetchAndResolveUnknownResource(t *testing.T) { + fakeClient := fake.NewFakeClientWithScheme(scheme.Scheme) + resolver := NewResolver(fakeClient, testPollInterval) + results := resolver.FetchAndResolve(context.TODO(), []ResourceIdentifier{ + &resourceKey{ + apiVersion: "apps/v1", + kind: "Deploymnet", + name: "myDeployment", + namespace: "default", + }, + }) + + if want, got := 1, len(results); want != got { + t.Errorf("expected %d results, but got %d", want, got) + } + + res := results[0] + + if want, got := status.CurrentStatus, res.Result.Status; got != want { + t.Errorf("expected status %s, but got %s", want, got) + } + + if res.Error != nil { + t.Errorf("expected no error, but got %v", res.Error) + } +} + +func TestFetchAndResolveWithFetchError(t *testing.T) { + expectedError := errors.New("failed to fetch resource") + resolver := NewResolver( + &fakeReader{ + Err: expectedError, + }, + testPollInterval, + ) + results := resolver.FetchAndResolve(context.TODO(), []ResourceIdentifier{ + &resourceKey{ + apiVersion: "apps/v1", + kind: "Deploymnet", + name: "myDeployment", + namespace: "default", + }, + }) + + if want, got := 1, len(results); want != got { + t.Errorf("expected %d results, but got %d", want, got) + } + + res := results[0] + + if want, got := status.UnknownStatus, res.Result.Status; got != want { + t.Errorf("expected status %s, but got %s", want, got) + } + + if want, got := expectedError, errors.Cause(res.Error); got != want { + t.Errorf("expected error %v, but got %v", want, got) + } +} + +func TestFetchAndResolveComputeStatusError(t *testing.T) { + expectedError := errors.New("this is a test") + resource := &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Generation: 1, + Name: "myDeployment", + Namespace: "default", + }, + } + + fakeClient := fake.NewFakeClientWithScheme(scheme.Scheme, resource) + resolver := NewResolver(fakeClient, testPollInterval) + + resolver.statusComputeFunc = func(u *unstructured.Unstructured) (*status.Result, error) { + return &status.Result{ + Status: status.UnknownStatus, + Message: "Got an error", + }, expectedError + } + results := resolver.FetchAndResolve(context.TODO(), []ResourceIdentifier{ + &resourceKey{ + apiVersion: resource.APIVersion, + kind: resource.Kind, + name: resource.GetName(), + namespace: resource.GetNamespace(), + }, + }) + + if want, got := 1, len(results); want != got { + t.Errorf("expected %d results, but got %d", want, got) + } + + res := results[0] + if want, got := expectedError, res.Error; got != want { + t.Errorf("expected error %v, but got %v", want, got) + } + + if want, got := status.UnknownStatus, res.Result.Status; got != want { + t.Errorf("expected status %s, but got %s", want, got) + } +} + +type fakeReader struct { + Called int + Err error +} + +func (f *fakeReader) Get(ctx context.Context, key client.ObjectKey, obj runtime.Object) error { + f.Called += 1 + return f.Err +} + +func (f *fakeReader) List(ctx context.Context, list runtime.Object, opts ...client.ListOption) error { + return errors.New("list not used") +} + +func TestWaitForStatus(t *testing.T) { + testCases := map[string]struct { + resources map[runtime.Object][]*status.Result + expectedResourceStatuses map[runtime.Object][]status.Status + expectedAggregateStatuses []status.Status + }{ + "no resources": { + resources: map[runtime.Object][]*status.Result{}, + expectedResourceStatuses: map[runtime.Object][]status.Status{}, + expectedAggregateStatuses: []status.Status{ + status.CurrentStatus, + }, + }, + "single resource": { + resources: map[runtime.Object][]*status.Result{ + deploymentResource: { + { + Status: status.InProgressStatus, + Message: "FirstInProgress", + }, + { + Status: status.InProgressStatus, + Message: "SecondInProgress", + }, + { + Status: status.CurrentStatus, + Message: "CurrentProgress", + }, + }, + }, + expectedResourceStatuses: map[runtime.Object][]status.Status{ + deploymentResource: { + status.InProgressStatus, + status.InProgressStatus, + status.CurrentStatus, + }, + }, + expectedAggregateStatuses: []status.Status{ + status.InProgressStatus, + status.InProgressStatus, + status.CurrentStatus, + status.CurrentStatus, + }, + }, + "multiple resource": { + resources: map[runtime.Object][]*status.Result{ + statefulSetResource: { + { + Status: status.InProgressStatus, + Message: "FirstUnknown", + }, + { + Status: status.InProgressStatus, + Message: "SecondInProgress", + }, + { + Status: status.CurrentStatus, + Message: "CurrentProgress", + }, + }, + serviceResource: { + { + Status: status.CurrentStatus, + Message: "CurrentImmediately", + }, + }, + }, + expectedResourceStatuses: map[runtime.Object][]status.Status{ + statefulSetResource: { + status.InProgressStatus, + status.InProgressStatus, + status.CurrentStatus, + }, + serviceResource: { + status.CurrentStatus, + }, + }, + expectedAggregateStatuses: []status.Status{ + status.UnknownStatus, + status.InProgressStatus, + status.InProgressStatus, + status.CurrentStatus, + status.CurrentStatus, + }, + }, + } + + for tn, tc := range testCases { + tc := tc + t.Run(tn, func(t *testing.T) { + var objs []runtime.Object + statusResults := make(map[resourceKey][]*status.Result) + var identifiers []ResourceIdentifier + + for obj, statuses := range tc.resources { + objs = append(objs, obj) + identifier := keyFromObject(obj) + identifiers = append(identifiers, &identifier) + statusResults[identifier] = statuses + } + + statusComputer := statusComputer{ + results: statusResults, + resourceCallCount: make(map[resourceKey]int), + } + + resolver := &Resolver{ + client: fake.NewFakeClientWithScheme(scheme.Scheme, objs...), + statusComputeFunc: statusComputer.Compute, + pollInterval: testPollInterval, + } + + eventChan := resolver.WaitForStatus(context.TODO(), identifiers) + + var events []Event + timer := time.NewTimer(testTimeout) + loop: + for { + select { + case event, ok := <-eventChan: + if !ok { + break loop + } + events = append(events, event) + case <-timer.C: + t.Fatalf("timeout waiting for resources to reach current status") + } + } + + var aggregateStatuses []status.Status + resourceStatuses := make(map[resourceKey][]status.Status) + for _, e := range events { + aggregateStatuses = append(aggregateStatuses, e.AggregateStatus) + if e.EventResource != nil { + identifier := keyFromResourceIdentifier(e.EventResource.Identifier) + resourceStatuses[identifier] = append(resourceStatuses[identifier], e.EventResource.Status) + } + } + + for resource, expectedStatuses := range tc.expectedResourceStatuses { + identifier := keyFromObject(resource) + actualStatuses := resourceStatuses[identifier] + if !reflect.DeepEqual(expectedStatuses, actualStatuses) { + t.Errorf("expected statuses %v for resource %s/%s, but got %v", expectedStatuses, identifier.namespace, identifier.name, actualStatuses) + } + } + + if !reflect.DeepEqual(tc.expectedAggregateStatuses, aggregateStatuses) { + t.Errorf("expected aggregate statuses %v, but got %v", tc.expectedAggregateStatuses, aggregateStatuses) + } + }) + } +} + +func TestWaitForStatusDeletedResources(t *testing.T) { + statusComputer := statusComputer{ + results: make(map[resourceKey][]*status.Result), + resourceCallCount: make(map[resourceKey]int), + } + + resolver := &Resolver{ + client: fake.NewFakeClientWithScheme(scheme.Scheme), + statusComputeFunc: statusComputer.Compute, + pollInterval: testPollInterval, + } + + depResourceIdentifier := keyFromObject(deploymentResource) + serviceResourceIdentifier := keyFromObject(serviceResource) + identifiers := []ResourceIdentifier{ + &depResourceIdentifier, + &serviceResourceIdentifier, + } + + eventChan := resolver.WaitForStatus(context.TODO(), identifiers) + + var events []Event + timer := time.NewTimer(testTimeout) +loop: + for { + select { + case event, ok := <-eventChan: + if !ok { + break loop + } + events = append(events, event) + case <-timer.C: + t.Fatalf("timeout waiting for resources to reach current status") + } + } + + expectedEvents := []struct { + aggregateStatus status.Status + hasResource bool + resourceStatus status.Status + }{ + { + aggregateStatus: status.UnknownStatus, + hasResource: true, + resourceStatus: status.CurrentStatus, + }, + { + aggregateStatus: status.CurrentStatus, + hasResource: true, + resourceStatus: status.CurrentStatus, + }, + { + aggregateStatus: status.CurrentStatus, + hasResource: false, + }, + } + + if want, got := len(expectedEvents), len(events); got != want { + t.Errorf("expected %d events, but got %d", want, got) + } + + for i, e := range events { + ee := expectedEvents[i] + if want, got := ee.aggregateStatus, e.AggregateStatus; got != want { + t.Errorf("expected event %d to be %s, but got %s", i, want, got) + } + + if ee.hasResource { + if want, got := ee.resourceStatus, e.EventResource.Status; want != got { + t.Errorf("expected resource event %d to be %s, but got %s", i, want, got) + } + } + } +} + +type statusComputer struct { + t *testing.T + + results map[resourceKey][]*status.Result + resourceCallCount map[resourceKey]int +} + +func (s *statusComputer) Compute(u *unstructured.Unstructured) (*status.Result, error) { + identifier := resourceKey{ + apiVersion: u.GetAPIVersion(), + kind: u.GetKind(), + name: u.GetName(), + namespace: u.GetNamespace(), + } + + resourceResults, ok := s.results[identifier] + if !ok { + s.t.Fatalf("No results available for resource %s/%s", u.GetNamespace(), u.GetName()) + } + callCount := s.resourceCallCount[identifier] + + var res *status.Result + if len(resourceResults) <= callCount { + res = resourceResults[len(resourceResults)-1] + } else { + res = resourceResults[callCount] + } + s.resourceCallCount[identifier] = callCount + 1 + return res, nil +} + +var deploymentResource = &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "myDeployment", + Namespace: "default", + }, +} + +var statefulSetResource = &appsv1.StatefulSet{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "StatefulSet", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "myStatefulSet", + Namespace: "default", + }, +} + +var serviceResource = &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "myService", + Namespace: "default", + }, +} diff --git a/kstatus/wait/waitstate.go b/kstatus/wait/waitstate.go new file mode 100644 index 000000000..a865938a6 --- /dev/null +++ b/kstatus/wait/waitstate.go @@ -0,0 +1,211 @@ +package wait + +import ( + "fmt" + "reflect" + + "github.com/pkg/errors" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/kustomize/kstatus/status" +) + +// resourceKey is a minimal implementation of +// the ResourceIdentifier interface. +type resourceKey struct { + name string + namespace string + apiVersion string + kind string +} + +// GetName returns the name of the resource. +func (r *resourceKey) GetName() string { + return r.name +} + +// GetNamespace returns the namespace of the resource. +func (r *resourceKey) GetNamespace() string { + return r.namespace +} + +// GetAPIVersion returns the API version of the resource. +func (r *resourceKey) GetAPIVersion() string { + return r.apiVersion +} + +// GetKind returns the Kind of the resource. +func (r *resourceKey) GetKind() string { + return r.kind +} + +// waitState keeps the state about the resources and their last +// observed state. This is used to determine any changes in state +// so events can be sent when needed. +type waitState struct { + // ResourceWaitStates contains wait state for each of the resources. + ResourceWaitStates map[resourceKey]*resourceWaitState + + // statusComputeFunc defines the function used to compute the state of + // a single resource. This is available for testing purposes. + statusComputeFunc func(u *unstructured.Unstructured) (*status.Result, error) +} + +// resourceWaitState contains state information about an individual resource. +type resourceWaitState struct { + FirstSeenGeneration *int64 + HasBeenCurrent bool + Observed bool + + LastEvent *EventResource +} + +// newWaitState creates a new waitState object and initializes it with the +// provided slice of resources and the provided statusComputeFunc. +func newWaitState(resources []ResourceIdentifier, statusComputeFunc func(u *unstructured.Unstructured) (*status.Result, error)) *waitState { + resourceWaitStates := make(map[resourceKey]*resourceWaitState) + + for _, r := range resources { + identifier := resourceKey{ + apiVersion: r.GetAPIVersion(), + kind: r.GetKind(), + name: r.GetName(), + namespace: r.GetNamespace(), + } + resourceWaitStates[identifier] = &resourceWaitState{} + } + + return &waitState{ + ResourceWaitStates: resourceWaitStates, + statusComputeFunc: statusComputeFunc, + } +} + +// AggregateStatus computes the aggregate status for all the resources. +// TODO: Ideally we would like this to be pluggable for different strategies. +func (w *waitState) AggregateStatus() status.Status { + allCurrent := true + for _, rws := range w.ResourceWaitStates { + if !rws.Observed { + return status.UnknownStatus + } + if !rws.HasBeenCurrent { + allCurrent = false + } + } + if allCurrent { + return status.CurrentStatus + } + return status.InProgressStatus +} + +// ResourceObserved notifies the waitState that we have new state for +// a resource. This also accepts an error in case fetching the resource +// from a cluster failed. It returns an EventResource object that contains +// information about the observed resource, including the identifier and +// the latest status for the resource. The function also returns a bool value +// that will be true if the status of the observed resource has changed +// since the previous observation and false it not. This is used to determine +// whether a new event should be sent based on this observation. +func (w *waitState) ResourceObserved(id ResourceIdentifier, resource *unstructured.Unstructured, err error) (EventResource, bool) { + identifier := resourceKey{ + name: id.GetName(), + namespace: id.GetNamespace(), + apiVersion: id.GetAPIVersion(), + kind: id.GetKind(), + } + + // Check for nil is not needed here as the id passed in comes + // from iterating over the keys of the map. + rws := w.ResourceWaitStates[identifier] + + eventResource := w.getEventResource(identifier, resource, err) + // If the new eventResource is identical to the previous one, we return + // with the last return value indicating this is not a new event. + if rws.LastEvent != nil && reflect.DeepEqual(eventResource, *rws.LastEvent) { + return eventResource, false + } + rws.LastEvent = &eventResource + return eventResource, true +} + +// getEventResource creates a new EventResource for the resource identified by +// the provided resourceKey. The EventResource contains information about the +// latest status for the given resource, so it computes status for the resource +// as well as check for deletion. +func (w *waitState) getEventResource(identifier resourceKey, resource *unstructured.Unstructured, err error) EventResource { + // Get the resourceWaitState for this resource. It contains information + // of the previous observed statuses. We don't need to check for nil here + // as the identifier comes from iterating over the keys of the + // ResourceWaitState map. + r := w.ResourceWaitStates[identifier] + + // If fetching the resource from the cluster failed, we don't really + // know anything about the status of the resource, so simply + // report the status as Unknown. + if err != nil && !k8serrors.IsNotFound(errors.Cause(err)) { + return EventResource{ + Identifier: &identifier, + Status: status.UnknownStatus, + Message: fmt.Sprintf("Error: %s", err), + Error: err, + } + } + + // If we get here, we have successfully fetched the resource from + // the cluster, or discovered that it doesn't exist. + r.Observed = true + + // We treat a non-existent resource as Current. This is to properly + // handle deletion scenarios. + if k8serrors.IsNotFound(errors.Cause(err)) { + r.HasBeenCurrent = true + return EventResource{ + Identifier: &identifier, + Status: status.CurrentStatus, + Message: fmt.Sprintf("Resource has been deleted"), + } + } + + // We want to capture the first seen generation of the resource. This + // allows us to discover if a resource is updated while we are waiting + // for it to become Current. + if r.FirstSeenGeneration != nil { + gen := resource.GetGeneration() + r.FirstSeenGeneration = &gen + } + + if resource.GetDeletionTimestamp() != nil { + return EventResource{ + Identifier: &identifier, + Status: status.TerminatingStatus, + Message: fmt.Sprintf("Resource is terminating"), + } + } + + statusResult, err := w.statusComputeFunc(resource) + // If we can't compute status for the resource, we report the status + // as Unknown. + if err != nil { + return EventResource{ + Identifier: &identifier, + Status: status.UnknownStatus, + Message: fmt.Sprintf("Error: %s", err), + Error: err, + } + } + + // We record whether a resource has ever been Current. This makes + // sure we can report a set of resources as being Current if all + // of them has reached the Current status at some point, but not + // necessarily at the same time. + if statusResult.Status == status.CurrentStatus { + r.HasBeenCurrent = true + } + + return EventResource{ + Identifier: &identifier, + Status: statusResult.Status, + Message: statusResult.Message, + } +}