From 0782c9ba7c523c4682e0b235c67359acedb177ff Mon Sep 17 00:00:00 2001 From: Zdenek Vydra Date: Mon, 6 Mar 2023 09:16:57 +0100 Subject: [PATCH 01/64] WIP --- .gitignore | 36 ++++ go.mod | 70 ++++++ go.sum | 359 +++++++++++++++++++++++++++++++ internal/KYPOClient/client.go | 114 ++++++++++ internal/KYPOClient/main/main.go | 11 + 5 files changed, 590 insertions(+) create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/KYPOClient/client.go create mode 100644 internal/KYPOClient/main/main.go diff --git a/.gitignore b/.gitignore index 3b735ec..12b9510 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,39 @@ # Go workspace file go.work +======= +*.dll +*.exe +.DS_Store +example.tf +terraform.tfplan +terraform.tfstate +bin/ +dist/ +modules-dev/ +/pkg/ +website/.vagrant +website/.bundle +website/build +website/node_modules +.vagrant/ +*.backup +./*.tfstate +.terraform/ +*.log +*.bak +*~ +.*.swp +.idea +*.iml +*.test +*.iml + +website/vendor + +# Test exclusions +!command/test-fixtures/**/*.tfstate +!command/test-fixtures/**/.terraform/ + +# Keep windows files with windows line endings +*.winfile eol=crlf diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..094f7f9 --- /dev/null +++ b/go.mod @@ -0,0 +1,70 @@ +module terraform-provider-kypo + +go 1.18 + +require ( + github.com/hashicorp/terraform-plugin-docs v0.13.0 + github.com/hashicorp/terraform-plugin-framework v1.1.1 + github.com/hashicorp/terraform-plugin-go v0.14.3 + github.com/hashicorp/terraform-plugin-log v0.8.0 + github.com/hashicorp/terraform-plugin-sdk/v2 v2.25.0 +) + +require ( + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.1.1 // indirect + github.com/Masterminds/sprig/v3 v3.2.2 // indirect + github.com/agext/levenshtein v1.2.2 // indirect + github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect + github.com/armon/go-radix v1.0.0 // indirect + github.com/bgentry/speakeasy v0.1.0 // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-checkpoint v0.5.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect + github.com/hashicorp/go-hclog v1.4.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-plugin v1.4.8 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect + github.com/hashicorp/hc-install v0.5.0 // indirect + github.com/hashicorp/hcl/v2 v2.16.1 // indirect + github.com/hashicorp/logutils v1.0.0 // indirect + github.com/hashicorp/terraform-exec v0.17.3 // indirect + github.com/hashicorp/terraform-json v0.15.0 // indirect + github.com/hashicorp/terraform-registry-address v0.1.0 // indirect + github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect + github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect + github.com/huandu/xstrings v1.3.2 // indirect + github.com/imdario/mergo v0.3.13 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mitchellh/cli v1.1.5 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-testing-interface v1.14.1 // indirect + github.com/mitchellh/go-wordwrap v1.0.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/oklog/run v1.0.0 // indirect + github.com/posener/complete v1.2.3 // indirect + github.com/russross/blackfriday v1.6.0 // indirect + github.com/shopspring/decimal v1.3.1 // indirect + github.com/spf13/cast v1.5.0 // indirect + github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect + github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect + github.com/vmihailenco/tagparser v0.1.1 // indirect + github.com/zclconf/go-cty v1.12.1 // indirect + golang.org/x/crypto v0.6.0 // indirect + golang.org/x/mod v0.7.0 // indirect + golang.org/x/net v0.6.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect + google.golang.org/appengine v1.6.6 // indirect + google.golang.org/genproto v0.0.0-20200711021454-869866162049 // indirect + google.golang.org/grpc v1.51.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..2289cdf --- /dev/null +++ b/go.sum @@ -0,0 +1,359 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= +github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= +github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= +github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= +github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +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/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= +github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= +github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= +github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +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/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= +github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= +github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= +github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= +github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= +github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= +github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= +github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I= +github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-plugin v1.4.8 h1:CHGwpxYDOttQOY7HOWgETU9dyVjOXzniXDqJcYJE1zM= +github.com/hashicorp/go-plugin v1.4.8/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/hc-install v0.5.0 h1:D9bl4KayIYKEeJ4vUDe9L5huqxZXczKaykSRcmQ0xY0= +github.com/hashicorp/hc-install v0.5.0/go.mod h1:JyzMfbzfSBSjoDCRPna1vi/24BEDxFaCPfdHtM5SCdo= +github.com/hashicorp/hcl/v2 v2.16.1 h1:BwuxEMD/tsYgbhIW7UuI3crjovf3MzuFWiVgiv57iHg= +github.com/hashicorp/hcl/v2 v2.16.1/go.mod h1:JRmR89jycNkrrqnMmvPDMd56n1rQJ2Q6KocSLCMCXng= +github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/terraform-exec v0.17.3 h1:MX14Kvnka/oWGmIkyuyvL6POx25ZmKrjlaclkx3eErU= +github.com/hashicorp/terraform-exec v0.17.3/go.mod h1:+NELG0EqQekJzhvikkeQsOAZpsw0cv/03rbeQJqscAI= +github.com/hashicorp/terraform-json v0.15.0 h1:/gIyNtR6SFw6h5yzlbDbACyGvIhKtQi8mTsbkNd79lE= +github.com/hashicorp/terraform-json v0.15.0/go.mod h1:+L1RNzjDU5leLFZkHTFTbJXaoqUC6TqXlFgDoOXrtvk= +github.com/hashicorp/terraform-plugin-docs v0.13.0 h1:6e+VIWsVGb6jYJewfzq2ok2smPzZrt1Wlm9koLeKazY= +github.com/hashicorp/terraform-plugin-docs v0.13.0/go.mod h1:W0oCmHAjIlTHBbvtppWHe8fLfZ2BznQbuv8+UD8OucQ= +github.com/hashicorp/terraform-plugin-framework v1.1.1 h1:PbnEKHsIU8KTTzoztHQGgjZUWx7Kk8uGtpGMMc1p+oI= +github.com/hashicorp/terraform-plugin-framework v1.1.1/go.mod h1:DyZPxQA+4OKK5ELxFIIcqggcszqdWWUpTLPHAhS/tkY= +github.com/hashicorp/terraform-plugin-go v0.14.3 h1:nlnJ1GXKdMwsC8g1Nh05tK2wsC3+3BL/DBBxFEki+j0= +github.com/hashicorp/terraform-plugin-go v0.14.3/go.mod h1:7ees7DMZ263q8wQ6E4RdIdR6nHHJtrdt4ogX5lPkX1A= +github.com/hashicorp/terraform-plugin-log v0.8.0 h1:pX2VQ/TGKu+UU1rCay0OlzosNKe4Nz1pepLXj95oyy0= +github.com/hashicorp/terraform-plugin-log v0.8.0/go.mod h1:1myFrhVsBLeylQzYYEV17VVjtG8oYPRFdaZs7xdW2xs= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.25.0 h1:iNRjaJCatQS1rIbHs/vDvJ0GECsaGgxx780chA2Irpk= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.25.0/go.mod h1:XnVNLIS6bdMJbjSDujhX4Rlk24QpbGKbnrVFM4tZ7OU= +github.com/hashicorp/terraform-registry-address v0.1.0 h1:W6JkV9wbum+m516rCl5/NjKxCyTVaaUBbzYcMzBDO3U= +github.com/hashicorp/terraform-registry-address v0.1.0/go.mod h1:EnyO2jYO6j29DTHbJcm00E5nQTFeTtyZH3H5ycydQ5A= +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-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= +github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= +github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +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/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= +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/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +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/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/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.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng= +github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +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-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= +github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= +github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= +github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U= +github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= +github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY= +github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +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= +github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= +github.com/zclconf/go-cty v1.12.1 h1:PcupnljUm9EIvbgSHQnHhUr3fO6oFmkOrvs2BAFNXXY= +github.com/zclconf/go-cty v1.12.1/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA= +github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +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= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/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-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +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= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200711021454-869866162049 h1:YFTFpQhgvrLrmxtiIncJxFXeCyq84ixuKWVCaCAi9Oc= +google.golang.org/genproto v0.0.0-20200711021454-869866162049/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/KYPOClient/client.go b/internal/KYPOClient/client.go new file mode 100644 index 0000000..fd1fa84 --- /dev/null +++ b/internal/KYPOClient/client.go @@ -0,0 +1,114 @@ +package KYPOClient + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strings" +) + +type Client struct { + Endpoint string + HTTPClient *http.Client + Token string +} + +func NewClient(endpoint, token string) *Client { + client := Client{ + Endpoint: endpoint, + HTTPClient: http.DefaultClient, + Token: token, + } + + return &client +} + +func (c *Client) doRequest(req *http.Request, expected_status int) ([]byte, error) { + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", c.Token) + + res, err := c.HTTPClient.Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + + if res.StatusCode != expected_status { + return nil, fmt.Errorf("status: %d, body: %s", res.StatusCode, body) + } + + return body, err +} + +type Definition struct { + Id int64 `json:"id"` + Url string `json:"url"` + Name string `json:"name"` + Rev string `json:"rev"` + CreatedBy UserModel `json:"created_by"` +} + +type UserModel struct { + Id int64 `json:"id"` + Sub string `json:"sub"` + FullName string `json:"full_name"` + GivenName string `json:"given_name"` + FamilyName string `json:"family_name"` + Mail string `json:"mail"` +} + +type DefinitionRequest struct { + Url string `json:"url"` + Rev string `json:"rev"` +} + +func (c *Client) GetDefinition(definitionID int64) (*Definition, error) { + req, err := http.NewRequest("GET", fmt.Sprintf("%s/definitions/%d", c.Endpoint, definitionID), nil) + if err != nil { + return nil, err + } + + body, err := c.doRequest(req, http.StatusOK) + if err != nil { + return nil, err + } + + definition := Definition{} + err = json.Unmarshal(body, &definition) + if err != nil { + return nil, err + } + + return &definition, nil +} + +func (c *Client) CreateDefinition(url, rev string) (*Definition, error) { + requestBody, err := json.Marshal(DefinitionRequest{url, rev}) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", fmt.Sprintf("%s/definitions", c.Endpoint), strings.NewReader(string(requestBody))) + if err != nil { + return nil, err + } + + body, err := c.doRequest(req, http.StatusCreated) + if err != nil { + return nil, err + } + + definition := Definition{} + err = json.Unmarshal(body, &definition) + if err != nil { + return nil, err + } + + return &definition, nil +} diff --git a/internal/KYPOClient/main/main.go b/internal/KYPOClient/main/main.go new file mode 100644 index 0000000..7d44336 --- /dev/null +++ b/internal/KYPOClient/main/main.go @@ -0,0 +1,11 @@ +package main + +import "terraform-provider-kypo/internal/KYPOClient" + +func main() { + client := KYPOClient.NewClient("https://images.crp.kypo.muni.cz/kypo-sandbox-service/api/v1", + "***") + + _, _ = client.CreateDefinition("git@gitlab.ics.muni.cz:muni-kypo-trainings/games/junior-hacker.git", "master") + +} From 987c85ddee1c223d4dc3f51347c1b33168fc7b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= Date: Mon, 6 Mar 2023 14:55:34 +0100 Subject: [PATCH 02/64] WIP --- internal/KYPOClient/client.go | 76 +++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/internal/KYPOClient/client.go b/internal/KYPOClient/client.go index fd1fa84..4085c78 100644 --- a/internal/KYPOClient/client.go +++ b/internal/KYPOClient/client.go @@ -24,43 +24,42 @@ func NewClient(endpoint, token string) *Client { return &client } -func (c *Client) doRequest(req *http.Request, expected_status int) ([]byte, error) { +type ResourceNotFound struct { +} + +func (c *Client) doRequest(req *http.Request) ([]byte, int, error) { req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", c.Token) res, err := c.HTTPClient.Do(req) if err != nil { - return nil, err + return nil, 0, err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { - return nil, err + return nil, 0, err } - if res.StatusCode != expected_status { - return nil, fmt.Errorf("status: %d, body: %s", res.StatusCode, body) - } - - return body, err + return body, res.StatusCode, nil } type Definition struct { - Id int64 `json:"id"` - Url string `json:"url"` - Name string `json:"name"` - Rev string `json:"rev"` - CreatedBy UserModel `json:"created_by"` + Id int64 `json:"id" tfsdk:"id"` + Url string `json:"url" tfsdk:"url"` + Name string `json:"name" tfsdk:"name"` + Rev string `json:"rev" tfsdk:"rev"` + CreatedBy UserModel `json:"created_by" tfsdk:"created_by"` } type UserModel struct { - Id int64 `json:"id"` - Sub string `json:"sub"` - FullName string `json:"full_name"` - GivenName string `json:"given_name"` - FamilyName string `json:"family_name"` - Mail string `json:"mail"` + Id int64 `json:"id" tfsdk:"id"` + Sub string `json:"sub" tfsdk:"sub"` + FullName string `json:"full_name" tfsdk:"full_name"` + GivenName string `json:"given_name" tfsdk:"given_name"` + FamilyName string `json:"family_name" tfsdk:"family_name"` + Mail string `json:"mail" tfsdk:"mail"` } type DefinitionRequest struct { @@ -74,12 +73,21 @@ func (c *Client) GetDefinition(definitionID int64) (*Definition, error) { return nil, err } - body, err := c.doRequest(req, http.StatusOK) + body, status, err := c.doRequest(req) if err != nil { return nil, err } definition := Definition{} + + if status == http.StatusNotFound { + return nil, ResourceNotFound("a") + } + + if status != http.StatusOK { + return nil, fmt.Errorf("status: %d, body: %s", status, body) + } + err = json.Unmarshal(body, &definition) if err != nil { return nil, err @@ -99,11 +107,15 @@ func (c *Client) CreateDefinition(url, rev string) (*Definition, error) { return nil, err } - body, err := c.doRequest(req, http.StatusCreated) + body, status, err := c.doRequest(req) if err != nil { return nil, err } + if status != http.StatusCreated { + return nil, fmt.Errorf("status: %d, body: %s", status, body) + } + definition := Definition{} err = json.Unmarshal(body, &definition) if err != nil { @@ -112,3 +124,25 @@ func (c *Client) CreateDefinition(url, rev string) (*Definition, error) { return &definition, nil } + +func (c *Client) DeleteDefinition(definitionID int64) error { + req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/definitions/%d", c.Endpoint, definitionID), nil) + if err != nil { + return err + } + + body, status, err := c.doRequest(req) + if err != nil { + return err + } + + if status == http.StatusNotFound { + return nil + } + + if status != http.StatusNoContent { + return fmt.Errorf("status: %d, body: %s", status, body) + } + + return nil +} From 08b70e59056265408469f42b6d5e5476689b0ad8 Mon Sep 17 00:00:00 2001 From: Zdenek Vydra Date: Mon, 6 Mar 2023 21:47:59 +0100 Subject: [PATCH 03/64] Fix read not found --- internal/KYPOClient/client.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/KYPOClient/client.go b/internal/KYPOClient/client.go index 4085c78..3158b15 100644 --- a/internal/KYPOClient/client.go +++ b/internal/KYPOClient/client.go @@ -2,6 +2,7 @@ package KYPOClient import ( "encoding/json" + "errors" "fmt" "io/ioutil" "net/http" @@ -24,8 +25,7 @@ func NewClient(endpoint, token string) *Client { return &client } -type ResourceNotFound struct { -} +var ErrNotFound = errors.New("not found") func (c *Client) doRequest(req *http.Request) ([]byte, int, error) { req.Header.Set("Content-Type", "application/json") @@ -81,7 +81,7 @@ func (c *Client) GetDefinition(definitionID int64) (*Definition, error) { definition := Definition{} if status == http.StatusNotFound { - return nil, ResourceNotFound("a") + return nil, fmt.Errorf("definition %d %w", definitionID, ErrNotFound) } if status != http.StatusOK { From f45de1e83420966a53438948e081250d8798f859 Mon Sep 17 00:00:00 2001 From: Zdenek Vydra Date: Mon, 6 Mar 2023 21:55:49 +0100 Subject: [PATCH 04/64] Implement delete --- internal/KYPOClient/client.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/internal/KYPOClient/client.go b/internal/KYPOClient/client.go index 3158b15..d591aa4 100644 --- a/internal/KYPOClient/client.go +++ b/internal/KYPOClient/client.go @@ -136,11 +136,7 @@ func (c *Client) DeleteDefinition(definitionID int64) error { return err } - if status == http.StatusNotFound { - return nil - } - - if status != http.StatusNoContent { + if status != http.StatusNoContent && status != http.StatusNotFound { return fmt.Errorf("status: %d, body: %s", status, body) } From fe8cc954799b1b2576c325eed301dbed55b36c9f Mon Sep 17 00:00:00 2001 From: Zdenek Vydra Date: Mon, 6 Mar 2023 23:00:22 +0100 Subject: [PATCH 05/64] Provider read environment variables --- internal/KYPOClient/client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/KYPOClient/client.go b/internal/KYPOClient/client.go index d591aa4..f53b5ef 100644 --- a/internal/KYPOClient/client.go +++ b/internal/KYPOClient/client.go @@ -15,14 +15,14 @@ type Client struct { Token string } -func NewClient(endpoint, token string) *Client { +func NewClient(endpoint, token string) (*Client, error) { client := Client{ Endpoint: endpoint, HTTPClient: http.DefaultClient, Token: token, } - return &client + return &client, nil } var ErrNotFound = errors.New("not found") From ad7c39618882a18c519a85fd8898c354ad6e876c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= Date: Tue, 7 Mar 2023 14:11:14 +0100 Subject: [PATCH 06/64] First attempt at login --- internal/KYPOClient/client.go | 63 ++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/internal/KYPOClient/client.go b/internal/KYPOClient/client.go index f53b5ef..b3c0e2a 100644 --- a/internal/KYPOClient/client.go +++ b/internal/KYPOClient/client.go @@ -6,6 +6,8 @@ import ( "fmt" "io/ioutil" "net/http" + "net/http/cookiejar" + "regexp" "strings" ) @@ -13,9 +15,11 @@ type Client struct { Endpoint string HTTPClient *http.Client Token string + Username string + Password string } -func NewClient(endpoint, token string) (*Client, error) { +func NewClientWithToken(endpoint, token string) (*Client, error) { client := Client{ Endpoint: endpoint, HTTPClient: http.DefaultClient, @@ -25,6 +29,63 @@ func NewClient(endpoint, token string) (*Client, error) { return &client, nil } +func NewClient(endpoint, username, password string) (*Client, error) { + jar, err := cookiejar.New(nil) + client := Client{ + Endpoint: endpoint, + HTTPClient: &http.Client{Jar: jar}, + Username: username, + Password: password, + } + _, err = client.signIn() + if err != nil { + return nil, err + } + return &client, nil +} + +func (c *Client) signIn() (string, error) { + req, err := http.NewRequest("GET", fmt.Sprintf("%s/csirtmu-dummy-issuer-server/login", c.Endpoint), nil) + if err != nil { + return "", err + } + + res, err := c.HTTPClient.Do(req) + if err != nil { + return "", err + } + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return "", err + } + + err = res.Body.Close() + if err != nil { + return "", err + } + + csrfRegex := regexp.MustCompile("") + matches := csrfRegex.FindStringSubmatch(string(body)) + if len(matches) != 2 { + return "", errors.New("failed to match csrf token") + } + csrf := matches[1] + requestBody := fmt.Sprintf("username=%s&password=%s&_csrf=%s&submit=Login", c.Username, c.Password, csrf) + req, err = http.NewRequest("POST", fmt.Sprintf("%s/csirtmu-dummy-issuer-server/login", c.Endpoint), strings.NewReader(requestBody)) + + if err != nil { + return "", err + } + + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + res, err = c.HTTPClient.Do(req) + if err != nil { + return "", err + } + return res.Proto, err +} + var ErrNotFound = errors.New("not found") func (c *Client) doRequest(req *http.Request) ([]byte, int, error) { From bdac6813f3a78ba4c9447e586135a4f4feee9707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= Date: Tue, 14 Mar 2023 15:46:51 +0100 Subject: [PATCH 07/64] Get token from username and password --- internal/KYPOClient/client.go | 91 +++++++++++++++++++++++++++----- internal/KYPOClient/main/main.go | 5 +- 2 files changed, 81 insertions(+), 15 deletions(-) diff --git a/internal/KYPOClient/client.go b/internal/KYPOClient/client.go index b3c0e2a..a037d06 100644 --- a/internal/KYPOClient/client.go +++ b/internal/KYPOClient/client.go @@ -7,21 +7,24 @@ import ( "io/ioutil" "net/http" "net/http/cookiejar" + "net/url" "regexp" "strings" ) type Client struct { Endpoint string + ClientID string HTTPClient *http.Client Token string Username string Password string } -func NewClientWithToken(endpoint, token string) (*Client, error) { +func NewClientWithToken(endpoint, clientId, token string) (*Client, error) { client := Client{ Endpoint: endpoint, + ClientID: clientId, HTTPClient: http.DefaultClient, Token: token, } @@ -29,28 +32,67 @@ func NewClientWithToken(endpoint, token string) (*Client, error) { return &client, nil } -func NewClient(endpoint, username, password string) (*Client, error) { +func NewClient(endpoint, clientId, username, password string) (*Client, error) { jar, err := cookiejar.New(nil) client := Client{ Endpoint: endpoint, + ClientID: clientId, HTTPClient: &http.Client{Jar: jar}, Username: username, Password: password, } - _, err = client.signIn() + token, err := client.signIn() if err != nil { return nil, err } + client.Token = token return &client, nil } func (c *Client) signIn() (string, error) { - req, err := http.NewRequest("GET", fmt.Sprintf("%s/csirtmu-dummy-issuer-server/login", c.Endpoint), nil) + //jar, err := cookiejar.New(nil) + //if err != nil { + // return "", err + //} + + httpClient := *c.HTTPClient + + body, err := c.authorize(httpClient) if err != nil { return "", err } - res, err := c.HTTPClient.Do(req) + csrf, err := extractCsrf(body) + if err != nil { + return "", err + } + + return c.login(httpClient, csrf) +} + +func (c *Client) authorize(httpClient http.Client) (string, error) { + query := url.Values{} + query.Add("response_type", "id_token token") + query.Add("client_id", c.ClientID) + query.Add("scope", "openid email profile") + query.Add("redirect_uri", c.Endpoint) + + reqBody := url.Values{} + reqBody.Add("scope_openid", "openid") + reqBody.Add("scope_profile", "profile") + reqBody.Add("scope_email", "email") + reqBody.Add("remember", "until-revoked") + reqBody.Add("user_oauth_approval", "true") + reqBody.Add("authorize", "Authorize") + + req, err := http.NewRequest("GET", fmt.Sprintf("%s/csirtmu-dummy-issuer-server/authorize?%s", + c.Endpoint, query.Encode()), strings.NewReader(reqBody.Encode())) + if err != nil { + return "", err + } + + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + res, err := httpClient.Do(req) if err != nil { return "", err } @@ -65,32 +107,57 @@ func (c *Client) signIn() (string, error) { return "", err } + return string(body), err +} + +func extractCsrf(body string) (string, error) { csrfRegex := regexp.MustCompile("") - matches := csrfRegex.FindStringSubmatch(string(body)) + matches := csrfRegex.FindStringSubmatch(body) if len(matches) != 2 { return "", errors.New("failed to match csrf token") } - csrf := matches[1] - requestBody := fmt.Sprintf("username=%s&password=%s&_csrf=%s&submit=Login", c.Username, c.Password, csrf) - req, err = http.NewRequest("POST", fmt.Sprintf("%s/csirtmu-dummy-issuer-server/login", c.Endpoint), strings.NewReader(requestBody)) + return matches[1], nil +} + +func (c *Client) login(httpClient http.Client, csrf string) (string, error) { + query := url.Values{} + query.Add("username", c.Username) + query.Add("password", c.Password) + query.Add("_csrf", csrf) + query.Add("submit", "Login") + req, err := http.NewRequest("POST", fmt.Sprintf("%s/csirtmu-dummy-issuer-server/login", + c.Endpoint), strings.NewReader(query.Encode())) if err != nil { return "", err } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - res, err = c.HTTPClient.Do(req) + res, err := httpClient.Do(req) + if err != nil { + return "", err + } + if res.StatusCode != http.StatusOK { + return "", fmt.Errorf("login failed, got HTTP code: %d", res.StatusCode) + } + + values, err := url.ParseQuery(res.Request.URL.Fragment) if err != nil { return "", err } - return res.Proto, err + + token := values.Get("access_token") + if token == "" { + return "", fmt.Errorf("login failed, token is empty") + } + return token, err } var ErrNotFound = errors.New("not found") func (c *Client) doRequest(req *http.Request) ([]byte, int, error) { req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", c.Token) + req.Header.Set("Authorization", "Bearer "+c.Token) res, err := c.HTTPClient.Do(req) if err != nil { diff --git a/internal/KYPOClient/main/main.go b/internal/KYPOClient/main/main.go index 7d44336..1402001 100644 --- a/internal/KYPOClient/main/main.go +++ b/internal/KYPOClient/main/main.go @@ -3,9 +3,8 @@ package main import "terraform-provider-kypo/internal/KYPOClient" func main() { - client := KYPOClient.NewClient("https://images.crp.kypo.muni.cz/kypo-sandbox-service/api/v1", - "***") - + client, _ := KYPOClient.NewClient("https://images.crp.kypo.muni.cz", "bzhwmbxgyxALbAdMjYOgpolQzkiQHGwWRXxm", "kypo-admin", "***") + client.Endpoint += "/kypo-sandbox-service/api/v1" _, _ = client.CreateDefinition("git@gitlab.ics.muni.cz:muni-kypo-trainings/games/junior-hacker.git", "master") } From 57992da8e996f386aaf329a06234db74bb61090e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= Date: Wed, 15 Mar 2023 10:28:25 +0100 Subject: [PATCH 08/64] Working signup --- internal/KYPOClient/client.go | 101 +++++++++++++++++++++++++--------- 1 file changed, 76 insertions(+), 25 deletions(-) diff --git a/internal/KYPOClient/client.go b/internal/KYPOClient/client.go index a037d06..c02e87e 100644 --- a/internal/KYPOClient/client.go +++ b/internal/KYPOClient/client.go @@ -33,11 +33,10 @@ func NewClientWithToken(endpoint, clientId, token string) (*Client, error) { } func NewClient(endpoint, clientId, username, password string) (*Client, error) { - jar, err := cookiejar.New(nil) client := Client{ Endpoint: endpoint, ClientID: clientId, - HTTPClient: &http.Client{Jar: jar}, + HTTPClient: http.DefaultClient, Username: username, Password: password, } @@ -50,24 +49,28 @@ func NewClient(endpoint, clientId, username, password string) (*Client, error) { } func (c *Client) signIn() (string, error) { - //jar, err := cookiejar.New(nil) - //if err != nil { - // return "", err - //} + jar, err := cookiejar.New(nil) + if err != nil { + return "", err + } - httpClient := *c.HTTPClient + httpClient := http.Client{Jar: jar} - body, err := c.authorize(httpClient) + csrf, err := c.authorize(httpClient) if err != nil { return "", err } - csrf, err := extractCsrf(body) + token, csrf, err := c.login(httpClient, csrf) if err != nil { return "", err } - return c.login(httpClient, csrf) + if token != "" { + return token, err + } + + return c.authorizeFirstTime(httpClient, csrf) } func (c *Client) authorize(httpClient http.Client) (string, error) { @@ -77,25 +80,19 @@ func (c *Client) authorize(httpClient http.Client) (string, error) { query.Add("scope", "openid email profile") query.Add("redirect_uri", c.Endpoint) - reqBody := url.Values{} - reqBody.Add("scope_openid", "openid") - reqBody.Add("scope_profile", "profile") - reqBody.Add("scope_email", "email") - reqBody.Add("remember", "until-revoked") - reqBody.Add("user_oauth_approval", "true") - reqBody.Add("authorize", "Authorize") - req, err := http.NewRequest("GET", fmt.Sprintf("%s/csirtmu-dummy-issuer-server/authorize?%s", - c.Endpoint, query.Encode()), strings.NewReader(reqBody.Encode())) + c.Endpoint, query.Encode()), nil) if err != nil { return "", err } - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") res, err := httpClient.Do(req) if err != nil { return "", err } + if res.StatusCode != http.StatusOK { + return "", fmt.Errorf("authorize failed, got HTTP code: %d", res.StatusCode) + } body, err := ioutil.ReadAll(res.Body) if err != nil { @@ -107,7 +104,12 @@ func (c *Client) authorize(httpClient http.Client) (string, error) { return "", err } - return string(body), err + csrf, err := extractCsrf(string(body)) + if err != nil { + return "", err + } + + return csrf, nil } func extractCsrf(body string) (string, error) { @@ -119,7 +121,7 @@ func extractCsrf(body string) (string, error) { return matches[1], nil } -func (c *Client) login(httpClient http.Client, csrf string) (string, error) { +func (c *Client) login(httpClient http.Client, csrf string) (string, string, error) { query := url.Values{} query.Add("username", c.Username) query.Add("password", c.Password) @@ -129,16 +131,65 @@ func (c *Client) login(httpClient http.Client, csrf string) (string, error) { req, err := http.NewRequest("POST", fmt.Sprintf("%s/csirtmu-dummy-issuer-server/login", c.Endpoint), strings.NewReader(query.Encode())) if err != nil { - return "", err + return "", "", err } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + res, err := httpClient.Do(req) + if err != nil { + return "", "", err + } + if res.StatusCode != http.StatusOK { + return "", "", fmt.Errorf("login failed, got HTTP code: %d", res.StatusCode) + } + + values, err := url.ParseQuery(res.Request.URL.Fragment) + if err != nil { + return "", "", err + } + + token := values.Get("access_token") + + if token != "" { + return token, "", err + } + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return "", "", err + } + + csrf, err = extractCsrf(string(body)) + if err != nil { + return "", "", err + } + + return "", csrf, nil +} + +func (c *Client) authorizeFirstTime(httpClient http.Client, csrf string) (string, error) { + query := url.Values{} + query.Add("scope_openid", "openid") + query.Add("scope_profile", "profile") + query.Add("scope_email", "email") + query.Add("remember", "until-revoked") + query.Add("user_oauth_approval", "true") + query.Add("authorize", "Authorize") + query.Add("_csrf", csrf) + + req, err := http.NewRequest("POST", fmt.Sprintf("%s/csirtmu-dummy-issuer-server/authorize", + c.Endpoint), strings.NewReader(query.Encode())) + if err != nil { + return "", err + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + res, err := httpClient.Do(req) if err != nil { return "", err } if res.StatusCode != http.StatusOK { - return "", fmt.Errorf("login failed, got HTTP code: %d", res.StatusCode) + return "", fmt.Errorf("authorizeFirstTime failed, got HTTP code: %d", res.StatusCode) } values, err := url.ParseQuery(res.Request.URL.Fragment) @@ -148,7 +199,7 @@ func (c *Client) login(httpClient http.Client, csrf string) (string, error) { token := values.Get("access_token") if token == "" { - return "", fmt.Errorf("login failed, token is empty") + return "", fmt.Errorf("authorizeFirstTime failed, token is empty") } return token, err } From 53a7d296fab575f2c563d053e15cc6233f9ecadf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= Date: Wed, 15 Mar 2023 12:50:30 +0100 Subject: [PATCH 09/64] Rename client_id, set sandbox service path in Go client --- internal/KYPOClient/client.go | 6 +++--- internal/KYPOClient/main/main.go | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/internal/KYPOClient/client.go b/internal/KYPOClient/client.go index c02e87e..fb95c73 100644 --- a/internal/KYPOClient/client.go +++ b/internal/KYPOClient/client.go @@ -247,7 +247,7 @@ type DefinitionRequest struct { } func (c *Client) GetDefinition(definitionID int64) (*Definition, error) { - req, err := http.NewRequest("GET", fmt.Sprintf("%s/definitions/%d", c.Endpoint, definitionID), nil) + req, err := http.NewRequest("GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions/%d", c.Endpoint, definitionID), nil) if err != nil { return nil, err } @@ -281,7 +281,7 @@ func (c *Client) CreateDefinition(url, rev string) (*Definition, error) { return nil, err } - req, err := http.NewRequest("POST", fmt.Sprintf("%s/definitions", c.Endpoint), strings.NewReader(string(requestBody))) + req, err := http.NewRequest("POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions", c.Endpoint), strings.NewReader(string(requestBody))) if err != nil { return nil, err } @@ -305,7 +305,7 @@ func (c *Client) CreateDefinition(url, rev string) (*Definition, error) { } func (c *Client) DeleteDefinition(definitionID int64) error { - req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/definitions/%d", c.Endpoint, definitionID), nil) + req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions/%d", c.Endpoint, definitionID), nil) if err != nil { return err } diff --git a/internal/KYPOClient/main/main.go b/internal/KYPOClient/main/main.go index 1402001..8e9a11e 100644 --- a/internal/KYPOClient/main/main.go +++ b/internal/KYPOClient/main/main.go @@ -4,7 +4,6 @@ import "terraform-provider-kypo/internal/KYPOClient" func main() { client, _ := KYPOClient.NewClient("https://images.crp.kypo.muni.cz", "bzhwmbxgyxALbAdMjYOgpolQzkiQHGwWRXxm", "kypo-admin", "***") - client.Endpoint += "/kypo-sandbox-service/api/v1" _, _ = client.CreateDefinition("git@gitlab.ics.muni.cz:muni-kypo-trainings/games/junior-hacker.git", "master") } From cb6742889b9363d963e07b53e6cf1033c5f45cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= Date: Wed, 15 Mar 2023 15:03:28 +0100 Subject: [PATCH 10/64] Rename definitions resource to sandbox_definition --- internal/KYPOClient/client.go | 16 ++++++++-------- internal/KYPOClient/main/main.go | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/KYPOClient/client.go b/internal/KYPOClient/client.go index fb95c73..0affa78 100644 --- a/internal/KYPOClient/client.go +++ b/internal/KYPOClient/client.go @@ -224,7 +224,7 @@ func (c *Client) doRequest(req *http.Request) ([]byte, int, error) { return body, res.StatusCode, nil } -type Definition struct { +type SandboxDefinition struct { Id int64 `json:"id" tfsdk:"id"` Url string `json:"url" tfsdk:"url"` Name string `json:"name" tfsdk:"name"` @@ -241,12 +241,12 @@ type UserModel struct { Mail string `json:"mail" tfsdk:"mail"` } -type DefinitionRequest struct { +type SandboxDefinitionRequest struct { Url string `json:"url"` Rev string `json:"rev"` } -func (c *Client) GetDefinition(definitionID int64) (*Definition, error) { +func (c *Client) GetSandboxDefinition(definitionID int64) (*SandboxDefinition, error) { req, err := http.NewRequest("GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions/%d", c.Endpoint, definitionID), nil) if err != nil { return nil, err @@ -257,7 +257,7 @@ func (c *Client) GetDefinition(definitionID int64) (*Definition, error) { return nil, err } - definition := Definition{} + definition := SandboxDefinition{} if status == http.StatusNotFound { return nil, fmt.Errorf("definition %d %w", definitionID, ErrNotFound) @@ -275,8 +275,8 @@ func (c *Client) GetDefinition(definitionID int64) (*Definition, error) { return &definition, nil } -func (c *Client) CreateDefinition(url, rev string) (*Definition, error) { - requestBody, err := json.Marshal(DefinitionRequest{url, rev}) +func (c *Client) CreateSandboxDefinition(url, rev string) (*SandboxDefinition, error) { + requestBody, err := json.Marshal(SandboxDefinitionRequest{url, rev}) if err != nil { return nil, err } @@ -295,7 +295,7 @@ func (c *Client) CreateDefinition(url, rev string) (*Definition, error) { return nil, fmt.Errorf("status: %d, body: %s", status, body) } - definition := Definition{} + definition := SandboxDefinition{} err = json.Unmarshal(body, &definition) if err != nil { return nil, err @@ -304,7 +304,7 @@ func (c *Client) CreateDefinition(url, rev string) (*Definition, error) { return &definition, nil } -func (c *Client) DeleteDefinition(definitionID int64) error { +func (c *Client) DeleteSandboxDefinition(definitionID int64) error { req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions/%d", c.Endpoint, definitionID), nil) if err != nil { return err diff --git a/internal/KYPOClient/main/main.go b/internal/KYPOClient/main/main.go index 8e9a11e..c573d90 100644 --- a/internal/KYPOClient/main/main.go +++ b/internal/KYPOClient/main/main.go @@ -4,6 +4,6 @@ import "terraform-provider-kypo/internal/KYPOClient" func main() { client, _ := KYPOClient.NewClient("https://images.crp.kypo.muni.cz", "bzhwmbxgyxALbAdMjYOgpolQzkiQHGwWRXxm", "kypo-admin", "***") - _, _ = client.CreateDefinition("git@gitlab.ics.muni.cz:muni-kypo-trainings/games/junior-hacker.git", "master") + _, _ = client.CreateSandboxDefinition("git@gitlab.ics.muni.cz:muni-kypo-trainings/games/junior-hacker.git", "master") } From 92f7abdedcd211381eeb28680c9c503b08c51d4c Mon Sep 17 00:00:00 2001 From: Zdenek Vydra Date: Sun, 19 Mar 2023 18:59:02 +0100 Subject: [PATCH 11/64] Move sandbox definition client to a separate file --- internal/KYPOClient/client.go | 90 --------------------- internal/KYPOClient/sandbox_definition.go | 97 +++++++++++++++++++++++ 2 files changed, 97 insertions(+), 90 deletions(-) create mode 100644 internal/KYPOClient/sandbox_definition.go diff --git a/internal/KYPOClient/client.go b/internal/KYPOClient/client.go index 0affa78..72ac08b 100644 --- a/internal/KYPOClient/client.go +++ b/internal/KYPOClient/client.go @@ -1,7 +1,6 @@ package KYPOClient import ( - "encoding/json" "errors" "fmt" "io/ioutil" @@ -224,14 +223,6 @@ func (c *Client) doRequest(req *http.Request) ([]byte, int, error) { return body, res.StatusCode, nil } -type SandboxDefinition struct { - Id int64 `json:"id" tfsdk:"id"` - Url string `json:"url" tfsdk:"url"` - Name string `json:"name" tfsdk:"name"` - Rev string `json:"rev" tfsdk:"rev"` - CreatedBy UserModel `json:"created_by" tfsdk:"created_by"` -} - type UserModel struct { Id int64 `json:"id" tfsdk:"id"` Sub string `json:"sub" tfsdk:"sub"` @@ -240,84 +231,3 @@ type UserModel struct { FamilyName string `json:"family_name" tfsdk:"family_name"` Mail string `json:"mail" tfsdk:"mail"` } - -type SandboxDefinitionRequest struct { - Url string `json:"url"` - Rev string `json:"rev"` -} - -func (c *Client) GetSandboxDefinition(definitionID int64) (*SandboxDefinition, error) { - req, err := http.NewRequest("GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions/%d", c.Endpoint, definitionID), nil) - if err != nil { - return nil, err - } - - body, status, err := c.doRequest(req) - if err != nil { - return nil, err - } - - definition := SandboxDefinition{} - - if status == http.StatusNotFound { - return nil, fmt.Errorf("definition %d %w", definitionID, ErrNotFound) - } - - if status != http.StatusOK { - return nil, fmt.Errorf("status: %d, body: %s", status, body) - } - - err = json.Unmarshal(body, &definition) - if err != nil { - return nil, err - } - - return &definition, nil -} - -func (c *Client) CreateSandboxDefinition(url, rev string) (*SandboxDefinition, error) { - requestBody, err := json.Marshal(SandboxDefinitionRequest{url, rev}) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions", c.Endpoint), strings.NewReader(string(requestBody))) - if err != nil { - return nil, err - } - - body, status, err := c.doRequest(req) - if err != nil { - return nil, err - } - - if status != http.StatusCreated { - return nil, fmt.Errorf("status: %d, body: %s", status, body) - } - - definition := SandboxDefinition{} - err = json.Unmarshal(body, &definition) - if err != nil { - return nil, err - } - - return &definition, nil -} - -func (c *Client) DeleteSandboxDefinition(definitionID int64) error { - req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions/%d", c.Endpoint, definitionID), nil) - if err != nil { - return err - } - - body, status, err := c.doRequest(req) - if err != nil { - return err - } - - if status != http.StatusNoContent && status != http.StatusNotFound { - return fmt.Errorf("status: %d, body: %s", status, body) - } - - return nil -} diff --git a/internal/KYPOClient/sandbox_definition.go b/internal/KYPOClient/sandbox_definition.go new file mode 100644 index 0000000..cb5042d --- /dev/null +++ b/internal/KYPOClient/sandbox_definition.go @@ -0,0 +1,97 @@ +package KYPOClient + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" +) + +type SandboxDefinition struct { + Id int64 `json:"id" tfsdk:"id"` + Url string `json:"url" tfsdk:"url"` + Name string `json:"name" tfsdk:"name"` + Rev string `json:"rev" tfsdk:"rev"` + CreatedBy UserModel `json:"created_by" tfsdk:"created_by"` +} + +type SandboxDefinitionRequest struct { + Url string `json:"url"` + Rev string `json:"rev"` +} + +func (c *Client) GetSandboxDefinition(definitionID int64) (*SandboxDefinition, error) { + req, err := http.NewRequest("GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions/%d", c.Endpoint, definitionID), nil) + if err != nil { + return nil, err + } + + body, status, err := c.doRequest(req) + if err != nil { + return nil, err + } + + definition := SandboxDefinition{} + + if status == http.StatusNotFound { + return nil, fmt.Errorf("definition %d %w", definitionID, ErrNotFound) + } + + if status != http.StatusOK { + return nil, fmt.Errorf("status: %d, body: %s", status, body) + } + + err = json.Unmarshal(body, &definition) + if err != nil { + return nil, err + } + + return &definition, nil +} + +func (c *Client) CreateSandboxDefinition(url, rev string) (*SandboxDefinition, error) { + requestBody, err := json.Marshal(SandboxDefinitionRequest{url, rev}) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions", c.Endpoint), strings.NewReader(string(requestBody))) + if err != nil { + return nil, err + } + + body, status, err := c.doRequest(req) + if err != nil { + return nil, err + } + + if status != http.StatusCreated { + return nil, fmt.Errorf("status: %d, body: %s", status, body) + } + + definition := SandboxDefinition{} + err = json.Unmarshal(body, &definition) + if err != nil { + return nil, err + } + + return &definition, nil +} + +func (c *Client) DeleteSandboxDefinition(definitionID int64) error { + req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions/%d", c.Endpoint, definitionID), nil) + if err != nil { + return err + } + + body, status, err := c.doRequest(req) + if err != nil { + return err + } + + if status != http.StatusNoContent && status != http.StatusNotFound { + return fmt.Errorf("status: %d, body: %s", status, body) + } + + return nil +} From 1be796c9a9e8a467e3c42bb7017bf3aa048f4eb0 Mon Sep 17 00:00:00 2001 From: Zdenek Vydra Date: Sun, 19 Mar 2023 19:00:05 +0100 Subject: [PATCH 12/64] Implement sandbox pool resource --- internal/KYPOClient/sandbox_pool.go | 111 ++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 internal/KYPOClient/sandbox_pool.go diff --git a/internal/KYPOClient/sandbox_pool.go b/internal/KYPOClient/sandbox_pool.go new file mode 100644 index 0000000..b495b3c --- /dev/null +++ b/internal/KYPOClient/sandbox_pool.go @@ -0,0 +1,111 @@ +package KYPOClient + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" +) + +type SandboxPool struct { + Id int64 `json:"id" tfsdk:"id"` + DefinitionId int64 `json:"definition_id" tfsdk:"definition_id"` + Size int64 `json:"size" tfsdk:"size"` + MaxSize int64 `json:"max_size" tfsdk:"max_size"` + LockId int64 `json:"lock_id" tfsdk:"lock_id"` + Rev string `json:"rev" tfsdk:"rev"` + RevSha string `json:"rev_sha" tfsdk:"rev_sha"` + CreatedBy UserModel `json:"created_by" tfsdk:"created_by"` + HardwareUsage HardwareUsage `json:"hardware_usage" tfsdk:"hardware_usage"` + Definition SandboxDefinition `json:"definition" tfsdk:"definition"` +} + +type SandboxPoolRequest struct { + DefinitionId int64 `json:"definition_id"` + MaxSize int64 `json:"max_size"` +} + +type HardwareUsage struct { + Vcpu string `json:"vcpu" tfsdk:"vcpu"` + Ram string `json:"ram" tfsdk:"ram"` + Instances string `json:"instances" tfsdk:"instances"` + Network string `json:"network" tfsdk:"network"` + Subnet string `json:"subnet" tfsdk:"subnet"` + Port string `json:"port" tfsdk:"port"` +} + +func (c *Client) GetSandboxPool(poolId int64) (*SandboxPool, error) { + req, err := http.NewRequest("GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d", c.Endpoint, poolId), nil) + if err != nil { + return nil, err + } + + body, status, err := c.doRequest(req) + if err != nil { + return nil, err + } + + pool := SandboxPool{} + + if status == http.StatusNotFound { + return nil, fmt.Errorf("pool %d %w", poolId, ErrNotFound) + } + + if status != http.StatusOK { + return nil, fmt.Errorf("status: %d, body: %s", status, body) + } + + err = json.Unmarshal(body, &pool) + if err != nil { + return nil, err + } + + return &pool, nil +} + +func (c *Client) CreateSandboxPool(definitionId, maxSize int64) (*SandboxPool, error) { + requestBody, err := json.Marshal(SandboxPoolRequest{definitionId, maxSize}) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools", c.Endpoint), strings.NewReader(string(requestBody))) + if err != nil { + return nil, err + } + + body, status, err := c.doRequest(req) + if err != nil { + return nil, err + } + + if status != http.StatusCreated { + return nil, fmt.Errorf("status: %d, body: %s", status, body) + } + + pool := SandboxPool{} + err = json.Unmarshal(body, &pool) + if err != nil { + return nil, err + } + + return &pool, nil +} + +func (c *Client) DeleteSandboxPool(poolId int64) error { + req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d", c.Endpoint, poolId), nil) + if err != nil { + return err + } + + body, status, err := c.doRequest(req) + if err != nil { + return err + } + + if status != http.StatusNoContent && status != http.StatusNotFound { + return fmt.Errorf("status: %d, body: %s", status, body) + } + + return nil +} From d1345618c2c488bf3d0da56b4cfb9eba53e0cc37 Mon Sep 17 00:00:00 2001 From: Zdenek Vydra Date: Sun, 19 Mar 2023 23:00:26 +0100 Subject: [PATCH 13/64] Pool cleanup WIP --- internal/KYPOClient/client.go | 7 +++++++ internal/KYPOClient/sandbox_pool.go | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/internal/KYPOClient/client.go b/internal/KYPOClient/client.go index 72ac08b..9114f0a 100644 --- a/internal/KYPOClient/client.go +++ b/internal/KYPOClient/client.go @@ -231,3 +231,10 @@ type UserModel struct { FamilyName string `json:"family_name" tfsdk:"family_name"` Mail string `json:"mail" tfsdk:"mail"` } + +func boolToString(b bool) string { + if b { + return "true" + } + return "false" +} diff --git a/internal/KYPOClient/sandbox_pool.go b/internal/KYPOClient/sandbox_pool.go index b495b3c..791b67f 100644 --- a/internal/KYPOClient/sandbox_pool.go +++ b/internal/KYPOClient/sandbox_pool.go @@ -93,6 +93,11 @@ func (c *Client) CreateSandboxPool(definitionId, maxSize int64) (*SandboxPool, e } func (c *Client) DeleteSandboxPool(poolId int64) error { + err := c.CleanupSandboxPool(poolId, true) + if err != nil { + return err + } + req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d", c.Endpoint, poolId), nil) if err != nil { return err @@ -109,3 +114,22 @@ func (c *Client) DeleteSandboxPool(poolId int64) error { return nil } + +func (c *Client) CleanupSandboxPool(poolId int64, force bool) error { + req, err := http.NewRequest("POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d/cleanup-requests?force=%s", + c.Endpoint, poolId, boolToString(force)), nil) + if err != nil { + return err + } + + body, status, err := c.doRequest(req) + if err != nil { + return err + } + + if status != http.StatusAccepted { + return fmt.Errorf("status: %d, body: %s", status, body) + } + + return nil +} From 03286902bf59addf713e25a2418e1e04cf1cd64a Mon Sep 17 00:00:00 2001 From: Zdenek Vydra Date: Mon, 20 Mar 2023 21:27:51 +0100 Subject: [PATCH 14/64] Do not cleanup pool on delete --- internal/KYPOClient/sandbox_pool.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/internal/KYPOClient/sandbox_pool.go b/internal/KYPOClient/sandbox_pool.go index 791b67f..434bf50 100644 --- a/internal/KYPOClient/sandbox_pool.go +++ b/internal/KYPOClient/sandbox_pool.go @@ -93,11 +93,6 @@ func (c *Client) CreateSandboxPool(definitionId, maxSize int64) (*SandboxPool, e } func (c *Client) DeleteSandboxPool(poolId int64) error { - err := c.CleanupSandboxPool(poolId, true) - if err != nil { - return err - } - req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d", c.Endpoint, poolId), nil) if err != nil { return err @@ -130,6 +125,6 @@ func (c *Client) CleanupSandboxPool(poolId int64, force bool) error { if status != http.StatusAccepted { return fmt.Errorf("status: %d, body: %s", status, body) } - + // Wait before cleanup has finished? return nil } From 6239399e790baa6db479420a9efa0d7920ee5721 Mon Sep 17 00:00:00 2001 From: Zdenek Vydra Date: Mon, 20 Mar 2023 21:32:09 +0100 Subject: [PATCH 15/64] Remove redundant definition_id from pool --- internal/KYPOClient/sandbox_pool.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/KYPOClient/sandbox_pool.go b/internal/KYPOClient/sandbox_pool.go index 434bf50..08fa698 100644 --- a/internal/KYPOClient/sandbox_pool.go +++ b/internal/KYPOClient/sandbox_pool.go @@ -9,7 +9,6 @@ import ( type SandboxPool struct { Id int64 `json:"id" tfsdk:"id"` - DefinitionId int64 `json:"definition_id" tfsdk:"definition_id"` Size int64 `json:"size" tfsdk:"size"` MaxSize int64 `json:"max_size" tfsdk:"max_size"` LockId int64 `json:"lock_id" tfsdk:"lock_id"` From c51a8fb65e43eef94087d27ed5681681c5533602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= Date: Tue, 21 Mar 2023 15:31:03 +0100 Subject: [PATCH 16/64] Delete scaffolding_example --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 12b9510..54bd8df 100644 --- a/.gitignore +++ b/.gitignore @@ -45,7 +45,6 @@ website/node_modules .idea *.iml *.test -*.iml website/vendor @@ -55,3 +54,4 @@ website/vendor # Keep windows files with windows line endings *.winfile eol=crlf +/env.sh From 01290c0b7f31d186fc8a5011d1ec8a781dc721e9 Mon Sep 17 00:00:00 2001 From: Zdenek Vydra Date: Fri, 24 Mar 2023 14:35:52 +0100 Subject: [PATCH 17/64] Basic implementation of allocation unit --- .../KYPOClient/sandbox_allocation_unit.go | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 internal/KYPOClient/sandbox_allocation_unit.go diff --git a/internal/KYPOClient/sandbox_allocation_unit.go b/internal/KYPOClient/sandbox_allocation_unit.go new file mode 100644 index 0000000..255aa8b --- /dev/null +++ b/internal/KYPOClient/sandbox_allocation_unit.go @@ -0,0 +1,95 @@ +package KYPOClient + +import ( + "encoding/json" + "fmt" + "net/http" +) + +type SandboxAllocationUnit struct { + Id int64 `json:"id" tfsdk:"id"` + PoolId int64 `json:"pool_id" tfsdk:"pool_id"` + AllocationRequest SandboxRequest `json:"allocation_request" tfsdk:"allocation_request"` + CleanupRequest SandboxRequest `json:"cleanup_request" tfsdk:"cleanup_request"` + CreatedBy UserModel `json:"created_by" tfsdk:"created_by"` + Locked bool `json:"locked" tfsdk:"locked"` +} + +type SandboxRequest struct { + Id int64 `json:"id" tfsdk:"id"` + AllocationUnitId int64 `json:"allocation_unit_id" tfsdk:"allocation_unit_id"` + Created string `json:"created" tfsdk:"created"` + Stages []string `json:"stages" tfsdk:"stages"` +} + +func (c *Client) GetSandboxAllocationUnit(unitId int64) (*SandboxAllocationUnit, error) { + req, err := http.NewRequest("GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d", c.Endpoint, unitId), nil) + if err != nil { + return nil, err + } + + body, status, err := c.doRequest(req) + if err != nil { + return nil, err + } + + allocationUnit := SandboxAllocationUnit{} + + if status == http.StatusNotFound { + return nil, fmt.Errorf("allocationUnit %d %w", unitId, ErrNotFound) + } + + if status != http.StatusOK { + return nil, fmt.Errorf("status: %d, body: %s", status, body) + } + + err = json.Unmarshal(body, &allocationUnit) + if err != nil { + return nil, err + } + + return &allocationUnit, nil +} + +func (c *Client) CreateSandboxAllocationUnits(poolId, count int64) ([]SandboxAllocationUnit, error) { + // check if cleanup request is already created + req, err := http.NewRequest("POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d/sandbox-allocation-units?count=%d", c.Endpoint, poolId, count), nil) + if err != nil { + return nil, err + } + + body, status, err := c.doRequest(req) + if err != nil { + return nil, err + } + + if status != http.StatusCreated { + return nil, fmt.Errorf("status: %d, body: %s", status, body) + } + + var allocationUnit []SandboxAllocationUnit + err = json.Unmarshal(body, &allocationUnit) + if err != nil { + return nil, err + } + + return allocationUnit, nil +} + +func (c *Client) DeleteSandboxAllocationUnit(unitId int64) error { + req, err := http.NewRequest("POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d/cleanup-request", c.Endpoint, unitId), nil) + if err != nil { + return err + } + + body, status, err := c.doRequest(req) + if err != nil { + return err + } + + if status != http.StatusCreated && status != http.StatusNotFound { + return fmt.Errorf("status: %d, body: %s", status, body) + } + // unmarshall response and await deletion + return nil +} From ac2e3c9ba6830b683d04086045cd1e4c14e985a2 Mon Sep 17 00:00:00 2001 From: Zdenek Vydra Date: Sun, 26 Mar 2023 23:09:10 +0200 Subject: [PATCH 18/64] Wait for completion of allocation unit --- go.mod | 1 + go.sum | 2 + .../KYPOClient/sandbox_allocation_unit.go | 126 ++++++++++++++++-- 3 files changed, 121 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 094f7f9..1b4a0e9 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/hashicorp/terraform-plugin-go v0.14.3 github.com/hashicorp/terraform-plugin-log v0.8.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.25.0 + golang.org/x/exp v0.0.0-20230321023759-10a507213a29 ) require ( diff --git a/go.sum b/go.sum index 2289cdf..167b95a 100644 --- a/go.sum +++ b/go.sum @@ -239,6 +239,8 @@ golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= diff --git a/internal/KYPOClient/sandbox_allocation_unit.go b/internal/KYPOClient/sandbox_allocation_unit.go index 255aa8b..4d533fc 100644 --- a/internal/KYPOClient/sandbox_allocation_unit.go +++ b/internal/KYPOClient/sandbox_allocation_unit.go @@ -2,8 +2,12 @@ package KYPOClient import ( "encoding/json" + "errors" "fmt" "net/http" + "time" + + "golang.org/x/exp/slices" ) type SandboxAllocationUnit struct { @@ -36,7 +40,7 @@ func (c *Client) GetSandboxAllocationUnit(unitId int64) (*SandboxAllocationUnit, allocationUnit := SandboxAllocationUnit{} if status == http.StatusNotFound { - return nil, fmt.Errorf("allocationUnit %d %w", unitId, ErrNotFound) + return nil, fmt.Errorf("sandbox allocation unit %d %w", unitId, ErrNotFound) } if status != http.StatusOK { @@ -52,7 +56,6 @@ func (c *Client) GetSandboxAllocationUnit(unitId int64) (*SandboxAllocationUnit, } func (c *Client) CreateSandboxAllocationUnits(poolId, count int64) ([]SandboxAllocationUnit, error) { - // check if cleanup request is already created req, err := http.NewRequest("POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d/sandbox-allocation-units?count=%d", c.Endpoint, poolId, count), nil) if err != nil { return nil, err @@ -76,20 +79,127 @@ func (c *Client) CreateSandboxAllocationUnits(poolId, count int64) ([]SandboxAll return allocationUnit, nil } -func (c *Client) DeleteSandboxAllocationUnit(unitId int64) error { +func (c *Client) CreateSandboxAllocationUnitAwait(poolId int64) (*SandboxAllocationUnit, error) { + units, err := c.CreateSandboxAllocationUnits(poolId, 1) + if err != nil { + return nil, err + } + if len(units) != 1 { + return nil, fmt.Errorf("expected one allocation unit to be created, got %d instead", len(units)) + } + unit := units[0] + request, err := c.PollRequestFinished(unit.Id, 5*time.Second, "allocation") + unit.AllocationRequest = *request + return &unit, err +} + +type valueOrError[T any] struct { + err error + value T +} + +func (c *Client) CreateSandboxAllocationUnitAwaitTimeout(poolId int64, timeout time.Duration) (*SandboxAllocationUnit, error) { + resultChannel := make(chan valueOrError[*SandboxAllocationUnit], 1) + go func() { + res, err := c.CreateSandboxAllocationUnitAwait(poolId) + resultChannel <- valueOrError[*SandboxAllocationUnit]{err: err, value: res} + }() + + select { + case result := <-resultChannel: + return result.value, result.err + case <-time.After(timeout): + return nil, fmt.Errorf("creating sandbox allocation unit %d %w %s", poolId, ErrTimeout, timeout) + } +} + +func (c *Client) CreateSandboxCleanupRequest(unitId int64) (*SandboxRequest, error) { req, err := http.NewRequest("POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d/cleanup-request", c.Endpoint, unitId), nil) if err != nil { - return err + return nil, err } body, status, err := c.doRequest(req) + if err != nil { + return nil, err + } + + if status == http.StatusNotFound { + return nil, fmt.Errorf("sandbox allocation unit %d %w", unitId, ErrNotFound) + } + + if status != http.StatusCreated { + return nil, fmt.Errorf("status: %d, body: %s", status, body) + } + + sandboxRequest := SandboxRequest{} + err = json.Unmarshal(body, &sandboxRequest) + if err != nil { + return nil, err + } + + return &sandboxRequest, nil +} + +func (c *Client) PollRequestFinished(unitId int64, pollTime time.Duration, requestType string) (*SandboxRequest, error) { + ticker := time.Tick(pollTime) + for range ticker { + req, err := http.NewRequest("GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d/%s-request", c.Endpoint, unitId, requestType), nil) + if err != nil { + return nil, err + } + + body, status, err := c.doRequest(req) + if err != nil { + return nil, err + } + + if status == http.StatusNotFound { + return nil, fmt.Errorf("sandbox request %d %w", unitId, ErrNotFound) + } + + if status != http.StatusOK { + return nil, fmt.Errorf("status: %d, body: %s", status, body) + } + sandboxRequest := SandboxRequest{} + err = json.Unmarshal(body, &sandboxRequest) + if err != nil { + return nil, err + } + + if !slices.Contains(sandboxRequest.Stages, "RUNNING") && !slices.Contains(sandboxRequest.Stages, "IN_QUEUE") { + return &sandboxRequest, nil + } + } + return nil, nil // Unreachable +} + +func (c *Client) DeleteSandboxAllocationUnit(unitId int64) error { + _, err := c.CreateSandboxCleanupRequest(unitId) if err != nil { return err } - if status != http.StatusCreated && status != http.StatusNotFound { - return fmt.Errorf("status: %d, body: %s", status, body) + _, err = c.PollRequestFinished(unitId, 3*time.Second, "cleanup") + // After cleanup is finished it deletes itself and 404 is thrown + if errors.Is(err, ErrNotFound) { + return nil + } + return err +} + +var ErrTimeout = errors.New("has not finished within timeout") + +func (c *Client) DeleteSandboxAllocationUnitWithTimeout(unitId int64, timeout time.Duration) error { + resultChannel := make(chan error, 1) + go func() { + resultChannel <- c.DeleteSandboxAllocationUnit(unitId) + }() + + select { + case result := <-resultChannel: + return result + case <-time.After(timeout): + return fmt.Errorf("deleting sandbox allocation unit %d %w %s", unitId, ErrTimeout, timeout) } - // unmarshall response and await deletion - return nil } From a9424a7a4566592e64ead85eb2beafd7bd363d9d Mon Sep 17 00:00:00 2001 From: Zdenek Vydra Date: Thu, 6 Apr 2023 18:10:04 +0200 Subject: [PATCH 19/64] Refactor go client - split into several files --- internal/KYPOClient/auth.go | 168 +++++++++++++++ internal/KYPOClient/client.go | 199 ------------------ internal/KYPOClient/error.go | 12 ++ .../KYPOClient/sandbox_allocation_unit.go | 13 +- internal/KYPOClient/util.go | 40 ++++ 5 files changed, 223 insertions(+), 209 deletions(-) create mode 100644 internal/KYPOClient/auth.go create mode 100644 internal/KYPOClient/error.go create mode 100644 internal/KYPOClient/util.go diff --git a/internal/KYPOClient/auth.go b/internal/KYPOClient/auth.go new file mode 100644 index 0000000..63655d4 --- /dev/null +++ b/internal/KYPOClient/auth.go @@ -0,0 +1,168 @@ +package KYPOClient + +import ( + "errors" + "fmt" + "io/ioutil" + "net/http" + "net/http/cookiejar" + "net/url" + "regexp" + "strings" +) + +func (c *Client) signIn() (string, error) { + jar, err := cookiejar.New(nil) + if err != nil { + return "", err + } + + httpClient := http.Client{Jar: jar} + + csrf, err := c.authorize(httpClient) + if err != nil { + return "", err + } + + token, csrf, err := c.login(httpClient, csrf) + if err != nil { + return "", err + } + + if token != "" { + return token, err + } + + return c.authorizeFirstTime(httpClient, csrf) +} + +func (c *Client) authorize(httpClient http.Client) (string, error) { + query := url.Values{} + query.Add("response_type", "id_token token") + query.Add("client_id", c.ClientID) + query.Add("scope", "openid email profile") + query.Add("redirect_uri", c.Endpoint) + + req, err := http.NewRequest("GET", fmt.Sprintf("%s/csirtmu-dummy-issuer-server/authorize?%s", + c.Endpoint, query.Encode()), nil) + if err != nil { + return "", err + } + + res, err := httpClient.Do(req) + if err != nil { + return "", err + } + if res.StatusCode != http.StatusOK { + return "", fmt.Errorf("authorize failed, got HTTP code: %d", res.StatusCode) + } + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return "", err + } + + err = res.Body.Close() + if err != nil { + return "", err + } + + csrf, err := extractCsrf(string(body)) + if err != nil { + return "", err + } + + return csrf, nil +} + +func extractCsrf(body string) (string, error) { + csrfRegex := regexp.MustCompile("") + matches := csrfRegex.FindStringSubmatch(body) + if len(matches) != 2 { + return "", errors.New("failed to match csrf token") + } + return matches[1], nil +} + +func (c *Client) login(httpClient http.Client, csrf string) (string, string, error) { + query := url.Values{} + query.Add("username", c.Username) + query.Add("password", c.Password) + query.Add("_csrf", csrf) + query.Add("submit", "Login") + + req, err := http.NewRequest("POST", fmt.Sprintf("%s/csirtmu-dummy-issuer-server/login", + c.Endpoint), strings.NewReader(query.Encode())) + if err != nil { + return "", "", err + } + + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + res, err := httpClient.Do(req) + if err != nil { + return "", "", err + } + if res.StatusCode != http.StatusOK { + return "", "", fmt.Errorf("login failed, got HTTP code: %d", res.StatusCode) + } + + values, err := url.ParseQuery(res.Request.URL.Fragment) + if err != nil { + return "", "", err + } + + token := values.Get("access_token") + + if token != "" { + return token, "", err + } + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return "", "", err + } + + csrf, err = extractCsrf(string(body)) + if err != nil { + return "", "", err + } + + return "", csrf, nil +} + +func (c *Client) authorizeFirstTime(httpClient http.Client, csrf string) (string, error) { + query := url.Values{} + query.Add("scope_openid", "openid") + query.Add("scope_profile", "profile") + query.Add("scope_email", "email") + query.Add("remember", "until-revoked") + query.Add("user_oauth_approval", "true") + query.Add("authorize", "Authorize") + query.Add("_csrf", csrf) + + req, err := http.NewRequest("POST", fmt.Sprintf("%s/csirtmu-dummy-issuer-server/authorize", + c.Endpoint), strings.NewReader(query.Encode())) + if err != nil { + return "", err + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + res, err := httpClient.Do(req) + if err != nil { + return "", err + } + if res.StatusCode != http.StatusOK { + return "", fmt.Errorf("authorizeFirstTime failed, got HTTP code: %d", res.StatusCode) + } + + values, err := url.ParseQuery(res.Request.URL.Fragment) + if err != nil { + return "", err + } + + token := values.Get("access_token") + if token == "" { + return "", fmt.Errorf("authorizeFirstTime failed, token is empty") + } + return token, err +} diff --git a/internal/KYPOClient/client.go b/internal/KYPOClient/client.go index 9114f0a..7ad0cb8 100644 --- a/internal/KYPOClient/client.go +++ b/internal/KYPOClient/client.go @@ -1,14 +1,7 @@ package KYPOClient import ( - "errors" - "fmt" - "io/ioutil" "net/http" - "net/http/cookiejar" - "net/url" - "regexp" - "strings" ) type Client struct { @@ -46,195 +39,3 @@ func NewClient(endpoint, clientId, username, password string) (*Client, error) { client.Token = token return &client, nil } - -func (c *Client) signIn() (string, error) { - jar, err := cookiejar.New(nil) - if err != nil { - return "", err - } - - httpClient := http.Client{Jar: jar} - - csrf, err := c.authorize(httpClient) - if err != nil { - return "", err - } - - token, csrf, err := c.login(httpClient, csrf) - if err != nil { - return "", err - } - - if token != "" { - return token, err - } - - return c.authorizeFirstTime(httpClient, csrf) -} - -func (c *Client) authorize(httpClient http.Client) (string, error) { - query := url.Values{} - query.Add("response_type", "id_token token") - query.Add("client_id", c.ClientID) - query.Add("scope", "openid email profile") - query.Add("redirect_uri", c.Endpoint) - - req, err := http.NewRequest("GET", fmt.Sprintf("%s/csirtmu-dummy-issuer-server/authorize?%s", - c.Endpoint, query.Encode()), nil) - if err != nil { - return "", err - } - - res, err := httpClient.Do(req) - if err != nil { - return "", err - } - if res.StatusCode != http.StatusOK { - return "", fmt.Errorf("authorize failed, got HTTP code: %d", res.StatusCode) - } - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - return "", err - } - - err = res.Body.Close() - if err != nil { - return "", err - } - - csrf, err := extractCsrf(string(body)) - if err != nil { - return "", err - } - - return csrf, nil -} - -func extractCsrf(body string) (string, error) { - csrfRegex := regexp.MustCompile("") - matches := csrfRegex.FindStringSubmatch(body) - if len(matches) != 2 { - return "", errors.New("failed to match csrf token") - } - return matches[1], nil -} - -func (c *Client) login(httpClient http.Client, csrf string) (string, string, error) { - query := url.Values{} - query.Add("username", c.Username) - query.Add("password", c.Password) - query.Add("_csrf", csrf) - query.Add("submit", "Login") - - req, err := http.NewRequest("POST", fmt.Sprintf("%s/csirtmu-dummy-issuer-server/login", - c.Endpoint), strings.NewReader(query.Encode())) - if err != nil { - return "", "", err - } - - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - res, err := httpClient.Do(req) - if err != nil { - return "", "", err - } - if res.StatusCode != http.StatusOK { - return "", "", fmt.Errorf("login failed, got HTTP code: %d", res.StatusCode) - } - - values, err := url.ParseQuery(res.Request.URL.Fragment) - if err != nil { - return "", "", err - } - - token := values.Get("access_token") - - if token != "" { - return token, "", err - } - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - return "", "", err - } - - csrf, err = extractCsrf(string(body)) - if err != nil { - return "", "", err - } - - return "", csrf, nil -} - -func (c *Client) authorizeFirstTime(httpClient http.Client, csrf string) (string, error) { - query := url.Values{} - query.Add("scope_openid", "openid") - query.Add("scope_profile", "profile") - query.Add("scope_email", "email") - query.Add("remember", "until-revoked") - query.Add("user_oauth_approval", "true") - query.Add("authorize", "Authorize") - query.Add("_csrf", csrf) - - req, err := http.NewRequest("POST", fmt.Sprintf("%s/csirtmu-dummy-issuer-server/authorize", - c.Endpoint), strings.NewReader(query.Encode())) - if err != nil { - return "", err - } - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - - res, err := httpClient.Do(req) - if err != nil { - return "", err - } - if res.StatusCode != http.StatusOK { - return "", fmt.Errorf("authorizeFirstTime failed, got HTTP code: %d", res.StatusCode) - } - - values, err := url.ParseQuery(res.Request.URL.Fragment) - if err != nil { - return "", err - } - - token := values.Get("access_token") - if token == "" { - return "", fmt.Errorf("authorizeFirstTime failed, token is empty") - } - return token, err -} - -var ErrNotFound = errors.New("not found") - -func (c *Client) doRequest(req *http.Request) ([]byte, int, error) { - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+c.Token) - - res, err := c.HTTPClient.Do(req) - if err != nil { - return nil, 0, err - } - defer res.Body.Close() - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - return nil, 0, err - } - - return body, res.StatusCode, nil -} - -type UserModel struct { - Id int64 `json:"id" tfsdk:"id"` - Sub string `json:"sub" tfsdk:"sub"` - FullName string `json:"full_name" tfsdk:"full_name"` - GivenName string `json:"given_name" tfsdk:"given_name"` - FamilyName string `json:"family_name" tfsdk:"family_name"` - Mail string `json:"mail" tfsdk:"mail"` -} - -func boolToString(b bool) string { - if b { - return "true" - } - return "false" -} diff --git a/internal/KYPOClient/error.go b/internal/KYPOClient/error.go new file mode 100644 index 0000000..fc36b0a --- /dev/null +++ b/internal/KYPOClient/error.go @@ -0,0 +1,12 @@ +package KYPOClient + +import "errors" + +var ErrTimeout = errors.New("has not finished within timeout") + +var ErrNotFound = errors.New("not found") + +type valueOrError[T any] struct { + err error + value T +} diff --git a/internal/KYPOClient/sandbox_allocation_unit.go b/internal/KYPOClient/sandbox_allocation_unit.go index 4d533fc..75060d4 100644 --- a/internal/KYPOClient/sandbox_allocation_unit.go +++ b/internal/KYPOClient/sandbox_allocation_unit.go @@ -93,11 +93,6 @@ func (c *Client) CreateSandboxAllocationUnitAwait(poolId int64) (*SandboxAllocat return &unit, err } -type valueOrError[T any] struct { - err error - value T -} - func (c *Client) CreateSandboxAllocationUnitAwaitTimeout(poolId int64, timeout time.Duration) (*SandboxAllocationUnit, error) { resultChannel := make(chan valueOrError[*SandboxAllocationUnit], 1) go func() { @@ -174,7 +169,7 @@ func (c *Client) PollRequestFinished(unitId int64, pollTime time.Duration, reque return nil, nil // Unreachable } -func (c *Client) DeleteSandboxAllocationUnit(unitId int64) error { +func (c *Client) CreateSandboxCleanupRequestAwait(unitId int64) error { _, err := c.CreateSandboxCleanupRequest(unitId) if err != nil { return err @@ -188,12 +183,10 @@ func (c *Client) DeleteSandboxAllocationUnit(unitId int64) error { return err } -var ErrTimeout = errors.New("has not finished within timeout") - -func (c *Client) DeleteSandboxAllocationUnitWithTimeout(unitId int64, timeout time.Duration) error { +func (c *Client) CreateSandboxCleanupRequestAwaitTimeout(unitId int64, timeout time.Duration) error { resultChannel := make(chan error, 1) go func() { - resultChannel <- c.DeleteSandboxAllocationUnit(unitId) + resultChannel <- c.CreateSandboxCleanupRequestAwait(unitId) }() select { diff --git a/internal/KYPOClient/util.go b/internal/KYPOClient/util.go new file mode 100644 index 0000000..215940c --- /dev/null +++ b/internal/KYPOClient/util.go @@ -0,0 +1,40 @@ +package KYPOClient + +import ( + "io/ioutil" + "net/http" +) + +type UserModel struct { + Id int64 `json:"id" tfsdk:"id"` + Sub string `json:"sub" tfsdk:"sub"` + FullName string `json:"full_name" tfsdk:"full_name"` + GivenName string `json:"given_name" tfsdk:"given_name"` + FamilyName string `json:"family_name" tfsdk:"family_name"` + Mail string `json:"mail" tfsdk:"mail"` +} + +func (c *Client) doRequest(req *http.Request) ([]byte, int, error) { + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+c.Token) + + res, err := c.HTTPClient.Do(req) + if err != nil { + return nil, 0, err + } + defer res.Body.Close() + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, 0, err + } + + return body, res.StatusCode, nil +} + +func boolToString(b bool) string { + if b { + return "true" + } + return "false" +} From c9f1ac13ba37e472fe9b47f80eb8f88518aab531 Mon Sep 17 00:00:00 2001 From: Zdenek Vydra Date: Sat, 8 Apr 2023 11:53:03 +0200 Subject: [PATCH 20/64] Improve error handling --- internal/KYPOClient/error.go | 24 ++++++++++++++++--- .../KYPOClient/sandbox_allocation_unit.go | 15 ++++++------ internal/KYPOClient/sandbox_definition.go | 3 ++- internal/KYPOClient/sandbox_pool.go | 3 ++- 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/internal/KYPOClient/error.go b/internal/KYPOClient/error.go index fc36b0a..763fa0f 100644 --- a/internal/KYPOClient/error.go +++ b/internal/KYPOClient/error.go @@ -1,10 +1,28 @@ package KYPOClient -import "errors" +import ( + "fmt" + "time" +) -var ErrTimeout = errors.New("has not finished within timeout") +type ErrTimeout struct { + Action string + Identifier string + Timeout time.Duration +} + +func (e *ErrTimeout) Error() string { + return fmt.Sprintf("%s: %s has not finished within %s", e.Action, e.Identifier, e.Timeout) +} -var ErrNotFound = errors.New("not found") +type ErrNotFound struct { + ResourceName string + Identifier string +} + +func (e *ErrNotFound) Error() string { + return fmt.Sprintf("resource %s: %s was not found", e.ResourceName, e.Identifier) +} type valueOrError[T any] struct { err error diff --git a/internal/KYPOClient/sandbox_allocation_unit.go b/internal/KYPOClient/sandbox_allocation_unit.go index 75060d4..c4ccda6 100644 --- a/internal/KYPOClient/sandbox_allocation_unit.go +++ b/internal/KYPOClient/sandbox_allocation_unit.go @@ -2,9 +2,9 @@ package KYPOClient import ( "encoding/json" - "errors" "fmt" "net/http" + "strconv" "time" "golang.org/x/exp/slices" @@ -40,7 +40,7 @@ func (c *Client) GetSandboxAllocationUnit(unitId int64) (*SandboxAllocationUnit, allocationUnit := SandboxAllocationUnit{} if status == http.StatusNotFound { - return nil, fmt.Errorf("sandbox allocation unit %d %w", unitId, ErrNotFound) + return nil, &ErrNotFound{ResourceName: "sandbox allocation unit", Identifier: strconv.FormatInt(unitId, 10)} } if status != http.StatusOK { @@ -104,7 +104,7 @@ func (c *Client) CreateSandboxAllocationUnitAwaitTimeout(poolId int64, timeout t case result := <-resultChannel: return result.value, result.err case <-time.After(timeout): - return nil, fmt.Errorf("creating sandbox allocation unit %d %w %s", poolId, ErrTimeout, timeout) + return nil, &ErrTimeout{Action: "creating sandbox allocation unit", Identifier: strconv.FormatInt(poolId, 10), Timeout: timeout} } } @@ -120,7 +120,7 @@ func (c *Client) CreateSandboxCleanupRequest(unitId int64) (*SandboxRequest, err } if status == http.StatusNotFound { - return nil, fmt.Errorf("sandbox allocation unit %d %w", unitId, ErrNotFound) + return nil, &ErrNotFound{ResourceName: "sandbox allocation unit", Identifier: strconv.FormatInt(unitId, 10)} } if status != http.StatusCreated { @@ -150,7 +150,8 @@ func (c *Client) PollRequestFinished(unitId int64, pollTime time.Duration, reque } if status == http.StatusNotFound { - return nil, fmt.Errorf("sandbox request %d %w", unitId, ErrNotFound) + return nil, &ErrNotFound{ResourceName: "sandbox request", Identifier: strconv.FormatInt(unitId, 10)} + } if status != http.StatusOK { @@ -177,7 +178,7 @@ func (c *Client) CreateSandboxCleanupRequestAwait(unitId int64) error { _, err = c.PollRequestFinished(unitId, 3*time.Second, "cleanup") // After cleanup is finished it deletes itself and 404 is thrown - if errors.Is(err, ErrNotFound) { + if _, ok := err.(*ErrNotFound); ok { return nil } return err @@ -193,6 +194,6 @@ func (c *Client) CreateSandboxCleanupRequestAwaitTimeout(unitId int64, timeout t case result := <-resultChannel: return result case <-time.After(timeout): - return fmt.Errorf("deleting sandbox allocation unit %d %w %s", unitId, ErrTimeout, timeout) + return &ErrTimeout{Action: "deleting sandbox allocation unit", Identifier: strconv.FormatInt(unitId, 10), Timeout: timeout} } } diff --git a/internal/KYPOClient/sandbox_definition.go b/internal/KYPOClient/sandbox_definition.go index cb5042d..86aba1f 100644 --- a/internal/KYPOClient/sandbox_definition.go +++ b/internal/KYPOClient/sandbox_definition.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "net/http" + "strconv" "strings" ) @@ -34,7 +35,7 @@ func (c *Client) GetSandboxDefinition(definitionID int64) (*SandboxDefinition, e definition := SandboxDefinition{} if status == http.StatusNotFound { - return nil, fmt.Errorf("definition %d %w", definitionID, ErrNotFound) + return nil, &ErrNotFound{ResourceName: "sandbox definition", Identifier: strconv.FormatInt(definitionID, 10)} } if status != http.StatusOK { diff --git a/internal/KYPOClient/sandbox_pool.go b/internal/KYPOClient/sandbox_pool.go index 08fa698..dac000a 100644 --- a/internal/KYPOClient/sandbox_pool.go +++ b/internal/KYPOClient/sandbox_pool.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "net/http" + "strconv" "strings" ) @@ -47,7 +48,7 @@ func (c *Client) GetSandboxPool(poolId int64) (*SandboxPool, error) { pool := SandboxPool{} if status == http.StatusNotFound { - return nil, fmt.Errorf("pool %d %w", poolId, ErrNotFound) + return nil, &ErrNotFound{ResourceName: "sandbox pool", Identifier: strconv.FormatInt(poolId, 10)} } if status != http.StatusOK { From f8c23eb7ce9c9b4783936840b4cb24ebedbc60c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= Date: Wed, 12 Apr 2023 12:46:44 +0200 Subject: [PATCH 21/64] Sandbox cancel allocation request on delete and verify cleanup was successful --- .../KYPOClient/sandbox_allocation_unit.go | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/internal/KYPOClient/sandbox_allocation_unit.go b/internal/KYPOClient/sandbox_allocation_unit.go index c4ccda6..7ea1798 100644 --- a/internal/KYPOClient/sandbox_allocation_unit.go +++ b/internal/KYPOClient/sandbox_allocation_unit.go @@ -181,6 +181,9 @@ func (c *Client) CreateSandboxCleanupRequestAwait(unitId int64) error { if _, ok := err.(*ErrNotFound); ok { return nil } + if err == nil { + return fmt.Errorf("sandbox cleanup request finished with error") + } return err } @@ -197,3 +200,25 @@ func (c *Client) CreateSandboxCleanupRequestAwaitTimeout(unitId int64, timeout t return &ErrTimeout{Action: "deleting sandbox allocation unit", Identifier: strconv.FormatInt(unitId, 10), Timeout: timeout} } } + +func (c *Client) CancelSandboxAllocationRequest(allocationRequestId int64) error { + req, err := http.NewRequest("PATCH", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/allocation-requests/%d/cancel", c.Endpoint, allocationRequestId), nil) + if err != nil { + return err + } + + body, status, err := c.doRequest(req) + if err != nil { + return err + } + + if status == http.StatusNotFound { + return &ErrNotFound{ResourceName: "sandbox allocation request", Identifier: strconv.FormatInt(allocationRequestId, 10)} + } + + if status != http.StatusOK { + return fmt.Errorf("status: %d, body: %s", status, body) + } + + return nil +} From 7b390fc1e5f28afdc198f907d04e2676c229f60e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= Date: Wed, 12 Apr 2023 16:31:22 +0200 Subject: [PATCH 22/64] Fix linter issues --- internal/KYPOClient/sandbox_allocation_unit.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/KYPOClient/sandbox_allocation_unit.go b/internal/KYPOClient/sandbox_allocation_unit.go index 7ea1798..06a3484 100644 --- a/internal/KYPOClient/sandbox_allocation_unit.go +++ b/internal/KYPOClient/sandbox_allocation_unit.go @@ -137,8 +137,8 @@ func (c *Client) CreateSandboxCleanupRequest(unitId int64) (*SandboxRequest, err } func (c *Client) PollRequestFinished(unitId int64, pollTime time.Duration, requestType string) (*SandboxRequest, error) { - ticker := time.Tick(pollTime) - for range ticker { + ticker := time.NewTicker(pollTime) + for range ticker.C { req, err := http.NewRequest("GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d/%s-request", c.Endpoint, unitId, requestType), nil) if err != nil { return nil, err From 5b71a6eec88aeae79884fe30005ea5863e767e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= Date: Thu, 13 Apr 2023 14:30:11 +0200 Subject: [PATCH 23/64] Check sandbox cleanup fail more thoroughly --- internal/KYPOClient/sandbox_allocation_unit.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/KYPOClient/sandbox_allocation_unit.go b/internal/KYPOClient/sandbox_allocation_unit.go index 06a3484..0f3dfeb 100644 --- a/internal/KYPOClient/sandbox_allocation_unit.go +++ b/internal/KYPOClient/sandbox_allocation_unit.go @@ -176,12 +176,12 @@ func (c *Client) CreateSandboxCleanupRequestAwait(unitId int64) error { return err } - _, err = c.PollRequestFinished(unitId, 3*time.Second, "cleanup") + cleanupRequest, err := c.PollRequestFinished(unitId, 3*time.Second, "cleanup") // After cleanup is finished it deletes itself and 404 is thrown if _, ok := err.(*ErrNotFound); ok { return nil } - if err == nil { + if err == nil && slices.Contains(cleanupRequest.Stages, "FAILED") { return fmt.Errorf("sandbox cleanup request finished with error") } return err From 99bff3df3c208b3e2aa49c17684e95ff0c605d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <80331839+vydrazde@users.noreply.github.com> Date: Tue, 2 May 2023 14:57:17 +0200 Subject: [PATCH 24/64] Sandbox request output data source (#7) * WIP * Implement and simplify sandbox request output datasource * Generate docs * Fix acc tests * Increase CI timeout, merge sandbox request output test into sandbox allocation unit test --------- Co-authored-by: Zdenek Vydra --- .../KYPOClient/sandbox_allocation_unit.go | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/internal/KYPOClient/sandbox_allocation_unit.go b/internal/KYPOClient/sandbox_allocation_unit.go index 0f3dfeb..141b56f 100644 --- a/internal/KYPOClient/sandbox_allocation_unit.go +++ b/internal/KYPOClient/sandbox_allocation_unit.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "net/http" + "net/url" "strconv" "time" @@ -26,6 +27,28 @@ type SandboxRequest struct { Stages []string `json:"stages" tfsdk:"stages"` } +type SandboxRequestStageOutput struct { + Page int64 `json:"page" tfsdk:"page"` + PageSize int64 `json:"page_size" tfsdk:"page_size"` + PageCount int64 `json:"page_count" tfsdk:"page_count"` + Count int64 `json:"count" tfsdk:"line_count"` + TotalCount int64 `json:"total_count" tfsdk:"total_count"` + Result string `json:"result" tfsdk:"result"` +} + +type sandboxRequestStageOutputRaw struct { + Page int64 `json:"page"` + PageSize int64 `json:"page_size"` + PageCount int64 `json:"page_count"` + Count int64 `json:"count"` + TotalCount int64 `json:"total_count"` + Results []outputLine `json:"results"` +} + +type outputLine struct { + Content string `json:"content"` +} + func (c *Client) GetSandboxAllocationUnit(unitId int64) (*SandboxAllocationUnit, error) { req, err := http.NewRequest("GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d", c.Endpoint, unitId), nil) if err != nil { @@ -222,3 +245,50 @@ func (c *Client) CancelSandboxAllocationRequest(allocationRequestId int64) error return nil } + +func (c *Client) GetSandboxRequestAnsibleOutputs(sandboxRequestId, page, pageSize int64, outputType string) (*SandboxRequestStageOutput, error) { + query := url.Values{} + query.Add("page", strconv.FormatInt(page, 10)) + query.Add("page_size", strconv.FormatInt(pageSize, 10)) + + req, err := http.NewRequest("GET", fmt.Sprintf( + "%s/kypo-sandbox-service/api/v1/allocation-requests/%d/stages/%s/outputs?%s", c.Endpoint, sandboxRequestId, outputType, query.Encode()), nil) + if err != nil { + return nil, err + } + + body, status, err := c.doRequest(req) + if err != nil { + return nil, err + } + + outputRaw := sandboxRequestStageOutputRaw{} + + if status == http.StatusNotFound { + return nil, &ErrNotFound{ResourceName: "sandbox request", Identifier: strconv.FormatInt(sandboxRequestId, 10)} + } + + if status != http.StatusOK { + return nil, fmt.Errorf("status: %d, body: %s", status, body) + } + + err = json.Unmarshal(body, &outputRaw) + if err != nil { + return nil, err + } + + output := SandboxRequestStageOutput{ + Page: outputRaw.Page, + PageSize: outputRaw.PageSize, + PageCount: outputRaw.PageCount, + Count: outputRaw.Count, + TotalCount: outputRaw.TotalCount, + Result: "", + } + + for _, line := range outputRaw.Results { + output.Result += "\n" + line.Content + } + + return &output, nil +} From a519892ba5394208827951023579d9f181b344b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <80331839+vydrazde@users.noreply.github.com> Date: Tue, 19 Sep 2023 09:14:16 +0200 Subject: [PATCH 25/64] Implement training definitions (#44) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Training definition WIP [skip ci] * Finish LTD * Finish LTD terraform resource * Add ATD * Generate docs * Add commented out tests --------- Co-authored-by: Zdeněk Vydra --- internal/KYPOClient/training_definition.go | 92 +++++++++++++++++++ .../training_definition_adaptive.go | 92 +++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 internal/KYPOClient/training_definition.go create mode 100644 internal/KYPOClient/training_definition_adaptive.go diff --git a/internal/KYPOClient/training_definition.go b/internal/KYPOClient/training_definition.go new file mode 100644 index 0000000..7205ffe --- /dev/null +++ b/internal/KYPOClient/training_definition.go @@ -0,0 +1,92 @@ +package KYPOClient + +import ( + "encoding/json" + "fmt" + "net/http" + "strconv" + "strings" +) + +type TrainingDefinition struct { + Id int64 `json:"id" tfsdk:"id"` + Content string `json:"content" tfsdk:"content"` +} + +func (c *Client) GetTrainingDefinition(definitionID int64) (*TrainingDefinition, error) { + req, err := http.NewRequest("GET", fmt.Sprintf("%s/kypo-rest-training/api/v1/exports/training-definitions/%d", c.Endpoint, definitionID), nil) + if err != nil { + return nil, err + } + req.Header.Set("accept", "application/octet-stream") + + body, status, err := c.doRequest(req) + if err != nil { + return nil, err + } + + if status == http.StatusNotFound { + return nil, &ErrNotFound{ResourceName: "training definition", Identifier: strconv.FormatInt(definitionID, 10)} + } + + if status != http.StatusOK { + return nil, fmt.Errorf("status: %d, body: %s", status, body) + } + + definition := TrainingDefinition{ + Id: definitionID, + Content: string(body), + } + + return &definition, nil +} + +func (c *Client) CreateTrainingDefinition(content string) (*TrainingDefinition, error) { + req, err := http.NewRequest("POST", fmt.Sprintf("%s/kypo-rest-training/api/v1/imports/training-definitions", c.Endpoint), strings.NewReader(content)) + if err != nil { + return nil, err + } + + body, status, err := c.doRequest(req) + if err != nil { + return nil, err + } + + if status != http.StatusOK { + return nil, fmt.Errorf("status: %d, body: %s", status, body) + } + + id := struct { + Id int64 `json:"id"` + }{} + + err = json.Unmarshal(body, &id) + if err != nil { + return nil, err + } + + definition := TrainingDefinition{ + Id: id.Id, + Content: content, + } + + return &definition, nil +} + +func (c *Client) DeleteTrainingDefinition(definitionID int64) error { + req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/kypo-rest-training/api/v1/training-definitions/%d", c.Endpoint, definitionID), nil) + if err != nil { + return err + } + + body, status, err := c.doRequest(req) + if err != nil { + return err + } + + if status != http.StatusOK && status != http.StatusNotFound { + return fmt.Errorf("status: %d, body: %s", status, body) + } + + return nil +} diff --git a/internal/KYPOClient/training_definition_adaptive.go b/internal/KYPOClient/training_definition_adaptive.go new file mode 100644 index 0000000..d7f9b6f --- /dev/null +++ b/internal/KYPOClient/training_definition_adaptive.go @@ -0,0 +1,92 @@ +package KYPOClient + +import ( + "encoding/json" + "fmt" + "net/http" + "strconv" + "strings" +) + +type TrainingDefinitionAdaptive struct { + Id int64 `json:"id" tfsdk:"id"` + Content string `json:"content" tfsdk:"content"` +} + +func (c *Client) GetTrainingDefinitionAdaptive(definitionID int64) (*TrainingDefinitionAdaptive, error) { + req, err := http.NewRequest("GET", fmt.Sprintf("%s/kypo-adaptive-training/api/v1/exports/training-definitions/%d", c.Endpoint, definitionID), nil) + if err != nil { + return nil, err + } + req.Header.Set("accept", "application/octet-stream") + + body, status, err := c.doRequest(req) + if err != nil { + return nil, err + } + + if status == http.StatusNotFound { + return nil, &ErrNotFound{ResourceName: "training definition adaptive", Identifier: strconv.FormatInt(definitionID, 10)} + } + + if status != http.StatusOK { + return nil, fmt.Errorf("status: %d, body: %s", status, body) + } + + definition := TrainingDefinitionAdaptive{ + Id: definitionID, + Content: string(body), + } + + return &definition, nil +} + +func (c *Client) CreateTrainingDefinitionAdaptive(content string) (*TrainingDefinitionAdaptive, error) { + req, err := http.NewRequest("POST", fmt.Sprintf("%s/kypo-adaptive-training/api/v1/imports/training-definitions", c.Endpoint), strings.NewReader(content)) + if err != nil { + return nil, err + } + + body, status, err := c.doRequest(req) + if err != nil { + return nil, err + } + + if status != http.StatusOK { + return nil, fmt.Errorf("status: %d, body: %s", status, body) + } + + id := struct { + Id int64 `json:"id"` + }{} + + err = json.Unmarshal(body, &id) + if err != nil { + return nil, err + } + + definition := TrainingDefinitionAdaptive{ + Id: id.Id, + Content: content, + } + + return &definition, nil +} + +func (c *Client) DeleteTrainingDefinitionAdaptive(definitionID int64) error { + req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/kypo-adaptive-training/api/v1/training-definitions/%d", c.Endpoint, definitionID), nil) + if err != nil { + return err + } + + body, status, err := c.doRequest(req) + if err != nil { + return err + } + + if status != http.StatusOK && status != http.StatusNotFound { + return fmt.Errorf("status: %d, body: %s", status, body) + } + + return nil +} From 324f5b788e16572efe465f4bc5faca04334ea373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Mon, 2 Oct 2023 14:16:32 +0200 Subject: [PATCH 26/64] Add support for Keycloak OIDC (#47) * Add support for KYPO Keycloak OIDC provider * Add support for refreshing token * Autodetect Keycloak OIDC instead of provider argument * Generate docs --------- Co-authored-by: Zdenek Vydra --- internal/KYPOClient/auth.go | 74 +++++++++++++++++++++++++++++++++++ internal/KYPOClient/client.go | 17 ++++---- internal/KYPOClient/util.go | 5 +++ 3 files changed, 88 insertions(+), 8 deletions(-) diff --git a/internal/KYPOClient/auth.go b/internal/KYPOClient/auth.go index 63655d4..05d0251 100644 --- a/internal/KYPOClient/auth.go +++ b/internal/KYPOClient/auth.go @@ -1,6 +1,7 @@ package KYPOClient import ( + "encoding/json" "errors" "fmt" "io/ioutil" @@ -9,6 +10,7 @@ import ( "net/url" "regexp" "strings" + "time" ) func (c *Client) signIn() (string, error) { @@ -166,3 +168,75 @@ func (c *Client) authorizeFirstTime(httpClient http.Client, csrf string) (string } return token, err } + +func (c *Client) authenticateKeycloak() error { + query := url.Values{} + query.Add("username", c.Username) + query.Add("password", c.Password) + query.Add("client_id", c.ClientID) + query.Add("grant_type", "password") + + req, err := http.NewRequest("POST", fmt.Sprintf("%s/keycloak/realms/KYPO/protocol/openid-connect/token", + c.Endpoint), strings.NewReader(query.Encode())) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + res, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + if res.StatusCode == http.StatusNotFound || res.StatusCode == http.StatusMethodNotAllowed { + return &ErrNotFound{ResourceName: "KYPO Keycloak endpoint"} + } + if res.StatusCode != http.StatusOK { + return fmt.Errorf("authenticateKeycloak failed, got HTTP code: %d", res.StatusCode) + } + + result := struct { + AccessToken string `json:"access_token"` + ExpiresIn int `json:"expires_in"` + }{} + + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return err + } + + err = json.Unmarshal(body, &result) + if err != nil { + return err + } + + c.Token = result.AccessToken + c.TokenExpiryTime = time.Now().Add(time.Duration(result.ExpiresIn) * time.Second) + + return nil +} + +func (c *Client) authenticate() error { + err := c.authenticateKeycloak() + var errNotFound *ErrNotFound + if errors.As(err, &errNotFound) { + var token string + token, err = c.signIn() + if err != nil { + return err + } + c.Token = token + return nil + } + + if err != nil { + return err + } + return nil +} + +func (c *Client) refreshToken() error { + if !c.TokenExpiryTime.IsZero() && time.Now().Add(10*time.Second).After(c.TokenExpiryTime) { + return c.authenticateKeycloak() + } + return nil +} diff --git a/internal/KYPOClient/client.go b/internal/KYPOClient/client.go index 7ad0cb8..7c5f965 100644 --- a/internal/KYPOClient/client.go +++ b/internal/KYPOClient/client.go @@ -2,15 +2,17 @@ package KYPOClient import ( "net/http" + "time" ) type Client struct { - Endpoint string - ClientID string - HTTPClient *http.Client - Token string - Username string - Password string + Endpoint string + ClientID string + HTTPClient *http.Client + Token string + TokenExpiryTime time.Time + Username string + Password string } func NewClientWithToken(endpoint, clientId, token string) (*Client, error) { @@ -32,10 +34,9 @@ func NewClient(endpoint, clientId, username, password string) (*Client, error) { Username: username, Password: password, } - token, err := client.signIn() + err := client.authenticate() if err != nil { return nil, err } - client.Token = token return &client, nil } diff --git a/internal/KYPOClient/util.go b/internal/KYPOClient/util.go index 215940c..1a50e51 100644 --- a/internal/KYPOClient/util.go +++ b/internal/KYPOClient/util.go @@ -15,6 +15,11 @@ type UserModel struct { } func (c *Client) doRequest(req *http.Request) ([]byte, int, error) { + err := c.refreshToken() + if err != nil { + return nil, 0, err + } + req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+c.Token) From 13a97751af0d49bd3f75d7471e96c15d519ac184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Wed, 15 Nov 2023 12:01:42 +0100 Subject: [PATCH 27/64] Extract kypo-go-client library --- internal/KYPOClient/auth.go => auth.go | 2 +- internal/KYPOClient/client.go => client.go | 2 +- cmd/main.go | 9 + internal/KYPOClient/error.go => error.go | 2 +- go.mod | 70 +--- go.sum | 361 +----------------- internal/KYPOClient/main/main.go | 9 - ...tion_unit.go => sandbox_allocation_unit.go | 2 +- ...box_definition.go => sandbox_definition.go | 2 +- .../sandbox_pool.go => sandbox_pool.go | 2 +- ...ng_definition.go => training_definition.go | 2 +- ...tive.go => training_definition_adaptive.go | 2 +- internal/KYPOClient/util.go => util.go | 2 +- 13 files changed, 22 insertions(+), 445 deletions(-) rename internal/KYPOClient/auth.go => auth.go (99%) rename internal/KYPOClient/client.go => client.go (97%) create mode 100644 cmd/main.go rename internal/KYPOClient/error.go => error.go (95%) delete mode 100644 internal/KYPOClient/main/main.go rename internal/KYPOClient/sandbox_allocation_unit.go => sandbox_allocation_unit.go (99%) rename internal/KYPOClient/sandbox_definition.go => sandbox_definition.go (99%) rename internal/KYPOClient/sandbox_pool.go => sandbox_pool.go (99%) rename internal/KYPOClient/training_definition.go => training_definition.go (98%) rename internal/KYPOClient/training_definition_adaptive.go => training_definition_adaptive.go (98%) rename internal/KYPOClient/util.go => util.go (97%) diff --git a/internal/KYPOClient/auth.go b/auth.go similarity index 99% rename from internal/KYPOClient/auth.go rename to auth.go index 05d0251..d0ff65b 100644 --- a/internal/KYPOClient/auth.go +++ b/auth.go @@ -1,4 +1,4 @@ -package KYPOClient +package kypo_go_client import ( "encoding/json" diff --git a/internal/KYPOClient/client.go b/client.go similarity index 97% rename from internal/KYPOClient/client.go rename to client.go index 7c5f965..b1419bd 100644 --- a/internal/KYPOClient/client.go +++ b/client.go @@ -1,4 +1,4 @@ -package KYPOClient +package kypo_go_client import ( "net/http" diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..096b925 --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,9 @@ +package main + +import "kypo-go-client" + +func main() { + client, _ := kypo_go_client.NewClient("https://images.crp.kypo.muni.cz", "bzhwmbxgyxALbAdMjYOgpolQzkiQHGwWRXxm", "kypo-admin", "UfMLMlEw0751kia002Kbv9MaLNlo3T") + _, _ = client.CreateSandboxDefinition("git@gitlab.ics.muni.cz:muni-kypo-trainings/games/junior-hacker.git", "master") + +} diff --git a/internal/KYPOClient/error.go b/error.go similarity index 95% rename from internal/KYPOClient/error.go rename to error.go index 763fa0f..e727e60 100644 --- a/internal/KYPOClient/error.go +++ b/error.go @@ -1,4 +1,4 @@ -package KYPOClient +package kypo_go_client import ( "fmt" diff --git a/go.mod b/go.mod index 1b4a0e9..b35fe9e 100644 --- a/go.mod +++ b/go.mod @@ -1,71 +1,5 @@ -module terraform-provider-kypo +module kypo-go-client go 1.18 -require ( - github.com/hashicorp/terraform-plugin-docs v0.13.0 - github.com/hashicorp/terraform-plugin-framework v1.1.1 - github.com/hashicorp/terraform-plugin-go v0.14.3 - github.com/hashicorp/terraform-plugin-log v0.8.0 - github.com/hashicorp/terraform-plugin-sdk/v2 v2.25.0 - golang.org/x/exp v0.0.0-20230321023759-10a507213a29 -) - -require ( - github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.1.1 // indirect - github.com/Masterminds/sprig/v3 v3.2.2 // indirect - github.com/agext/levenshtein v1.2.2 // indirect - github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect - github.com/armon/go-radix v1.0.0 // indirect - github.com/bgentry/speakeasy v0.1.0 // indirect - github.com/fatih/color v1.13.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-cmp v0.5.9 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-checkpoint v0.5.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect - github.com/hashicorp/go-hclog v1.4.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-plugin v1.4.8 // indirect - github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hashicorp/go-version v1.6.0 // indirect - github.com/hashicorp/hc-install v0.5.0 // indirect - github.com/hashicorp/hcl/v2 v2.16.1 // indirect - github.com/hashicorp/logutils v1.0.0 // indirect - github.com/hashicorp/terraform-exec v0.17.3 // indirect - github.com/hashicorp/terraform-json v0.15.0 // indirect - github.com/hashicorp/terraform-registry-address v0.1.0 // indirect - github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 // indirect - github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect - github.com/huandu/xstrings v1.3.2 // indirect - github.com/imdario/mergo v0.3.13 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - github.com/mitchellh/cli v1.1.5 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/go-testing-interface v1.14.1 // indirect - github.com/mitchellh/go-wordwrap v1.0.0 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/oklog/run v1.0.0 // indirect - github.com/posener/complete v1.2.3 // indirect - github.com/russross/blackfriday v1.6.0 // indirect - github.com/shopspring/decimal v1.3.1 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect - github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect - github.com/vmihailenco/tagparser v0.1.1 // indirect - github.com/zclconf/go-cty v1.12.1 // indirect - golang.org/x/crypto v0.6.0 // indirect - golang.org/x/mod v0.7.0 // indirect - golang.org/x/net v0.6.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect - google.golang.org/appengine v1.6.6 // indirect - google.golang.org/genproto v0.0.0-20200711021454-869866162049 // indirect - google.golang.org/grpc v1.51.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect -) +require golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa diff --git a/go.sum b/go.sum index 167b95a..1def10a 100644 --- a/go.sum +++ b/go.sum @@ -1,361 +1,4 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= -github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= -github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= -github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= -github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= -github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= -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/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= -github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= -github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= -github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= -github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -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/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= -github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= -github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= -github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= -github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= -github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= -github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= -github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= -github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I= -github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.4.8 h1:CHGwpxYDOttQOY7HOWgETU9dyVjOXzniXDqJcYJE1zM= -github.com/hashicorp/go-plugin v1.4.8/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= -github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/hc-install v0.5.0 h1:D9bl4KayIYKEeJ4vUDe9L5huqxZXczKaykSRcmQ0xY0= -github.com/hashicorp/hc-install v0.5.0/go.mod h1:JyzMfbzfSBSjoDCRPna1vi/24BEDxFaCPfdHtM5SCdo= -github.com/hashicorp/hcl/v2 v2.16.1 h1:BwuxEMD/tsYgbhIW7UuI3crjovf3MzuFWiVgiv57iHg= -github.com/hashicorp/hcl/v2 v2.16.1/go.mod h1:JRmR89jycNkrrqnMmvPDMd56n1rQJ2Q6KocSLCMCXng= -github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/terraform-exec v0.17.3 h1:MX14Kvnka/oWGmIkyuyvL6POx25ZmKrjlaclkx3eErU= -github.com/hashicorp/terraform-exec v0.17.3/go.mod h1:+NELG0EqQekJzhvikkeQsOAZpsw0cv/03rbeQJqscAI= -github.com/hashicorp/terraform-json v0.15.0 h1:/gIyNtR6SFw6h5yzlbDbACyGvIhKtQi8mTsbkNd79lE= -github.com/hashicorp/terraform-json v0.15.0/go.mod h1:+L1RNzjDU5leLFZkHTFTbJXaoqUC6TqXlFgDoOXrtvk= -github.com/hashicorp/terraform-plugin-docs v0.13.0 h1:6e+VIWsVGb6jYJewfzq2ok2smPzZrt1Wlm9koLeKazY= -github.com/hashicorp/terraform-plugin-docs v0.13.0/go.mod h1:W0oCmHAjIlTHBbvtppWHe8fLfZ2BznQbuv8+UD8OucQ= -github.com/hashicorp/terraform-plugin-framework v1.1.1 h1:PbnEKHsIU8KTTzoztHQGgjZUWx7Kk8uGtpGMMc1p+oI= -github.com/hashicorp/terraform-plugin-framework v1.1.1/go.mod h1:DyZPxQA+4OKK5ELxFIIcqggcszqdWWUpTLPHAhS/tkY= -github.com/hashicorp/terraform-plugin-go v0.14.3 h1:nlnJ1GXKdMwsC8g1Nh05tK2wsC3+3BL/DBBxFEki+j0= -github.com/hashicorp/terraform-plugin-go v0.14.3/go.mod h1:7ees7DMZ263q8wQ6E4RdIdR6nHHJtrdt4ogX5lPkX1A= -github.com/hashicorp/terraform-plugin-log v0.8.0 h1:pX2VQ/TGKu+UU1rCay0OlzosNKe4Nz1pepLXj95oyy0= -github.com/hashicorp/terraform-plugin-log v0.8.0/go.mod h1:1myFrhVsBLeylQzYYEV17VVjtG8oYPRFdaZs7xdW2xs= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.25.0 h1:iNRjaJCatQS1rIbHs/vDvJ0GECsaGgxx780chA2Irpk= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.25.0/go.mod h1:XnVNLIS6bdMJbjSDujhX4Rlk24QpbGKbnrVFM4tZ7OU= -github.com/hashicorp/terraform-registry-address v0.1.0 h1:W6JkV9wbum+m516rCl5/NjKxCyTVaaUBbzYcMzBDO3U= -github.com/hashicorp/terraform-registry-address v0.1.0/go.mod h1:EnyO2jYO6j29DTHbJcm00E5nQTFeTtyZH3H5ycydQ5A= -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-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= -github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= -github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -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/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= -github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= -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/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -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/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/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.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng= -github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= -github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -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-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= -github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= -github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= -github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= -github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= -github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= -github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= -github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= -github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U= -github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= -github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY= -github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= -github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= -github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -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= -github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= -github.com/zclconf/go-cty v1.12.1 h1:PcupnljUm9EIvbgSHQnHhUr3fO6oFmkOrvs2BAFNXXY= -github.com/zclconf/go-cty v1.12.1/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA= -github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= -golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -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= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/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-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -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= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200711021454-869866162049 h1:YFTFpQhgvrLrmxtiIncJxFXeCyq84ixuKWVCaCAi9Oc= -google.golang.org/genproto v0.0.0-20200711021454-869866162049/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= -google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= diff --git a/internal/KYPOClient/main/main.go b/internal/KYPOClient/main/main.go deleted file mode 100644 index c573d90..0000000 --- a/internal/KYPOClient/main/main.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import "terraform-provider-kypo/internal/KYPOClient" - -func main() { - client, _ := KYPOClient.NewClient("https://images.crp.kypo.muni.cz", "bzhwmbxgyxALbAdMjYOgpolQzkiQHGwWRXxm", "kypo-admin", "***") - _, _ = client.CreateSandboxDefinition("git@gitlab.ics.muni.cz:muni-kypo-trainings/games/junior-hacker.git", "master") - -} diff --git a/internal/KYPOClient/sandbox_allocation_unit.go b/sandbox_allocation_unit.go similarity index 99% rename from internal/KYPOClient/sandbox_allocation_unit.go rename to sandbox_allocation_unit.go index 141b56f..b08b64e 100644 --- a/internal/KYPOClient/sandbox_allocation_unit.go +++ b/sandbox_allocation_unit.go @@ -1,4 +1,4 @@ -package KYPOClient +package kypo_go_client import ( "encoding/json" diff --git a/internal/KYPOClient/sandbox_definition.go b/sandbox_definition.go similarity index 99% rename from internal/KYPOClient/sandbox_definition.go rename to sandbox_definition.go index 86aba1f..f993a5c 100644 --- a/internal/KYPOClient/sandbox_definition.go +++ b/sandbox_definition.go @@ -1,4 +1,4 @@ -package KYPOClient +package kypo_go_client import ( "encoding/json" diff --git a/internal/KYPOClient/sandbox_pool.go b/sandbox_pool.go similarity index 99% rename from internal/KYPOClient/sandbox_pool.go rename to sandbox_pool.go index dac000a..685a750 100644 --- a/internal/KYPOClient/sandbox_pool.go +++ b/sandbox_pool.go @@ -1,4 +1,4 @@ -package KYPOClient +package kypo_go_client import ( "encoding/json" diff --git a/internal/KYPOClient/training_definition.go b/training_definition.go similarity index 98% rename from internal/KYPOClient/training_definition.go rename to training_definition.go index 7205ffe..083612b 100644 --- a/internal/KYPOClient/training_definition.go +++ b/training_definition.go @@ -1,4 +1,4 @@ -package KYPOClient +package kypo_go_client import ( "encoding/json" diff --git a/internal/KYPOClient/training_definition_adaptive.go b/training_definition_adaptive.go similarity index 98% rename from internal/KYPOClient/training_definition_adaptive.go rename to training_definition_adaptive.go index d7f9b6f..5e0c647 100644 --- a/internal/KYPOClient/training_definition_adaptive.go +++ b/training_definition_adaptive.go @@ -1,4 +1,4 @@ -package KYPOClient +package kypo_go_client import ( "encoding/json" diff --git a/internal/KYPOClient/util.go b/util.go similarity index 97% rename from internal/KYPOClient/util.go rename to util.go index 1a50e51..93a1ca5 100644 --- a/internal/KYPOClient/util.go +++ b/util.go @@ -1,4 +1,4 @@ -package KYPOClient +package kypo_go_client import ( "io/ioutil" From 5dbd0199d0a81396e1108fdcc5c7c237539f8d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Wed, 15 Nov 2023 12:47:36 +0100 Subject: [PATCH 28/64] Rename package to include github path --- cmd/main.go | 2 +- go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 096b925..1481f4c 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,6 +1,6 @@ package main -import "kypo-go-client" +import "github.com/vydrazde/kypo-go-client" func main() { client, _ := kypo_go_client.NewClient("https://images.crp.kypo.muni.cz", "bzhwmbxgyxALbAdMjYOgpolQzkiQHGwWRXxm", "kypo-admin", "UfMLMlEw0751kia002Kbv9MaLNlo3T") diff --git a/go.mod b/go.mod index b35fe9e..4f79859 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module kypo-go-client +module github.com/vydrazde/kypo-go-client go 1.18 From d7eece1bde7af3c7f93b87788c47391e041f84b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Wed, 15 Nov 2023 14:15:03 +0100 Subject: [PATCH 29/64] Delete main package --- cmd/main.go | 9 --------- go.sum | 2 -- 2 files changed, 11 deletions(-) delete mode 100644 cmd/main.go diff --git a/cmd/main.go b/cmd/main.go deleted file mode 100644 index 1481f4c..0000000 --- a/cmd/main.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import "github.com/vydrazde/kypo-go-client" - -func main() { - client, _ := kypo_go_client.NewClient("https://images.crp.kypo.muni.cz", "bzhwmbxgyxALbAdMjYOgpolQzkiQHGwWRXxm", "kypo-admin", "UfMLMlEw0751kia002Kbv9MaLNlo3T") - _, _ = client.CreateSandboxDefinition("git@gitlab.ics.muni.cz:muni-kypo-trainings/games/junior-hacker.git", "master") - -} diff --git a/go.sum b/go.sum index 1def10a..0a4b649 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,2 @@ -golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= -golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= From 6c46b94e4993554ab569779f22708f7d37871d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Thu, 16 Nov 2023 10:42:32 +0100 Subject: [PATCH 30/64] Rename package to kypo --- auth.go | 2 +- client.go | 2 +- error.go | 2 +- sandbox_allocation_unit.go | 2 +- sandbox_definition.go | 2 +- sandbox_pool.go | 2 +- training_definition.go | 2 +- training_definition_adaptive.go | 2 +- util.go | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/auth.go b/auth.go index d0ff65b..5050496 100644 --- a/auth.go +++ b/auth.go @@ -1,4 +1,4 @@ -package kypo_go_client +package kypo import ( "encoding/json" diff --git a/client.go b/client.go index b1419bd..7d83f63 100644 --- a/client.go +++ b/client.go @@ -1,4 +1,4 @@ -package kypo_go_client +package kypo import ( "net/http" diff --git a/error.go b/error.go index e727e60..01ecd76 100644 --- a/error.go +++ b/error.go @@ -1,4 +1,4 @@ -package kypo_go_client +package kypo import ( "fmt" diff --git a/sandbox_allocation_unit.go b/sandbox_allocation_unit.go index b08b64e..dfb90c2 100644 --- a/sandbox_allocation_unit.go +++ b/sandbox_allocation_unit.go @@ -1,4 +1,4 @@ -package kypo_go_client +package kypo import ( "encoding/json" diff --git a/sandbox_definition.go b/sandbox_definition.go index f993a5c..21e9f7d 100644 --- a/sandbox_definition.go +++ b/sandbox_definition.go @@ -1,4 +1,4 @@ -package kypo_go_client +package kypo import ( "encoding/json" diff --git a/sandbox_pool.go b/sandbox_pool.go index 685a750..d713c9e 100644 --- a/sandbox_pool.go +++ b/sandbox_pool.go @@ -1,4 +1,4 @@ -package kypo_go_client +package kypo import ( "encoding/json" diff --git a/training_definition.go b/training_definition.go index 083612b..eb3254d 100644 --- a/training_definition.go +++ b/training_definition.go @@ -1,4 +1,4 @@ -package kypo_go_client +package kypo import ( "encoding/json" diff --git a/training_definition_adaptive.go b/training_definition_adaptive.go index 5e0c647..2094117 100644 --- a/training_definition_adaptive.go +++ b/training_definition_adaptive.go @@ -1,4 +1,4 @@ -package kypo_go_client +package kypo import ( "encoding/json" diff --git a/util.go b/util.go index 93a1ca5..ff7eade 100644 --- a/util.go +++ b/util.go @@ -1,4 +1,4 @@ -package kypo_go_client +package kypo import ( "io/ioutil" From 0de4237b08b6696f926999e3af50d69b872ccc76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Thu, 16 Nov 2023 12:27:15 +0100 Subject: [PATCH 31/64] Remove pkg from gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 54bd8df..b954168 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,6 @@ terraform.tfstate bin/ dist/ modules-dev/ -/pkg/ website/.vagrant website/.bundle website/build From 84c7762c4c9634b3967f82d8e9ccf40c8a00e9c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Thu, 16 Nov 2023 10:51:14 +0100 Subject: [PATCH 32/64] Move package to pkg directory --- auth.go => pkg/kypo/auth.go | 0 client.go => pkg/kypo/client.go | 0 error.go => pkg/kypo/error.go | 0 sandbox_allocation_unit.go => pkg/kypo/sandbox_allocation_unit.go | 0 sandbox_definition.go => pkg/kypo/sandbox_definition.go | 0 sandbox_pool.go => pkg/kypo/sandbox_pool.go | 0 training_definition.go => pkg/kypo/training_definition.go | 0 .../kypo/training_definition_adaptive.go | 0 util.go => pkg/kypo/util.go | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename auth.go => pkg/kypo/auth.go (100%) rename client.go => pkg/kypo/client.go (100%) rename error.go => pkg/kypo/error.go (100%) rename sandbox_allocation_unit.go => pkg/kypo/sandbox_allocation_unit.go (100%) rename sandbox_definition.go => pkg/kypo/sandbox_definition.go (100%) rename sandbox_pool.go => pkg/kypo/sandbox_pool.go (100%) rename training_definition.go => pkg/kypo/training_definition.go (100%) rename training_definition_adaptive.go => pkg/kypo/training_definition_adaptive.go (100%) rename util.go => pkg/kypo/util.go (100%) diff --git a/auth.go b/pkg/kypo/auth.go similarity index 100% rename from auth.go rename to pkg/kypo/auth.go diff --git a/client.go b/pkg/kypo/client.go similarity index 100% rename from client.go rename to pkg/kypo/client.go diff --git a/error.go b/pkg/kypo/error.go similarity index 100% rename from error.go rename to pkg/kypo/error.go diff --git a/sandbox_allocation_unit.go b/pkg/kypo/sandbox_allocation_unit.go similarity index 100% rename from sandbox_allocation_unit.go rename to pkg/kypo/sandbox_allocation_unit.go diff --git a/sandbox_definition.go b/pkg/kypo/sandbox_definition.go similarity index 100% rename from sandbox_definition.go rename to pkg/kypo/sandbox_definition.go diff --git a/sandbox_pool.go b/pkg/kypo/sandbox_pool.go similarity index 100% rename from sandbox_pool.go rename to pkg/kypo/sandbox_pool.go diff --git a/training_definition.go b/pkg/kypo/training_definition.go similarity index 100% rename from training_definition.go rename to pkg/kypo/training_definition.go diff --git a/training_definition_adaptive.go b/pkg/kypo/training_definition_adaptive.go similarity index 100% rename from training_definition_adaptive.go rename to pkg/kypo/training_definition_adaptive.go diff --git a/util.go b/pkg/kypo/util.go similarity index 100% rename from util.go rename to pkg/kypo/util.go From 38cb5679f3b9e0f4f3f89cefa4e267a42d21ead4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Sat, 18 Nov 2023 11:26:56 +0100 Subject: [PATCH 33/64] Add context structure to sandbox_definition, doRequest and refreshToken --- pkg/kypo/auth.go | 11 ++++++----- pkg/kypo/sandbox_definition.go | 13 +++++++------ pkg/kypo/util.go | 2 +- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/pkg/kypo/auth.go b/pkg/kypo/auth.go index 5050496..9c7d02a 100644 --- a/pkg/kypo/auth.go +++ b/pkg/kypo/auth.go @@ -1,6 +1,7 @@ package kypo import ( + "context" "encoding/json" "errors" "fmt" @@ -169,14 +170,14 @@ func (c *Client) authorizeFirstTime(httpClient http.Client, csrf string) (string return token, err } -func (c *Client) authenticateKeycloak() error { +func (c *Client) authenticateKeycloak(ctx context.Context) error { query := url.Values{} query.Add("username", c.Username) query.Add("password", c.Password) query.Add("client_id", c.ClientID) query.Add("grant_type", "password") - req, err := http.NewRequest("POST", fmt.Sprintf("%s/keycloak/realms/KYPO/protocol/openid-connect/token", + req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/keycloak/realms/KYPO/protocol/openid-connect/token", c.Endpoint), strings.NewReader(query.Encode())) if err != nil { return err @@ -216,7 +217,7 @@ func (c *Client) authenticateKeycloak() error { } func (c *Client) authenticate() error { - err := c.authenticateKeycloak() + err := c.authenticateKeycloak(context.Background()) var errNotFound *ErrNotFound if errors.As(err, &errNotFound) { var token string @@ -234,9 +235,9 @@ func (c *Client) authenticate() error { return nil } -func (c *Client) refreshToken() error { +func (c *Client) refreshToken(ctx context.Context) error { if !c.TokenExpiryTime.IsZero() && time.Now().Add(10*time.Second).After(c.TokenExpiryTime) { - return c.authenticateKeycloak() + return c.authenticateKeycloak(ctx) } return nil } diff --git a/pkg/kypo/sandbox_definition.go b/pkg/kypo/sandbox_definition.go index 21e9f7d..44abe2c 100644 --- a/pkg/kypo/sandbox_definition.go +++ b/pkg/kypo/sandbox_definition.go @@ -1,6 +1,7 @@ package kypo import ( + "context" "encoding/json" "fmt" "net/http" @@ -21,8 +22,8 @@ type SandboxDefinitionRequest struct { Rev string `json:"rev"` } -func (c *Client) GetSandboxDefinition(definitionID int64) (*SandboxDefinition, error) { - req, err := http.NewRequest("GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions/%d", c.Endpoint, definitionID), nil) +func (c *Client) GetSandboxDefinition(ctx context.Context, definitionID int64) (*SandboxDefinition, error) { + req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions/%d", c.Endpoint, definitionID), nil) if err != nil { return nil, err } @@ -50,13 +51,13 @@ func (c *Client) GetSandboxDefinition(definitionID int64) (*SandboxDefinition, e return &definition, nil } -func (c *Client) CreateSandboxDefinition(url, rev string) (*SandboxDefinition, error) { +func (c *Client) CreateSandboxDefinition(ctx context.Context, url, rev string) (*SandboxDefinition, error) { requestBody, err := json.Marshal(SandboxDefinitionRequest{url, rev}) if err != nil { return nil, err } - req, err := http.NewRequest("POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions", c.Endpoint), strings.NewReader(string(requestBody))) + req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions", c.Endpoint), strings.NewReader(string(requestBody))) if err != nil { return nil, err } @@ -79,8 +80,8 @@ func (c *Client) CreateSandboxDefinition(url, rev string) (*SandboxDefinition, e return &definition, nil } -func (c *Client) DeleteSandboxDefinition(definitionID int64) error { - req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions/%d", c.Endpoint, definitionID), nil) +func (c *Client) DeleteSandboxDefinition(ctx context.Context, definitionID int64) error { + req, err := http.NewRequestWithContext(ctx, "DELETE", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions/%d", c.Endpoint, definitionID), nil) if err != nil { return err } diff --git a/pkg/kypo/util.go b/pkg/kypo/util.go index ff7eade..baa334c 100644 --- a/pkg/kypo/util.go +++ b/pkg/kypo/util.go @@ -15,7 +15,7 @@ type UserModel struct { } func (c *Client) doRequest(req *http.Request) ([]byte, int, error) { - err := c.refreshToken() + err := c.refreshToken(req.Context()) if err != nil { return nil, 0, err } From f2ec2086827489979d388ea7a49a495913c3c9d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Sat, 18 Nov 2023 11:31:05 +0100 Subject: [PATCH 34/64] Add context structure to sandbox_pool and training_definitions --- pkg/kypo/sandbox_pool.go | 17 +++++++++-------- pkg/kypo/training_definition.go | 13 +++++++------ pkg/kypo/training_definition_adaptive.go | 13 +++++++------ 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/pkg/kypo/sandbox_pool.go b/pkg/kypo/sandbox_pool.go index d713c9e..4d1058a 100644 --- a/pkg/kypo/sandbox_pool.go +++ b/pkg/kypo/sandbox_pool.go @@ -1,6 +1,7 @@ package kypo import ( + "context" "encoding/json" "fmt" "net/http" @@ -34,8 +35,8 @@ type HardwareUsage struct { Port string `json:"port" tfsdk:"port"` } -func (c *Client) GetSandboxPool(poolId int64) (*SandboxPool, error) { - req, err := http.NewRequest("GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d", c.Endpoint, poolId), nil) +func (c *Client) GetSandboxPool(ctx context.Context, poolId int64) (*SandboxPool, error) { + req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d", c.Endpoint, poolId), nil) if err != nil { return nil, err } @@ -63,13 +64,13 @@ func (c *Client) GetSandboxPool(poolId int64) (*SandboxPool, error) { return &pool, nil } -func (c *Client) CreateSandboxPool(definitionId, maxSize int64) (*SandboxPool, error) { +func (c *Client) CreateSandboxPool(ctx context.Context, definitionId, maxSize int64) (*SandboxPool, error) { requestBody, err := json.Marshal(SandboxPoolRequest{definitionId, maxSize}) if err != nil { return nil, err } - req, err := http.NewRequest("POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools", c.Endpoint), strings.NewReader(string(requestBody))) + req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools", c.Endpoint), strings.NewReader(string(requestBody))) if err != nil { return nil, err } @@ -92,8 +93,8 @@ func (c *Client) CreateSandboxPool(definitionId, maxSize int64) (*SandboxPool, e return &pool, nil } -func (c *Client) DeleteSandboxPool(poolId int64) error { - req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d", c.Endpoint, poolId), nil) +func (c *Client) DeleteSandboxPool(ctx context.Context, poolId int64) error { + req, err := http.NewRequestWithContext(ctx, "DELETE", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d", c.Endpoint, poolId), nil) if err != nil { return err } @@ -110,8 +111,8 @@ func (c *Client) DeleteSandboxPool(poolId int64) error { return nil } -func (c *Client) CleanupSandboxPool(poolId int64, force bool) error { - req, err := http.NewRequest("POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d/cleanup-requests?force=%s", +func (c *Client) CleanupSandboxPool(ctx context.Context, poolId int64, force bool) error { + req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d/cleanup-requests?force=%s", c.Endpoint, poolId, boolToString(force)), nil) if err != nil { return err diff --git a/pkg/kypo/training_definition.go b/pkg/kypo/training_definition.go index eb3254d..e964109 100644 --- a/pkg/kypo/training_definition.go +++ b/pkg/kypo/training_definition.go @@ -1,6 +1,7 @@ package kypo import ( + "context" "encoding/json" "fmt" "net/http" @@ -13,8 +14,8 @@ type TrainingDefinition struct { Content string `json:"content" tfsdk:"content"` } -func (c *Client) GetTrainingDefinition(definitionID int64) (*TrainingDefinition, error) { - req, err := http.NewRequest("GET", fmt.Sprintf("%s/kypo-rest-training/api/v1/exports/training-definitions/%d", c.Endpoint, definitionID), nil) +func (c *Client) GetTrainingDefinition(ctx context.Context, definitionID int64) (*TrainingDefinition, error) { + req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/kypo-rest-training/api/v1/exports/training-definitions/%d", c.Endpoint, definitionID), nil) if err != nil { return nil, err } @@ -41,8 +42,8 @@ func (c *Client) GetTrainingDefinition(definitionID int64) (*TrainingDefinition, return &definition, nil } -func (c *Client) CreateTrainingDefinition(content string) (*TrainingDefinition, error) { - req, err := http.NewRequest("POST", fmt.Sprintf("%s/kypo-rest-training/api/v1/imports/training-definitions", c.Endpoint), strings.NewReader(content)) +func (c *Client) CreateTrainingDefinition(ctx context.Context, content string) (*TrainingDefinition, error) { + req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/kypo-rest-training/api/v1/imports/training-definitions", c.Endpoint), strings.NewReader(content)) if err != nil { return nil, err } @@ -73,8 +74,8 @@ func (c *Client) CreateTrainingDefinition(content string) (*TrainingDefinition, return &definition, nil } -func (c *Client) DeleteTrainingDefinition(definitionID int64) error { - req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/kypo-rest-training/api/v1/training-definitions/%d", c.Endpoint, definitionID), nil) +func (c *Client) DeleteTrainingDefinition(ctx context.Context, definitionID int64) error { + req, err := http.NewRequestWithContext(ctx, "DELETE", fmt.Sprintf("%s/kypo-rest-training/api/v1/training-definitions/%d", c.Endpoint, definitionID), nil) if err != nil { return err } diff --git a/pkg/kypo/training_definition_adaptive.go b/pkg/kypo/training_definition_adaptive.go index 2094117..4f45cea 100644 --- a/pkg/kypo/training_definition_adaptive.go +++ b/pkg/kypo/training_definition_adaptive.go @@ -1,6 +1,7 @@ package kypo import ( + "context" "encoding/json" "fmt" "net/http" @@ -13,8 +14,8 @@ type TrainingDefinitionAdaptive struct { Content string `json:"content" tfsdk:"content"` } -func (c *Client) GetTrainingDefinitionAdaptive(definitionID int64) (*TrainingDefinitionAdaptive, error) { - req, err := http.NewRequest("GET", fmt.Sprintf("%s/kypo-adaptive-training/api/v1/exports/training-definitions/%d", c.Endpoint, definitionID), nil) +func (c *Client) GetTrainingDefinitionAdaptive(ctx context.Context, definitionID int64) (*TrainingDefinitionAdaptive, error) { + req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/kypo-adaptive-training/api/v1/exports/training-definitions/%d", c.Endpoint, definitionID), nil) if err != nil { return nil, err } @@ -41,8 +42,8 @@ func (c *Client) GetTrainingDefinitionAdaptive(definitionID int64) (*TrainingDef return &definition, nil } -func (c *Client) CreateTrainingDefinitionAdaptive(content string) (*TrainingDefinitionAdaptive, error) { - req, err := http.NewRequest("POST", fmt.Sprintf("%s/kypo-adaptive-training/api/v1/imports/training-definitions", c.Endpoint), strings.NewReader(content)) +func (c *Client) CreateTrainingDefinitionAdaptive(ctx context.Context, content string) (*TrainingDefinitionAdaptive, error) { + req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/kypo-adaptive-training/api/v1/imports/training-definitions", c.Endpoint), strings.NewReader(content)) if err != nil { return nil, err } @@ -73,8 +74,8 @@ func (c *Client) CreateTrainingDefinitionAdaptive(content string) (*TrainingDefi return &definition, nil } -func (c *Client) DeleteTrainingDefinitionAdaptive(definitionID int64) error { - req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/kypo-adaptive-training/api/v1/training-definitions/%d", c.Endpoint, definitionID), nil) +func (c *Client) DeleteTrainingDefinitionAdaptive(ctx context.Context, definitionID int64) error { + req, err := http.NewRequestWithContext(ctx, "DELETE", fmt.Sprintf("%s/kypo-adaptive-training/api/v1/training-definitions/%d", c.Endpoint, definitionID), nil) if err != nil { return err } From 9b9fe698c20dc67b2e80532d566d49ef739cc411 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Sat, 18 Nov 2023 15:39:15 +0100 Subject: [PATCH 35/64] Add context structure to sandbox_allocation_unit --- pkg/kypo/sandbox_allocation_unit.go | 127 ++++++++++++---------------- 1 file changed, 53 insertions(+), 74 deletions(-) diff --git a/pkg/kypo/sandbox_allocation_unit.go b/pkg/kypo/sandbox_allocation_unit.go index dfb90c2..13ef6b4 100644 --- a/pkg/kypo/sandbox_allocation_unit.go +++ b/pkg/kypo/sandbox_allocation_unit.go @@ -1,6 +1,7 @@ package kypo import ( + "context" "encoding/json" "fmt" "net/http" @@ -49,8 +50,8 @@ type outputLine struct { Content string `json:"content"` } -func (c *Client) GetSandboxAllocationUnit(unitId int64) (*SandboxAllocationUnit, error) { - req, err := http.NewRequest("GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d", c.Endpoint, unitId), nil) +func (c *Client) GetSandboxAllocationUnit(ctx context.Context, unitId int64) (*SandboxAllocationUnit, error) { + req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d", c.Endpoint, unitId), nil) if err != nil { return nil, err } @@ -78,8 +79,8 @@ func (c *Client) GetSandboxAllocationUnit(unitId int64) (*SandboxAllocationUnit, return &allocationUnit, nil } -func (c *Client) CreateSandboxAllocationUnits(poolId, count int64) ([]SandboxAllocationUnit, error) { - req, err := http.NewRequest("POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d/sandbox-allocation-units?count=%d", c.Endpoint, poolId, count), nil) +func (c *Client) CreateSandboxAllocationUnits(ctx context.Context, poolId, count int64) ([]SandboxAllocationUnit, error) { + req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d/sandbox-allocation-units?count=%d", c.Endpoint, poolId, count), nil) if err != nil { return nil, err } @@ -102,8 +103,8 @@ func (c *Client) CreateSandboxAllocationUnits(poolId, count int64) ([]SandboxAll return allocationUnit, nil } -func (c *Client) CreateSandboxAllocationUnitAwait(poolId int64) (*SandboxAllocationUnit, error) { - units, err := c.CreateSandboxAllocationUnits(poolId, 1) +func (c *Client) CreateSandboxAllocationUnitAwait(ctx context.Context, poolId int64) (*SandboxAllocationUnit, error) { + units, err := c.CreateSandboxAllocationUnits(ctx, poolId, 1) if err != nil { return nil, err } @@ -111,28 +112,16 @@ func (c *Client) CreateSandboxAllocationUnitAwait(poolId int64) (*SandboxAllocat return nil, fmt.Errorf("expected one allocation unit to be created, got %d instead", len(units)) } unit := units[0] - request, err := c.PollRequestFinished(unit.Id, 5*time.Second, "allocation") + request, err := c.PollRequestFinished(ctx, unit.Id, 5*time.Second, "allocation") + if err != nil { + return nil, err + } unit.AllocationRequest = *request return &unit, err } -func (c *Client) CreateSandboxAllocationUnitAwaitTimeout(poolId int64, timeout time.Duration) (*SandboxAllocationUnit, error) { - resultChannel := make(chan valueOrError[*SandboxAllocationUnit], 1) - go func() { - res, err := c.CreateSandboxAllocationUnitAwait(poolId) - resultChannel <- valueOrError[*SandboxAllocationUnit]{err: err, value: res} - }() - - select { - case result := <-resultChannel: - return result.value, result.err - case <-time.After(timeout): - return nil, &ErrTimeout{Action: "creating sandbox allocation unit", Identifier: strconv.FormatInt(poolId, 10), Timeout: timeout} - } -} - -func (c *Client) CreateSandboxCleanupRequest(unitId int64) (*SandboxRequest, error) { - req, err := http.NewRequest("POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d/cleanup-request", c.Endpoint, unitId), nil) +func (c *Client) CreateSandboxCleanupRequest(ctx context.Context, unitId int64) (*SandboxRequest, error) { + req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d/cleanup-request", c.Endpoint, unitId), nil) if err != nil { return nil, err } @@ -159,47 +148,51 @@ func (c *Client) CreateSandboxCleanupRequest(unitId int64) (*SandboxRequest, err return &sandboxRequest, nil } -func (c *Client) PollRequestFinished(unitId int64, pollTime time.Duration, requestType string) (*SandboxRequest, error) { +func (c *Client) PollRequestFinished(ctx context.Context, unitId int64, pollTime time.Duration, requestType string) (*SandboxRequest, error) { ticker := time.NewTicker(pollTime) - for range ticker.C { - req, err := http.NewRequest("GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d/%s-request", c.Endpoint, unitId, requestType), nil) - if err != nil { - return nil, err - } - - body, status, err := c.doRequest(req) - if err != nil { - return nil, err - } - - if status == http.StatusNotFound { - return nil, &ErrNotFound{ResourceName: "sandbox request", Identifier: strconv.FormatInt(unitId, 10)} - - } - - if status != http.StatusOK { - return nil, fmt.Errorf("status: %d, body: %s", status, body) - } - sandboxRequest := SandboxRequest{} - err = json.Unmarshal(body, &sandboxRequest) - if err != nil { - return nil, err - } - - if !slices.Contains(sandboxRequest.Stages, "RUNNING") && !slices.Contains(sandboxRequest.Stages, "IN_QUEUE") { - return &sandboxRequest, nil + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-ticker.C: + req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d/%s-request", c.Endpoint, unitId, requestType), nil) + if err != nil { + return nil, err + } + + body, status, err := c.doRequest(req) + if err != nil { + return nil, err + } + + if status == http.StatusNotFound { + return nil, &ErrNotFound{ResourceName: "sandbox request", Identifier: strconv.FormatInt(unitId, 10)} + } + + if status != http.StatusOK { + return nil, fmt.Errorf("status: %d, body: %s", status, body) + } + sandboxRequest := SandboxRequest{} + err = json.Unmarshal(body, &sandboxRequest) + if err != nil { + return nil, err + } + + if !slices.Contains(sandboxRequest.Stages, "RUNNING") && !slices.Contains(sandboxRequest.Stages, "IN_QUEUE") { + return &sandboxRequest, nil + } } } - return nil, nil // Unreachable } -func (c *Client) CreateSandboxCleanupRequestAwait(unitId int64) error { - _, err := c.CreateSandboxCleanupRequest(unitId) +func (c *Client) CreateSandboxCleanupRequestAwait(ctx context.Context, unitId int64) error { + _, err := c.CreateSandboxCleanupRequest(ctx, unitId) if err != nil { return err } - cleanupRequest, err := c.PollRequestFinished(unitId, 3*time.Second, "cleanup") + cleanupRequest, err := c.PollRequestFinished(ctx, unitId, 3*time.Second, "cleanup") // After cleanup is finished it deletes itself and 404 is thrown if _, ok := err.(*ErrNotFound); ok { return nil @@ -210,22 +203,8 @@ func (c *Client) CreateSandboxCleanupRequestAwait(unitId int64) error { return err } -func (c *Client) CreateSandboxCleanupRequestAwaitTimeout(unitId int64, timeout time.Duration) error { - resultChannel := make(chan error, 1) - go func() { - resultChannel <- c.CreateSandboxCleanupRequestAwait(unitId) - }() - - select { - case result := <-resultChannel: - return result - case <-time.After(timeout): - return &ErrTimeout{Action: "deleting sandbox allocation unit", Identifier: strconv.FormatInt(unitId, 10), Timeout: timeout} - } -} - -func (c *Client) CancelSandboxAllocationRequest(allocationRequestId int64) error { - req, err := http.NewRequest("PATCH", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/allocation-requests/%d/cancel", c.Endpoint, allocationRequestId), nil) +func (c *Client) CancelSandboxAllocationRequest(ctx context.Context, allocationRequestId int64) error { + req, err := http.NewRequestWithContext(ctx, "PATCH", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/allocation-requests/%d/cancel", c.Endpoint, allocationRequestId), nil) if err != nil { return err } @@ -246,12 +225,12 @@ func (c *Client) CancelSandboxAllocationRequest(allocationRequestId int64) error return nil } -func (c *Client) GetSandboxRequestAnsibleOutputs(sandboxRequestId, page, pageSize int64, outputType string) (*SandboxRequestStageOutput, error) { +func (c *Client) GetSandboxRequestAnsibleOutputs(ctx context.Context, sandboxRequestId, page, pageSize int64, outputType string) (*SandboxRequestStageOutput, error) { query := url.Values{} query.Add("page", strconv.FormatInt(page, 10)) query.Add("page_size", strconv.FormatInt(pageSize, 10)) - req, err := http.NewRequest("GET", fmt.Sprintf( + req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf( "%s/kypo-sandbox-service/api/v1/allocation-requests/%d/stages/%s/outputs?%s", c.Endpoint, sandboxRequestId, outputType, query.Encode()), nil) if err != nil { return nil, err From 1870d6af3cd816a218df40dcdd04e2e11dbc2b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Sat, 18 Nov 2023 16:31:47 +0100 Subject: [PATCH 36/64] Add pollTime to sandbox allocation and cleanup await variants --- pkg/kypo/sandbox_allocation_unit.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/kypo/sandbox_allocation_unit.go b/pkg/kypo/sandbox_allocation_unit.go index 13ef6b4..ff5969d 100644 --- a/pkg/kypo/sandbox_allocation_unit.go +++ b/pkg/kypo/sandbox_allocation_unit.go @@ -103,7 +103,7 @@ func (c *Client) CreateSandboxAllocationUnits(ctx context.Context, poolId, count return allocationUnit, nil } -func (c *Client) CreateSandboxAllocationUnitAwait(ctx context.Context, poolId int64) (*SandboxAllocationUnit, error) { +func (c *Client) CreateSandboxAllocationUnitAwait(ctx context.Context, poolId int64, pollTime time.Duration) (*SandboxAllocationUnit, error) { units, err := c.CreateSandboxAllocationUnits(ctx, poolId, 1) if err != nil { return nil, err @@ -112,7 +112,7 @@ func (c *Client) CreateSandboxAllocationUnitAwait(ctx context.Context, poolId in return nil, fmt.Errorf("expected one allocation unit to be created, got %d instead", len(units)) } unit := units[0] - request, err := c.PollRequestFinished(ctx, unit.Id, 5*time.Second, "allocation") + request, err := c.PollRequestFinished(ctx, unit.Id, pollTime, "allocation") if err != nil { return nil, err } @@ -186,13 +186,13 @@ func (c *Client) PollRequestFinished(ctx context.Context, unitId int64, pollTime } } -func (c *Client) CreateSandboxCleanupRequestAwait(ctx context.Context, unitId int64) error { +func (c *Client) CreateSandboxCleanupRequestAwait(ctx context.Context, unitId int64, pollTime time.Duration) error { _, err := c.CreateSandboxCleanupRequest(ctx, unitId) if err != nil { return err } - cleanupRequest, err := c.PollRequestFinished(ctx, unitId, 3*time.Second, "cleanup") + cleanupRequest, err := c.PollRequestFinished(ctx, unitId, pollTime, "cleanup") // After cleanup is finished it deletes itself and 404 is thrown if _, ok := err.(*ErrNotFound); ok { return nil From 4bf8df1c056caa5ed9a8400e97f134cb972677a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Sun, 19 Nov 2023 21:25:18 +0100 Subject: [PATCH 37/64] Add documentation for client and sandbox functions --- pkg/kypo/client.go | 6 ++++++ pkg/kypo/sandbox_allocation_unit.go | 12 ++++++++++++ pkg/kypo/sandbox_definition.go | 5 +++++ pkg/kypo/sandbox_pool.go | 4 ++++ 4 files changed, 27 insertions(+) diff --git a/pkg/kypo/client.go b/pkg/kypo/client.go index 7d83f63..fc7e22f 100644 --- a/pkg/kypo/client.go +++ b/pkg/kypo/client.go @@ -5,6 +5,8 @@ import ( "time" ) +// Client struct stores information for authentication to the KYPO API. +// All functions are methods of this struct type Client struct { Endpoint string ClientID string @@ -15,6 +17,7 @@ type Client struct { Password string } +// NewClientWithToken creates and returns a Client which uses an already created Bearer token. func NewClientWithToken(endpoint, clientId, token string) (*Client, error) { client := Client{ Endpoint: endpoint, @@ -26,6 +29,9 @@ func NewClientWithToken(endpoint, clientId, token string) (*Client, error) { return &client, nil } +// NewClient creates and returns a Client which uses username and password for authentication. +// The username and password is used to login to Keycloak of the KYPO instance. If the login fails, +// login to the legacy CSIRT-MU dummy OIDC issuer is attempted. func NewClient(endpoint, clientId, username, password string) (*Client, error) { client := Client{ Endpoint: endpoint, diff --git a/pkg/kypo/sandbox_allocation_unit.go b/pkg/kypo/sandbox_allocation_unit.go index ff5969d..eb14db2 100644 --- a/pkg/kypo/sandbox_allocation_unit.go +++ b/pkg/kypo/sandbox_allocation_unit.go @@ -50,6 +50,7 @@ type outputLine struct { Content string `json:"content"` } +// GetSandboxAllocationUnit reads a sandbox allocation unit based on its id. func (c *Client) GetSandboxAllocationUnit(ctx context.Context, unitId int64) (*SandboxAllocationUnit, error) { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d", c.Endpoint, unitId), nil) if err != nil { @@ -79,6 +80,7 @@ func (c *Client) GetSandboxAllocationUnit(ctx context.Context, unitId int64) (*S return &allocationUnit, nil } +// CreateSandboxAllocationUnits starts the allocation of `count` sandboxes in the sandbox pool specified by `poolId`. func (c *Client) CreateSandboxAllocationUnits(ctx context.Context, poolId, count int64) ([]SandboxAllocationUnit, error) { req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d/sandbox-allocation-units?count=%d", c.Endpoint, poolId, count), nil) if err != nil { @@ -103,6 +105,8 @@ func (c *Client) CreateSandboxAllocationUnits(ctx context.Context, poolId, count return allocationUnit, nil } +// CreateSandboxAllocationUnitAwait creates a single sandbox allocation unit and waits until its allocation finishes. +// Once the allocation is started, the status is checked once every `pollTime` elapses. func (c *Client) CreateSandboxAllocationUnitAwait(ctx context.Context, poolId int64, pollTime time.Duration) (*SandboxAllocationUnit, error) { units, err := c.CreateSandboxAllocationUnits(ctx, poolId, 1) if err != nil { @@ -120,6 +124,7 @@ func (c *Client) CreateSandboxAllocationUnitAwait(ctx context.Context, poolId in return &unit, err } +// CreateSandboxCleanupRequest starts a cleanup request for the specified sandbox allocation unit. func (c *Client) CreateSandboxCleanupRequest(ctx context.Context, unitId int64) (*SandboxRequest, error) { req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d/cleanup-request", c.Endpoint, unitId), nil) if err != nil { @@ -148,6 +153,8 @@ func (c *Client) CreateSandboxCleanupRequest(ctx context.Context, unitId int64) return &sandboxRequest, nil } +// PollRequestFinished periodically checks whether the specified request on given allocation unit has finished. +// The `requestType` should be one of `allocation` or `cleanup`. The check is done once every `pollTime` elapses. func (c *Client) PollRequestFinished(ctx context.Context, unitId int64, pollTime time.Duration, requestType string) (*SandboxRequest, error) { ticker := time.NewTicker(pollTime) defer ticker.Stop() @@ -186,6 +193,8 @@ func (c *Client) PollRequestFinished(ctx context.Context, unitId int64, pollTime } } +// CreateSandboxCleanupRequestAwait starts the cleanup request for the given sandbox allocation unit and waits until it finishes. +// Once the cleanup is started, the status is checked once every `pollTime` elapses. func (c *Client) CreateSandboxCleanupRequestAwait(ctx context.Context, unitId int64, pollTime time.Duration) error { _, err := c.CreateSandboxCleanupRequest(ctx, unitId) if err != nil { @@ -203,6 +212,7 @@ func (c *Client) CreateSandboxCleanupRequestAwait(ctx context.Context, unitId in return err } +// CancelSandboxAllocationRequest sends a request to cancel the given allocation request. func (c *Client) CancelSandboxAllocationRequest(ctx context.Context, allocationRequestId int64) error { req, err := http.NewRequestWithContext(ctx, "PATCH", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/allocation-requests/%d/cancel", c.Endpoint, allocationRequestId), nil) if err != nil { @@ -225,6 +235,8 @@ func (c *Client) CancelSandboxAllocationRequest(ctx context.Context, allocationR return nil } +// GetSandboxRequestAnsibleOutputs reads the output of given allocation request stage. +// The `outputType` should be one of `user-ansible`, `networking-ansible` or `terraform`. func (c *Client) GetSandboxRequestAnsibleOutputs(ctx context.Context, sandboxRequestId, page, pageSize int64, outputType string) (*SandboxRequestStageOutput, error) { query := url.Values{} query.Add("page", strconv.FormatInt(page, 10)) diff --git a/pkg/kypo/sandbox_definition.go b/pkg/kypo/sandbox_definition.go index 44abe2c..ae8671a 100644 --- a/pkg/kypo/sandbox_definition.go +++ b/pkg/kypo/sandbox_definition.go @@ -22,6 +22,7 @@ type SandboxDefinitionRequest struct { Rev string `json:"rev"` } +// GetSandboxDefinition reads the given sandbox definition. func (c *Client) GetSandboxDefinition(ctx context.Context, definitionID int64) (*SandboxDefinition, error) { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions/%d", c.Endpoint, definitionID), nil) if err != nil { @@ -51,6 +52,9 @@ func (c *Client) GetSandboxDefinition(ctx context.Context, definitionID int64) ( return &definition, nil } +// CreateSandboxDefinition creates a sandbox definition. +// The `url` must be a URL to a GitLab repository where the sandbox definition is hosted. +// The `rev` specifies the Git revision to be used. func (c *Client) CreateSandboxDefinition(ctx context.Context, url, rev string) (*SandboxDefinition, error) { requestBody, err := json.Marshal(SandboxDefinitionRequest{url, rev}) if err != nil { @@ -80,6 +84,7 @@ func (c *Client) CreateSandboxDefinition(ctx context.Context, url, rev string) ( return &definition, nil } +// DeleteSandboxDefinition deletes the given sandbox definition. func (c *Client) DeleteSandboxDefinition(ctx context.Context, definitionID int64) error { req, err := http.NewRequestWithContext(ctx, "DELETE", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions/%d", c.Endpoint, definitionID), nil) if err != nil { diff --git a/pkg/kypo/sandbox_pool.go b/pkg/kypo/sandbox_pool.go index 4d1058a..5c342aa 100644 --- a/pkg/kypo/sandbox_pool.go +++ b/pkg/kypo/sandbox_pool.go @@ -35,6 +35,7 @@ type HardwareUsage struct { Port string `json:"port" tfsdk:"port"` } +// GetSandboxPool reads the given sandbox pool. func (c *Client) GetSandboxPool(ctx context.Context, poolId int64) (*SandboxPool, error) { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d", c.Endpoint, poolId), nil) if err != nil { @@ -64,6 +65,7 @@ func (c *Client) GetSandboxPool(ctx context.Context, poolId int64) (*SandboxPool return &pool, nil } +// CreateSandboxPool creates a sandbox pool from given sandbox definition id and the maximum size of the pool. func (c *Client) CreateSandboxPool(ctx context.Context, definitionId, maxSize int64) (*SandboxPool, error) { requestBody, err := json.Marshal(SandboxPoolRequest{definitionId, maxSize}) if err != nil { @@ -93,6 +95,7 @@ func (c *Client) CreateSandboxPool(ctx context.Context, definitionId, maxSize in return &pool, nil } +// DeleteSandboxPool deletes the given sandbox pool. func (c *Client) DeleteSandboxPool(ctx context.Context, poolId int64) error { req, err := http.NewRequestWithContext(ctx, "DELETE", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d", c.Endpoint, poolId), nil) if err != nil { @@ -111,6 +114,7 @@ func (c *Client) DeleteSandboxPool(ctx context.Context, poolId int64) error { return nil } +// CleanupSandboxPool creates a cleanup request for all allocation units in the pool. func (c *Client) CleanupSandboxPool(ctx context.Context, poolId int64, force bool) error { req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d/cleanup-requests?force=%s", c.Endpoint, poolId, boolToString(force)), nil) From 5854a769edfaf9bf0ca0d5cd60291c7100a9250e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Tue, 21 Nov 2023 15:26:36 +0100 Subject: [PATCH 38/64] Redo error handling to wrap not found errors --- pkg/kypo/auth.go | 5 ++--- pkg/kypo/error.go | 26 ++++++++---------------- pkg/kypo/sandbox_allocation_unit.go | 13 ++++++------ pkg/kypo/sandbox_definition.go | 3 +-- pkg/kypo/sandbox_pool.go | 3 +-- pkg/kypo/training_definition.go | 3 +-- pkg/kypo/training_definition_adaptive.go | 3 +-- 7 files changed, 22 insertions(+), 34 deletions(-) diff --git a/pkg/kypo/auth.go b/pkg/kypo/auth.go index 9c7d02a..a54d7d3 100644 --- a/pkg/kypo/auth.go +++ b/pkg/kypo/auth.go @@ -189,7 +189,7 @@ func (c *Client) authenticateKeycloak(ctx context.Context) error { return err } if res.StatusCode == http.StatusNotFound || res.StatusCode == http.StatusMethodNotAllowed { - return &ErrNotFound{ResourceName: "KYPO Keycloak endpoint"} + return &Error{ResourceName: "KYPO Keycloak endpoint", Err: ErrNotFound} } if res.StatusCode != http.StatusOK { return fmt.Errorf("authenticateKeycloak failed, got HTTP code: %d", res.StatusCode) @@ -218,8 +218,7 @@ func (c *Client) authenticateKeycloak(ctx context.Context) error { func (c *Client) authenticate() error { err := c.authenticateKeycloak(context.Background()) - var errNotFound *ErrNotFound - if errors.As(err, &errNotFound) { + if errors.Is(err, ErrNotFound) { var token string token, err = c.signIn() if err != nil { diff --git a/pkg/kypo/error.go b/pkg/kypo/error.go index 01ecd76..9d4d8c1 100644 --- a/pkg/kypo/error.go +++ b/pkg/kypo/error.go @@ -1,30 +1,22 @@ package kypo import ( + "errors" "fmt" - "time" ) -type ErrTimeout struct { - Action string - Identifier string - Timeout time.Duration -} - -func (e *ErrTimeout) Error() string { - return fmt.Sprintf("%s: %s has not finished within %s", e.Action, e.Identifier, e.Timeout) -} +var ErrNotFound = errors.New("not found") -type ErrNotFound struct { +type Error struct { ResourceName string - Identifier string + Identifier any + Err error } -func (e *ErrNotFound) Error() string { - return fmt.Sprintf("resource %s: %s was not found", e.ResourceName, e.Identifier) +func (e *Error) Error() string { + return fmt.Sprintf("resource %s %v: %s", e.ResourceName, e.Identifier, e.Err.Error()) } -type valueOrError[T any] struct { - err error - value T +func (e *Error) Unwrap() error { + return e.Err } diff --git a/pkg/kypo/sandbox_allocation_unit.go b/pkg/kypo/sandbox_allocation_unit.go index eb14db2..7072c20 100644 --- a/pkg/kypo/sandbox_allocation_unit.go +++ b/pkg/kypo/sandbox_allocation_unit.go @@ -3,6 +3,7 @@ package kypo import ( "context" "encoding/json" + "errors" "fmt" "net/http" "net/url" @@ -65,7 +66,7 @@ func (c *Client) GetSandboxAllocationUnit(ctx context.Context, unitId int64) (*S allocationUnit := SandboxAllocationUnit{} if status == http.StatusNotFound { - return nil, &ErrNotFound{ResourceName: "sandbox allocation unit", Identifier: strconv.FormatInt(unitId, 10)} + return nil, &Error{ResourceName: "sandbox allocation unit", Identifier: unitId, Err: ErrNotFound} } if status != http.StatusOK { @@ -137,7 +138,7 @@ func (c *Client) CreateSandboxCleanupRequest(ctx context.Context, unitId int64) } if status == http.StatusNotFound { - return nil, &ErrNotFound{ResourceName: "sandbox allocation unit", Identifier: strconv.FormatInt(unitId, 10)} + return nil, &Error{ResourceName: "sandbox allocation unit", Identifier: unitId, Err: ErrNotFound} } if status != http.StatusCreated { @@ -174,7 +175,7 @@ func (c *Client) PollRequestFinished(ctx context.Context, unitId int64, pollTime } if status == http.StatusNotFound { - return nil, &ErrNotFound{ResourceName: "sandbox request", Identifier: strconv.FormatInt(unitId, 10)} + return nil, &Error{ResourceName: "sandbox request", Identifier: unitId, Err: ErrNotFound} } if status != http.StatusOK { @@ -203,7 +204,7 @@ func (c *Client) CreateSandboxCleanupRequestAwait(ctx context.Context, unitId in cleanupRequest, err := c.PollRequestFinished(ctx, unitId, pollTime, "cleanup") // After cleanup is finished it deletes itself and 404 is thrown - if _, ok := err.(*ErrNotFound); ok { + if errors.Is(err, ErrNotFound) { return nil } if err == nil && slices.Contains(cleanupRequest.Stages, "FAILED") { @@ -225,7 +226,7 @@ func (c *Client) CancelSandboxAllocationRequest(ctx context.Context, allocationR } if status == http.StatusNotFound { - return &ErrNotFound{ResourceName: "sandbox allocation request", Identifier: strconv.FormatInt(allocationRequestId, 10)} + return &Error{ResourceName: "sandbox allocation request", Identifier: allocationRequestId, Err: ErrNotFound} } if status != http.StatusOK { @@ -256,7 +257,7 @@ func (c *Client) GetSandboxRequestAnsibleOutputs(ctx context.Context, sandboxReq outputRaw := sandboxRequestStageOutputRaw{} if status == http.StatusNotFound { - return nil, &ErrNotFound{ResourceName: "sandbox request", Identifier: strconv.FormatInt(sandboxRequestId, 10)} + return nil, &Error{ResourceName: "sandbox request", Identifier: sandboxRequestId, Err: ErrNotFound} } if status != http.StatusOK { diff --git a/pkg/kypo/sandbox_definition.go b/pkg/kypo/sandbox_definition.go index ae8671a..ee0e94a 100644 --- a/pkg/kypo/sandbox_definition.go +++ b/pkg/kypo/sandbox_definition.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "net/http" - "strconv" "strings" ) @@ -37,7 +36,7 @@ func (c *Client) GetSandboxDefinition(ctx context.Context, definitionID int64) ( definition := SandboxDefinition{} if status == http.StatusNotFound { - return nil, &ErrNotFound{ResourceName: "sandbox definition", Identifier: strconv.FormatInt(definitionID, 10)} + return nil, &Error{ResourceName: "sandbox definition", Identifier: definitionID, Err: ErrNotFound} } if status != http.StatusOK { diff --git a/pkg/kypo/sandbox_pool.go b/pkg/kypo/sandbox_pool.go index 5c342aa..a602f90 100644 --- a/pkg/kypo/sandbox_pool.go +++ b/pkg/kypo/sandbox_pool.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "net/http" - "strconv" "strings" ) @@ -50,7 +49,7 @@ func (c *Client) GetSandboxPool(ctx context.Context, poolId int64) (*SandboxPool pool := SandboxPool{} if status == http.StatusNotFound { - return nil, &ErrNotFound{ResourceName: "sandbox pool", Identifier: strconv.FormatInt(poolId, 10)} + return nil, &Error{ResourceName: "sandbox pool", Identifier: poolId, Err: ErrNotFound} } if status != http.StatusOK { diff --git a/pkg/kypo/training_definition.go b/pkg/kypo/training_definition.go index e964109..614ee1b 100644 --- a/pkg/kypo/training_definition.go +++ b/pkg/kypo/training_definition.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "net/http" - "strconv" "strings" ) @@ -27,7 +26,7 @@ func (c *Client) GetTrainingDefinition(ctx context.Context, definitionID int64) } if status == http.StatusNotFound { - return nil, &ErrNotFound{ResourceName: "training definition", Identifier: strconv.FormatInt(definitionID, 10)} + return nil, &Error{ResourceName: "training definition", Identifier: definitionID, Err: ErrNotFound} } if status != http.StatusOK { diff --git a/pkg/kypo/training_definition_adaptive.go b/pkg/kypo/training_definition_adaptive.go index 4f45cea..beab302 100644 --- a/pkg/kypo/training_definition_adaptive.go +++ b/pkg/kypo/training_definition_adaptive.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "net/http" - "strconv" "strings" ) @@ -27,7 +26,7 @@ func (c *Client) GetTrainingDefinitionAdaptive(ctx context.Context, definitionID } if status == http.StatusNotFound { - return nil, &ErrNotFound{ResourceName: "training definition adaptive", Identifier: strconv.FormatInt(definitionID, 10)} + return nil, &Error{ResourceName: "training definition adaptive", Identifier: definitionID, Err: ErrNotFound} } if status != http.StatusOK { From 3c4f26c26b3c1f434cf271bbf667d360de7741d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Thu, 23 Nov 2023 15:53:57 +0100 Subject: [PATCH 39/64] Handle doRequest error in a defer statement --- pkg/kypo/util.go | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/pkg/kypo/util.go b/pkg/kypo/util.go index baa334c..955e62d 100644 --- a/pkg/kypo/util.go +++ b/pkg/kypo/util.go @@ -14,10 +14,10 @@ type UserModel struct { Mail string `json:"mail" tfsdk:"mail"` } -func (c *Client) doRequest(req *http.Request) ([]byte, int, error) { - err := c.refreshToken(req.Context()) +func (c *Client) doRequest(req *http.Request) (body []byte, statusCode int, err error) { + err = c.refreshToken(req.Context()) if err != nil { - return nil, 0, err + return } req.Header.Set("Content-Type", "application/json") @@ -25,16 +25,22 @@ func (c *Client) doRequest(req *http.Request) ([]byte, int, error) { res, err := c.HTTPClient.Do(req) if err != nil { - return nil, 0, err + return } - defer res.Body.Close() - - body, err := ioutil.ReadAll(res.Body) + defer func() { + err2 := res.Body.Close() + // If there was an error already, I assume it is more important + if err == nil { + err = err2 + } + }() + statusCode = res.StatusCode + body, err = ioutil.ReadAll(res.Body) if err != nil { - return nil, 0, err + return } - return body, res.StatusCode, nil + return } func boolToString(b bool) string { From ce66591195a6a09fe3b3216b318e960d901b9c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Thu, 23 Nov 2023 17:28:23 +0100 Subject: [PATCH 40/64] Implement retry without timeout for sandbox_allocation_unit --- pkg/kypo/client.go | 1 + pkg/kypo/sandbox_allocation_unit.go | 46 ++++------------------------- pkg/kypo/util.go | 21 +++++++++++++ 3 files changed, 27 insertions(+), 41 deletions(-) diff --git a/pkg/kypo/client.go b/pkg/kypo/client.go index fc7e22f..748b276 100644 --- a/pkg/kypo/client.go +++ b/pkg/kypo/client.go @@ -15,6 +15,7 @@ type Client struct { TokenExpiryTime time.Time Username string Password string + RetryCount int } // NewClientWithToken creates and returns a Client which uses an already created Bearer token. diff --git a/pkg/kypo/sandbox_allocation_unit.go b/pkg/kypo/sandbox_allocation_unit.go index 7072c20..221089d 100644 --- a/pkg/kypo/sandbox_allocation_unit.go +++ b/pkg/kypo/sandbox_allocation_unit.go @@ -58,21 +58,13 @@ func (c *Client) GetSandboxAllocationUnit(ctx context.Context, unitId int64) (*S return nil, err } - body, status, err := c.doRequest(req) + body, _, err := c.doRequestWithRetry(req, http.StatusOK, "sandbox allocation unit", unitId) if err != nil { return nil, err } allocationUnit := SandboxAllocationUnit{} - if status == http.StatusNotFound { - return nil, &Error{ResourceName: "sandbox allocation unit", Identifier: unitId, Err: ErrNotFound} - } - - if status != http.StatusOK { - return nil, fmt.Errorf("status: %d, body: %s", status, body) - } - err = json.Unmarshal(body, &allocationUnit) if err != nil { return nil, err @@ -88,15 +80,11 @@ func (c *Client) CreateSandboxAllocationUnits(ctx context.Context, poolId, count return nil, err } - body, status, err := c.doRequest(req) + body, _, err := c.doRequestWithRetry(req, http.StatusCreated, "sandbox allocation units", "") if err != nil { return nil, err } - if status != http.StatusCreated { - return nil, fmt.Errorf("status: %d, body: %s", status, body) - } - var allocationUnit []SandboxAllocationUnit err = json.Unmarshal(body, &allocationUnit) if err != nil { @@ -132,19 +120,11 @@ func (c *Client) CreateSandboxCleanupRequest(ctx context.Context, unitId int64) return nil, err } - body, status, err := c.doRequest(req) + body, _, err := c.doRequestWithRetry(req, http.StatusCreated, "sandbox cleanup request", "") if err != nil { return nil, err } - if status == http.StatusNotFound { - return nil, &Error{ResourceName: "sandbox allocation unit", Identifier: unitId, Err: ErrNotFound} - } - - if status != http.StatusCreated { - return nil, fmt.Errorf("status: %d, body: %s", status, body) - } - sandboxRequest := SandboxRequest{} err = json.Unmarshal(body, &sandboxRequest) if err != nil { @@ -220,19 +200,11 @@ func (c *Client) CancelSandboxAllocationRequest(ctx context.Context, allocationR return err } - body, status, err := c.doRequest(req) + _, _, err = c.doRequestWithRetry(req, http.StatusOK, "sandbox allocation request", allocationRequestId) if err != nil { return err } - if status == http.StatusNotFound { - return &Error{ResourceName: "sandbox allocation request", Identifier: allocationRequestId, Err: ErrNotFound} - } - - if status != http.StatusOK { - return fmt.Errorf("status: %d, body: %s", status, body) - } - return nil } @@ -249,21 +221,13 @@ func (c *Client) GetSandboxRequestAnsibleOutputs(ctx context.Context, sandboxReq return nil, err } - body, status, err := c.doRequest(req) + body, _, err := c.doRequestWithRetry(req, http.StatusOK, "sandbox request output", sandboxRequestId) if err != nil { return nil, err } outputRaw := sandboxRequestStageOutputRaw{} - if status == http.StatusNotFound { - return nil, &Error{ResourceName: "sandbox request", Identifier: sandboxRequestId, Err: ErrNotFound} - } - - if status != http.StatusOK { - return nil, fmt.Errorf("status: %d, body: %s", status, body) - } - err = json.Unmarshal(body, &outputRaw) if err != nil { return nil, err diff --git a/pkg/kypo/util.go b/pkg/kypo/util.go index 955e62d..8b8f0b5 100644 --- a/pkg/kypo/util.go +++ b/pkg/kypo/util.go @@ -1,6 +1,7 @@ package kypo import ( + "fmt" "io/ioutil" "net/http" ) @@ -43,6 +44,26 @@ func (c *Client) doRequest(req *http.Request) (body []byte, statusCode int, err return } +func (c *Client) doRequestWithRetry(req *http.Request, expectedStatusCode int, resourceName string, identifier any) (body []byte, statusCode int, err error) { + for i := 0; i <= c.RetryCount; i++ { + body, statusCode, err = c.doRequest(req) + if err != nil { + return + } + switch statusCode { + case expectedStatusCode: + return + case http.StatusNotFound: + err = &Error{ResourceName: resourceName, Identifier: identifier, Err: ErrNotFound} + default: + err = &Error{ResourceName: resourceName, Identifier: identifier, Err: fmt.Errorf("status: %d, body: %s", statusCode, body)} + } + } + // Only the last error will be returned + // Aggregating the errors in a readable way seems overly complex + return +} + func boolToString(b bool) string { if b { return "true" From f179a7133358b1bb176ec32f11b6ca72296b40e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Fri, 24 Nov 2023 09:56:12 +0100 Subject: [PATCH 41/64] Replace deprecated ioutil.ReadAll with io.ReadAll --- pkg/kypo/util.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/kypo/util.go b/pkg/kypo/util.go index 8b8f0b5..f1440db 100644 --- a/pkg/kypo/util.go +++ b/pkg/kypo/util.go @@ -2,7 +2,7 @@ package kypo import ( "fmt" - "io/ioutil" + "io" "net/http" ) @@ -36,7 +36,7 @@ func (c *Client) doRequest(req *http.Request) (body []byte, statusCode int, err } }() statusCode = res.StatusCode - body, err = ioutil.ReadAll(res.Body) + body, err = io.ReadAll(res.Body) if err != nil { return } From e7137bd816d9ecedfa6b6be4c766a4198bd01d79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Fri, 24 Nov 2023 10:46:50 +0100 Subject: [PATCH 42/64] Implement exponential backoff for doRequestWithRetry --- pkg/kypo/util.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pkg/kypo/util.go b/pkg/kypo/util.go index f1440db..558a0be 100644 --- a/pkg/kypo/util.go +++ b/pkg/kypo/util.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "net/http" + "time" ) type UserModel struct { @@ -45,6 +46,9 @@ func (c *Client) doRequest(req *http.Request) (body []byte, statusCode int, err } func (c *Client) doRequestWithRetry(req *http.Request, expectedStatusCode int, resourceName string, identifier any) (body []byte, statusCode int, err error) { + duration := 50 * time.Millisecond + timer := time.NewTimer(duration) + defer timer.Stop() for i := 0; i <= c.RetryCount; i++ { body, statusCode, err = c.doRequest(req) if err != nil { @@ -58,6 +62,15 @@ func (c *Client) doRequestWithRetry(req *http.Request, expectedStatusCode int, r default: err = &Error{ResourceName: resourceName, Identifier: identifier, Err: fmt.Errorf("status: %d, body: %s", statusCode, body)} } + timer.Stop() + duration *= 2 + timer.Reset(duration) + select { + case <-req.Context().Done(): + err = req.Context().Err() + return + case <-timer.C: + } } // Only the last error will be returned // Aggregating the errors in a readable way seems overly complex From afb1405664e43ad3a22cee6ca5849b558e9de4de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Fri, 24 Nov 2023 11:00:15 +0100 Subject: [PATCH 43/64] Use doRequestWithRetry for all resources --- pkg/kypo/sandbox_definition.go | 23 +++---------------- pkg/kypo/sandbox_pool.go | 28 ++++-------------------- pkg/kypo/training_definition.go | 22 +++---------------- pkg/kypo/training_definition_adaptive.go | 22 +++---------------- 4 files changed, 13 insertions(+), 82 deletions(-) diff --git a/pkg/kypo/sandbox_definition.go b/pkg/kypo/sandbox_definition.go index ee0e94a..7874a77 100644 --- a/pkg/kypo/sandbox_definition.go +++ b/pkg/kypo/sandbox_definition.go @@ -28,21 +28,12 @@ func (c *Client) GetSandboxDefinition(ctx context.Context, definitionID int64) ( return nil, err } - body, status, err := c.doRequest(req) + body, _, err := c.doRequestWithRetry(req, http.StatusOK, "sandbox definition", definitionID) if err != nil { return nil, err } definition := SandboxDefinition{} - - if status == http.StatusNotFound { - return nil, &Error{ResourceName: "sandbox definition", Identifier: definitionID, Err: ErrNotFound} - } - - if status != http.StatusOK { - return nil, fmt.Errorf("status: %d, body: %s", status, body) - } - err = json.Unmarshal(body, &definition) if err != nil { return nil, err @@ -65,15 +56,11 @@ func (c *Client) CreateSandboxDefinition(ctx context.Context, url, rev string) ( return nil, err } - body, status, err := c.doRequest(req) + body, _, err := c.doRequestWithRetry(req, http.StatusCreated, "sandbox definition", "") if err != nil { return nil, err } - if status != http.StatusCreated { - return nil, fmt.Errorf("status: %d, body: %s", status, body) - } - definition := SandboxDefinition{} err = json.Unmarshal(body, &definition) if err != nil { @@ -90,14 +77,10 @@ func (c *Client) DeleteSandboxDefinition(ctx context.Context, definitionID int64 return err } - body, status, err := c.doRequest(req) + _, _, err = c.doRequestWithRetry(req, http.StatusNoContent, "sandbox definition", definitionID) if err != nil { return err } - if status != http.StatusNoContent && status != http.StatusNotFound { - return fmt.Errorf("status: %d, body: %s", status, body) - } - return nil } diff --git a/pkg/kypo/sandbox_pool.go b/pkg/kypo/sandbox_pool.go index a602f90..2005af9 100644 --- a/pkg/kypo/sandbox_pool.go +++ b/pkg/kypo/sandbox_pool.go @@ -41,21 +41,12 @@ func (c *Client) GetSandboxPool(ctx context.Context, poolId int64) (*SandboxPool return nil, err } - body, status, err := c.doRequest(req) + body, _, err := c.doRequestWithRetry(req, http.StatusOK, "sandbox pool", poolId) if err != nil { return nil, err } pool := SandboxPool{} - - if status == http.StatusNotFound { - return nil, &Error{ResourceName: "sandbox pool", Identifier: poolId, Err: ErrNotFound} - } - - if status != http.StatusOK { - return nil, fmt.Errorf("status: %d, body: %s", status, body) - } - err = json.Unmarshal(body, &pool) if err != nil { return nil, err @@ -76,15 +67,11 @@ func (c *Client) CreateSandboxPool(ctx context.Context, definitionId, maxSize in return nil, err } - body, status, err := c.doRequest(req) + body, _, err := c.doRequestWithRetry(req, http.StatusCreated, "sandbox pool", "") if err != nil { return nil, err } - if status != http.StatusCreated { - return nil, fmt.Errorf("status: %d, body: %s", status, body) - } - pool := SandboxPool{} err = json.Unmarshal(body, &pool) if err != nil { @@ -101,15 +88,11 @@ func (c *Client) DeleteSandboxPool(ctx context.Context, poolId int64) error { return err } - body, status, err := c.doRequest(req) + _, _, err = c.doRequestWithRetry(req, http.StatusNoContent, "sandbox pool", poolId) if err != nil { return err } - if status != http.StatusNoContent && status != http.StatusNotFound { - return fmt.Errorf("status: %d, body: %s", status, body) - } - return nil } @@ -121,14 +104,11 @@ func (c *Client) CleanupSandboxPool(ctx context.Context, poolId int64, force boo return err } - body, status, err := c.doRequest(req) + _, _, err = c.doRequestWithRetry(req, http.StatusAccepted, "sandbox pool", poolId) if err != nil { return err } - if status != http.StatusAccepted { - return fmt.Errorf("status: %d, body: %s", status, body) - } // Wait before cleanup has finished? return nil } diff --git a/pkg/kypo/training_definition.go b/pkg/kypo/training_definition.go index 614ee1b..1bfe207 100644 --- a/pkg/kypo/training_definition.go +++ b/pkg/kypo/training_definition.go @@ -20,19 +20,11 @@ func (c *Client) GetTrainingDefinition(ctx context.Context, definitionID int64) } req.Header.Set("accept", "application/octet-stream") - body, status, err := c.doRequest(req) + body, _, err := c.doRequestWithRetry(req, http.StatusOK, "training definition", definitionID) if err != nil { return nil, err } - if status == http.StatusNotFound { - return nil, &Error{ResourceName: "training definition", Identifier: definitionID, Err: ErrNotFound} - } - - if status != http.StatusOK { - return nil, fmt.Errorf("status: %d, body: %s", status, body) - } - definition := TrainingDefinition{ Id: definitionID, Content: string(body), @@ -47,15 +39,11 @@ func (c *Client) CreateTrainingDefinition(ctx context.Context, content string) ( return nil, err } - body, status, err := c.doRequest(req) + body, _, err := c.doRequestWithRetry(req, http.StatusOK, "training definition", "") if err != nil { return nil, err } - if status != http.StatusOK { - return nil, fmt.Errorf("status: %d, body: %s", status, body) - } - id := struct { Id int64 `json:"id"` }{} @@ -79,14 +67,10 @@ func (c *Client) DeleteTrainingDefinition(ctx context.Context, definitionID int6 return err } - body, status, err := c.doRequest(req) + _, _, err = c.doRequestWithRetry(req, http.StatusOK, "training definition", definitionID) if err != nil { return err } - if status != http.StatusOK && status != http.StatusNotFound { - return fmt.Errorf("status: %d, body: %s", status, body) - } - return nil } diff --git a/pkg/kypo/training_definition_adaptive.go b/pkg/kypo/training_definition_adaptive.go index beab302..360d2e3 100644 --- a/pkg/kypo/training_definition_adaptive.go +++ b/pkg/kypo/training_definition_adaptive.go @@ -20,19 +20,11 @@ func (c *Client) GetTrainingDefinitionAdaptive(ctx context.Context, definitionID } req.Header.Set("accept", "application/octet-stream") - body, status, err := c.doRequest(req) + body, _, err := c.doRequestWithRetry(req, http.StatusOK, "training definition adaptive", definitionID) if err != nil { return nil, err } - if status == http.StatusNotFound { - return nil, &Error{ResourceName: "training definition adaptive", Identifier: definitionID, Err: ErrNotFound} - } - - if status != http.StatusOK { - return nil, fmt.Errorf("status: %d, body: %s", status, body) - } - definition := TrainingDefinitionAdaptive{ Id: definitionID, Content: string(body), @@ -47,15 +39,11 @@ func (c *Client) CreateTrainingDefinitionAdaptive(ctx context.Context, content s return nil, err } - body, status, err := c.doRequest(req) + body, _, err := c.doRequestWithRetry(req, http.StatusOK, "training definition adaptive", "") if err != nil { return nil, err } - if status != http.StatusOK { - return nil, fmt.Errorf("status: %d, body: %s", status, body) - } - id := struct { Id int64 `json:"id"` }{} @@ -79,14 +67,10 @@ func (c *Client) DeleteTrainingDefinitionAdaptive(ctx context.Context, definitio return err } - body, status, err := c.doRequest(req) + _, _, err = c.doRequestWithRetry(req, http.StatusOK, "training definition adaptive", definitionID) if err != nil { return err } - if status != http.StatusOK && status != http.StatusNotFound { - return fmt.Errorf("status: %d, body: %s", status, body) - } - return nil } From f6a41eef88fce0f7879534bc3a1513922ed4b0e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Fri, 24 Nov 2023 17:33:34 +0100 Subject: [PATCH 44/64] Document Client struct --- pkg/kypo/client.go | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/pkg/kypo/client.go b/pkg/kypo/client.go index 748b276..103d9f3 100644 --- a/pkg/kypo/client.go +++ b/pkg/kypo/client.go @@ -8,14 +8,31 @@ import ( // Client struct stores information for authentication to the KYPO API. // All functions are methods of this struct type Client struct { - Endpoint string - ClientID string - HTTPClient *http.Client - Token string + // Endpoint of the KYPO instance to connect to. For example `https://your.kypo.ex`. + Endpoint string + + // ClientID used by the KYPO instance OIDC provider. + ClientID string + + // HTTPClient which is used to do requests. + HTTPClient *http.Client + + // Bearer Token which is used for authentication to the KYPO instance. Is set by NewClient function. + Token string + + // Time when Token expires, used to refresh it automatically when required. Is set by NewClient function. + // Is used only with KYPO instances using Keycloak OIDC provider. TokenExpiryTime time.Time - Username string - Password string - RetryCount int + + // Username of the user to login as. + Username string + + // Password of the user to login as. + Password string + + // How many times should a failed HTTP request be retried. There is a delay of 100ms before the first retry. + // The delay is doubled before each following retry. + RetryCount int } // NewClientWithToken creates and returns a Client which uses an already created Bearer token. From a4bd7c97083479a0c611d922fe8b86c34b72997a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Fri, 24 Nov 2023 17:34:22 +0100 Subject: [PATCH 45/64] Hide sandboxDefinitionRequest and sandboxPoolRequest structs --- pkg/kypo/sandbox_definition.go | 4 ++-- pkg/kypo/sandbox_pool.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/kypo/sandbox_definition.go b/pkg/kypo/sandbox_definition.go index 7874a77..9cde88d 100644 --- a/pkg/kypo/sandbox_definition.go +++ b/pkg/kypo/sandbox_definition.go @@ -16,7 +16,7 @@ type SandboxDefinition struct { CreatedBy UserModel `json:"created_by" tfsdk:"created_by"` } -type SandboxDefinitionRequest struct { +type sandboxDefinitionRequest struct { Url string `json:"url"` Rev string `json:"rev"` } @@ -46,7 +46,7 @@ func (c *Client) GetSandboxDefinition(ctx context.Context, definitionID int64) ( // The `url` must be a URL to a GitLab repository where the sandbox definition is hosted. // The `rev` specifies the Git revision to be used. func (c *Client) CreateSandboxDefinition(ctx context.Context, url, rev string) (*SandboxDefinition, error) { - requestBody, err := json.Marshal(SandboxDefinitionRequest{url, rev}) + requestBody, err := json.Marshal(sandboxDefinitionRequest{url, rev}) if err != nil { return nil, err } diff --git a/pkg/kypo/sandbox_pool.go b/pkg/kypo/sandbox_pool.go index 2005af9..38786f7 100644 --- a/pkg/kypo/sandbox_pool.go +++ b/pkg/kypo/sandbox_pool.go @@ -20,7 +20,7 @@ type SandboxPool struct { Definition SandboxDefinition `json:"definition" tfsdk:"definition"` } -type SandboxPoolRequest struct { +type sandboxPoolRequest struct { DefinitionId int64 `json:"definition_id"` MaxSize int64 `json:"max_size"` } @@ -57,7 +57,7 @@ func (c *Client) GetSandboxPool(ctx context.Context, poolId int64) (*SandboxPool // CreateSandboxPool creates a sandbox pool from given sandbox definition id and the maximum size of the pool. func (c *Client) CreateSandboxPool(ctx context.Context, definitionId, maxSize int64) (*SandboxPool, error) { - requestBody, err := json.Marshal(SandboxPoolRequest{definitionId, maxSize}) + requestBody, err := json.Marshal(sandboxPoolRequest{definitionId, maxSize}) if err != nil { return nil, err } From a5d9381db8eb4469a7373186ba6e80b5011beda2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Fri, 24 Nov 2023 21:46:52 +0100 Subject: [PATCH 46/64] Return more detailed errors from authenticateKeycloak --- pkg/kypo/auth.go | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/pkg/kypo/auth.go b/pkg/kypo/auth.go index a54d7d3..7f554ca 100644 --- a/pkg/kypo/auth.go +++ b/pkg/kypo/auth.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "io/ioutil" "net/http" "net/http/cookiejar" @@ -170,7 +171,7 @@ func (c *Client) authorizeFirstTime(httpClient http.Client, csrf string) (string return token, err } -func (c *Client) authenticateKeycloak(ctx context.Context) error { +func (c *Client) authenticateKeycloak(ctx context.Context) (err error) { query := url.Values{} query.Add("username", c.Username) query.Add("password", c.Password) @@ -180,19 +181,32 @@ func (c *Client) authenticateKeycloak(ctx context.Context) error { req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/keycloak/realms/KYPO/protocol/openid-connect/token", c.Endpoint), strings.NewReader(query.Encode())) if err != nil { - return err + return } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") res, err := http.DefaultClient.Do(req) if err != nil { - return err + return } + defer func() { + err2 := res.Body.Close() + // If there was an error already, I assume it is more important + if err == nil { + err = err2 + } + }() + + body, err := io.ReadAll(res.Body) + if err != nil { + return + } + if res.StatusCode == http.StatusNotFound || res.StatusCode == http.StatusMethodNotAllowed { return &Error{ResourceName: "KYPO Keycloak endpoint", Err: ErrNotFound} } if res.StatusCode != http.StatusOK { - return fmt.Errorf("authenticateKeycloak failed, got HTTP code: %d", res.StatusCode) + return fmt.Errorf("authentication to Keycloak failed, status: %d, body: %s", res.StatusCode, body) } result := struct { @@ -200,20 +214,15 @@ func (c *Client) authenticateKeycloak(ctx context.Context) error { ExpiresIn int `json:"expires_in"` }{} - body, err := ioutil.ReadAll(res.Body) - if err != nil { - return err - } - err = json.Unmarshal(body, &result) if err != nil { - return err + return } c.Token = result.AccessToken c.TokenExpiryTime = time.Now().Add(time.Duration(result.ExpiresIn) * time.Second) - return nil + return } func (c *Client) authenticate() error { From f8ea242690f0f080c2dd58ecae8d91a3330fa111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Fri, 24 Nov 2023 21:47:46 +0100 Subject: [PATCH 47/64] Add Keycloak tests --- go.mod | 11 +++- go.sum | 10 ++++ pkg/kypo/auth_test.go | 129 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 pkg/kypo/auth_test.go diff --git a/go.mod b/go.mod index 4f79859..07de4d1 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,13 @@ module github.com/vydrazde/kypo-go-client go 1.18 -require golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa +require ( + github.com/stretchr/testify v1.8.4 + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index 0a4b649..087fabe 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,12 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/kypo/auth_test.go b/pkg/kypo/auth_test.go new file mode 100644 index 0000000..8ecaab5 --- /dev/null +++ b/pkg/kypo/auth_test.go @@ -0,0 +1,129 @@ +package kypo + +import ( + "context" + "encoding/json" + "fmt" + "github.com/stretchr/testify/assert" + "net/http" + "net/http/httptest" + "testing" + "time" +) + +func assertRequestToKeycloak(t *testing.T, request *http.Request) { + assert.Equal(t, "application/x-www-form-urlencoded", request.Header.Get("Content-Type")) + assert.Equal(t, "/keycloak/realms/KYPO/protocol/openid-connect/token", request.URL.Path) + assert.Equal(t, "POST", request.Method) + + err := request.ParseForm() + assert.NoError(t, err) + + assert.Equal(t, request.PostFormValue("username"), "username") + assert.Equal(t, request.PostFormValue("password"), "password") + assert.Equal(t, request.PostFormValue("client_id"), "client_id") + assert.Equal(t, request.PostFormValue("grant_type"), "password") +} + +func keycloakSuccessfulHandler(t *testing.T, writer http.ResponseWriter, request *http.Request) { + assertRequestToKeycloak(t, request) + + r := struct { + AccessToken string `json:"access_token"` + ExpiresIn int `json:"expires_in"` + RefreshExpiresIn int `json:"refresh_expires_in"` + RefreshToken string `json:"refresh_token"` + TokenType string `json:"token_type"` + NotBeforePolicy int `json:"not-before-policy"` + SessionState string `json:"session_state"` + Scope string `json:"scope"` + }{ + AccessToken: "token", + ExpiresIn: 60, + RefreshExpiresIn: 30, + RefreshToken: "refresh_token", + TokenType: "Bearer", + NotBeforePolicy: 0, + SessionState: "session_state", + Scope: "email openid profile", + } + response, _ := json.Marshal(r) + fmt.Fprint(writer, string(response)) +} + +func TestLoginKeycloakSuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + keycloakSuccessfulHandler(t, writer, request) + })) + defer ts.Close() + + c, err := NewClient(ts.URL, "client_id", "username", "password") + + assert.NoError(t, err) + assert.Equal(t, ts.URL, c.Endpoint) + assert.Equal(t, "client_id", c.ClientID) + assert.Equal(t, http.DefaultClient, c.HTTPClient) + assert.Equal(t, "token", c.Token) + assert.WithinDuration(t, time.Now().Add(time.Duration(60)*time.Second), c.TokenExpiryTime, 100*time.Millisecond) + assert.Equal(t, "username", c.Username) + assert.Equal(t, "password", c.Password) + assert.Equal(t, 0, c.RetryCount) +} + +func TestLoginKeycloakUnsuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertRequestToKeycloak(t, request) + + r := struct { + Error string `json:"error"` + ErrorDescription string `json:"error_description"` + }{ + Error: "invalid_grant", + ErrorDescription: "Invalid user credentials", + } + response, _ := json.Marshal(r) + writer.WriteHeader(http.StatusUnauthorized) + fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c, err := NewClient(ts.URL, "client_id", "username", "password") + + assert.Equal(t, fmt.Errorf("authentication to Keycloak failed, status: 401, body: "+ + "{\"error\":\"invalid_grant\",\"error_description\":\"Invalid user credentials\"}"), err) + assert.Nil(t, c) +} + +func TestRefreshToken(t *testing.T) { + requestCounter := 0 + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + requestCounter++ + keycloakSuccessfulHandler(t, writer, request) + })) + defer ts.Close() + + c := Client{ + Endpoint: ts.URL, + ClientID: "client_id", + HTTPClient: http.DefaultClient, + Token: "old_token", + Username: "username", + Password: "password", + } + + err := c.refreshToken(context.Background()) + assert.NoError(t, err) + assert.Equal(t, 0, requestCounter) + + c.TokenExpiryTime = time.Now().Add(time.Hour) + + err = c.refreshToken(context.Background()) + assert.NoError(t, err) + assert.Equal(t, 0, requestCounter) + + c.TokenExpiryTime = time.Now() + err = c.refreshToken(context.Background()) + assert.NoError(t, err) + assert.Equal(t, 1, requestCounter) + assert.Equal(t, "token", c.Token) +} From 7271b0b9c94072fa5be135bc2aca2ff7846afd42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Fri, 24 Nov 2023 21:48:08 +0100 Subject: [PATCH 48/64] Add CI --- .github/dependabot.yml | 8 ++++++++ .github/workflows/test.yml | 40 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..df80774 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +# See GitHub's documentation for more information on this file: +# https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..46d3961 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,40 @@ +name: Tests + +# This GitHub action runs your tests for each pull request and push. +# Optionally, you can turn it on using a schedule for regular testing. +on: + push: + paths-ignore: + - 'README.md' + +# Testing only needs permissions to read the repository contents. +permissions: + contents: read + +jobs: + lint: + name: Go Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: '1.21' + cache: false + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: v1.54 + + test: + name: Go Tests + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 + with: + go-version-file: 'go.mod' + cache: true + - run: go mod download + - run: go test -v -cover ./pkg/kypo/ -timeout 10m From 5713c8c81f2c5daa71fea889abc37abcfc8cbd65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Sun, 26 Nov 2023 09:48:48 +0100 Subject: [PATCH 49/64] Move tests to separate package --- pkg/kypo/auth_test.go | 41 +++++++++++++++++++++++++++++++++-------- pkg/kypo/export_test.go | 8 ++++++++ 2 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 pkg/kypo/export_test.go diff --git a/pkg/kypo/auth_test.go b/pkg/kypo/auth_test.go index 8ecaab5..ed35291 100644 --- a/pkg/kypo/auth_test.go +++ b/pkg/kypo/auth_test.go @@ -1,10 +1,11 @@ -package kypo +package kypo_test import ( "context" "encoding/json" "fmt" "github.com/stretchr/testify/assert" + "github.com/vydrazde/kypo-go-client/pkg/kypo" "net/http" "net/http/httptest" "testing" @@ -57,7 +58,16 @@ func TestLoginKeycloakSuccessful(t *testing.T) { })) defer ts.Close() - c, err := NewClient(ts.URL, "client_id", "username", "password") + c := kypo.Client{ + Endpoint: ts.URL, + ClientID: "client_id", + HTTPClient: http.DefaultClient, + Token: "old_token", + Username: "username", + Password: "password", + } + + err := kypo.Authenticate(&c) assert.NoError(t, err) assert.Equal(t, ts.URL, c.Endpoint) @@ -87,11 +97,26 @@ func TestLoginKeycloakUnsuccessful(t *testing.T) { })) defer ts.Close() - c, err := NewClient(ts.URL, "client_id", "username", "password") + c := kypo.Client{ + Endpoint: ts.URL, + ClientID: "client_id", + HTTPClient: http.DefaultClient, + Token: "token", + Username: "username", + Password: "password", + } + + err := kypo.Authenticate(&c) assert.Equal(t, fmt.Errorf("authentication to Keycloak failed, status: 401, body: "+ "{\"error\":\"invalid_grant\",\"error_description\":\"Invalid user credentials\"}"), err) - assert.Nil(t, c) + assert.Equal(t, ts.URL, c.Endpoint) + assert.Equal(t, "client_id", c.ClientID) + assert.Equal(t, http.DefaultClient, c.HTTPClient) + assert.Equal(t, "token", c.Token) + assert.Equal(t, "username", c.Username) + assert.Equal(t, "password", c.Password) + assert.Equal(t, 0, c.RetryCount) } func TestRefreshToken(t *testing.T) { @@ -102,7 +127,7 @@ func TestRefreshToken(t *testing.T) { })) defer ts.Close() - c := Client{ + c := kypo.Client{ Endpoint: ts.URL, ClientID: "client_id", HTTPClient: http.DefaultClient, @@ -111,18 +136,18 @@ func TestRefreshToken(t *testing.T) { Password: "password", } - err := c.refreshToken(context.Background()) + err := kypo.RefreshToken(&c, context.Background()) assert.NoError(t, err) assert.Equal(t, 0, requestCounter) c.TokenExpiryTime = time.Now().Add(time.Hour) - err = c.refreshToken(context.Background()) + err = kypo.RefreshToken(&c, context.Background()) assert.NoError(t, err) assert.Equal(t, 0, requestCounter) c.TokenExpiryTime = time.Now() - err = c.refreshToken(context.Background()) + err = kypo.RefreshToken(&c, context.Background()) assert.NoError(t, err) assert.Equal(t, 1, requestCounter) assert.Equal(t, "token", c.Token) diff --git a/pkg/kypo/export_test.go b/pkg/kypo/export_test.go new file mode 100644 index 0000000..9a3af1b --- /dev/null +++ b/pkg/kypo/export_test.go @@ -0,0 +1,8 @@ +package kypo + +import "context" + +var ( + RefreshToken = func(c *Client, ctx context.Context) error { return c.refreshToken(ctx) } + Authenticate = func(c *Client) error { return c.authenticate() } +) From 268c5d0fa245b2003787c229b0799a5b2c361aac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Sun, 26 Nov 2023 10:33:19 +0100 Subject: [PATCH 50/64] Add training definition tests --- pkg/kypo/training_definition_test.go | 288 +++++++++++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 pkg/kypo/training_definition_test.go diff --git a/pkg/kypo/training_definition_test.go b/pkg/kypo/training_definition_test.go new file mode 100644 index 0000000..b1745ea --- /dev/null +++ b/pkg/kypo/training_definition_test.go @@ -0,0 +1,288 @@ +package kypo_test + +import ( + "context" + "encoding/json" + "fmt" + "github.com/stretchr/testify/assert" + "github.com/vydrazde/kypo-go-client/pkg/kypo" + "net/http" + "net/http/httptest" + "testing" +) + +var trainingDefinitionJsonString = `{"title":"title","description":"description","prerequisites":[],"outcomes":[],"state":"UNRELEASED","show_stepper_bar":true,"levels":[],"estimated_duration":0,"variant_sandboxes":false}` + +func minimalClient(ts *httptest.Server) kypo.Client { + c := kypo.Client{ + Endpoint: ts.URL, + HTTPClient: http.DefaultClient, + Token: "token", + } + return c +} + +func assertTrainingDefinitionGet(t *testing.T, request *http.Request) { + assert.Equal(t, "application/json", request.Header.Get("Content-Type")) + assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) + assert.Equal(t, "application/octet-stream", request.Header.Get("accept")) + assert.Equal(t, "/kypo-rest-training/api/v1/exports/training-definitions/1", request.URL.Path) + assert.Equal(t, "GET", request.Method) +} + +func TestGetTrainingDefinitionSuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertTrainingDefinitionGet(t, request) + + r := struct { + Title string `json:"title"` + Description string `json:"description"` + Prerequisites []string `json:"prerequisites"` + Outcomes []string `json:"outcomes"` + State string `json:"state"` + ShowStepperBar bool `json:"show_stepper_bar"` + Levels []string `json:"levels"` + EstimatedDuration int `json:"estimated_duration"` + VariantSandboxes bool `json:"variant_sandboxes"` + }{ + Title: "title", + Description: "description", + Prerequisites: []string{}, + Outcomes: []string{}, + State: "UNRELEASED", + ShowStepperBar: true, + Levels: []string{}, + EstimatedDuration: 0, + VariantSandboxes: false, + } + response, _ := json.Marshal(r) + fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := kypo.TrainingDefinition{ + Id: 1, + Content: trainingDefinitionJsonString, + } + + actual, err := c.GetTrainingDefinition(context.Background(), 1) + + assert.NoError(t, err) + assert.Equal(t, &expected, actual) +} + +func TestGetTrainingDefinitionNotFound(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertTrainingDefinitionGet(t, request) + + writer.WriteHeader(http.StatusNotFound) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := &kypo.Error{ + ResourceName: "training definition", + Identifier: int64(1), + Err: kypo.ErrNotFound, + } + + td, actual := c.GetTrainingDefinition(context.Background(), 1) + + assert.Nil(t, td) + assert.Equal(t, expected, actual) +} + +func TestGetTrainingDefinitionServerError(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertTrainingDefinitionGet(t, request) + + writer.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := &kypo.Error{ + ResourceName: "training definition", + Identifier: int64(1), + Err: fmt.Errorf("status: 500, body: "), + } + + td, actual := c.GetTrainingDefinition(context.Background(), 1) + + assert.Nil(t, td) + assert.Equal(t, expected, actual) +} + +func assertTrainingDefinitionCreate(t *testing.T, request *http.Request) { + assert.Equal(t, "application/json", request.Header.Get("Content-Type")) + assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) + assert.Equal(t, "/kypo-rest-training/api/v1/imports/training-definitions", request.URL.Path) + assert.Equal(t, "POST", request.Method) +} + +func TestCreateTrainingDefinitionSuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertTrainingDefinitionCreate(t, request) + + r := struct { + Id int `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + Prerequisites []string `json:"prerequisites"` + Outcomes []string `json:"outcomes"` + State string `json:"state"` + BetaTestingGroupId *string `json:"beta_testing_group_id"` + ShowStepperBar bool `json:"show_stepper_bar"` + Levels []string `json:"levels"` + CanBeArchived bool `json:"can_be_archived"` + EstimatedDuration int `json:"estimated_duration"` + LastEdited string `json:"last_edited"` + LastEditedBy string `json:"last_edited_by"` + }{ + Id: 1, + Title: "title", + Description: "description", + Prerequisites: []string{}, + Outcomes: []string{}, + State: "UNRELEASED", + BetaTestingGroupId: nil, + ShowStepperBar: true, + Levels: []string{}, + CanBeArchived: false, + EstimatedDuration: 0, + LastEdited: "2023-11-26T09:03:35.313174494Z", + LastEditedBy: "User 1", + } + response, _ := json.Marshal(r) + fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := kypo.TrainingDefinition{ + Id: 1, + Content: trainingDefinitionJsonString, + } + + actual, err := c.CreateTrainingDefinition(context.Background(), trainingDefinitionJsonString) + + assert.NoError(t, err) + assert.Equal(t, &expected, actual) +} + +func TestCreateTrainingDefinitionServerError(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertTrainingDefinitionCreate(t, request) + + writer.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := &kypo.Error{ + ResourceName: "training definition", + Identifier: "", + Err: fmt.Errorf("status: 500, body: "), + } + + td, actual := c.CreateTrainingDefinition(context.Background(), trainingDefinitionJsonString) + + assert.Nil(t, td) + assert.Equal(t, expected, actual) +} + +func assertTrainingDefinitionDelete(t *testing.T, request *http.Request) { + assert.Equal(t, "application/json", request.Header.Get("Content-Type")) + assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) + assert.Equal(t, "/kypo-rest-training/api/v1/training-definitions/1", request.URL.Path) + assert.Equal(t, "DELETE", request.Method) +} + +func TestDeleteTrainingDefinitionSuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertTrainingDefinitionDelete(t, request) + + writer.WriteHeader(http.StatusOK) + })) + defer ts.Close() + + c := minimalClient(ts) + + err := c.DeleteTrainingDefinition(context.Background(), 1) + + assert.NoError(t, err) +} + +func TestDeleteTrainingDefinitionNotFound(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertTrainingDefinitionDelete(t, request) + + writer.WriteHeader(http.StatusNotFound) + type EntityErrorDetail struct { + Entity string `json:"entity"` + Identifier string `json:"identifier"` + IdentifierValue int `json:"identifier_value"` + Reason string `json:"reason"` + } + + r := struct { + Timestamp int `json:"timestamp"` + Status string `json:"status"` + Message string `json:"message"` + Errors []*string `json:"errors"` + Path string `json:"path"` + EntityErrorDetail EntityErrorDetail `json:"entity_error_detail"` + }{ + Timestamp: 1700990353304, + Status: "NOT_FOUND", + Message: "Entity TrainingDefinition (id: 1) not found.", + Errors: []*string{nil}, + Path: "/kypo-rest-training/api/v1/training-definitions/1", + EntityErrorDetail: EntityErrorDetail{ + Entity: "TrainingDefinition", + Identifier: "id", + IdentifierValue: 1, + Reason: "Entity TrainingDefinition (id: 1) not found.", + }, + } + response, _ := json.Marshal(r) + fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + expected := &kypo.Error{ + ResourceName: "training definition", + Identifier: int64(1), + Err: kypo.ErrNotFound, + } + + actual := c.DeleteTrainingDefinition(context.Background(), 1) + + assert.Equal(t, expected, actual) +} + +func TestDeleteTrainingDefinitionServerError(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertTrainingDefinitionDelete(t, request) + + writer.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + c := minimalClient(ts) + expected := &kypo.Error{ + ResourceName: "training definition", + Identifier: int64(1), + Err: fmt.Errorf("status: 500, body: "), + } + actual := c.DeleteTrainingDefinition(context.Background(), 1) + + assert.Equal(t, expected, actual) +} From 633b705ec1aa2f4797f2822fc80f001023403048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Sun, 26 Nov 2023 10:36:14 +0100 Subject: [PATCH 51/64] Explicitly ignore impossible errors --- pkg/kypo/auth_test.go | 4 ++-- pkg/kypo/training_definition_test.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/kypo/auth_test.go b/pkg/kypo/auth_test.go index ed35291..6ff7f63 100644 --- a/pkg/kypo/auth_test.go +++ b/pkg/kypo/auth_test.go @@ -49,7 +49,7 @@ func keycloakSuccessfulHandler(t *testing.T, writer http.ResponseWriter, request Scope: "email openid profile", } response, _ := json.Marshal(r) - fmt.Fprint(writer, string(response)) + _, _ = fmt.Fprint(writer, string(response)) } func TestLoginKeycloakSuccessful(t *testing.T) { @@ -93,7 +93,7 @@ func TestLoginKeycloakUnsuccessful(t *testing.T) { } response, _ := json.Marshal(r) writer.WriteHeader(http.StatusUnauthorized) - fmt.Fprint(writer, string(response)) + _, _ = fmt.Fprint(writer, string(response)) })) defer ts.Close() diff --git a/pkg/kypo/training_definition_test.go b/pkg/kypo/training_definition_test.go index b1745ea..bace6e9 100644 --- a/pkg/kypo/training_definition_test.go +++ b/pkg/kypo/training_definition_test.go @@ -56,7 +56,7 @@ func TestGetTrainingDefinitionSuccessful(t *testing.T) { VariantSandboxes: false, } response, _ := json.Marshal(r) - fmt.Fprint(writer, string(response)) + _, _ = fmt.Fprint(writer, string(response)) })) defer ts.Close() @@ -158,7 +158,7 @@ func TestCreateTrainingDefinitionSuccessful(t *testing.T) { LastEditedBy: "User 1", } response, _ := json.Marshal(r) - fmt.Fprint(writer, string(response)) + _, _ = fmt.Fprint(writer, string(response)) })) defer ts.Close() @@ -252,7 +252,7 @@ func TestDeleteTrainingDefinitionNotFound(t *testing.T) { }, } response, _ := json.Marshal(r) - fmt.Fprint(writer, string(response)) + _, _ = fmt.Fprint(writer, string(response)) })) defer ts.Close() From 38c2680afaec8e783ccd328a08f9609aa363c722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Sun, 26 Nov 2023 10:38:49 +0100 Subject: [PATCH 52/64] Extract variable for expected error in test --- pkg/kypo/auth_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/kypo/auth_test.go b/pkg/kypo/auth_test.go index 6ff7f63..47b6381 100644 --- a/pkg/kypo/auth_test.go +++ b/pkg/kypo/auth_test.go @@ -105,11 +105,12 @@ func TestLoginKeycloakUnsuccessful(t *testing.T) { Username: "username", Password: "password", } + expected := fmt.Errorf("authentication to Keycloak failed, status: 401, body: " + + "{\"error\":\"invalid_grant\",\"error_description\":\"Invalid user credentials\"}") err := kypo.Authenticate(&c) - assert.Equal(t, fmt.Errorf("authentication to Keycloak failed, status: 401, body: "+ - "{\"error\":\"invalid_grant\",\"error_description\":\"Invalid user credentials\"}"), err) + assert.Equal(t, expected, err) assert.Equal(t, ts.URL, c.Endpoint) assert.Equal(t, "client_id", c.ClientID) assert.Equal(t, http.DefaultClient, c.HTTPClient) From 034519a883ac20fc6fd6a91fb0c125b12bad78b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Sun, 26 Nov 2023 10:40:37 +0100 Subject: [PATCH 53/64] Rename User struct and move it to separate file --- pkg/kypo/sandbox_allocation_unit.go | 2 +- pkg/kypo/sandbox_definition.go | 10 +++++----- pkg/kypo/sandbox_pool.go | 2 +- pkg/kypo/user.go | 10 ++++++++++ pkg/kypo/util.go | 9 --------- 5 files changed, 17 insertions(+), 16 deletions(-) create mode 100644 pkg/kypo/user.go diff --git a/pkg/kypo/sandbox_allocation_unit.go b/pkg/kypo/sandbox_allocation_unit.go index 221089d..df7c29c 100644 --- a/pkg/kypo/sandbox_allocation_unit.go +++ b/pkg/kypo/sandbox_allocation_unit.go @@ -18,7 +18,7 @@ type SandboxAllocationUnit struct { PoolId int64 `json:"pool_id" tfsdk:"pool_id"` AllocationRequest SandboxRequest `json:"allocation_request" tfsdk:"allocation_request"` CleanupRequest SandboxRequest `json:"cleanup_request" tfsdk:"cleanup_request"` - CreatedBy UserModel `json:"created_by" tfsdk:"created_by"` + CreatedBy User `json:"created_by" tfsdk:"created_by"` Locked bool `json:"locked" tfsdk:"locked"` } diff --git a/pkg/kypo/sandbox_definition.go b/pkg/kypo/sandbox_definition.go index 9cde88d..a57f0c9 100644 --- a/pkg/kypo/sandbox_definition.go +++ b/pkg/kypo/sandbox_definition.go @@ -9,11 +9,11 @@ import ( ) type SandboxDefinition struct { - Id int64 `json:"id" tfsdk:"id"` - Url string `json:"url" tfsdk:"url"` - Name string `json:"name" tfsdk:"name"` - Rev string `json:"rev" tfsdk:"rev"` - CreatedBy UserModel `json:"created_by" tfsdk:"created_by"` + Id int64 `json:"id" tfsdk:"id"` + Url string `json:"url" tfsdk:"url"` + Name string `json:"name" tfsdk:"name"` + Rev string `json:"rev" tfsdk:"rev"` + CreatedBy User `json:"created_by" tfsdk:"created_by"` } type sandboxDefinitionRequest struct { diff --git a/pkg/kypo/sandbox_pool.go b/pkg/kypo/sandbox_pool.go index 38786f7..40f1475 100644 --- a/pkg/kypo/sandbox_pool.go +++ b/pkg/kypo/sandbox_pool.go @@ -15,7 +15,7 @@ type SandboxPool struct { LockId int64 `json:"lock_id" tfsdk:"lock_id"` Rev string `json:"rev" tfsdk:"rev"` RevSha string `json:"rev_sha" tfsdk:"rev_sha"` - CreatedBy UserModel `json:"created_by" tfsdk:"created_by"` + CreatedBy User `json:"created_by" tfsdk:"created_by"` HardwareUsage HardwareUsage `json:"hardware_usage" tfsdk:"hardware_usage"` Definition SandboxDefinition `json:"definition" tfsdk:"definition"` } diff --git a/pkg/kypo/user.go b/pkg/kypo/user.go new file mode 100644 index 0000000..46d0b2f --- /dev/null +++ b/pkg/kypo/user.go @@ -0,0 +1,10 @@ +package kypo + +type User struct { + Id int64 `json:"id" tfsdk:"id"` + Sub string `json:"sub" tfsdk:"sub"` + FullName string `json:"full_name" tfsdk:"full_name"` + GivenName string `json:"given_name" tfsdk:"given_name"` + FamilyName string `json:"family_name" tfsdk:"family_name"` + Mail string `json:"mail" tfsdk:"mail"` +} diff --git a/pkg/kypo/util.go b/pkg/kypo/util.go index 558a0be..6f8c73d 100644 --- a/pkg/kypo/util.go +++ b/pkg/kypo/util.go @@ -7,15 +7,6 @@ import ( "time" ) -type UserModel struct { - Id int64 `json:"id" tfsdk:"id"` - Sub string `json:"sub" tfsdk:"sub"` - FullName string `json:"full_name" tfsdk:"full_name"` - GivenName string `json:"given_name" tfsdk:"given_name"` - FamilyName string `json:"family_name" tfsdk:"family_name"` - Mail string `json:"mail" tfsdk:"mail"` -} - func (c *Client) doRequest(req *http.Request) (body []byte, statusCode int, err error) { err = c.refreshToken(req.Context()) if err != nil { From 4ac709dfd2f0891f876a5f3757d9ab7602ff08c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Sun, 26 Nov 2023 14:42:19 +0100 Subject: [PATCH 54/64] Add tests for training definition adaptive --- pkg/kypo/training_definition_adaptive_test.go | 277 ++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 pkg/kypo/training_definition_adaptive_test.go diff --git a/pkg/kypo/training_definition_adaptive_test.go b/pkg/kypo/training_definition_adaptive_test.go new file mode 100644 index 0000000..bcb28f3 --- /dev/null +++ b/pkg/kypo/training_definition_adaptive_test.go @@ -0,0 +1,277 @@ +package kypo_test + +import ( + "context" + "encoding/json" + "fmt" + "github.com/stretchr/testify/assert" + "github.com/vydrazde/kypo-go-client/pkg/kypo" + "net/http" + "net/http/httptest" + "testing" +) + +var trainingDefinitionAdaptiveJsonString = `{"title":"title","description":"description","prerequisites":[],"outcomes":[],"state":"UNRELEASED","show_stepper_bar":true,"phases":[],"estimated_duration":0}` + +func assertTrainingDefinitionAdaptiveGet(t *testing.T, request *http.Request) { + assert.Equal(t, "application/json", request.Header.Get("Content-Type")) + assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) + assert.Equal(t, "application/octet-stream", request.Header.Get("accept")) + assert.Equal(t, "/kypo-adaptive-training/api/v1/exports/training-definitions/1", request.URL.Path) + assert.Equal(t, "GET", request.Method) +} + +func TestGetTrainingDefinitionAdaptiveSuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertTrainingDefinitionAdaptiveGet(t, request) + + r := struct { + Title string `json:"title"` + Description string `json:"description"` + Prerequisites []string `json:"prerequisites"` + Outcomes []string `json:"outcomes"` + State string `json:"state"` + ShowStepperBar bool `json:"show_stepper_bar"` + Phases []string `json:"phases"` + EstimatedDuration int `json:"estimated_duration"` + }{ + Title: "title", + Description: "description", + Prerequisites: []string{}, + Outcomes: []string{}, + State: "UNRELEASED", + ShowStepperBar: true, + Phases: []string{}, + EstimatedDuration: 0, + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := kypo.TrainingDefinitionAdaptive{ + Id: 1, + Content: trainingDefinitionAdaptiveJsonString, + } + + actual, err := c.GetTrainingDefinitionAdaptive(context.Background(), 1) + + assert.NoError(t, err) + assert.Equal(t, &expected, actual) +} + +func TestGetTrainingDefinitionAdaptiveNotFound(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertTrainingDefinitionAdaptiveGet(t, request) + + writer.WriteHeader(http.StatusNotFound) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := &kypo.Error{ + ResourceName: "training definition adaptive", + Identifier: int64(1), + Err: kypo.ErrNotFound, + } + + td, actual := c.GetTrainingDefinitionAdaptive(context.Background(), 1) + + assert.Nil(t, td) + assert.Equal(t, expected, actual) +} + +func TestGetTrainingDefinitionAdaptiveServerError(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertTrainingDefinitionAdaptiveGet(t, request) + + writer.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := &kypo.Error{ + ResourceName: "training definition adaptive", + Identifier: int64(1), + Err: fmt.Errorf("status: 500, body: "), + } + + td, actual := c.GetTrainingDefinitionAdaptive(context.Background(), 1) + + assert.Nil(t, td) + assert.Equal(t, expected, actual) +} + +func assertTrainingDefinitionAdaptiveCreate(t *testing.T, request *http.Request) { + assert.Equal(t, "application/json", request.Header.Get("Content-Type")) + assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) + assert.Equal(t, "/kypo-adaptive-training/api/v1/imports/training-definitions", request.URL.Path) + assert.Equal(t, "POST", request.Method) +} + +func TestCreateTrainingDefinitionAdaptiveSuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertTrainingDefinitionAdaptiveCreate(t, request) + + r := struct { + Id int `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + Prerequisites []string `json:"prerequisites"` + Outcomes []string `json:"outcomes"` + State string `json:"state"` + BetaTestingGroupId *string `json:"beta_testing_group_id"` + ShowStepperBar bool `json:"show_stepper_bar"` + Levels []string `json:"levels"` + CanBeArchived bool `json:"can_be_archived"` + EstimatedDuration int `json:"estimated_duration"` + LastEdited string `json:"last_edited"` + LastEditedBy string `json:"last_edited_by"` + }{ + Id: 1, + Title: "title", + Description: "description", + Prerequisites: []string{}, + Outcomes: []string{}, + State: "UNRELEASED", + BetaTestingGroupId: nil, + ShowStepperBar: true, + Levels: []string{}, + CanBeArchived: false, + EstimatedDuration: 0, + LastEdited: "2023-11-26T09:03:35.313174494Z", + LastEditedBy: "User 1", + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := kypo.TrainingDefinitionAdaptive{ + Id: 1, + Content: trainingDefinitionJsonString, + } + + actual, err := c.CreateTrainingDefinitionAdaptive(context.Background(), trainingDefinitionJsonString) + + assert.NoError(t, err) + assert.Equal(t, &expected, actual) +} + +func TestCreateTrainingDefinitionAdaptiveServerError(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertTrainingDefinitionAdaptiveCreate(t, request) + + writer.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := &kypo.Error{ + ResourceName: "training definition adaptive", + Identifier: "", + Err: fmt.Errorf("status: 500, body: "), + } + + td, actual := c.CreateTrainingDefinitionAdaptive(context.Background(), trainingDefinitionJsonString) + + assert.Nil(t, td) + assert.Equal(t, expected, actual) +} + +func assertTrainingDefinitionAdaptiveDelete(t *testing.T, request *http.Request) { + assert.Equal(t, "application/json", request.Header.Get("Content-Type")) + assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) + assert.Equal(t, "/kypo-adaptive-training/api/v1/training-definitions/1", request.URL.Path) + assert.Equal(t, "DELETE", request.Method) +} + +func TestDeleteTrainingDefinitionAdaptiveSuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertTrainingDefinitionAdaptiveDelete(t, request) + + writer.WriteHeader(http.StatusOK) + })) + defer ts.Close() + + c := minimalClient(ts) + + err := c.DeleteTrainingDefinitionAdaptive(context.Background(), 1) + + assert.NoError(t, err) +} + +func TestDeleteTrainingDefinitionAdaptiveNotFound(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertTrainingDefinitionAdaptiveDelete(t, request) + + writer.WriteHeader(http.StatusNotFound) + type EntityErrorDetail struct { + Entity string `json:"entity"` + Identifier string `json:"identifier"` + IdentifierValue int `json:"identifier_value"` + Reason string `json:"reason"` + } + + r := struct { + Timestamp int `json:"timestamp"` + Status string `json:"status"` + Message string `json:"message"` + Errors []*string `json:"errors"` + Path string `json:"path"` + EntityErrorDetail EntityErrorDetail `json:"entity_error_detail"` + }{ + Timestamp: 1700990353304, + Status: "NOT_FOUND", + Message: "Entity TrainingDefinition (id: 1) not found.", + Errors: []*string{nil}, + Path: "/kypo-adaptive-training/api/v1/training-definitions/1", + EntityErrorDetail: EntityErrorDetail{ + Entity: "TrainingDefinition", + Identifier: "id", + IdentifierValue: 1, + Reason: "Entity TrainingDefinition (id: 1) not found.", + }, + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + expected := &kypo.Error{ + ResourceName: "training definition adaptive", + Identifier: int64(1), + Err: kypo.ErrNotFound, + } + + actual := c.DeleteTrainingDefinitionAdaptive(context.Background(), 1) + + assert.Equal(t, expected, actual) +} + +func TestDeleteTrainingDefinitionAdaptiveServerError(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertTrainingDefinitionAdaptiveDelete(t, request) + + writer.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + c := minimalClient(ts) + expected := &kypo.Error{ + ResourceName: "training definition adaptive", + Identifier: int64(1), + Err: fmt.Errorf("status: 500, body: "), + } + actual := c.DeleteTrainingDefinitionAdaptive(context.Background(), 1) + + assert.Equal(t, expected, actual) +} From eb2c15742fe3bfc61cc62356b2b16b915e45dfd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Sun, 26 Nov 2023 15:06:36 +0100 Subject: [PATCH 55/64] Add tests for sandbox definition --- pkg/kypo/sandbox_definition_test.go | 262 ++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 pkg/kypo/sandbox_definition_test.go diff --git a/pkg/kypo/sandbox_definition_test.go b/pkg/kypo/sandbox_definition_test.go new file mode 100644 index 0000000..a20ebd8 --- /dev/null +++ b/pkg/kypo/sandbox_definition_test.go @@ -0,0 +1,262 @@ +package kypo_test + +import ( + "context" + "encoding/json" + "fmt" + "github.com/stretchr/testify/assert" + "github.com/vydrazde/kypo-go-client/pkg/kypo" + "net/http" + "net/http/httptest" + "testing" +) + +type User struct { + Id int `json:"id"` + Sub string `json:"sub"` + FullName string `json:"full_name"` + GivenName string `json:"given_name"` + FamilyName string `json:"family_name"` + Mail string `json:"mail"` +} + +var sandboxDefinitionResponse = struct { + Id int `json:"id"` + Name string `json:"name"` + Url string `json:"url"` + Rev string `json:"rev"` + CreatedBy User `json:"created_by"` +}{ + Id: 1, + Name: "name", + Url: "url", + Rev: "rev", + CreatedBy: User{ + Id: 1, + Sub: "sub", + FullName: "full_name", + GivenName: "given_name", + FamilyName: "family_name", + Mail: "mail", + }, +} + +func assertSandboxDefinitionGet(t *testing.T, request *http.Request) { + assert.Equal(t, "application/json", request.Header.Get("Content-Type")) + assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) + assert.Equal(t, "/kypo-sandbox-service/api/v1/definitions/1", request.URL.Path) + assert.Equal(t, "GET", request.Method) +} + +func TestGetSandboxDefinitionSuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxDefinitionGet(t, request) + + response, _ := json.Marshal(sandboxDefinitionResponse) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := kypo.SandboxDefinition{ + Id: 1, + Name: "name", + Url: "url", + Rev: "rev", + CreatedBy: kypo.User{ + Id: 1, + Sub: "sub", + FullName: "full_name", + GivenName: "given_name", + FamilyName: "family_name", + Mail: "mail", + }, + } + + actual, err := c.GetSandboxDefinition(context.Background(), 1) + + assert.NoError(t, err) + assert.Equal(t, &expected, actual) +} + +func TestGetSandboxDefinitionNotFound(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxDefinitionGet(t, request) + + writer.WriteHeader(http.StatusNotFound) + r := struct { + Detail string `json:"detail"` + }{ + Detail: "No Definition matches the given query", + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := &kypo.Error{ + ResourceName: "sandbox definition", + Identifier: int64(1), + Err: kypo.ErrNotFound, + } + + td, actual := c.GetSandboxDefinition(context.Background(), 1) + + assert.Nil(t, td) + assert.Equal(t, expected, actual) +} + +func TestGetSandboxDefinitionServerError(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxDefinitionGet(t, request) + + writer.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := &kypo.Error{ + ResourceName: "sandbox definition", + Identifier: int64(1), + Err: fmt.Errorf("status: 500, body: "), + } + + td, actual := c.GetSandboxDefinition(context.Background(), 1) + + assert.Nil(t, td) + assert.Equal(t, expected, actual) +} + +func assertSandboxDefinitionCreate(t *testing.T, request *http.Request) { + assert.Equal(t, "application/json", request.Header.Get("Content-Type")) + assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) + assert.Equal(t, "/kypo-sandbox-service/api/v1/definitions", request.URL.Path) + assert.Equal(t, "POST", request.Method) +} + +func TestCreateSandboxDefinitionSuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxDefinitionCreate(t, request) + + writer.WriteHeader(http.StatusCreated) + response, _ := json.Marshal(sandboxDefinitionResponse) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := kypo.SandboxDefinition{ + Id: 1, + Name: "name", + Url: "url", + Rev: "rev", + CreatedBy: kypo.User{ + Id: 1, + Sub: "sub", + FullName: "full_name", + GivenName: "given_name", + FamilyName: "family_name", + Mail: "mail", + }, + } + + actual, err := c.CreateSandboxDefinition(context.Background(), "url", "rev") + + assert.NoError(t, err) + assert.Equal(t, &expected, actual) +} + +func TestCreateSandboxDefinitionServerError(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxDefinitionCreate(t, request) + + writer.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := &kypo.Error{ + ResourceName: "sandbox definition", + Identifier: "", + Err: fmt.Errorf("status: 500, body: "), + } + + actual, err := c.CreateSandboxDefinition(context.Background(), "url", "rev") + + assert.Nil(t, actual) + assert.Equal(t, expected, err) +} + +func assertSandboxDefinitionDelete(t *testing.T, request *http.Request) { + assert.Equal(t, "application/json", request.Header.Get("Content-Type")) + assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) + assert.Equal(t, "/kypo-sandbox-service/api/v1/definitions/1", request.URL.Path) + assert.Equal(t, "DELETE", request.Method) +} + +func TestDeleteSandboxDefinitionSuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxDefinitionDelete(t, request) + + writer.WriteHeader(http.StatusNoContent) + })) + defer ts.Close() + + c := minimalClient(ts) + + err := c.DeleteSandboxDefinition(context.Background(), 1) + + assert.NoError(t, err) +} + +func TestDeleteSandboxDefinitionNotFound(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxDefinitionDelete(t, request) + + writer.WriteHeader(http.StatusNotFound) + r := struct { + Detail string `json:"detail"` + }{ + Detail: "No Definition matches the given query", + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + expected := &kypo.Error{ + ResourceName: "sandbox definition", + Identifier: int64(1), + Err: kypo.ErrNotFound, + } + + actual := c.DeleteSandboxDefinition(context.Background(), 1) + + assert.Equal(t, expected, actual) +} + +func TestDeleteSandboxDefinitionServerError(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxDefinitionDelete(t, request) + + writer.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + c := minimalClient(ts) + expected := &kypo.Error{ + ResourceName: "sandbox definition", + Identifier: int64(1), + Err: fmt.Errorf("status: 500, body: "), + } + actual := c.DeleteSandboxDefinition(context.Background(), 1) + + assert.Equal(t, expected, actual) +} From 6420bf8890bb08c5f8cdc769f820ac20e9c5ab3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Sun, 26 Nov 2023 16:41:00 +0100 Subject: [PATCH 56/64] Add tests for sandbox pool --- pkg/kypo/sandbox_allocation_unit_test.go | 8 + pkg/kypo/sandbox_definition_test.go | 6 +- pkg/kypo/sandbox_pool_test.go | 394 +++++++++++++++++++++++ 3 files changed, 406 insertions(+), 2 deletions(-) create mode 100644 pkg/kypo/sandbox_allocation_unit_test.go create mode 100644 pkg/kypo/sandbox_pool_test.go diff --git a/pkg/kypo/sandbox_allocation_unit_test.go b/pkg/kypo/sandbox_allocation_unit_test.go new file mode 100644 index 0000000..ec28372 --- /dev/null +++ b/pkg/kypo/sandbox_allocation_unit_test.go @@ -0,0 +1,8 @@ +package kypo_test + +type AllocationRequest struct { + Id int `json:"id"` + AllocationUnitId int `json:"allocation_unit_id"` + Created string `json:"created"` + Stages []string `json:"stages"` +} diff --git a/pkg/kypo/sandbox_definition_test.go b/pkg/kypo/sandbox_definition_test.go index a20ebd8..73140df 100644 --- a/pkg/kypo/sandbox_definition_test.go +++ b/pkg/kypo/sandbox_definition_test.go @@ -20,13 +20,15 @@ type User struct { Mail string `json:"mail"` } -var sandboxDefinitionResponse = struct { +type SandboxDefinition struct { Id int `json:"id"` Name string `json:"name"` Url string `json:"url"` Rev string `json:"rev"` CreatedBy User `json:"created_by"` -}{ +} + +var sandboxDefinitionResponse = SandboxDefinition{ Id: 1, Name: "name", Url: "url", diff --git a/pkg/kypo/sandbox_pool_test.go b/pkg/kypo/sandbox_pool_test.go new file mode 100644 index 0000000..0d1fe25 --- /dev/null +++ b/pkg/kypo/sandbox_pool_test.go @@ -0,0 +1,394 @@ +package kypo_test + +import ( + "context" + "encoding/json" + "fmt" + "github.com/stretchr/testify/assert" + "github.com/vydrazde/kypo-go-client/pkg/kypo" + "io" + "net/http" + "net/http/httptest" + "testing" +) + +type SandboxPool struct { + Id int `json:"id"` + Size int `json:"size"` + MaxSize int `json:"max_size"` + LockId *int `json:"lock_id"` + Rev string `json:"rev"` + RevSha string `json:"rev_sha"` + CreatedBy User `json:"created_by"` + HardwareUsage HardwareUsage `json:"hardware_usage"` + Definition SandboxDefinition `json:"definition"` +} + +type HardwareUsage struct { + Vcpu string `json:"vcpu"` + Ram string `json:"ram"` + Instances string `json:"instances"` + Network string `json:"network"` + Subnet string `json:"subnet"` + Port string `json:"port"` +} + +var ( + sandboxPoolResponse = SandboxPool{ + Id: 1, + Size: 0, + MaxSize: 1, + LockId: nil, + Rev: "rev", + RevSha: "rev_sha", + CreatedBy: User{ + Id: 1, + Sub: "sub", + FullName: "full_name", + GivenName: "given_name", + FamilyName: "family_name", + Mail: "mail", + }, + HardwareUsage: HardwareUsage{ + Vcpu: "0.000", + Ram: "0.000", + Instances: "0.000", + Network: "0.000", + Subnet: "0.000", + Port: "0.000", + }, + Definition: SandboxDefinition{ + Id: 1, + Name: "name", + Url: "url", + Rev: "rev", + CreatedBy: User{ + Id: 1, + Sub: "sub", + FullName: "full_name", + GivenName: "given_name", + FamilyName: "family_name", + Mail: "mail", + }, + }, + } + expectedPoolResponse = kypo.SandboxPool{ + Id: 1, + Size: 0, + MaxSize: 1, + LockId: 0, + Rev: "rev", + RevSha: "rev_sha", + CreatedBy: kypo.User{ + Id: 1, + Sub: "sub", + FullName: "full_name", + GivenName: "given_name", + FamilyName: "family_name", + Mail: "mail", + }, + HardwareUsage: kypo.HardwareUsage{ + Vcpu: "0.000", + Ram: "0.000", + Instances: "0.000", + Network: "0.000", + Subnet: "0.000", + Port: "0.000", + }, + Definition: kypo.SandboxDefinition{ + Id: 1, + Name: "name", + Url: "url", + Rev: "rev", + CreatedBy: kypo.User{ + Id: 1, + Sub: "sub", + FullName: "full_name", + GivenName: "given_name", + FamilyName: "family_name", + Mail: "mail", + }, + }, + } +) + +func assertSandboxPoolGet(t *testing.T, request *http.Request) { + assert.Equal(t, "application/json", request.Header.Get("Content-Type")) + assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) + assert.Equal(t, "/kypo-sandbox-service/api/v1/pools/1", request.URL.Path) + assert.Equal(t, "GET", request.Method) +} + +func TestGetSandboxPoolSuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxPoolGet(t, request) + + response, _ := json.Marshal(sandboxPoolResponse) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + + actual, err := c.GetSandboxPool(context.Background(), 1) + + assert.NoError(t, err) + assert.Equal(t, &expectedPoolResponse, actual) +} + +func TestGetSandboxPoolNotFound(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxPoolGet(t, request) + + writer.WriteHeader(http.StatusNotFound) + r := struct { + Detail string `json:"detail"` + }{ + Detail: "No Pool matches the given query", + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := &kypo.Error{ + ResourceName: "sandbox pool", + Identifier: int64(1), + Err: kypo.ErrNotFound, + } + + td, actual := c.GetSandboxPool(context.Background(), 1) + + assert.Nil(t, td) + assert.Equal(t, expected, actual) +} + +func TestGetSandboxPoolServerError(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxPoolGet(t, request) + + writer.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := &kypo.Error{ + ResourceName: "sandbox pool", + Identifier: int64(1), + Err: fmt.Errorf("status: 500, body: "), + } + + td, actual := c.GetSandboxPool(context.Background(), 1) + + assert.Nil(t, td) + assert.Equal(t, expected, actual) +} + +func assertSandboxPoolCreate(t *testing.T, request *http.Request) { + assert.Equal(t, "application/json", request.Header.Get("Content-Type")) + assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) + assert.Equal(t, "/kypo-sandbox-service/api/v1/pools", request.URL.Path) + assert.Equal(t, "POST", request.Method) + + body, err := io.ReadAll(request.Body) + assert.NoError(t, err) + + parsedBody := struct { + DefinitionId int `json:"definition_id"` + MaxSize int `json:"max_size"` + }{} + err = json.Unmarshal(body, &parsedBody) + assert.NoError(t, err) + + assert.Equal(t, 1, parsedBody.DefinitionId) + assert.Equal(t, 1, parsedBody.MaxSize) +} + +func TestCreateSandboxPoolSuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxPoolCreate(t, request) + + writer.WriteHeader(http.StatusCreated) + response, _ := json.Marshal(sandboxPoolResponse) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + + actual, err := c.CreateSandboxPool(context.Background(), 1, 1) + + assert.NoError(t, err) + assert.Equal(t, &expectedPoolResponse, actual) +} + +func TestCreateSandboxPoolServerError(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxPoolCreate(t, request) + + writer.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := &kypo.Error{ + ResourceName: "sandbox pool", + Identifier: "", + Err: fmt.Errorf("status: 500, body: "), + } + + actual, err := c.CreateSandboxPool(context.Background(), 1, 1) + + assert.Nil(t, actual) + assert.Equal(t, expected, err) +} + +func assertSandboxPoolDelete(t *testing.T, request *http.Request) { + assert.Equal(t, "application/json", request.Header.Get("Content-Type")) + assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) + assert.Equal(t, "/kypo-sandbox-service/api/v1/pools/1", request.URL.Path) + assert.Equal(t, "DELETE", request.Method) +} + +func TestDeleteSandboxPoolSuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxPoolDelete(t, request) + + writer.WriteHeader(http.StatusNoContent) + })) + defer ts.Close() + + c := minimalClient(ts) + + err := c.DeleteSandboxPool(context.Background(), 1) + + assert.NoError(t, err) +} + +func TestDeleteSandboxPoolNotFound(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxPoolDelete(t, request) + + writer.WriteHeader(http.StatusNotFound) + r := struct { + Detail string `json:"detail"` + }{ + Detail: "No Pool matches the given query", + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + expected := &kypo.Error{ + ResourceName: "sandbox pool", + Identifier: int64(1), + Err: kypo.ErrNotFound, + } + + actual := c.DeleteSandboxPool(context.Background(), 1) + + assert.Equal(t, expected, actual) +} + +func TestDeleteSandboxPoolServerError(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxPoolDelete(t, request) + + writer.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + c := minimalClient(ts) + expected := &kypo.Error{ + ResourceName: "sandbox pool", + Identifier: int64(1), + Err: fmt.Errorf("status: 500, body: "), + } + actual := c.DeleteSandboxPool(context.Background(), 1) + + assert.Equal(t, expected, actual) +} + +func assertSandboxPoolCleanup(t *testing.T, request *http.Request) { + assert.Equal(t, "application/json", request.Header.Get("Content-Type")) + assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) + assert.Equal(t, "/kypo-sandbox-service/api/v1/pools/1/cleanup-requests", request.URL.Path) + assert.Equal(t, "POST", request.Method) +} + +func TestCleanupSandboxPoolSuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxPoolCleanup(t, request) + + writer.WriteHeader(http.StatusAccepted) + r := []AllocationRequest{ + { + Id: 1, + AllocationUnitId: 1, + Created: "2023-11-26T15:48:41.614625+01:00", + Stages: []string{"IN_QUEUE", "IN_QUEUE", "IN_QUEUE"}, + }, + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + + err := c.CleanupSandboxPool(context.Background(), 1, false) + + assert.NoError(t, err) +} + +func TestCleanupSandboxPoolNotFound(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxPoolCleanup(t, request) + + writer.WriteHeader(http.StatusNotFound) + r := struct { + Detail string `json:"detail"` + }{ + Detail: "The instance of Pool with {'pk': 1} not found.", + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + expected := &kypo.Error{ + ResourceName: "sandbox pool", + Identifier: int64(1), + Err: kypo.ErrNotFound, + } + + actual := c.CleanupSandboxPool(context.Background(), 1, false) + + assert.Equal(t, expected, actual) +} + +func TestCleanupSandboxPoolServerError(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxPoolCleanup(t, request) + + writer.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + c := minimalClient(ts) + expected := &kypo.Error{ + ResourceName: "sandbox pool", + Identifier: int64(1), + Err: fmt.Errorf("status: 500, body: "), + } + actual := c.CleanupSandboxPool(context.Background(), 1, false) + + assert.Equal(t, expected, actual) +} From 724a08ebe413725883e023db2582f2b45d9a4d4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Sun, 26 Nov 2023 17:23:42 +0100 Subject: [PATCH 57/64] Return more information in error when creating pool or allocation unit --- pkg/kypo/sandbox_allocation_unit.go | 4 ++-- pkg/kypo/sandbox_pool.go | 2 +- pkg/kypo/sandbox_pool_test.go | 35 ++++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/pkg/kypo/sandbox_allocation_unit.go b/pkg/kypo/sandbox_allocation_unit.go index df7c29c..a96cb43 100644 --- a/pkg/kypo/sandbox_allocation_unit.go +++ b/pkg/kypo/sandbox_allocation_unit.go @@ -80,7 +80,7 @@ func (c *Client) CreateSandboxAllocationUnits(ctx context.Context, poolId, count return nil, err } - body, _, err := c.doRequestWithRetry(req, http.StatusCreated, "sandbox allocation units", "") + body, _, err := c.doRequestWithRetry(req, http.StatusCreated, "sandbox allocation units", fmt.Sprintf("sandbox pool %d", poolId)) if err != nil { return nil, err } @@ -120,7 +120,7 @@ func (c *Client) CreateSandboxCleanupRequest(ctx context.Context, unitId int64) return nil, err } - body, _, err := c.doRequestWithRetry(req, http.StatusCreated, "sandbox cleanup request", "") + body, _, err := c.doRequestWithRetry(req, http.StatusCreated, "sandbox cleanup request", fmt.Sprintf("sandbox allocation unit %d", unitId)) if err != nil { return nil, err } diff --git a/pkg/kypo/sandbox_pool.go b/pkg/kypo/sandbox_pool.go index 40f1475..ac87bd4 100644 --- a/pkg/kypo/sandbox_pool.go +++ b/pkg/kypo/sandbox_pool.go @@ -67,7 +67,7 @@ func (c *Client) CreateSandboxPool(ctx context.Context, definitionId, maxSize in return nil, err } - body, _, err := c.doRequestWithRetry(req, http.StatusCreated, "sandbox pool", "") + body, _, err := c.doRequestWithRetry(req, http.StatusCreated, "sandbox pool", fmt.Sprintf("sandbox definition %d", definitionId)) if err != nil { return nil, err } diff --git a/pkg/kypo/sandbox_pool_test.go b/pkg/kypo/sandbox_pool_test.go index 0d1fe25..2b09d30 100644 --- a/pkg/kypo/sandbox_pool_test.go +++ b/pkg/kypo/sandbox_pool_test.go @@ -225,6 +225,35 @@ func TestCreateSandboxPoolSuccessful(t *testing.T) { assert.Equal(t, &expectedPoolResponse, actual) } +func TestCreateSandboxPoolNotFound(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxPoolCreate(t, request) + + writer.WriteHeader(http.StatusNotFound) + r := struct { + Detail string `json:"detail"` + }{ + Detail: "No Definition matches the given query", + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := &kypo.Error{ + ResourceName: "sandbox pool", + Identifier: "sandbox definition 1", + Err: kypo.ErrNotFound, + } + + actual, err := c.CreateSandboxPool(context.Background(), 1, 1) + + assert.Nil(t, actual) + assert.Equal(t, expected, err) +} + func TestCreateSandboxPoolServerError(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { assertSandboxPoolCreate(t, request) @@ -237,7 +266,7 @@ func TestCreateSandboxPoolServerError(t *testing.T) { expected := &kypo.Error{ ResourceName: "sandbox pool", - Identifier: "", + Identifier: "sandbox definition 1", Err: fmt.Errorf("status: 500, body: "), } @@ -320,6 +349,10 @@ func assertSandboxPoolCleanup(t *testing.T, request *http.Request) { assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, "/kypo-sandbox-service/api/v1/pools/1/cleanup-requests", request.URL.Path) assert.Equal(t, "POST", request.Method) + + err := request.ParseForm() + assert.NoError(t, err) + assert.Equal(t, "false", request.Form.Get("force")) } func TestCleanupSandboxPoolSuccessful(t *testing.T) { From 8ee751d6ce1d810a54f1640ead7d21192233c174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Sun, 26 Nov 2023 17:37:35 +0100 Subject: [PATCH 58/64] Add simple tests for sandbox allocation unit --- pkg/kypo/sandbox_allocation_unit_test.go | 307 ++++++++++++++++++++++- pkg/kypo/sandbox_pool_test.go | 2 +- 2 files changed, 307 insertions(+), 2 deletions(-) diff --git a/pkg/kypo/sandbox_allocation_unit_test.go b/pkg/kypo/sandbox_allocation_unit_test.go index ec28372..d49947c 100644 --- a/pkg/kypo/sandbox_allocation_unit_test.go +++ b/pkg/kypo/sandbox_allocation_unit_test.go @@ -1,8 +1,313 @@ package kypo_test -type AllocationRequest struct { +import ( + "context" + "encoding/json" + "fmt" + "github.com/stretchr/testify/assert" + "github.com/vydrazde/kypo-go-client/pkg/kypo" + "net/http" + "net/http/httptest" + "testing" +) + +type SandboxAllocationRequest struct { Id int `json:"id"` AllocationUnitId int `json:"allocation_unit_id"` Created string `json:"created"` Stages []string `json:"stages"` } + +type SandboxAllocationUnit struct { + Id int `json:"id"` + PoolId int `json:"pool_id"` + AllocationRequest *SandboxAllocationRequest `json:"allocation_request"` + CleanupRequest *SandboxAllocationRequest `json:"cleanup_request"` + CreatedBy User `json:"created_by"` + Locked bool `json:"locked"` +} + +var ( + sandboxAllocationUnitResponse = SandboxAllocationUnit{ + Id: 1, + PoolId: 1, + AllocationRequest: &SandboxAllocationRequest{ + Id: 1, + AllocationUnitId: 1, + Created: "2023-10-23T11:58:21.757093+02:00", + Stages: []string{"FINISHED", "FINISHED", "FINISHED"}, + }, + CleanupRequest: nil, + CreatedBy: User{ + Id: 1, + Sub: "sub", + FullName: "full_name", + GivenName: "give_name", + FamilyName: "family_name", + Mail: "mail", + }, + Locked: false, + } + expectedSandboxAllocationUnit = kypo.SandboxAllocationUnit{ + Id: 1, + PoolId: 1, + AllocationRequest: kypo.SandboxRequest{ + Id: 1, + AllocationUnitId: 1, + Created: "2023-10-23T11:58:21.757093+02:00", + Stages: []string{"FINISHED", "FINISHED", "FINISHED"}, + }, + CleanupRequest: kypo.SandboxRequest{}, + CreatedBy: kypo.User{ + Id: 1, + Sub: "sub", + FullName: "full_name", + GivenName: "give_name", + FamilyName: "family_name", + Mail: "mail", + }, + Locked: false, + } +) + +func assertSandboxAllocationUnitGet(t *testing.T, request *http.Request) { + assert.Equal(t, "application/json", request.Header.Get("Content-Type")) + assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) + assert.Equal(t, "/kypo-sandbox-service/api/v1/sandbox-allocation-units/1", request.URL.Path) + assert.Equal(t, "GET", request.Method) +} + +func TestGetSandboxAllocationUnitSuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxAllocationUnitGet(t, request) + + response, _ := json.Marshal(sandboxAllocationUnitResponse) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + + actual, err := c.GetSandboxAllocationUnit(context.Background(), 1) + + assert.NoError(t, err) + assert.Equal(t, &expectedSandboxAllocationUnit, actual) +} + +func TestGetSandboxAllocationUnitNotFound(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxAllocationUnitGet(t, request) + + writer.WriteHeader(http.StatusNotFound) + r := struct { + Detail string `json:"detail"` + }{ + Detail: "No SandboxAllocationUnit matches the given query", + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := &kypo.Error{ + ResourceName: "sandbox allocation unit", + Identifier: int64(1), + Err: kypo.ErrNotFound, + } + + td, actual := c.GetSandboxAllocationUnit(context.Background(), 1) + + assert.Nil(t, td) + assert.Equal(t, expected, actual) +} + +func TestGetSandboxAllocationUnitServerError(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxAllocationUnitGet(t, request) + + writer.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := &kypo.Error{ + ResourceName: "sandbox allocation unit", + Identifier: int64(1), + Err: fmt.Errorf("status: 500, body: "), + } + + td, actual := c.GetSandboxAllocationUnit(context.Background(), 1) + + assert.Nil(t, td) + assert.Equal(t, expected, actual) +} + +func assertSandboxAllocationUnitCreate(t *testing.T, request *http.Request) { + assert.Equal(t, "application/json", request.Header.Get("Content-Type")) + assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) + assert.Equal(t, "/kypo-sandbox-service/api/v1/pools/1/sandbox-allocation-units", request.URL.Path) + assert.Equal(t, "POST", request.Method) + + err := request.ParseForm() + assert.NoError(t, err) + assert.Equal(t, "1", request.Form.Get("count")) +} + +func TestCreateSandboxAllocationUnitSuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxAllocationUnitCreate(t, request) + + writer.WriteHeader(http.StatusCreated) + response, _ := json.Marshal([]SandboxAllocationUnit{sandboxAllocationUnitResponse}) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := []kypo.SandboxAllocationUnit{expectedSandboxAllocationUnit} + + actual, err := c.CreateSandboxAllocationUnits(context.Background(), 1, 1) + + assert.NoError(t, err) + assert.Equal(t, expected, actual) +} + +func TestCreateSandboxAllocationUnitNotFound(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxAllocationUnitCreate(t, request) + + writer.WriteHeader(http.StatusNotFound) + r := struct { + Detail string `json:"detail"` + }{ + Detail: "No Pool matches the given query", + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := &kypo.Error{ + ResourceName: "sandbox allocation units", + Identifier: "sandbox pool 1", + Err: kypo.ErrNotFound, + } + + actual, err := c.CreateSandboxAllocationUnits(context.Background(), 1, 1) + + assert.Nil(t, actual) + assert.Equal(t, expected, err) +} + +func TestCreateSandboxAllocationUnitServerError(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxAllocationUnitCreate(t, request) + + writer.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + c := minimalClient(ts) + + expected := &kypo.Error{ + ResourceName: "sandbox allocation units", + Identifier: "sandbox pool 1", + Err: fmt.Errorf("status: 500, body: "), + } + + actual, err := c.CreateSandboxAllocationUnits(context.Background(), 1, 1) + + assert.Nil(t, actual) + assert.Equal(t, expected, err) +} + +func assertSandboxAllocationUnitCleanup(t *testing.T, request *http.Request) { + assert.Equal(t, "application/json", request.Header.Get("Content-Type")) + assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) + assert.Equal(t, "/kypo-sandbox-service/api/v1/sandbox-allocation-units/1/cleanup-request", request.URL.Path) + assert.Equal(t, "POST", request.Method) +} + +func TestCleanupSandboxAllocationUnitSuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxAllocationUnitCleanup(t, request) + + writer.WriteHeader(http.StatusCreated) + r := SandboxAllocationRequest{ + Id: 1, + AllocationUnitId: 1, + Created: "2023-11-26T17:04:20.032500+01:00", + Stages: []string{"IN_QUEUE", "IN_QUEUE", "IN_QUEUE"}, + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + + actual, err := c.CreateSandboxCleanupRequest(context.Background(), 1) + expected := kypo.SandboxRequest{ + Id: 1, + AllocationUnitId: 1, + Created: "2023-11-26T17:04:20.032500+01:00", + Stages: []string{"IN_QUEUE", "IN_QUEUE", "IN_QUEUE"}, + } + + assert.NoError(t, err) + assert.Equal(t, &expected, actual) +} + +func TestCleanupSandboxAllocationUnitNotFound(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxAllocationUnitCleanup(t, request) + + writer.WriteHeader(http.StatusNotFound) + r := struct { + Detail string `json:"detail"` + }{ + Detail: "No SandboxAllocationUnit matches the given query", + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + expected := &kypo.Error{ + ResourceName: "sandbox cleanup request", + Identifier: "sandbox allocation unit 1", + Err: kypo.ErrNotFound, + } + + actual, err := c.CreateSandboxCleanupRequest(context.Background(), 1) + + assert.Nil(t, actual) + assert.Equal(t, expected, err) +} + +func TestCleanupSandboxAllocationUnitServerError(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assertSandboxAllocationUnitCleanup(t, request) + + writer.WriteHeader(http.StatusInternalServerError) + })) + defer ts.Close() + + c := minimalClient(ts) + expected := &kypo.Error{ + ResourceName: "sandbox cleanup request", + Identifier: "sandbox allocation unit 1", + Err: fmt.Errorf("status: 500, body: "), + } + actual, err := c.CreateSandboxCleanupRequest(context.Background(), 1) + + assert.Nil(t, actual) + assert.Equal(t, expected, err) +} diff --git a/pkg/kypo/sandbox_pool_test.go b/pkg/kypo/sandbox_pool_test.go index 2b09d30..fe4b333 100644 --- a/pkg/kypo/sandbox_pool_test.go +++ b/pkg/kypo/sandbox_pool_test.go @@ -360,7 +360,7 @@ func TestCleanupSandboxPoolSuccessful(t *testing.T) { assertSandboxPoolCleanup(t, request) writer.WriteHeader(http.StatusAccepted) - r := []AllocationRequest{ + r := []SandboxAllocationRequest{ { Id: 1, AllocationUnitId: 1, From 53fec3fac7ddc3ed394d67faa8df50aacd780671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Sun, 26 Nov 2023 18:18:08 +0100 Subject: [PATCH 59/64] Return more information when cleanup request await fails --- pkg/kypo/sandbox_allocation_unit.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/kypo/sandbox_allocation_unit.go b/pkg/kypo/sandbox_allocation_unit.go index a96cb43..741a77f 100644 --- a/pkg/kypo/sandbox_allocation_unit.go +++ b/pkg/kypo/sandbox_allocation_unit.go @@ -188,7 +188,8 @@ func (c *Client) CreateSandboxCleanupRequestAwait(ctx context.Context, unitId in return nil } if err == nil && slices.Contains(cleanupRequest.Stages, "FAILED") { - return fmt.Errorf("sandbox cleanup request finished with error") + return &Error{ResourceName: "sandbox cleanup request", Identifier: fmt.Sprintf("sandbox allocation unit %d", unitId), + Err: fmt.Errorf("sandbox cleanup request finished with error")} } return err } From 78e7b603e1d27a2eeb5b49a6c830933ea93489b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Sun, 26 Nov 2023 18:47:09 +0100 Subject: [PATCH 60/64] GetSandboxRequestAnsibleOutputs add newline at the end instead of the beginning --- pkg/kypo/sandbox_allocation_unit.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/kypo/sandbox_allocation_unit.go b/pkg/kypo/sandbox_allocation_unit.go index 741a77f..3a81b44 100644 --- a/pkg/kypo/sandbox_allocation_unit.go +++ b/pkg/kypo/sandbox_allocation_unit.go @@ -244,7 +244,7 @@ func (c *Client) GetSandboxRequestAnsibleOutputs(ctx context.Context, sandboxReq } for _, line := range outputRaw.Results { - output.Result += "\n" + line.Content + output.Result += line.Content + "\n" } return &output, nil From 919aa442684b6cea0af14170167f13a52b58a6ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Sun, 26 Nov 2023 18:47:30 +0100 Subject: [PATCH 61/64] Add more complicated tests for sandbox allocation unit --- pkg/kypo/sandbox_allocation_unit_test.go | 295 +++++++++++++++++++++++ 1 file changed, 295 insertions(+) diff --git a/pkg/kypo/sandbox_allocation_unit_test.go b/pkg/kypo/sandbox_allocation_unit_test.go index d49947c..6d1e627 100644 --- a/pkg/kypo/sandbox_allocation_unit_test.go +++ b/pkg/kypo/sandbox_allocation_unit_test.go @@ -227,6 +227,97 @@ func TestCreateSandboxAllocationUnitServerError(t *testing.T) { assert.Equal(t, expected, err) } +func assertSandboxRequest(t *testing.T, request *http.Request, requestType string) { + assert.Equal(t, "application/json", request.Header.Get("Content-Type")) + assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) + assert.Equal(t, fmt.Sprintf("/kypo-sandbox-service/api/v1/sandbox-allocation-units/1/%s-request", requestType), request.URL.Path) + assert.Equal(t, "GET", request.Method) +} + +func TestCreateSandboxAllocationUnitAwaitSuccessful(t *testing.T) { + counter := 0 + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + counter++ + if counter == 1 { + assertSandboxAllocationUnitCreate(t, request) + + writer.WriteHeader(http.StatusCreated) + response, _ := json.Marshal([]SandboxAllocationUnit{sandboxAllocationUnitResponse}) + _, _ = fmt.Fprint(writer, string(response)) + return + } + + assertSandboxRequest(t, request, "allocation") + r := SandboxAllocationRequest{ + Id: 1, + AllocationUnitId: 1, + Created: "2023-11-26T17:04:20.032500+01:00", + Stages: []string{"FINISHED", "FINISHED", "FINISHED"}, + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + expected := expectedSandboxAllocationUnit + expected.AllocationRequest.Stages = []string{"FINISHED", "FINISHED", "FINISHED"} + expected.AllocationRequest.Created = "2023-11-26T17:04:20.032500+01:00" + + actual, err := c.CreateSandboxAllocationUnitAwait(context.Background(), 1, 1) + + assert.NoError(t, err) + assert.Equal(t, &expected, actual) + assert.Equal(t, 2, counter) +} + +func TestCreateSandboxAllocationUnitAwaitSuccessfulWithDelay(t *testing.T) { + counter := 0 + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + counter++ + if counter == 1 { + assertSandboxAllocationUnitCreate(t, request) + + writer.WriteHeader(http.StatusCreated) + response, _ := json.Marshal([]SandboxAllocationUnit{sandboxAllocationUnitResponse}) + _, _ = fmt.Fprint(writer, string(response)) + return + } + + assertSandboxRequest(t, request, "allocation") + r := SandboxAllocationRequest{ + Id: 1, + AllocationUnitId: 1, + Created: "2023-11-26T17:04:20.032500+01:00", + Stages: []string{"FINISHED", "FINISHED", "RUNNING"}, + } + + switch counter { + case 2: + r.Stages = []string{"FINISHED", "FINISHED", "IN_QUEUE"} + case 3: + r.Stages = []string{"FINISHED", "FINISHED", "RUNNING"} + default: + r.Stages = []string{"FINISHED", "FINISHED", "FINISHED"} + } + + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + expected := expectedSandboxAllocationUnit + expected.AllocationRequest.Stages = []string{"FINISHED", "FINISHED", "FINISHED"} + expected.AllocationRequest.Created = "2023-11-26T17:04:20.032500+01:00" + + actual, err := c.CreateSandboxAllocationUnitAwait(context.Background(), 1, 1) + + assert.NoError(t, err) + assert.Equal(t, &expected, actual) + assert.Equal(t, 4, counter) +} + func assertSandboxAllocationUnitCleanup(t *testing.T, request *http.Request) { assert.Equal(t, "application/json", request.Header.Get("Content-Type")) assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) @@ -311,3 +402,207 @@ func TestCleanupSandboxAllocationUnitServerError(t *testing.T) { assert.Nil(t, actual) assert.Equal(t, expected, err) } + +func TestCleanupSandboxAllocationUnitAwaitSuccessful(t *testing.T) { + counter := 0 + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + counter++ + if counter == 1 { + assertSandboxAllocationUnitCleanup(t, request) + + writer.WriteHeader(http.StatusCreated) + r := SandboxAllocationRequest{ + Id: 1, + AllocationUnitId: 1, + Created: "2023-11-26T17:04:20.032500+01:00", + Stages: []string{"IN_QUEUE", "IN_QUEUE", "IN_QUEUE"}, + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + return + } + + assertSandboxRequest(t, request, "cleanup") + r := SandboxAllocationRequest{ + Id: 1, + AllocationUnitId: 1, + Created: "2023-11-26T17:04:20.032500+01:00", + Stages: []string{"FINISHED", "FINISHED", "FINISHED"}, + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + + err := c.CreateSandboxCleanupRequestAwait(context.Background(), 1, 1) + + assert.NoError(t, err) + assert.Equal(t, 2, counter) +} + +func TestCleanupSandboxAllocationUnitAwaitSuccessfulWithDelay(t *testing.T) { + counter := 0 + r := SandboxAllocationRequest{ + Id: 1, + AllocationUnitId: 1, + Created: "2023-11-26T17:04:20.032500+01:00", + Stages: []string{"IN_QUEUE", "IN_QUEUE", "IN_QUEUE"}, + } + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + counter++ + if counter == 1 { + assertSandboxAllocationUnitCleanup(t, request) + + writer.WriteHeader(http.StatusCreated) + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + return + } + + assertSandboxRequest(t, request, "cleanup") + + switch counter { + case 2: + r.Stages = []string{"FINISHED", "FINISHED", "IN_QUEUE"} + case 3: + r.Stages = []string{"FINISHED", "FINISHED", "RUNNING"} + default: + r.Stages = []string{"FINISHED", "FINISHED", "FINISHED"} + } + + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + + err := c.CreateSandboxCleanupRequestAwait(context.Background(), 1, 1) + + assert.NoError(t, err) + assert.Equal(t, 4, counter) +} + +func TestCleanupSandboxAllocationUnitAwaitFailed(t *testing.T) { + counter := 0 + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + counter++ + if counter == 1 { + assertSandboxAllocationUnitCleanup(t, request) + + writer.WriteHeader(http.StatusCreated) + r := SandboxAllocationRequest{ + Id: 1, + AllocationUnitId: 1, + Created: "2023-11-26T17:04:20.032500+01:00", + Stages: []string{"IN_QUEUE", "IN_QUEUE", "IN_QUEUE"}, + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + return + } + + assertSandboxRequest(t, request, "cleanup") + r := SandboxAllocationRequest{ + Id: 1, + AllocationUnitId: 1, + Created: "2023-11-26T17:04:20.032500+01:00", + Stages: []string{"FINISHED", "FINISHED", "FAILED"}, + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + expected := &kypo.Error{ + ResourceName: "sandbox cleanup request", + Identifier: "sandbox allocation unit 1", + Err: fmt.Errorf("sandbox cleanup request finished with error"), + } + + err := c.CreateSandboxCleanupRequestAwait(context.Background(), 1, 1) + + assert.Equal(t, expected, err) + assert.Equal(t, 2, counter) +} + +func TestCancelSandboxAllocationRequestSuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assert.Equal(t, "application/json", request.Header.Get("Content-Type")) + assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) + assert.Equal(t, "/kypo-sandbox-service/api/v1/allocation-requests/1/cancel", request.URL.Path) + assert.Equal(t, "PATCH", request.Method) + })) + defer ts.Close() + + c := minimalClient(ts) + + err := c.CancelSandboxAllocationRequest(context.Background(), 1) + + assert.NoError(t, err) +} + +func TestGetSandboxAllocationRequestOutputSuccessful(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + assert.Equal(t, "application/json", request.Header.Get("Content-Type")) + assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) + assert.Equal(t, "/kypo-sandbox-service/api/v1/allocation-requests/1/stages/user-ansible/outputs", request.URL.Path) + assert.Equal(t, "GET", request.Method) + + err := request.ParseForm() + assert.NoError(t, err) + assert.Equal(t, "1", request.Form.Get("page")) + assert.Equal(t, "10", request.Form.Get("page_size")) + + type Content struct { + Content string `json:"content"` + } + r := struct { + Page int `json:"page"` + PageSize int `json:"page_size"` + PageCount int `json:"page_count"` + Count int `json:"count"` + TotalCount int `json:"total_count"` + Results []Content `json:"results"` + }{ + Page: 1, + PageSize: 10, + PageCount: 10, + Count: 10, + TotalCount: 100, + Results: []Content{ + {Content: "1"}, + {Content: "2"}, + {Content: "3"}, + {Content: "4"}, + {Content: "5"}, + {Content: "6"}, + {Content: "7"}, + {Content: "8"}, + {Content: "9"}, + {Content: "10"}, + }, + } + response, _ := json.Marshal(r) + _, _ = fmt.Fprint(writer, string(response)) + })) + defer ts.Close() + + c := minimalClient(ts) + expected := kypo.SandboxRequestStageOutput{ + Page: 1, + PageSize: 10, + PageCount: 10, + Count: 10, + TotalCount: 100, + Result: "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n", + } + + actual, err := c.GetSandboxRequestAnsibleOutputs(context.Background(), 1, 1, 10, "user-ansible") + + assert.NoError(t, err) + assert.Equal(t, &expected, actual) +} From fb9d1469ab90d4875341cd30f8510a70d86806b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Mon, 27 Nov 2023 13:24:52 +0100 Subject: [PATCH 62/64] Add documentation for training definitions --- pkg/kypo/training_definition.go | 6 ++++++ pkg/kypo/training_definition_adaptive.go | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/pkg/kypo/training_definition.go b/pkg/kypo/training_definition.go index 1bfe207..808f1af 100644 --- a/pkg/kypo/training_definition.go +++ b/pkg/kypo/training_definition.go @@ -13,6 +13,7 @@ type TrainingDefinition struct { Content string `json:"content" tfsdk:"content"` } +// GetTrainingDefinition reads the definition given by definitionID. func (c *Client) GetTrainingDefinition(ctx context.Context, definitionID int64) (*TrainingDefinition, error) { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/kypo-rest-training/api/v1/exports/training-definitions/%d", c.Endpoint, definitionID), nil) if err != nil { @@ -33,6 +34,10 @@ func (c *Client) GetTrainingDefinition(ctx context.Context, definitionID int64) return &definition, nil } +// CreateTrainingDefinition imports a JSON string content as a training definition. +// The JSON string must be a previously exported training definition. +// Since KYPO returns an answer with a definition in a different format than the exported definition, +// only the Id is read and the input content is set as the returned TrainingDefinition.Content. func (c *Client) CreateTrainingDefinition(ctx context.Context, content string) (*TrainingDefinition, error) { req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/kypo-rest-training/api/v1/imports/training-definitions", c.Endpoint), strings.NewReader(content)) if err != nil { @@ -61,6 +66,7 @@ func (c *Client) CreateTrainingDefinition(ctx context.Context, content string) ( return &definition, nil } +// DeleteTrainingDefinition deletes the definition given by definitionID. func (c *Client) DeleteTrainingDefinition(ctx context.Context, definitionID int64) error { req, err := http.NewRequestWithContext(ctx, "DELETE", fmt.Sprintf("%s/kypo-rest-training/api/v1/training-definitions/%d", c.Endpoint, definitionID), nil) if err != nil { diff --git a/pkg/kypo/training_definition_adaptive.go b/pkg/kypo/training_definition_adaptive.go index 360d2e3..9e91381 100644 --- a/pkg/kypo/training_definition_adaptive.go +++ b/pkg/kypo/training_definition_adaptive.go @@ -13,6 +13,7 @@ type TrainingDefinitionAdaptive struct { Content string `json:"content" tfsdk:"content"` } +// GetTrainingDefinitionAdaptive reads the adaptive definition given by definitionID. func (c *Client) GetTrainingDefinitionAdaptive(ctx context.Context, definitionID int64) (*TrainingDefinitionAdaptive, error) { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/kypo-adaptive-training/api/v1/exports/training-definitions/%d", c.Endpoint, definitionID), nil) if err != nil { @@ -33,6 +34,10 @@ func (c *Client) GetTrainingDefinitionAdaptive(ctx context.Context, definitionID return &definition, nil } +// CreateTrainingDefinitionAdaptive imports a JSON string content as a adaptive training definition. +// The JSON string must be a previously exported adaptive training definition. +// Since KYPO returns an answer with a definition in a different format than the exported definition, +// only the Id is read and the input content is set as the returned TrainingDefinition.Content. func (c *Client) CreateTrainingDefinitionAdaptive(ctx context.Context, content string) (*TrainingDefinitionAdaptive, error) { req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/kypo-adaptive-training/api/v1/imports/training-definitions", c.Endpoint), strings.NewReader(content)) if err != nil { @@ -61,6 +66,7 @@ func (c *Client) CreateTrainingDefinitionAdaptive(ctx context.Context, content s return &definition, nil } +// DeleteTrainingDefinitionAdaptive deletes the adaptive definition given by definitionID. func (c *Client) DeleteTrainingDefinitionAdaptive(ctx context.Context, definitionID int64) error { req, err := http.NewRequestWithContext(ctx, "DELETE", fmt.Sprintf("%s/kypo-adaptive-training/api/v1/training-definitions/%d", c.Endpoint, definitionID), nil) if err != nil { From 5ccf77679f83d2307890d052a58c8a8af08636c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Tue, 28 Nov 2023 10:34:56 +0100 Subject: [PATCH 63/64] Add README --- README.md | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5ad6b7a..0140932 100644 --- a/README.md +++ b/README.md @@ -1 +1,34 @@ -# kypo-go-client \ No newline at end of file +# kypo-go-client +A [KYPO CRP](https://docs.crp.kypo.muni.cz/) client library written in Go. + +## Supported API calls: +- Login to CSIRT-MU Dummy OIDC and Keycloak +- Sandbox Definition - Get, Create, Delete +- Sandbox Pool - Get, Create, Delete, Cleanup +- Sandbox Allocation Unit - Get, CreateAllocation, CreateAllocationAwait, CancelAllocation, CreateCleanup, CreateCleanupAwait, GetAllocationOutput +- Training Definition - Get, Create, Delete +- Training Definition Adaptive - Get, Create, Delete + +## Usage +```go +import "github.com/vydrazde/kypo-go-client" +``` + +Create a client with username and password: +```go +client, err := kypo.NewClient("https://your.kypo.ex", "KYPO-Client", "username", "password") +if err != nil { + log.Fatalf("Failed to create KYPO client: %v", err) +} +``` + +Use the client to create a sandbox definition: +```go +sandboxDefinition, err := client.CreateSandboxDefinition(context.Background(), + "git@gitlab.ics.muni.cz:kypo-library/content/kypo-library-demo-training.git", "master") + +if err != nil { + log.Fatalf("Failed to create sandbox definition: %v", err) +} +``` + From ecc5236169e1bbeddf390cd6a1e3efd726317b81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Vydra?= <485514@mail.muni.cz> Date: Sun, 17 Dec 2023 20:59:14 +0100 Subject: [PATCH 64/64] Use http constants for methods --- pkg/kypo/auth.go | 8 ++++---- pkg/kypo/auth_test.go | 2 +- pkg/kypo/sandbox_allocation_unit.go | 12 ++++++------ pkg/kypo/sandbox_allocation_unit_test.go | 12 ++++++------ pkg/kypo/sandbox_definition.go | 6 +++--- pkg/kypo/sandbox_definition_test.go | 6 +++--- pkg/kypo/sandbox_pool.go | 8 ++++---- pkg/kypo/sandbox_pool_test.go | 8 ++++---- pkg/kypo/training_definition.go | 6 +++--- pkg/kypo/training_definition_adaptive.go | 6 +++--- pkg/kypo/training_definition_adaptive_test.go | 6 +++--- pkg/kypo/training_definition_test.go | 6 +++--- 12 files changed, 43 insertions(+), 43 deletions(-) diff --git a/pkg/kypo/auth.go b/pkg/kypo/auth.go index 7f554ca..cd74947 100644 --- a/pkg/kypo/auth.go +++ b/pkg/kypo/auth.go @@ -47,7 +47,7 @@ func (c *Client) authorize(httpClient http.Client) (string, error) { query.Add("scope", "openid email profile") query.Add("redirect_uri", c.Endpoint) - req, err := http.NewRequest("GET", fmt.Sprintf("%s/csirtmu-dummy-issuer-server/authorize?%s", + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/csirtmu-dummy-issuer-server/authorize?%s", c.Endpoint, query.Encode()), nil) if err != nil { return "", err @@ -95,7 +95,7 @@ func (c *Client) login(httpClient http.Client, csrf string) (string, string, err query.Add("_csrf", csrf) query.Add("submit", "Login") - req, err := http.NewRequest("POST", fmt.Sprintf("%s/csirtmu-dummy-issuer-server/login", + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/csirtmu-dummy-issuer-server/login", c.Endpoint), strings.NewReader(query.Encode())) if err != nil { return "", "", err @@ -144,7 +144,7 @@ func (c *Client) authorizeFirstTime(httpClient http.Client, csrf string) (string query.Add("authorize", "Authorize") query.Add("_csrf", csrf) - req, err := http.NewRequest("POST", fmt.Sprintf("%s/csirtmu-dummy-issuer-server/authorize", + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/csirtmu-dummy-issuer-server/authorize", c.Endpoint), strings.NewReader(query.Encode())) if err != nil { return "", err @@ -178,7 +178,7 @@ func (c *Client) authenticateKeycloak(ctx context.Context) (err error) { query.Add("client_id", c.ClientID) query.Add("grant_type", "password") - req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/keycloak/realms/KYPO/protocol/openid-connect/token", + req, err := http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("%s/keycloak/realms/KYPO/protocol/openid-connect/token", c.Endpoint), strings.NewReader(query.Encode())) if err != nil { return diff --git a/pkg/kypo/auth_test.go b/pkg/kypo/auth_test.go index 47b6381..03bef49 100644 --- a/pkg/kypo/auth_test.go +++ b/pkg/kypo/auth_test.go @@ -15,7 +15,7 @@ import ( func assertRequestToKeycloak(t *testing.T, request *http.Request) { assert.Equal(t, "application/x-www-form-urlencoded", request.Header.Get("Content-Type")) assert.Equal(t, "/keycloak/realms/KYPO/protocol/openid-connect/token", request.URL.Path) - assert.Equal(t, "POST", request.Method) + assert.Equal(t, http.MethodPost, request.Method) err := request.ParseForm() assert.NoError(t, err) diff --git a/pkg/kypo/sandbox_allocation_unit.go b/pkg/kypo/sandbox_allocation_unit.go index 3a81b44..fba359f 100644 --- a/pkg/kypo/sandbox_allocation_unit.go +++ b/pkg/kypo/sandbox_allocation_unit.go @@ -53,7 +53,7 @@ type outputLine struct { // GetSandboxAllocationUnit reads a sandbox allocation unit based on its id. func (c *Client) GetSandboxAllocationUnit(ctx context.Context, unitId int64) (*SandboxAllocationUnit, error) { - req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d", c.Endpoint, unitId), nil) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d", c.Endpoint, unitId), nil) if err != nil { return nil, err } @@ -75,7 +75,7 @@ func (c *Client) GetSandboxAllocationUnit(ctx context.Context, unitId int64) (*S // CreateSandboxAllocationUnits starts the allocation of `count` sandboxes in the sandbox pool specified by `poolId`. func (c *Client) CreateSandboxAllocationUnits(ctx context.Context, poolId, count int64) ([]SandboxAllocationUnit, error) { - req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d/sandbox-allocation-units?count=%d", c.Endpoint, poolId, count), nil) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d/sandbox-allocation-units?count=%d", c.Endpoint, poolId, count), nil) if err != nil { return nil, err } @@ -115,7 +115,7 @@ func (c *Client) CreateSandboxAllocationUnitAwait(ctx context.Context, poolId in // CreateSandboxCleanupRequest starts a cleanup request for the specified sandbox allocation unit. func (c *Client) CreateSandboxCleanupRequest(ctx context.Context, unitId int64) (*SandboxRequest, error) { - req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d/cleanup-request", c.Endpoint, unitId), nil) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d/cleanup-request", c.Endpoint, unitId), nil) if err != nil { return nil, err } @@ -144,7 +144,7 @@ func (c *Client) PollRequestFinished(ctx context.Context, unitId int64, pollTime case <-ctx.Done(): return nil, ctx.Err() case <-ticker.C: - req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d/%s-request", c.Endpoint, unitId, requestType), nil) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s/kypo-sandbox-service/api/v1/sandbox-allocation-units/%d/%s-request", c.Endpoint, unitId, requestType), nil) if err != nil { return nil, err } @@ -196,7 +196,7 @@ func (c *Client) CreateSandboxCleanupRequestAwait(ctx context.Context, unitId in // CancelSandboxAllocationRequest sends a request to cancel the given allocation request. func (c *Client) CancelSandboxAllocationRequest(ctx context.Context, allocationRequestId int64) error { - req, err := http.NewRequestWithContext(ctx, "PATCH", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/allocation-requests/%d/cancel", c.Endpoint, allocationRequestId), nil) + req, err := http.NewRequestWithContext(ctx, http.MethodPatch, fmt.Sprintf("%s/kypo-sandbox-service/api/v1/allocation-requests/%d/cancel", c.Endpoint, allocationRequestId), nil) if err != nil { return err } @@ -216,7 +216,7 @@ func (c *Client) GetSandboxRequestAnsibleOutputs(ctx context.Context, sandboxReq query.Add("page", strconv.FormatInt(page, 10)) query.Add("page_size", strconv.FormatInt(pageSize, 10)) - req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf( + req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf( "%s/kypo-sandbox-service/api/v1/allocation-requests/%d/stages/%s/outputs?%s", c.Endpoint, sandboxRequestId, outputType, query.Encode()), nil) if err != nil { return nil, err diff --git a/pkg/kypo/sandbox_allocation_unit_test.go b/pkg/kypo/sandbox_allocation_unit_test.go index 6d1e627..d51b1fa 100644 --- a/pkg/kypo/sandbox_allocation_unit_test.go +++ b/pkg/kypo/sandbox_allocation_unit_test.go @@ -74,7 +74,7 @@ func assertSandboxAllocationUnitGet(t *testing.T, request *http.Request) { assert.Equal(t, "application/json", request.Header.Get("Content-Type")) assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, "/kypo-sandbox-service/api/v1/sandbox-allocation-units/1", request.URL.Path) - assert.Equal(t, "GET", request.Method) + assert.Equal(t, http.MethodGet, request.Method) } func TestGetSandboxAllocationUnitSuccessful(t *testing.T) { @@ -149,7 +149,7 @@ func assertSandboxAllocationUnitCreate(t *testing.T, request *http.Request) { assert.Equal(t, "application/json", request.Header.Get("Content-Type")) assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, "/kypo-sandbox-service/api/v1/pools/1/sandbox-allocation-units", request.URL.Path) - assert.Equal(t, "POST", request.Method) + assert.Equal(t, http.MethodPost, request.Method) err := request.ParseForm() assert.NoError(t, err) @@ -231,7 +231,7 @@ func assertSandboxRequest(t *testing.T, request *http.Request, requestType strin assert.Equal(t, "application/json", request.Header.Get("Content-Type")) assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, fmt.Sprintf("/kypo-sandbox-service/api/v1/sandbox-allocation-units/1/%s-request", requestType), request.URL.Path) - assert.Equal(t, "GET", request.Method) + assert.Equal(t, http.MethodGet, request.Method) } func TestCreateSandboxAllocationUnitAwaitSuccessful(t *testing.T) { @@ -322,7 +322,7 @@ func assertSandboxAllocationUnitCleanup(t *testing.T, request *http.Request) { assert.Equal(t, "application/json", request.Header.Get("Content-Type")) assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, "/kypo-sandbox-service/api/v1/sandbox-allocation-units/1/cleanup-request", request.URL.Path) - assert.Equal(t, "POST", request.Method) + assert.Equal(t, http.MethodPost, request.Method) } func TestCleanupSandboxAllocationUnitSuccessful(t *testing.T) { @@ -534,7 +534,7 @@ func TestCancelSandboxAllocationRequestSuccessful(t *testing.T) { assert.Equal(t, "application/json", request.Header.Get("Content-Type")) assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, "/kypo-sandbox-service/api/v1/allocation-requests/1/cancel", request.URL.Path) - assert.Equal(t, "PATCH", request.Method) + assert.Equal(t, http.MethodPatch, request.Method) })) defer ts.Close() @@ -550,7 +550,7 @@ func TestGetSandboxAllocationRequestOutputSuccessful(t *testing.T) { assert.Equal(t, "application/json", request.Header.Get("Content-Type")) assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, "/kypo-sandbox-service/api/v1/allocation-requests/1/stages/user-ansible/outputs", request.URL.Path) - assert.Equal(t, "GET", request.Method) + assert.Equal(t, http.MethodGet, request.Method) err := request.ParseForm() assert.NoError(t, err) diff --git a/pkg/kypo/sandbox_definition.go b/pkg/kypo/sandbox_definition.go index a57f0c9..3e91116 100644 --- a/pkg/kypo/sandbox_definition.go +++ b/pkg/kypo/sandbox_definition.go @@ -23,7 +23,7 @@ type sandboxDefinitionRequest struct { // GetSandboxDefinition reads the given sandbox definition. func (c *Client) GetSandboxDefinition(ctx context.Context, definitionID int64) (*SandboxDefinition, error) { - req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions/%d", c.Endpoint, definitionID), nil) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions/%d", c.Endpoint, definitionID), nil) if err != nil { return nil, err } @@ -51,7 +51,7 @@ func (c *Client) CreateSandboxDefinition(ctx context.Context, url, rev string) ( return nil, err } - req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions", c.Endpoint), strings.NewReader(string(requestBody))) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions", c.Endpoint), strings.NewReader(string(requestBody))) if err != nil { return nil, err } @@ -72,7 +72,7 @@ func (c *Client) CreateSandboxDefinition(ctx context.Context, url, rev string) ( // DeleteSandboxDefinition deletes the given sandbox definition. func (c *Client) DeleteSandboxDefinition(ctx context.Context, definitionID int64) error { - req, err := http.NewRequestWithContext(ctx, "DELETE", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions/%d", c.Endpoint, definitionID), nil) + req, err := http.NewRequestWithContext(ctx, http.MethodDelete, fmt.Sprintf("%s/kypo-sandbox-service/api/v1/definitions/%d", c.Endpoint, definitionID), nil) if err != nil { return err } diff --git a/pkg/kypo/sandbox_definition_test.go b/pkg/kypo/sandbox_definition_test.go index 73140df..ad30ed4 100644 --- a/pkg/kypo/sandbox_definition_test.go +++ b/pkg/kypo/sandbox_definition_test.go @@ -47,7 +47,7 @@ func assertSandboxDefinitionGet(t *testing.T, request *http.Request) { assert.Equal(t, "application/json", request.Header.Get("Content-Type")) assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, "/kypo-sandbox-service/api/v1/definitions/1", request.URL.Path) - assert.Equal(t, "GET", request.Method) + assert.Equal(t, http.MethodGet, request.Method) } func TestGetSandboxDefinitionSuccessful(t *testing.T) { @@ -137,7 +137,7 @@ func assertSandboxDefinitionCreate(t *testing.T, request *http.Request) { assert.Equal(t, "application/json", request.Header.Get("Content-Type")) assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, "/kypo-sandbox-service/api/v1/definitions", request.URL.Path) - assert.Equal(t, "POST", request.Method) + assert.Equal(t, http.MethodPost, request.Method) } func TestCreateSandboxDefinitionSuccessful(t *testing.T) { @@ -199,7 +199,7 @@ func assertSandboxDefinitionDelete(t *testing.T, request *http.Request) { assert.Equal(t, "application/json", request.Header.Get("Content-Type")) assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, "/kypo-sandbox-service/api/v1/definitions/1", request.URL.Path) - assert.Equal(t, "DELETE", request.Method) + assert.Equal(t, http.MethodDelete, request.Method) } func TestDeleteSandboxDefinitionSuccessful(t *testing.T) { diff --git a/pkg/kypo/sandbox_pool.go b/pkg/kypo/sandbox_pool.go index ac87bd4..9a37387 100644 --- a/pkg/kypo/sandbox_pool.go +++ b/pkg/kypo/sandbox_pool.go @@ -36,7 +36,7 @@ type HardwareUsage struct { // GetSandboxPool reads the given sandbox pool. func (c *Client) GetSandboxPool(ctx context.Context, poolId int64) (*SandboxPool, error) { - req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d", c.Endpoint, poolId), nil) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d", c.Endpoint, poolId), nil) if err != nil { return nil, err } @@ -62,7 +62,7 @@ func (c *Client) CreateSandboxPool(ctx context.Context, definitionId, maxSize in return nil, err } - req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools", c.Endpoint), strings.NewReader(string(requestBody))) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools", c.Endpoint), strings.NewReader(string(requestBody))) if err != nil { return nil, err } @@ -83,7 +83,7 @@ func (c *Client) CreateSandboxPool(ctx context.Context, definitionId, maxSize in // DeleteSandboxPool deletes the given sandbox pool. func (c *Client) DeleteSandboxPool(ctx context.Context, poolId int64) error { - req, err := http.NewRequestWithContext(ctx, "DELETE", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d", c.Endpoint, poolId), nil) + req, err := http.NewRequestWithContext(ctx, http.MethodDelete, fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d", c.Endpoint, poolId), nil) if err != nil { return err } @@ -98,7 +98,7 @@ func (c *Client) DeleteSandboxPool(ctx context.Context, poolId int64) error { // CleanupSandboxPool creates a cleanup request for all allocation units in the pool. func (c *Client) CleanupSandboxPool(ctx context.Context, poolId int64, force bool) error { - req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d/cleanup-requests?force=%s", + req, err := http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("%s/kypo-sandbox-service/api/v1/pools/%d/cleanup-requests?force=%s", c.Endpoint, poolId, boolToString(force)), nil) if err != nil { return err diff --git a/pkg/kypo/sandbox_pool_test.go b/pkg/kypo/sandbox_pool_test.go index fe4b333..3313d05 100644 --- a/pkg/kypo/sandbox_pool_test.go +++ b/pkg/kypo/sandbox_pool_test.go @@ -116,7 +116,7 @@ func assertSandboxPoolGet(t *testing.T, request *http.Request) { assert.Equal(t, "application/json", request.Header.Get("Content-Type")) assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, "/kypo-sandbox-service/api/v1/pools/1", request.URL.Path) - assert.Equal(t, "GET", request.Method) + assert.Equal(t, http.MethodGet, request.Method) } func TestGetSandboxPoolSuccessful(t *testing.T) { @@ -191,7 +191,7 @@ func assertSandboxPoolCreate(t *testing.T, request *http.Request) { assert.Equal(t, "application/json", request.Header.Get("Content-Type")) assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, "/kypo-sandbox-service/api/v1/pools", request.URL.Path) - assert.Equal(t, "POST", request.Method) + assert.Equal(t, http.MethodPost, request.Method) body, err := io.ReadAll(request.Body) assert.NoError(t, err) @@ -280,7 +280,7 @@ func assertSandboxPoolDelete(t *testing.T, request *http.Request) { assert.Equal(t, "application/json", request.Header.Get("Content-Type")) assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, "/kypo-sandbox-service/api/v1/pools/1", request.URL.Path) - assert.Equal(t, "DELETE", request.Method) + assert.Equal(t, http.MethodDelete, request.Method) } func TestDeleteSandboxPoolSuccessful(t *testing.T) { @@ -348,7 +348,7 @@ func assertSandboxPoolCleanup(t *testing.T, request *http.Request) { assert.Equal(t, "application/json", request.Header.Get("Content-Type")) assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, "/kypo-sandbox-service/api/v1/pools/1/cleanup-requests", request.URL.Path) - assert.Equal(t, "POST", request.Method) + assert.Equal(t, http.MethodPost, request.Method) err := request.ParseForm() assert.NoError(t, err) diff --git a/pkg/kypo/training_definition.go b/pkg/kypo/training_definition.go index 808f1af..0e57d3a 100644 --- a/pkg/kypo/training_definition.go +++ b/pkg/kypo/training_definition.go @@ -15,7 +15,7 @@ type TrainingDefinition struct { // GetTrainingDefinition reads the definition given by definitionID. func (c *Client) GetTrainingDefinition(ctx context.Context, definitionID int64) (*TrainingDefinition, error) { - req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/kypo-rest-training/api/v1/exports/training-definitions/%d", c.Endpoint, definitionID), nil) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s/kypo-rest-training/api/v1/exports/training-definitions/%d", c.Endpoint, definitionID), nil) if err != nil { return nil, err } @@ -39,7 +39,7 @@ func (c *Client) GetTrainingDefinition(ctx context.Context, definitionID int64) // Since KYPO returns an answer with a definition in a different format than the exported definition, // only the Id is read and the input content is set as the returned TrainingDefinition.Content. func (c *Client) CreateTrainingDefinition(ctx context.Context, content string) (*TrainingDefinition, error) { - req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/kypo-rest-training/api/v1/imports/training-definitions", c.Endpoint), strings.NewReader(content)) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("%s/kypo-rest-training/api/v1/imports/training-definitions", c.Endpoint), strings.NewReader(content)) if err != nil { return nil, err } @@ -68,7 +68,7 @@ func (c *Client) CreateTrainingDefinition(ctx context.Context, content string) ( // DeleteTrainingDefinition deletes the definition given by definitionID. func (c *Client) DeleteTrainingDefinition(ctx context.Context, definitionID int64) error { - req, err := http.NewRequestWithContext(ctx, "DELETE", fmt.Sprintf("%s/kypo-rest-training/api/v1/training-definitions/%d", c.Endpoint, definitionID), nil) + req, err := http.NewRequestWithContext(ctx, http.MethodDelete, fmt.Sprintf("%s/kypo-rest-training/api/v1/training-definitions/%d", c.Endpoint, definitionID), nil) if err != nil { return err } diff --git a/pkg/kypo/training_definition_adaptive.go b/pkg/kypo/training_definition_adaptive.go index 9e91381..9a97c0d 100644 --- a/pkg/kypo/training_definition_adaptive.go +++ b/pkg/kypo/training_definition_adaptive.go @@ -15,7 +15,7 @@ type TrainingDefinitionAdaptive struct { // GetTrainingDefinitionAdaptive reads the adaptive definition given by definitionID. func (c *Client) GetTrainingDefinitionAdaptive(ctx context.Context, definitionID int64) (*TrainingDefinitionAdaptive, error) { - req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/kypo-adaptive-training/api/v1/exports/training-definitions/%d", c.Endpoint, definitionID), nil) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s/kypo-adaptive-training/api/v1/exports/training-definitions/%d", c.Endpoint, definitionID), nil) if err != nil { return nil, err } @@ -39,7 +39,7 @@ func (c *Client) GetTrainingDefinitionAdaptive(ctx context.Context, definitionID // Since KYPO returns an answer with a definition in a different format than the exported definition, // only the Id is read and the input content is set as the returned TrainingDefinition.Content. func (c *Client) CreateTrainingDefinitionAdaptive(ctx context.Context, content string) (*TrainingDefinitionAdaptive, error) { - req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/kypo-adaptive-training/api/v1/imports/training-definitions", c.Endpoint), strings.NewReader(content)) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("%s/kypo-adaptive-training/api/v1/imports/training-definitions", c.Endpoint), strings.NewReader(content)) if err != nil { return nil, err } @@ -68,7 +68,7 @@ func (c *Client) CreateTrainingDefinitionAdaptive(ctx context.Context, content s // DeleteTrainingDefinitionAdaptive deletes the adaptive definition given by definitionID. func (c *Client) DeleteTrainingDefinitionAdaptive(ctx context.Context, definitionID int64) error { - req, err := http.NewRequestWithContext(ctx, "DELETE", fmt.Sprintf("%s/kypo-adaptive-training/api/v1/training-definitions/%d", c.Endpoint, definitionID), nil) + req, err := http.NewRequestWithContext(ctx, http.MethodDelete, fmt.Sprintf("%s/kypo-adaptive-training/api/v1/training-definitions/%d", c.Endpoint, definitionID), nil) if err != nil { return err } diff --git a/pkg/kypo/training_definition_adaptive_test.go b/pkg/kypo/training_definition_adaptive_test.go index bcb28f3..8f0f182 100644 --- a/pkg/kypo/training_definition_adaptive_test.go +++ b/pkg/kypo/training_definition_adaptive_test.go @@ -18,7 +18,7 @@ func assertTrainingDefinitionAdaptiveGet(t *testing.T, request *http.Request) { assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, "application/octet-stream", request.Header.Get("accept")) assert.Equal(t, "/kypo-adaptive-training/api/v1/exports/training-definitions/1", request.URL.Path) - assert.Equal(t, "GET", request.Method) + assert.Equal(t, http.MethodGet, request.Method) } func TestGetTrainingDefinitionAdaptiveSuccessful(t *testing.T) { @@ -110,7 +110,7 @@ func assertTrainingDefinitionAdaptiveCreate(t *testing.T, request *http.Request) assert.Equal(t, "application/json", request.Header.Get("Content-Type")) assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, "/kypo-adaptive-training/api/v1/imports/training-definitions", request.URL.Path) - assert.Equal(t, "POST", request.Method) + assert.Equal(t, http.MethodPost, request.Method) } func TestCreateTrainingDefinitionAdaptiveSuccessful(t *testing.T) { @@ -190,7 +190,7 @@ func assertTrainingDefinitionAdaptiveDelete(t *testing.T, request *http.Request) assert.Equal(t, "application/json", request.Header.Get("Content-Type")) assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, "/kypo-adaptive-training/api/v1/training-definitions/1", request.URL.Path) - assert.Equal(t, "DELETE", request.Method) + assert.Equal(t, http.MethodDelete, request.Method) } func TestDeleteTrainingDefinitionAdaptiveSuccessful(t *testing.T) { diff --git a/pkg/kypo/training_definition_test.go b/pkg/kypo/training_definition_test.go index bace6e9..796c931 100644 --- a/pkg/kypo/training_definition_test.go +++ b/pkg/kypo/training_definition_test.go @@ -27,7 +27,7 @@ func assertTrainingDefinitionGet(t *testing.T, request *http.Request) { assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, "application/octet-stream", request.Header.Get("accept")) assert.Equal(t, "/kypo-rest-training/api/v1/exports/training-definitions/1", request.URL.Path) - assert.Equal(t, "GET", request.Method) + assert.Equal(t, http.MethodGet, request.Method) } func TestGetTrainingDefinitionSuccessful(t *testing.T) { @@ -121,7 +121,7 @@ func assertTrainingDefinitionCreate(t *testing.T, request *http.Request) { assert.Equal(t, "application/json", request.Header.Get("Content-Type")) assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, "/kypo-rest-training/api/v1/imports/training-definitions", request.URL.Path) - assert.Equal(t, "POST", request.Method) + assert.Equal(t, http.MethodPost, request.Method) } func TestCreateTrainingDefinitionSuccessful(t *testing.T) { @@ -201,7 +201,7 @@ func assertTrainingDefinitionDelete(t *testing.T, request *http.Request) { assert.Equal(t, "application/json", request.Header.Get("Content-Type")) assert.Equal(t, "Bearer token", request.Header.Get("Authorization")) assert.Equal(t, "/kypo-rest-training/api/v1/training-definitions/1", request.URL.Path) - assert.Equal(t, "DELETE", request.Method) + assert.Equal(t, http.MethodDelete, request.Method) } func TestDeleteTrainingDefinitionSuccessful(t *testing.T) {