diff --git a/go.mod b/go.mod index 521aeb7a..4f62b04e 100644 --- a/go.mod +++ b/go.mod @@ -6,19 +6,12 @@ require ( bou.ke/monkey v1.0.2 github.com/AlecAivazis/survey/v2 v2.3.4 github.com/Azure/go-autorest/autorest/mocks v0.4.1 - github.com/MakeNowJust/heredoc v1.0.0 // indirect - github.com/agext/levenshtein v1.2.3 // indirect github.com/aliyun/aliyun-oss-go-sdk v2.1.8+incompatible github.com/aws/aws-sdk-go v1.42.35 github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 github.com/davecgh/go-spew v1.1.1 github.com/didi/gendry v1.7.0 - github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 // indirect github.com/evanphx/json-patch v4.11.0+incompatible - github.com/fatih/color v1.13.0 // indirect - github.com/go-errors/errors v1.4.0 // indirect - github.com/go-openapi/jsonreference v0.19.6 // indirect - github.com/go-openapi/swag v0.19.15 // indirect github.com/go-sql-driver/mysql v1.6.0 github.com/goccy/go-yaml v1.8.9 github.com/gonvenience/bunt v1.1.1 @@ -27,45 +20,29 @@ require ( github.com/gonvenience/text v1.0.5 github.com/gonvenience/wrap v1.1.0 github.com/gonvenience/ytbx v1.3.0 + github.com/google/go-cmp v0.5.8 github.com/google/uuid v1.2.0 github.com/gookit/goutil v0.5.1 github.com/hashicorp/go-version v1.4.0 - github.com/hashicorp/hcl/v2 v2.11.1 // indirect + github.com/hashicorp/hcl/v2 v2.11.1 github.com/hashicorp/terraform v0.15.3 - github.com/imdario/mergo v0.3.12 // indirect + github.com/imdario/mergo v0.3.13 github.com/jinzhu/copier v0.3.2 - github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect github.com/lucasb-eyer/go-colorful v1.0.3 - github.com/mattn/go-colorable v0.1.11 // indirect - github.com/mitchellh/go-ps v1.0.0 // indirect - github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/hashstructure v1.0.0 - github.com/nxadm/tail v1.4.8 // indirect github.com/pkg/errors v0.9.1 github.com/pterm/pterm v0.12.42-0.20220427210824-6bb8c6e6cc77 github.com/sergi/go-diff v1.2.0 github.com/spf13/afero v1.2.2 github.com/spf13/cobra v1.1.1 - github.com/stretchr/objx v0.3.0 // indirect github.com/stretchr/testify v1.7.1 github.com/texttheater/golang-levenshtein v1.0.1 - github.com/xanzy/ssh-agent v0.3.1 // indirect github.com/zclconf/go-cty v1.10.0 - go.uber.org/atomic v1.7.0 // indirect go.uber.org/zap v1.16.0 - golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa // indirect - golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c // indirect - golang.org/x/sys v0.0.0-20220429121018-84afa8d3f7b3 // indirect - golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 // indirect - golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20210420162539-3c870d7478d2 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/src-d/go-git.v4 v4.13.1 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.0 - honnef.co/go/tools v0.3.0 // indirect k8s.io/api v0.21.2 k8s.io/apimachinery v0.21.2 k8s.io/client-go v10.0.0+incompatible diff --git a/go.sum b/go.sum index 99816af0..9d69d9ed 100644 --- a/go.sum +++ b/go.sum @@ -67,7 +67,6 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -76,9 +75,8 @@ github.com/ChrisTrenkamp/goxpath v0.0.0-20190607011252-c5096ec8773d/go.mod h1:nu github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DATA-DOG/go-sqlmock v1.4.0 h1:yxQ63CFIA8Sxkh0vqIofuNrsXl/LZ42TpeTLV4Nb5HM= github.com/DATA-DOG/go-sqlmock v1.4.0/go.mod h1:3TucWNLPFOLcHhha1CPp7Kis1UG2h/AqGROPyOeZzsM= +github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= -github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= -github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs= github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8= github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII= @@ -90,8 +88,6 @@ github.com/MarvinJWendt/testza v0.3.5/go.mod h1:ExbTpWmA1z2E9HSskvrNcwApoX4F9bID github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= -github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= @@ -104,9 +100,8 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdko github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= -github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= -github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= @@ -217,10 +212,8 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCfx+QkYnoQ= github.com/dylanmei/winrmtest v0.0.0-20190225150635-99b7fe2fddf1/go.mod h1:lcy9/2gH1jn/VCLouHA6tOEwLoNVd4GW6zhuKLmHC2Y= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 h1:pEtiCjIXx3RvGjlUJuCNxNOw0MNblyR9Wi+vJGBFh+8= -github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= -github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= @@ -238,9 +231,8 @@ github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQL github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= 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= @@ -252,9 +244,8 @@ github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-errors/errors v1.4.0 h1:2OA7MFw38+e9na72T1xgkomPb6GzZzzxvJ5U630FoRM= -github.com/go-errors/errors v1.4.0/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= 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= @@ -282,9 +273,8 @@ github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34 github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= 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/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= @@ -306,9 +296,8 @@ github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= 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/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= @@ -503,6 +492,7 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hashicorp/terraform v0.15.3 h1:2QWbTj2xJ/8W1gCyIrd0WAqVF4weKPMYjx8nKjbkQjA= github.com/hashicorp/terraform v0.15.3/go.mod h1:w4eBEsluZfYumXUTLe834eqHh969AabcLqbj2WAYlM8= github.com/hashicorp/terraform-config-inspect v0.0.0-20210209133302-4fd17a0faac2/go.mod h1:Z0Nnk4+3Cy89smEbrq+sl1bxc9198gIP4I7wcQF6Kqs= +github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 h1:HKLsbzeOsfXmKNpr3GiT18XAblV0BjCbzL8KQAMZGa0= github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= @@ -513,8 +503,8 @@ github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq 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.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -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/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= @@ -530,8 +520,6 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -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/joyent/triton-go v0.0.0-20180313100802-d8f9c0314926/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= @@ -546,9 +534,8 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= -github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 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= @@ -559,9 +546,8 @@ github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuOb github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/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/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/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/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= @@ -569,6 +555,7 @@ 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/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= @@ -592,9 +579,8 @@ github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 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.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= @@ -604,10 +590,8 @@ github.com/mattn/go-ciede2000 v0.0.0-20170301095244-782e8c62fec3/go.mod h1:x1uk6 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= -github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -636,15 +620,13 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-linereader v0.0.0-20190213213312-1b945b3263eb/go.mod h1:OaY7UOoTkkrX3wRwjpYRKafIkkyeD0UtweSHAWWiqQM= +github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936 h1:kw1v0NlnN+GZcU8Ma8CLF2Zzgjfx95gs3/GN3vYAPpo= github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= -github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= -github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= -github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4= github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y= @@ -674,11 +656,11 @@ github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISe github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= @@ -750,7 +732,6 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -768,7 +749,6 @@ github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNX github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -796,9 +776,8 @@ github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jW github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= -github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 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= @@ -827,9 +806,8 @@ github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74/go.mod h1 github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= -github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo= -github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w= github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= @@ -842,7 +820,6 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de 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.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= @@ -866,9 +843,8 @@ go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee33 go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -898,11 +874,8 @@ golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa h1:idItI2DDfCokpg0N51B2VtiLdJ4vAuXC9fnCb2gACo4= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/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= @@ -912,10 +885,7 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u0 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 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM= -golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= 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= @@ -927,9 +897,8 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl 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 h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/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= @@ -938,9 +907,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB 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.3.1-0.20200828183125-ce943fd02449 h1:xUIPaMhvROX9dhPvRCenIJtU78+lbEenGbgqB5hfHCQ= golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -987,9 +955,6 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -997,9 +962,8 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr 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-20200902213428-5d25da1a8d43 h1:ld7aEMNHoBnnDAX15v1T6z31v8HwR2A9FYOuAhWqkwc= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/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= @@ -1008,9 +972,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ 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 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1065,30 +1028,22 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220429121018-84afa8d3f7b3 h1:kBsBifDikLCf5sUMbcD8p73OinDtAQWQp8+n7FiyzlA= -golang.org/x/sys v0.0.0-20220429121018-84afa8d3f7b3/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-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +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/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8= -golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1096,15 +1051,13 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 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 h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= -golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1161,9 +1114,8 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a h1:ofrrl6c6NG5/IOSx/R1cyiQxxjqlur0h/TvbUhkH0II= -golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= 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= @@ -1191,9 +1143,8 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 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 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= 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-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 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= @@ -1225,9 +1176,8 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc 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-20200904004341-0bd0a958aa1d h1:92D1fum1bJLKSdr11OJ+54YeCMCGYIygTA7R/YZxH5M= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210420162539-3c870d7478d2 h1:g2sJMUGCpeHZqTx8p3wsAWRS64nFq20i4dvJWcKGqvY= -google.golang.org/genproto v0.0.0-20210420162539-3c870d7478d2/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1245,7 +1195,6 @@ google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.31.1/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.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -1267,9 +1216,8 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks 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 h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 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/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -1317,9 +1265,8 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh 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 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.3.0 h1:2LdYUZ7CIxnYgskbUZfY7FPggmqnh6shBqfWa8Tn3XU= -honnef.co/go/tools v0.3.0/go.mod h1:vlRD9XErLMGT+mDuofSr0mMMquscM/1nQqtRSsh6m70= 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= diff --git a/pkg/engine/models/resource.go b/pkg/engine/models/resource.go index 3a653ddd..85cccd84 100644 --- a/pkg/engine/models/resource.go +++ b/pkg/engine/models/resource.go @@ -1,5 +1,7 @@ package models +import "encoding/json" + type Type string type Resources []Resource @@ -26,6 +28,17 @@ func (r *Resource) ResourceKey() string { return r.ID } +// DeepCopy return a copy of resource +func (r *Resource) DeepCopy() *Resource { + var out Resource + data, err := json.Marshal(r) + if err != nil { + panic(err) + } + _ = json.Unmarshal(data, &out) + return &out +} + func (rs Resources) Index() map[string]*Resource { m := make(map[string]*Resource) for i := range rs { diff --git a/pkg/engine/operation/graph/resource_node.go b/pkg/engine/operation/graph/resource_node.go index d8cead7e..d7b6621c 100644 --- a/pkg/engine/operation/graph/resource_node.go +++ b/pkg/engine/operation/graph/resource_node.go @@ -54,10 +54,8 @@ func (rn *ResourceNode) Execute(operation *opsmodels.Operation) status.Status { priorState := operation.PriorStateResourceIndex[key] // 3. get the latest resource from runtime - readRequest := &runtime.ReadRequest{Resource: planedState} - if readRequest.Resource == nil { - readRequest.Resource = priorState - } + readRequest := &runtime.ReadRequest{PlanResource: planedState, PriorResource: priorState} + response := operation.Runtime.Read(context.Background(), readRequest) liveState := response.Resource s := response.Status @@ -138,7 +136,7 @@ func (rn *ResourceNode) applyResource(operation *opsmodels.Operation, priorState } case types.UnChange: log.Infof("planed resource not update live state") - res = planedState + res = priorState } if status.IsErr(s) { return s diff --git a/pkg/engine/operation/graph/resource_node_test.go b/pkg/engine/operation/graph/resource_node_test.go index eb0cc7fe..3e40e90a 100644 --- a/pkg/engine/operation/graph/resource_node_test.go +++ b/pkg/engine/operation/graph/resource_node_test.go @@ -181,7 +181,7 @@ func TestResourceNode_Execute(t *testing.T) { }) monkey.PatchInstanceMethod(reflect.TypeOf(tt.args.operation.Runtime), "Read", func(k *runtime.KubernetesRuntime, ctx context.Context, request *runtime.ReadRequest) *runtime.ReadResponse { - return &runtime.ReadResponse{Resource: request.Resource} + return &runtime.ReadResponse{Resource: request.PriorResource} }) monkey.PatchInstanceMethod(reflect.TypeOf(tt.args.operation.StateStorage), "Apply", func(f *local.FileSystemState, state *states.State) error { diff --git a/pkg/engine/operation/preview_test.go b/pkg/engine/operation/preview_test.go index f6c3f8a5..96291b6e 100644 --- a/pkg/engine/operation/preview_test.go +++ b/pkg/engine/operation/preview_test.go @@ -51,14 +51,18 @@ func (f *fakePreviewRuntime) Apply(ctx context.Context, request *runtime.ApplyRe } func (f *fakePreviewRuntime) Read(ctx context.Context, request *runtime.ReadRequest) *runtime.ReadResponse { - if request.Resource.ResourceKey() == "fake-id" { + requestResource := request.PlanResource + if requestResource == nil { + requestResource = request.PriorResource + } + if requestResource.ResourceKey() == "fake-id" { return &runtime.ReadResponse{ Resource: nil, Status: nil, } } return &runtime.ReadResponse{ - Resource: request.Resource, + Resource: requestResource, Status: nil, } } diff --git a/pkg/engine/runtime/init/init.go b/pkg/engine/runtime/init/init.go new file mode 100644 index 00000000..c3df8338 --- /dev/null +++ b/pkg/engine/runtime/init/init.go @@ -0,0 +1,18 @@ +package init + +import ( + "kusionstack.io/kusion/pkg/engine/models" + "kusionstack.io/kusion/pkg/engine/runtime" + "kusionstack.io/kusion/pkg/engine/runtime/terraform" +) + +func InitRuntime() map[models.Type]InitFn { + runtimes := map[models.Type]InitFn{ + runtime.Kubernetes: runtime.NewKubernetesRuntime, + runtime.Terraform: terraform.NewTerraformRuntime, + } + return runtimes +} + +// InitFn init Runtime +type InitFn func() (runtime.Runtime, error) diff --git a/pkg/engine/runtime/kubernetes_runtime.go b/pkg/engine/runtime/kubernetes_runtime.go index 7e26c39d..e4c91884 100644 --- a/pkg/engine/runtime/kubernetes_runtime.go +++ b/pkg/engine/runtime/kubernetes_runtime.go @@ -64,7 +64,7 @@ func (k *KubernetesRuntime) Apply(ctx context.Context, request *ApplyRequest) *A } // Get live state - response := k.Read(ctx, &ReadRequest{planState}) + response := k.Read(ctx, &ReadRequest{PlanResource: planState}) if status.IsErr(response.Status) { return &ApplyResponse{nil, response.Status} } @@ -127,7 +127,10 @@ func (k *KubernetesRuntime) Apply(ctx context.Context, request *ApplyRequest) *A // Read kubernetes Resource by client-go func (k *KubernetesRuntime) Read(ctx context.Context, request *ReadRequest) *ReadResponse { - requestResource := request.Resource + requestResource := request.PlanResource + if requestResource == nil { + requestResource = request.PriorResource + } // Validate if requestResource == nil { return &ReadResponse{nil, status.NewErrorStatus(errors.New("requestResource is nil"))} diff --git a/pkg/engine/runtime/runtime.go b/pkg/engine/runtime/runtime.go index 8e844026..a370976a 100644 --- a/pkg/engine/runtime/runtime.go +++ b/pkg/engine/runtime/runtime.go @@ -8,6 +8,11 @@ import ( "kusionstack.io/kusion/pkg/status" ) +const ( + Kubernetes models.Type = "Kubernetes" + Terraform models.Type = "Terraform" +) + // Runtime represents an actual infrastructure runtime managed by Kusion and every runtime implements this interface can be orchestrated // by Kusion like normal K8s resources. All methods in this interface are designed for manipulating one Resource at a time and will be // invoked in operations like Apply, Preview, Destroy, etc. @@ -52,8 +57,11 @@ type ApplyResponse struct { } type ReadRequest struct { - // Resource represents the resource we want to read from the actual infra - Resource *models.Resource + // PriorResource is the last applied resource saved in state storage + PriorResource *models.Resource + + // PlanResource is the resource we want to apply in this request + PlanResource *models.Resource } type ReadResponse struct { diff --git a/pkg/engine/runtime/terraform/terraform_runtime.go b/pkg/engine/runtime/terraform/terraform_runtime.go new file mode 100644 index 00000000..e01e39cf --- /dev/null +++ b/pkg/engine/runtime/terraform/terraform_runtime.go @@ -0,0 +1,156 @@ +package terraform + +import ( + "context" + "fmt" + + "github.com/imdario/mergo" + "github.com/spf13/afero" + "kusionstack.io/kusion/pkg/engine/models" + "kusionstack.io/kusion/pkg/engine/runtime" + "kusionstack.io/kusion/pkg/engine/runtime/terraform/tfops" + "kusionstack.io/kusion/pkg/status" +) + +var _ runtime.Runtime = &TerraformRuntime{} + +type TerraformRuntime struct { + tfops.WorkspaceStore +} + +func NewTerraformRuntime() (runtime.Runtime, error) { + fs := afero.Afero{Fs: afero.NewOsFs()} + ws, err := tfops.GetWorkspaceStore(fs) + if err != nil { + return nil, err + } + TFRuntime := &TerraformRuntime{ws} + return TFRuntime, nil +} + +// Apply terraform apply resource +func (t *TerraformRuntime) Apply(ctx context.Context, request *runtime.ApplyRequest) *runtime.ApplyResponse { + planState := request.PlanResource + w, ok := t.Store[planState.ResourceKey()] + if !ok { + err := t.Create(ctx, planState) + if err != nil { + return &runtime.ApplyResponse{Resource: nil, Status: status.NewErrorStatus(err)} + } + w = t.Store[planState.ResourceKey()] + } + + // get terraform provider version + providerAddr, err := w.GetProvider() + if err != nil { + return &runtime.ApplyResponse{Resource: nil, Status: status.NewErrorStatus(err)} + } + + // terraform dry run merge state + // TODO: terraform dry run apply,not only merge state + if request.DryRun { + prior := request.PriorResource.DeepCopy() + if err := mergo.Merge(prior, planState, mergo.WithSliceDeepCopy, mergo.WithOverride); err != nil { + return &runtime.ApplyResponse{Resource: nil, Status: status.NewErrorStatus(err)} + } + + return &runtime.ApplyResponse{Resource: &models.Resource{ + ID: planState.ID, + Type: planState.Type, + Attributes: prior.Attributes, + DependsOn: planState.DependsOn, + Extensions: planState.Extensions, + }, Status: nil} + } + w.SetResource(planState) + + if err := w.WriteHCL(); err != nil { + return &runtime.ApplyResponse{Resource: nil, Status: status.NewErrorStatus(err)} + } + + tfstate, err := w.Apply(ctx) + if err != nil { + return &runtime.ApplyResponse{Resource: nil, Status: status.NewErrorStatus(err)} + } + + r := tfops.ConvertTFState(tfstate, providerAddr) + + return &runtime.ApplyResponse{ + Resource: &models.Resource{ + ID: r.ID, + Type: r.Type, + Attributes: r.Attributes, + DependsOn: planState.DependsOn, + Extensions: planState.Extensions, + }, + Status: nil, + } +} + +// Read terraform show state +func (t *TerraformRuntime) Read(ctx context.Context, request *runtime.ReadRequest) *runtime.ReadResponse { + priorState := request.PriorResource + planState := request.PlanResource + if priorState == nil { + return &runtime.ReadResponse{Resource: nil, Status: nil} + } + var tfstate *tfops.TFState + w, ok := t.Store[planState.ResourceKey()] + if !ok { + err := t.Create(ctx, planState) + if err != nil { + return &runtime.ReadResponse{Resource: nil, Status: status.NewErrorStatus(err)} + } + w = t.Store[priorState.ResourceKey()] + if err := w.WriteTFState(priorState); err != nil { + return &runtime.ReadResponse{Resource: nil, Status: status.NewErrorStatus(err)} + } + } + + tfstate, err := w.RefreshOnly(ctx) + if err != nil { + return &runtime.ReadResponse{Resource: nil, Status: status.NewErrorStatus(err)} + } + if tfstate == nil || tfstate.Values == nil { + return &runtime.ReadResponse{Resource: nil, Status: nil} + } + + // get terraform provider addr + providerAddr, err := w.GetProvider() + if err != nil { + return &runtime.ReadResponse{Resource: nil, Status: status.NewErrorStatus(err)} + } + + r := tfops.ConvertTFState(tfstate, providerAddr) + return &runtime.ReadResponse{ + Resource: &models.Resource{ + ID: r.ID, + Type: r.Type, + Attributes: r.Attributes, + DependsOn: planState.DependsOn, + Extensions: planState.Extensions, + }, + Status: nil, + } +} + +// Delete terraform resource and remove workspace +func (t *TerraformRuntime) Delete(ctx context.Context, request *runtime.DeleteRequest) *runtime.DeleteResponse { + w, ok := t.Store[request.Resource.ResourceKey()] + if !ok { + return &runtime.DeleteResponse{Status: status.NewErrorStatus(fmt.Errorf("%s terraform workspace not exist, cannot delete", request.Resource.ResourceKey()))} + } + if err := w.Destroy(ctx); err != nil { + return &runtime.DeleteResponse{Status: status.NewErrorStatus(err)} + } + + if err := t.Remove(ctx, request.Resource); err != nil { + return &runtime.DeleteResponse{Status: status.NewErrorStatus(err)} + } + return &runtime.DeleteResponse{Status: nil} +} + +// Watch terraform resource +func (t *TerraformRuntime) Watch(ctx context.Context, request *runtime.WatchRequest) *runtime.WatchResponse { + return nil +} diff --git a/pkg/engine/runtime/terraform/terraform_runtime_test.go b/pkg/engine/runtime/terraform/terraform_runtime_test.go new file mode 100644 index 00000000..9b8207ef --- /dev/null +++ b/pkg/engine/runtime/terraform/terraform_runtime_test.go @@ -0,0 +1,50 @@ +package terraform + +import ( + "context" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "kusionstack.io/kusion/pkg/engine/models" + "kusionstack.io/kusion/pkg/engine/runtime" + "kusionstack.io/kusion/pkg/engine/runtime/terraform/tfops" +) + +var testResource = models.Resource{ + ID: "example", + Type: "Terraform", + Attributes: map[string]interface{}{ + "content": "kusion", + "filename": "test.txt", + }, + Extensions: map[string]interface{}{ + "provider": "registry.terraform.io/hashicorp/local/2.2.3", + "resourceType": "local_file", + }, +} + +func TestTerraformRuntime(t *testing.T) { + wd, _ := tfops.GetWorkSpaceDir() + defer os.RemoveAll(filepath.Join(wd, testResource.ID)) + tfRuntime, _ := NewTerraformRuntime() + t.Run("ApplyDryRun", func(t *testing.T) { + response := tfRuntime.Apply(context.TODO(), &runtime.ApplyRequest{PlanResource: &testResource, DryRun: true}) + assert.Equalf(t, nil, response.Status, "Execute(%v)", "Apply") + }) + t.Run("Apply", func(t *testing.T) { + response := tfRuntime.Apply(context.TODO(), &runtime.ApplyRequest{PlanResource: &testResource, DryRun: false}) + assert.Equalf(t, nil, response.Status, "Execute(%v)", "Apply") + }) + + t.Run("Read", func(t *testing.T) { + response := tfRuntime.Read(context.TODO(), &runtime.ReadRequest{PlanResource: &testResource}) + assert.Equalf(t, nil, response.Status, "Execute(%v)", "Read") + }) + + t.Run("Delete", func(t *testing.T) { + response := tfRuntime.Delete(context.TODO(), &runtime.DeleteRequest{Resource: &testResource}) + assert.Equalf(t, nil, response.Status, "Execute(%v)", "Delete") + }) +} diff --git a/pkg/engine/runtime/terraform/tfops/state.go b/pkg/engine/runtime/terraform/tfops/state.go new file mode 100644 index 00000000..a5e6116f --- /dev/null +++ b/pkg/engine/runtime/terraform/tfops/state.go @@ -0,0 +1,108 @@ +package tfops + +import ( + "encoding/json" + + "github.com/hashicorp/terraform/addrs" + "kusionstack.io/kusion/pkg/engine/models" +) + +// Terraform State schema from https://github.com/hashicorp/terraform/blob/main/internal/command/jsonstate/state.go +type TFState struct { + FormatVersion string `json:"format_version,omitempty"` + TerraformVersion string `json:"terraform_version,omitempty"` + Values *stateValues `json:"values,omitempty"` +} + +// stateValues is the common representation of resolved values for both the prior +// state (which is always complete) and the planned new state. +type stateValues struct { + Outputs map[string]output `json:"outputs,omitempty"` + RootModule module `json:"root_module,omitempty"` +} + +type output struct { + Sensitive bool `json:"sensitive"` + Value json.RawMessage `json:"value,omitempty"` + Type json.RawMessage `json:"type,omitempty"` +} + +// module is the representation of a module in state. This can be the root module +// or a child module +type module struct { + // Resources are sorted in a user-friendly order that is undefined at this + // time, but consistent. + Resources []resource `json:"resources,omitempty"` + + // Address is the absolute module address, omitted for the root module + Address string `json:"address,omitempty"` + + // Each module object can optionally have its own nested "child_modules", + // recursively describing the full module tree. + ChildModules []module `json:"child_modules,omitempty"` +} + +// Resource is the representation of a resource in the state. +type resource struct { + // Address is the absolute resource address + Address string `json:"address,omitempty"` + + // Mode can be "managed" or "data" + Mode string `json:"mode,omitempty"` + + Type string `json:"type,omitempty"` + Name string `json:"name,omitempty"` + + // Index is omitted for a resource not using `count` or `for_each`. + Index addrs.InstanceKey `json:"index,omitempty"` + + // ProviderName allows the property "type" to be interpreted unambiguously + // in the unusual situation where a provider offers a resource type whose + // name does not start with its own name, such as the "googlebeta" provider + // offering "google_compute_instance". + ProviderName string `json:"provider_name"` + + // SchemaVersion indicates which version of the resource type schema the + // "values" property conforms to. + SchemaVersion uint64 `json:"schema_version"` + + // AttributeValues is the JSON representation of the attribute values of the + // resource, whose structure depends on the resource type schema. Any + // unknown values are omitted or set to null, making them indistinguishable + // from absent values. + AttributeValues attributeValues `json:"values,omitempty"` + + // DependsOn contains a list of the resource's dependencies. The entries are + // addresses relative to the containing module. + DependsOn []string `json:"depends_on,omitempty"` + + // Tainted is true if the resource is tainted in terraform state. + Tainted bool `json:"tainted,omitempty"` + + // Deposed is set if the resource is deposed in terraform state. + DeposedKey string `json:"deposed_key,omitempty"` +} + +// attributeValues is the JSON representation of the attribute values of the +// resource, whose structure depends on the resource type schema. +type attributeValues map[string]interface{} + +// ConvertTFState convert Terraform State to kusion State +func ConvertTFState(tfState *TFState, providerAddr string) models.Resource { + if tfState == nil || tfState.Values == nil { + return models.Resource{} + } + // terrafrom runtime execute single node + tResource := tfState.Values.RootModule.Resources[0] + extension := make(map[string]interface{}) + extension["resourceType"] = tResource.Type + extension["provider"] = providerAddr + r := models.Resource{ + ID: tResource.Name, + Type: "Terraform", + Attributes: tResource.AttributeValues, + Extensions: extension, + } + + return r +} diff --git a/pkg/engine/runtime/terraform/tfops/state_test.go b/pkg/engine/runtime/terraform/tfops/state_test.go new file mode 100644 index 00000000..01bef4f8 --- /dev/null +++ b/pkg/engine/runtime/terraform/tfops/state_test.go @@ -0,0 +1,67 @@ +package tfops + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "kusionstack.io/kusion/pkg/engine/models" +) + +var providerAddr = "registry.terraform.io/hashicorp/local/2.2.3" + +func TestConvertTFState(t *testing.T) { + tests := map[string]struct { + args TFState + want models.Resource + }{ + "success": { + args: TFState{ + FormatVersion: "0.2", + TerraformVersion: "1.0.6", + Values: &stateValues{ + RootModule: module{ + Resources: []resource{ + { + Address: "local_file.test", + Mode: "managed", + Type: "local_file", + Name: "test", + ProviderName: "registry.terraform.io/hashicorp/local", + SchemaVersion: 0, + AttributeValues: attributeValues{ + "content": "kusion", + "directory_permission": "0777", + "file_permission": "0777", + "filename": "text.txt", + }, + }, + }, + }, + }, + }, + want: models.Resource{ + ID: "test", + Type: "Terraform", + Attributes: map[string]interface{}{ + "content": "kusion", + "directory_permission": "0777", + "file_permission": "0777", + "filename": "text.txt", + }, + Extensions: map[string]interface{}{ + "provider": "registry.terraform.io/hashicorp/local/2.2.3", + "resourceType": "local_file", + }, + }, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + state := ConvertTFState(&tc.args, providerAddr) + if diff := cmp.Diff(tc.want, state); diff != "" { + t.Errorf("\nConvertTFStateFailed(...) -want message, +got message: \n%s", diff) + } + }) + } +} diff --git a/pkg/engine/runtime/terraform/tfops/store.go b/pkg/engine/runtime/terraform/tfops/store.go new file mode 100644 index 00000000..afcd1c5a --- /dev/null +++ b/pkg/engine/runtime/terraform/tfops/store.go @@ -0,0 +1,83 @@ +package tfops + +import ( + "context" + "fmt" + "os" + "path/filepath" + + "github.com/spf13/afero" + "kusionstack.io/kusion/pkg/engine/models" +) + +// WorkspaceStore store Terraform workspaces. +type WorkspaceStore struct { + Store map[string]*WorkSpace + Fs afero.Afero +} + +// Create make Terraform workspace for given resources. +// convert kusion resource to hcl json and write to file +// and init in the workspace folder +func (ws *WorkspaceStore) Create(ctx context.Context, resource *models.Resource) error { + w, ok := ws.Store[resource.ResourceKey()] + if !ok { + ws.Store[resource.ResourceKey()] = NewWorkSpace(resource, ws.Fs) + w = ws.Store[resource.ResourceKey()] + } + // write hcl json to file + if err := w.WriteHCL(); err != nil { + return fmt.Errorf("write hcl error: %v", err) + } + + // init workspace + if err := w.InitWorkSpace(ctx); err != nil { + return fmt.Errorf("init workspace error: %v", err) + } + return nil +} + +// Remove delete workspace directory and delete its record from the store. +func (ws *WorkspaceStore) Remove(ctx context.Context, resource *models.Resource) error { + w, ok := ws.Store[resource.ResourceKey()] + if !ok { + return nil + } + if err := w.fs.RemoveAll(w.dir); err != nil { + return fmt.Errorf("remove workspace error %v", err) + } + delete(ws.Store, resource.ResourceKey()) + return nil +} + +// GetWorkspaceStore find directory in the filesystem and store workspace +// return all terraform workspace record in the filesystem. +func GetWorkspaceStore(fs afero.Afero) (WorkspaceStore, error) { + ws := WorkspaceStore{ + Store: make(map[string]*WorkSpace), + Fs: fs, + } + wd, _ := GetWorkSpaceDir() + _, err := fs.Stat(wd) + if err != nil { + if os.IsNotExist(err) { + if err = fs.MkdirAll(wd, os.ModePerm); err != nil { + return ws, err + } + } else { + return ws, err + } + } + dirs, err := afero.ReadDir(fs, wd) + if err != nil { + return ws, err + } + for _, dir := range dirs { + workspace := WorkSpace{ + fs: fs, + dir: filepath.Join(wd, dir.Name()), + } + ws.Store[dir.Name()] = &workspace + } + return ws, nil +} diff --git a/pkg/engine/runtime/terraform/tfops/store_test.go b/pkg/engine/runtime/terraform/tfops/store_test.go new file mode 100644 index 00000000..b63116f5 --- /dev/null +++ b/pkg/engine/runtime/terraform/tfops/store_test.go @@ -0,0 +1,88 @@ +package tfops + +import ( + "context" + "fmt" + "testing" +) + +func TestCreate(t *testing.T) { + type args struct { + ws *WorkspaceStore + } + + tests := map[string]struct { + args + }{ + "Success": { + args: args{ + ws: &WorkspaceStore{ + Store: make(map[string]*WorkSpace), + Fs: fs, + }, + }, + }, + } + + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + if err := tt.args.ws.Create(context.TODO(), &resourceTest); err != nil { + t.Errorf("\n workspaceStore Create error: %v", err) + } + }) + } +} + +func TestRemove(t *testing.T) { + type args struct { + ws *WorkspaceStore + } + + tests := map[string]struct { + args + }{ + "SuccessRemove": { + args: args{ + ws: &WorkspaceStore{ + Store: make(map[string]*WorkSpace), + Fs: fs, + }, + }, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + if err := tt.args.ws.Remove(context.TODO(), &resourceTest); err != nil { + t.Errorf("\n workspaceStore Remove error: %v", err) + } + }) + } +} + +func TestGetWorkspaceStore(t *testing.T) { + type args struct { + ws *WorkspaceStore + } + + tests := map[string]struct { + args + }{ + "GetworkspaceStore": { + args: args{ + &WorkspaceStore{ + Store: make(map[string]*WorkSpace), + Fs: fs, + }, + }, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + ws, err := GetWorkspaceStore(tt.ws.Fs) + fmt.Println(ws) + if err != nil { + t.Errorf("\nGetWorkspaceStore error: %v", err) + } + }) + } +} diff --git a/pkg/engine/runtime/terraform/tfops/tferrors.go b/pkg/engine/runtime/terraform/tfops/tferrors.go new file mode 100644 index 00000000..a5c9137d --- /dev/null +++ b/pkg/engine/runtime/terraform/tfops/tferrors.go @@ -0,0 +1,107 @@ +package tfops + +import ( + "encoding/json" + "errors" + "fmt" + "strings" + "time" +) + +// TerraformInfo represent fields of a Terraform CLI JSON-formatted log line +type TerraformInfo struct { + Level string `json:"@level"` + Message string `json:"@message"` + Module string `json:"@module"` + Timestamp time.Time `json:"@timestamp"` + Diagnostic Diagnostic `json:"diagnostic"` + Type string `json:"type"` +} + +// Diagnostic schema from https://github.com/hashicorp/terraform/blob/main/internal/command/views/json/diagnostic.go. + +// Diagnostic represents relevant fields of a Terraform CLI JSON-formatted +// log line diagnostic info +type Diagnostic struct { + Severity string `json:"severity"` + Summary string `json:"summary"` + Detail string `json:"detail"` + Range Range `json:"range"` + Snippet Snippet `json:"snippet"` +} + +// Pos represents a position in the source code. +type Pos struct { + // Line is a one-based count for the line in the indicated file. + Line int `json:"line"` + + // Column is a one-based count of Unicode characters from the start of the line. + Column int `json:"column"` + + // Byte is a zero-based offset into the indicated file. + Byte int `json:"byte"` +} + +// DiagnosticRange represents the filename and position of the diagnostic +// subject. This defines the range of the source to be highlighted in the +// output. Note that the snippet may include additional surrounding source code +// if the diagnostic has a context range. +// +// The Start position is inclusive, and the End position is exclusive. Exact +// positions are intended for highlighting for human interpretation only and +// are subject to change. +type Range struct { + Filename string `json:"filename"` + Start Pos `json:"start"` + End Pos `json:"end"` +} + +// Snippet represents source code information about the diagnostic. +// It is possible for a diagnostic to have a source (and therefore a range) but +// no source code can be found. In this case, the range field will be present and +// the snippet field will not. +type Snippet struct { + Context string `json:"context"` + Code string `json:"code"` + StartLine int `json:"start_line"` + HighlightStartOffset int `json:"highlight_start_offset"` + HighlightEndOffset int `json:"highlight_end_offset"` + Values []interface{} `json:"values"` +} + +// Parse Terraform CLI output infos +func parseTerraformInfo(infos []byte) ([]*TerraformInfo, error) { + info := strings.Split(string(infos), "\n") + tfInfos := make([]*TerraformInfo, 0, len(info)) + for _, v := range info { + terraformInfo := &TerraformInfo{} + if v == "" { + continue + } + err := json.Unmarshal([]byte(v), terraformInfo) + if err != nil { + return nil, err + } + tfInfos = append(tfInfos, terraformInfo) + } + return tfInfos, nil +} + +// TFError parse Terraform CLI output infos +// return error with given infos +func TFError(infos []byte) error { + tfInfo, err := parseTerraformInfo(infos) + if err != nil { + return err + } + for _, v := range tfInfo { + if v == nil || v.Level != "error" { + continue + } + if v.Diagnostic.Severity == "error" { + msg := fmt.Sprintf("%s: %s", v.Diagnostic.Summary, v.Diagnostic.Detail) + return errors.New(msg) + } + } + return nil +} diff --git a/pkg/engine/runtime/terraform/tfops/tferrors_test.go b/pkg/engine/runtime/terraform/tfops/tferrors_test.go new file mode 100644 index 00000000..3634df15 --- /dev/null +++ b/pkg/engine/runtime/terraform/tfops/tferrors_test.go @@ -0,0 +1,68 @@ +package tfops + +import ( + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" +) + +var applyInfos = `{"@level":"info","@message":"Terraform 1.0.6","@module":"terraform.ui","@timestamp":"2022-07-28T17:47:22.522277+08:00","terraform":"1.0.6","type":"version","ui":"0.1.0"} + {"@level":"error","@message":"Error: Extraneous JSON object property","@module":"terraform.ui","@timestamp":"2022-07-28T17:47:22.898885+08:00","diagnostic":{"severity":"error","summary":"Extraneous JSON object property","detail":"No argument or block type is named \"content!\". Did you mean \"content\"?","range":{"filename":"main.tf.json","start":{"line":1,"column":62,"byte":61},"end":{"line":1,"column":72,"byte":71}},"snippet":{"context":"resource.local_file.test","code":"{\"provider\":{\"local\":null},\"resource\":{\"local_file\":{\"test\":{\"content!\":\"kusion12345\",\"filename\":\"test.txt\"}}},\"terraform\":{\"required_providers\":{\"local\":{\"source\":\"registry.terraform.io/hashicorp/local\",\"version\":\"2.2.3\"}}}}","start_line":1,"highlight_start_offset":61,"highlight_end_offset":71,"values":[]}},"type":"diagnostic"}` + +func TestParseTerraformInfo(t *testing.T) { + type args struct { + infos []byte + } + tests := map[string]struct { + args args + wantErrMessage string + }{ + "PlanError": { + args: args{ + infos: []byte(`{"@level":"info","@message":"Terraform 1.0.6","@module":"terraform.ui","@timestamp":"2022-07-27T15:57:25.538747+08:00","terraform":"1.0.6","type":"version","ui":"0.1.0"} + {"@level":"error","@message":"Error: Extraneous JSON object property","@module":"terraform.ui","@timestamp":"2022-07-27T15:57:25.824426+08:00","diagnostic":{"severity":"error","summary":"Extraneous JSON object property","detail":"No argument or block type is named \"content!\". Did you mean \"content\"?","range":{"filename":"main.tf.json","start":{"line":1,"column":62,"byte":61},"end":{"line":1,"column":72,"byte":71}},"snippet":{"context":"resource.local_file.test","code":"{\"provider\":{\"local\":null},\"resource\":{\"local_file\":{\"test\":{\"content!\":\"kusion12345\",\"filename\":\"test.txt\"}}},\"terraform\":{\"required_providers\":{\"local\":{\"source\":\"registry.terraform.io/hashicorp/local\",\"version\":\"2.2.3\"}}}}","start_line":1,"highlight_start_offset":61,"highlight_end_offset":71,"values":[]}},"type":"diagnostic"}`), + }, + wantErrMessage: "plan failed: Missing required argument: The argument \"location\" is required, but no definition was found.: File name: main.tf.json\nMissing required argument: The argument \"name\" is required, but no definition was found.: File name: main.tf.json", + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + TFInfos, err := parseTerraformInfo(tc.args.infos) + if err != nil { + t.Errorf("parseTerraformInfo error: %v", err) + } + fmt.Println(TFInfos) + }) + } +} + +func TestTFError(t *testing.T) { + type args struct { + infos []byte + } + tests := map[string]struct { + args args + wantMessage string + }{ + "apply": { + args: args{ + infos: []byte(applyInfos), + }, + wantMessage: "Extraneous JSON object property: No argument or block type is named \"content!\". Did you mean \"content\"?", + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + err := TFError(tc.args.infos) + got := "" + if err != nil { + got = err.Error() + } + if diff := cmp.Diff(tc.wantMessage, got); diff != "" { + t.Errorf("\nWrapApplyFailed(...): -want message, +got message:\n%s", diff) + } + }) + } +} diff --git a/pkg/engine/runtime/terraform/tfops/workspace.go b/pkg/engine/runtime/terraform/tfops/workspace.go new file mode 100644 index 00000000..bda22124 --- /dev/null +++ b/pkg/engine/runtime/terraform/tfops/workspace.go @@ -0,0 +1,251 @@ +package tfops + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/gohcl" + "github.com/hashicorp/hcl/v2/hclparse" + "github.com/spf13/afero" + "kusionstack.io/kusion/pkg/engine/models" + "kusionstack.io/kusion/pkg/util/kfile" +) + +const ( + HCLMAINFILE = "main.tf.json" + HCLLOCKFILE = ".terraform.hcl.lock" + TFSTATEFILE = "terraform.tfstate" +) + +type WorkSpace struct { + resource *models.Resource + fs afero.Afero + dir string +} + +// SetResource set workspace resource +func (w *WorkSpace) SetResource(resource *models.Resource) { + w.resource = resource +} + +func NewWorkSpace(resource *models.Resource, fs afero.Afero) *WorkSpace { + wd, _ := GetWorkSpaceDir() + return &WorkSpace{ + resource: resource, + fs: fs, + dir: filepath.Join(wd, resource.ResourceKey()), + } +} + +// GetWrokSpaceDir return kusion terrafrom runtime workspace dir +// Defalut workspace dir is ~/.kusion/.terraform +func GetWorkSpaceDir() (string, error) { + kusionDir, err := kfile.KusionDataFolder() + if err != nil { + return "", err + } + return filepath.Join(kusionDir, ".terraform"), nil +} + +// WriteHCL convert kusion Resource to HCL json +// and write hcl json to main.tf.json +func (w *WorkSpace) WriteHCL() error { + provider := strings.Split(w.resource.Extensions["provider"].(string), "/") + resourceType := w.resource.Extensions["resourceType"].(string) + + m := map[string]interface{}{ + "terraform": map[string]interface{}{ + "required_providers": map[string]interface{}{ + provider[len(provider)-2]: map[string]string{ + "source": strings.Join(provider[:len(provider)-1], "/"), + "version": provider[len(provider)-1], + }, + }, + }, + "provider": map[string]interface{}{ + provider[len(provider)-2]: w.resource.Extensions["providerMeta"], + }, + "resource": map[string]interface{}{ + resourceType: map[string]interface{}{ + w.resource.ResourceKey(): w.resource.Attributes, + }, + }, + } + hclMain, err := json.Marshal(m) + if err != nil { + return fmt.Errorf("marshal hcl main error: %v", err) + } + + _, err = w.fs.Stat(w.dir) + + if err != nil { + if os.IsNotExist(err) { + if err := w.fs.MkdirAll(w.dir, os.ModePerm); err != nil { + return fmt.Errorf("create workspace error: %v", err) + } + } else { + return err + } + } + err = w.fs.WriteFile(filepath.Join(w.dir, HCLMAINFILE), hclMain, 0o600) + if err != nil { + return fmt.Errorf("write hcl main.tf.json error: %v", err) + } + + return nil +} + +// WriteTFState writes TFState to the file, this function is for terraform apply refresh only +func (w *WorkSpace) WriteTFState(priorState *models.Resource) error { + provider := strings.Split(priorState.Extensions["provider"].(string), "/") + m := map[string]interface{}{ + "version": 4, + "resources": []map[string]interface{}{ + { + "mode": "managed", + "type": priorState.Extensions["resourceType"].(string), + "name": priorState.ID, + "provider": fmt.Sprintf("provider[\"%s\"]", strings.Join(provider[:len(provider)-1], "/")), + "instances": []map[string]interface{}{ + { + "attributes": priorState.Attributes, + }, + }, + }, + }, + } + hclState, err := json.Marshal(m) + if err != nil { + return fmt.Errorf("marshal hcl state error: %v", err) + } + + err = w.fs.WriteFile(filepath.Join(w.dir, TFSTATEFILE), hclState, os.ModePerm) + if err != nil { + return fmt.Errorf("write hcl error: %v", err) + } + return nil +} + +// InitWorkSpace init terraform runtime workspace +func (w *WorkSpace) InitWorkSpace(ctx context.Context) error { + cmd := exec.CommandContext(ctx, "terraform", "init") + cmd.Dir = w.dir + out, err := cmd.CombinedOutput() + if err != nil { + return TFError(out) + } + return nil +} + +// Apply invoke terraform apply cli +func (w *WorkSpace) Apply(ctx context.Context) (*TFState, error) { + cmd := exec.CommandContext(ctx, "terraform", "apply", "-auto-approve", "-json", "-lock=false") + cmd.Dir = w.dir + out, err := cmd.CombinedOutput() + if err != nil { + return nil, TFError(out) + } + s, err := w.RefreshOnly(ctx) + if err != nil { + return nil, fmt.Errorf("terraform read state error: %v", err) + } + return s, err +} + +// Read make terraform show call. Return terraform state model +// TODO: terraform show livestate. +func (w *WorkSpace) Read(ctx context.Context) (*TFState, error) { + _, err := w.fs.Stat(filepath.Join(w.dir, "terraform.tfstate")) + if os.IsNotExist(err) { + return nil, nil + } + if err != nil { + return nil, err + } + cmd := exec.CommandContext(ctx, "terraform", "show", "-json") + cmd.Dir = w.dir + out, err := cmd.CombinedOutput() + if err != nil { + return nil, TFError(out) + } + s := &TFState{} + if err = json.Unmarshal(out, s); err != nil { + return nil, fmt.Errorf("json umarshal state failed: %v", err) + } + return s, nil +} + +// Refresh Sync Terraform State +func (w *WorkSpace) RefreshOnly(ctx context.Context) (*TFState, error) { + cmd := exec.CommandContext(ctx, "terraform", "apply", "-auto-approve", "-json", "--refresh-only", "-lock=false") + cmd.Dir = w.dir + out, err := cmd.CombinedOutput() + if err != nil { + return nil, TFError(out) + } + s, err := w.Read(ctx) + if err != nil { + return nil, fmt.Errorf("terraform read state error: %v", err) + } + return s, err +} + +// Destroy make terraform destroy call. +func (w *WorkSpace) Destroy(ctx context.Context) error { + cmd := exec.CommandContext(ctx, "terraform", "destroy", "-auto-approve") + cmd.Dir = w.dir + out, err := cmd.CombinedOutput() + if err != nil { + return TFError(out) + } + return nil +} + +// GetProvider get provider addr from terraform lock file. +// return provider addr and erros +// eg. registry.terraform.io/hashicorp/local/2.2.3 +func (w *WorkSpace) GetProvider() (string, error) { + parser := hclparse.NewParser() + hclFile, diags := parser.ParseHCLFile(filepath.Join(w.dir, ".terraform.lock.hcl")) + if diags != nil { + return "", errors.New(diags.Error()) + } + body := hclFile.Body + content, diags := body.Content(&hcl.BodySchema{ + Blocks: []hcl.BlockHeaderSchema{ + { + Type: "provider", + LabelNames: []string{"source_addr"}, + }, + }, + }) + if diags != nil { + return "", errors.New(diags.Error()) + } + rawAddr := content.Blocks[0].Labels[0] + + block := content.Blocks[0] + providerVersion, _ := block.Body.Content(&hcl.BodySchema{ + Attributes: []hcl.AttributeSchema{ + {Name: "version", Required: true}, + {Name: "constraints"}, + {Name: "hashes"}, + }, + }) + expr := providerVersion.Attributes["version"].Expr + var rawVersion string + diags = gohcl.DecodeExpression(expr, nil, &rawVersion) + if diags != nil { + return "", errors.New(diags.Error()) + } + + providerAddr := fmt.Sprintf("%s/%s", rawAddr, rawVersion) + return providerAddr, nil +} diff --git a/pkg/engine/runtime/terraform/tfops/workspace_test.go b/pkg/engine/runtime/terraform/tfops/workspace_test.go new file mode 100644 index 00000000..a6127923 --- /dev/null +++ b/pkg/engine/runtime/terraform/tfops/workspace_test.go @@ -0,0 +1,334 @@ +package tfops + +import ( + "context" + "path/filepath" + "testing" + + "bou.ke/monkey" + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclparse" + "github.com/hashicorp/hcl/v2/hclsyntax" + "github.com/spf13/afero" + "github.com/zclconf/go-cty/cty" + "kusionstack.io/kusion/pkg/engine/models" +) + +var ( + resourceTest = models.Resource{ + ID: "kusion_example", + Type: "Terraform", + Attributes: map[string]interface{}{ + "content": "kusion", + "filename": "test.txt", + }, + Extensions: map[string]interface{}{ + "provider": "registry.terraform.io/hashicorp/local/2.2.3", + "resourceType": "local_file", + }, + } + tfstateTest = TFState{ + FormatVersion: "0.2", + TerraformVersion: "1.0.6", + Values: &stateValues{ + RootModule: module{ + Resources: []resource{ + { + Address: "local_file.kusion_example", + Mode: "managed", + Type: "local_file", + Name: "kusion_example", + ProviderName: "registry.terraform.io/hashicorp/local", + SchemaVersion: 0, + AttributeValues: attributeValues{ + "content": "kusion", + "directory_permission": "0777", + "file_permission": "0777", + "filename": "text.txt", + "sensitive_content": nil, + "source": nil, + "content_base64": nil, + }, + }, + }, + }, + }, + } + + fs = afero.Afero{Fs: afero.NewMemMapFs()} +) + +func TestWriteHCL(t *testing.T) { + type args struct { + w *WorkSpace + } + + type want struct { + maintf string + } + + cases := map[string]struct { + args + want + }{ + "writeSuccess": { + args: args{ + w: NewWorkSpace(&resourceTest, fs), + }, + want: want{ + maintf: "{\"provider\":{\"local\":null},\"resource\":{\"local_file\":{\"kusion_example\":{\"content\":\"kusion\",\"filename\":\"test.txt\"}}},\"terraform\":{\"required_providers\":{\"local\":{\"source\":\"registry.terraform.io/hashicorp/local\",\"version\":\"2.2.3\"}}}}", + }, + }, + } + + for name, tt := range cases { + t.Run(name, func(t *testing.T) { + if err := tt.args.w.WriteHCL(); err != nil { + t.Errorf("writeHCL error: %v", err) + } + + s, _ := fs.ReadFile(filepath.Join(tt.w.dir, "main.tf.json")) + if diff := cmp.Diff(string(s), tt.want.maintf); diff != "" { + t.Errorf("\n%s\nWriteHCL(...): -want maintf, +got maintf:\n%s", name, diff) + } + }) + } +} + +func TestWriteTFState(t *testing.T) { + type args struct { + w *WorkSpace + } + + type want struct { + tfstate string + } + + cases := map[string]struct { + args + want + }{ + "writeSuccess": { + args: args{ + w: NewWorkSpace(&resourceTest, fs), + }, + want: want{ + tfstate: "{\"resources\":[{\"instances\":[{\"attributes\":{\"content\":\"kusion\",\"filename\":\"test.txt\"}}],\"mode\":\"managed\",\"name\":\"kusion_example\",\"provider\":\"provider[\\\"registry.terraform.io/hashicorp/local\\\"]\",\"type\":\"local_file\"}],\"version\":4}", + }, + }, + } + + for name, tt := range cases { + t.Run(name, func(t *testing.T) { + if err := tt.args.w.WriteTFState(&resourceTest); err != nil { + t.Errorf("WriteTFState error: %v", err) + } + + s, _ := fs.ReadFile(filepath.Join(tt.w.dir, "terraform.tfstate")) + if diff := cmp.Diff(string(s), tt.want.tfstate); diff != "" { + t.Errorf("\n%s\nWriteTFState(...): -want tfstate, +got tfstate:\n%s", name, diff) + } + }) + } +} + +func TestInitWorkspace(t *testing.T) { + type args struct { + w *WorkSpace + } + + type want struct { + err error + } + + cases := map[string]struct { + args + want + }{ + "initws": { + args: args{ + w: NewWorkSpace(&resourceTest, fs), + }, + }, + } + for name, tt := range cases { + t.Run(name, func(t *testing.T) { + err := tt.args.w.InitWorkSpace(context.TODO()) + if diff := cmp.Diff(tt.want.err, err); diff != "" { + t.Errorf("\nInitWorkSpace(...) -want err, +got err: \n%s", diff) + } + }) + } +} + +func TestApply(t *testing.T) { + type args struct { + w *WorkSpace + } + + tests := map[string]struct { + args + }{ + "applySuccess": { + args: args{ + w: NewWorkSpace(&resourceTest, fs), + }, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + if err := tt.w.WriteHCL(); err != nil { + t.Errorf("\nWriteHCL error: %v", err) + } + if err := tt.w.InitWorkSpace(context.TODO()); err != nil { + t.Errorf("\nInitWorkSpace error: %v", err) + } + if _, err := tt.w.Apply(context.TODO()); err != nil { + t.Errorf("\n Apply error: %v", err) + } + }) + } +} + +func TestRead(t *testing.T) { + type args struct { + w *WorkSpace + } + tests := map[string]struct { + args args + }{ + "readSuccess": { + args: args{ + w: NewWorkSpace(&resourceTest, fs), + }, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + if _, err := tt.args.w.Read(context.TODO()); err != nil { + t.Errorf("\n Read error: %v", err) + } + }) + } +} + +func TestRefreshOnly(t *testing.T) { + type args struct { + w *WorkSpace + } + tests := map[string]struct { + args args + }{ + "readSuccess": { + args: args{ + w: NewWorkSpace(&resourceTest, fs), + }, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + if _, err := tt.args.w.RefreshOnly(context.TODO()); err != nil { + t.Errorf("\n RefreshOnly error: %v", err) + } + }) + } +} + +func TestGerProvider(t *testing.T) { + defer monkey.UnpatchAll() + type args struct { + w *WorkSpace + } + + type want struct { + addr string + err error + } + + tests := map[string]struct { + args + want + }{ + "Success": { + args: args{ + w: NewWorkSpace(&resourceTest, fs), + }, + want: want{ + addr: "registry.terraform.io/hashicorp/local/2.2.3", + }, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + mockProviderAddr() + addr, err := tt.args.w.GetProvider() + if diff := cmp.Diff(tt.want.addr, addr); diff != "" { + t.Errorf("\nGetProvider(...) -want addr, +got addr:\n%s", diff) + } + if diff := cmp.Diff(tt.want.err, err); diff != "" { + t.Errorf("\nGetProvider(...) -want error, +got error:\n%s", diff) + } + }) + } +} + +func mockProviderAddr() { + monkey.Patch((*hclparse.Parser).ParseHCLFile, func(parse *hclparse.Parser, fileName string) (*hcl.File, hcl.Diagnostics) { + return &hcl.File{ + Body: &hclsyntax.Body{ + Blocks: []*hclsyntax.Block{ + { + Type: "provider", + Labels: []string{"registry.terraform.io/hashicorp/local"}, + Body: &hclsyntax.Body{ + Attributes: hclsyntax.Attributes{ + "version": &hclsyntax.Attribute{ + Name: "version", + Expr: &hclsyntax.TemplateExpr{ + Parts: []hclsyntax.Expression{ + &hclsyntax.LiteralValueExpr{ + Val: cty.StringVal("2.2.3"), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, nil + }) +} + +func TestDestory(t *testing.T) { + type args struct { + w *WorkSpace + } + + type want struct { + err error + } + + cases := map[string]struct { + args + want + }{ + "success": { + args: args{ + w: NewWorkSpace(&resourceTest, fs), + }, + want: want{ + err: nil, + }, + }, + } + for name, tt := range cases { + t.Run(name, func(t *testing.T) { + if err := tt.w.Destroy(context.TODO()); err != nil { + t.Errorf("terraform destroy error: %v", err) + } + }) + } +} diff --git a/pkg/kusionctl/cmd/apply/options.go b/pkg/kusionctl/cmd/apply/options.go index d6761853..63e5dff2 100644 --- a/pkg/kusionctl/cmd/apply/options.go +++ b/pkg/kusionctl/cmd/apply/options.go @@ -8,6 +8,7 @@ import ( "strings" "sync" + runtimeInit "kusionstack.io/kusion/pkg/engine/runtime/init" "kusionstack.io/kusion/pkg/engine/states/local" "kusionstack.io/kusion/pkg/engine/operation" @@ -83,12 +84,14 @@ func (o *ApplyOptions) Run() error { // Compute changes for preview stateStorage := &local.FileSystemState{Path: filepath.Join(o.WorkDir, local.KusionState)} - kubernetesRuntime, err := runtime.NewKubernetesRuntime() + + runtimes := runtimeInit.InitRuntime() + runtime, err := runtimes[planResources.Resources[0].Type]() if err != nil { return err } - changes, err := Preview(o, kubernetesRuntime, stateStorage, planResources, project, stack, os.Stdout) + changes, err := Preview(o, runtime, stateStorage, planResources, project, stack, os.Stdout) if err != nil { return err } @@ -131,7 +134,7 @@ func (o *ApplyOptions) Run() error { if !o.OnlyPreview { fmt.Println("Start applying diffs ...") - if err := Apply(o, kubernetesRuntime, stateStorage, planResources, changes, os.Stdout); err != nil { + if err := Apply(o, runtime, stateStorage, planResources, changes, os.Stdout); err != nil { return err } diff --git a/pkg/kusionctl/cmd/apply/options_test.go b/pkg/kusionctl/cmd/apply/options_test.go index 2e96d569..d50cf1a1 100644 --- a/pkg/kusionctl/cmd/apply/options_test.go +++ b/pkg/kusionctl/cmd/apply/options_test.go @@ -146,14 +146,14 @@ func (f *fakerRuntime) Apply(ctx context.Context, request *runtime.ApplyRequest) } func (f *fakerRuntime) Read(ctx context.Context, request *runtime.ReadRequest) *runtime.ReadResponse { - if request.Resource.ResourceKey() == "fake-id" { + if request.PlanResource.ResourceKey() == "fake-id" { return &runtime.ReadResponse{ Resource: nil, Status: nil, } } return &runtime.ReadResponse{ - Resource: request.Resource, + Resource: request.PlanResource, Status: nil, } } @@ -209,8 +209,8 @@ var ( func newSA(name string) models.Resource { return models.Resource{ - ID: engine.BuildIDForKubernetes(apiVersion, kind, namespace, name), - + ID: engine.BuildIDForKubernetes(apiVersion, kind, namespace, name), + Type: "Kubernetes", Attributes: map[string]interface{}{ "apiVersion": apiVersion, "kind": kind, diff --git a/pkg/kusionctl/cmd/destroy/options.go b/pkg/kusionctl/cmd/destroy/options.go index cd0c5e6e..fe569d8b 100644 --- a/pkg/kusionctl/cmd/destroy/options.go +++ b/pkg/kusionctl/cmd/destroy/options.go @@ -6,6 +6,7 @@ import ( "strings" "sync" + runtimeInit "kusionstack.io/kusion/pkg/engine/runtime/init" "kusionstack.io/kusion/pkg/engine/states/local" "kusionstack.io/kusion/pkg/engine/operation" @@ -75,8 +76,14 @@ func (o *DestroyOptions) Run() error { return nil } + runtimes := runtimeInit.InitRuntime() + runtime, err := runtimes[planResources.Resources[0].Type]() + if err != nil { + return nil + } + // Compute changes for preview - changes, err := o.preview(planResources, project, stack) + changes, err := o.preview(planResources, project, stack, runtime) if err != nil { return err } @@ -114,26 +121,19 @@ func (o *DestroyOptions) Run() error { // Destroy fmt.Println("Start destroying resources......") - if err := o.destroy(planResources, changes); err != nil { + if err := o.destroy(planResources, changes, runtime); err != nil { return err } return nil } -func (o *DestroyOptions) preview(planResources *models.Spec, - project *projectstack.Project, stack *projectstack.Stack, -) (*opsmodels.Changes, error) { +func (o *DestroyOptions) preview(planResources *models.Spec, project *projectstack.Project, stack *projectstack.Stack, runtime runtime.Runtime) (*opsmodels.Changes, error) { log.Info("Start compute preview changes ...") - kubernetesRuntime, err := runtime.NewKubernetesRuntime() - if err != nil { - return nil, err - } - pc := &operation.PreviewOperation{ Operation: opsmodels.Operation{ OperationType: types.DestroyPreview, - Runtime: kubernetesRuntime, + Runtime: runtime, StateStorage: &local.FileSystemState{Path: filepath.Join(o.WorkDir, local.KusionState)}, ChangeOrder: &opsmodels.ChangeOrder{StepKeys: []string{}, ChangeSteps: map[string]*opsmodels.ChangeStep{}}, }, @@ -157,16 +157,12 @@ func (o *DestroyOptions) preview(planResources *models.Spec, return opsmodels.NewChanges(project, stack, rsp.Order), nil } -func (o *DestroyOptions) destroy(planResources *models.Spec, changes *opsmodels.Changes) error { +func (o *DestroyOptions) destroy(planResources *models.Spec, changes *opsmodels.Changes, runtime runtime.Runtime) error { // Build apply operation - kubernetesRuntime, err := runtime.NewKubernetesRuntime() - if err != nil { - return err - } do := &operation.DestroyOperation{ Operation: opsmodels.Operation{ - Runtime: kubernetesRuntime, + Runtime: runtime, StateStorage: &local.FileSystemState{Path: filepath.Join(o.WorkDir, local.KusionState)}, MsgCh: make(chan opsmodels.Message), }, diff --git a/pkg/kusionctl/cmd/destroy/options_test.go b/pkg/kusionctl/cmd/destroy/options_test.go index 543f1fab..ca41bc23 100644 --- a/pkg/kusionctl/cmd/destroy/options_test.go +++ b/pkg/kusionctl/cmd/destroy/options_test.go @@ -112,7 +112,7 @@ func Test_preview(t *testing.T) { mockOperationPreview() o := NewDestroyOptions() - _, err := o.preview(&models.Spec{Resources: []models.Resource{sa1}}, project, stack) + _, err := o.preview(&models.Spec{Resources: []models.Resource{sa1}}, project, stack, &fakerRuntime{}) assert.Nil(t, err) }) } @@ -135,14 +135,14 @@ func (f *fakerRuntime) Apply(ctx context.Context, request *runtime.ApplyRequest) } func (f *fakerRuntime) Read(ctx context.Context, request *runtime.ReadRequest) *runtime.ReadResponse { - if request.Resource.ResourceKey() == "fake-id" { + if request.PlanResource.ResourceKey() == "fake-id" { return &runtime.ReadResponse{ Resource: nil, Status: nil, } } return &runtime.ReadResponse{ - Resource: request.Resource, + Resource: request.PlanResource, Status: nil, } } @@ -187,8 +187,8 @@ var ( func newSA(name string) models.Resource { return models.Resource{ - ID: engine.BuildIDForKubernetes(apiVersion, kind, namespace, name), - + ID: engine.BuildIDForKubernetes(apiVersion, kind, namespace, name), + Type: "Kubernetes", Attributes: map[string]interface{}{ "apiVersion": apiVersion, "kind": kind, @@ -225,7 +225,7 @@ func Test_destroy(t *testing.T) { } changes := opsmodels.NewChanges(project, stack, order) - err := o.destroy(planResources, changes) + err := o.destroy(planResources, changes, &fakerRuntime{}) assert.Nil(t, err) }) t.Run("destroy failed", func(t *testing.T) { @@ -247,7 +247,7 @@ func Test_destroy(t *testing.T) { } changes := opsmodels.NewChanges(project, stack, order) - err := o.destroy(planResources, changes) + err := o.destroy(planResources, changes, &fakerRuntime{}) assert.NotNil(t, err) }) }