diff --git a/chaoscenter/subscriber/go.mod b/chaoscenter/subscriber/go.mod new file mode 100644 index 00000000000..0334abcd461 --- /dev/null +++ b/chaoscenter/subscriber/go.mod @@ -0,0 +1,92 @@ +module subscriber + +go 1.18 + +require ( + github.com/argoproj/argo-workflows/v3 v3.3.1 + github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 + github.com/gorilla/websocket v1.5.0 + github.com/kelseyhightower/envconfig v1.4.0 + github.com/litmuschaos/chaos-operator v0.0.0-20230109130222-de7c74a937a9 + github.com/sirupsen/logrus v1.8.1 + gopkg.in/yaml.v2 v2.4.0 + k8s.io/api v0.23.3 + k8s.io/apimachinery v0.23.3 + k8s.io/client-go v12.0.0+incompatible +) + +require ( + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful v2.15.0+incompatible // indirect + github.com/go-logr/logr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/swag v0.19.15 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-cmp v0.5.7 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/googleapis/gnostic v0.5.5 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/hashicorp/golang-lru v0.5.3 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/testify v1.8.2 // indirect + golang.org/x/net v0.6.0 // indirect + golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + golang.org/x/text v0.7.0 // indirect + golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c // indirect + google.golang.org/grpc v1.44.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/klog/v2 v2.40.1 // indirect + k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf // indirect + k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect + sigs.k8s.io/controller-runtime v0.11.1 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) + +// Pinned to kubernetes-1.21.2 +replace ( + github.com/docker/docker => github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309 + + github.com/emicklei/go-restful => github.com/emicklei/go-restful v2.16.0+incompatible + + golang.org/x/net => golang.org/x/net v0.0.0-20220906165146-f3363e06e74c + golang.org/x/text => golang.org/x/text v0.3.8 + k8s.io/api => k8s.io/api v0.21.2 + k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.21.2 + k8s.io/apimachinery => k8s.io/apimachinery v0.21.2 + k8s.io/apiserver => k8s.io/apiserver v0.21.2 + k8s.io/cli-runtime => k8s.io/cli-runtime v0.21.2 + k8s.io/client-go => k8s.io/client-go v0.21.2 + k8s.io/cloud-provider => k8s.io/cloud-provider v0.21.2 + k8s.io/code-generator => k8s.io/code-generator v0.21.2 + k8s.io/component-base => k8s.io/component-base v0.21.2 + k8s.io/cri-api => k8s.io/cri-api v0.21.2 + k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.21.2 + k8s.io/infra-bootstrap => k8s.io/infra-bootstrap v0.21.2 + k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.21.2 + k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.21.2 + k8s.io/kube-proxy => k8s.io/kube-proxy v0.21.2 + k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.21.2 + k8s.io/kubectl => k8s.io/kubectl v0.21.2 + k8s.io/kubelet => k8s.io/kubelet v0.21.2 + k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.21.2 + k8s.io/metrics => k8s.io/metrics v0.21.2 + k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.21.2 +) diff --git a/chaoscenter/subscriber/go.sum b/chaoscenter/subscriber/go.sum new file mode 100644 index 00000000000..f5886cda7d5 --- /dev/null +++ b/chaoscenter/subscriber/go.sum @@ -0,0 +1,587 @@ +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.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +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/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/argoproj/argo-workflows/v3 v3.3.1 h1:cNhY6JRlMvwu0qhfCWtS/XmQ8nm2OjR7CBZ3K6zbcKI= +github.com/argoproj/argo-workflows/v3 v3.3.1/go.mod h1:/VCBeEAgrhuNlrmlrdqS75mx2x77FwbotAjTmqpoAE4= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +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/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +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/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v2.16.0+incompatible h1:rgqiKNjTnFQA6kkhFe16D8epTksy9HQ1MyrbDXSdYhM= +github.com/emicklei/go-restful v2.16.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= +github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew= +github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +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/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +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/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +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/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +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/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +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.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= +github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= +github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +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.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +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.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +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/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/litmuschaos/chaos-operator v0.0.0-20230109130222-de7c74a937a9 h1:5VkAw2Zqf6xXEElnmFTVuNiP+YVZqH2Qr2t/yv6eI5g= +github.com/litmuschaos/chaos-operator v0.0.0-20230109130222-de7c74a937a9/go.mod h1:jRA6jKGed6ytLDJ7897yr2Kr2ygg+cuRXJqwvNmE4Bw= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +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= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +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.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/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/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.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +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.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +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-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +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/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +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.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20220906165146-f3363e06e74c h1:yKufUcDwucU5urd+50/Opbt4AYpqthk7wHpHok8f1lo= +golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +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/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +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/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190215142949-d0b11bdaac8a/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-20190412213103-97732733099d/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-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/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-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/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-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/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-20190621195816-6e04913cbbac/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-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +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/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/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-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c h1:TU4rFa5APdKTq0s6B7WTsH6Xmx0Knj86s6Biz56mErE= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +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.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.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.3/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/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +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/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.21.2 h1:vz7DqmRsXTCSa6pNxXwQ1IYeAZgdIsua+DZU+o+SX3Y= +k8s.io/api v0.21.2/go.mod h1:Lv6UGJZ1rlMI1qusN8ruAp9PUBFyBwpEHAdG24vIsiU= +k8s.io/apimachinery v0.21.2 h1:vezUc/BHqWlQDnZ+XkrpXSmnANSLbpnlpwo0Lhk0gpc= +k8s.io/apimachinery v0.21.2/go.mod h1:CdTY8fU/BlvAbJ2z/8kBwimGki5Zp8/fbVuLY8gJumM= +k8s.io/client-go v0.21.2 h1:Q1j4L/iMN4pTw6Y4DWppBoUxgKO8LbffEMVEV00MUp0= +k8s.io/client-go v0.21.2/go.mod h1:HdJ9iknWpbl3vMGtib6T2PyI/VYxiZfq936WNVHBRrA= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/klog/v2 v2.40.1 h1:P4RRucWk/lFOlDdkAr3mc7iWFkgKrZY9qZMAgek06S4= +k8s.io/klog/v2 v2.40.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= +k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf h1:M9XBsiMslw2lb2ZzglC0TOkBPK5NQi0/noUrdnoFwUg= +k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= +k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= +k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/controller-runtime v0.11.1 h1:7YIHT2QnHJArj/dk9aUkYhfqfK5cIxPOX5gPECfdZLU= +sigs.k8s.io/controller-runtime v0.11.1/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eMbCQznLP5zcqA= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y= +sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/chaoscenter/subscriber/pkg/events/chaosengine.go b/chaoscenter/subscriber/pkg/events/chaosengine.go new file mode 100644 index 00000000000..313b13fec37 --- /dev/null +++ b/chaoscenter/subscriber/pkg/events/chaosengine.go @@ -0,0 +1,211 @@ +package events + +import ( + "context" + "errors" + "fmt" + "strconv" + "subscriber/pkg/k8s" + "subscriber/pkg/types" + + "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" + chaosTypes "github.com/litmuschaos/chaos-operator/api/litmuschaos/v1alpha1" + "github.com/litmuschaos/chaos-operator/pkg/client/clientset/versioned" + litmusV1alpha1 "github.com/litmuschaos/chaos-operator/pkg/client/clientset/versioned/typed/litmuschaos/v1alpha1" + "github.com/litmuschaos/chaos-operator/pkg/client/informers/externalversions" + "github.com/sirupsen/logrus" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + mergeType "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/cache" +) + +// ChaosEventWatcher initializes the Litmus ChaosEngine event watcher +func ChaosEventWatcher(stopCh chan struct{}, stream chan types.WorkflowEvent, infraData map[string]string) { + startTime, err := strconv.Atoi(infraData["START_TIME"]) + if err != nil { + logrus.WithError(err).Fatal("failed to parse startTime") + } + + cfg, err := k8s.GetKubeConfig() + if err != nil { + logrus.WithError(err).Fatal("could not get kube config") + } + + // ClientSet to create Informer + clientSet, err := versioned.NewForConfig(cfg) + if err != nil { + logrus.WithError(err).Fatal("could not generate dynamic client for config") + } + + // Create a factory object to watch workflows depending on default scope + f := externalversions.NewSharedInformerFactoryWithOptions(clientSet, resyncPeriod, + externalversions.WithTweakListOptions(func(list *v1.ListOptions) { + list.LabelSelector = fmt.Sprintf("infra_id=%s,type=standalone_workflow", InfraID) + })) + + informer := f.Litmuschaos().V1alpha1().ChaosEngines().Informer() + if InfraScope == "namespace" { + f = externalversions.NewSharedInformerFactoryWithOptions(clientSet, resyncPeriod, externalversions.WithNamespace(InfraNamespace), + externalversions.WithTweakListOptions(func(list *v1.ListOptions) { + list.LabelSelector = fmt.Sprintf("infra_id=%s,type=standalone_workflow", InfraID) + })) + informer = f.Litmuschaos().V1alpha1().ChaosEngines().Informer() + } + + go startWatchEngine(stopCh, informer, stream, int64(startTime)) +} + +// handles the different events events - add, update and delete +func startWatchEngine(stopCh <-chan struct{}, s cache.SharedIndexInformer, stream chan types.WorkflowEvent, startTime int64) { + handlers := cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + chaosEventHandler(obj, "ADD", stream, startTime) + }, + UpdateFunc: func(oldObj, obj interface{}) { + chaosEventHandler(obj, "UPDATE", stream, startTime) + }, + } + + s.AddEventHandler(handlers) + s.Run(stopCh) +} + +// responsible for extracting the required data from the event and streaming +func chaosEventHandler(obj interface{}, eventType string, stream chan types.WorkflowEvent, startTime int64) { + workflowObj := obj.(*chaosTypes.ChaosEngine) + if workflowObj.Labels["workflow_id"] == "" { + logrus.WithFields(map[string]interface{}{ + "uid": string(workflowObj.ObjectMeta.UID), + "wf_id": workflowObj.Labels["workflow_id"], + "infra_id": workflowObj.Labels["infra_id"], + }).Printf("CHAOSENGINE RUN IGNORED [INVALID METADATA]") + return + } + + if workflowObj.ObjectMeta.CreationTimestamp.Unix() < startTime { + return + } + + cfg, err := k8s.GetKubeConfig() + if err != nil { + logrus.WithError(err).Fatal("could not get kube config") + } + + chaosClient, err := litmusV1alpha1.NewForConfig(cfg) + if err != nil { + logrus.WithError(err).Fatal("could not get Chaos ClientSet") + } + + nodes := make(map[string]types.Node) + logrus.Print("STANDALONE CHAOSENGINE EVENT ", workflowObj.UID, " ", eventType) + var cd *types.ChaosData = nil + + //extracts chaos data + cd, err = getChaosData(v1alpha1.NodeStatus{StartedAt: workflowObj.ObjectMeta.CreationTimestamp}, workflowObj.Name, workflowObj.Namespace, chaosClient) + if err != nil { + logrus.WithError(err).Print("FAILED PARSING CHAOS ENGINE CRD") + } + + // considering chaos events has only 1 artifact with manifest as raw data + finTime := int64(-1) + if workflowObj.Status.EngineStatus == chaosTypes.EngineStatusCompleted || workflowObj.Status.EngineStatus == chaosTypes.EngineStatusStopped { + if len(workflowObj.Status.Experiments) > 0 { + finTime = workflowObj.Status.Experiments[0].LastUpdateTime.Unix() + } + } + + nodes[workflowObj.Name] = types.Node{ + Name: workflowObj.Name, + Phase: "Succeeded", + StartedAt: StrConvTime(workflowObj.CreationTimestamp.Unix()), + FinishedAt: StrConvTime(finTime), + Children: []string{workflowObj.Name + "-engine"}, + Type: "Steps", + } + details := types.Node{ + Name: workflowObj.Name, + Phase: mapStatus(workflowObj.Status.EngineStatus), + Type: "ChaosEngine", + StartedAt: StrConvTime(workflowObj.CreationTimestamp.Unix()), + FinishedAt: StrConvTime(finTime), + Children: []string{}, + ChaosExp: cd, + Message: string(workflowObj.Status.EngineStatus), + } + + nodes[workflowObj.Name+"-engine"] = details + workflow := types.WorkflowEvent{ + WorkflowType: "chaosengine", + WorkflowID: workflowObj.Labels["workflow_id"], + EventType: eventType, + UID: string(workflowObj.ObjectMeta.UID), + Namespace: workflowObj.ObjectMeta.Namespace, + Name: workflowObj.ObjectMeta.Name, + CreationTimestamp: StrConvTime(workflowObj.ObjectMeta.CreationTimestamp.Unix()), + Phase: details.Phase, + Message: details.Message, + StartedAt: details.StartedAt, + FinishedAt: details.FinishedAt, + Nodes: nodes, + } + + //stream + stream <- workflow +} + +//StopChaosEngineState is used to patch all the chaosEngines with engineState=stop +func StopChaosEngineState(namespace string, workflowRunID *string) error { + ctx := context.TODO() + + //Define the GVR + resourceType := schema.GroupVersionResource{ + Group: "litmuschaos.io", + Version: "v1alpha1", + Resource: "chaosengines", + } + + //Generate the dynamic client + _, dynamicClient, err := k8s.GetDynamicAndDiscoveryClient() + if err != nil { + return errors.New("failed to get dynamic client, error: " + err.Error()) + } + + listOption := v1.ListOptions{} + + if workflowRunID != nil { + listOption.LabelSelector = fmt.Sprintf("workflow_run_id=%s", *workflowRunID) + } + + //List all chaosEngines present in the particular namespace + chaosEngines, err := dynamicClient.Resource(resourceType).Namespace(namespace).List(context.TODO(), listOption) + if err != nil { + return errors.New("failed to list chaosengines: " + err.Error()) + } + + //Foe every chaosEngine patch the engineState to Stop + for _, val := range chaosEngines.Items { + patch := []byte(`{"spec":{"engineState":"stop"}}`) + patched, err := dynamicClient.Resource(resourceType).Namespace(namespace).Patch(ctx, val.GetName(), mergeType.MergePatchType, patch, v1.PatchOptions{}) + if err != nil { + return errors.New("failed to patch chaosengines: " + err.Error()) + } + if patched != nil { + logrus.Info("Successfully patched ChaosEngine: ", patched.GetName()) + } + } + return nil +} + +func mapStatus(status chaosTypes.EngineStatus) string { + switch status { + case chaosTypes.EngineStatusInitialized: + return "Running" + case chaosTypes.EngineStatusCompleted: + return "Succeeded" + case chaosTypes.EngineStatusStopped: + return "Skipped" + default: + return "Running" + } +} diff --git a/chaoscenter/subscriber/pkg/events/util.go b/chaoscenter/subscriber/pkg/events/util.go new file mode 100644 index 00000000000..4e60532865e --- /dev/null +++ b/chaoscenter/subscriber/pkg/events/util.go @@ -0,0 +1,195 @@ +package events + +import ( + "context" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "regexp" + "strconv" + "strings" + "subscriber/pkg/k8s" + "subscriber/pkg/types" + + "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" + v1alpha13 "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" + wfclientset "github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned" + v1alpha12 "github.com/litmuschaos/chaos-operator/pkg/client/clientset/versioned/typed/litmuschaos/v1alpha1" + "github.com/sirupsen/logrus" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/serializer/yaml" +) + +// util function, extracts the chaos data using the litmus go-client +func getChaosData(nodeStatus v1alpha13.NodeStatus, engineName, engineNS string, chaosClient *v1alpha12.LitmuschaosV1alpha1Client) (*types.ChaosData, error) { + cd := &types.ChaosData{} + cd.EngineName = engineName + cd.Namespace = engineNS + crd, err := chaosClient.ChaosEngines(cd.Namespace).Get(context.Background(), cd.EngineName, v1.GetOptions{}) + if err != nil { + return nil, err + } + if nodeStatus.StartedAt.Unix() > crd.ObjectMeta.CreationTimestamp.Unix() { + logrus.Errorf("chaosengine resource older than current events node | workflow time : %v | engine time : %v", nodeStatus.StartedAt.Unix(), crd.ObjectMeta.CreationTimestamp.Unix()) + return nil, nil + } + cd.ProbeSuccessPercentage = "0" + cd.FailStep = "" + cd.EngineUID = string(crd.ObjectMeta.UID) + cd.EngineContext = string(crd.Labels["context"]) + + if strings.ToLower(string(crd.Status.EngineStatus)) == "stopped" { + cd.ExperimentVerdict = "Fail" + cd.ExperimentStatus = string(crd.Status.EngineStatus) + } + if len(crd.Status.Experiments) == 0 { + return cd, nil + } + + // considering chaosengine will only have 1 experiment + cd.ExperimentPod = crd.Status.Experiments[0].ExpPod + cd.RunnerPod = crd.Status.Experiments[0].Runner + cd.ExperimentStatus = string(crd.Status.Experiments[0].Status) + cd.ExperimentName = crd.Status.Experiments[0].Name + cd.LastUpdatedAt = strconv.FormatInt(crd.Status.Experiments[0].LastUpdateTime.Unix(), 10) + cd.ExperimentVerdict = crd.Status.Experiments[0].Verdict + if strings.ToLower(string(crd.Status.EngineStatus)) == "stopped" || (strings.ToLower(string(crd.Status.EngineStatus)) == "completed" && strings.ToLower(cd.ExperimentVerdict) != "pass") { + cd.ExperimentVerdict = "Fail" + cd.ExperimentStatus = string(crd.Status.EngineStatus) + } + + if len(crd.Status.Experiments) == 1 { + expRes, err := chaosClient.ChaosResults(cd.Namespace).Get(context.Background(), crd.Name+"-"+crd.Status.Experiments[0].Name, v1.GetOptions{}) + if err != nil { + return cd, err + } + // annotations sometimes cause failure in gql message escaping + expRes.Annotations = nil + cd.ChaosResult = expRes + cd.ProbeSuccessPercentage = expRes.Status.ExperimentStatus.ProbeSuccessPercentage + if expRes.Status.ExperimentStatus.FailureOutput != nil { + cd.FailStep = expRes.Status.ExperimentStatus.FailureOutput.FailedStep + } + } + return cd, nil +} + +//CheckChaosData util function, checks if event is a chaos-exp event, if so - extract the chaos data +func CheckChaosData(nodeStatus v1alpha13.NodeStatus, workflowNS string, chaosClient *v1alpha12.LitmuschaosV1alpha1Client) (string, *types.ChaosData, error) { + nodeType := string(nodeStatus.Type) + var cd *types.ChaosData = nil + // considering chaos events has only 1 artifact with manifest as raw data + data := nodeStatus.Inputs.Artifacts[0].Raw.Data + obj := &unstructured.Unstructured{} + decUnstructured := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) + _, _, err := decUnstructured.Decode([]byte(data), nil, obj) + if err == nil && obj.GetKind() == "ChaosEngine" { + nodeType = "ChaosEngine" + if nodeStatus.Phase != "Pending" { + name := obj.GetName() + if obj.GetGenerateName() != "" { + log, err := k8s.GetLogs(nodeStatus.ID, workflowNS, "main") + if err != nil { + return nodeType, nil, err + } + name = getNameFromLog(log) + if name == "" { + return nodeType, nil, errors.New("Chaos-Engine Generated Name couldn't be retrieved") + } + } + cd, err = getChaosData(nodeStatus, name, obj.GetNamespace(), chaosClient) + return nodeType, cd, err + } + } + return nodeType, nil, nil +} + +func getNameFromLog(log string) string { + s := regexp.MustCompile(`\n\nChaosEngine Name : .*\n\n`).FindString(log) + if s == "" { + return "" + } + s = strings.TrimSpace(s) + name := strings.Split(s, ": ") + if len(name) != 2 { + return "" + } + return name[1] +} + +// converts unix timestamp to string +func StrConvTime(time int64) string { + if time < 0 { + return "" + } else { + return strconv.FormatInt(time, 10) + } +} + +func GetWorkflowObj(uid string) (*v1alpha1.Workflow, error) { + ctx := context.TODO() + conf, err := k8s.GetKubeConfig() + if err != nil { + return nil, err + } + + // create the events client + wfClient := wfclientset.NewForConfigOrDie(conf).ArgoprojV1alpha1().Workflows(InfraNamespace) + listWf, err := wfClient.List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, err + } + + for _, wf := range listWf.Items { + if string(wf.UID) == uid { + return &wf, nil + } + } + + return nil, nil +} + +func ListWorkflowObject(wfid string) (*v1alpha1.WorkflowList, error) { + ctx := context.TODO() + conf, err := k8s.GetKubeConfig() + if err != nil { + return nil, err + } + listOption := v1.ListOptions{} + listOption.LabelSelector = fmt.Sprintf("workflow_id=%s", wfid) + // create the events client + wfClient := wfclientset.NewForConfigOrDie(conf).ArgoprojV1alpha1().Workflows(InfraNamespace) + listWf, err := wfClient.List(ctx, listOption) + if err != nil { + return nil, err + } + return listWf, nil +} + +//GenerateWorkflowPayload generate graphql mutation payload for events event +func GenerateWorkflowPayload(cid, accessKey, version, completed string, wfEvent types.WorkflowEvent) ([]byte, error) { + infraID := `{infraID: \"` + cid + `\", version: \"` + version + `\", accessKey: \"` + accessKey + `\"}` + for id, event := range wfEvent.Nodes { + event.Message = strings.Replace(event.Message, `"`, ``, -1) + wfEvent.Nodes[id] = event + } + + data, err := json.Marshal(wfEvent) + if err != nil { + return nil, err + } + + executionData := base64.StdEncoding.EncodeToString(data) + + mutation := `{ experimentID: \"` + wfEvent.WorkflowID + `\", experimentRunID: \"` + wfEvent.UID + `\", revisionID:\"` + wfEvent.RevisionID + `\", completed: ` + completed + `, experimentName:\"` + wfEvent.Name + `\", infraID: ` + infraID + `, updatedBy:\"` + wfEvent.UpdatedBy + `\", executionData:\"` + executionData + `\"}` + + if wfEvent.NotifyID != nil { + mutation = `{ experimentID: \"` + wfEvent.WorkflowID + `\", experimentRunID: \"` + wfEvent.UID + `\", revisionID:\"` + wfEvent.RevisionID + `\", notifyID:\"` + *wfEvent.NotifyID + `\", completed: ` + completed + `, experimentName:\"` + wfEvent.Name + `\", infraID: ` + infraID + `, updatedBy:\"` + wfEvent.UpdatedBy + `\", executionData:\"` + executionData + `\"}` + } + + var payload = []byte(`{"query":"mutation { chaosExperimentRun(request:` + mutation + ` )}"}`) + return payload, nil +} diff --git a/chaoscenter/subscriber/pkg/events/workflow.go b/chaoscenter/subscriber/pkg/events/workflow.go new file mode 100644 index 00000000000..b534e4b3933 --- /dev/null +++ b/chaoscenter/subscriber/pkg/events/workflow.go @@ -0,0 +1,278 @@ +package events + +import ( + "errors" + "fmt" + "os" + "strconv" + "strings" + "time" + + "subscriber/pkg/graphql" + + "subscriber/pkg/k8s" + "subscriber/pkg/types" + + "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" + "github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned" + "github.com/argoproj/argo-workflows/v3/pkg/client/informers/externalversions" + litmusV1alpha1 "github.com/litmuschaos/chaos-operator/pkg/client/clientset/versioned/typed/litmuschaos/v1alpha1" + "github.com/sirupsen/logrus" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/cache" +) + +// 0 means no resync +const ( + resyncPeriod time.Duration = 0 +) + +var eventMap map[string]types.WorkflowEvent + +func init() { + eventMap = make(map[string]types.WorkflowEvent) +} + +var ( + InfraScope = os.Getenv("INFRA_SCOPE") + InfraNamespace = os.Getenv("INFRA_NAMESPACE") + InfraID = os.Getenv("INFRA_ID") +) + +// WorkflowEventWatcher initializes the Argo Workflow event watcher +func WorkflowEventWatcher(stopCh chan struct{}, stream chan types.WorkflowEvent, infraData map[string]string) { + startTime, err := strconv.Atoi(infraData["START_TIME"]) + if err != nil { + logrus.WithError(err).Fatal("Failed to parse START_TIME") + } + cfg, err := k8s.GetKubeConfig() + if err != nil { + logrus.WithError(err).Fatal("Could not get kube config") + } + // ClientSet to create Informer + clientSet, err := versioned.NewForConfig(cfg) + if err != nil { + logrus.WithError(err).Fatal("Could not generate dynamic client for config") + } + // Create a factory object to watch workflows depending on default scope + f := externalversions.NewSharedInformerFactoryWithOptions(clientSet, resyncPeriod, + externalversions.WithTweakListOptions(func(list *v1.ListOptions) { + list.LabelSelector = fmt.Sprintf("infra_id=%s,workflows.argoproj.io/controller-instanceid=%s", InfraID, InfraID) + })) + informer := f.Argoproj().V1alpha1().Workflows().Informer() + if InfraScope == "namespace" { + f = externalversions.NewSharedInformerFactoryWithOptions(clientSet, resyncPeriod, externalversions.WithNamespace(InfraNamespace), + externalversions.WithTweakListOptions(func(list *v1.ListOptions) { + list.LabelSelector = fmt.Sprintf("infra_id=%s,workflows.argoproj.io/controller-instanceid=%s", InfraID, InfraID) + })) + informer = f.Argoproj().V1alpha1().Workflows().Informer() + // Start Event Watch + } + go startWatchWorkflow(stopCh, informer, stream, int64(startTime)) +} + +// handles the different events events - add, update and delete +func startWatchWorkflow(stopCh <-chan struct{}, s cache.SharedIndexInformer, stream chan types.WorkflowEvent, startTime int64) { + handlers := cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + workflowObj := obj.(*v1alpha1.Workflow) + workflow, err := WorkflowEventHandler(workflowObj, "ADD", startTime) + if err != nil { + logrus.Error(err) + } + + //stream + stream <- workflow + + }, + UpdateFunc: func(oldObj, obj interface{}) { + workflowObj := obj.(*v1alpha1.Workflow) + workflow, err := WorkflowEventHandler(workflowObj, "UPDATE", startTime) + if err != nil { + logrus.Error(err) + } + //stream + stream <- workflow + + }, + } + s.AddEventHandler(handlers) + s.Run(stopCh) +} + +// WorkflowEventHandler is responsible for extracting the required data from the event and streaming +func WorkflowEventHandler(workflowObj *v1alpha1.Workflow, eventType string, startTime int64) (types.WorkflowEvent, error) { + if workflowObj.Labels["workflow_id"] == "" { + logrus.WithFields(map[string]interface{}{ + "uid": string(workflowObj.ObjectMeta.UID), + "wf_id": workflowObj.Labels["workflow_id"], + "instance_id": workflowObj.Labels["workflows.argoproj.io/controller-instanceid"], + }).Printf("WORKFLOW RUN IGNORED [INVALID METADATA]") + return types.WorkflowEvent{}, nil + } + + if workflowObj.ObjectMeta.CreationTimestamp.Unix() < startTime { + return types.WorkflowEvent{}, nil + } + + cfg, err := k8s.GetKubeConfig() + if err != nil { + logrus.WithError(err).Fatal("Could not get kube config") + } + + chaosClient, err := litmusV1alpha1.NewForConfig(cfg) + if err != nil { + return types.WorkflowEvent{}, errors.New("could not get Chaos ClientSet: " + err.Error()) + } + + nodes := make(map[string]types.Node) + logrus.Info("Workflow RUN_ID: ", workflowObj.UID, " and event type: ", eventType) + + for _, nodeStatus := range workflowObj.Status.Nodes { + + var ( + nodeType = string(nodeStatus.Type) + cd *types.ChaosData = nil + ) + + // considering chaos events has only 1 artifact with manifest as raw data + if nodeStatus.Type == "Pod" && nodeStatus.Inputs != nil && len(nodeStatus.Inputs.Artifacts) == 1 && nodeStatus.Inputs.Artifacts[0].Raw != nil { + //extracts chaos data + nodeType, cd, err = CheckChaosData(nodeStatus, workflowObj.ObjectMeta.Namespace, chaosClient) + if err != nil { + logrus.WithError(err).Print("Failed to parse ChaosEngine CRD") + } + } + + details := types.Node{ + Name: nodeStatus.DisplayName, + Phase: string(nodeStatus.Phase), + Type: nodeType, + StartedAt: StrConvTime(nodeStatus.StartedAt.Unix()), + FinishedAt: StrConvTime(nodeStatus.FinishedAt.Unix()), + Children: nodeStatus.Children, + ChaosExp: cd, + Message: nodeStatus.Message, + } + if cd != nil && strings.ToLower(cd.ExperimentVerdict) == "fail" { + details.Phase = "Failed" + details.Message = "Chaos Experiment Failed" + cd.ExperimentVerdict = "Fail" + } else if cd != nil && strings.ToLower(cd.ExperimentVerdict) == "pass" { + details.Phase = "Passed" + cd.ExperimentVerdict = "Pass" + } + nodes[nodeStatus.ID] = details + } + + status := updateWorkflowStatus(workflowObj.Status.Phase) + + finishedTime := StrConvTime(workflowObj.Status.FinishedAt.Unix()) + if eventType == "STOPPED" { + status = "Stopped" + finishedTime = StrConvTime(time.Now().Unix()) + nodes[workflowObj.Name] = types.Node{ + Name: workflowObj.Name, + Phase: "Stopped", + StartedAt: StrConvTime(workflowObj.CreationTimestamp.Unix()), + FinishedAt: finishedTime, + Children: nodes[workflowObj.Name].Children, + Type: nodes[workflowObj.Name].Type, + } + } + var notifyID *string = nil + + if id, ok := workflowObj.Labels["notify_id"]; ok { + notifyID = &id + } + + workflow := types.WorkflowEvent{ + WorkflowType: "events", + WorkflowID: workflowObj.Labels["workflow_id"], + EventType: eventType, + RevisionID: workflowObj.Labels["revision_id"], + NotifyID: notifyID, + UID: string(workflowObj.ObjectMeta.UID), + Namespace: workflowObj.ObjectMeta.Namespace, + Name: workflowObj.ObjectMeta.Name, + CreationTimestamp: StrConvTime(workflowObj.ObjectMeta.CreationTimestamp.Unix()), + Phase: status, + Message: workflowObj.Status.Message, + StartedAt: StrConvTime(workflowObj.Status.StartedAt.Unix()), + FinishedAt: finishedTime, + Nodes: nodes, + UpdatedBy: workflowObj.Labels["updated_by"], + } + + return workflow, nil +} + +// SendWorkflowUpdates generates graphql mutation to send events updates to graphql server +func SendWorkflowUpdates(infraData map[string]string, event types.WorkflowEvent) (string, error) { + if wfEvent, ok := eventMap[event.UID]; ok { + for key, node := range wfEvent.Nodes { + if node.Type == "ChaosEngine" && node.ChaosExp != nil && event.Nodes[key].ChaosExp == nil { + nodeData := event.Nodes[key] + nodeData.ChaosExp = node.ChaosExp + nodeData.Phase = node.Phase + nodeData.Message = node.Message + event.Nodes[key] = nodeData + } + if event.Phase == "Stopped" { + if event.Nodes[key].Phase == "Running" || event.Nodes[key].Phase == "Pending" { + nodeData := event.Nodes[key] + nodeData.Phase = "Stopped" + event.Nodes[key] = nodeData + } + } + } + } + eventMap[event.UID] = event + + // generate graphql payload + payload, err := GenerateWorkflowPayload(infraData["INFRA_ID"], infraData["ACCESS_KEY"], infraData["VERSION"], "false", event) + if err != nil { + return "", errors.New("Error while generating graphql payload from the workflow event" + err.Error()) + } + + if event.FinishedAt != "" { + payload, err = GenerateWorkflowPayload(infraData["INFRA_ID"], infraData["ACCESS_KEY"], infraData["VERSION"], "true", event) + delete(eventMap, event.UID) + } + + body, err := graphql.SendRequest(infraData["SERVER_ADDR"], payload) + if err != nil { + return "", err + } + + return body, nil +} + +func WorkflowUpdates(infraData map[string]string, event chan types.WorkflowEvent) { + // listen on the channel for streaming event updates + for eventData := range event { + response, err := SendWorkflowUpdates(infraData, eventData) + if err != nil { + logrus.Print(err.Error()) + } + + logrus.Print("Response from the server: ", response) + } +} + +func updateWorkflowStatus(status v1alpha1.WorkflowPhase) string { + switch status { + case v1alpha1.WorkflowRunning: + return "Running" + case v1alpha1.WorkflowSucceeded: + return "Completed" + case v1alpha1.WorkflowFailed: + return "Completed" + case v1alpha1.WorkflowPending: + return "Pending" + case v1alpha1.WorkflowError: + return "Error" + default: + return "Pending" + } +} diff --git a/chaoscenter/subscriber/pkg/graphql/operations.go b/chaoscenter/subscriber/pkg/graphql/operations.go new file mode 100644 index 00000000000..15a96429eb3 --- /dev/null +++ b/chaoscenter/subscriber/pkg/graphql/operations.go @@ -0,0 +1,43 @@ +package graphql + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "net/http" + "strconv" + "strings" +) + +func SendRequest(server string, payload []byte) (string, error) { + req, err := http.NewRequest("POST", server, bytes.NewBuffer(payload)) + if err != nil { + return "", err + } + req.Header.Set("Content-Type", "application/json") + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "", err + } + + body, err := ioutil.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + return "", err + } + + return string(body), nil +} + +//MarshalGQLData processes event data into proper format acceptable by graphql +func MarshalGQLData(gqlData interface{}) (string, error) { + data, err := json.Marshal(gqlData) + if err != nil { + return "", err + } + + // process the marshalled data to make it graphql compatible + processed := strconv.Quote(string(data)) + processed = strings.Replace(processed, `\"`, `\\\"`, -1) + return processed, nil +} diff --git a/chaoscenter/subscriber/pkg/k8s/client.go b/chaoscenter/subscriber/pkg/k8s/client.go new file mode 100644 index 00000000000..62f834bb3a7 --- /dev/null +++ b/chaoscenter/subscriber/pkg/k8s/client.go @@ -0,0 +1,67 @@ +package k8s + +import ( + wfclientset "github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned" + v1alpha12 "github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned/typed/workflow/v1alpha1" + "k8s.io/client-go/discovery" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" +) + +var KubeConfig *string + +//getKubeConfig setup the config for access cluster resource +func GetKubeConfig() (*rest.Config, error) { + // Use in-cluster config if kubeconfig path is not specified + if *KubeConfig == "" { + return rest.InClusterConfig() + } + return clientcmd.BuildConfigFromFlags("", *KubeConfig) +} + +func GetGenericK8sClient() (*kubernetes.Clientset, error) { + config, err := GetKubeConfig() + if err != nil { + return nil, err + } + + return kubernetes.NewForConfig(config) +} + +//This function returns dynamic client and discovery client +func GetDynamicAndDiscoveryClient() (discovery.DiscoveryInterface, dynamic.Interface, error) { + // returns a config object which uses the service account kubernetes gives to pods + config, err := GetKubeConfig() + if err != nil { + return nil, nil, err + } + + // NewDiscoveryClientForConfig creates a new DiscoveryClient for the given config + discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) + if err != nil { + return nil, nil, err + } + + // NewForConfig creates a new dynamic client or returns an error. + dynamicClient, err := dynamic.NewForConfig(config) + if err != nil { + return nil, nil, err + } + + return discoveryClient, dynamicClient, nil +} + +func GenerateArgoClient(namespace string) (v1alpha12.WorkflowInterface, error) { + + //List all chaosEngines present in the particular namespace + conf, err := GetKubeConfig() + if err != nil { + return nil, err + } + + // create the events client + wfClient := wfclientset.NewForConfigOrDie(conf).ArgoprojV1alpha1().Workflows(namespace) + return wfClient, nil +} diff --git a/chaoscenter/subscriber/pkg/k8s/log.go b/chaoscenter/subscriber/pkg/k8s/log.go new file mode 100644 index 00000000000..02bd86b9d2e --- /dev/null +++ b/chaoscenter/subscriber/pkg/k8s/log.go @@ -0,0 +1,130 @@ +package k8s + +import ( + "bytes" + "context" + "io" + "strconv" + "strings" + + "subscriber/pkg/graphql" + "subscriber/pkg/types" + + "github.com/sirupsen/logrus" + + v1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes" +) + +func GetLogs(podName, namespace, container string) (string, error) { + ctx := context.TODO() + conf, err := GetKubeConfig() + if err != nil { + return "", err + } + + podLogOpts := v1.PodLogOptions{} + if container != "" { + podLogOpts.Container = container + } + + // creates the clientset + clientset, err := kubernetes.NewForConfig(conf) + if err != nil { + return "", err + } + + req := clientset.CoreV1().Pods(namespace).GetLogs(podName, &podLogOpts) + podLogs, err := req.Stream(ctx) + if err != nil { + return "", err + } + + defer podLogs.Close() + + buf := new(bytes.Buffer) + _, err = io.Copy(buf, podLogs) + if err != nil { + return "", err + } + + str := buf.String() + + return str, nil +} + +// create pod log for normal pods and chaos-engine pods +func CreatePodLog(podLog types.PodLogRequest) (types.PodLog, error) { + logDetails := types.PodLog{} + mainLog, err := GetLogs(podLog.PodName, podLog.PodNamespace, "main") + // try getting argo pod logs + if err != nil { + logrus.Errorf("Failed to get argo pod %v logs, err: %v", podLog.PodName, err) + logDetails.MainPod = "Failed to get argo pod logs" + } else { + logDetails.MainPod = strconv.Quote(strings.Replace(mainLog, `"`, `'`, -1)) + logDetails.MainPod = logDetails.MainPod[1 : len(logDetails.MainPod)-1] + } + // try getting experiment pod logs if requested + if strings.ToLower(podLog.PodType) == "chaosengine" && podLog.ChaosNamespace != nil { + chaosLog := make(map[string]string) + if podLog.ExpPod != nil { + expLog, err := GetLogs(*podLog.ExpPod, *podLog.ChaosNamespace, "") + if err == nil { + chaosLog[*podLog.ExpPod] = strconv.Quote(strings.Replace(expLog, `"`, `'`, -1)) + chaosLog[*podLog.ExpPod] = chaosLog[*podLog.ExpPod][1 : len(chaosLog[*podLog.ExpPod])-1] + } else { + logrus.Errorf("Failed to get experiment pod %v logs, err: %v", *podLog.ExpPod, err) + } + } + if podLog.RunnerPod != nil { + runnerLog, err := GetLogs(*podLog.RunnerPod, *podLog.ChaosNamespace, "") + if err == nil { + chaosLog[*podLog.RunnerPod] = strconv.Quote(strings.Replace(runnerLog, `"`, `'`, -1)) + chaosLog[*podLog.RunnerPod] = chaosLog[*podLog.RunnerPod][1 : len(chaosLog[*podLog.RunnerPod])-1] + } else { + logrus.Errorf("Failed to get runner pod %v logs, err: %v", *podLog.RunnerPod, err) + } + } + if podLog.ExpPod == nil && podLog.RunnerPod == nil { + logDetails.ChaosPod = nil + } else { + logDetails.ChaosPod = chaosLog + } + } + return logDetails, nil +} + +// SendPodLogs generates graphql mutation to send events updates to graphql server +func SendPodLogs(infraData map[string]string, podLog types.PodLogRequest) { + // generate graphql payload + payload, err := GenerateLogPayload(infraData["INFRA_ID"], infraData["ACCESS_KEY"], infraData["VERSION"], podLog) + if err != nil { + logrus.WithError(err).Print("Error while retrieving the workflow logs") + } + body, err := graphql.SendRequest(infraData["SERVER_ADDR"], payload) + if err != nil { + logrus.Print(err.Error()) + } + logrus.Print("Response from the server: ", body) +} + +func GenerateLogPayload(cid, accessKey, version string, podLog types.PodLogRequest) ([]byte, error) { + infraID := `{infraID: \"` + cid + `\", version: \"` + version + `\", accessKey: \"` + accessKey + `\"}` + processed := " Could not get logs " + + // get the logs + logDetails, err := CreatePodLog(podLog) + if err == nil { + // process log data + processed, err = graphql.MarshalGQLData(logDetails) + if err != nil { + processed = " Could not get logs " + } + } + + mutation := `{ infraID: ` + infraID + `, requestID:\"` + podLog.RequestID + `\", experimentRunID: \"` + podLog.ExperimentRunID + `\", podName: \"` + podLog.PodName + `\", podType: \"` + podLog.PodType + `\", log:\"` + processed[1:len(processed)-1] + `\"}` + var payload = []byte(`{"query":"mutation { podLog(request:` + mutation + ` )}"}`) + + return payload, nil +} diff --git a/chaoscenter/subscriber/pkg/k8s/objects.go b/chaoscenter/subscriber/pkg/k8s/objects.go new file mode 100644 index 00000000000..4d6cf692433 --- /dev/null +++ b/chaoscenter/subscriber/pkg/k8s/objects.go @@ -0,0 +1,156 @@ +package k8s + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "os" + "strings" + + "subscriber/pkg/graphql" + + "github.com/sirupsen/logrus" + + "subscriber/pkg/types" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" +) + +var ( + InfraNamespace = os.Getenv("INFRA_NAMESPACE") + InfraScope = os.Getenv("INFRA_SCOPE") +) + +// GetKubernetesObjects is used to get the Kubernetes Object details according to the request type +func GetKubernetesObjects(request types.KubeObjRequest) ([]*types.KubeObject, error) { + conf, err := GetKubeConfig() + if err != nil { + return nil, err + } + clientset, err := kubernetes.NewForConfig(conf) + if err != nil { + return nil, err + } + + resourceType := schema.GroupVersionResource{ + Group: request.KubeGVRRequest.Group, + Version: request.KubeGVRRequest.Version, + Resource: request.KubeGVRRequest.Resource, + } + _, dynamicClient, err := GetDynamicAndDiscoveryClient() + if err != nil { + return nil, err + } + var ObjData []*types.KubeObject + + if strings.ToLower(InfraScope) == "namespace" { + dataList, err := GetObjectDataByNamespace(InfraNamespace, dynamicClient, resourceType) + if err != nil { + return nil, err + } + KubeObj := &types.KubeObject{ + Namespace: InfraNamespace, + Data: dataList, + } + ObjData = append(ObjData, KubeObj) + } else { + namespace, err := clientset.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{}) + if err != nil { + return nil, err + } + + if len(namespace.Items) > 0 { + for _, namespace := range namespace.Items { + podList, err := GetObjectDataByNamespace(namespace.GetName(), dynamicClient, resourceType) + if err != nil { + return nil, err + } + KubeObj := &types.KubeObject{ + Namespace: namespace.GetName(), + Data: podList, + } + ObjData = append(ObjData, KubeObj) + } + } else { + return nil, errors.New("No namespace available") + } + + } + kubeData, _ := json.Marshal(ObjData) + var kubeObjects []*types.KubeObject + err = json.Unmarshal(kubeData, &kubeObjects) + if err != nil { + return nil, err + } + return kubeObjects, nil +} + +// GetObjectDataByNamespace uses dynamic client to fetch Kubernetes Objects data. +func GetObjectDataByNamespace(namespace string, dynamicClient dynamic.Interface, resourceType schema.GroupVersionResource) ([]types.ObjectData, error) { + list, err := dynamicClient.Resource(resourceType).Namespace(namespace).List(context.TODO(), metav1.ListOptions{}) + var kubeObjects []types.ObjectData + if err != nil { + return kubeObjects, nil + } + for _, list := range list.Items { + listInfo := types.ObjectData{ + Name: list.GetName(), + UID: list.GetUID(), + Namespace: list.GetNamespace(), + APIVersion: list.GetAPIVersion(), + CreationTimestamp: list.GetCreationTimestamp(), + TerminationGracePeriods: list.GetDeletionGracePeriodSeconds(), + Labels: updateLabels(list.GetLabels()), + } + kubeObjects = append(kubeObjects, listInfo) + } + return kubeObjects, nil +} + +func updateLabels(labels map[string]string) []string { + var updatedLabels []string + + for k, v := range labels { + updatedLabels = append(updatedLabels, fmt.Sprintf("%s=%s", k, v)) + } + return updatedLabels +} + +func GenerateKubeObject(cid string, accessKey, version string, kubeobjectrequest types.KubeObjRequest) ([]byte, error) { + infraID := `{infraID: \"` + cid + `\", version: \"` + version + `\", accessKey: \"` + accessKey + `\"}` + kubeObj, err := GetKubernetesObjects(kubeobjectrequest) + if err != nil { + return nil, err + } + processed, err := graphql.MarshalGQLData(kubeObj) + if err != nil { + return nil, err + } + mutation := `{ infraID: ` + infraID + `, requestID:\"` + kubeobjectrequest.RequestID + `\", kubeObj:\"` + processed[1:len(processed)-1] + `\"}` + + var payload = []byte(`{"query":"mutation { kubeObj(request:` + mutation + ` )}"}`) + return payload, nil +} + +// SendKubeObjects generates graphql mutation to send kubernetes objects data to graphql server +func SendKubeObjects(infraData map[string]string, kubeobjectrequest types.KubeObjRequest) error { + // generate graphql payload + payload, err := GenerateKubeObject(infraData["INFRA_ID"], infraData["ACCESS_KEY"], infraData["VERSION"], kubeobjectrequest) + if err != nil { + logrus.WithError(err).Print("Error while getting KubeObject Data") + return err + } + + body, err := graphql.SendRequest(infraData["SERVER_ADDR"], payload) + if err != nil { + logrus.Print(err.Error()) + return err + } + + logrus.Println("Response", body) + return nil +} diff --git a/chaoscenter/subscriber/pkg/k8s/operations.go b/chaoscenter/subscriber/pkg/k8s/operations.go new file mode 100644 index 00000000000..dec65cb72f4 --- /dev/null +++ b/chaoscenter/subscriber/pkg/k8s/operations.go @@ -0,0 +1,360 @@ +package k8s + +import ( + "context" + "encoding/base64" + "errors" + "fmt" + "k8s.io/apimachinery/pkg/labels" + "strings" + "sync" + "time" + + "subscriber/pkg/graphql" + "subscriber/pkg/types" + + yaml_converter "github.com/ghodss/yaml" + "github.com/sirupsen/logrus" + yaml2 "gopkg.in/yaml.v2" + corev1 "k8s.io/api/core/v1" + k8s_errors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/serializer/yaml" + memory "k8s.io/client-go/discovery/cached" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/restmapper" +) + +const ( + InfraConfigName = "subscriber-config" + InfraSecretName = "subscriber-secret" + LiveCheckMaxTries = 6 +) + +type InfraComponents struct { + Deployments []string `yaml:"DEPLOYMENTS"` + LiveStatus bool + AccessLiveStatus sync.Mutex +} + +var ( + Ctx = context.Background() + decUnstructured = yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) + dr dynamic.ResourceInterface +) + +func CheckComponentStatus(componentEnv string) error { + if componentEnv == "" { + return errors.New("components not found in infra config") + } + + clientset, err := GetGenericK8sClient() + if err != nil { + return err + } + + var components InfraComponents + + err = yaml2.Unmarshal([]byte(strings.TrimSpace(componentEnv)), &components) + if err != nil { + return err + } + + components.LiveStatus = true + components.AccessLiveStatus = sync.Mutex{} + + wait := sync.WaitGroup{} + + // add all agent components to waitgroup + wait.Add(1) + go checkDeploymentStatus(&components, clientset, &wait) + + wait.Wait() + if !components.LiveStatus { + return errors.New("all components failed to startup") + } + return nil +} + +func checkDeploymentStatus(components *InfraComponents, clientset *kubernetes.Clientset, wait *sync.WaitGroup) { + ctx := context.TODO() + downCount := 0 + retries := 0 + defer wait.Done() + for retries < LiveCheckMaxTries { + for _, dep := range components.Deployments { + podList, err := clientset.CoreV1().Pods(InfraNamespace).List(ctx, metav1.ListOptions{LabelSelector: dep}) + if err != nil { + logrus.Errorf("failed to get deployment pods %v , err : %v", dep, err.Error()) + downCount += 1 + continue + } + if len(podList.Items) == 0 { + logrus.Errorf("failed to get deployments pods %v , err : pods not found", dep) + downCount += 1 + continue + } + outer: + for _, pod := range podList.Items { + if pod.Status.Phase != corev1.PodRunning { + logrus.Errorf("failed to get running pods for dep %v", dep) + downCount += 1 + break + } + for _, containerStatus := range pod.Status.ContainerStatuses { + if !containerStatus.Ready { + logrus.Errorf("failed to get ready containers for pod %v, dep %v", pod.Name, dep) + downCount += 1 + break outer + } + } + } + } + if downCount == 0 { + logrus.Info("All infra deployments are up") + return + } else { + retries += 1 + downCount = 0 + time.Sleep(30 * time.Second) + } + } + components.AccessLiveStatus.Lock() + defer components.AccessLiveStatus.Unlock() + components.LiveStatus = false +} + +func IsAgentConfirmed() (bool, string, error) { + clientset, err := GetGenericK8sClient() + if err != nil { + return false, "", err + } + + getCM, err := clientset.CoreV1().ConfigMaps(InfraNamespace).Get(context.TODO(), InfraConfigName, metav1.GetOptions{}) + if k8s_errors.IsNotFound(err) { + return false, "", errors.New(InfraConfigName + " configmap not found") + } else if getCM.Data["IS_INFRA_CONFIRMED"] == "true" { + getSecret, err := clientset.CoreV1().Secrets(InfraNamespace).Get(context.TODO(), InfraSecretName, metav1.GetOptions{}) + if err != nil { + return false, "", errors.New(InfraSecretName + " secret not found") + } + + if k8s_errors.IsNotFound(err) { + return false, "", err + } + + return true, string(getSecret.Data["ACCESS_KEY"]), nil + } else if err != nil { + return false, "", err + } + + return false, "", nil +} + +// AgentRegister function creates litmus-portal config map in the litmus namespace +func AgentRegister(infraData map[string]string) (bool, error) { + clientset, err := GetGenericK8sClient() + if err != nil { + return false, err + } + + newConfigMapData := map[string]string{ + "IS_INFRA_CONFIRMED": infraData["IS_INFRA_CONFIRMED"], + "SERVER_ADDR": infraData["SERVER_ADDR"], + "INFRA_SCOPE": infraData["INFRA_SCOPE"], + "COMPONENTS": infraData["COMPONENTS"], + "START_TIME": infraData["START_TIME"], + "VERSION": infraData["VERSION"], + "SKIP_SSL_VERIFY": infraData["SKIP_SSL_VERIFY"], + "CUSTOM_TLS_CERT": infraData["CUSTOM_TLS_CERT"], + } + + _, err = clientset.CoreV1().ConfigMaps(InfraNamespace).Update(context.TODO(), &corev1.ConfigMap{ + Data: newConfigMapData, + ObjectMeta: metav1.ObjectMeta{ + Name: InfraConfigName, + }, + }, metav1.UpdateOptions{}) + if err != nil { + return false, err + } + + logrus.Info(InfraConfigName + " has been updated") + + newSecretData := map[string]string{ + "ACCESS_KEY": infraData["ACCESS_KEY"], + "INFRA_ID": infraData["INFRA_ID"], + } + + _, err = clientset.CoreV1().Secrets(InfraNamespace).Update(context.TODO(), &corev1.Secret{ + StringData: newSecretData, + ObjectMeta: metav1.ObjectMeta{ + Name: InfraSecretName, + }, + }, metav1.UpdateOptions{}) + if err != nil { + return false, err + } + + logrus.Info(InfraSecretName + " has been updated") + + return true, nil +} + +func applyRequest(requestType string, obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { + ctx := context.TODO() + logrus.Info("Applying request for kind: ", obj.GetKind(), ", resource name: ", obj.GetName(), ", and namespace: ", obj.GetNamespace()) + if requestType == "create" { + response, err := dr.Create(ctx, obj, metav1.CreateOptions{}) + if k8s_errors.IsAlreadyExists(err) { + // This doesnt ever happen even if it does already exist + logrus.Info("Already exists") + return nil, nil + } + if err != nil { + return nil, err + } + + logrus.Info("Successfully created for kind: ", response.GetKind(), ", resource name: ", response.GetName(), ", and namespace: ", response.GetNamespace()) + return response, nil + } else if requestType == "update" { + getObj, err := dr.Get(ctx, obj.GetName(), metav1.GetOptions{}) + if k8s_errors.IsNotFound(err) { + // This doesnt ever happen even if it is already deleted or not found + + logrus.Info("%v not found ", obj.GetName()) + return nil, nil + } + if err != nil { + return nil, err + } + + obj.SetResourceVersion(getObj.GetResourceVersion()) + + response, err := dr.Update(ctx, obj, metav1.UpdateOptions{}) + if err != nil { + return nil, err + } + + logrus.Info("successfully updated for kind: ", response.GetKind(), ", resource name: ", response.GetName(), ", and namespace: ", response.GetNamespace()) + return response, nil + } else if requestType == "delete" { + var err error + if obj.GetName() != "" { + err = dr.Delete(ctx, obj.GetName(), metav1.DeleteOptions{}) + if k8s_errors.IsNotFound(err) { + fmt.Println(obj) + // This doesnt ever happen even if it is already deleted or not found + logrus.Info("%v not found ", obj.GetName()) + return nil, nil + } + logrus.Info("successfully deleted for kind: ", obj.GetKind(), ", resource name: ", obj.GetName(), ", and namespace: ", obj.GetNamespace()) + } else if obj.GetLabels() != nil { + fmt.Println(obj) + objLabels := obj.GetLabels() + delete(objLabels, "updated_by") + err = dr.DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: labels.FormatLabels(objLabels)}) + if k8s_errors.IsNotFound(err) { + + // This doesnt ever happen even if it is already deleted or not found + logrus.Info("%v not found ", obj.GetName()) + return nil, nil + } + logrus.Info("successfully deleted for kind: ", obj.GetKind(), ", resource labels: ", objLabels, ", and namespace: ", obj.GetNamespace()) + } + + if err != nil { + return nil, err + } + + logrus.Info("successfully deleted for kind: ", obj.GetKind(), ", resource name: ", obj.GetName(), ", and namespace: ", obj.GetNamespace()) + return &unstructured.Unstructured{}, nil + } else if requestType == "get" { + response, err := dr.Get(ctx, obj.GetName(), metav1.GetOptions{}) + if k8s_errors.IsNotFound(err) { + // This doesnt ever happen even if it is already deleted or not found + logrus.Info("%v not found", obj.GetName()) + return nil, nil + } + if err != nil { + return nil, err + } + + logrus.Info("successfully retrieved for kind: ", response.GetKind(), ", resource name: ", response.GetName(), ", and namespace: ", response.GetNamespace()) + return response, nil + } + + return nil, fmt.Errorf("err: %v\n", "Invalid Request") +} + +func addCustomLabels(obj *unstructured.Unstructured, customLabels map[string]string) { + newLabels := obj.GetLabels() + + if len(newLabels) == 0 { + newLabels = make(map[string]string) + } + + for label, value := range customLabels { + newLabels[label] = value + } + + obj.SetLabels(newLabels) +} + +// AgentOperations This function handles agent operations +func AgentOperations(infraAction types.Action) (*unstructured.Unstructured, error) { + // Converting JSON to YAML and store it in yamlStr variable + yamlStr, err := yaml_converter.JSONToYAML([]byte(infraAction.K8SManifest)) + if err != nil { + return nil, err + } + + // Getting dynamic and discovery client + discoveryClient, dynamicClient, err := GetDynamicAndDiscoveryClient() + if err != nil { + return nil, err + } + + // Create a mapper using dynamic client + mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(discoveryClient)) + + // Decode YAML manifest into unstructured.Unstructured + obj := &unstructured.Unstructured{} + _, gvk, err := decUnstructured.Decode(yamlStr, nil, obj) + if err != nil { + return nil, err + } + + if infraAction.RequestType != "delete" { + addCustomLabels(obj, map[string]string{"updated_by": base64.RawURLEncoding.EncodeToString([]byte(infraAction.Username))}) + } + + // Find GVR + mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version) + if err != nil { + return nil, err + } + + // Obtain REST interface for the GVR + if mapping.Scope.Name() == meta.RESTScopeNameNamespace { + // namespaced resources should specify the namespace + dr = dynamicClient.Resource(mapping.Resource).Namespace(infraAction.Namespace) + } else { + // for agent-wide resources + dr = dynamicClient.Resource(mapping.Resource) + } + + return applyRequest(infraAction.RequestType, obj) +} + +func AgentConfirm(infraData map[string]string) ([]byte, error) { + payload := `{"query":"mutation{ confirmInfraRegistration(request: {infraID: \"` + infraData["INFRA_ID"] + `\", version: \"` + infraData["VERSION"] + `\", accessKey: \"` + infraData["ACCESS_KEY"] + `\"}){isInfraConfirmed newAccessKey infraID}}"}` + resp, err := graphql.SendRequest(infraData["SERVER_ADDR"], []byte(payload)) + if err != nil { + return nil, err + } + return []byte(resp), nil +} diff --git a/chaoscenter/subscriber/pkg/requests/webhook.go b/chaoscenter/subscriber/pkg/requests/webhook.go new file mode 100644 index 00000000000..3d9cf4af6f2 --- /dev/null +++ b/chaoscenter/subscriber/pkg/requests/webhook.go @@ -0,0 +1,143 @@ +package requests + +import ( + "encoding/json" + "errors" + "net/url" + "strings" + "time" + + "subscriber/pkg/k8s" + "subscriber/pkg/types" + "subscriber/pkg/utils" + + "github.com/gorilla/websocket" + "github.com/sirupsen/logrus" +) + +func AgentConnect(infraData map[string]string) { + query := `{"query":"subscription {\n infraConnect(request: {infraID: \"` + infraData["INFRA_ID"] + `\", version: \"` + infraData["VERSION"] + `\", accessKey: \"` + infraData["ACCESS_KEY"] + `\"}) {\n action{\n k8sManifest,\n externalData,\n requestID\n requestType\n username\n namespace\n }\n }\n}\n"}` + serverURL, err := url.Parse(infraData["SERVER_ADDR"]) + if err != nil { + logrus.WithError(err).Fatal("Failed to parse URL") + } + scheme := "ws" + if serverURL.Scheme == "https" { + scheme = "wss" + } + + u := url.URL{Scheme: scheme, Host: serverURL.Host, Path: serverURL.Path} + logrus.Info("Connecting to " + u.String()) + + c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) + if err != nil { + logrus.WithError(err).Fatal("Failed to established websocket connection") + } + defer c.Close() + + go func() { + time.Sleep(1 * time.Second) + payload := types.OperationMessage{ + Type: "connection_init", + } + data, err := json.Marshal(payload) + if err != nil { + logrus.WithError(err).Fatal("Failed to marshal message") + } + + err = c.WriteMessage(websocket.TextMessage, data) + if err != nil { + logrus.WithError(err).Fatal("Failed to write message after init") + return + } + + payload = types.OperationMessage{ + Payload: []byte(query), + Type: "start", + } + + data, err = json.Marshal(payload) + if err != nil { + logrus.WithError(err).Fatal("Failed to marshal message") + } + + err = c.WriteMessage(websocket.TextMessage, data) + if err != nil { + logrus.WithError(err).Fatal("Failed to write message after start") + return + } + }() + + for { + _, message, err := c.ReadMessage() + if err != nil { + logrus.WithError(err).Fatal("Failed to read message") + } + + var r types.RawData + err = json.Unmarshal(message, &r) + if err != nil { + logrus.WithError(err).Error("Error while parsing the server response") + continue + } + + if r.Type == "connection_ack" { + logrus.Info("Server connection established, Listening....") + } + if r.Type != "data" { + continue + } + if r.Payload.Errors != nil { + logrus.Error("Error response from the server : ", string(message)) + continue + } + + err = RequestProcessor(infraData, r) + if err != nil { + logrus.WithError(err).Error("Error on processing request") + } + } +} + +func RequestProcessor(infraData map[string]string, r types.RawData) error { + if strings.Index("kubeobject kubeobjects", strings.ToLower(r.Payload.Data.InfraConnect.Action.RequestType)) >= 0 { + KubeObjRequest := types.KubeObjRequest{ + RequestID: r.Payload.Data.InfraConnect.Action.RequestID, + } + + err := json.Unmarshal([]byte(r.Payload.Data.InfraConnect.Action.ExternalData), &KubeObjRequest) + if err != nil { + return errors.New("failed to json unmarshal: " + err.Error()) + } + + err = k8s.SendKubeObjects(infraData, KubeObjRequest) + if err != nil { + return errors.New("error getting kubernetes object data: " + err.Error()) + } + } + if strings.ToLower(r.Payload.Data.InfraConnect.Action.RequestType) == "logs" { + podRequest := types.PodLogRequest{ + RequestID: r.Payload.Data.InfraConnect.Action.RequestID, + } + err := json.Unmarshal([]byte(r.Payload.Data.InfraConnect.Action.ExternalData), &podRequest) + if err != nil { + return errors.New("error reading infra-action request [external-data]: " + err.Error()) + } + + logrus.Print("Log Request: ", r.Payload.Data.InfraConnect.Action.ExternalData) + k8s.SendPodLogs(infraData, podRequest) + } else if strings.Index("create update delete get", strings.ToLower(r.Payload.Data.InfraConnect.Action.RequestType)) >= 0 { + _, err := k8s.AgentOperations(r.Payload.Data.InfraConnect.Action) + if err != nil { + return errors.New("error performing infra operationn: " + err.Error()) + } + } else if strings.Index("workflow_delete workflow_run_delete ", strings.ToLower(r.Payload.Data.InfraConnect.Action.RequestType)) >= 0 { + + err := utils.WorkflowRequest(infraData, r.Payload.Data.InfraConnect.Action.RequestType, r.Payload.Data.InfraConnect.Action.ExternalData, r.Payload.Data.InfraConnect.Action.Username) + if err != nil { + return errors.New("error performing events operation: " + err.Error()) + } + } + + return nil +} diff --git a/chaoscenter/subscriber/pkg/types/connect.go b/chaoscenter/subscriber/pkg/types/connect.go new file mode 100644 index 00000000000..98c17b7418f --- /dev/null +++ b/chaoscenter/subscriber/pkg/types/connect.go @@ -0,0 +1,55 @@ +package types + +import "encoding/json" + +type OperationMessage struct { + Payload json.RawMessage `json:"payload,omitempty"` + ID string `json:"id,omitempty"` + Type string `json:"type"` +} + +type RawData struct { + Payload Payload `json:"payload"` + Type string `json:"type"` +} + +type Payload struct { + Errors interface{} `json:"errors"` + Data Data `json:"data"` +} + +type Data struct { + InfraConfirm InfraConfirm `json:"confirmInfraRegistration"` + InfraConnect InfraConnect `json:"infraConnect"` +} + +type InfraConfirm struct { + IsInfraConfirmed bool `json:"isInfraConfirmed"` + NewAccessKey string `json:"newAccessKey"` + InfraID string `json:"infraID"` +} + +type InfraConnect struct { + ProjectID string `json:"projectID"` + Action Action `json:"action"` +} + +type Action struct { + RequestID string `json:"requestID"` + K8SManifest string `json:"k8sManifest"` + ExternalData string `json:"externalData"` + RequestType string `json:"requestType"` + Username string `json:"username"` + Namespace string `json:"namespace"` +} + +type WorkflowSyncExternalData struct { + WorkflowID string `json:"workflowID"` + WorkflowRunID string `json:"workflowRunID"` +} + +type WorkflowStopExternalData struct { + WorkflowName string `json:"workflow_name"` + WorkflowID string `json:"workflow_id"` + WorkflowRunIDs []string `json:"workflow_run_ids"` +} diff --git a/chaoscenter/subscriber/pkg/types/event.go b/chaoscenter/subscriber/pkg/types/event.go new file mode 100644 index 00000000000..f2e450a4a84 --- /dev/null +++ b/chaoscenter/subscriber/pkg/types/event.go @@ -0,0 +1,51 @@ +package types + +import "github.com/litmuschaos/chaos-operator/api/litmuschaos/v1alpha1" + +// events data +type WorkflowEvent struct { + WorkflowType string `json:"experimentType"` + RevisionID string `json:"revisionID"` + NotifyID *string `json:"notifyID"` + WorkflowID string `json:"experimentID"` + EventType string `json:"eventType"` + UID string `json:"uid"` + Namespace string `json:"namespace"` + Name string `json:"name"` + CreationTimestamp string `json:"creationTimestamp"` + Phase string `json:"phase"` + Message string `json:"message"` + StartedAt string `json:"startedAt"` + FinishedAt string `json:"finishedAt"` + Nodes map[string]Node `json:"nodes"` + UpdatedBy string `json:"updatedBy"` +} + +// each node/step data +type Node struct { + Name string `json:"name"` + Phase string `json:"phase"` + Message string `json:"message"` + StartedAt string `json:"startedAt"` + FinishedAt string `json:"finishedAt"` + Children []string `json:"children"` + Type string `json:"type"` + ChaosExp *ChaosData `json:"chaos_data,omitempty"` +} + +// chaos data +type ChaosData struct { + EngineUID string `json:"engineUID"` + EngineContext string `json:"engineContext"` + EngineName string `json:"engineName"` + Namespace string `json:"namespace"` + ExperimentName string `json:"experimentName"` + ExperimentStatus string `json:"experimentStatus"` + LastUpdatedAt string `json:"lastUpdatedAt"` + ExperimentVerdict string `json:"experimentVerdict"` + ExperimentPod string `json:"experimentPod"` + RunnerPod string `json:"runnerPod"` + ProbeSuccessPercentage string `json:"probeSuccessPercentage"` + FailStep string `json:"failStep"` + ChaosResult *v1alpha1.ChaosResult `json:"chaosResult"` +} diff --git a/chaoscenter/subscriber/pkg/types/kubeobject.go b/chaoscenter/subscriber/pkg/types/kubeobject.go new file mode 100644 index 00000000000..e862cc0b875 --- /dev/null +++ b/chaoscenter/subscriber/pkg/types/kubeobject.go @@ -0,0 +1,37 @@ +package types + +import ( + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +type KubeObject struct { + Namespace string `json:"namespace"` + Data []ObjectData `json:"data"` +} + +type KubeObjRequest struct { + RequestID string + InfraID string `json:"infraID"` + ObjectType string `json:"objectType"` + KubeGVRRequest KubeGVRRequest `json:"kubeObjRequest"` +} + +type KubeGVRRequest struct { + Group string `json:"group"` + Version string `json:"version"` + Resource string `json:"resource"` +} + +type ObjectData struct { + Name string `json:"name"` + UID types.UID `json:"uid"` + Namespace string `json:"namespace"` + APIVersion string `json:"apiVersion"` + CreationTimestamp metav1.Time `json:"creationTimestamp"` + Containers []v1.Container `json:"containers"` + TerminationGracePeriods *int64 `json:"terminationGracePeriods"` + Volumes []v1.Volume `json:"volumes"` + Labels []string `json:"labels"` +} diff --git a/chaoscenter/subscriber/pkg/types/log.go b/chaoscenter/subscriber/pkg/types/log.go new file mode 100644 index 00000000000..41553a116bf --- /dev/null +++ b/chaoscenter/subscriber/pkg/types/log.go @@ -0,0 +1,18 @@ +package types + +type PodLogRequest struct { + RequestID string `json:"requestID"` + InfraID string `json:"infraID"` + ExperimentRunID string `json:"experimentRunID"` + PodName string `json:"podName"` + PodNamespace string `json:"podNamespace"` + PodType string `json:"podType"` + ExpPod *string `json:"expPod"` + RunnerPod *string `json:"runnerPod"` + ChaosNamespace *string `json:"chaosNamespace"` +} + +type PodLog struct { + MainPod string `json:"mainLogs"` + ChaosPod map[string]string `json:"chaosLogs,omitempty"` +} diff --git a/chaoscenter/subscriber/pkg/utils/workflow.go b/chaoscenter/subscriber/pkg/utils/workflow.go new file mode 100644 index 00000000000..7b314fac884 --- /dev/null +++ b/chaoscenter/subscriber/pkg/utils/workflow.go @@ -0,0 +1,57 @@ +package utils + +import ( + "context" + wfclientset "github.com/argoproj/argo-workflows/v3/pkg/client/clientset/versioned" + "github.com/sirupsen/logrus" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "subscriber/pkg/events" + "subscriber/pkg/k8s" +) + +func WorkflowRequest(agentData map[string]string, requestType string, externalData string, uuid string) error { + if requestType == "workflow_delete" { + wfOb, err := events.ListWorkflowObject(externalData) + if err != nil { + return err + } + for _, wfs := range wfOb.Items { + uid := string(wfs.UID) + err = events.StopChaosEngineState(agentData["AGENT_NAMESPACE"], &uid) + if err != nil { + logrus.Info("failed to stop chaosEngine for : ", wfs.Name, " namespace: ", wfs.Namespace) + } + err = DeleteWorkflow(wfs.Name, agentData) + if err != nil { + logrus.Info("failed to delete workflow: ", wfs.Name, " namespace: ", wfs.Namespace) + } + logrus.Info("events delete name: ", wfs.Name, " namespace: ", wfs.Namespace) + } + } else if requestType == "workflow_run_delete" { + wfOb, err := events.GetWorkflowObj(externalData) + if err != nil { + return err + } + + err = DeleteWorkflow(wfOb.Name, agentData) + if err != nil { + return err + } + + logrus.Info("events delete name: ", wfOb.Name, "namespace: ", wfOb.Namespace) + } + + return nil +} + +func DeleteWorkflow(wfname string, agentData map[string]string) error { + ctx := context.TODO() + conf, err := k8s.GetKubeConfig() + if err != nil { + return err + } + + // create the events client + wfClient := wfclientset.NewForConfigOrDie(conf).ArgoprojV1alpha1().Workflows(agentData["INFRA_NAMESPACE"]) + return wfClient.Delete(ctx, wfname, metav1.DeleteOptions{}) +} diff --git a/chaoscenter/subscriber/subscriber.go b/chaoscenter/subscriber/subscriber.go new file mode 100644 index 00000000000..5d7c9277cbe --- /dev/null +++ b/chaoscenter/subscriber/subscriber.go @@ -0,0 +1,150 @@ +package main + +import ( + "crypto/tls" + "crypto/x509" + "encoding/base64" + "encoding/json" + "flag" + "net/http" + "os" + "os/signal" + "runtime" + "strings" + + "github.com/gorilla/websocket" + + "subscriber/pkg/events" + "subscriber/pkg/requests" + + "github.com/kelseyhightower/envconfig" + + "subscriber/pkg/k8s" + "subscriber/pkg/types" + + "github.com/sirupsen/logrus" +) + +var ( + infraData = map[string]string{ + "ACCESS_KEY": os.Getenv("ACCESS_KEY"), + "INFRA_ID": os.Getenv("INFRA_ID"), + "SERVER_ADDR": os.Getenv("SERVER_ADDR"), + "IS_INFRA_CONFIRMED": os.Getenv("IS_INFRA_CONFIRMED"), + "INFRA_SCOPE": os.Getenv("INFRA_SCOPE"), + "COMPONENTS": os.Getenv("COMPONENTS"), + "INFRA_NAMESPACE": os.Getenv("INFRA_NAMESPACE"), + "VERSION": os.Getenv("VERSION"), + "START_TIME": os.Getenv("START_TIME"), + "SKIP_SSL_VERIFY": os.Getenv("SKIP_SSL_VERIFY"), + "CUSTOM_TLS_CERT": os.Getenv("CUSTOM_TLS_CERT"), + } + + err error +) + +type Config struct { + AccessKey string `required:"true" split_words:"true"` + InfraId string `required:"true" split_words:"true"` + ServerAddr string `required:"true" split_words:"true"` + IsInfraConfirmed string `required:"true" split_words:"true"` + InfraScope string `required:"true" split_words:"true"` + Components string `required:"true"` + InfraNamespace string `required:"true" split_words:"true"` + Version string `required:"true"` + StartTime string `required:"true" split_words:"true"` + SkipSSLVerify bool `default:"false" split_words:"true"` +} + +func init() { + logrus.Info("Go Version: ", runtime.Version()) + logrus.Info("Go OS/Arch: ", runtime.GOOS, "/", runtime.GOARCH) + + var c Config + + err := envconfig.Process("", &c) + if err != nil { + logrus.Fatal(err) + } + + // disable ssl verification if configured + if strings.ToLower(infraData["SKIP_SSL_VERIFY"]) == "true" { + http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + websocket.DefaultDialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + } else if infraData["CUSTOM_TLS_CERT"] != "" { + cert, err := base64.StdEncoding.DecodeString(infraData["CUSTOM_TLS_CERT"]) + if err != nil { + logrus.Fatalf("Failed to parse custom tls cert %v", err) + } + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(cert) + http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{RootCAs: caCertPool} + websocket.DefaultDialer.TLSClientConfig = &tls.Config{RootCAs: caCertPool} + } + + k8s.KubeConfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file") + flag.Parse() + + // check agent component status + err = k8s.CheckComponentStatus(infraData["COMPONENTS"]) + if err != nil { + logrus.Fatal(err) + } + + logrus.Info("Starting the subscriber") + + isConfirmed, newKey, err := k8s.IsAgentConfirmed() + if err != nil { + logrus.WithError(err).Fatal("Failed to check agent confirmed status") + } + + if isConfirmed { + infraData["ACCESS_KEY"] = newKey + } else if !isConfirmed { + infraConfirmByte, err := k8s.AgentConfirm(infraData) + if err != nil { + logrus.WithError(err).WithField("data", string(infraConfirmByte)).Fatal("Failed to confirm agent") + } + + var infraConfirmInterface types.Payload + err = json.Unmarshal(infraConfirmByte, &infraConfirmInterface) + if err != nil { + logrus.WithError(err).WithField("data", string(infraConfirmByte)).Fatal("Failed to parse agent confirm data") + } + + if infraConfirmInterface.Data.InfraConfirm.IsInfraConfirmed { + infraData["ACCESS_KEY"] = infraConfirmInterface.Data.InfraConfirm.NewAccessKey + infraData["IS_INFRA_CONFIRMED"] = "true" + + _, err = k8s.AgentRegister(infraData) + if err != nil { + logrus.Fatal(err) + } + logrus.Info("AgentID: ", infraData["INFRA_ID"]+" has been confirmed") + } else { + logrus.Info("AgentID: ", infraData["INFRA_ID"]+" hasn't been confirmed") + } + } +} + +func main() { + stopCh := make(chan struct{}) + sigCh := make(chan os.Signal) + stream := make(chan types.WorkflowEvent, 10) + + //start events event watcher + events.WorkflowEventWatcher(stopCh, stream, infraData) + + //start events event watcher + events.ChaosEventWatcher(stopCh, stream, infraData) + //streams the event data to graphql server + go events.WorkflowUpdates(infraData, stream) + + // listen for agent actions + go requests.AgentConnect(infraData) + + signal.Notify(sigCh, os.Kill, os.Interrupt) + <-sigCh + close(stopCh) + close(stream) +} diff --git a/litmus-portal/cluster-agents/subscriber/pkg/events/chaosengine.go b/litmus-portal/cluster-agents/subscriber/pkg/events/chaosengine.go index 8acbcba6e99..6a83f08cf4a 100644 --- a/litmus-portal/cluster-agents/subscriber/pkg/events/chaosengine.go +++ b/litmus-portal/cluster-agents/subscriber/pkg/events/chaosengine.go @@ -26,7 +26,7 @@ func ChaosEventWatcher(stopCh chan struct{}, stream chan types.WorkflowEvent, cl cfg, err := k8s.GetKubeConfig() if err != nil { - logrus.WithError(err).Fatal("could not get config") + logrus.WithError(err).Fatal("could not get kube config") } // ClientSet to create Informer @@ -86,7 +86,7 @@ func chaosEventHandler(obj interface{}, eventType string, stream chan types.Work cfg, err := k8s.GetKubeConfig() if err != nil { - logrus.WithError(err).Fatal("could not get config") + logrus.WithError(err).Fatal("could not get kube config") } chaosClient, err := litmusV1alpha1.NewForConfig(cfg)