From f33e1af0ff01bac632d7bae1f5bcc82eb7878dbd Mon Sep 17 00:00:00 2001 From: Rounak Adhikary Date: Mon, 16 Oct 2023 10:40:16 -0400 Subject: [PATCH 01/11] Adding simple update resource initial --- go.mod | 26 +- go.sum | 97 +- redfish/models/provider.go | 6 +- redfish/models/simpleUpdate.go | 17 + redfish/provider/provider_test.go | 3 +- redfish/provider/resource_redfish_power.go | 2 +- .../resource_redfish_simple_update.go | 968 ++++++++++-------- .../resource_redfish_simple_update_test.go | 4 +- 8 files changed, 639 insertions(+), 484 deletions(-) create mode 100644 redfish/models/simpleUpdate.go diff --git a/go.mod b/go.mod index 969050ac..1cba4ee7 100644 --- a/go.mod +++ b/go.mod @@ -5,18 +5,20 @@ go 1.20 require ( github.com/hashicorp/terraform-plugin-docs v0.16.0 github.com/hashicorp/terraform-plugin-framework v1.4.0 + github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 github.com/hashicorp/terraform-plugin-log v0.9.0 - github.com/hashicorp/terraform-plugin-sdk/v2 v2.27.0 + github.com/hashicorp/terraform-plugin-sdk/v2 v2.29.0 github.com/stmcginnis/gofish v0.14.1-0.20230828052805-4738a5dd9470 ) +require github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect + 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/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // 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/cloudflare/circl v1.3.3 // indirect @@ -33,17 +35,17 @@ require ( github.com/hashicorp/go-plugin v1.5.1 // 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.2 // indirect - github.com/hashicorp/hcl/v2 v2.17.0 // indirect + github.com/hashicorp/hc-install v0.6.0 // indirect + github.com/hashicorp/hcl/v2 v2.18.0 // indirect github.com/hashicorp/logutils v1.0.0 // indirect - github.com/hashicorp/terraform-exec v0.18.1 // indirect + github.com/hashicorp/terraform-exec v0.19.0 // indirect github.com/hashicorp/terraform-json v0.17.1 // indirect github.com/hashicorp/terraform-plugin-go v0.19.0 github.com/hashicorp/terraform-registry-address v0.2.2 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // 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/imdario/mergo v0.3.15 // indirect github.com/joho/godotenv v1.5.1 github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.16 // indirect @@ -61,13 +63,13 @@ require ( github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - github.com/zclconf/go-cty v1.13.2 // indirect - golang.org/x/crypto v0.11.0 // indirect + github.com/zclconf/go-cty v1.14.0 // indirect + golang.org/x/crypto v0.13.0 // indirect golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect - golang.org/x/mod v0.11.0 // indirect + golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.13.0 // indirect - golang.org/x/sys v0.10.0 // indirect - golang.org/x/text v0.12.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect google.golang.org/grpc v1.57.0 // indirect diff --git a/go.sum b/go.sum index 3dac7743..90dc76e8 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= 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= @@ -5,23 +6,22 @@ github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0 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.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= -github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= -github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs= +github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= 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/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/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= +github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= 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/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= -github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= +github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -32,10 +32,11 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= -github.com/go-git/go-git/v5 v5.6.1 h1:q4ZRqQl4pR/ZJHc1L5CFjGA1a10u76aV1iC+nh+bHsk= +github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/protobuf v1.1.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.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -71,26 +72,28 @@ github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/C github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 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.2 h1:SfwMFnEXVVirpwkDuSF5kymUOhrUxrTq3udEseZdOD0= -github.com/hashicorp/hc-install v0.5.2/go.mod h1:9QISwe6newMWIfEiXpzuu1k9HAGtQYgnSH8H9T8wmoI= -github.com/hashicorp/hcl/v2 v2.17.0 h1:z1XvSUyXd1HP10U4lrLg5e0JMVz6CPaJvAgxM0KNZVY= -github.com/hashicorp/hcl/v2 v2.17.0/go.mod h1:gJyW2PTShkJqQBKpAmPO3yxMxIuoXkOF2TpqXzrQyx4= +github.com/hashicorp/hc-install v0.6.0 h1:fDHnU7JNFNSQebVKYhHZ0va1bC6SrPQ8fpebsvNr2w4= +github.com/hashicorp/hc-install v0.6.0/go.mod h1:10I912u3nntx9Umo1VAeYPUUuehk0aRQJYpMwbX5wQA= +github.com/hashicorp/hcl/v2 v2.18.0 h1:wYnG7Lt31t2zYkcquwgKo6MWXzRUDIeIVU5naZwHLl8= +github.com/hashicorp/hcl/v2 v2.18.0/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= 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.18.1 h1:LAbfDvNQU1l0NOQlTuudjczVhHj061fNX5H8XZxHlH4= -github.com/hashicorp/terraform-exec v0.18.1/go.mod h1:58wg4IeuAJ6LVsLUeD2DWZZoc/bYi6dzhLHzxM41980= +github.com/hashicorp/terraform-exec v0.19.0 h1:FpqZ6n50Tk95mItTSS9BjeOVUb4eg81SpgVtZNNtFSM= +github.com/hashicorp/terraform-exec v0.19.0/go.mod h1:tbxUpe3JKruE9Cuf65mycSIT8KiNPZ0FkuTE3H4urQg= github.com/hashicorp/terraform-json v0.17.1 h1:eMfvh/uWggKmY7Pmb3T85u86E2EQg6EQHgyRwf3RkyA= github.com/hashicorp/terraform-json v0.17.1/go.mod h1:Huy6zt6euxaY9knPAFKjUITn8QxUFIe9VuSzb4zn/0o= github.com/hashicorp/terraform-plugin-docs v0.16.0 h1:UmxFr3AScl6Wged84jndJIfFccGyBZn52KtMNsS12dI= github.com/hashicorp/terraform-plugin-docs v0.16.0/go.mod h1:M3ZrlKBJAbPMtNOPwHicGi1c+hZUh7/g0ifT/z7TVfA= github.com/hashicorp/terraform-plugin-framework v1.4.0 h1:WKbtCRtNrjsh10eA7NZvC/Qyr7zp77j+D21aDO5th9c= github.com/hashicorp/terraform-plugin-framework v1.4.0/go.mod h1:XC0hPcQbBvlbxwmjxuV/8sn8SbZRg4XwGMs22f+kqV0= +github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 h1:HOjBuMbOEzl7snOdOoUfE2Jgeto6JOjLVQ39Ls2nksc= +github.com/hashicorp/terraform-plugin-framework-validators v0.12.0/go.mod h1:jfHGE/gzjxYz6XoUwi/aYiiKrJDeutQNUtGQXkaHklg= github.com/hashicorp/terraform-plugin-go v0.19.0 h1:BuZx/6Cp+lkmiG0cOBk6Zps0Cb2tmqQpDM3iAtnhDQU= github.com/hashicorp/terraform-plugin-go v0.19.0/go.mod h1:EhRSkEPNoylLQntYsk5KrDHTZJh9HQoumZXbOGOXmec= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.27.0 h1:I8efBnjuDrgPjNF1MEypHy48VgcTIUY4X6rOFunrR3Y= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.27.0/go.mod h1:cUEP4ly/nxlHy5HzD6YRrHydtlheGvGRJDhiWqqVik4= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.29.0 h1:wcOKYwPI9IorAJEBLzgclh3xVolO7ZorYd6U1vnok14= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.29.0/go.mod h1:qH/34G25Ugdj5FcM95cSoXzUgIbgfhVLXCcEcYaMwq8= github.com/hashicorp/terraform-registry-address v0.2.2 h1:lPQBg403El8PPicg/qONZJDC6YlgCVbWDtNmmZKtBno= github.com/hashicorp/terraform-registry-address v0.2.2/go.mod h1:LtwNbCihUoUZ3RYriyS2wF/lGPB6gF9ICLRtuDk7hSo= github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= @@ -101,8 +104,8 @@ github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq 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.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= +github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= @@ -153,7 +156,7 @@ 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/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0= +github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM= 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= @@ -174,24 +177,37 @@ github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= -github.com/zclconf/go-cty v1.13.2 h1:4GvrUxe/QUDYuJKAav4EYqdM47/kZa672LwmXFmEKT0= -github.com/zclconf/go-cty v1.13.2/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zclconf/go-cty v1.14.0 h1:/Xrd39K7DXbHzlisFP9c4pHao4yyf+/Ug9LEz+Y/yhc= +github.com/zclconf/go-cty v1.14.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= 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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= -golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/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/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -200,18 +216,36 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w 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-20211007075335-d3039528d8ac/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.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= 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.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +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/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +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.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= @@ -231,6 +265,5 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/yaml.v2 v2.2.2/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/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= diff --git a/redfish/models/provider.go b/redfish/models/provider.go index e2759dbf..153adabe 100644 --- a/redfish/models/provider.go +++ b/redfish/models/provider.go @@ -11,8 +11,8 @@ type ProviderConfig struct { } type RedfishServer struct { - User types.String `tfsdk:"user"` - Password types.String `tfsdk:"password"` - Endpoint types.String `tfsdk:"endpoint"` + User types.String `tfsdk:"user"` + Password types.String `tfsdk:"password"` + Endpoint types.String `tfsdk:"endpoint"` ValidateCert types.Bool `tfsdk:"validate_cert"` } diff --git a/redfish/models/simpleUpdate.go b/redfish/models/simpleUpdate.go new file mode 100644 index 00000000..90416042 --- /dev/null +++ b/redfish/models/simpleUpdate.go @@ -0,0 +1,17 @@ +package models + +import ( + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type SimpleUpdateRes struct { + Id types.String `tfsdk:"id"` + RedfishServer RedfishServer `tfsdk:"redfish_server"` + Protocol types.String `tfsdk:"transfer_protocol"` + Image types.String `tfsdk:"target_firmware_image"` + ResetType types.String `tfsdk:"reset_type"` + ResetTimeout types.Int64 `tfsdk:"reset_timeout"` + JobTimeout types.Int64 `tfsdk:"simple_update_job_timeout"` + SoftwareId types.String `tfsdk:"software_id"` + Version types.String `tfsdk:"version"` +} diff --git a/redfish/provider/provider_test.go b/redfish/provider/provider_test.go index f21986ac..e390e906 100644 --- a/redfish/provider/provider_test.go +++ b/redfish/provider/provider_test.go @@ -1,6 +1,7 @@ package provider import ( + "fmt" "os" "testing" @@ -25,7 +26,7 @@ type TestingServerCredentials struct { func init() { err := godotenv.Load("redfish_test.env") if err != nil { - panic(err) + fmt.Println(err.Error()) } testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ diff --git a/redfish/provider/resource_redfish_power.go b/redfish/provider/resource_redfish_power.go index 3de57337..71a0dc1a 100644 --- a/redfish/provider/resource_redfish_power.go +++ b/redfish/provider/resource_redfish_power.go @@ -82,7 +82,7 @@ func PowerSchema() map[string]schema.Attribute { MarkdownDescription: "The frequency with which to check the server's power state in seconds", Description: "The frequency with which to check the server's power state in seconds", Optional: true, - Computed: true, + Computed: true, Default: int64default.StaticInt64(10), }, diff --git a/redfish/provider/resource_redfish_simple_update.go b/redfish/provider/resource_redfish_simple_update.go index df64c48d..bcaf3893 100644 --- a/redfish/provider/resource_redfish_simple_update.go +++ b/redfish/provider/resource_redfish_simple_update.go @@ -1,436 +1,536 @@ package provider -// import ( -// "context" -// "fmt" -// "github.com/dell/terraform-provider-redfish/common" -// "github.com/hashicorp/terraform-plugin-sdk/v2/diag" -// "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -// "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" -// "github.com/stmcginnis/gofish" -// redfishcommon "github.com/stmcginnis/gofish/common" -// "github.com/stmcginnis/gofish/redfish" -// "io" -// "log" -// "net/http" -// "os" -// "path/filepath" -// "strings" -// ) - -// const ( -// defaultSimpleUpdateResetTimeout int = 120 -// defaultSimpleUpdateJobTimeout int = 1200 -// intervalSimpleUpdateJobCheckTime int = 10 -// ) - -// func resourceRedfishSimpleUpdate() *schema.Resource { -// return &schema.Resource{ -// CreateContext: resourceRedfishSimpleUpdateCreate, -// ReadContext: resourceRedfishSimpleUpdateRead, -// UpdateContext: resourceRedfishSimpleUpdateUpdate, -// DeleteContext: resourceRedfishSimpleUpdateDelete, -// Schema: getResourceRedfishSimpleUpdateSchema(), -// } -// } - -// func getResourceRedfishSimpleUpdateSchema() map[string]*schema.Schema { -// return map[string]*schema.Schema{ -// "redfish_server": { -// Type: schema.TypeList, -// Required: true, -// Description: "List of server BMCs and their respective user credentials", -// Elem: &schema.Resource{ -// Schema: map[string]*schema.Schema{ -// "user": { -// Type: schema.TypeString, -// Optional: true, -// Description: "User name for login", -// }, -// "password": { -// Type: schema.TypeString, -// Optional: true, -// Description: "User password for login", -// Sensitive: true, -// }, -// "endpoint": { -// Type: schema.TypeString, -// Required: true, -// Description: "Server BMC IP address or hostname", -// }, -// "ssl_insecure": { -// Type: schema.TypeBool, -// Optional: true, -// Description: "This field indicates whether the SSL/TLS certificate must be verified or not", -// }, -// }, -// }, -// }, -// "transfer_protocol": { -// Type: schema.TypeString, -// Required: true, -// Description: "The network protocol that the Update Service uses to retrieve the software image file located at the URI provided " + -// "in ImageURI, if the URI does not contain a scheme." + -// " Accepted values: CIFS, FTP, SFTP, HTTP, HTTPS, NSF, SCP, TFTP, OEM, NFS." + -// " Currently only HTTP, HTTPS and NFS are supported with local file path or HTTP(s)/NFS link.", -// }, -// /* For the time being, target_firmware_image will be the local path for our firmware packages. -// It is intended to work along HTTP transfer protocol -// In the future it could be used for targetting FTP/CIFS/NFS images -// TBD - Think about a custom diff function that grabs only the file name and not the path, to avoid unneeded update triggers -// */ -// "target_firmware_image": { -// Type: schema.TypeString, -// Required: true, -// Description: "Target firmware image used for firmware update on the redfish instance. " + -// "Make sure you place your firmware packages in the same folder as the module and set it as follows: \"${path.module}/BIOS_FXC54_WN64_1.15.0.EXE\"", -// // DiffSuppressFunc will allow moving fw packages through the filesystem without triggering an update if so. -// // At the moment it uses filename to see if they're the same. We need to strengthen that by somehow using hashing -// DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { -// if filepath.Base(old) == filepath.Base(new) { -// return true -// } -// return false -// }, -// }, -// "reset_type": { -// Type: schema.TypeString, -// Required: true, -// Description: "Reset type allows to choose the type of restart to apply when firmware upgrade is scheduled." + -// "Possible values are: \"ForceRestart\", \"GracefulRestart\" or \"PowerCycle\"", -// ValidateFunc: validation.StringInSlice([]string{ -// string(redfish.ForceRestartResetType), -// string(redfish.GracefulRestartResetType), -// string(redfish.PowerCycleResetType), -// }, false), -// }, -// "reset_timeout": { -// Type: schema.TypeInt, -// Optional: true, -// Description: "reset_timeout is the time in seconds that the provider waits for the server to be reset before timing out.", -// }, -// "simple_update_job_timeout": { -// Type: schema.TypeInt, -// Optional: true, -// Description: "simple_update_job_timeout is the time in seconds that the provider waits for the simple update job to be completed before timing out.", -// }, -// "software_id": { -// Type: schema.TypeString, -// Computed: true, -// Description: "Software ID from the firmware package uploaded", -// }, -// "version": { -// Type: schema.TypeString, -// Computed: true, -// Description: "Software version from the firmware package uploaded", -// }, -// } -// } - -// func resourceRedfishSimpleUpdateCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { -// service, err := NewConfig(m.(*schema.ResourceData), d) -// if err != nil { -// return diag.Errorf(err.Error()) -// } -// return updateRedfishSimpleUpdate(ctx, service, d, m) -// } - -// func resourceRedfishSimpleUpdateRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { -// service, err := NewConfig(m.(*schema.ResourceData), d) -// if err != nil { -// return diag.Errorf(err.Error()) -// } -// return readRedfishSimpleUpdate(service, d) -// } - -// func resourceRedfishSimpleUpdateUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { -// service, err := NewConfig(m.(*schema.ResourceData), d) -// if err != nil { -// return diag.Errorf(err.Error()) -// } -// if diags := updateRedfishSimpleUpdate(ctx, service, d, m); diags.HasError() { -// return diags -// } -// return resourceRedfishSimpleUpdateRead(ctx, d, m) -// } - -// func resourceRedfishSimpleUpdateDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { -// service, err := NewConfig(m.(*schema.ResourceData), d) -// if err != nil { -// return diag.Errorf(err.Error()) -// } -// return deleteRedfishSimpleUpdate(service, d) -// } - -// func readRedfishSimpleUpdate(service *gofish.Service, d *schema.ResourceData) diag.Diagnostics { -// var diags diag.Diagnostics - -// // Try to get software inventory -// _, err := redfish.GetSoftwareInventory(service.GetClient(), d.Id()) -// if err != nil { -// _, ok := err.(*redfishcommon.Error) -// if !ok { -// return diag.Errorf("there was an issue with the API") -// } -// // the firmware package previously applied has changed, trigger update -// d.SetId("") -// } - -// return diags -// } - -// func updateRedfishSimpleUpdate(ctx context.Context, service *gofish.Service, d *schema.ResourceData, m interface{}) diag.Diagnostics { -// var diags diag.Diagnostics - -// // Lock the mutex to avoid race conditions with other resources -// redfishMutexKV.Lock(getRedfishServerEndpoint(d)) -// defer redfishMutexKV.Unlock(getRedfishServerEndpoint(d)) - -// transferProtocol := d.Get("transfer_protocol").(string) -// targetFirmwareImage := d.Get("target_firmware_image").(string) -// resetType := d.Get("reset_type").(string) - -// // Check if chosen reset type is supported before doing anything else -// systems, err := service.Systems() -// if err != nil { -// return diag.Errorf("Couldn't retrieve allowed reset types from systems - %s", err) -// } -// if ok := checkResetType(resetType, systems[0].SupportedResetTypes); !ok { -// return diag.Errorf("reset type %s is not available in this redfish implementation", resetType) -// } - -// // Get update service from root -// updateService, err := service.UpdateService() -// if err != nil { -// return diag.Errorf("error while retrieving UpdateService - %s", err) -// } - -// //Check if the transfer protocol is available in the redfish instance -// err = checkTransferProtocol(transferProtocol, updateService) -// if err != nil { -// var availableTransferProtocols string -// for _, v := range updateService.TransferProtocol { -// availableTransferProtocols += fmt.Sprintf("%s ", v) -// } -// return diag.Errorf("%s. Supported transfer protocols in this implementation: %s", err, availableTransferProtocols) // !!!! append list of supported transfer protocols -// } - -// if transferProtocol == "NFS" { -// err := pullUpdate(service, d, resetType) -// if err != nil { -// return diag.Errorf(" %s", err) -// } -// } else if transferProtocol == "HTTP" || transferProtocol == "HTTPS" { -// if strings.HasPrefix(targetFirmwareImage, "http") { -// err := pullUpdate(service, d, resetType) -// if err != nil { -// return diag.Errorf(" %s", err) -// } -// } else { -// // Get ETag from FW inventory -// response, err := service.GetClient().Get(updateService.FirmwareInventory) -// if err != nil { -// diag.Errorf("error while retrieving Etag from FirmwareInventory") -// } -// response.Body.Close() -// etag := response.Header.Get("ETag") - -// // Set custom headers -// customHeaders := map[string]string{ -// "if-match": etag, -// } - -// // Open file to upload -// file, err := openFile(targetFirmwareImage) -// if err != nil { -// return diag.Errorf("couldn't open FW file to upload - %s", err) -// } -// defer file.Close() - -// // Set payload -// payload := map[string]io.Reader{ -// "file": file, -// } - -// // Upload FW Package to FW inventory -// response, err = service.GetClient().PostMultipartWithHeaders(updateService.HTTPPushURI, payload, customHeaders) -// if err != nil { -// return diag.Errorf("there was an issue when uploading FW package to redfish - %s", err) -// } -// response.Body.Close() -// packageLocation := response.Header.Get("Location") - -// // Get package information ( SoftwareID - Version ) -// packageInformation, err := redfish.GetSoftwareInventory(service.GetClient(), packageLocation) -// if err != nil { -// return diag.Errorf("there was an issue when retrieving uploaded package information - %s", err) -// } - -// // Set payload for POST call that'll trigger the update job scheduling -// triggerUpdatePayload := struct { -// ImageURI string -// }{ -// ImageURI: packageLocation, -// } -// // Do the POST call against Simple.Update service -// response, err = service.GetClient().Post(updateService.UpdateServiceTarget, triggerUpdatePayload) -// if err != nil { -// // Delete uploaded package - TBD -// return diag.Errorf("there was an issue when scheduling the update job - %s", err) -// } -// response.Body.Close() - -// err = updateJobStatus(service, d, response, resetType) -// if err != nil { -// diag.Errorf("Error running job %v", err) -// } -// // Get updated FW inventory -// fwInventory, err := updateService.FirmwareInventories() -// if err != nil { -// // TBD - HOW TO HANDLE WHEN FAILS BUT FIRMWARE WAS INSTALLED? -// return diag.Errorf("error when getting firmware inventory - %s", err) -// } - -// // Get fw ID -// fwPackage, err := getFWfromInventory(fwInventory, packageInformation.SoftwareID, packageInformation.Version) -// if err != nil { -// // TBD - HOW TO HANDLE WHEN FAILS BUT FIRMWARE WAS INSTALLED? -// return diag.Errorf("error when retrieving fw package from fw inventory - %s", err) -// } -// d.Set("software_id", fwPackage.SoftwareID) -// d.Set("version", fwPackage.Version) -// d.SetId(fwPackage.ODataID) - -// diags = readRedfishSimpleUpdate(service, d) - -// return diags -// } -// } else { -// return diag.Errorf("Transfer protocol not available in this implementation") -// } - -// return diags -// } - -// func deleteRedfishSimpleUpdate(service *gofish.Service, d *schema.ResourceData) diag.Diagnostics { -// var diags diag.Diagnostics - -// d.SetId("") - -// return diags -// } - -// // checkResetType check if the resetType passed is within the allowableValues slice -// func checkResetType(resetType string, allowableValues []redfish.ResetType) bool { -// for _, v := range allowableValues { -// if resetType == string(v) { -// return true -// } -// } -// return false -// } - -// // openFile is a simple function that opens a file -// func openFile(filePath string) (*os.File, error) { -// if f, err := os.Open(filePath); err != nil { -// return nil, fmt.Errorf("error when opening %s file - %s", filePath, err) -// } else { -// return f, nil -// } -// } - -// // checkTransferProtocol checks if the chosen transfer protocol is available in the redfish instance -// func checkTransferProtocol(transferProtocol string, updateService *redfish.UpdateService) error { -// for _, v := range updateService.TransferProtocol { -// if transferProtocol == v { -// return nil -// } -// } -// return fmt.Errorf("this transfer protocol is not available in this redfish instance") -// } - -// // getFWfromInventory get the right SoftwareInventory struct if exists -// func getFWfromInventory(softwareInventories []*redfish.SoftwareInventory, softwareID, version string) (*redfish.SoftwareInventory, error) { -// for _, v := range softwareInventories { -// if v.SoftwareID == softwareID && v.Version == version { -// return v, nil -// } -// } -// return nil, fmt.Errorf("couldn't find FW on Firmware inventory") -// } -// func pullUpdate(service *gofish.Service, d *schema.ResourceData, resetType string) error { - -// // Get update service from root -// updateService, err := service.UpdateService() -// if err != nil { -// return fmt.Errorf("error while retrieving UpdateService - %s", err) -// } - -// protocol := d.Get("transfer_protocol") -// imagePath := d.Get("target_firmware_image") -// httpURI := updateService.UpdateServiceTarget - -// payload := make(map[string]interface{}) -// payload["ImageURI"] = imagePath -// payload["TransferProtocol"] = protocol - -// response, err := service.GetClient().Post(httpURI, payload) -// if err != nil { -// // Delete uploaded package - TBD -// return fmt.Errorf("there was an issue when scheduling the update job - %s", err) -// } - -// // Get jobid -// jobID := response.Header.Get("Location") -// err = updateJobStatus(service, d, response, resetType) -// if err != nil { -// // Delete uploaded package - TBD -// return fmt.Errorf("there was an issue when waiting for the job to complete - %s", err) -// } - -// job, err := redfish.GetTask(service.GetClient(), jobID) -// if len(job.Messages) > 0 { -// message := job.Messages[0].Message -// if strings.Contains(message, "Unable to transfer") || strings.Contains(message, "Module took more time than expected.") { -// return fmt.Errorf("please check the image path, download failed") -// } -// } - -// swInventory, err := redfish.GetSoftwareInventory(service.GetClient(), d.Id()) -// if err != nil { -// return fmt.Errorf("unable to fetch data %v", err) -// } -// d.SetId(swInventory.ODataID) -// return nil -// } - -// func updateJobStatus(service *gofish.Service, d *schema.ResourceData, response *http.Response, resetType string) error { -// // Get jobid -// jobID := response.Header.Get("Location") - -// resetTimeout, ok := d.GetOk("reset_timeout") -// if !ok { -// resetTimeout = defaultSimpleUpdateResetTimeout -// } -// simpleUpdateJobTimeout, ok := d.GetOk("simple_update_job_timeout") -// if !ok { -// simpleUpdateJobTimeout = defaultSimpleUpdateJobTimeout -// } -// log.Printf("[DEBUG] resetTimeout is set to %d and simpleUpdateJobTimeout to %d", resetTimeout.(int), simpleUpdateJobTimeout.(int)) - -// // Reboot the server -// _, diags := PowerOperation(resetType, resetTimeout.(int), intervalSimpleUpdateJobCheckTime, service) -// if diags.HasError() { -// // Delete uploaded package - TBD -// return fmt.Errorf("there was an issue when restarting the server") -// } - -// // Check JID -// err := common.WaitForJobToFinish(service, jobID, intervalSimpleUpdateJobCheckTime, simpleUpdateJobTimeout.(int)) -// if err != nil { -// // Delete uploaded package - TBD -// return fmt.Errorf("there was an issue when waiting for the job to complete - %s", err) -// } - -// return nil -// } +import ( + "context" + "errors" + "fmt" + "io" + "log" + "net/http" + "os" + "path/filepath" + "strings" + "terraform-provider-redfish/common" + "terraform-provider-redfish/redfish/models" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/stmcginnis/gofish" + redfishcommon "github.com/stmcginnis/gofish/common" + "github.com/stmcginnis/gofish/redfish" +) + +const ( + defaultSimpleUpdateResetTimeout int = 120 + defaultSimpleUpdateJobTimeout int = 1200 + intervalSimpleUpdateJobCheckTime int = 10 +) + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ resource.Resource = &simpleUpdateResource{} +) + +// NewpowerResource is a helper function to simplify the provider implementation. +func NewSimpleUpdateResource() resource.Resource { + return &simpleUpdateResource{} +} + +// powerResource is the resource implementation. +type simpleUpdateResource struct { + p *redfishProvider +} + +// Configure implements resource.ResourceWithConfigure +func (r *simpleUpdateResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + r.p = req.ProviderData.(*redfishProvider) +} + +// Metadata returns the resource type name. +func (r *simpleUpdateResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "simple_update" +} + +func SimpleUpdateSchema() map[string]schema.Attribute { + return map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: "ID of the simple update resource", + MarkdownDescription: "ID of the simple update resource", + Computed: true, + }, + "redfish_server": schema.SingleNestedAttribute{ + MarkdownDescription: "Redfish Server", + Description: "Redfish Server", + Required: true, + Attributes: RedfishServerSchema(), + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.RequiresReplace(), + }, + }, + "transfer_protocol": schema.StringAttribute{ + Required: true, + Description: "The network protocol that the Update Service uses to retrieve the software image file located at the URI provided " + + "in ImageURI, if the URI does not contain a scheme." + + " Accepted values: CIFS, FTP, SFTP, HTTP, HTTPS, NSF, SCP, TFTP, OEM, NFS." + + " Currently only HTTP, HTTPS and NFS are supported with local file path or HTTP(s)/NFS link.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + /* For the time being, target_firmware_image will be the local path for our firmware packages. + It is intended to work along HTTP transfer protocol + In the future it could be used for targetting FTP/CIFS/NFS images + TBD - Think about a custom diff function that grabs only the file name and not the path, to avoid unneeded update triggers + */ + "target_firmware_image": schema.StringAttribute{ + Required: true, + Description: "Target firmware image used for firmware update on the redfish instance. " + + "Make sure you place your firmware packages in the same folder as the module and set it as follows: \"${path.module}/BIOS_FXC54_WN64_1.15.0.EXE\"", + // DiffSuppressFunc will allow moving fw packages through the filesystem without triggering an update if so. + // At the moment it uses filename to see if they're the same. We need to strengthen that by somehow using hashing + // DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + // if filepath.Base(old) == filepath.Base(new) { + // return true + // } + // return false + // }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplaceIf( + func( + _ context.Context, + req planmodifier.StringRequest, + resp *stringplanmodifier.RequiresReplaceIfFuncResponse, + ) { + spath, ppath := req.StateValue.ValueString(), req.ConfigValue.ValueString() + if filepath.Base(spath) == filepath.Base(ppath) { + resp.RequiresReplace = false + } + resp.RequiresReplace = true + }, + "", + "", + ), + }, + }, + "reset_type": schema.StringAttribute{ + Required: true, + Description: "Reset type allows to choose the type of restart to apply when firmware upgrade is scheduled." + + "Possible values are: \"ForceRestart\", \"GracefulRestart\" or \"PowerCycle\"", + Validators: []validator.String{ + stringvalidator.OneOf([]string{ + string(redfish.ForceRestartResetType), + string(redfish.GracefulRestartResetType), + string(redfish.PowerCycleResetType), + }...), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "reset_timeout": schema.Int64Attribute{ + Optional: true, + Computed: true, + Default: int64default.StaticInt64(int64(defaultSimpleUpdateResetTimeout)), + Description: "reset_timeout is the time in seconds that the provider waits for the server to be reset before timing out.", + }, + "simple_update_job_timeout": schema.Int64Attribute{ + Optional: true, + Computed: true, + Default: int64default.StaticInt64(int64(defaultSimpleUpdateJobTimeout)), + Description: "simple_update_job_timeout is the time in seconds that the provider waits for the simple update job to be completed before timing out.", + }, + "software_id": schema.StringAttribute{ + Computed: true, + Description: "Software ID from the firmware package uploaded", + }, + "version": schema.StringAttribute{ + Computed: true, + Description: "Software version from the firmware package uploaded", + }, + } +} + +// Schema defines the schema for the resource. +func (r *simpleUpdateResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "Resource for managing power.", + Version: 1, + Attributes: SimpleUpdateSchema(), + } +} + +// Create creates the resource and sets the initial Terraform state. +func (r *simpleUpdateResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + tflog.Trace(ctx, "resource_simple_update create : Started") + // Get Plan Data + var plan models.SimpleUpdateRes + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + service, err := NewConfig(r.p, &plan.RedfishServer) + if err != nil { + resp.Diagnostics.AddError("service error", err.Error()) + return + } + system, err := getSystemResource(service) + if err != nil { + resp.Diagnostics.AddError("system error", err.Error()) + return + } + + plan.Id = types.StringValue(system.SerialNumber + "_simple_update") + + // resetType := plan.DesiredPowerAction.ValueString() + dia, state := updateRedfishSimpleUpdate(ctx, service, plan) + resp.Diagnostics.Append(dia...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +// Read refreshes the resource and writes to state +func (r *simpleUpdateResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + tflog.Trace(ctx, "resource_simple_update read : Started") + // Get Plan Data + var state models.SimpleUpdateRes + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + service, err := NewConfig(r.p, &state.RedfishServer) + if err != nil { + resp.Diagnostics.AddError("service error", err.Error()) + return + } + dia, newState := readRedfishSimpleUpdate(service, state) + resp.Diagnostics.Append(dia...) + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +// Update also refreshes the resource and writes to state +func (r *simpleUpdateResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // update can be triggerred by only a change in non-functional requirements. + // So set them to state. + tflog.Trace(ctx, "resource_simple_update update : Started") + // Get Plan Data + var state models.SimpleUpdateRes + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + service, err := NewConfig(r.p, &state.RedfishServer) + if err != nil { + resp.Diagnostics.AddError("service error", err.Error()) + return + } + dia, newState := readRedfishSimpleUpdate(service, state) + resp.Diagnostics.Append(dia...) + resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) +} + +// Delete removes resource from state +func (r *simpleUpdateResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + resp.State.RemoveResource(ctx) +} + +func readRedfishSimpleUpdate(service *gofish.Service, d models.SimpleUpdateRes) (diag.Diagnostics, models.SimpleUpdateRes) { + var diags diag.Diagnostics + + // Try to get software inventory + _, err := redfish.GetSoftwareInventory(service.GetClient(), d.Id.ValueString()) + if err != nil { + _, ok := err.(*redfishcommon.Error) + if !ok { + diags.AddError("there was an issue with the API", err.Error()) + } else { + // the firmware package previously applied has changed, trigger update + d.Image = types.StringValue("none") + } + } + + return diags, d +} + +func updateRedfishSimpleUpdate(ctx context.Context, service *gofish.Service, d models.SimpleUpdateRes) (diag.Diagnostics, models.SimpleUpdateRes) { + var diags diag.Diagnostics + ret := d + + transferProtocol := d.Protocol.ValueString() + targetFirmwareImage := d.Image.ValueString() + resetType := d.ResetType.ValueString() + + // Check if chosen reset type is supported before doing anything else + systems, err := service.Systems() + if err != nil { + diags.AddError( + "Couldn't retrieve allowed reset types from systems", + err.Error(), + ) + return diags, ret + } + + if ok := checkResetType(resetType, systems[0].SupportedResetTypes); !ok { + diags.AddError( + fmt.Sprintf("Reset type %s is not available in this redfish implementation", resetType), + err.Error(), + ) + return diags, ret + } + + // Get update service from root + updateService, err := service.UpdateService() + if err != nil { + diags.AddError("error while retrieving UpdateService", err.Error()) + return diags, ret + } + + // Check if the transfer protocol is available in the redfish instance + err = checkTransferProtocol(transferProtocol, updateService) + if err != nil { + var availableTransferProtocols string + for _, v := range updateService.TransferProtocol { + availableTransferProtocols += fmt.Sprintf("%s ", v) + } + diags.AddError( + err.Error(), + fmt.Sprintf("Supported transfer protocols in this implementation: %s", availableTransferProtocols), + ) + return diags, ret // !!!! append list of supported transfer protocols + } + + if transferProtocol == "NFS" { + id, err := pullUpdate(service, d) + if err != nil { + diags.AddError(err.Error(), "") + } else { + d.Id = types.StringValue(id) + } + } else if transferProtocol == "HTTP" || transferProtocol == "HTTPS" { + if strings.HasPrefix(targetFirmwareImage, "http") { + id, err := pullUpdate(service, d) + if err != nil { + diags.AddError(err.Error(), "") + } else { + d.Id = types.StringValue(id) + } + } else { + fwPackage, err := uploadLocalFirmware(service, updateService, d) + if err != nil { + // TBD - HOW TO HANDLE WHEN FAILS BUT FIRMWARE WAS INSTALLED? + diags.AddError(err.Error(), "") + } + ret.SoftwareId = types.StringValue(fwPackage.SoftwareID) + ret.Version = types.StringValue(fwPackage.Version) + ret.Id = types.StringValue(fwPackage.ODataID) + + diagsRead, state := readRedfishSimpleUpdate(service, d) + diags.Append(diagsRead...) + ret = state + } + } else { + diags.AddError("Transfer protocol not available in this implementation", "") + } + + return diags, ret +} + +func uploadLocalFirmware(service *gofish.Service, updateService *redfish.UpdateService, d models.SimpleUpdateRes) (*redfish.SoftwareInventory, error) { + // Get ETag from FW inventory + response, err := service.GetClient().Get(updateService.FirmwareInventory) + if err != nil { + return nil, fmt.Errorf("error while retrieving Etag from FirmwareInventory: %w", err) + } + response.Body.Close() + etag := response.Header.Get("ETag") + + // Set custom headers + customHeaders := map[string]string{ + "if-match": etag, + } + + // Open file to upload + file, err := openFile(d.Image.ValueString()) + if err != nil { + return nil, fmt.Errorf("couldn't open FW file to upload - %w", err) + } + defer file.Close() + + // Set payload + payload := map[string]io.Reader{ + "file": file, + } + + // Upload FW Package to FW inventory + response, err = service.GetClient().PostMultipartWithHeaders(updateService.HTTPPushURI, payload, customHeaders) + if err != nil { + return nil, fmt.Errorf("there was an issue when uploading FW package to redfish - %w", err) + } + response.Body.Close() + packageLocation := response.Header.Get("Location") + + // Get package information ( SoftwareID - Version ) + packageInformation, err := redfish.GetSoftwareInventory(service.GetClient(), packageLocation) + if err != nil { + return nil, fmt.Errorf("there was an issue when retrieving uploaded package information - %w", err) + } + + // Set payload for POST call that'll trigger the update job scheduling + triggerUpdatePayload := struct { + ImageURI string + }{ + ImageURI: packageLocation, + } + // Do the POST call against Simple.Update service + response, err = service.GetClient().Post(updateService.UpdateServiceTarget, triggerUpdatePayload) + if err != nil { + // Delete uploaded package - TBD + return nil, fmt.Errorf("there was an issue when scheduling the update job - %w", err) + } + response.Body.Close() + + err = updateJobStatus(service, d, response) + if err != nil { + return nil, fmt.Errorf("error running job %w", err) + } + // Get updated FW inventory + fwInventory, err := updateService.FirmwareInventories() + if err != nil { + // TBD - HOW TO HANDLE WHEN FAILS BUT FIRMWARE WAS INSTALLED? + return nil, fmt.Errorf("error when getting firmware inventory - %w", err) + } + + inv, err := getFWfromInventory(fwInventory, packageInformation.SoftwareID, packageInformation.Version) + if err != nil { + err = fmt.Errorf("error when retrieving fw package from fw inventory - %w", err) + } + return inv, err +} + +// checkResetType check if the resetType passed is within the allowableValues slice +func checkResetType(resetType string, allowableValues []redfish.ResetType) bool { + for _, v := range allowableValues { + if resetType == string(v) { + return true + } + } + return false +} + +// openFile is a simple function that opens a file +func openFile(filePath string) (*os.File, error) { + f, err := os.Open(filePath) + if err != nil { + err = fmt.Errorf("error when opening %s file - %w", filePath, err) + } + return f, err +} + +// checkTransferProtocol checks if the chosen transfer protocol is available in the redfish instance +func checkTransferProtocol(transferProtocol string, updateService *redfish.UpdateService) error { + for _, v := range updateService.TransferProtocol { + if transferProtocol == v { + return nil + } + } + return fmt.Errorf("this transfer protocol is not available in this redfish instance") +} + +// getFWfromInventory get the right SoftwareInventory struct if exists +func getFWfromInventory(softwareInventories []*redfish.SoftwareInventory, softwareID, version string) (*redfish.SoftwareInventory, error) { + for _, v := range softwareInventories { + if v.SoftwareID == softwareID && v.Version == version { + return v, nil + } + } + return nil, fmt.Errorf("couldn't find FW on Firmware inventory") +} + +func pullUpdate(service *gofish.Service, d models.SimpleUpdateRes) (string, error) { + // Get update service from root + updateService, err := service.UpdateService() + if err != nil { + return "", fmt.Errorf("error while retrieving UpdateService - %w", err) + } + + protocol := d.Protocol.ValueString() + imagePath := d.Image.ValueString() + httpURI := updateService.UpdateServiceTarget + + payload := make(map[string]interface{}) + payload["ImageURI"] = imagePath + payload["TransferProtocol"] = protocol + + response, err := service.GetClient().Post(httpURI, payload) + if err != nil { + // Delete uploaded package - TBD + return "", fmt.Errorf("there was an issue when scheduling the update job - %s", err) + } + + // Get jobid + jobID := response.Header.Get("Location") + err = updateJobStatus(service, d, response) + if err != nil { + // Delete uploaded package - TBD + return "", fmt.Errorf("there was an issue when waiting for the job to complete - %s", err) + } + + job, err := redfish.GetTask(service.GetClient(), jobID) + if len(job.Messages) > 0 { + message := job.Messages[0].Message + if strings.Contains(message, "Unable to transfer") || strings.Contains(message, "Module took more time than expected.") { + err = errors.Join(err, fmt.Errorf("please check the image path, download failed")) + } + } + if err != nil { + return "", err + } + + swInventory, err := redfish.GetSoftwareInventory(service.GetClient(), d.Id.ValueString()) + if err != nil { + return "", fmt.Errorf("unable to fetch data %v", err) + } + return swInventory.ODataID, nil +} + +func updateJobStatus(service *gofish.Service, d models.SimpleUpdateRes, response *http.Response) error { + // Get jobid + jobID := response.Header.Get("Location") + + resetTimeout := d.ResetTimeout.ValueInt64() + simpleUpdateJobTimeout := d.JobTimeout.ValueInt64() + log.Printf("[DEBUG] resetTimeout is set to %d and simpleUpdateJobTimeout to %d", resetTimeout, simpleUpdateJobTimeout) + + // Reboot the server + _, diags := PowerOperation(d.ResetType.ValueString(), int(resetTimeout), intervalSimpleUpdateJobCheckTime, service) + if diags.HasError() { + // Delete uploaded package - TBD + return fmt.Errorf("there was an issue when restarting the server") + } + + // Check JID + err := common.WaitForJobToFinish(service, jobID, intervalSimpleUpdateJobCheckTime, int(simpleUpdateJobTimeout)) + if err != nil { + // Delete uploaded package - TBD + return fmt.Errorf("there was an issue when waiting for the job to complete - %s", err) + } + + return nil +} diff --git a/redfish/provider/resource_redfish_simple_update_test.go b/redfish/provider/resource_redfish_simple_update_test.go index dca4d2e7..6aff88a0 100644 --- a/redfish/provider/resource_redfish_simple_update_test.go +++ b/redfish/provider/resource_redfish_simple_update_test.go @@ -2,10 +2,11 @@ package provider import ( "fmt" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "os" "regexp" "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) // Test to create and update Simple update - Positive @@ -75,6 +76,7 @@ func TestAccRedfishSimpleUpdate_InvalidProto(t *testing.T) { }, }) } + func testAccRedfishResourceUpdateConfig(testingInfo TestingServerCredentials, transferProtocol string, imagePath string) string { From d02aec8507b8f39e4fd1859da03633cc018dff2d Mon Sep 17 00:00:00 2001 From: Rounak Adhikary Date: Wed, 18 Oct 2023 10:22:11 -0400 Subject: [PATCH 02/11] Added debug messages --- common/job_management.go | 4 +- go.mod | 1 - redfish/provider/common.go | 50 +++++++------ redfish/provider/provider.go | 1 + redfish/provider/resource_redfish_power.go | 5 +- .../resource_redfish_simple_update.go | 75 ++++++++++++------- .../resource_redfish_simple_update_test.go | 20 +++-- 7 files changed, 93 insertions(+), 63 deletions(-) diff --git a/common/job_management.go b/common/job_management.go index 12228b3a..c9aeb33e 100644 --- a/common/job_management.go +++ b/common/job_management.go @@ -21,7 +21,7 @@ const ( // - jobURI -> URI for the job to check. // - timeBetweenAttempts -> time to wait between attempts. I.e. 30 means 30 seconds. // - timeout -> maximun time to wait until job is considered failed. -func WaitForJobToFinish(service *gofish.Service, jobURI string, timeBetweenAttempts int, timeout int) error { +func WaitForJobToFinish(service *gofish.Service, jobURI string, timeBetweenAttempts int64, timeout int64) error { // Create tickers attemptTick := time.NewTicker(time.Duration(timeBetweenAttempts) * time.Second) timeoutTick := time.NewTicker(time.Duration(timeout) * time.Second) @@ -44,7 +44,7 @@ func WaitForJobToFinish(service *gofish.Service, jobURI string, timeBetweenAttem } case <-timeoutTick.C: log.Printf("[DEBUG] - Error. Timeout reached\n") - return fmt.Errorf("Timeout waiting for the job to finish") + return fmt.Errorf("timeout waiting for the job to finish") } } } diff --git a/go.mod b/go.mod index 02eaeed3..08e57d6a 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,6 @@ require ( github.com/Masterminds/sprig/v3 v3.2.2 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // indirect github.com/agext/levenshtein v1.2.2 // indirect - github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/cloudflare/circl v1.3.3 // indirect diff --git a/redfish/provider/common.go b/redfish/provider/common.go index 6c96f5d8..6839068b 100644 --- a/redfish/provider/common.go +++ b/redfish/provider/common.go @@ -1,6 +1,7 @@ package provider import ( + "context" "errors" "fmt" "log" @@ -8,7 +9,7 @@ import ( "time" "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/stmcginnis/gofish" "github.com/stmcginnis/gofish/redfish" ) @@ -89,6 +90,11 @@ func NewConfig(pconfig *redfishProvider, rserver *models.RedfishServer) (*gofish return api.Service, nil } +type powerOperator struct { + ctx context.Context + service *gofish.Service +} + // PowerOperation Executes a power operation against the target server. It takes four arguments. The first is the reset // type. See the struct "ResetType" at https://github.com/stmcginnis/gofish/blob/main/redfish/computersystem.go for all // possible options. The second is maximumWaitTime which is the maximum amount of time to wait for the server to reach @@ -96,21 +102,20 @@ func NewConfig(pconfig *redfishProvider, rserver *models.RedfishServer) (*gofish // server's power state for updates. The last is a pointer to a gofish.Service object with which the function can // interact with the server. It will return a tuple consisting of the server's power state at time of return and // diagnostics -func PowerOperation(resetType string, maximumWaitTime int, checkInterval int, service *gofish.Service) (redfish.PowerState, diag.Diagnostics) { - var diags diag.Diagnostics +func (p powerOperator) PowerOperation(resetType string, maximumWaitTime int64, checkInterval int64) (redfish.PowerState, error) { - system, err := getSystemResource(service) + system, err := getSystemResource(p.service) if err != nil { - log.Printf("[ERROR]: Failed to identify system: %s", err) - return "", diag.Errorf(err.Error()) + tflog.Error(p.ctx, fmt.Sprintf("Failed to identify system: %s", err)) + return "", fmt.Errorf("failed to identify system: %w", err) } var targetPowerState redfish.PowerState if resetType == "ForceOff" || resetType == "GracefulShutdown" { if system.PowerState == "Off" { - log.Printf("[TRACE]: Server already powered off. No action required.") - return redfish.OffPowerState, diags + tflog.Trace(p.ctx, "Server already powered off. No action required.") + return redfish.OffPowerState, nil } else { targetPowerState = "Off" } @@ -118,8 +123,8 @@ func PowerOperation(resetType string, maximumWaitTime int, checkInterval int, se if resetType == "On" || resetType == "ForceOn" { if system.PowerState == "On" { - log.Printf("[TRACE]: Server already powered on. No action required.") - return redfish.OnPowerState, diags + tflog.Error(p.ctx, "Server already powered on. No action required") + return redfish.OnPowerState, nil } else { targetPowerState = "On" } @@ -143,38 +148,37 @@ func PowerOperation(resetType string, maximumWaitTime int, checkInterval int, se } // Run the power operation against the target server - log.Printf("[TRACE]: Performing system.Reset(%s)", resetType) + tflog.Trace(p.ctx, fmt.Sprintf("Performing system.Reset(%s)", resetType)) if err = system.Reset(redfish.ResetType(resetType)); err != nil { - log.Printf("[WARN]: system.Reset returned an error: %s", err) - return system.PowerState, diag.Errorf(err.Error()) + tflog.Warn(p.ctx, fmt.Sprintf("system.Reset returned an error: %s", err)) + return system.PowerState, err } // Wait for the server to be in the correct power state - totalTime := 0 + var totalTime int64 = 0 for totalTime < maximumWaitTime { time.Sleep(time.Duration(checkInterval) * time.Second) totalTime += checkInterval - log.Printf("[TRACE]: Total time is %d seconds. Checking power state now.", totalTime) + tflog.Trace(p.ctx, fmt.Sprintf("Total time is %d seconds. Checking power state now.", totalTime)) - system, err := getSystemResource(service) + system, err := getSystemResource(p.service) if err != nil { - log.Printf("[ERROR]: Failed to identify system: %s", err) - return targetPowerState, diag.Errorf(err.Error()) + tflog.Error(p.ctx, fmt.Sprintf("Failed to identify system: %s", err)) + return targetPowerState, err } if system.PowerState == targetPowerState { - log.Printf("[TRACE]: system.Reset successful") - return system.PowerState, diags + tflog.Debug(p.ctx, "system.Reset successful") + return system.PowerState, nil } - } // If we've reached here it means the system never reached the appropriate target state // We will instead set the power state to whatever the current state is and return // TODO : Change to warning when updated to plugin framework - log.Printf("[ERROR]: The system failed to update the server's power status within the maximum wait time specified!") - return system.PowerState, diags + tflog.Warn(p.ctx, "The system failed to update the server's power status within the maximum wait time specified!") + return system.PowerState, nil } // import ( diff --git a/redfish/provider/provider.go b/redfish/provider/provider.go index bdb924c6..d083e35b 100644 --- a/redfish/provider/provider.go +++ b/redfish/provider/provider.go @@ -96,6 +96,7 @@ func (p *redfishProvider) Configure(ctx context.Context, req provider.ConfigureR func (p *redfishProvider) Resources(ctx context.Context) []func() resource.Resource { return []func() resource.Resource{ NewPowerResource, + NewSimpleUpdateResource, } } diff --git a/redfish/provider/resource_redfish_power.go b/redfish/provider/resource_redfish_power.go index 5eecec0f..1c1b1e9e 100644 --- a/redfish/provider/resource_redfish_power.go +++ b/redfish/provider/resource_redfish_power.go @@ -149,8 +149,9 @@ func (r *powerResource) Create(ctx context.Context, req resource.CreateRequest, plan.PowerId = types.StringValue(system.SerialNumber + "_power") resetType := plan.DesiredPowerAction.ValueString() - powerState, diags1 := PowerOperation(resetType, int(plan.MaximumWaitTime.ValueInt64()), int(plan.CheckInterval.ValueInt64()), service) - if diags1.HasError() { + pOp := powerOperator{ctx, service} + powerState, pErr := pOp.PowerOperation(resetType, plan.MaximumWaitTime.ValueInt64(), plan.CheckInterval.ValueInt64()) + if pErr != nil { return } // time to allow changes to get reflected diff --git a/redfish/provider/resource_redfish_simple_update.go b/redfish/provider/resource_redfish_simple_update.go index bcaf3893..e275833e 100644 --- a/redfish/provider/resource_redfish_simple_update.go +++ b/redfish/provider/resource_redfish_simple_update.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "io" - "log" "net/http" "os" "path/filepath" @@ -30,9 +29,9 @@ import ( ) const ( - defaultSimpleUpdateResetTimeout int = 120 - defaultSimpleUpdateJobTimeout int = 1200 - intervalSimpleUpdateJobCheckTime int = 10 + defaultSimpleUpdateResetTimeout int = 120 + defaultSimpleUpdateJobTimeout int = 1200 + intervalSimpleUpdateJobCheckTime int64 = 10 ) // Ensure the implementation satisfies the expected interfaces. @@ -195,7 +194,11 @@ func (r *simpleUpdateResource) Create(ctx context.Context, req resource.CreateRe plan.Id = types.StringValue(system.SerialNumber + "_simple_update") // resetType := plan.DesiredPowerAction.ValueString() - dia, state := updateRedfishSimpleUpdate(ctx, service, plan) + updater := simpleUpdater{ + ctx: ctx, + service: service, + } + dia, state := updater.updateRedfishSimpleUpdate(plan) resp.Diagnostics.Append(dia...) if resp.Diagnostics.HasError() { return @@ -269,7 +272,13 @@ func readRedfishSimpleUpdate(service *gofish.Service, d models.SimpleUpdateRes) return diags, d } -func updateRedfishSimpleUpdate(ctx context.Context, service *gofish.Service, d models.SimpleUpdateRes) (diag.Diagnostics, models.SimpleUpdateRes) { +type simpleUpdater struct { + ctx context.Context + service *gofish.Service + updateService *redfish.UpdateService +} + +func (u simpleUpdater) updateRedfishSimpleUpdate(d models.SimpleUpdateRes) (diag.Diagnostics, models.SimpleUpdateRes) { var diags diag.Diagnostics ret := d @@ -278,7 +287,7 @@ func updateRedfishSimpleUpdate(ctx context.Context, service *gofish.Service, d m resetType := d.ResetType.ValueString() // Check if chosen reset type is supported before doing anything else - systems, err := service.Systems() + systems, err := u.service.Systems() if err != nil { diags.AddError( "Couldn't retrieve allowed reset types from systems", @@ -286,6 +295,7 @@ func updateRedfishSimpleUpdate(ctx context.Context, service *gofish.Service, d m ) return diags, ret } + tflog.Debug(u.ctx, "resource_simple_update : found system") if ok := checkResetType(resetType, systems[0].SupportedResetTypes); !ok { diags.AddError( @@ -294,13 +304,16 @@ func updateRedfishSimpleUpdate(ctx context.Context, service *gofish.Service, d m ) return diags, ret } + tflog.Debug(u.ctx, "resource_simple_update : reset type "+resetType+"is available") // Get update service from root - updateService, err := service.UpdateService() + updateService, err := u.service.UpdateService() if err != nil { diags.AddError("error while retrieving UpdateService", err.Error()) return diags, ret } + tflog.Debug(u.ctx, "resource_simple_update : found update service") + u.updateService = updateService // Check if the transfer protocol is available in the redfish instance err = checkTransferProtocol(transferProtocol, updateService) @@ -315,9 +328,10 @@ func updateRedfishSimpleUpdate(ctx context.Context, service *gofish.Service, d m ) return diags, ret // !!!! append list of supported transfer protocols } + tflog.Debug(u.ctx, "resource_simple_update : update type "+transferProtocol+" is valid") if transferProtocol == "NFS" { - id, err := pullUpdate(service, d) + id, err := u.pullUpdate(d) if err != nil { diags.AddError(err.Error(), "") } else { @@ -325,23 +339,24 @@ func updateRedfishSimpleUpdate(ctx context.Context, service *gofish.Service, d m } } else if transferProtocol == "HTTP" || transferProtocol == "HTTPS" { if strings.HasPrefix(targetFirmwareImage, "http") { - id, err := pullUpdate(service, d) + id, err := u.pullUpdate(d) if err != nil { diags.AddError(err.Error(), "") } else { d.Id = types.StringValue(id) } } else { - fwPackage, err := uploadLocalFirmware(service, updateService, d) + fwPackage, err := u.uploadLocalFirmware(d) if err != nil { // TBD - HOW TO HANDLE WHEN FAILS BUT FIRMWARE WAS INSTALLED? diags.AddError(err.Error(), "") + return diags, ret } ret.SoftwareId = types.StringValue(fwPackage.SoftwareID) ret.Version = types.StringValue(fwPackage.Version) ret.Id = types.StringValue(fwPackage.ODataID) - diagsRead, state := readRedfishSimpleUpdate(service, d) + diagsRead, state := readRedfishSimpleUpdate(u.service, d) diags.Append(diagsRead...) ret = state } @@ -352,8 +367,9 @@ func updateRedfishSimpleUpdate(ctx context.Context, service *gofish.Service, d m return diags, ret } -func uploadLocalFirmware(service *gofish.Service, updateService *redfish.UpdateService, d models.SimpleUpdateRes) (*redfish.SoftwareInventory, error) { +func (u simpleUpdater) uploadLocalFirmware(d models.SimpleUpdateRes) (*redfish.SoftwareInventory, error) { // Get ETag from FW inventory + service, updateService := u.service, u.updateService response, err := service.GetClient().Get(updateService.FirmwareInventory) if err != nil { return nil, fmt.Errorf("error while retrieving Etag from FirmwareInventory: %w", err) @@ -406,10 +422,11 @@ func uploadLocalFirmware(service *gofish.Service, updateService *redfish.UpdateS } response.Body.Close() - err = updateJobStatus(service, d, response) + err = u.updateJobStatus(d, response) if err != nil { return nil, fmt.Errorf("error running job %w", err) } + tflog.Debug(u.ctx, "resource_simple_update : Job finished successfully") // Get updated FW inventory fwInventory, err := updateService.FirmwareInventories() if err != nil { @@ -463,12 +480,10 @@ func getFWfromInventory(softwareInventories []*redfish.SoftwareInventory, softwa return nil, fmt.Errorf("couldn't find FW on Firmware inventory") } -func pullUpdate(service *gofish.Service, d models.SimpleUpdateRes) (string, error) { +func (u simpleUpdater) pullUpdate(d models.SimpleUpdateRes) (string, error) { // Get update service from root - updateService, err := service.UpdateService() - if err != nil { - return "", fmt.Errorf("error while retrieving UpdateService - %w", err) - } + updateService := u.updateService + service := u.service protocol := d.Protocol.ValueString() imagePath := d.Image.ValueString() @@ -477,6 +492,7 @@ func pullUpdate(service *gofish.Service, d models.SimpleUpdateRes) (string, erro payload := make(map[string]interface{}) payload["ImageURI"] = imagePath payload["TransferProtocol"] = protocol + tflog.Trace(u.ctx, fmt.Sprintf("resource_simple_update : Job is scheduling payload %v", payload)) response, err := service.GetClient().Post(httpURI, payload) if err != nil { @@ -486,7 +502,8 @@ func pullUpdate(service *gofish.Service, d models.SimpleUpdateRes) (string, erro // Get jobid jobID := response.Header.Get("Location") - err = updateJobStatus(service, d, response) + tflog.Debug(u.ctx, "resource_simple_update : Job is scheduled with id "+jobID) + err = u.updateJobStatus(d, response) if err != nil { // Delete uploaded package - TBD return "", fmt.Errorf("there was an issue when waiting for the job to complete - %s", err) @@ -510,26 +527,30 @@ func pullUpdate(service *gofish.Service, d models.SimpleUpdateRes) (string, erro return swInventory.ODataID, nil } -func updateJobStatus(service *gofish.Service, d models.SimpleUpdateRes, response *http.Response) error { +func (u simpleUpdater) updateJobStatus(d models.SimpleUpdateRes, response *http.Response) error { // Get jobid jobID := response.Header.Get("Location") resetTimeout := d.ResetTimeout.ValueInt64() simpleUpdateJobTimeout := d.JobTimeout.ValueInt64() - log.Printf("[DEBUG] resetTimeout is set to %d and simpleUpdateJobTimeout to %d", resetTimeout, simpleUpdateJobTimeout) + tflog.Debug(u.ctx, fmt.Sprintf( + "resource_simple_update : resetTimeout is set to %d and simpleUpdateJobTimeout to %d", + resetTimeout, + simpleUpdateJobTimeout)) // Reboot the server - _, diags := PowerOperation(d.ResetType.ValueString(), int(resetTimeout), intervalSimpleUpdateJobCheckTime, service) - if diags.HasError() { + pOp := powerOperator{u.ctx, u.service} + _, err := pOp.PowerOperation(d.ResetType.ValueString(), resetTimeout, intervalSimpleUpdateJobCheckTime) + if err != nil { // Delete uploaded package - TBD - return fmt.Errorf("there was an issue when restarting the server") + return fmt.Errorf("there was an issue when restarting the server: %w", err) } // Check JID - err := common.WaitForJobToFinish(service, jobID, intervalSimpleUpdateJobCheckTime, int(simpleUpdateJobTimeout)) + err = common.WaitForJobToFinish(u.service, jobID, intervalSimpleUpdateJobCheckTime, simpleUpdateJobTimeout) if err != nil { // Delete uploaded package - TBD - return fmt.Errorf("there was an issue when waiting for the job to complete - %s", err) + return fmt.Errorf("there was an issue when waiting for the job to complete - %w", err) } return nil diff --git a/redfish/provider/resource_redfish_simple_update_test.go b/redfish/provider/resource_redfish_simple_update_test.go index 6aff88a0..938f0067 100644 --- a/redfish/provider/resource_redfish_simple_update_test.go +++ b/redfish/provider/resource_redfish_simple_update_test.go @@ -11,6 +11,10 @@ import ( // Test to create and update Simple update - Positive func TestAccRedfishSimpleUpdate_basic(t *testing.T) { + t.Log(testAccRedfishResourceUpdateConfig( + creds, + "HTTP", + os.Getenv("TF_TESTING_FIRMWARE_IMAGE_LOCAL"))) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, @@ -66,13 +70,13 @@ func TestAccRedfishSimpleUpdate_InvalidProto(t *testing.T) { os.Getenv("TF_TESTING_FIRMWARE_IMAGE_HTTP")), ExpectError: regexp.MustCompile("this transfer protocol is not available in this redfish instance"), }, - { - Config: testAccRedfishResourceUpdateConfig( - creds, - "CIFS", - os.Getenv("TF_TESTING_FIRMWARE_IMAGE_HTTP")), - ExpectError: regexp.MustCompile("Transfer protocol not available in this implementation"), - }, + // { + // Config: testAccRedfishResourceUpdateConfig( + // creds, + // "CIFS", + // os.Getenv("TF_TESTING_FIRMWARE_IMAGE_HTTP")), + // ExpectError: regexp.MustCompile("Transfer protocol not available in this implementation"), + // }, }, }) } @@ -84,7 +88,7 @@ func testAccRedfishResourceUpdateConfig(testingInfo TestingServerCredentials, resource "redfish_simple_update" "update" { - redfish_server { + redfish_server = { user = "%s" password = "%s" endpoint = "https://%s" From 6744bb50f07595f3a93e00824ee870a86429b2f4 Mon Sep 17 00:00:00 2001 From: Rounak Adhikary Date: Wed, 25 Oct 2023 15:53:02 -0400 Subject: [PATCH 03/11] me more changes --- redfish/provider/common.go | 1 - .../resource_redfish_simple_update.go | 89 ++++++++++++------- .../resource_redfish_simple_update_test.go | 29 ++++-- 3 files changed, 77 insertions(+), 42 deletions(-) diff --git a/redfish/provider/common.go b/redfish/provider/common.go index 6839068b..e3dc7079 100644 --- a/redfish/provider/common.go +++ b/redfish/provider/common.go @@ -103,7 +103,6 @@ type powerOperator struct { // interact with the server. It will return a tuple consisting of the server's power state at time of return and // diagnostics func (p powerOperator) PowerOperation(resetType string, maximumWaitTime int64, checkInterval int64) (redfish.PowerState, error) { - system, err := getSystemResource(p.service) if err != nil { tflog.Error(p.ctx, fmt.Sprintf("Failed to identify system: %s", err)) diff --git a/redfish/provider/resource_redfish_simple_update.go b/redfish/provider/resource_redfish_simple_update.go index e275833e..c2ad4971 100644 --- a/redfish/provider/resource_redfish_simple_update.go +++ b/redfish/provider/resource_redfish_simple_update.go @@ -5,12 +5,12 @@ import ( "errors" "fmt" "io" - "net/http" "os" "path/filepath" "strings" "terraform-provider-redfish/common" "terraform-provider-redfish/redfish/models" + "time" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -68,6 +68,9 @@ func SimpleUpdateSchema() map[string]schema.Attribute { Description: "ID of the simple update resource", MarkdownDescription: "ID of the simple update resource", Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, "redfish_server": schema.SingleNestedAttribute{ MarkdownDescription: "Redfish Server", @@ -113,10 +116,10 @@ func SimpleUpdateSchema() map[string]schema.Attribute { resp *stringplanmodifier.RequiresReplaceIfFuncResponse, ) { spath, ppath := req.StateValue.ValueString(), req.ConfigValue.ValueString() + resp.RequiresReplace = true if filepath.Base(spath) == filepath.Base(ppath) { resp.RequiresReplace = false } - resp.RequiresReplace = true }, "", "", @@ -153,10 +156,16 @@ func SimpleUpdateSchema() map[string]schema.Attribute { "software_id": schema.StringAttribute{ Computed: true, Description: "Software ID from the firmware package uploaded", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, "version": schema.StringAttribute{ Computed: true, Description: "Software version from the firmware package uploaded", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, } } @@ -229,24 +238,17 @@ func (r *simpleUpdateResource) Read(ctx context.Context, req resource.ReadReques // Update also refreshes the resource and writes to state func (r *simpleUpdateResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - // update can be triggerred by only a change in non-functional requirements. - // So set them to state. + // update can be triggerred by only a change in image path, where base name of image remains same + // So set plan to state. tflog.Trace(ctx, "resource_simple_update update : Started") // Get Plan Data - var state models.SimpleUpdateRes - diags := req.State.Get(ctx, &state) + var plan models.SimpleUpdateRes + diags := req.Plan.Get(ctx, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } - service, err := NewConfig(r.p, &state.RedfishServer) - if err != nil { - resp.Diagnostics.AddError("service error", err.Error()) - return - } - dia, newState := readRedfishSimpleUpdate(service, state) - resp.Diagnostics.Append(dia...) - resp.Diagnostics.Append(resp.State.Set(ctx, &newState)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } // Delete removes resource from state @@ -265,7 +267,7 @@ func readRedfishSimpleUpdate(service *gofish.Service, d models.SimpleUpdateRes) diags.AddError("there was an issue with the API", err.Error()) } else { // the firmware package previously applied has changed, trigger update - d.Image = types.StringValue("none") + d.Image = types.StringNull() } } @@ -331,22 +333,23 @@ func (u simpleUpdater) updateRedfishSimpleUpdate(d models.SimpleUpdateRes) (diag tflog.Debug(u.ctx, "resource_simple_update : update type "+transferProtocol+" is valid") if transferProtocol == "NFS" { - id, err := u.pullUpdate(d) + tflog.Info(u.ctx, "Remote NFS protocol detected") + ret, err = u.pullUpdate(ret) if err != nil { diags.AddError(err.Error(), "") - } else { - d.Id = types.StringValue(id) } + tflog.Debug(u.ctx, "Update Complete") } else if transferProtocol == "HTTP" || transferProtocol == "HTTPS" { if strings.HasPrefix(targetFirmwareImage, "http") { - id, err := u.pullUpdate(d) + tflog.Info(u.ctx, "Remote HTTP protocol detected") + ret, err = u.pullUpdate(ret) if err != nil { diags.AddError(err.Error(), "") - } else { - d.Id = types.StringValue(id) } + tflog.Debug(u.ctx, "Update Complete") } else { - fwPackage, err := u.uploadLocalFirmware(d) + tflog.Info(u.ctx, "Local firmware detected") + fwPackage, err := u.uploadLocalFirmware(ret) if err != nil { // TBD - HOW TO HANDLE WHEN FAILS BUT FIRMWARE WAS INSTALLED? diags.AddError(err.Error(), "") @@ -355,8 +358,9 @@ func (u simpleUpdater) updateRedfishSimpleUpdate(d models.SimpleUpdateRes) (diag ret.SoftwareId = types.StringValue(fwPackage.SoftwareID) ret.Version = types.StringValue(fwPackage.Version) ret.Id = types.StringValue(fwPackage.ODataID) + tflog.Info(u.ctx, "Uploading Local Firmware Complete") - diagsRead, state := readRedfishSimpleUpdate(u.service, d) + diagsRead, state := readRedfishSimpleUpdate(u.service, ret) diags.Append(diagsRead...) ret = state } @@ -422,22 +426,28 @@ func (u simpleUpdater) uploadLocalFirmware(d models.SimpleUpdateRes) (*redfish.S } response.Body.Close() - err = u.updateJobStatus(d, response) + // Get jobid + jobID := response.Header.Get("Location") + d.Id = types.StringValue(jobID) + err = u.updateJobStatus(d) if err != nil { return nil, fmt.Errorf("error running job %w", err) } tflog.Debug(u.ctx, "resource_simple_update : Job finished successfully") // Get updated FW inventory + time.Sleep(30 * time.Second) fwInventory, err := updateService.FirmwareInventories() if err != nil { // TBD - HOW TO HANDLE WHEN FAILS BUT FIRMWARE WAS INSTALLED? return nil, fmt.Errorf("error when getting firmware inventory - %w", err) } + tflog.Debug(u.ctx, "resource_simple_update : Retrieved Firmware Inventories") inv, err := getFWfromInventory(fwInventory, packageInformation.SoftwareID, packageInformation.Version) if err != nil { err = fmt.Errorf("error when retrieving fw package from fw inventory - %w", err) } + tflog.Debug(u.ctx, "resource_simple_update : Retrieved Status from Inventories") return inv, err } @@ -480,7 +490,7 @@ func getFWfromInventory(softwareInventories []*redfish.SoftwareInventory, softwa return nil, fmt.Errorf("couldn't find FW on Firmware inventory") } -func (u simpleUpdater) pullUpdate(d models.SimpleUpdateRes) (string, error) { +func (u simpleUpdater) pullUpdate(d models.SimpleUpdateRes) (models.SimpleUpdateRes, error) { // Get update service from root updateService := u.updateService service := u.service @@ -497,16 +507,18 @@ func (u simpleUpdater) pullUpdate(d models.SimpleUpdateRes) (string, error) { response, err := service.GetClient().Post(httpURI, payload) if err != nil { // Delete uploaded package - TBD - return "", fmt.Errorf("there was an issue when scheduling the update job - %s", err) + return d, fmt.Errorf("there was an issue when scheduling the update job - %s", err) } // Get jobid jobID := response.Header.Get("Location") - tflog.Debug(u.ctx, "resource_simple_update : Job is scheduled with id "+jobID) - err = u.updateJobStatus(d, response) + tflog.Info(u.ctx, "resource_simple_update : Job is scheduled with id "+jobID) + + d.Id = types.StringValue(jobID) + err = u.updateJobStatus(d) if err != nil { // Delete uploaded package - TBD - return "", fmt.Errorf("there was an issue when waiting for the job to complete - %s", err) + return d, fmt.Errorf("there was an issue when waiting for the job to complete - %s", err) } job, err := redfish.GetTask(service.GetClient(), jobID) @@ -517,19 +529,25 @@ func (u simpleUpdater) pullUpdate(d models.SimpleUpdateRes) (string, error) { } } if err != nil { - return "", err + return d, err } + tflog.Info(u.ctx, "Retrieved successful task") swInventory, err := redfish.GetSoftwareInventory(service.GetClient(), d.Id.ValueString()) if err != nil { - return "", fmt.Errorf("unable to fetch data %v", err) + return d, fmt.Errorf("unable to fetch data %w", err) } - return swInventory.ODataID, nil + tflog.Debug(u.ctx, "Retrieved inventory with ID "+swInventory.ODataID) + + d.Id = types.StringValue(swInventory.ODataID) + d.Version = types.StringValue(swInventory.Version) + d.SoftwareId = types.StringValue(swInventory.SoftwareID) + return d, nil } -func (u simpleUpdater) updateJobStatus(d models.SimpleUpdateRes, response *http.Response) error { +func (u simpleUpdater) updateJobStatus(d models.SimpleUpdateRes) error { // Get jobid - jobID := response.Header.Get("Location") + jobID := d.Id.ValueString() resetTimeout := d.ResetTimeout.ValueInt64() simpleUpdateJobTimeout := d.JobTimeout.ValueInt64() @@ -539,12 +557,14 @@ func (u simpleUpdater) updateJobStatus(d models.SimpleUpdateRes, response *http. simpleUpdateJobTimeout)) // Reboot the server + tflog.Debug(u.ctx, "Rebooting the server") pOp := powerOperator{u.ctx, u.service} _, err := pOp.PowerOperation(d.ResetType.ValueString(), resetTimeout, intervalSimpleUpdateJobCheckTime) if err != nil { // Delete uploaded package - TBD return fmt.Errorf("there was an issue when restarting the server: %w", err) } + tflog.Debug(u.ctx, "Reboot Complete") // Check JID err = common.WaitForJobToFinish(u.service, jobID, intervalSimpleUpdateJobCheckTime, simpleUpdateJobTimeout) @@ -552,6 +572,7 @@ func (u simpleUpdater) updateJobStatus(d models.SimpleUpdateRes, response *http. // Delete uploaded package - TBD return fmt.Errorf("there was an issue when waiting for the job to complete - %w", err) } + tflog.Debug(u.ctx, "Job has been completed") return nil } diff --git a/redfish/provider/resource_redfish_simple_update_test.go b/redfish/provider/resource_redfish_simple_update_test.go index 938f0067..d54b1c00 100644 --- a/redfish/provider/resource_redfish_simple_update_test.go +++ b/redfish/provider/resource_redfish_simple_update_test.go @@ -9,6 +9,21 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) +func TestAccRf(t *testing.T) { + t.Log(testAccRedfishResourceUpdateConfig( + creds, + "HTTP", + os.Getenv("TF_TESTING_FIRMWARE_IMAGE_LOCAL"))) + t.Log(testAccRedfishResourceUpdateConfig( + creds, + "HTTP", + os.Getenv("TF_TESTING_FIRMWARE_IMAGE_HTTP"))) + t.Log(testAccRedfishResourceUpdateConfig( + creds, + "NFS", + os.Getenv("TF_TESTING_FIRMWARE_IMAGE_NFS"))) +} + // Test to create and update Simple update - Positive func TestAccRedfishSimpleUpdate_basic(t *testing.T) { t.Log(testAccRedfishResourceUpdateConfig( @@ -70,13 +85,13 @@ func TestAccRedfishSimpleUpdate_InvalidProto(t *testing.T) { os.Getenv("TF_TESTING_FIRMWARE_IMAGE_HTTP")), ExpectError: regexp.MustCompile("this transfer protocol is not available in this redfish instance"), }, - // { - // Config: testAccRedfishResourceUpdateConfig( - // creds, - // "CIFS", - // os.Getenv("TF_TESTING_FIRMWARE_IMAGE_HTTP")), - // ExpectError: regexp.MustCompile("Transfer protocol not available in this implementation"), - // }, + { + Config: testAccRedfishResourceUpdateConfig( + creds, + "CIFS", + os.Getenv("TF_TESTING_FIRMWARE_IMAGE_HTTP")), + ExpectError: regexp.MustCompile("Transfer protocol not available in this implementation"), + }, }, }) } From 5f32e17235d19123707412a6e3ab3ed80e9672d6 Mon Sep 17 00:00:00 2001 From: Rounak Adhikary Date: Thu, 26 Oct 2023 02:34:55 -0400 Subject: [PATCH 04/11] Fixing lint issues --- redfish/models/simpleUpdate.go | 1 + .../resource_redfish_simple_update_test.go | 19 ------------------- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/redfish/models/simpleUpdate.go b/redfish/models/simpleUpdate.go index 90416042..6e7b0abf 100644 --- a/redfish/models/simpleUpdate.go +++ b/redfish/models/simpleUpdate.go @@ -4,6 +4,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) +// SimpleUpdateRes is struct for simple update resource type SimpleUpdateRes struct { Id types.String `tfsdk:"id"` RedfishServer RedfishServer `tfsdk:"redfish_server"` diff --git a/redfish/provider/resource_redfish_simple_update_test.go b/redfish/provider/resource_redfish_simple_update_test.go index d54b1c00..25667fb7 100644 --- a/redfish/provider/resource_redfish_simple_update_test.go +++ b/redfish/provider/resource_redfish_simple_update_test.go @@ -9,27 +9,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) -func TestAccRf(t *testing.T) { - t.Log(testAccRedfishResourceUpdateConfig( - creds, - "HTTP", - os.Getenv("TF_TESTING_FIRMWARE_IMAGE_LOCAL"))) - t.Log(testAccRedfishResourceUpdateConfig( - creds, - "HTTP", - os.Getenv("TF_TESTING_FIRMWARE_IMAGE_HTTP"))) - t.Log(testAccRedfishResourceUpdateConfig( - creds, - "NFS", - os.Getenv("TF_TESTING_FIRMWARE_IMAGE_NFS"))) -} - // Test to create and update Simple update - Positive func TestAccRedfishSimpleUpdate_basic(t *testing.T) { - t.Log(testAccRedfishResourceUpdateConfig( - creds, - "HTTP", - os.Getenv("TF_TESTING_FIRMWARE_IMAGE_LOCAL"))) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, From 1c3a9994adda5d13ca534573623705f0ee5b2498 Mon Sep 17 00:00:00 2001 From: Rounak Adhikary Date: Thu, 26 Oct 2023 02:43:43 -0400 Subject: [PATCH 05/11] Updating docs --- docs/resources/simple_update.md | 202 ++++++++++++++++++ .../resource_redfish_simple_update.go | 1 + .../resources}/simple_update.md.tmpl | 1 - 3 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 docs/resources/simple_update.md rename {template-bcks/resources-bck => templates/resources}/simple_update.md.tmpl (91%) diff --git a/docs/resources/simple_update.md b/docs/resources/simple_update.md new file mode 100644 index 00000000..cac18190 --- /dev/null +++ b/docs/resources/simple_update.md @@ -0,0 +1,202 @@ +--- +# Copyright (c) 2023 Dell Inc., or its subsidiaries. All Rights Reserved. +# +# Licensed under the Mozilla Public License Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://mozilla.org/MPL/2.0/ +# +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +title: "redfish_simple_update resource" +linkTitle: "redfish_simple_update" +page_title: "redfish_simple_update Resource - terraform-provider-redfish" +subcategory: "" +description: |- + Resource for managing power. +--- + +# redfish_simple_update (Resource) + +Resource for managing power. +This Terraform resource is used to Update the iDRAC Server. We can Read the existing version or update the same using this resource. + +## Example Usage + +variables.tf +```terraform +/* +Copyright (c) 2023 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the Mozilla Public License Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://mozilla.org/MPL/2.0/ + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +variable "rack1" { + type = map(object({ + user = string + password = string + endpoint = string + ssl_insecure = bool + })) +} +``` + +terraform.tfvars +```terraform +/* +Copyright (c) 2023 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the Mozilla Public License Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://mozilla.org/MPL/2.0/ + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +rack1 = { + "my-server-1" = { + user = "admin" + password = "passw0rd" + endpoint = "https://my-server-1.myawesomecompany.org" + ssl_insecure = true + }, + "my-server-2" = { + user = "admin" + password = "passw0rd" + endpoint = "https://my-server-2.myawesomecompany.org" + ssl_insecure = true + }, +} +``` + +provider.tf +```terraform +/* +Copyright (c) 2023 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the Mozilla Public License Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://mozilla.org/MPL/2.0/ + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +terraform { + required_providers { + redfish = { + version = "1.0.0" + source = "registry.terraform.io/dell/redfish" + } + } +} +``` + +main.tf +```terraform +/* +Copyright (c) 2023 Dell Inc., or its subsidiaries. All Rights Reserved. + +Licensed under the Mozilla Public License Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://mozilla.org/MPL/2.0/ + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +resource "redfish_simple_update" "update" { + for_each = var.rack1 + + redfish_server { + user = each.value.user + password = each.value.password + endpoint = each.value.endpoint + ssl_insecure = each.value.ssl_insecure + } + + // The network protocols and image for firmware update + transfer_protocol = "HTTP" + target_firmware_image = "/home/mikeletux/Downloads/BIOS_FXC54_WN64_1.15.0.EXE" + // Reset parameters to be applied when upgrade is completed + reset_type = "ForceRestart" + reset_timeout = 120 // If not set, by default will be 120s + // The maximum amount of time to wait for the simple update job to be completed + simple_update_job_timeout = 1200 // If not set, by default will be 1200s +} +``` + +After the successful execution of the above resource block, firmware would have got updated. It can be verified through state file. + + +## Schema + +### Required + +- `redfish_server` (Attributes) Redfish Server (see [below for nested schema](#nestedatt--redfish_server)) +- `reset_type` (String) Reset type allows to choose the type of restart to apply when firmware upgrade is scheduled.Possible values are: "ForceRestart", "GracefulRestart" or "PowerCycle" +- `target_firmware_image` (String) Target firmware image used for firmware update on the redfish instance. Make sure you place your firmware packages in the same folder as the module and set it as follows: "${path.module}/BIOS_FXC54_WN64_1.15.0.EXE" +- `transfer_protocol` (String) The network protocol that the Update Service uses to retrieve the software image file located at the URI provided in ImageURI, if the URI does not contain a scheme. Accepted values: CIFS, FTP, SFTP, HTTP, HTTPS, NSF, SCP, TFTP, OEM, NFS. Currently only HTTP, HTTPS and NFS are supported with local file path or HTTP(s)/NFS link. + +### Optional + +- `reset_timeout` (Number) reset_timeout is the time in seconds that the provider waits for the server to be reset before timing out. +- `simple_update_job_timeout` (Number) simple_update_job_timeout is the time in seconds that the provider waits for the simple update job to be completed before timing out. + +### Read-Only + +- `id` (String) ID of the simple update resource +- `software_id` (String) Software ID from the firmware package uploaded +- `version` (String) Software version from the firmware package uploaded + + +### Nested Schema for `redfish_server` + +Required: + +- `endpoint` (String) Server BMC IP address or hostname + +Optional: + +- `password` (String, Sensitive) User password for login +- `user` (String) User name for login +- `validate_cert` (Boolean) This field indicates whether the SSL/TLS certificate must be verified or not + + + diff --git a/redfish/provider/resource_redfish_simple_update.go b/redfish/provider/resource_redfish_simple_update.go index c2ad4971..72157519 100644 --- a/redfish/provider/resource_redfish_simple_update.go +++ b/redfish/provider/resource_redfish_simple_update.go @@ -435,6 +435,7 @@ func (u simpleUpdater) uploadLocalFirmware(d models.SimpleUpdateRes) (*redfish.S } tflog.Debug(u.ctx, "resource_simple_update : Job finished successfully") // Get updated FW inventory + // sleep time to allow the inventory service to get started time.Sleep(30 * time.Second) fwInventory, err := updateService.FirmwareInventories() if err != nil { diff --git a/template-bcks/resources-bck/simple_update.md.tmpl b/templates/resources/simple_update.md.tmpl similarity index 91% rename from template-bcks/resources-bck/simple_update.md.tmpl rename to templates/resources/simple_update.md.tmpl index 6bae7bad..02bd87be 100644 --- a/template-bcks/resources-bck/simple_update.md.tmpl +++ b/templates/resources/simple_update.md.tmpl @@ -27,7 +27,6 @@ description: |- {{ .Description | trimspace }} This Terraform resource is used to Update the iDRAC Server. We can Read the existing version or update the same using this resource. -~> **Note:** In case of any failure in the middle of update, you can check the server. If required to run again, then terraform destroy need to be performed first. {{ if .HasExample -}} ## Example Usage From ccf84a8e65302129349928ca2ac774317597f1fa Mon Sep 17 00:00:00 2001 From: Rounak Adhikary Date: Thu, 26 Oct 2023 11:48:47 -0400 Subject: [PATCH 06/11] Some cleanup --- redfish/provider/common.go | 2 +- redfish/provider/resource_redfish_simple_update.go | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/redfish/provider/common.go b/redfish/provider/common.go index 1a527ff6..9653ee82 100644 --- a/redfish/provider/common.go +++ b/redfish/provider/common.go @@ -148,7 +148,7 @@ func (p powerOperator) PowerOperation(resetType string, maximumWaitTime int64, c if resetType == "On" || resetType == "ForceOn" { if system.PowerState == powerON { - tflog.Error(p.ctx, "Server already powered on. No action required") + tflog.Trace(p.ctx, "Server already powered on. No action required") return redfish.OnPowerState, nil } targetPowerState = powerON diff --git a/redfish/provider/resource_redfish_simple_update.go b/redfish/provider/resource_redfish_simple_update.go index 72157519..94f20240 100644 --- a/redfish/provider/resource_redfish_simple_update.go +++ b/redfish/provider/resource_redfish_simple_update.go @@ -99,15 +99,10 @@ func SimpleUpdateSchema() map[string]schema.Attribute { "target_firmware_image": schema.StringAttribute{ Required: true, Description: "Target firmware image used for firmware update on the redfish instance. " + - "Make sure you place your firmware packages in the same folder as the module and set it as follows: \"${path.module}/BIOS_FXC54_WN64_1.15.0.EXE\"", + "Make sure you place your firmware packages in the same folder as the module and set " + + "it as follows: \"${path.module}/BIOS_FXC54_WN64_1.15.0.EXE\"", // DiffSuppressFunc will allow moving fw packages through the filesystem without triggering an update if so. // At the moment it uses filename to see if they're the same. We need to strengthen that by somehow using hashing - // DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - // if filepath.Base(old) == filepath.Base(new) { - // return true - // } - // return false - // }, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplaceIf( func( From 9a8719ff65dd4f191e96ab27a59a11c2086f6c3e Mon Sep 17 00:00:00 2001 From: Rounak Adhikary Date: Thu, 26 Oct 2023 11:52:17 -0400 Subject: [PATCH 07/11] lint fix --- redfish/provider/resource_redfish_simple_update.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redfish/provider/resource_redfish_simple_update.go b/redfish/provider/resource_redfish_simple_update.go index 94f20240..e15f4521 100644 --- a/redfish/provider/resource_redfish_simple_update.go +++ b/redfish/provider/resource_redfish_simple_update.go @@ -50,7 +50,7 @@ type simpleUpdateResource struct { } // Configure implements resource.ResourceWithConfigure -func (r *simpleUpdateResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { +func (r *simpleUpdateResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { return } From fe0890d18e6747c3d41294131c53694a63564aae Mon Sep 17 00:00:00 2001 From: Rounak Adhikary Date: Fri, 27 Oct 2023 04:57:32 -0400 Subject: [PATCH 08/11] Fixing linter issues --- redfish/provider/common.go | 2 +- .../resource_redfish_dell_idrac_attributes.go | 6 ++-- .../resource_redfish_simple_update.go | 32 +++++++++---------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/redfish/provider/common.go b/redfish/provider/common.go index 9653ee82..0c88c75c 100644 --- a/redfish/provider/common.go +++ b/redfish/provider/common.go @@ -180,7 +180,7 @@ func (p powerOperator) PowerOperation(resetType string, maximumWaitTime int64, c } // Wait for the server to be in the correct power state - var totalTime int64 = 0 + var totalTime int64 for totalTime < maximumWaitTime { time.Sleep(time.Duration(checkInterval) * time.Second) totalTime += checkInterval diff --git a/redfish/provider/resource_redfish_dell_idrac_attributes.go b/redfish/provider/resource_redfish_dell_idrac_attributes.go index 3c550216..2b550490 100644 --- a/redfish/provider/resource_redfish_dell_idrac_attributes.go +++ b/redfish/provider/resource_redfish_dell_idrac_attributes.go @@ -297,10 +297,10 @@ func readRedfishDellIdracAttributes(_ context.Context, service *gofish.Service, for k, v := range old { // Check if attribute from config exists in idrac attributes - attrValue := idracAttributes.Attributes[k] - // This is done to avoid triggering an update when reading Password values, + attrValue := idracAttributes.Attributes[k] + // This is done to avoid triggering an update when reading Password values, // that are shown as null (nil to Go) - if attrValue != nil { + if attrValue != nil { readAttributes[k] = v } else { readAttributes[k] = v.(types.String) diff --git a/redfish/provider/resource_redfish_simple_update.go b/redfish/provider/resource_redfish_simple_update.go index e15f4521..6c2172fe 100644 --- a/redfish/provider/resource_redfish_simple_update.go +++ b/redfish/provider/resource_redfish_simple_update.go @@ -39,18 +39,18 @@ var ( _ resource.Resource = &simpleUpdateResource{} ) -// NewpowerResource is a helper function to simplify the provider implementation. +// NewSimpleUpdateResource is a helper function to simplify the provider implementation. func NewSimpleUpdateResource() resource.Resource { return &simpleUpdateResource{} } -// powerResource is the resource implementation. +// simpleUpdateResource is the resource implementation. type simpleUpdateResource struct { p *redfishProvider } // Configure implements resource.ResourceWithConfigure -func (r *simpleUpdateResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { +func (r *simpleUpdateResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { if req.ProviderData == nil { return } @@ -58,11 +58,11 @@ func (r *simpleUpdateResource) Configure(_ context.Context, req resource.Configu } // Metadata returns the resource type name. -func (r *simpleUpdateResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { +func (*simpleUpdateResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { resp.TypeName = req.ProviderTypeName + "simple_update" } -func SimpleUpdateSchema() map[string]schema.Attribute { +func simpleUpdateSchema() map[string]schema.Attribute { return map[string]schema.Attribute{ "id": schema.StringAttribute{ Description: "ID of the simple update resource", @@ -124,7 +124,7 @@ func SimpleUpdateSchema() map[string]schema.Attribute { "reset_type": schema.StringAttribute{ Required: true, Description: "Reset type allows to choose the type of restart to apply when firmware upgrade is scheduled." + - "Possible values are: \"ForceRestart\", \"GracefulRestart\" or \"PowerCycle\"", + " Possible values are: \"ForceRestart\", \"GracefulRestart\" or \"PowerCycle\"", Validators: []validator.String{ stringvalidator.OneOf([]string{ string(redfish.ForceRestartResetType), @@ -140,13 +140,13 @@ func SimpleUpdateSchema() map[string]schema.Attribute { Optional: true, Computed: true, Default: int64default.StaticInt64(int64(defaultSimpleUpdateResetTimeout)), - Description: "reset_timeout is the time in seconds that the provider waits for the server to be reset before timing out.", + Description: "Time in seconds that the provider waits for the server to be reset before timing out.", }, "simple_update_job_timeout": schema.Int64Attribute{ Optional: true, Computed: true, Default: int64default.StaticInt64(int64(defaultSimpleUpdateJobTimeout)), - Description: "simple_update_job_timeout is the time in seconds that the provider waits for the simple update job to be completed before timing out.", + Description: "Time in seconds that the provider waits for the simple update job to be completed before timing out.", }, "software_id": schema.StringAttribute{ Computed: true, @@ -166,11 +166,11 @@ func SimpleUpdateSchema() map[string]schema.Attribute { } // Schema defines the schema for the resource. -func (r *simpleUpdateResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { +func (*simpleUpdateResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ MarkdownDescription: "Resource for managing power.", Version: 1, - Attributes: SimpleUpdateSchema(), + Attributes: simpleUpdateSchema(), } } @@ -232,7 +232,7 @@ func (r *simpleUpdateResource) Read(ctx context.Context, req resource.ReadReques } // Update also refreshes the resource and writes to state -func (r *simpleUpdateResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { +func (*simpleUpdateResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // update can be triggerred by only a change in image path, where base name of image remains same // So set plan to state. tflog.Trace(ctx, "resource_simple_update update : Started") @@ -247,7 +247,7 @@ func (r *simpleUpdateResource) Update(ctx context.Context, req resource.UpdateRe } // Delete removes resource from state -func (r *simpleUpdateResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { +func (*simpleUpdateResource) Delete(ctx context.Context, _ resource.DeleteRequest, resp *resource.DeleteResponse) { resp.State.RemoveResource(ctx) } @@ -275,7 +275,7 @@ type simpleUpdater struct { updateService *redfish.UpdateService } -func (u simpleUpdater) updateRedfishSimpleUpdate(d models.SimpleUpdateRes) (diag.Diagnostics, models.SimpleUpdateRes) { +func (u *simpleUpdater) updateRedfishSimpleUpdate(d models.SimpleUpdateRes) (diag.Diagnostics, models.SimpleUpdateRes) { var diags diag.Diagnostics ret := d @@ -366,7 +366,7 @@ func (u simpleUpdater) updateRedfishSimpleUpdate(d models.SimpleUpdateRes) (diag return diags, ret } -func (u simpleUpdater) uploadLocalFirmware(d models.SimpleUpdateRes) (*redfish.SoftwareInventory, error) { +func (u *simpleUpdater) uploadLocalFirmware(d models.SimpleUpdateRes) (*redfish.SoftwareInventory, error) { // Get ETag from FW inventory service, updateService := u.service, u.updateService response, err := service.GetClient().Get(updateService.FirmwareInventory) @@ -486,7 +486,7 @@ func getFWfromInventory(softwareInventories []*redfish.SoftwareInventory, softwa return nil, fmt.Errorf("couldn't find FW on Firmware inventory") } -func (u simpleUpdater) pullUpdate(d models.SimpleUpdateRes) (models.SimpleUpdateRes, error) { +func (u *simpleUpdater) pullUpdate(d models.SimpleUpdateRes) (models.SimpleUpdateRes, error) { // Get update service from root updateService := u.updateService service := u.service @@ -541,7 +541,7 @@ func (u simpleUpdater) pullUpdate(d models.SimpleUpdateRes) (models.SimpleUpdate return d, nil } -func (u simpleUpdater) updateJobStatus(d models.SimpleUpdateRes) error { +func (u *simpleUpdater) updateJobStatus(d models.SimpleUpdateRes) error { // Get jobid jobID := d.Id.ValueString() From ff49fad3bdc4f0ea9ea95c4fe5fc592385ce580a Mon Sep 17 00:00:00 2001 From: Rounak Adhikary Date: Fri, 27 Oct 2023 04:58:16 -0400 Subject: [PATCH 09/11] Relaxing checks --- .golangci.yaml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.golangci.yaml b/.golangci.yaml index 2d13cbca..e03ef46c 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -38,6 +38,16 @@ run: # If it's not please let us know. # "/" will be replaced by current OS file path separator to properly work on Windows. skip-files: + - "redfish/provider/data_source_redfish_bios.go" + - "redfish/provider/data_source_redfish_firmware_inventory.go" + - "redfish/provider/data_source_redfish_storage.go" + - "redfish/provider/data_source_redfish_system_boot.go" + - "redfish/provider/data_source_redfish_virtual_media.go" + - "redfish/provider/resource_redfish_storage_volume.go" + - "redfish/provider/resource_redfish_storage_volume.go" + - "redfish/provider/resource_redfish_user_account.go" + - "redfish/provider/resource_redfish_virtual_media.go" + - "redfish/provider/resource_redfish_bios.go" # - ".*\\.my\\.go$" # If set we pass it to "go list -mod={option}". From "go help modules": # If invoked with -mod=readonly, the go command is disallowed from the implicit @@ -164,7 +174,7 @@ linters-settings: - name: cyclomatic severity: warning disabled: false - arguments: [10] # TBD + arguments: [20] # TBD 10 # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#datarace - name: datarace severity: warning From 9133a101a5dcf315bb1e234bb1dcd876f5cd4cf3 Mon Sep 17 00:00:00 2001 From: Rounak Adhikary Date: Fri, 27 Oct 2023 05:02:25 -0400 Subject: [PATCH 10/11] Gnenerate --- docs/resources/simple_update.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/resources/simple_update.md b/docs/resources/simple_update.md index cac18190..c7fbaa05 100644 --- a/docs/resources/simple_update.md +++ b/docs/resources/simple_update.md @@ -170,14 +170,14 @@ After the successful execution of the above resource block, firmware would have ### Required - `redfish_server` (Attributes) Redfish Server (see [below for nested schema](#nestedatt--redfish_server)) -- `reset_type` (String) Reset type allows to choose the type of restart to apply when firmware upgrade is scheduled.Possible values are: "ForceRestart", "GracefulRestart" or "PowerCycle" +- `reset_type` (String) Reset type allows to choose the type of restart to apply when firmware upgrade is scheduled. Possible values are: "ForceRestart", "GracefulRestart" or "PowerCycle" - `target_firmware_image` (String) Target firmware image used for firmware update on the redfish instance. Make sure you place your firmware packages in the same folder as the module and set it as follows: "${path.module}/BIOS_FXC54_WN64_1.15.0.EXE" - `transfer_protocol` (String) The network protocol that the Update Service uses to retrieve the software image file located at the URI provided in ImageURI, if the URI does not contain a scheme. Accepted values: CIFS, FTP, SFTP, HTTP, HTTPS, NSF, SCP, TFTP, OEM, NFS. Currently only HTTP, HTTPS and NFS are supported with local file path or HTTP(s)/NFS link. ### Optional -- `reset_timeout` (Number) reset_timeout is the time in seconds that the provider waits for the server to be reset before timing out. -- `simple_update_job_timeout` (Number) simple_update_job_timeout is the time in seconds that the provider waits for the simple update job to be completed before timing out. +- `reset_timeout` (Number) Time in seconds that the provider waits for the server to be reset before timing out. +- `simple_update_job_timeout` (Number) Time in seconds that the provider waits for the simple update job to be completed before timing out. ### Read-Only From e07dddc88bb5b719090d401ed982a1ee99f0a9ae Mon Sep 17 00:00:00 2001 From: Rounak Adhikary Date: Mon, 30 Oct 2023 05:36:02 -0400 Subject: [PATCH 11/11] Removing skip entries for user and virtual media resources --- .golangci.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index e03ef46c..d969e655 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -45,8 +45,6 @@ run: - "redfish/provider/data_source_redfish_virtual_media.go" - "redfish/provider/resource_redfish_storage_volume.go" - "redfish/provider/resource_redfish_storage_volume.go" - - "redfish/provider/resource_redfish_user_account.go" - - "redfish/provider/resource_redfish_virtual_media.go" - "redfish/provider/resource_redfish_bios.go" # - ".*\\.my\\.go$" # If set we pass it to "go list -mod={option}". From "go help modules":