mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-08-29 03:51:54 +08:00
fix(region): esxi upload iso image (#20163)
This commit is contained in:
@@ -24,7 +24,7 @@ require (
|
||||
github.com/golang-plus/uuid v1.0.0
|
||||
github.com/golang/mock v1.4.4
|
||||
github.com/google/gopacket v1.1.17
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/googollee/go-socket.io v0.0.0-20181214084611-0ad7206c347a
|
||||
github.com/gorilla/mux v1.7.0
|
||||
github.com/gorilla/websocket v1.4.1
|
||||
@@ -58,7 +58,7 @@ require (
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9
|
||||
github.com/smartystreets/goconvey v1.7.2
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e
|
||||
github.com/tjfoc/gmsm v1.4.1
|
||||
github.com/tredoe/osutil/v2 v2.0.0-rc.16
|
||||
@@ -89,7 +89,7 @@ require (
|
||||
k8s.io/cluster-bootstrap v0.19.3
|
||||
k8s.io/cri-api v0.22.17
|
||||
moul.io/http2curl/v2 v2.3.0
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240424102100-82028fa4df21
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240428085651-4a6c4a74cf2e
|
||||
yunion.io/x/executor v0.0.0-20230705125604-c5ac3141db32
|
||||
yunion.io/x/jsonutils v1.0.1-0.20240203102553-4096f103b401
|
||||
yunion.io/x/log v1.0.1-0.20240305175729-7cf2d6cd5a91
|
||||
@@ -233,7 +233,7 @@ require (
|
||||
github.com/smartystreets/assertions v1.2.0 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.413 // indirect
|
||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.24 // indirect
|
||||
github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e // indirect
|
||||
@@ -241,7 +241,7 @@ require (
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.4.0 // indirect
|
||||
github.com/ugorji/go/codec v1.1.7 // indirect
|
||||
github.com/vmware/govmomi v0.20.1 // indirect
|
||||
github.com/vmware/govmomi v0.37.1 // indirect
|
||||
github.com/volcengine/ve-tos-golang-sdk/v2 v2.6.2 // indirect
|
||||
github.com/volcengine/volc-sdk-golang v1.0.23 // indirect
|
||||
github.com/willf/bitset v1.1.9 // indirect
|
||||
@@ -255,7 +255,7 @@ require (
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.17.0 // indirect
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/mod v0.9.0 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c // indirect
|
||||
golang.org/x/term v0.7.0 // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
|
||||
@@ -361,8 +361,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
@@ -385,8 +385,9 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
@@ -640,8 +641,8 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8=
|
||||
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
@@ -684,8 +685,9 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
@@ -694,8 +696,9 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/syncthing/syncthing v0.14.48-rc.4/go.mod h1:nw3siZwHPA6M8iSfjDCWQ402eqvEIasMQOE8nFOxy7M=
|
||||
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=
|
||||
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e h1:nt2877sKfojlHCTOBXbpWjBkuWKritFaGIfgQwbQUls=
|
||||
@@ -727,8 +730,8 @@ github.com/vishvananda/netns v0.0.5-0.20240412164733-9469873f4601 h1:eELqkILRCR0
|
||||
github.com/vishvananda/netns v0.0.5-0.20240412164733-9469873f4601/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
github.com/vmware/govmomi v0.20.1 h1:7b/SeTUB3tER8ZLGLLLH3xcnB2xeuLULXmfPFqPSRZA=
|
||||
github.com/vmware/govmomi v0.20.1/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
|
||||
github.com/vmware/govmomi v0.37.1 h1:SpI+Ofq+lC1zsLcJ9szLSb7fL4TypReVvUoWIgk2b6U=
|
||||
github.com/vmware/govmomi v0.37.1/go.mod h1:mtGWtM+YhTADHlCgJBiskSRPOZRsN9MSjPzaZLte/oQ=
|
||||
github.com/volcengine/ve-tos-golang-sdk/v2 v2.6.2 h1:FZ7zrUf70YKVuES50mHO3z2VW0K+Arq4uGlDqb+1VA0=
|
||||
github.com/volcengine/ve-tos-golang-sdk/v2 v2.6.2/go.mod h1:IrjK84IJJTuOZOTMv/P18Ydjy/x+ow7fF7q11jAxXLM=
|
||||
github.com/volcengine/volc-sdk-golang v1.0.23 h1:anOslb2Qp6ywnsbyq9jqR0ljuO63kg9PY+4OehIk5R8=
|
||||
@@ -826,8 +829,9 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
|
||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180524181706-dfa909b99c79/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -1209,8 +1213,8 @@ sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240424102100-82028fa4df21 h1:WjRPH/q5psgGKz3IfWgbwMykVHJkbzhAnF7o3mzoj0w=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240424102100-82028fa4df21/go.mod h1:dsUESXIbXJ+/ywbNClhldOrbPOiBi2udrgOnB/ffoWk=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240428085651-4a6c4a74cf2e h1:GpCaR/5OivnZ4wNWCWJximsVDJF9JpFozQ/6OeV7+uc=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240428085651-4a6c4a74cf2e/go.mod h1:PkfAKJcu5mic7jyJpT4GxZ8BKIVMa+6eyOP2G94VKAs=
|
||||
yunion.io/x/executor v0.0.0-20230705125604-c5ac3141db32 h1:v7POYkQwo1XzOxBoIoRVr/k0V9Y5JyjpshlIFa9raug=
|
||||
yunion.io/x/executor v0.0.0-20230705125604-c5ac3141db32/go.mod h1:Uxuou9WQIeJXNpy7t2fPLL0BYLvLiMvGQwY7Qc6aSws=
|
||||
yunion.io/x/jsonutils v0.0.0-20190625054549-a964e1e8a051/go.mod h1:4N0/RVzsYL3kH3WE/H1BjUQdFiWu50JGCFQuuy+Z634=
|
||||
|
||||
@@ -196,9 +196,9 @@ func (c *SAgentImageCacheManager) prefetchImageCacheByUpload(ctx context.Context
|
||||
log.Debugf("exist: %t, remotePath: %s", exists, remotePath)
|
||||
if !exists || data.IsForce {
|
||||
if format == "iso" {
|
||||
err := ds.ImportISO(ctx, localImgPath, remotePath, host)
|
||||
err := ds.ImportISO(ctx, localImgPath, remotePath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SDatastore.ImportISO")
|
||||
return nil, errors.Wrapf(err, "SDatastore.ImportISO %s -> %s", localImage, remotePath)
|
||||
}
|
||||
} else {
|
||||
err := ds.ImportVMDK(ctx, localImgPath, remotePath, host)
|
||||
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.4.3
|
||||
- 1.5.3
|
||||
- tip
|
||||
|
||||
script:
|
||||
- go test -v ./...
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
# Changelog
|
||||
|
||||
## [1.6.0](https://github.com/google/uuid/compare/v1.5.0...v1.6.0) (2024-01-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add Max UUID constant ([#149](https://github.com/google/uuid/issues/149)) ([c58770e](https://github.com/google/uuid/commit/c58770eb495f55fe2ced6284f93c5158a62e53e3))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix typo in version 7 uuid documentation ([#153](https://github.com/google/uuid/issues/153)) ([016b199](https://github.com/google/uuid/commit/016b199544692f745ffc8867b914129ecb47ef06))
|
||||
* Monotonicity in UUIDv7 ([#150](https://github.com/google/uuid/issues/150)) ([a2b2b32](https://github.com/google/uuid/commit/a2b2b32373ff0b1a312b7fdf6d38a977099698a6))
|
||||
|
||||
## [1.5.0](https://github.com/google/uuid/compare/v1.4.0...v1.5.0) (2023-12-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Validate UUID without creating new UUID ([#141](https://github.com/google/uuid/issues/141)) ([9ee7366](https://github.com/google/uuid/commit/9ee7366e66c9ad96bab89139418a713dc584ae29))
|
||||
|
||||
## [1.4.0](https://github.com/google/uuid/compare/v1.3.1...v1.4.0) (2023-10-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* UUIDs slice type with Strings() convenience method ([#133](https://github.com/google/uuid/issues/133)) ([cd5fbbd](https://github.com/google/uuid/commit/cd5fbbdd02f3e3467ac18940e07e062be1f864b4))
|
||||
|
||||
### Fixes
|
||||
|
||||
* Clarify that Parse's job is to parse but not necessarily validate strings. (Documents current behavior)
|
||||
|
||||
## [1.3.1](https://github.com/google/uuid/compare/v1.3.0...v1.3.1) (2023-08-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Use .EqualFold() to parse urn prefixed UUIDs ([#118](https://github.com/google/uuid/issues/118)) ([574e687](https://github.com/google/uuid/commit/574e6874943741fb99d41764c705173ada5293f0))
|
||||
|
||||
## Changelog
|
||||
+16
@@ -2,6 +2,22 @@
|
||||
|
||||
We definitely welcome patches and contribution to this project!
|
||||
|
||||
### Tips
|
||||
|
||||
Commits must be formatted according to the [Conventional Commits Specification](https://www.conventionalcommits.org).
|
||||
|
||||
Always try to include a test case! If it is not possible or not necessary,
|
||||
please explain why in the pull request description.
|
||||
|
||||
### Releasing
|
||||
|
||||
Commits that would precipitate a SemVer change, as described in the Conventional
|
||||
Commits Specification, will trigger [`release-please`](https://github.com/google-github-actions/release-please-action)
|
||||
to create a release candidate pull request. Once submitted, `release-please`
|
||||
will create a release.
|
||||
|
||||
For tips on how to work with `release-please`, see its documentation.
|
||||
|
||||
### Legal requirements
|
||||
|
||||
In order to protect both you and ourselves, you will need to sign the
|
||||
|
||||
+6
-4
@@ -1,6 +1,6 @@
|
||||
# uuid 
|
||||
# uuid
|
||||
The uuid package generates and inspects UUIDs based on
|
||||
[RFC 4122](http://tools.ietf.org/html/rfc4122)
|
||||
[RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122)
|
||||
and DCE 1.1: Authentication and Security Services.
|
||||
|
||||
This package is based on the github.com/pborman/uuid package (previously named
|
||||
@@ -9,10 +9,12 @@ a UUID is a 16 byte array rather than a byte slice. One loss due to this
|
||||
change is the ability to represent an invalid UUID (vs a NIL UUID).
|
||||
|
||||
###### Install
|
||||
`go get github.com/google/uuid`
|
||||
```sh
|
||||
go get github.com/google/uuid
|
||||
```
|
||||
|
||||
###### Documentation
|
||||
[](http://godoc.org/github.com/google/uuid)
|
||||
[](https://pkg.go.dev/github.com/google/uuid)
|
||||
|
||||
Full `go doc` style documentation for the package can be viewed online without
|
||||
installing this package by using the GoDoc site here:
|
||||
|
||||
+6
@@ -17,6 +17,12 @@ var (
|
||||
NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
|
||||
NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
|
||||
Nil UUID // empty UUID, all zeros
|
||||
|
||||
// The Max UUID is special form of UUID that is specified to have all 128 bits set to 1.
|
||||
Max = UUID{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
}
|
||||
)
|
||||
|
||||
// NewHash returns a new UUID derived from the hash of space concatenated with
|
||||
|
||||
+1
-1
@@ -7,6 +7,6 @@
|
||||
package uuid
|
||||
|
||||
// getHardwareInterface returns nil values for the JS version of the code.
|
||||
// This remvoves the "net" dependency, because it is not used in the browser.
|
||||
// This removes the "net" dependency, because it is not used in the browser.
|
||||
// Using the "net" library inflates the size of the transpiled JS code by 673k bytes.
|
||||
func getHardwareInterface(name string) (string, []byte) { return "", nil }
|
||||
|
||||
+16
-5
@@ -108,12 +108,23 @@ func setClockSequence(seq int) {
|
||||
}
|
||||
|
||||
// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
|
||||
// uuid. The time is only defined for version 1 and 2 UUIDs.
|
||||
// uuid. The time is only defined for version 1, 2, 6 and 7 UUIDs.
|
||||
func (uuid UUID) Time() Time {
|
||||
time := int64(binary.BigEndian.Uint32(uuid[0:4]))
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
|
||||
return Time(time)
|
||||
var t Time
|
||||
switch uuid.Version() {
|
||||
case 6:
|
||||
time := binary.BigEndian.Uint64(uuid[:8]) // Ignore uuid[6] version b0110
|
||||
t = Time(time)
|
||||
case 7:
|
||||
time := binary.BigEndian.Uint64(uuid[:8])
|
||||
t = Time((time>>16)*10000 + g1582ns100)
|
||||
default: // forward compatible
|
||||
time := int64(binary.BigEndian.Uint32(uuid[0:4]))
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
|
||||
t = Time(time)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// ClockSequence returns the clock sequence encoded in uuid.
|
||||
|
||||
+80
-9
@@ -56,11 +56,15 @@ func IsInvalidLengthError(err error) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// Parse decodes s into a UUID or returns an error. Both the standard UUID
|
||||
// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the
|
||||
// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex
|
||||
// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
|
||||
// Parse decodes s into a UUID or returns an error if it cannot be parsed. Both
|
||||
// the standard UUID forms defined in RFC 4122
|
||||
// (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) are decoded. In addition,
|
||||
// Parse accepts non-standard strings such as the raw hex encoding
|
||||
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx and 38 byte "Microsoft style" encodings,
|
||||
// e.g. {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}. Only the middle 36 bytes are
|
||||
// examined in the latter case. Parse should not be used to validate strings as
|
||||
// it parses non-standard encodings as indicated above.
|
||||
func Parse(s string) (UUID, error) {
|
||||
var uuid UUID
|
||||
switch len(s) {
|
||||
@@ -69,7 +73,7 @@ func Parse(s string) (UUID, error) {
|
||||
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
case 36 + 9:
|
||||
if strings.ToLower(s[:9]) != "urn:uuid:" {
|
||||
if !strings.EqualFold(s[:9], "urn:uuid:") {
|
||||
return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9])
|
||||
}
|
||||
s = s[9:]
|
||||
@@ -101,7 +105,8 @@ func Parse(s string) (UUID, error) {
|
||||
9, 11,
|
||||
14, 16,
|
||||
19, 21,
|
||||
24, 26, 28, 30, 32, 34} {
|
||||
24, 26, 28, 30, 32, 34,
|
||||
} {
|
||||
v, ok := xtob(s[x], s[x+1])
|
||||
if !ok {
|
||||
return uuid, errors.New("invalid UUID format")
|
||||
@@ -117,7 +122,7 @@ func ParseBytes(b []byte) (UUID, error) {
|
||||
switch len(b) {
|
||||
case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) {
|
||||
if !bytes.EqualFold(b[:9], []byte("urn:uuid:")) {
|
||||
return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9])
|
||||
}
|
||||
b = b[9:]
|
||||
@@ -145,7 +150,8 @@ func ParseBytes(b []byte) (UUID, error) {
|
||||
9, 11,
|
||||
14, 16,
|
||||
19, 21,
|
||||
24, 26, 28, 30, 32, 34} {
|
||||
24, 26, 28, 30, 32, 34,
|
||||
} {
|
||||
v, ok := xtob(b[x], b[x+1])
|
||||
if !ok {
|
||||
return uuid, errors.New("invalid UUID format")
|
||||
@@ -180,6 +186,59 @@ func Must(uuid UUID, err error) UUID {
|
||||
return uuid
|
||||
}
|
||||
|
||||
// Validate returns an error if s is not a properly formatted UUID in one of the following formats:
|
||||
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
|
||||
// It returns an error if the format is invalid, otherwise nil.
|
||||
func Validate(s string) error {
|
||||
switch len(s) {
|
||||
// Standard UUID format
|
||||
case 36:
|
||||
|
||||
// UUID with "urn:uuid:" prefix
|
||||
case 36 + 9:
|
||||
if !strings.EqualFold(s[:9], "urn:uuid:") {
|
||||
return fmt.Errorf("invalid urn prefix: %q", s[:9])
|
||||
}
|
||||
s = s[9:]
|
||||
|
||||
// UUID enclosed in braces
|
||||
case 36 + 2:
|
||||
if s[0] != '{' || s[len(s)-1] != '}' {
|
||||
return fmt.Errorf("invalid bracketed UUID format")
|
||||
}
|
||||
s = s[1 : len(s)-1]
|
||||
|
||||
// UUID without hyphens
|
||||
case 32:
|
||||
for i := 0; i < len(s); i += 2 {
|
||||
_, ok := xtob(s[i], s[i+1])
|
||||
if !ok {
|
||||
return errors.New("invalid UUID format")
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return invalidLengthError{len(s)}
|
||||
}
|
||||
|
||||
// Check for standard UUID format
|
||||
if len(s) == 36 {
|
||||
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
|
||||
return errors.New("invalid UUID format")
|
||||
}
|
||||
for _, x := range []int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} {
|
||||
if _, ok := xtob(s[x], s[x+1]); !ok {
|
||||
return errors.New("invalid UUID format")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
// , or "" if uuid is invalid.
|
||||
func (uuid UUID) String() string {
|
||||
@@ -292,3 +351,15 @@ func DisableRandPool() {
|
||||
poolMu.Lock()
|
||||
poolPos = randPoolSize
|
||||
}
|
||||
|
||||
// UUIDs is a slice of UUID types.
|
||||
type UUIDs []UUID
|
||||
|
||||
// Strings returns a string slice containing the string form of each UUID in uuids.
|
||||
func (uuids UUIDs) Strings() []string {
|
||||
var uuidStrs = make([]string, len(uuids))
|
||||
for i, uuid := range uuids {
|
||||
uuidStrs[i] = uuid.String()
|
||||
}
|
||||
return uuidStrs
|
||||
}
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
// Copyright 2023 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
// UUID version 6 is a field-compatible version of UUIDv1, reordered for improved DB locality.
|
||||
// It is expected that UUIDv6 will primarily be used in contexts where there are existing v1 UUIDs.
|
||||
// Systems that do not involve legacy UUIDv1 SHOULD consider using UUIDv7 instead.
|
||||
//
|
||||
// see https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-03#uuidv6
|
||||
//
|
||||
// NewV6 returns a Version 6 UUID based on the current NodeID and clock
|
||||
// sequence, and the current time. If the NodeID has not been set by SetNodeID
|
||||
// or SetNodeInterface then it will be set automatically. If the NodeID cannot
|
||||
// be set NewV6 set NodeID is random bits automatically . If clock sequence has not been set by
|
||||
// SetClockSequence then it will be set automatically. If GetTime fails to
|
||||
// return the current NewV6 returns Nil and an error.
|
||||
func NewV6() (UUID, error) {
|
||||
var uuid UUID
|
||||
now, seq, err := GetTime()
|
||||
if err != nil {
|
||||
return uuid, err
|
||||
}
|
||||
|
||||
/*
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| time_high |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| time_mid | time_low_and_version |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|clk_seq_hi_res | clk_seq_low | node (0-1) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| node (2-5) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
binary.BigEndian.PutUint64(uuid[0:], uint64(now))
|
||||
binary.BigEndian.PutUint16(uuid[8:], seq)
|
||||
|
||||
uuid[6] = 0x60 | (uuid[6] & 0x0F)
|
||||
uuid[8] = 0x80 | (uuid[8] & 0x3F)
|
||||
|
||||
nodeMu.Lock()
|
||||
if nodeID == zeroID {
|
||||
setNodeInterface("")
|
||||
}
|
||||
copy(uuid[10:], nodeID[:])
|
||||
nodeMu.Unlock()
|
||||
|
||||
return uuid, nil
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
// Copyright 2023 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// UUID version 7 features a time-ordered value field derived from the widely
|
||||
// implemented and well known Unix Epoch timestamp source,
|
||||
// the number of milliseconds seconds since midnight 1 Jan 1970 UTC, leap seconds excluded.
|
||||
// As well as improved entropy characteristics over versions 1 or 6.
|
||||
//
|
||||
// see https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-03#name-uuid-version-7
|
||||
//
|
||||
// Implementations SHOULD utilize UUID version 7 over UUID version 1 and 6 if possible.
|
||||
//
|
||||
// NewV7 returns a Version 7 UUID based on the current time(Unix Epoch).
|
||||
// Uses the randomness pool if it was enabled with EnableRandPool.
|
||||
// On error, NewV7 returns Nil and an error
|
||||
func NewV7() (UUID, error) {
|
||||
uuid, err := NewRandom()
|
||||
if err != nil {
|
||||
return uuid, err
|
||||
}
|
||||
makeV7(uuid[:])
|
||||
return uuid, nil
|
||||
}
|
||||
|
||||
// NewV7FromReader returns a Version 7 UUID based on the current time(Unix Epoch).
|
||||
// it use NewRandomFromReader fill random bits.
|
||||
// On error, NewV7FromReader returns Nil and an error.
|
||||
func NewV7FromReader(r io.Reader) (UUID, error) {
|
||||
uuid, err := NewRandomFromReader(r)
|
||||
if err != nil {
|
||||
return uuid, err
|
||||
}
|
||||
|
||||
makeV7(uuid[:])
|
||||
return uuid, nil
|
||||
}
|
||||
|
||||
// makeV7 fill 48 bits time (uuid[0] - uuid[5]), set version b0111 (uuid[6])
|
||||
// uuid[8] already has the right version number (Variant is 10)
|
||||
// see function NewV7 and NewV7FromReader
|
||||
func makeV7(uuid []byte) {
|
||||
/*
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| unix_ts_ms |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| unix_ts_ms | ver | rand_a (12 bit seq) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|var| rand_b |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| rand_b |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
_ = uuid[15] // bounds check
|
||||
|
||||
t, s := getV7Time()
|
||||
|
||||
uuid[0] = byte(t >> 40)
|
||||
uuid[1] = byte(t >> 32)
|
||||
uuid[2] = byte(t >> 24)
|
||||
uuid[3] = byte(t >> 16)
|
||||
uuid[4] = byte(t >> 8)
|
||||
uuid[5] = byte(t)
|
||||
|
||||
uuid[6] = 0x70 | (0x0F & byte(s>>8))
|
||||
uuid[7] = byte(s)
|
||||
}
|
||||
|
||||
// lastV7time is the last time we returned stored as:
|
||||
//
|
||||
// 52 bits of time in milliseconds since epoch
|
||||
// 12 bits of (fractional nanoseconds) >> 8
|
||||
var lastV7time int64
|
||||
|
||||
const nanoPerMilli = 1000000
|
||||
|
||||
// getV7Time returns the time in milliseconds and nanoseconds / 256.
|
||||
// The returned (milli << 12 + seq) is guarenteed to be greater than
|
||||
// (milli << 12 + seq) returned by any previous call to getV7Time.
|
||||
func getV7Time() (milli, seq int64) {
|
||||
timeMu.Lock()
|
||||
defer timeMu.Unlock()
|
||||
|
||||
nano := timeNow().UnixNano()
|
||||
milli = nano / nanoPerMilli
|
||||
// Sequence number is between 0 and 3906 (nanoPerMilli>>8)
|
||||
seq = (nano - milli*nanoPerMilli) >> 8
|
||||
now := milli<<12 + seq
|
||||
if now <= lastV7time {
|
||||
now = lastV7time + 1
|
||||
milli = now >> 12
|
||||
seq = now & 0xfff
|
||||
}
|
||||
lastV7time = now
|
||||
return milli, seq
|
||||
}
|
||||
+4
-4
@@ -4,20 +4,20 @@
|
||||
[](https://codeclimate.com/github/stretchr/objx/maintainability)
|
||||
[](https://codeclimate.com/github/stretchr/objx/test_coverage)
|
||||
[](https://sourcegraph.com/github.com/stretchr/objx)
|
||||
[](https://godoc.org/github.com/stretchr/objx)
|
||||
[](https://pkg.go.dev/github.com/stretchr/objx)
|
||||
|
||||
Objx - Go package for dealing with maps, slices, JSON and other data.
|
||||
|
||||
Get started:
|
||||
|
||||
- Install Objx with [one line of code](#installation), or [update it with another](#staying-up-to-date)
|
||||
- Check out the API Documentation http://godoc.org/github.com/stretchr/objx
|
||||
- Check out the API Documentation http://pkg.go.dev/github.com/stretchr/objx
|
||||
|
||||
## Overview
|
||||
Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes a powerful `Get` method (among others) that allows you to easily and quickly get access to data within the map, without having to worry too much about type assertions, missing data, default values etc.
|
||||
|
||||
### Pattern
|
||||
Objx uses a preditable pattern to make access data from within `map[string]interface{}` easy. Call one of the `objx.` functions to create your `objx.Map` to get going:
|
||||
Objx uses a predictable pattern to make access data from within `map[string]interface{}` easy. Call one of the `objx.` functions to create your `objx.Map` to get going:
|
||||
|
||||
m, err := objx.FromJSON(json)
|
||||
|
||||
@@ -74,7 +74,7 @@ To update Objx to the latest version, run:
|
||||
go get -u github.com/stretchr/objx
|
||||
|
||||
### Supported go versions
|
||||
We support the lastest three major Go versions, which are 1.10, 1.11 and 1.12 at the moment.
|
||||
We currently support the three recent major Go versions.
|
||||
|
||||
## Contributing
|
||||
Please feel free to submit issues, fork the repository and send pull requests!
|
||||
|
||||
+1
-4
@@ -1,7 +1,4 @@
|
||||
version: '2'
|
||||
|
||||
env:
|
||||
GOFLAGS: -mod=vendor
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
default:
|
||||
|
||||
+12
-12
@@ -14,17 +14,17 @@ const (
|
||||
// For example, `location.address.city`
|
||||
PathSeparator string = "."
|
||||
|
||||
// arrayAccesRegexString is the regex used to extract the array number
|
||||
// arrayAccessRegexString is the regex used to extract the array number
|
||||
// from the access path
|
||||
arrayAccesRegexString = `^(.+)\[([0-9]+)\]$`
|
||||
arrayAccessRegexString = `^(.+)\[([0-9]+)\]$`
|
||||
|
||||
// mapAccessRegexString is the regex used to extract the map key
|
||||
// from the access path
|
||||
mapAccessRegexString = `^([^\[]*)\[([^\]]+)\](.*)$`
|
||||
)
|
||||
|
||||
// arrayAccesRegex is the compiled arrayAccesRegexString
|
||||
var arrayAccesRegex = regexp.MustCompile(arrayAccesRegexString)
|
||||
// arrayAccessRegex is the compiled arrayAccessRegexString
|
||||
var arrayAccessRegex = regexp.MustCompile(arrayAccessRegexString)
|
||||
|
||||
// mapAccessRegex is the compiled mapAccessRegexString
|
||||
var mapAccessRegex = regexp.MustCompile(mapAccessRegexString)
|
||||
@@ -37,11 +37,11 @@ var mapAccessRegex = regexp.MustCompile(mapAccessRegexString)
|
||||
//
|
||||
// Get can only operate directly on map[string]interface{} and []interface.
|
||||
//
|
||||
// Example
|
||||
// # Example
|
||||
//
|
||||
// To access the title of the third chapter of the second book, do:
|
||||
//
|
||||
// o.Get("books[1].chapters[2].title")
|
||||
// o.Get("books[1].chapters[2].title")
|
||||
func (m Map) Get(selector string) *Value {
|
||||
rawObj := access(m, selector, nil, false)
|
||||
return &Value{data: rawObj}
|
||||
@@ -52,26 +52,26 @@ func (m Map) Get(selector string) *Value {
|
||||
//
|
||||
// Set can only operate directly on map[string]interface{} and []interface
|
||||
//
|
||||
// Example
|
||||
// # Example
|
||||
//
|
||||
// To set the title of the third chapter of the second book, do:
|
||||
//
|
||||
// o.Set("books[1].chapters[2].title","Time to Go")
|
||||
// o.Set("books[1].chapters[2].title","Time to Go")
|
||||
func (m Map) Set(selector string, value interface{}) Map {
|
||||
access(m, selector, value, true)
|
||||
return m
|
||||
}
|
||||
|
||||
// getIndex returns the index, which is hold in s by two braches.
|
||||
// It also returns s withour the index part, e.g. name[1] will return (1, name).
|
||||
// getIndex returns the index, which is hold in s by two branches.
|
||||
// It also returns s without the index part, e.g. name[1] will return (1, name).
|
||||
// If no index is found, -1 is returned
|
||||
func getIndex(s string) (int, string) {
|
||||
arrayMatches := arrayAccesRegex.FindStringSubmatch(s)
|
||||
arrayMatches := arrayAccessRegex.FindStringSubmatch(s)
|
||||
if len(arrayMatches) > 0 {
|
||||
// Get the key into the map
|
||||
selector := arrayMatches[1]
|
||||
// Get the index into the array at the key
|
||||
// We know this cannt fail because arrayMatches[2] is an int for sure
|
||||
// We know this can't fail because arrayMatches[2] is an int for sure
|
||||
index, _ := strconv.Atoi(arrayMatches[2])
|
||||
return index, selector
|
||||
}
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@ import (
|
||||
const SignatureSeparator = "_"
|
||||
|
||||
// URLValuesSliceKeySuffix is the character that is used to
|
||||
// specify a suffic for slices parsed by URLValues.
|
||||
// specify a suffix for slices parsed by URLValues.
|
||||
// If the suffix is set to "[i]", then the index of the slice
|
||||
// is used in place of i
|
||||
// Ex: Suffix "[]" would have the form a[]=b&a[]=c
|
||||
@@ -30,7 +30,7 @@ const (
|
||||
)
|
||||
|
||||
// SetURLValuesSliceKeySuffix sets the character that is used to
|
||||
// specify a suffic for slices parsed by URLValues.
|
||||
// specify a suffix for slices parsed by URLValues.
|
||||
// If the suffix is set to "[i]", then the index of the slice
|
||||
// is used in place of i
|
||||
// Ex: Suffix "[]" would have the form a[]=b&a[]=c
|
||||
|
||||
+22
-22
@@ -1,19 +1,19 @@
|
||||
/*
|
||||
Objx - Go package for dealing with maps, slices, JSON and other data.
|
||||
Package objx provides utilities for dealing with maps, slices, JSON and other data.
|
||||
|
||||
Overview
|
||||
# Overview
|
||||
|
||||
Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes
|
||||
a powerful `Get` method (among others) that allows you to easily and quickly get
|
||||
access to data within the map, without having to worry too much about type assertions,
|
||||
missing data, default values etc.
|
||||
|
||||
Pattern
|
||||
# Pattern
|
||||
|
||||
Objx uses a preditable pattern to make access data from within `map[string]interface{}` easy.
|
||||
Objx uses a predictable pattern to make access data from within `map[string]interface{}` easy.
|
||||
Call one of the `objx.` functions to create your `objx.Map` to get going:
|
||||
|
||||
m, err := objx.FromJSON(json)
|
||||
m, err := objx.FromJSON(json)
|
||||
|
||||
NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong,
|
||||
the rest will be optimistic and try to figure things out without panicking.
|
||||
@@ -21,46 +21,46 @@ the rest will be optimistic and try to figure things out without panicking.
|
||||
Use `Get` to access the value you're interested in. You can use dot and array
|
||||
notation too:
|
||||
|
||||
m.Get("places[0].latlng")
|
||||
m.Get("places[0].latlng")
|
||||
|
||||
Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type.
|
||||
|
||||
if m.Get("code").IsStr() { // Your code... }
|
||||
if m.Get("code").IsStr() { // Your code... }
|
||||
|
||||
Or you can just assume the type, and use one of the strong type methods to extract the real value:
|
||||
|
||||
m.Get("code").Int()
|
||||
m.Get("code").Int()
|
||||
|
||||
If there's no value there (or if it's the wrong type) then a default value will be returned,
|
||||
or you can be explicit about the default value.
|
||||
|
||||
Get("code").Int(-1)
|
||||
Get("code").Int(-1)
|
||||
|
||||
If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating,
|
||||
manipulating and selecting that data. You can find out more by exploring the index below.
|
||||
|
||||
Reading data
|
||||
# Reading data
|
||||
|
||||
A simple example of how to use Objx:
|
||||
|
||||
// Use MustFromJSON to make an objx.Map from some JSON
|
||||
m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`)
|
||||
// Use MustFromJSON to make an objx.Map from some JSON
|
||||
m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`)
|
||||
|
||||
// Get the details
|
||||
name := m.Get("name").Str()
|
||||
age := m.Get("age").Int()
|
||||
// Get the details
|
||||
name := m.Get("name").Str()
|
||||
age := m.Get("age").Int()
|
||||
|
||||
// Get their nickname (or use their name if they don't have one)
|
||||
nickname := m.Get("nickname").Str(name)
|
||||
// Get their nickname (or use their name if they don't have one)
|
||||
nickname := m.Get("nickname").Str(name)
|
||||
|
||||
Ranging
|
||||
# Ranging
|
||||
|
||||
Since `objx.Map` is a `map[string]interface{}` you can treat it as such.
|
||||
For example, to `range` the data, do what you would expect:
|
||||
|
||||
m := objx.MustFromJSON(json)
|
||||
for key, value := range m {
|
||||
// Your code...
|
||||
}
|
||||
m := objx.MustFromJSON(json)
|
||||
for key, value := range m {
|
||||
// Your code...
|
||||
}
|
||||
*/
|
||||
package objx
|
||||
|
||||
+4
-5
@@ -47,17 +47,16 @@ func New(data interface{}) Map {
|
||||
//
|
||||
// The arguments follow a key, value pattern.
|
||||
//
|
||||
//
|
||||
// Returns nil if any key argument is non-string or if there are an odd number of arguments.
|
||||
//
|
||||
// Example
|
||||
// # Example
|
||||
//
|
||||
// To easily create Maps:
|
||||
//
|
||||
// m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true))
|
||||
// m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true))
|
||||
//
|
||||
// // creates an Map equivalent to
|
||||
// m := objx.Map{"name": "Mat", "age": 29, "subobj": objx.Map{"active": true}}
|
||||
// // creates an Map equivalent to
|
||||
// m := objx.Map{"name": "Mat", "age": 29, "subobj": objx.Map{"active": true}}
|
||||
func MSI(keyAndValuePairs ...interface{}) Map {
|
||||
newMap := Map{}
|
||||
keyAndValuePairsLen := len(keyAndValuePairs)
|
||||
|
||||
+43
-21
@@ -28,6 +28,8 @@ var (
|
||||
uint32Type = reflect.TypeOf(uint32(1))
|
||||
uint64Type = reflect.TypeOf(uint64(1))
|
||||
|
||||
uintptrType = reflect.TypeOf(uintptr(1))
|
||||
|
||||
float32Type = reflect.TypeOf(float32(1))
|
||||
float64Type = reflect.TypeOf(float64(1))
|
||||
|
||||
@@ -308,11 +310,11 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
case reflect.Struct:
|
||||
{
|
||||
// All structs enter here. We're not interested in most types.
|
||||
if !canConvert(obj1Value, timeType) {
|
||||
if !obj1Value.CanConvert(timeType) {
|
||||
break
|
||||
}
|
||||
|
||||
// time.Time can compared!
|
||||
// time.Time can be compared!
|
||||
timeObj1, ok := obj1.(time.Time)
|
||||
if !ok {
|
||||
timeObj1 = obj1Value.Convert(timeType).Interface().(time.Time)
|
||||
@@ -328,7 +330,7 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
case reflect.Slice:
|
||||
{
|
||||
// We only care about the []byte type.
|
||||
if !canConvert(obj1Value, bytesType) {
|
||||
if !obj1Value.CanConvert(bytesType) {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -345,6 +347,26 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
|
||||
return CompareType(bytes.Compare(bytesObj1, bytesObj2)), true
|
||||
}
|
||||
case reflect.Uintptr:
|
||||
{
|
||||
uintptrObj1, ok := obj1.(uintptr)
|
||||
if !ok {
|
||||
uintptrObj1 = obj1Value.Convert(uintptrType).Interface().(uintptr)
|
||||
}
|
||||
uintptrObj2, ok := obj2.(uintptr)
|
||||
if !ok {
|
||||
uintptrObj2 = obj2Value.Convert(uintptrType).Interface().(uintptr)
|
||||
}
|
||||
if uintptrObj1 > uintptrObj2 {
|
||||
return compareGreater, true
|
||||
}
|
||||
if uintptrObj1 == uintptrObj2 {
|
||||
return compareEqual, true
|
||||
}
|
||||
if uintptrObj1 < uintptrObj2 {
|
||||
return compareLess, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return compareEqual, false
|
||||
@@ -352,9 +374,9 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
|
||||
|
||||
// Greater asserts that the first element is greater than the second
|
||||
//
|
||||
// assert.Greater(t, 2, 1)
|
||||
// assert.Greater(t, float64(2), float64(1))
|
||||
// assert.Greater(t, "b", "a")
|
||||
// assert.Greater(t, 2, 1)
|
||||
// assert.Greater(t, float64(2), float64(1))
|
||||
// assert.Greater(t, "b", "a")
|
||||
func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -364,10 +386,10 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface
|
||||
|
||||
// GreaterOrEqual asserts that the first element is greater than or equal to the second
|
||||
//
|
||||
// assert.GreaterOrEqual(t, 2, 1)
|
||||
// assert.GreaterOrEqual(t, 2, 2)
|
||||
// assert.GreaterOrEqual(t, "b", "a")
|
||||
// assert.GreaterOrEqual(t, "b", "b")
|
||||
// assert.GreaterOrEqual(t, 2, 1)
|
||||
// assert.GreaterOrEqual(t, 2, 2)
|
||||
// assert.GreaterOrEqual(t, "b", "a")
|
||||
// assert.GreaterOrEqual(t, "b", "b")
|
||||
func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -377,9 +399,9 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in
|
||||
|
||||
// Less asserts that the first element is less than the second
|
||||
//
|
||||
// assert.Less(t, 1, 2)
|
||||
// assert.Less(t, float64(1), float64(2))
|
||||
// assert.Less(t, "a", "b")
|
||||
// assert.Less(t, 1, 2)
|
||||
// assert.Less(t, float64(1), float64(2))
|
||||
// assert.Less(t, "a", "b")
|
||||
func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -389,10 +411,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{})
|
||||
|
||||
// LessOrEqual asserts that the first element is less than or equal to the second
|
||||
//
|
||||
// assert.LessOrEqual(t, 1, 2)
|
||||
// assert.LessOrEqual(t, 2, 2)
|
||||
// assert.LessOrEqual(t, "a", "b")
|
||||
// assert.LessOrEqual(t, "b", "b")
|
||||
// assert.LessOrEqual(t, 1, 2)
|
||||
// assert.LessOrEqual(t, 2, 2)
|
||||
// assert.LessOrEqual(t, "a", "b")
|
||||
// assert.LessOrEqual(t, "b", "b")
|
||||
func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -402,8 +424,8 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter
|
||||
|
||||
// Positive asserts that the specified element is positive
|
||||
//
|
||||
// assert.Positive(t, 1)
|
||||
// assert.Positive(t, 1.23)
|
||||
// assert.Positive(t, 1)
|
||||
// assert.Positive(t, 1.23)
|
||||
func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -414,8 +436,8 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
|
||||
|
||||
// Negative asserts that the specified element is negative
|
||||
//
|
||||
// assert.Negative(t, -1)
|
||||
// assert.Negative(t, -1.23)
|
||||
// assert.Negative(t, -1)
|
||||
// assert.Negative(t, -1.23)
|
||||
func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
//go:build go1.17
|
||||
// +build go1.17
|
||||
|
||||
// TODO: once support for Go 1.16 is dropped, this file can be
|
||||
// merged/removed with assertion_compare_go1.17_test.go and
|
||||
// assertion_compare_legacy.go
|
||||
|
||||
package assert
|
||||
|
||||
import "reflect"
|
||||
|
||||
// Wrapper around reflect.Value.CanConvert, for compatibility
|
||||
// reasons.
|
||||
func canConvert(value reflect.Value, to reflect.Type) bool {
|
||||
return value.CanConvert(to)
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
//go:build !go1.17
|
||||
// +build !go1.17
|
||||
|
||||
// TODO: once support for Go 1.16 is dropped, this file can be
|
||||
// merged/removed with assertion_compare_go1.17_test.go and
|
||||
// assertion_compare_can_convert.go
|
||||
|
||||
package assert
|
||||
|
||||
import "reflect"
|
||||
|
||||
// Older versions of Go does not have the reflect.Value.CanConvert
|
||||
// method.
|
||||
func canConvert(value reflect.Value, to reflect.Type) bool {
|
||||
return false
|
||||
}
|
||||
+148
-96
@@ -1,7 +1,4 @@
|
||||
/*
|
||||
* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
|
||||
* THIS FILE MUST NOT BE EDITED BY HAND
|
||||
*/
|
||||
// Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT.
|
||||
|
||||
package assert
|
||||
|
||||
@@ -22,9 +19,9 @@ func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bo
|
||||
// Containsf asserts that the specified string, list(array, slice...) or map contains the
|
||||
// specified substring or element.
|
||||
//
|
||||
// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted")
|
||||
// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted")
|
||||
// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted")
|
||||
// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted")
|
||||
// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted")
|
||||
// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted")
|
||||
func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -56,7 +53,7 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string
|
||||
// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// assert.Emptyf(t, obj, "error message %s", "formatted")
|
||||
// assert.Emptyf(t, obj, "error message %s", "formatted")
|
||||
func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -66,7 +63,7 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) boo
|
||||
|
||||
// Equalf asserts that two objects are equal.
|
||||
//
|
||||
// assert.Equalf(t, 123, 123, "error message %s", "formatted")
|
||||
// assert.Equalf(t, 123, 123, "error message %s", "formatted")
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses). Function equality
|
||||
@@ -81,8 +78,8 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar
|
||||
// EqualErrorf asserts that a function returned an error (i.e. not `nil`)
|
||||
// and that it is equal to the provided error.
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted")
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted")
|
||||
func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -90,10 +87,27 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args
|
||||
return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// EqualValuesf asserts that two objects are equal or convertable to the same types
|
||||
// EqualExportedValuesf asserts that the types of two objects are equal and their public
|
||||
// fields are also equal. This is useful for comparing structs that have private fields
|
||||
// that could potentially differ.
|
||||
//
|
||||
// type S struct {
|
||||
// Exported int
|
||||
// notExported int
|
||||
// }
|
||||
// assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true
|
||||
// assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false
|
||||
func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return EqualExportedValues(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// EqualValuesf asserts that two objects are equal or convertible to the same types
|
||||
// and equal.
|
||||
//
|
||||
// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted")
|
||||
// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted")
|
||||
func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -103,10 +117,10 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri
|
||||
|
||||
// Errorf asserts that a function returned an error (i.e. not `nil`).
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.Errorf(t, err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedErrorf, err)
|
||||
// }
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.Errorf(t, err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedErrorf, err)
|
||||
// }
|
||||
func Errorf(t TestingT, err error, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -126,8 +140,8 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int
|
||||
// ErrorContainsf asserts that a function returned an error (i.e. not `nil`)
|
||||
// and that the error contains the specified substring.
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted")
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted")
|
||||
func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -147,7 +161,7 @@ func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface
|
||||
// Eventuallyf asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick.
|
||||
//
|
||||
// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
|
||||
// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
|
||||
func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -155,9 +169,34 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick
|
||||
return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// EventuallyWithTf asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick. In contrast to Eventually,
|
||||
// it supplies a CollectT to the condition function, so that the condition
|
||||
// function can use the CollectT to call other assertions.
|
||||
// The condition is considered "met" if no errors are raised in a tick.
|
||||
// The supplied CollectT collects all errors from one tick (if there are any).
|
||||
// If the condition is not met before waitFor, the collected errors of
|
||||
// the last tick are copied to t.
|
||||
//
|
||||
// externalValue := false
|
||||
// go func() {
|
||||
// time.Sleep(8*time.Second)
|
||||
// externalValue = true
|
||||
// }()
|
||||
// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") {
|
||||
// // add assertions as needed; any assertion failure will fail the current tick
|
||||
// assert.True(c, externalValue, "expected 'externalValue' to be true")
|
||||
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
|
||||
func EventuallyWithTf(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return EventuallyWithT(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Exactlyf asserts that two objects are equal in value and type.
|
||||
//
|
||||
// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted")
|
||||
// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted")
|
||||
func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -183,7 +222,7 @@ func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}
|
||||
|
||||
// Falsef asserts that the specified value is false.
|
||||
//
|
||||
// assert.Falsef(t, myBool, "error message %s", "formatted")
|
||||
// assert.Falsef(t, myBool, "error message %s", "formatted")
|
||||
func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -202,9 +241,9 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool
|
||||
|
||||
// Greaterf asserts that the first element is greater than the second
|
||||
//
|
||||
// assert.Greaterf(t, 2, 1, "error message %s", "formatted")
|
||||
// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted")
|
||||
// assert.Greaterf(t, "b", "a", "error message %s", "formatted")
|
||||
// assert.Greaterf(t, 2, 1, "error message %s", "formatted")
|
||||
// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted")
|
||||
// assert.Greaterf(t, "b", "a", "error message %s", "formatted")
|
||||
func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -214,10 +253,10 @@ func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...in
|
||||
|
||||
// GreaterOrEqualf asserts that the first element is greater than or equal to the second
|
||||
//
|
||||
// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted")
|
||||
// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted")
|
||||
func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -228,7 +267,7 @@ func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, arg
|
||||
// HTTPBodyContainsf asserts that a specified handler returns a
|
||||
// body that contains a string.
|
||||
//
|
||||
// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
|
||||
@@ -241,7 +280,7 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url
|
||||
// HTTPBodyNotContainsf asserts that a specified handler returns a
|
||||
// body that does not contain a string.
|
||||
//
|
||||
// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool {
|
||||
@@ -253,7 +292,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u
|
||||
|
||||
// HTTPErrorf asserts that a specified handler returns an error status code.
|
||||
//
|
||||
// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
|
||||
@@ -265,7 +304,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string,
|
||||
|
||||
// HTTPRedirectf asserts that a specified handler returns a redirect status code.
|
||||
//
|
||||
// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
|
||||
@@ -277,7 +316,7 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri
|
||||
|
||||
// HTTPStatusCodef asserts that a specified handler returns a specified status code.
|
||||
//
|
||||
// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted")
|
||||
// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool {
|
||||
@@ -289,7 +328,7 @@ func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url st
|
||||
|
||||
// HTTPSuccessf asserts that a specified handler returns a success status code.
|
||||
//
|
||||
// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
|
||||
// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
|
||||
@@ -301,7 +340,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin
|
||||
|
||||
// Implementsf asserts that an object is implemented by the specified interface.
|
||||
//
|
||||
// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted")
|
||||
// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted")
|
||||
func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -311,7 +350,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms
|
||||
|
||||
// InDeltaf asserts that the two numerals are within delta of each other.
|
||||
//
|
||||
// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted")
|
||||
// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted")
|
||||
func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -353,9 +392,9 @@ func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsil
|
||||
|
||||
// IsDecreasingf asserts that the collection is decreasing
|
||||
//
|
||||
// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted")
|
||||
// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted")
|
||||
// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
|
||||
// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted")
|
||||
// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted")
|
||||
// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
|
||||
func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -365,9 +404,9 @@ func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface
|
||||
|
||||
// IsIncreasingf asserts that the collection is increasing
|
||||
//
|
||||
// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted")
|
||||
// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted")
|
||||
// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
|
||||
// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted")
|
||||
// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted")
|
||||
// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
|
||||
func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -377,9 +416,9 @@ func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface
|
||||
|
||||
// IsNonDecreasingf asserts that the collection is not decreasing
|
||||
//
|
||||
// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted")
|
||||
// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted")
|
||||
// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
|
||||
// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted")
|
||||
// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted")
|
||||
// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted")
|
||||
func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -389,9 +428,9 @@ func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interf
|
||||
|
||||
// IsNonIncreasingf asserts that the collection is not increasing
|
||||
//
|
||||
// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted")
|
||||
// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted")
|
||||
// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
|
||||
// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted")
|
||||
// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted")
|
||||
// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted")
|
||||
func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -409,7 +448,7 @@ func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg strin
|
||||
|
||||
// JSONEqf asserts that two JSON strings are equivalent.
|
||||
//
|
||||
// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
|
||||
// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
|
||||
func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -420,7 +459,7 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int
|
||||
// Lenf asserts that the specified object has specific length.
|
||||
// Lenf also fails if the object has a type that len() not accept.
|
||||
//
|
||||
// assert.Lenf(t, mySlice, 3, "error message %s", "formatted")
|
||||
// assert.Lenf(t, mySlice, 3, "error message %s", "formatted")
|
||||
func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -430,9 +469,9 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf
|
||||
|
||||
// Lessf asserts that the first element is less than the second
|
||||
//
|
||||
// assert.Lessf(t, 1, 2, "error message %s", "formatted")
|
||||
// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted")
|
||||
// assert.Lessf(t, "a", "b", "error message %s", "formatted")
|
||||
// assert.Lessf(t, 1, 2, "error message %s", "formatted")
|
||||
// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted")
|
||||
// assert.Lessf(t, "a", "b", "error message %s", "formatted")
|
||||
func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -442,10 +481,10 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter
|
||||
|
||||
// LessOrEqualf asserts that the first element is less than or equal to the second
|
||||
//
|
||||
// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted")
|
||||
// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted")
|
||||
func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -455,8 +494,8 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args .
|
||||
|
||||
// Negativef asserts that the specified element is negative
|
||||
//
|
||||
// assert.Negativef(t, -1, "error message %s", "formatted")
|
||||
// assert.Negativef(t, -1.23, "error message %s", "formatted")
|
||||
// assert.Negativef(t, -1, "error message %s", "formatted")
|
||||
// assert.Negativef(t, -1.23, "error message %s", "formatted")
|
||||
func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -467,7 +506,7 @@ func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool
|
||||
// Neverf asserts that the given condition doesn't satisfy in waitFor time,
|
||||
// periodically checking the target function each tick.
|
||||
//
|
||||
// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
|
||||
// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
|
||||
func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -477,7 +516,7 @@ func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.
|
||||
|
||||
// Nilf asserts that the specified object is nil.
|
||||
//
|
||||
// assert.Nilf(t, err, "error message %s", "formatted")
|
||||
// assert.Nilf(t, err, "error message %s", "formatted")
|
||||
func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -496,10 +535,10 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool
|
||||
|
||||
// NoErrorf asserts that a function returned no error (i.e. `nil`).
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.NoErrorf(t, err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.NoErrorf(t, err, "error message %s", "formatted") {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -519,9 +558,9 @@ func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) boo
|
||||
// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the
|
||||
// specified substring or element.
|
||||
//
|
||||
// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted")
|
||||
// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted")
|
||||
// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted")
|
||||
// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted")
|
||||
// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted")
|
||||
// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted")
|
||||
func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -532,9 +571,9 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a
|
||||
// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// if assert.NotEmptyf(t, obj, "error message %s", "formatted") {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
// if assert.NotEmptyf(t, obj, "error message %s", "formatted") {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -544,7 +583,7 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{})
|
||||
|
||||
// NotEqualf asserts that the specified values are NOT equal.
|
||||
//
|
||||
// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted")
|
||||
// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted")
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses).
|
||||
@@ -557,7 +596,7 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string,
|
||||
|
||||
// NotEqualValuesf asserts that two objects are not equal even when converted to the same type
|
||||
//
|
||||
// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted")
|
||||
// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted")
|
||||
func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -574,9 +613,19 @@ func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interf
|
||||
return NotErrorIs(t, err, target, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotImplementsf asserts that an object does not implement the specified interface.
|
||||
//
|
||||
// assert.NotImplementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted")
|
||||
func NotImplementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
return NotImplements(t, interfaceObject, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotNilf asserts that the specified object is not nil.
|
||||
//
|
||||
// assert.NotNilf(t, err, "error message %s", "formatted")
|
||||
// assert.NotNilf(t, err, "error message %s", "formatted")
|
||||
func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -586,7 +635,7 @@ func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bo
|
||||
|
||||
// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic.
|
||||
//
|
||||
// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted")
|
||||
// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted")
|
||||
func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -596,8 +645,8 @@ func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bo
|
||||
|
||||
// NotRegexpf asserts that a specified regexp does not match a string.
|
||||
//
|
||||
// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted")
|
||||
// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted")
|
||||
// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted")
|
||||
// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted")
|
||||
func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -607,7 +656,7 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ..
|
||||
|
||||
// NotSamef asserts that two pointers do not reference the same object.
|
||||
//
|
||||
// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted")
|
||||
// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted")
|
||||
//
|
||||
// Both arguments must be pointer variables. Pointer variable sameness is
|
||||
// determined based on the equality of both type and value.
|
||||
@@ -618,10 +667,12 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string,
|
||||
return NotSame(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotSubsetf asserts that the specified list(array, slice...) contains not all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
// NotSubsetf asserts that the specified list(array, slice...) or map does NOT
|
||||
// contain all elements given in the specified subset list(array, slice...) or
|
||||
// map.
|
||||
//
|
||||
// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
|
||||
// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted")
|
||||
// assert.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted")
|
||||
func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -639,7 +690,7 @@ func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
|
||||
|
||||
// Panicsf asserts that the code inside the specified PanicTestFunc panics.
|
||||
//
|
||||
// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -651,7 +702,7 @@ func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool
|
||||
// panics, and that the recovered panic value is an error that satisfies the
|
||||
// EqualError comparison.
|
||||
//
|
||||
// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -662,7 +713,7 @@ func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string,
|
||||
// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that
|
||||
// the recovered panic value equals the expected panic value.
|
||||
//
|
||||
// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
|
||||
func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -672,8 +723,8 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str
|
||||
|
||||
// Positivef asserts that the specified element is positive
|
||||
//
|
||||
// assert.Positivef(t, 1, "error message %s", "formatted")
|
||||
// assert.Positivef(t, 1.23, "error message %s", "formatted")
|
||||
// assert.Positivef(t, 1, "error message %s", "formatted")
|
||||
// assert.Positivef(t, 1.23, "error message %s", "formatted")
|
||||
func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -683,8 +734,8 @@ func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool
|
||||
|
||||
// Regexpf asserts that a specified regexp matches a string.
|
||||
//
|
||||
// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted")
|
||||
// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted")
|
||||
// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted")
|
||||
// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted")
|
||||
func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -694,7 +745,7 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in
|
||||
|
||||
// Samef asserts that two pointers reference the same object.
|
||||
//
|
||||
// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted")
|
||||
// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted")
|
||||
//
|
||||
// Both arguments must be pointer variables. Pointer variable sameness is
|
||||
// determined based on the equality of both type and value.
|
||||
@@ -705,10 +756,11 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg
|
||||
return Same(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Subsetf asserts that the specified list(array, slice...) contains all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
// Subsetf asserts that the specified list(array, slice...) or map contains all
|
||||
// elements given in the specified subset list(array, slice...) or map.
|
||||
//
|
||||
// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
|
||||
// assert.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted")
|
||||
// assert.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted")
|
||||
func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -718,7 +770,7 @@ func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args
|
||||
|
||||
// Truef asserts that the specified value is true.
|
||||
//
|
||||
// assert.Truef(t, myBool, "error message %s", "formatted")
|
||||
// assert.Truef(t, myBool, "error message %s", "formatted")
|
||||
func Truef(t TestingT, value bool, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -728,7 +780,7 @@ func Truef(t TestingT, value bool, msg string, args ...interface{}) bool {
|
||||
|
||||
// WithinDurationf asserts that the two times are within duration delta of each other.
|
||||
//
|
||||
// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted")
|
||||
// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted")
|
||||
func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -738,7 +790,7 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim
|
||||
|
||||
// WithinRangef asserts that a time is within a time range (inclusive).
|
||||
//
|
||||
// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted")
|
||||
// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted")
|
||||
func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
||||
+295
-188
File diff suppressed because it is too large
Load Diff
+12
-12
@@ -46,36 +46,36 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareT
|
||||
|
||||
// IsIncreasing asserts that the collection is increasing
|
||||
//
|
||||
// assert.IsIncreasing(t, []int{1, 2, 3})
|
||||
// assert.IsIncreasing(t, []float{1, 2})
|
||||
// assert.IsIncreasing(t, []string{"a", "b"})
|
||||
// assert.IsIncreasing(t, []int{1, 2, 3})
|
||||
// assert.IsIncreasing(t, []float{1, 2})
|
||||
// assert.IsIncreasing(t, []string{"a", "b"})
|
||||
func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...)
|
||||
}
|
||||
|
||||
// IsNonIncreasing asserts that the collection is not increasing
|
||||
//
|
||||
// assert.IsNonIncreasing(t, []int{2, 1, 1})
|
||||
// assert.IsNonIncreasing(t, []float{2, 1})
|
||||
// assert.IsNonIncreasing(t, []string{"b", "a"})
|
||||
// assert.IsNonIncreasing(t, []int{2, 1, 1})
|
||||
// assert.IsNonIncreasing(t, []float{2, 1})
|
||||
// assert.IsNonIncreasing(t, []string{"b", "a"})
|
||||
func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...)
|
||||
}
|
||||
|
||||
// IsDecreasing asserts that the collection is decreasing
|
||||
//
|
||||
// assert.IsDecreasing(t, []int{2, 1, 0})
|
||||
// assert.IsDecreasing(t, []float{2, 1})
|
||||
// assert.IsDecreasing(t, []string{"b", "a"})
|
||||
// assert.IsDecreasing(t, []int{2, 1, 0})
|
||||
// assert.IsDecreasing(t, []float{2, 1})
|
||||
// assert.IsDecreasing(t, []string{"b", "a"})
|
||||
func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...)
|
||||
}
|
||||
|
||||
// IsNonDecreasing asserts that the collection is not decreasing
|
||||
//
|
||||
// assert.IsNonDecreasing(t, []int{1, 1, 2})
|
||||
// assert.IsNonDecreasing(t, []float{1, 2})
|
||||
// assert.IsNonDecreasing(t, []string{"a", "b"})
|
||||
// assert.IsNonDecreasing(t, []int{1, 1, 2})
|
||||
// assert.IsNonDecreasing(t, []float{1, 2})
|
||||
// assert.IsNonDecreasing(t, []string{"a", "b"})
|
||||
func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...)
|
||||
}
|
||||
|
||||
+389
-152
@@ -8,7 +8,6 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
@@ -20,7 +19,7 @@ import (
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/pmezard/go-difflib/difflib"
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl"
|
||||
@@ -76,6 +75,84 @@ func ObjectsAreEqual(expected, actual interface{}) bool {
|
||||
return bytes.Equal(exp, act)
|
||||
}
|
||||
|
||||
// copyExportedFields iterates downward through nested data structures and creates a copy
|
||||
// that only contains the exported struct fields.
|
||||
func copyExportedFields(expected interface{}) interface{} {
|
||||
if isNil(expected) {
|
||||
return expected
|
||||
}
|
||||
|
||||
expectedType := reflect.TypeOf(expected)
|
||||
expectedKind := expectedType.Kind()
|
||||
expectedValue := reflect.ValueOf(expected)
|
||||
|
||||
switch expectedKind {
|
||||
case reflect.Struct:
|
||||
result := reflect.New(expectedType).Elem()
|
||||
for i := 0; i < expectedType.NumField(); i++ {
|
||||
field := expectedType.Field(i)
|
||||
isExported := field.IsExported()
|
||||
if isExported {
|
||||
fieldValue := expectedValue.Field(i)
|
||||
if isNil(fieldValue) || isNil(fieldValue.Interface()) {
|
||||
continue
|
||||
}
|
||||
newValue := copyExportedFields(fieldValue.Interface())
|
||||
result.Field(i).Set(reflect.ValueOf(newValue))
|
||||
}
|
||||
}
|
||||
return result.Interface()
|
||||
|
||||
case reflect.Ptr:
|
||||
result := reflect.New(expectedType.Elem())
|
||||
unexportedRemoved := copyExportedFields(expectedValue.Elem().Interface())
|
||||
result.Elem().Set(reflect.ValueOf(unexportedRemoved))
|
||||
return result.Interface()
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
var result reflect.Value
|
||||
if expectedKind == reflect.Array {
|
||||
result = reflect.New(reflect.ArrayOf(expectedValue.Len(), expectedType.Elem())).Elem()
|
||||
} else {
|
||||
result = reflect.MakeSlice(expectedType, expectedValue.Len(), expectedValue.Len())
|
||||
}
|
||||
for i := 0; i < expectedValue.Len(); i++ {
|
||||
index := expectedValue.Index(i)
|
||||
if isNil(index) {
|
||||
continue
|
||||
}
|
||||
unexportedRemoved := copyExportedFields(index.Interface())
|
||||
result.Index(i).Set(reflect.ValueOf(unexportedRemoved))
|
||||
}
|
||||
return result.Interface()
|
||||
|
||||
case reflect.Map:
|
||||
result := reflect.MakeMap(expectedType)
|
||||
for _, k := range expectedValue.MapKeys() {
|
||||
index := expectedValue.MapIndex(k)
|
||||
unexportedRemoved := copyExportedFields(index.Interface())
|
||||
result.SetMapIndex(k, reflect.ValueOf(unexportedRemoved))
|
||||
}
|
||||
return result.Interface()
|
||||
|
||||
default:
|
||||
return expected
|
||||
}
|
||||
}
|
||||
|
||||
// ObjectsExportedFieldsAreEqual determines if the exported (public) fields of two objects are
|
||||
// considered equal. This comparison of only exported fields is applied recursively to nested data
|
||||
// structures.
|
||||
//
|
||||
// This function does no assertion of any kind.
|
||||
//
|
||||
// Deprecated: Use [EqualExportedValues] instead.
|
||||
func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool {
|
||||
expectedCleaned := copyExportedFields(expected)
|
||||
actualCleaned := copyExportedFields(actual)
|
||||
return ObjectsAreEqualValues(expectedCleaned, actualCleaned)
|
||||
}
|
||||
|
||||
// ObjectsAreEqualValues gets whether two objects are equal, or if their
|
||||
// values are equal.
|
||||
func ObjectsAreEqualValues(expected, actual interface{}) bool {
|
||||
@@ -83,17 +160,40 @@ func ObjectsAreEqualValues(expected, actual interface{}) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
actualType := reflect.TypeOf(actual)
|
||||
if actualType == nil {
|
||||
expectedValue := reflect.ValueOf(expected)
|
||||
actualValue := reflect.ValueOf(actual)
|
||||
if !expectedValue.IsValid() || !actualValue.IsValid() {
|
||||
return false
|
||||
}
|
||||
expectedValue := reflect.ValueOf(expected)
|
||||
if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) {
|
||||
// Attempt comparison after type conversion
|
||||
return reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual)
|
||||
|
||||
expectedType := expectedValue.Type()
|
||||
actualType := actualValue.Type()
|
||||
if !expectedType.ConvertibleTo(actualType) {
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
if !isNumericType(expectedType) || !isNumericType(actualType) {
|
||||
// Attempt comparison after type conversion
|
||||
return reflect.DeepEqual(
|
||||
expectedValue.Convert(actualType).Interface(), actual,
|
||||
)
|
||||
}
|
||||
|
||||
// If BOTH values are numeric, there are chances of false positives due
|
||||
// to overflow or underflow. So, we need to make sure to always convert
|
||||
// the smaller type to a larger type before comparing.
|
||||
if expectedType.Size() >= actualType.Size() {
|
||||
return actualValue.Convert(expectedType).Interface() == expected
|
||||
}
|
||||
|
||||
return expectedValue.Convert(actualType).Interface() == actual
|
||||
}
|
||||
|
||||
// isNumericType returns true if the type is one of:
|
||||
// int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64,
|
||||
// float32, float64, complex64, complex128
|
||||
func isNumericType(t reflect.Type) bool {
|
||||
return t.Kind() >= reflect.Int && t.Kind() <= reflect.Complex128
|
||||
}
|
||||
|
||||
/* CallerInfo is necessary because the assert functions use the testing object
|
||||
@@ -141,12 +241,11 @@ func CallerInfo() []string {
|
||||
}
|
||||
|
||||
parts := strings.Split(file, "/")
|
||||
file = parts[len(parts)-1]
|
||||
if len(parts) > 1 {
|
||||
filename := parts[len(parts)-1]
|
||||
dir := parts[len(parts)-2]
|
||||
if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" {
|
||||
path, _ := filepath.Abs(file)
|
||||
callers = append(callers, fmt.Sprintf("%s:%d", path, line))
|
||||
if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" {
|
||||
callers = append(callers, fmt.Sprintf("%s:%d", file, line))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +296,7 @@ func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
|
||||
|
||||
// Aligns the provided message so that all lines after the first line start at the same location as the first line.
|
||||
// Assumes that the first line starts at the correct location (after carriage return, tab, label, spacer and tab).
|
||||
// The longestLabelLen parameter specifies the length of the longest label in the output (required becaues this is the
|
||||
// The longestLabelLen parameter specifies the length of the longest label in the output (required because this is the
|
||||
// basis on which the alignment occurs).
|
||||
func indentMessageLines(message string, longestLabelLen int) string {
|
||||
outBuf := new(bytes.Buffer)
|
||||
@@ -273,7 +372,7 @@ type labeledContent struct {
|
||||
|
||||
// labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner:
|
||||
//
|
||||
// \t{{label}}:{{align_spaces}}\t{{content}}\n
|
||||
// \t{{label}}:{{align_spaces}}\t{{content}}\n
|
||||
//
|
||||
// The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label.
|
||||
// If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this
|
||||
@@ -296,7 +395,7 @@ func labeledOutput(content ...labeledContent) string {
|
||||
|
||||
// Implements asserts that an object is implemented by the specified interface.
|
||||
//
|
||||
// assert.Implements(t, (*MyInterface)(nil), new(MyObject))
|
||||
// assert.Implements(t, (*MyInterface)(nil), new(MyObject))
|
||||
func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -313,6 +412,25 @@ func Implements(t TestingT, interfaceObject interface{}, object interface{}, msg
|
||||
return true
|
||||
}
|
||||
|
||||
// NotImplements asserts that an object does not implement the specified interface.
|
||||
//
|
||||
// assert.NotImplements(t, (*MyInterface)(nil), new(MyObject))
|
||||
func NotImplements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
interfaceType := reflect.TypeOf(interfaceObject).Elem()
|
||||
|
||||
if object == nil {
|
||||
return Fail(t, fmt.Sprintf("Cannot check if nil does not implement %v", interfaceType), msgAndArgs...)
|
||||
}
|
||||
if reflect.TypeOf(object).Implements(interfaceType) {
|
||||
return Fail(t, fmt.Sprintf("%T implements %v", object, interfaceType), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsType asserts that the specified objects are of the same type.
|
||||
func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
@@ -328,7 +446,7 @@ func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs
|
||||
|
||||
// Equal asserts that two objects are equal.
|
||||
//
|
||||
// assert.Equal(t, 123, 123)
|
||||
// assert.Equal(t, 123, 123)
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses). Function equality
|
||||
@@ -369,7 +487,7 @@ func validateEqualArgs(expected, actual interface{}) error {
|
||||
|
||||
// Same asserts that two pointers reference the same object.
|
||||
//
|
||||
// assert.Same(t, ptr1, ptr2)
|
||||
// assert.Same(t, ptr1, ptr2)
|
||||
//
|
||||
// Both arguments must be pointer variables. Pointer variable sameness is
|
||||
// determined based on the equality of both type and value.
|
||||
@@ -389,7 +507,7 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b
|
||||
|
||||
// NotSame asserts that two pointers do not reference the same object.
|
||||
//
|
||||
// assert.NotSame(t, ptr1, ptr2)
|
||||
// assert.NotSame(t, ptr1, ptr2)
|
||||
//
|
||||
// Both arguments must be pointer variables. Pointer variable sameness is
|
||||
// determined based on the equality of both type and value.
|
||||
@@ -427,7 +545,7 @@ func samePointers(first, second interface{}) bool {
|
||||
// representations appropriate to be presented to the user.
|
||||
//
|
||||
// If the values are not of like type, the returned strings will be prefixed
|
||||
// with the type name, and the value will be enclosed in parenthesis similar
|
||||
// with the type name, and the value will be enclosed in parentheses similar
|
||||
// to a type conversion in the Go grammar.
|
||||
func formatUnequalValues(expected, actual interface{}) (e string, a string) {
|
||||
if reflect.TypeOf(expected) != reflect.TypeOf(actual) {
|
||||
@@ -454,10 +572,10 @@ func truncatingFormat(data interface{}) string {
|
||||
return value
|
||||
}
|
||||
|
||||
// EqualValues asserts that two objects are equal or convertable to the same types
|
||||
// EqualValues asserts that two objects are equal or convertible to the same types
|
||||
// and equal.
|
||||
//
|
||||
// assert.EqualValues(t, uint32(123), int32(123))
|
||||
// assert.EqualValues(t, uint32(123), int32(123))
|
||||
func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -475,9 +593,60 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa
|
||||
|
||||
}
|
||||
|
||||
// EqualExportedValues asserts that the types of two objects are equal and their public
|
||||
// fields are also equal. This is useful for comparing structs that have private fields
|
||||
// that could potentially differ.
|
||||
//
|
||||
// type S struct {
|
||||
// Exported int
|
||||
// notExported int
|
||||
// }
|
||||
// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true
|
||||
// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false
|
||||
func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
aType := reflect.TypeOf(expected)
|
||||
bType := reflect.TypeOf(actual)
|
||||
|
||||
if aType != bType {
|
||||
return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...)
|
||||
}
|
||||
|
||||
if aType.Kind() == reflect.Ptr {
|
||||
aType = aType.Elem()
|
||||
}
|
||||
if bType.Kind() == reflect.Ptr {
|
||||
bType = bType.Elem()
|
||||
}
|
||||
|
||||
if aType.Kind() != reflect.Struct {
|
||||
return Fail(t, fmt.Sprintf("Types expected to both be struct or pointer to struct \n\t%v != %v", aType.Kind(), reflect.Struct), msgAndArgs...)
|
||||
}
|
||||
|
||||
if bType.Kind() != reflect.Struct {
|
||||
return Fail(t, fmt.Sprintf("Types expected to both be struct or pointer to struct \n\t%v != %v", bType.Kind(), reflect.Struct), msgAndArgs...)
|
||||
}
|
||||
|
||||
expected = copyExportedFields(expected)
|
||||
actual = copyExportedFields(actual)
|
||||
|
||||
if !ObjectsAreEqualValues(expected, actual) {
|
||||
diff := diff(expected, actual)
|
||||
expected, actual = formatUnequalValues(expected, actual)
|
||||
return Fail(t, fmt.Sprintf("Not equal (comparing only exported fields): \n"+
|
||||
"expected: %s\n"+
|
||||
"actual : %s%s", expected, actual, diff), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Exactly asserts that two objects are equal in value and type.
|
||||
//
|
||||
// assert.Exactly(t, int32(123), int64(123))
|
||||
// assert.Exactly(t, int32(123), int64(123))
|
||||
func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -496,7 +665,7 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}
|
||||
|
||||
// NotNil asserts that the specified object is not nil.
|
||||
//
|
||||
// assert.NotNil(t, err)
|
||||
// assert.NotNil(t, err)
|
||||
func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if !isNil(object) {
|
||||
return true
|
||||
@@ -507,17 +676,6 @@ func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
return Fail(t, "Expected value not to be nil.", msgAndArgs...)
|
||||
}
|
||||
|
||||
// containsKind checks if a specified kind in the slice of kinds.
|
||||
func containsKind(kinds []reflect.Kind, kind reflect.Kind) bool {
|
||||
for i := 0; i < len(kinds); i++ {
|
||||
if kind == kinds[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// isNil checks if a specified object is nil or not, without Failing.
|
||||
func isNil(object interface{}) bool {
|
||||
if object == nil {
|
||||
@@ -525,16 +683,13 @@ func isNil(object interface{}) bool {
|
||||
}
|
||||
|
||||
value := reflect.ValueOf(object)
|
||||
kind := value.Kind()
|
||||
isNilableKind := containsKind(
|
||||
[]reflect.Kind{
|
||||
reflect.Chan, reflect.Func,
|
||||
reflect.Interface, reflect.Map,
|
||||
reflect.Ptr, reflect.Slice},
|
||||
kind)
|
||||
switch value.Kind() {
|
||||
case
|
||||
reflect.Chan, reflect.Func,
|
||||
reflect.Interface, reflect.Map,
|
||||
reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
|
||||
|
||||
if isNilableKind && value.IsNil() {
|
||||
return true
|
||||
return value.IsNil()
|
||||
}
|
||||
|
||||
return false
|
||||
@@ -542,7 +697,7 @@ func isNil(object interface{}) bool {
|
||||
|
||||
// Nil asserts that the specified object is nil.
|
||||
//
|
||||
// assert.Nil(t, err)
|
||||
// assert.Nil(t, err)
|
||||
func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if isNil(object) {
|
||||
return true
|
||||
@@ -585,7 +740,7 @@ func isEmpty(object interface{}) bool {
|
||||
// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// assert.Empty(t, obj)
|
||||
// assert.Empty(t, obj)
|
||||
func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
pass := isEmpty(object)
|
||||
if !pass {
|
||||
@@ -602,9 +757,9 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// if assert.NotEmpty(t, obj) {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
// if assert.NotEmpty(t, obj) {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
pass := !isEmpty(object)
|
||||
if !pass {
|
||||
@@ -618,40 +773,38 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
|
||||
}
|
||||
|
||||
// getLen try to get length of object.
|
||||
// return (false, 0) if impossible.
|
||||
func getLen(x interface{}) (ok bool, length int) {
|
||||
// getLen tries to get the length of an object.
|
||||
// It returns (0, false) if impossible.
|
||||
func getLen(x interface{}) (length int, ok bool) {
|
||||
v := reflect.ValueOf(x)
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
ok = false
|
||||
}
|
||||
ok = recover() == nil
|
||||
}()
|
||||
return true, v.Len()
|
||||
return v.Len(), true
|
||||
}
|
||||
|
||||
// Len asserts that the specified object has specific length.
|
||||
// Len also fails if the object has a type that len() not accept.
|
||||
//
|
||||
// assert.Len(t, mySlice, 3)
|
||||
// assert.Len(t, mySlice, 3)
|
||||
func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
ok, l := getLen(object)
|
||||
l, ok := getLen(object)
|
||||
if !ok {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", object), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("\"%v\" could not be applied builtin len()", object), msgAndArgs...)
|
||||
}
|
||||
|
||||
if l != length {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" should have %d item(s), but has %d", object, length, l), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("\"%v\" should have %d item(s), but has %d", object, length, l), msgAndArgs...)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// True asserts that the specified value is true.
|
||||
//
|
||||
// assert.True(t, myBool)
|
||||
// assert.True(t, myBool)
|
||||
func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
if !value {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
@@ -666,7 +819,7 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
|
||||
// False asserts that the specified value is false.
|
||||
//
|
||||
// assert.False(t, myBool)
|
||||
// assert.False(t, myBool)
|
||||
func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
if value {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
@@ -681,7 +834,7 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
|
||||
|
||||
// NotEqual asserts that the specified values are NOT equal.
|
||||
//
|
||||
// assert.NotEqual(t, obj1, obj2)
|
||||
// assert.NotEqual(t, obj1, obj2)
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses).
|
||||
@@ -704,7 +857,7 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{
|
||||
|
||||
// NotEqualValues asserts that two objects are not equal even when converted to the same type
|
||||
//
|
||||
// assert.NotEqualValues(t, obj1, obj2)
|
||||
// assert.NotEqualValues(t, obj1, obj2)
|
||||
func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -763,9 +916,9 @@ func containsElement(list interface{}, element interface{}) (ok, found bool) {
|
||||
// Contains asserts that the specified string, list(array, slice...) or map contains the
|
||||
// specified substring or element.
|
||||
//
|
||||
// assert.Contains(t, "Hello World", "World")
|
||||
// assert.Contains(t, ["Hello", "World"], "World")
|
||||
// assert.Contains(t, {"Hello": "World"}, "Hello")
|
||||
// assert.Contains(t, "Hello World", "World")
|
||||
// assert.Contains(t, ["Hello", "World"], "World")
|
||||
// assert.Contains(t, {"Hello": "World"}, "Hello")
|
||||
func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -786,9 +939,9 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo
|
||||
// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
|
||||
// specified substring or element.
|
||||
//
|
||||
// assert.NotContains(t, "Hello World", "Earth")
|
||||
// assert.NotContains(t, ["Hello", "World"], "Earth")
|
||||
// assert.NotContains(t, {"Hello": "World"}, "Earth")
|
||||
// assert.NotContains(t, "Hello World", "Earth")
|
||||
// assert.NotContains(t, ["Hello", "World"], "Earth")
|
||||
// assert.NotContains(t, {"Hello": "World"}, "Earth")
|
||||
func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -796,20 +949,21 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{})
|
||||
|
||||
ok, found := containsElement(s, contains)
|
||||
if !ok {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...)
|
||||
}
|
||||
if found {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" should not contain \"%s\"", s, contains), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("%#v should not contain %#v", s, contains), msgAndArgs...)
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
// Subset asserts that the specified list(array, slice...) contains all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
// Subset asserts that the specified list(array, slice...) or map contains all
|
||||
// elements given in the specified subset list(array, slice...) or map.
|
||||
//
|
||||
// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
|
||||
// assert.Subset(t, [1, 2, 3], [1, 2])
|
||||
// assert.Subset(t, {"x": 1, "y": 2}, {"x": 1})
|
||||
func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -818,59 +972,56 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
|
||||
return true // we consider nil to be equal to the nil set
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
ok = false
|
||||
}
|
||||
}()
|
||||
|
||||
listKind := reflect.TypeOf(list).Kind()
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
|
||||
if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
subsetValue := reflect.ValueOf(subset)
|
||||
if subsetKind == reflect.Map && listKind == reflect.Map {
|
||||
listValue := reflect.ValueOf(list)
|
||||
subsetKeys := subsetValue.MapKeys()
|
||||
subsetMap := reflect.ValueOf(subset)
|
||||
actualMap := reflect.ValueOf(list)
|
||||
|
||||
for i := 0; i < len(subsetKeys); i++ {
|
||||
subsetKey := subsetKeys[i]
|
||||
subsetElement := subsetValue.MapIndex(subsetKey).Interface()
|
||||
listElement := listValue.MapIndex(subsetKey).Interface()
|
||||
for _, k := range subsetMap.MapKeys() {
|
||||
ev := subsetMap.MapIndex(k)
|
||||
av := actualMap.MapIndex(k)
|
||||
|
||||
if !ObjectsAreEqual(subsetElement, listElement) {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, subsetElement), msgAndArgs...)
|
||||
if !av.IsValid() {
|
||||
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...)
|
||||
}
|
||||
if !ObjectsAreEqual(ev.Interface(), av.Interface()) {
|
||||
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
for i := 0; i < subsetValue.Len(); i++ {
|
||||
element := subsetValue.Index(i).Interface()
|
||||
subsetList := reflect.ValueOf(subset)
|
||||
for i := 0; i < subsetList.Len(); i++ {
|
||||
element := subsetList.Index(i).Interface()
|
||||
ok, found := containsElement(list, element)
|
||||
if !ok {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...)
|
||||
}
|
||||
if !found {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, element), msgAndArgs...)
|
||||
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, element), msgAndArgs...)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// NotSubset asserts that the specified list(array, slice...) contains not all
|
||||
// elements given in the specified subset(array, slice...).
|
||||
// NotSubset asserts that the specified list(array, slice...) or map does NOT
|
||||
// contain all elements given in the specified subset list(array, slice...) or
|
||||
// map.
|
||||
//
|
||||
// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
|
||||
// assert.NotSubset(t, [1, 3, 4], [1, 2])
|
||||
// assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3})
|
||||
func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -879,34 +1030,28 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
|
||||
return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
ok = false
|
||||
}
|
||||
}()
|
||||
|
||||
listKind := reflect.TypeOf(list).Kind()
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
|
||||
if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
subsetKind := reflect.TypeOf(subset).Kind()
|
||||
if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
|
||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
subsetValue := reflect.ValueOf(subset)
|
||||
if subsetKind == reflect.Map && listKind == reflect.Map {
|
||||
listValue := reflect.ValueOf(list)
|
||||
subsetKeys := subsetValue.MapKeys()
|
||||
subsetMap := reflect.ValueOf(subset)
|
||||
actualMap := reflect.ValueOf(list)
|
||||
|
||||
for i := 0; i < len(subsetKeys); i++ {
|
||||
subsetKey := subsetKeys[i]
|
||||
subsetElement := subsetValue.MapIndex(subsetKey).Interface()
|
||||
listElement := listValue.MapIndex(subsetKey).Interface()
|
||||
for _, k := range subsetMap.MapKeys() {
|
||||
ev := subsetMap.MapIndex(k)
|
||||
av := actualMap.MapIndex(k)
|
||||
|
||||
if !ObjectsAreEqual(subsetElement, listElement) {
|
||||
if !av.IsValid() {
|
||||
return true
|
||||
}
|
||||
if !ObjectsAreEqual(ev.Interface(), av.Interface()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -914,8 +1059,9 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
|
||||
return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...)
|
||||
}
|
||||
|
||||
for i := 0; i < subsetValue.Len(); i++ {
|
||||
element := subsetValue.Index(i).Interface()
|
||||
subsetList := reflect.ValueOf(subset)
|
||||
for i := 0; i < subsetList.Len(); i++ {
|
||||
element := subsetList.Index(i).Interface()
|
||||
ok, found := containsElement(list, element)
|
||||
if !ok {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
|
||||
@@ -1060,7 +1206,7 @@ func didPanic(f PanicTestFunc) (didPanic bool, message interface{}, stack string
|
||||
|
||||
// Panics asserts that the code inside the specified PanicTestFunc panics.
|
||||
//
|
||||
// assert.Panics(t, func(){ GoCrazy() })
|
||||
// assert.Panics(t, func(){ GoCrazy() })
|
||||
func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1076,7 +1222,7 @@ func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that
|
||||
// the recovered panic value equals the expected panic value.
|
||||
//
|
||||
// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() })
|
||||
// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() })
|
||||
func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1097,7 +1243,7 @@ func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndAr
|
||||
// panics, and that the recovered panic value is an error that satisfies the
|
||||
// EqualError comparison.
|
||||
//
|
||||
// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() })
|
||||
// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() })
|
||||
func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1117,7 +1263,7 @@ func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs .
|
||||
|
||||
// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
|
||||
//
|
||||
// assert.NotPanics(t, func(){ RemainCalm() })
|
||||
// assert.NotPanics(t, func(){ RemainCalm() })
|
||||
func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1132,7 +1278,7 @@ func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
|
||||
|
||||
// WithinDuration asserts that the two times are within duration delta of each other.
|
||||
//
|
||||
// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second)
|
||||
// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second)
|
||||
func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1148,7 +1294,7 @@ func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration,
|
||||
|
||||
// WithinRange asserts that a time is within a time range (inclusive).
|
||||
//
|
||||
// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second))
|
||||
// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second))
|
||||
func WithinRange(t TestingT, actual, start, end time.Time, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1207,7 +1353,7 @@ func toFloat(x interface{}) (float64, bool) {
|
||||
|
||||
// InDelta asserts that the two numerals are within delta of each other.
|
||||
//
|
||||
// assert.InDelta(t, math.Pi, 22/7.0, 0.01)
|
||||
// assert.InDelta(t, math.Pi, 22/7.0, 0.01)
|
||||
func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1336,7 +1482,7 @@ func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAnd
|
||||
h.Helper()
|
||||
}
|
||||
if math.IsNaN(epsilon) {
|
||||
return Fail(t, "epsilon must not be NaN")
|
||||
return Fail(t, "epsilon must not be NaN", msgAndArgs...)
|
||||
}
|
||||
actualEpsilon, err := calcRelativeError(expected, actual)
|
||||
if err != nil {
|
||||
@@ -1355,19 +1501,26 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
if expected == nil || actual == nil ||
|
||||
reflect.TypeOf(actual).Kind() != reflect.Slice ||
|
||||
reflect.TypeOf(expected).Kind() != reflect.Slice {
|
||||
|
||||
if expected == nil || actual == nil {
|
||||
return Fail(t, "Parameters must be slice", msgAndArgs...)
|
||||
}
|
||||
|
||||
actualSlice := reflect.ValueOf(actual)
|
||||
expectedSlice := reflect.ValueOf(expected)
|
||||
actualSlice := reflect.ValueOf(actual)
|
||||
|
||||
for i := 0; i < actualSlice.Len(); i++ {
|
||||
result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), epsilon)
|
||||
if !result {
|
||||
return result
|
||||
if expectedSlice.Type().Kind() != reflect.Slice {
|
||||
return Fail(t, "Expected value must be slice", msgAndArgs...)
|
||||
}
|
||||
|
||||
expectedLen := expectedSlice.Len()
|
||||
if !IsType(t, expected, actual) || !Len(t, actual, expectedLen) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < expectedLen; i++ {
|
||||
if !InEpsilon(t, expectedSlice.Index(i).Interface(), actualSlice.Index(i).Interface(), epsilon, "at index %d", i) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1380,10 +1533,10 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m
|
||||
|
||||
// NoError asserts that a function returned no error (i.e. `nil`).
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.NoError(t, err) {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.NoError(t, err) {
|
||||
// assert.Equal(t, expectedObj, actualObj)
|
||||
// }
|
||||
func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
if err != nil {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
@@ -1397,10 +1550,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
|
||||
// Error asserts that a function returned an error (i.e. not `nil`).
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.Error(t, err) {
|
||||
// assert.Equal(t, expectedError, err)
|
||||
// }
|
||||
// actualObj, err := SomeFunction()
|
||||
// if assert.Error(t, err) {
|
||||
// assert.Equal(t, expectedError, err)
|
||||
// }
|
||||
func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
if err == nil {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
@@ -1415,8 +1568,8 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
|
||||
// EqualError asserts that a function returned an error (i.e. not `nil`)
|
||||
// and that it is equal to the provided error.
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.EqualError(t, err, expectedErrorString)
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.EqualError(t, err, expectedErrorString)
|
||||
func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1438,8 +1591,8 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte
|
||||
// ErrorContains asserts that a function returned an error (i.e. not `nil`)
|
||||
// and that the error contains the specified substring.
|
||||
//
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.ErrorContains(t, err, expectedErrorSubString)
|
||||
// actualObj, err := SomeFunction()
|
||||
// assert.ErrorContains(t, err, expectedErrorSubString)
|
||||
func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1472,8 +1625,8 @@ func matchRegexp(rx interface{}, str interface{}) bool {
|
||||
|
||||
// Regexp asserts that a specified regexp matches a string.
|
||||
//
|
||||
// assert.Regexp(t, regexp.MustCompile("start"), "it's starting")
|
||||
// assert.Regexp(t, "start...$", "it's not starting")
|
||||
// assert.Regexp(t, regexp.MustCompile("start"), "it's starting")
|
||||
// assert.Regexp(t, "start...$", "it's not starting")
|
||||
func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1490,8 +1643,8 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface
|
||||
|
||||
// NotRegexp asserts that a specified regexp does not match a string.
|
||||
//
|
||||
// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
|
||||
// assert.NotRegexp(t, "^start", "it's not starting")
|
||||
// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
|
||||
// assert.NotRegexp(t, "^start", "it's not starting")
|
||||
func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1603,7 +1756,7 @@ func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
|
||||
|
||||
// JSONEq asserts that two JSON strings are equivalent.
|
||||
//
|
||||
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
|
||||
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
|
||||
func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1726,7 +1879,7 @@ type tHelper interface {
|
||||
// Eventually asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick.
|
||||
//
|
||||
// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond)
|
||||
// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond)
|
||||
func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
@@ -1756,10 +1909,94 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t
|
||||
}
|
||||
}
|
||||
|
||||
// CollectT implements the TestingT interface and collects all errors.
|
||||
type CollectT struct {
|
||||
errors []error
|
||||
}
|
||||
|
||||
// Errorf collects the error.
|
||||
func (c *CollectT) Errorf(format string, args ...interface{}) {
|
||||
c.errors = append(c.errors, fmt.Errorf(format, args...))
|
||||
}
|
||||
|
||||
// FailNow panics.
|
||||
func (*CollectT) FailNow() {
|
||||
panic("Assertion failed")
|
||||
}
|
||||
|
||||
// Deprecated: That was a method for internal usage that should not have been published. Now just panics.
|
||||
func (*CollectT) Reset() {
|
||||
panic("Reset() is deprecated")
|
||||
}
|
||||
|
||||
// Deprecated: That was a method for internal usage that should not have been published. Now just panics.
|
||||
func (*CollectT) Copy(TestingT) {
|
||||
panic("Copy() is deprecated")
|
||||
}
|
||||
|
||||
// EventuallyWithT asserts that given condition will be met in waitFor time,
|
||||
// periodically checking target function each tick. In contrast to Eventually,
|
||||
// it supplies a CollectT to the condition function, so that the condition
|
||||
// function can use the CollectT to call other assertions.
|
||||
// The condition is considered "met" if no errors are raised in a tick.
|
||||
// The supplied CollectT collects all errors from one tick (if there are any).
|
||||
// If the condition is not met before waitFor, the collected errors of
|
||||
// the last tick are copied to t.
|
||||
//
|
||||
// externalValue := false
|
||||
// go func() {
|
||||
// time.Sleep(8*time.Second)
|
||||
// externalValue = true
|
||||
// }()
|
||||
// assert.EventuallyWithT(t, func(c *assert.CollectT) {
|
||||
// // add assertions as needed; any assertion failure will fail the current tick
|
||||
// assert.True(c, externalValue, "expected 'externalValue' to be true")
|
||||
// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false")
|
||||
func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
|
||||
var lastFinishedTickErrs []error
|
||||
ch := make(chan []error, 1)
|
||||
|
||||
timer := time.NewTimer(waitFor)
|
||||
defer timer.Stop()
|
||||
|
||||
ticker := time.NewTicker(tick)
|
||||
defer ticker.Stop()
|
||||
|
||||
for tick := ticker.C; ; {
|
||||
select {
|
||||
case <-timer.C:
|
||||
for _, err := range lastFinishedTickErrs {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
return Fail(t, "Condition never satisfied", msgAndArgs...)
|
||||
case <-tick:
|
||||
tick = nil
|
||||
go func() {
|
||||
collect := new(CollectT)
|
||||
defer func() {
|
||||
ch <- collect.errors
|
||||
}()
|
||||
condition(collect)
|
||||
}()
|
||||
case errs := <-ch:
|
||||
if len(errs) == 0 {
|
||||
return true
|
||||
}
|
||||
// Keep the errors from the last ended condition, so that they can be copied to t if timeout is reached.
|
||||
lastFinishedTickErrs = errs
|
||||
tick = ticker.C
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Never asserts that the given condition doesn't satisfy in waitFor time,
|
||||
// periodically checking the target function each tick.
|
||||
//
|
||||
// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond)
|
||||
// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond)
|
||||
func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
|
||||
+22
-21
@@ -1,39 +1,40 @@
|
||||
// Package assert provides a set of comprehensive testing tools for use with the normal Go testing system.
|
||||
//
|
||||
// Example Usage
|
||||
// # Example Usage
|
||||
//
|
||||
// The following is a complete example using assert in a standard test function:
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// )
|
||||
//
|
||||
// func TestSomething(t *testing.T) {
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// )
|
||||
//
|
||||
// var a string = "Hello"
|
||||
// var b string = "Hello"
|
||||
// func TestSomething(t *testing.T) {
|
||||
//
|
||||
// assert.Equal(t, a, b, "The two words should be the same.")
|
||||
// var a string = "Hello"
|
||||
// var b string = "Hello"
|
||||
//
|
||||
// }
|
||||
// assert.Equal(t, a, b, "The two words should be the same.")
|
||||
//
|
||||
// }
|
||||
//
|
||||
// if you assert many times, use the format below:
|
||||
//
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// )
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// )
|
||||
//
|
||||
// func TestSomething(t *testing.T) {
|
||||
// assert := assert.New(t)
|
||||
// func TestSomething(t *testing.T) {
|
||||
// assert := assert.New(t)
|
||||
//
|
||||
// var a string = "Hello"
|
||||
// var b string = "Hello"
|
||||
// var a string = "Hello"
|
||||
// var b string = "Hello"
|
||||
//
|
||||
// assert.Equal(a, b, "The two words should be the same.")
|
||||
// }
|
||||
// assert.Equal(a, b, "The two words should be the same.")
|
||||
// }
|
||||
//
|
||||
// Assertions
|
||||
// # Assertions
|
||||
//
|
||||
// Assertions allow you to easily write test code, and are global funcs in the `assert` package.
|
||||
// All assertion functions take, as the first argument, the `*testing.T` object provided by the
|
||||
|
||||
+21
-18
@@ -12,7 +12,7 @@ import (
|
||||
// an error if building a new request fails.
|
||||
func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) {
|
||||
w := httptest.NewRecorder()
|
||||
req, err := http.NewRequest(method, url, nil)
|
||||
req, err := http.NewRequest(method, url, http.NoBody)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
@@ -23,7 +23,7 @@ func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (
|
||||
|
||||
// HTTPSuccess asserts that a specified handler returns a success status code.
|
||||
//
|
||||
// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
|
||||
// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
|
||||
@@ -32,12 +32,12 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value
|
||||
}
|
||||
code, err := httpCode(handler, method, url, values)
|
||||
if err != nil {
|
||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
|
||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...)
|
||||
}
|
||||
|
||||
isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent
|
||||
if !isSuccessCode {
|
||||
Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), code))
|
||||
Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...)
|
||||
}
|
||||
|
||||
return isSuccessCode
|
||||
@@ -45,7 +45,7 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value
|
||||
|
||||
// HTTPRedirect asserts that a specified handler returns a redirect status code.
|
||||
//
|
||||
// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
|
||||
@@ -54,12 +54,12 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu
|
||||
}
|
||||
code, err := httpCode(handler, method, url, values)
|
||||
if err != nil {
|
||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
|
||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...)
|
||||
}
|
||||
|
||||
isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect
|
||||
if !isRedirectCode {
|
||||
Fail(t, fmt.Sprintf("Expected HTTP redirect status code for %q but received %d", url+"?"+values.Encode(), code))
|
||||
Fail(t, fmt.Sprintf("Expected HTTP redirect status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...)
|
||||
}
|
||||
|
||||
return isRedirectCode
|
||||
@@ -67,7 +67,7 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu
|
||||
|
||||
// HTTPError asserts that a specified handler returns an error status code.
|
||||
//
|
||||
// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool {
|
||||
@@ -76,12 +76,12 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values
|
||||
}
|
||||
code, err := httpCode(handler, method, url, values)
|
||||
if err != nil {
|
||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
|
||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...)
|
||||
}
|
||||
|
||||
isErrorCode := code >= http.StatusBadRequest
|
||||
if !isErrorCode {
|
||||
Fail(t, fmt.Sprintf("Expected HTTP error status code for %q but received %d", url+"?"+values.Encode(), code))
|
||||
Fail(t, fmt.Sprintf("Expected HTTP error status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...)
|
||||
}
|
||||
|
||||
return isErrorCode
|
||||
@@ -89,7 +89,7 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values
|
||||
|
||||
// HTTPStatusCode asserts that a specified handler returns a specified status code.
|
||||
//
|
||||
// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501)
|
||||
// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501)
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool {
|
||||
@@ -98,12 +98,12 @@ func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, va
|
||||
}
|
||||
code, err := httpCode(handler, method, url, values)
|
||||
if err != nil {
|
||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
|
||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...)
|
||||
}
|
||||
|
||||
successful := code == statuscode
|
||||
if !successful {
|
||||
Fail(t, fmt.Sprintf("Expected HTTP status code %d for %q but received %d", statuscode, url+"?"+values.Encode(), code))
|
||||
Fail(t, fmt.Sprintf("Expected HTTP status code %d for %q but received %d", statuscode, url+"?"+values.Encode(), code), msgAndArgs...)
|
||||
}
|
||||
|
||||
return successful
|
||||
@@ -113,7 +113,10 @@ func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, va
|
||||
// empty string if building a new request fails.
|
||||
func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string {
|
||||
w := httptest.NewRecorder()
|
||||
req, err := http.NewRequest(method, url+"?"+values.Encode(), nil)
|
||||
if len(values) > 0 {
|
||||
url += "?" + values.Encode()
|
||||
}
|
||||
req, err := http.NewRequest(method, url, http.NoBody)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
@@ -124,7 +127,7 @@ func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) s
|
||||
// HTTPBodyContains asserts that a specified handler returns a
|
||||
// body that contains a string.
|
||||
//
|
||||
// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
|
||||
// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
@@ -135,7 +138,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string,
|
||||
|
||||
contains := strings.Contains(body, fmt.Sprint(str))
|
||||
if !contains {
|
||||
Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body))
|
||||
Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...)
|
||||
}
|
||||
|
||||
return contains
|
||||
@@ -144,7 +147,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string,
|
||||
// HTTPBodyNotContains asserts that a specified handler returns a
|
||||
// body that does not contain a string.
|
||||
//
|
||||
// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
|
||||
// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
|
||||
//
|
||||
// Returns whether the assertion was successful (true) or not (false).
|
||||
func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool {
|
||||
@@ -155,7 +158,7 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url strin
|
||||
|
||||
contains := strings.Contains(body, fmt.Sprint(str))
|
||||
if contains {
|
||||
Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body))
|
||||
Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...)
|
||||
}
|
||||
|
||||
return !contains
|
||||
|
||||
+15
-15
@@ -1,17 +1,17 @@
|
||||
// Package mock provides a system by which it is possible to mock your objects
|
||||
// and verify calls are happening as expected.
|
||||
//
|
||||
// Example Usage
|
||||
// # Example Usage
|
||||
//
|
||||
// The mock package provides an object, Mock, that tracks activity on another object. It is usually
|
||||
// embedded into a test object as shown below:
|
||||
//
|
||||
// type MyTestObject struct {
|
||||
// // add a Mock object instance
|
||||
// mock.Mock
|
||||
// type MyTestObject struct {
|
||||
// // add a Mock object instance
|
||||
// mock.Mock
|
||||
//
|
||||
// // other fields go here as normal
|
||||
// }
|
||||
// // other fields go here as normal
|
||||
// }
|
||||
//
|
||||
// When implementing the methods of an interface, you wire your functions up
|
||||
// to call the Mock.Called(args...) method, and return the appropriate values.
|
||||
@@ -19,25 +19,25 @@
|
||||
// For example, to mock a method that saves the name and age of a person and returns
|
||||
// the year of their birth or an error, you might write this:
|
||||
//
|
||||
// func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) {
|
||||
// args := o.Called(firstname, lastname, age)
|
||||
// return args.Int(0), args.Error(1)
|
||||
// }
|
||||
// func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) {
|
||||
// args := o.Called(firstname, lastname, age)
|
||||
// return args.Int(0), args.Error(1)
|
||||
// }
|
||||
//
|
||||
// The Int, Error and Bool methods are examples of strongly typed getters that take the argument
|
||||
// index position. Given this argument list:
|
||||
//
|
||||
// (12, true, "Something")
|
||||
// (12, true, "Something")
|
||||
//
|
||||
// You could read them out strongly typed like this:
|
||||
//
|
||||
// args.Int(0)
|
||||
// args.Bool(1)
|
||||
// args.String(2)
|
||||
// args.Int(0)
|
||||
// args.Bool(1)
|
||||
// args.String(2)
|
||||
//
|
||||
// For objects of your own type, use the generic Arguments.Get(index) method and make a type assertion:
|
||||
//
|
||||
// return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine)
|
||||
// return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine)
|
||||
//
|
||||
// This may cause a panic if the object you are getting is nil (the type assertion will fail), in those
|
||||
// cases you should check for nil first.
|
||||
|
||||
+204
-61
@@ -3,6 +3,7 @@ package mock
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
@@ -13,9 +14,13 @@ import (
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/pmezard/go-difflib/difflib"
|
||||
"github.com/stretchr/objx"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// regex for GCCGO functions
|
||||
var gccgoRE = regexp.MustCompile(`\.pN\d+_`)
|
||||
|
||||
// TestingT is an interface wrapper around *testing.T
|
||||
type TestingT interface {
|
||||
Logf(format string, args ...interface{})
|
||||
@@ -99,7 +104,7 @@ func (c *Call) unlock() {
|
||||
|
||||
// Return specifies the return arguments for the expectation.
|
||||
//
|
||||
// Mock.On("DoSomething").Return(errors.New("failed"))
|
||||
// Mock.On("DoSomething").Return(errors.New("failed"))
|
||||
func (c *Call) Return(returnArguments ...interface{}) *Call {
|
||||
c.lock()
|
||||
defer c.unlock()
|
||||
@@ -109,9 +114,9 @@ func (c *Call) Return(returnArguments ...interface{}) *Call {
|
||||
return c
|
||||
}
|
||||
|
||||
// Panic specifies if the functon call should fail and the panic message
|
||||
// Panic specifies if the function call should fail and the panic message
|
||||
//
|
||||
// Mock.On("DoSomething").Panic("test panic")
|
||||
// Mock.On("DoSomething").Panic("test panic")
|
||||
func (c *Call) Panic(msg string) *Call {
|
||||
c.lock()
|
||||
defer c.unlock()
|
||||
@@ -121,24 +126,24 @@ func (c *Call) Panic(msg string) *Call {
|
||||
return c
|
||||
}
|
||||
|
||||
// Once indicates that that the mock should only return the value once.
|
||||
// Once indicates that the mock should only return the value once.
|
||||
//
|
||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once()
|
||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once()
|
||||
func (c *Call) Once() *Call {
|
||||
return c.Times(1)
|
||||
}
|
||||
|
||||
// Twice indicates that that the mock should only return the value twice.
|
||||
// Twice indicates that the mock should only return the value twice.
|
||||
//
|
||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice()
|
||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice()
|
||||
func (c *Call) Twice() *Call {
|
||||
return c.Times(2)
|
||||
}
|
||||
|
||||
// Times indicates that that the mock should only return the indicated number
|
||||
// Times indicates that the mock should only return the indicated number
|
||||
// of times.
|
||||
//
|
||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5)
|
||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5)
|
||||
func (c *Call) Times(i int) *Call {
|
||||
c.lock()
|
||||
defer c.unlock()
|
||||
@@ -149,7 +154,7 @@ func (c *Call) Times(i int) *Call {
|
||||
// WaitUntil sets the channel that will block the mock's return until its closed
|
||||
// or a message is received.
|
||||
//
|
||||
// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second))
|
||||
// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second))
|
||||
func (c *Call) WaitUntil(w <-chan time.Time) *Call {
|
||||
c.lock()
|
||||
defer c.unlock()
|
||||
@@ -159,7 +164,7 @@ func (c *Call) WaitUntil(w <-chan time.Time) *Call {
|
||||
|
||||
// After sets how long to block until the call returns
|
||||
//
|
||||
// Mock.On("MyMethod", arg1, arg2).After(time.Second)
|
||||
// Mock.On("MyMethod", arg1, arg2).After(time.Second)
|
||||
func (c *Call) After(d time.Duration) *Call {
|
||||
c.lock()
|
||||
defer c.unlock()
|
||||
@@ -171,10 +176,10 @@ func (c *Call) After(d time.Duration) *Call {
|
||||
// mocking a method (such as an unmarshaler) that takes a pointer to a struct and
|
||||
// sets properties in such struct
|
||||
//
|
||||
// Mock.On("Unmarshal", AnythingOfType("*map[string]interface{}")).Return().Run(func(args Arguments) {
|
||||
// arg := args.Get(0).(*map[string]interface{})
|
||||
// arg["foo"] = "bar"
|
||||
// })
|
||||
// Mock.On("Unmarshal", AnythingOfType("*map[string]interface{}")).Return().Run(func(args Arguments) {
|
||||
// arg := args.Get(0).(*map[string]interface{})
|
||||
// arg["foo"] = "bar"
|
||||
// })
|
||||
func (c *Call) Run(fn func(args Arguments)) *Call {
|
||||
c.lock()
|
||||
defer c.unlock()
|
||||
@@ -194,16 +199,18 @@ func (c *Call) Maybe() *Call {
|
||||
// On chains a new expectation description onto the mocked interface. This
|
||||
// allows syntax like.
|
||||
//
|
||||
// Mock.
|
||||
// On("MyMethod", 1).Return(nil).
|
||||
// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error"))
|
||||
// Mock.
|
||||
// On("MyMethod", 1).Return(nil).
|
||||
// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error"))
|
||||
//
|
||||
//go:noinline
|
||||
func (c *Call) On(methodName string, arguments ...interface{}) *Call {
|
||||
return c.Parent.On(methodName, arguments...)
|
||||
}
|
||||
|
||||
// Unset removes a mock handler from being called.
|
||||
// test.On("func", mock.Anything).Unset()
|
||||
//
|
||||
// test.On("func", mock.Anything).Unset()
|
||||
func (c *Call) Unset() *Call {
|
||||
var unlockOnce sync.Once
|
||||
|
||||
@@ -218,16 +225,22 @@ func (c *Call) Unset() *Call {
|
||||
|
||||
foundMatchingCall := false
|
||||
|
||||
for i, call := range c.Parent.ExpectedCalls {
|
||||
// in-place filter slice for calls to be removed - iterate from 0'th to last skipping unnecessary ones
|
||||
var index int // write index
|
||||
for _, call := range c.Parent.ExpectedCalls {
|
||||
if call.Method == c.Method {
|
||||
_, diffCount := call.Arguments.Diff(c.Arguments)
|
||||
if diffCount == 0 {
|
||||
foundMatchingCall = true
|
||||
// Remove from ExpectedCalls
|
||||
c.Parent.ExpectedCalls = append(c.Parent.ExpectedCalls[:i], c.Parent.ExpectedCalls[i+1:]...)
|
||||
// Remove from ExpectedCalls - just skip it
|
||||
continue
|
||||
}
|
||||
}
|
||||
c.Parent.ExpectedCalls[index] = call
|
||||
index++
|
||||
}
|
||||
// trim slice up to last copied index
|
||||
c.Parent.ExpectedCalls = c.Parent.ExpectedCalls[:index]
|
||||
|
||||
if !foundMatchingCall {
|
||||
unlockOnce.Do(c.unlock)
|
||||
@@ -243,9 +256,9 @@ func (c *Call) Unset() *Call {
|
||||
// calls have been called as expected. The referenced calls may be from the
|
||||
// same mock instance and/or other mock instances.
|
||||
//
|
||||
// Mock.On("Do").Return(nil).Notbefore(
|
||||
// Mock.On("Init").Return(nil)
|
||||
// )
|
||||
// Mock.On("Do").Return(nil).Notbefore(
|
||||
// Mock.On("Init").Return(nil)
|
||||
// )
|
||||
func (c *Call) NotBefore(calls ...*Call) *Call {
|
||||
c.lock()
|
||||
defer c.unlock()
|
||||
@@ -328,7 +341,7 @@ func (m *Mock) fail(format string, args ...interface{}) {
|
||||
// On starts a description of an expectation of the specified method
|
||||
// being called.
|
||||
//
|
||||
// Mock.On("MyMethod", arg1, arg2)
|
||||
// Mock.On("MyMethod", arg1, arg2)
|
||||
func (m *Mock) On(methodName string, arguments ...interface{}) *Call {
|
||||
for _, arg := range arguments {
|
||||
if v := reflect.ValueOf(arg); v.Kind() == reflect.Func {
|
||||
@@ -418,6 +431,10 @@ func callString(method string, arguments Arguments, includeArgumentValues bool)
|
||||
if includeArgumentValues {
|
||||
var argVals []string
|
||||
for argIndex, arg := range arguments {
|
||||
if _, ok := arg.(*FunctionalOptionsArgument); ok {
|
||||
argVals = append(argVals, fmt.Sprintf("%d: %s", argIndex, arg))
|
||||
continue
|
||||
}
|
||||
argVals = append(argVals, fmt.Sprintf("%d: %#v", argIndex, arg))
|
||||
}
|
||||
argValsString = fmt.Sprintf("\n\t\t%s", strings.Join(argVals, "\n\t\t"))
|
||||
@@ -441,9 +458,8 @@ func (m *Mock) Called(arguments ...interface{}) Arguments {
|
||||
// For Ex: github_com_docker_libkv_store_mock.WatchTree.pN39_github_com_docker_libkv_store_mock.Mock
|
||||
// uses interface information unlike golang github.com/docker/libkv/store/mock.(*Mock).WatchTree
|
||||
// With GCCGO we need to remove interface information starting from pN<dd>.
|
||||
re := regexp.MustCompile("\\.pN\\d+_")
|
||||
if re.MatchString(functionPath) {
|
||||
functionPath = re.Split(functionPath, -1)[0]
|
||||
if gccgoRE.MatchString(functionPath) {
|
||||
functionPath = gccgoRE.Split(functionPath, -1)[0]
|
||||
}
|
||||
parts := strings.Split(functionPath, ".")
|
||||
functionName := parts[len(parts)-1]
|
||||
@@ -460,7 +476,7 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen
|
||||
found, call := m.findExpectedCall(methodName, arguments...)
|
||||
|
||||
if found < 0 {
|
||||
// expected call found but it has already been called with repeatable times
|
||||
// expected call found, but it has already been called with repeatable times
|
||||
if call != nil {
|
||||
m.mutex.Unlock()
|
||||
m.fail("\nassert: mock: The method has been called over %d times.\n\tEither do one more Mock.On(\"%s\").Return(...), or remove extra call.\n\tThis call was unexpected:\n\t\t%s\n\tat: %s", call.totalCalls, methodName, callString(methodName, arguments, true), assert.CallerInfo())
|
||||
@@ -549,7 +565,7 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen
|
||||
Assertions
|
||||
*/
|
||||
|
||||
type assertExpectationser interface {
|
||||
type assertExpectationiser interface {
|
||||
AssertExpectations(TestingT) bool
|
||||
}
|
||||
|
||||
@@ -566,7 +582,7 @@ func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool {
|
||||
t.Logf("Deprecated mock.AssertExpectationsForObjects(myMock.Mock) use mock.AssertExpectationsForObjects(myMock)")
|
||||
obj = m
|
||||
}
|
||||
m := obj.(assertExpectationser)
|
||||
m := obj.(assertExpectationiser)
|
||||
if !m.AssertExpectations(t) {
|
||||
t.Logf("Expectations didn't match for Mock: %+v", reflect.TypeOf(m))
|
||||
return false
|
||||
@@ -578,6 +594,9 @@ func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool {
|
||||
// AssertExpectations asserts that everything specified with On and Return was
|
||||
// in fact called as expected. Calls may have occurred in any order.
|
||||
func (m *Mock) AssertExpectations(t TestingT) bool {
|
||||
if s, ok := t.(interface{ Skipped() bool }); ok && s.Skipped() {
|
||||
return true
|
||||
}
|
||||
if h, ok := t.(tHelper); ok {
|
||||
h.Helper()
|
||||
}
|
||||
@@ -592,8 +611,8 @@ func (m *Mock) AssertExpectations(t TestingT) bool {
|
||||
satisfied, reason := m.checkExpectation(expectedCall)
|
||||
if !satisfied {
|
||||
failedExpectations++
|
||||
t.Logf(reason)
|
||||
}
|
||||
t.Logf(reason)
|
||||
}
|
||||
|
||||
if failedExpectations != 0 {
|
||||
@@ -744,24 +763,33 @@ const (
|
||||
Anything = "mock.Anything"
|
||||
)
|
||||
|
||||
// AnythingOfTypeArgument is a string that contains the type of an argument
|
||||
// AnythingOfTypeArgument contains the type of an argument
|
||||
// for use when type checking. Used in Diff and Assert.
|
||||
type AnythingOfTypeArgument string
|
||||
//
|
||||
// Deprecated: this is an implementation detail that must not be used. Use [AnythingOfType] instead.
|
||||
type AnythingOfTypeArgument = anythingOfTypeArgument
|
||||
|
||||
// AnythingOfType returns an AnythingOfTypeArgument object containing the
|
||||
// name of the type to check for. Used in Diff and Assert.
|
||||
// anythingOfTypeArgument is a string that contains the type of an argument
|
||||
// for use when type checking. Used in Diff and Assert.
|
||||
type anythingOfTypeArgument string
|
||||
|
||||
// AnythingOfType returns a special value containing the
|
||||
// name of the type to check for. The type name will be matched against the type name returned by [reflect.Type.String].
|
||||
//
|
||||
// Used in Diff and Assert.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// Assert(t, AnythingOfType("string"), AnythingOfType("int"))
|
||||
func AnythingOfType(t string) AnythingOfTypeArgument {
|
||||
return AnythingOfTypeArgument(t)
|
||||
return anythingOfTypeArgument(t)
|
||||
}
|
||||
|
||||
// IsTypeArgument is a struct that contains the type of an argument
|
||||
// for use when type checking. This is an alternative to AnythingOfType.
|
||||
// Used in Diff and Assert.
|
||||
type IsTypeArgument struct {
|
||||
t interface{}
|
||||
t reflect.Type
|
||||
}
|
||||
|
||||
// IsType returns an IsTypeArgument object containing the type to check for.
|
||||
@@ -771,7 +799,35 @@ type IsTypeArgument struct {
|
||||
// For example:
|
||||
// Assert(t, IsType(""), IsType(0))
|
||||
func IsType(t interface{}) *IsTypeArgument {
|
||||
return &IsTypeArgument{t: t}
|
||||
return &IsTypeArgument{t: reflect.TypeOf(t)}
|
||||
}
|
||||
|
||||
// FunctionalOptionsArgument is a struct that contains the type and value of an functional option argument
|
||||
// for use when type checking.
|
||||
type FunctionalOptionsArgument struct {
|
||||
value interface{}
|
||||
}
|
||||
|
||||
// String returns the string representation of FunctionalOptionsArgument
|
||||
func (f *FunctionalOptionsArgument) String() string {
|
||||
var name string
|
||||
tValue := reflect.ValueOf(f.value)
|
||||
if tValue.Len() > 0 {
|
||||
name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String()
|
||||
}
|
||||
|
||||
return strings.Replace(fmt.Sprintf("%#v", f.value), "[]interface {}", name, 1)
|
||||
}
|
||||
|
||||
// FunctionalOptions returns an FunctionalOptionsArgument object containing the functional option type
|
||||
// and the values to check of
|
||||
//
|
||||
// For example:
|
||||
// Assert(t, FunctionalOptions("[]foo.FunctionalOption", foo.Opt1(), foo.Opt2()))
|
||||
func FunctionalOptions(value ...interface{}) *FunctionalOptionsArgument {
|
||||
return &FunctionalOptionsArgument{
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// argumentMatcher performs custom argument matching, returning whether or
|
||||
@@ -907,29 +963,54 @@ func (args Arguments) Diff(objects []interface{}) (string, int) {
|
||||
differences++
|
||||
output = fmt.Sprintf("%s\t%d: FAIL: %s not matched by %s\n", output, i, actualFmt, matcher)
|
||||
}
|
||||
} else if reflect.TypeOf(expected) == reflect.TypeOf((*AnythingOfTypeArgument)(nil)).Elem() {
|
||||
// type checking
|
||||
if reflect.TypeOf(actual).Name() != string(expected.(AnythingOfTypeArgument)) && reflect.TypeOf(actual).String() != string(expected.(AnythingOfTypeArgument)) {
|
||||
// not match
|
||||
differences++
|
||||
output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt)
|
||||
}
|
||||
} else if reflect.TypeOf(expected) == reflect.TypeOf((*IsTypeArgument)(nil)) {
|
||||
t := expected.(*IsTypeArgument).t
|
||||
if reflect.TypeOf(t) != reflect.TypeOf(actual) {
|
||||
differences++
|
||||
output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, reflect.TypeOf(t).Name(), reflect.TypeOf(actual).Name(), actualFmt)
|
||||
}
|
||||
} else {
|
||||
// normal checking
|
||||
switch expected := expected.(type) {
|
||||
case anythingOfTypeArgument:
|
||||
// type checking
|
||||
if reflect.TypeOf(actual).Name() != string(expected) && reflect.TypeOf(actual).String() != string(expected) {
|
||||
// not match
|
||||
differences++
|
||||
output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt)
|
||||
}
|
||||
case *IsTypeArgument:
|
||||
actualT := reflect.TypeOf(actual)
|
||||
if actualT != expected.t {
|
||||
differences++
|
||||
output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected.t.Name(), actualT.Name(), actualFmt)
|
||||
}
|
||||
case *FunctionalOptionsArgument:
|
||||
t := expected.value
|
||||
|
||||
if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) {
|
||||
// match
|
||||
output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, actualFmt, expectedFmt)
|
||||
} else {
|
||||
// not match
|
||||
differences++
|
||||
output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, actualFmt, expectedFmt)
|
||||
var name string
|
||||
tValue := reflect.ValueOf(t)
|
||||
if tValue.Len() > 0 {
|
||||
name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String()
|
||||
}
|
||||
|
||||
tName := reflect.TypeOf(t).Name()
|
||||
if name != reflect.TypeOf(actual).String() && tValue.Len() != 0 {
|
||||
differences++
|
||||
output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt)
|
||||
} else {
|
||||
if ef, af := assertOpts(t, actual); ef == "" && af == "" {
|
||||
// match
|
||||
output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, tName, tName)
|
||||
} else {
|
||||
// not match
|
||||
differences++
|
||||
output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, af, ef)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) {
|
||||
// match
|
||||
output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, actualFmt, expectedFmt)
|
||||
} else {
|
||||
// not match
|
||||
differences++
|
||||
output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, actualFmt, expectedFmt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1096,3 +1177,65 @@ var spewConfig = spew.ConfigState{
|
||||
type tHelper interface {
|
||||
Helper()
|
||||
}
|
||||
|
||||
func assertOpts(expected, actual interface{}) (expectedFmt, actualFmt string) {
|
||||
expectedOpts := reflect.ValueOf(expected)
|
||||
actualOpts := reflect.ValueOf(actual)
|
||||
var expectedNames []string
|
||||
for i := 0; i < expectedOpts.Len(); i++ {
|
||||
expectedNames = append(expectedNames, funcName(expectedOpts.Index(i).Interface()))
|
||||
}
|
||||
var actualNames []string
|
||||
for i := 0; i < actualOpts.Len(); i++ {
|
||||
actualNames = append(actualNames, funcName(actualOpts.Index(i).Interface()))
|
||||
}
|
||||
if !assert.ObjectsAreEqual(expectedNames, actualNames) {
|
||||
expectedFmt = fmt.Sprintf("%v", expectedNames)
|
||||
actualFmt = fmt.Sprintf("%v", actualNames)
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < expectedOpts.Len(); i++ {
|
||||
expectedOpt := expectedOpts.Index(i).Interface()
|
||||
actualOpt := actualOpts.Index(i).Interface()
|
||||
|
||||
expectedFunc := expectedNames[i]
|
||||
actualFunc := actualNames[i]
|
||||
if expectedFunc != actualFunc {
|
||||
expectedFmt = expectedFunc
|
||||
actualFmt = actualFunc
|
||||
return
|
||||
}
|
||||
|
||||
ot := reflect.TypeOf(expectedOpt)
|
||||
var expectedValues []reflect.Value
|
||||
var actualValues []reflect.Value
|
||||
if ot.NumIn() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < ot.NumIn(); i++ {
|
||||
vt := ot.In(i).Elem()
|
||||
expectedValues = append(expectedValues, reflect.New(vt))
|
||||
actualValues = append(actualValues, reflect.New(vt))
|
||||
}
|
||||
|
||||
reflect.ValueOf(expectedOpt).Call(expectedValues)
|
||||
reflect.ValueOf(actualOpt).Call(actualValues)
|
||||
|
||||
for i := 0; i < ot.NumIn(); i++ {
|
||||
if !assert.ObjectsAreEqual(expectedValues[i].Interface(), actualValues[i].Interface()) {
|
||||
expectedFmt = fmt.Sprintf("%s %+v", expectedNames[i], expectedValues[i].Interface())
|
||||
actualFmt = fmt.Sprintf("%s %+v", expectedNames[i], actualValues[i].Interface())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func funcName(opt interface{}) string {
|
||||
n := runtime.FuncForPC(reflect.ValueOf(opt).Pointer()).Name()
|
||||
return strings.TrimSuffix(path.Base(n), path.Ext(n))
|
||||
}
|
||||
|
||||
+12
-11
@@ -1,24 +1,25 @@
|
||||
// Package require implements the same assertions as the `assert` package but
|
||||
// stops test execution when a test fails.
|
||||
//
|
||||
// Example Usage
|
||||
// # Example Usage
|
||||
//
|
||||
// The following is a complete example using require in a standard test function:
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/require"
|
||||
// )
|
||||
//
|
||||
// func TestSomething(t *testing.T) {
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/require"
|
||||
// )
|
||||
//
|
||||
// var a string = "Hello"
|
||||
// var b string = "Hello"
|
||||
// func TestSomething(t *testing.T) {
|
||||
//
|
||||
// require.Equal(t, a, b, "The two words should be the same.")
|
||||
// var a string = "Hello"
|
||||
// var b string = "Hello"
|
||||
//
|
||||
// }
|
||||
// require.Equal(t, a, b, "The two words should be the same.")
|
||||
//
|
||||
// Assertions
|
||||
// }
|
||||
//
|
||||
// # Assertions
|
||||
//
|
||||
// The `require` package have same global functions as in the `assert` package,
|
||||
// but instead of returning a boolean result they call `t.FailNow()`.
|
||||
|
||||
+313
-188
File diff suppressed because it is too large
Load Diff
+295
-188
File diff suppressed because it is too large
Load Diff
+30
-29
@@ -29,37 +29,38 @@
|
||||
// Suite object has assertion methods.
|
||||
//
|
||||
// A crude example:
|
||||
// // Basic imports
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// "github.com/stretchr/testify/suite"
|
||||
// )
|
||||
//
|
||||
// // Define the suite, and absorb the built-in basic suite
|
||||
// // functionality from testify - including a T() method which
|
||||
// // returns the current testing context
|
||||
// type ExampleTestSuite struct {
|
||||
// suite.Suite
|
||||
// VariableThatShouldStartAtFive int
|
||||
// }
|
||||
// // Basic imports
|
||||
// import (
|
||||
// "testing"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// "github.com/stretchr/testify/suite"
|
||||
// )
|
||||
//
|
||||
// // Make sure that VariableThatShouldStartAtFive is set to five
|
||||
// // before each test
|
||||
// func (suite *ExampleTestSuite) SetupTest() {
|
||||
// suite.VariableThatShouldStartAtFive = 5
|
||||
// }
|
||||
// // Define the suite, and absorb the built-in basic suite
|
||||
// // functionality from testify - including a T() method which
|
||||
// // returns the current testing context
|
||||
// type ExampleTestSuite struct {
|
||||
// suite.Suite
|
||||
// VariableThatShouldStartAtFive int
|
||||
// }
|
||||
//
|
||||
// // All methods that begin with "Test" are run as tests within a
|
||||
// // suite.
|
||||
// func (suite *ExampleTestSuite) TestExample() {
|
||||
// assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive)
|
||||
// suite.Equal(5, suite.VariableThatShouldStartAtFive)
|
||||
// }
|
||||
// // Make sure that VariableThatShouldStartAtFive is set to five
|
||||
// // before each test
|
||||
// func (suite *ExampleTestSuite) SetupTest() {
|
||||
// suite.VariableThatShouldStartAtFive = 5
|
||||
// }
|
||||
//
|
||||
// // In order for 'go test' to run this suite, we need to create
|
||||
// // a normal test function and pass our suite to suite.Run
|
||||
// func TestExampleTestSuite(t *testing.T) {
|
||||
// suite.Run(t, new(ExampleTestSuite))
|
||||
// }
|
||||
// // All methods that begin with "Test" are run as tests within a
|
||||
// // suite.
|
||||
// func (suite *ExampleTestSuite) TestExample() {
|
||||
// assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive)
|
||||
// suite.Equal(5, suite.VariableThatShouldStartAtFive)
|
||||
// }
|
||||
//
|
||||
// // In order for 'go test' to run this suite, we need to create
|
||||
// // a normal test function and pass our suite to suite.Run
|
||||
// func TestExampleTestSuite(t *testing.T) {
|
||||
// suite.Run(t, new(ExampleTestSuite))
|
||||
// }
|
||||
package suite
|
||||
|
||||
+13
@@ -7,6 +7,7 @@ import "testing"
|
||||
type TestingSuite interface {
|
||||
T() *testing.T
|
||||
SetT(*testing.T)
|
||||
SetS(suite TestingSuite)
|
||||
}
|
||||
|
||||
// SetupAllSuite has a SetupSuite method, which will run before the
|
||||
@@ -51,3 +52,15 @@ type AfterTest interface {
|
||||
type WithStats interface {
|
||||
HandleStats(suiteName string, stats *SuiteInformation)
|
||||
}
|
||||
|
||||
// SetupSubTest has a SetupSubTest method, which will run before each
|
||||
// subtest in the suite.
|
||||
type SetupSubTest interface {
|
||||
SetupSubTest()
|
||||
}
|
||||
|
||||
// TearDownSubTest has a TearDownSubTest method, which will run after
|
||||
// each subtest in the suite have been run.
|
||||
type TearDownSubTest interface {
|
||||
TearDownSubTest()
|
||||
}
|
||||
|
||||
+30
-3
@@ -22,9 +22,13 @@ var matchMethod = flag.String("testify.m", "", "regular expression to select tes
|
||||
// retrieving the current *testing.T context.
|
||||
type Suite struct {
|
||||
*assert.Assertions
|
||||
|
||||
mu sync.RWMutex
|
||||
require *require.Assertions
|
||||
t *testing.T
|
||||
|
||||
// Parent suite to have access to the implemented methods of parent struct
|
||||
s TestingSuite
|
||||
}
|
||||
|
||||
// T retrieves the current *testing.T context.
|
||||
@@ -43,12 +47,18 @@ func (suite *Suite) SetT(t *testing.T) {
|
||||
suite.require = require.New(t)
|
||||
}
|
||||
|
||||
// SetS needs to set the current test suite as parent
|
||||
// to get access to the parent methods
|
||||
func (suite *Suite) SetS(s TestingSuite) {
|
||||
suite.s = s
|
||||
}
|
||||
|
||||
// Require returns a require context for suite.
|
||||
func (suite *Suite) Require() *require.Assertions {
|
||||
suite.mu.Lock()
|
||||
defer suite.mu.Unlock()
|
||||
if suite.require == nil {
|
||||
suite.require = require.New(suite.T())
|
||||
panic("'Require' must not be called before 'Run' or 'SetT'")
|
||||
}
|
||||
return suite.require
|
||||
}
|
||||
@@ -62,17 +72,19 @@ func (suite *Suite) Assert() *assert.Assertions {
|
||||
suite.mu.Lock()
|
||||
defer suite.mu.Unlock()
|
||||
if suite.Assertions == nil {
|
||||
suite.Assertions = assert.New(suite.T())
|
||||
panic("'Assert' must not be called before 'Run' or 'SetT'")
|
||||
}
|
||||
return suite.Assertions
|
||||
}
|
||||
|
||||
func recoverAndFailOnPanic(t *testing.T) {
|
||||
t.Helper()
|
||||
r := recover()
|
||||
failOnPanic(t, r)
|
||||
}
|
||||
|
||||
func failOnPanic(t *testing.T, r interface{}) {
|
||||
t.Helper()
|
||||
if r != nil {
|
||||
t.Errorf("test panicked: %v\n%s", r, debug.Stack())
|
||||
t.FailNow()
|
||||
@@ -85,9 +97,21 @@ func failOnPanic(t *testing.T, r interface{}) {
|
||||
// Provides compatibility with go test pkg -run TestSuite/TestName/SubTestName.
|
||||
func (suite *Suite) Run(name string, subtest func()) bool {
|
||||
oldT := suite.T()
|
||||
defer suite.SetT(oldT)
|
||||
|
||||
return oldT.Run(name, func(t *testing.T) {
|
||||
suite.SetT(t)
|
||||
defer suite.SetT(oldT)
|
||||
|
||||
defer recoverAndFailOnPanic(t)
|
||||
|
||||
if setupSubTest, ok := suite.s.(SetupSubTest); ok {
|
||||
setupSubTest.SetupSubTest()
|
||||
}
|
||||
|
||||
if tearDownSubTest, ok := suite.s.(TearDownSubTest); ok {
|
||||
defer tearDownSubTest.TearDownSubTest()
|
||||
}
|
||||
|
||||
subtest()
|
||||
})
|
||||
}
|
||||
@@ -98,6 +122,7 @@ func Run(t *testing.T, suite TestingSuite) {
|
||||
defer recoverAndFailOnPanic(t)
|
||||
|
||||
suite.SetT(t)
|
||||
suite.SetS(suite)
|
||||
|
||||
var suiteSetupDone bool
|
||||
|
||||
@@ -142,6 +167,8 @@ func Run(t *testing.T, suite TestingSuite) {
|
||||
suite.SetT(t)
|
||||
defer recoverAndFailOnPanic(t)
|
||||
defer func() {
|
||||
t.Helper()
|
||||
|
||||
r := recover()
|
||||
|
||||
if stats != nil {
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
Dockerfile*
|
||||
.*ignore
|
||||
+10
@@ -1,3 +1,13 @@
|
||||
secrets.yml
|
||||
dist/
|
||||
.idea/
|
||||
|
||||
# ignore tools binaries
|
||||
/git-chglog
|
||||
|
||||
# ignore RELEASE-specific CHANGELOG
|
||||
/RELEASE_CHANGELOG.md
|
||||
|
||||
# Ignore editor temp files
|
||||
*~
|
||||
.vscode/
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- goimports
|
||||
- govet
|
||||
# Run with --fast=false for more extensive checks
|
||||
fast: true
|
||||
# override defaults
|
||||
linters-settings:
|
||||
goimports:
|
||||
# put imports beginning with prefix after 3rd-party packages;
|
||||
# it's a comma-separated list of prefixes
|
||||
local-prefixes: github.com/vmware/govmomi
|
||||
run:
|
||||
timeout: 6m
|
||||
skip-dirs:
|
||||
- vim25/json
|
||||
- vim25/xml
|
||||
- cns/types
|
||||
+156
-47
@@ -1,57 +1,166 @@
|
||||
---
|
||||
project_name: govc
|
||||
project_name: govmomi
|
||||
|
||||
builds:
|
||||
- goos:
|
||||
- linux
|
||||
- darwin
|
||||
- windows
|
||||
- freebsd
|
||||
goarch:
|
||||
- amd64
|
||||
- 386
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
main: ./govc/main.go
|
||||
binary: govc
|
||||
flags: -compiler gc
|
||||
ldflags: -X github.com/vmware/govmomi/govc/flags.GitVersion={{.Version}}
|
||||
archive:
|
||||
name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}'
|
||||
format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
files:
|
||||
- none*
|
||||
- id: govc
|
||||
no_main_check: true
|
||||
goos: &goos-defs
|
||||
- linux
|
||||
- darwin
|
||||
- windows
|
||||
- freebsd
|
||||
goarch: &goarch-defs
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
- mips64le
|
||||
- s390x
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
- PKGPATH=github.com/vmware/govmomi/govc/flags
|
||||
main: ./govc/main.go
|
||||
binary: govc
|
||||
ldflags:
|
||||
- "-X {{.Env.PKGPATH}}.BuildVersion={{.Version}} -X {{.Env.PKGPATH}}.BuildCommit={{.ShortCommit}} -X {{.Env.PKGPATH}}.BuildDate={{.Date}}"
|
||||
- id: vcsim
|
||||
no_main_check: true
|
||||
goos: *goos-defs
|
||||
goarch: *goarch-defs
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
main: ./vcsim/main.go
|
||||
binary: vcsim
|
||||
ldflags:
|
||||
- "-X main.buildVersion={{.Version}} -X main.buildCommit={{.ShortCommit}} -X main.buildDate={{.Date}}"
|
||||
|
||||
nfpms:
|
||||
- package_name: govmomi
|
||||
builds:
|
||||
- govc
|
||||
- vcsim
|
||||
homepage: https://github.com/vmware/govmomi
|
||||
maintainer: Doug MacEachern <dougm@vmware.com>
|
||||
description: |-
|
||||
vSphere CLI
|
||||
formats:
|
||||
- rpm
|
||||
|
||||
archives:
|
||||
- id: govcbuild
|
||||
builds:
|
||||
- govc
|
||||
name_template: >-
|
||||
govc_
|
||||
{{- title .Os }}_
|
||||
{{- if eq .Arch "amd64" }}x86_64
|
||||
{{- else if eq .Arch "386" }}i386
|
||||
{{- else }}{{ .Arch }}{{ end }}
|
||||
format_overrides: &overrides
|
||||
- goos: windows
|
||||
format: zip
|
||||
files: &extrafiles
|
||||
- CHANGELOG.md
|
||||
- LICENSE.txt
|
||||
- README.md
|
||||
|
||||
- id: vcsimbuild
|
||||
builds:
|
||||
- vcsim
|
||||
name_template: >-
|
||||
vcsim_
|
||||
{{- title .Os }}_
|
||||
{{- if eq .Arch "amd64" }}x86_64
|
||||
{{- else if eq .Arch "386" }}i386
|
||||
{{- else }}{{ .Arch }}{{ end }}
|
||||
format_overrides: *overrides
|
||||
files: *extrafiles
|
||||
|
||||
snapshot:
|
||||
name_template: "{{ .Tag }}-next"
|
||||
|
||||
checksum:
|
||||
name_template: '{{ .ProjectName }}_{{ .Version }}_checksums.txt'
|
||||
name_template: "checksums.txt"
|
||||
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
- "^docs:"
|
||||
- "^test:"
|
||||
- Merge pull request
|
||||
- Merge branch
|
||||
brew:
|
||||
github:
|
||||
owner: govmomi
|
||||
name: homebrew-tap
|
||||
commit_author:
|
||||
name: Alfred the Narwhal
|
||||
email: cna-alfred@vmware.com
|
||||
folder: Formula
|
||||
homepage: "https://github.com/vmware/govmomi/blob/master/govc/README.md"
|
||||
description: "govc is a vSphere CLI built on top of govmomi."
|
||||
test: |
|
||||
system "#{bin}/govc version"
|
||||
|
||||
# upload disabled since it is maintained in homebrew-core
|
||||
brews:
|
||||
- name: govc
|
||||
ids:
|
||||
- govcbuild
|
||||
repository:
|
||||
owner: govmomi
|
||||
name: homebrew-tap
|
||||
# TODO: create token in specified tap repo, add as secret to govmomi repo and reference in release workflow
|
||||
# token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
|
||||
# enable once we do fully automated releases
|
||||
skip_upload: true
|
||||
commit_author:
|
||||
name: Alfred the Narwhal
|
||||
email: cna-alfred@vmware.com
|
||||
directory: Formula
|
||||
homepage: "https://github.com/vmware/govmomi/blob/main/govc/README.md"
|
||||
description: "govc is a vSphere CLI built on top of govmomi."
|
||||
test: |
|
||||
system "#{bin}/govc version"
|
||||
install: |
|
||||
bin.install "govc"
|
||||
- name: vcsim
|
||||
ids:
|
||||
- vcsimbuild
|
||||
repository:
|
||||
owner: govmomi
|
||||
name: homebrew-tap
|
||||
# TODO: create token in specified tap repo, add as secret to govmomi repo and reference in release workflow
|
||||
# token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
|
||||
# enable once we do fully automated releases
|
||||
skip_upload: true
|
||||
commit_author:
|
||||
name: Alfred the Narwhal
|
||||
email: cna-alfred@vmware.com
|
||||
directory: Formula
|
||||
homepage: "https://github.com/vmware/govmomi/blob/main/vcsim/README.md"
|
||||
description: "vcsim is a vSphere API simulator built on top of govmomi."
|
||||
test: |
|
||||
system "#{bin}/vcsim -h"
|
||||
install: |
|
||||
bin.install "vcsim"
|
||||
|
||||
dockers:
|
||||
- image: vmware/govc
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
binary: govc
|
||||
tag_templates:
|
||||
- "{{ .Tag }}"
|
||||
- "v{{ .Major }}"
|
||||
- "v{{ .Major }}.{{ .Minor }}"
|
||||
- latest
|
||||
- image_templates:
|
||||
- "vmware/govc:{{ .Tag }}"
|
||||
- "vmware/govc:{{ .ShortCommit }}"
|
||||
- "vmware/govc:latest"
|
||||
dockerfile: Dockerfile.govc
|
||||
ids:
|
||||
- govc
|
||||
build_flag_templates:
|
||||
- "--pull"
|
||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||
- "--label=org.opencontainers.image.title={{.ProjectName}}"
|
||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||
- "--label=org.opencontainers.image.url=https://github.com/vmware/govmomi"
|
||||
- "--platform=linux/amd64"
|
||||
- image_templates:
|
||||
- "vmware/vcsim:{{ .Tag }}"
|
||||
- "vmware/vcsim:{{ .ShortCommit }}"
|
||||
- "vmware/vcsim:latest"
|
||||
dockerfile: Dockerfile.vcsim
|
||||
ids:
|
||||
- vcsim
|
||||
build_flag_templates:
|
||||
- "--pull"
|
||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||
- "--label=org.opencontainers.image.title={{.ProjectName}}"
|
||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||
- "--label=org.opencontainers.image.url=https://github.com/vmware/govmomi"
|
||||
- "--platform=linux/amd64"
|
||||
|
||||
+39
-8
@@ -1,28 +1,59 @@
|
||||
amanpaha <amanpahariya@microsoft.com> amanpaha <84718160+amanpaha@users.noreply.github.com>
|
||||
Amanda H. L. de Andrade <amanda.andrade@serpro.gov.br> Amanda Hager Lopes de Andrade Katz <amanda.katz@serpro.gov.br>
|
||||
Amanda H. L. de Andrade <amanda.andrade@serpro.gov.br> amandahla <amanda.andrade@serpro.gov.br>
|
||||
Amit Bathla <abathla@.vmware.com> <abathla@promb-1s-dhcp216.eng.vmware.com>
|
||||
Andrew Kutz <akutz@vmware.com> akutz <akutz@vmware.com>
|
||||
Andrew Kutz <akutz@vmware.com> <sakutz@gmail.com>
|
||||
Andrew Kutz <akutz@vmware.com> akutz <akutz@vmware.com>
|
||||
Andrew Kutz <akutz@vmware.com> Andrew Kutz <101085+akutz@users.noreply.github.com>
|
||||
Andrew Kutz <akutz@vmware.com> akutz <akutz@users.noreply.github.com>
|
||||
Anfernee Yongkun Gui <agui@vmware.com> <anfernee.gui@gmail.com>
|
||||
Anfernee Yongkun Gui <agui@vmware.com> Yongkun Anfernee Gui <agui@vmware.com>
|
||||
Anna Carrigan <anna.carrigan@hpe.com> Anna <anna.carrigan@outlook.com>
|
||||
Balu Dontu <bdontu@vmware.com> BaluDontu <bdontu@vmware.com>
|
||||
Bruce Downs <bruceadowns@gmail.com> <bdowns@vmware.com>
|
||||
Bruce Downs <bruceadowns@gmail.com> <bruce.downs@jivesoftware.com>
|
||||
Bruce Downs <bruceadowns@gmail.com> <bruce.downs@autodesk.com>
|
||||
Bruce Downs <bruceadowns@gmail.com> <bruce.downs@jivesoftware.com>
|
||||
Bryan Venteicher <bryanventeicher@gmail.com> <bryanv@users.noreply.github.com>
|
||||
Brian Rak <brak@vmware.com> <brakthehack@users.noreply.github.com>
|
||||
Clint Greenwood <cgreenwood@vmware.com> <clint.greenwood@gmail.com>
|
||||
Cédric Blomart <cblomart@gmail.com> <cedric.blomart@minfin.fed.be>
|
||||
Cédric Blomart <cblomart@gmail.com> cedric <cblomart@gmail.com>
|
||||
David Stark <dave@davidstark.name> <david.stark@bskyb.com>
|
||||
Doug MacEachern <dougm@vmware.com> dougm <dougm@users.noreply.github.com>
|
||||
Deyan Popov <deyan.popov@gmail.com> <126056852+dekp@users.noreply.github.com>
|
||||
Eric Gray <egray@vmware.com> <ericgray@users.noreply.github.com>
|
||||
Eric Yutao <eric.yutao@gmail.com> eric <eric.yutao@gmail.com>
|
||||
Fabio Rapposelli <fabio@vmware.com> <fabio@rapposelli.org>
|
||||
Faiyaz Ahmed <faiyaza@vmware.com> Faiyaz Ahmed <ahmedf@vmware.com>
|
||||
Faiyaz Ahmed <faiyaza@vmware.com> Faiyaz Ahmed <faiyaza@gmail.com>
|
||||
Faiyaz Ahmed <faiyaza@vmware.com> Faiyaz Ahmed <fdawg4l@users.noreply.github.com>
|
||||
Hakan Halil <hhalil@vmware.com> <25109775+HakanSunay@users.noreply.github.com>
|
||||
Henrik Hodne <henrik@travis-ci.com> <henrik@hodne.io>
|
||||
Ian Eyberg <ian@deferpanic.com> <ian@opuler.com>
|
||||
Jeremy Canady <jcanady@jackhenry.com> <jcanady@gmail.com>
|
||||
Jiatong Wang <wjiatong@vmware.com> jiatongw <wjiatong@vmware.com>
|
||||
Kiril Karaatanassov <kkaraatanassov@vmware.com> kkaraatanassov <kkaraatanassov@vmware.com>
|
||||
Kiril Karaatanassov <kkaraatanassov@vmware.com> <karaatanassov@users.noreply.github.com>
|
||||
Lintong Jiang <lintongj@vmware.com> lintongj <55512168+lintongj@users.noreply.github.com>
|
||||
Lubron Zhan <lzhan@vmware.com> lubronzhan <lzhan@vmware.com>
|
||||
Lubron Zhan <lzhan@vmware.com> lubronzhan <lubronzhan@gmail.com>
|
||||
Lubron Zhan <lzhan@vmware.com> Lubron <lzhan@vmware.com>
|
||||
Michael Gasch <mgasch@vmware.com> Michael Gasch <embano1@live.com>
|
||||
Michael Gasch <mgasch@vmware.com> <15986659+embano1@users.noreply.github.com>
|
||||
Michael Gasch <mgasch@vmware.com> embano1 <embano1@users.noreply.github.com>
|
||||
Mincho Tonev <mtonev@vmware.com> matonev <31008054+matonev@users.noreply.github.com>
|
||||
Parveen Chahal <parkuma@microsoft.com> <mail.chahal@gmail.com>
|
||||
Pieter Noordhuis <pnoordhuis@vmware.com> <pcnoordhuis@gmail.com>
|
||||
Ricardo Katz <rkatz@vmware.com> <rikatz@users.noreply.github.com>
|
||||
Saad Malik <saad@spectrocloud.com> <simfox3@gmail.com>
|
||||
Stoyan Zhelyazkov <stoyan.zhelyazkov@broadcom.com> <156204153+stoyanzhelyazkov@users.noreply.github.com>
|
||||
Takaaki Furukawa <takaaki.frkw@gmail.com> takaaki.furukawa <takaaki.furukawa@mail.rakuten.com>
|
||||
Takaaki Furukawa <takaaki.frkw@gmail.com> tkak <takaaki.frkw@gmail.com>
|
||||
Vadim Egorov <vegorov@vmware.com> <egorovv@gmail.com>
|
||||
Anfernee Yongkun Gui <agui@vmware.com> <anfernee.gui@gmail.com>
|
||||
Anfernee Yongkun Gui <agui@vmware.com> Yongkun Anfernee Gui <agui@vmware.com>
|
||||
Zach Tucker <ztucker@vmware.com> <jzt@users.noreply.github.com>
|
||||
Zee Yang <zeey@vmware.com> <zee.yang@gmail.com>
|
||||
Jiatong Wang <wjiatong@vmware.com> jiatongw <wjiatong@vmware.com>
|
||||
Uwe Bessle <Uwe.Bessle@iteratec.de> Uwe Bessle <u.bessle.extern@eos-ts.com>
|
||||
Uwe Bessle <Uwe.Bessle@iteratec.de> Uwe Bessle <uwe.bessle@web.de>
|
||||
Vadim Egorov <vegorov@vmware.com> <egorovv@gmail.com>
|
||||
William Lam <wlam@vmware.com> <info.virtuallyghetto@gmail.com>
|
||||
Yun Zhou <yunz@vmware.com> <41678287+gh05tn0va@users.noreply.github.com>
|
||||
Zach G <zguan@vmware.com> zach96guan <zach96guan@users.noreply.github.com>
|
||||
Zach Tucker <ztucker@vmware.com> <jzt@users.noreply.github.com>
|
||||
Zee Yang <zeey@vmware.com> <zee.yang@gmail.com>
|
||||
|
||||
-95
@@ -1,95 +0,0 @@
|
||||
# Use the newer Travis-CI build templates based on the
|
||||
# Debian Linux distribution "Trusty" release.
|
||||
os: linux
|
||||
dist: trusty
|
||||
|
||||
# Disable sudo for all builds by default. This ensures all jobs use
|
||||
# Travis-CI's containerized build environment unless specified otherwise.
|
||||
# The container builds have *much* shorter queue times than the VM-based
|
||||
# build environment on which the sudo builds depend.
|
||||
sudo: false
|
||||
services: false
|
||||
|
||||
# Set the version of Go.
|
||||
language: go
|
||||
go: 1.11
|
||||
|
||||
# Always set the project's Go import path to ensure that forked
|
||||
# builds get cloned to the correct location.
|
||||
go_import_path: github.com/vmware/govmomi
|
||||
|
||||
# Ensure all the jobs know where the temp directory is.
|
||||
env:
|
||||
global: TMPDIR=/tmp
|
||||
|
||||
jobs:
|
||||
include:
|
||||
|
||||
# The "lint" stage runs the various linters against the project.
|
||||
- &lint-stage
|
||||
stage: lint
|
||||
env: LINTER=govet
|
||||
install: true
|
||||
script: make "${LINTER}"
|
||||
|
||||
- <<: *lint-stage
|
||||
env: LINTER=goimports
|
||||
|
||||
# The "build" stage verifies the program can be built against the
|
||||
# various GOOS and GOARCH combinations found in the Go releaser
|
||||
# config file, ".goreleaser.yml".
|
||||
- &build-stage
|
||||
stage: build
|
||||
env: GOOS=linux GOARCH=amd64
|
||||
install: true
|
||||
script: make install
|
||||
|
||||
- <<: *build-stage
|
||||
env: GOOS=linux GOARCH=386
|
||||
|
||||
- <<: *build-stage
|
||||
env: GOOS=darwin GOARCH=amd64
|
||||
- <<: *build-stage
|
||||
env: GOOS=darwin GOARCH=386
|
||||
|
||||
- <<: *build-stage
|
||||
env: GOOS=freebsd GOARCH=amd64
|
||||
- <<: *build-stage
|
||||
env: GOOS=freebsd GOARCH=386
|
||||
|
||||
- <<: *build-stage
|
||||
env: GOOS=windows GOARCH=amd64
|
||||
- <<: *build-stage
|
||||
env: GOOS=windows GOARCH=386
|
||||
|
||||
# The test stage executes the test target.
|
||||
- stage: test
|
||||
install: true
|
||||
script: make test
|
||||
|
||||
# The deploy stage deploys the build artifacts using goreleaser.
|
||||
#
|
||||
# This stage will only be activated when there is an annotated tag present
|
||||
# or when the text "/ci-deploy" is present in the commit message. However,
|
||||
# the "deploy" phase of the build will still only be executed on non-PR
|
||||
# builds as that restriction is baked into Travis-CI.
|
||||
#
|
||||
# Finally, this stage requires the Travis-CI VM infrastructure in order to
|
||||
# leverage Docker. This will increase the amount of time the jobs sit
|
||||
# in the queue, waiting to be built. However, it's a necessity as Travis-CI
|
||||
# only allows the use of Docker with VM builds.
|
||||
- stage: deploy
|
||||
if: tag IS present OR commit_message =~ /\/ci-deploy/
|
||||
sudo: required
|
||||
services: docker
|
||||
install: true
|
||||
script: make install
|
||||
after_success: docker login -u="${DOCKER_USERNAME}" -p="${DOCKER_PASSWORD}"
|
||||
deploy:
|
||||
- provider: script
|
||||
skip_cleanup: true
|
||||
script: curl -sL http://git.io/goreleaser | bash
|
||||
addons:
|
||||
apt:
|
||||
update: true
|
||||
packages: xmlstarlet
|
||||
+4624
-173
File diff suppressed because it is too large
Load Diff
+152
-56
@@ -1,101 +1,197 @@
|
||||
# Contributing to govmomi
|
||||
# Contributing to `govmomi`
|
||||
|
||||
## Getting started
|
||||
|
||||
First, fork the repository on GitHub to your personal account.
|
||||
|
||||
Note that _GOPATH_ can be any directory, the example below uses _$HOME/govmomi_.
|
||||
Change _$USER_ below to your github username if they are not the same.
|
||||
Change `$USER` in the examples below to your Github username if they are not the
|
||||
same.
|
||||
|
||||
``` shell
|
||||
export GOPATH=$HOME/govmomi
|
||||
go get github.com/vmware/govmomi
|
||||
cd $GOPATH/src/github.com/vmware/govmomi
|
||||
git config push.default nothing # anything to avoid pushing to vmware/govmomi by default
|
||||
```bash
|
||||
git clone https://github.com/vmware/govmomi.git && cd govmomi
|
||||
|
||||
# prevent accidentally pushing to vmware/govmomi
|
||||
git config push.default nothing
|
||||
git remote rename origin vmware
|
||||
|
||||
# add your fork
|
||||
git remote add $USER git@github.com:$USER/govmomi.git
|
||||
git fetch $USER
|
||||
|
||||
git fetch -av
|
||||
```
|
||||
|
||||
## Installing from source
|
||||
|
||||
Compile the govmomi libraries and install govc using:
|
||||
|
||||
``` shell
|
||||
go install -v github.com/vmware/govmomi/govc
|
||||
```
|
||||
|
||||
Note that **govc/build.sh** is only used for building release binaries.
|
||||
|
||||
## Contribution flow
|
||||
## Contribution Flow
|
||||
|
||||
This is a rough outline of what a contributor's workflow looks like:
|
||||
|
||||
- Create an issue describing the feature/fix
|
||||
- Create a topic branch from where you want to base your work.
|
||||
- Make commits of logical units.
|
||||
- Make sure your commit messages are in the proper format (see below).
|
||||
- Update CHANGELOG.md and/or govc/CHANGELOG.md when appropriate.
|
||||
- Push your changes to a topic branch in your fork of the repository.
|
||||
- Submit a pull request to vmware/govmomi.
|
||||
- Submit a pull request to `vmware/govmomi`.
|
||||
|
||||
Example:
|
||||
See [below](#format-of-the-commit-message) for details on commit best practices
|
||||
and **supported prefixes**, e.g. `govc: <message>`.
|
||||
|
||||
``` shell
|
||||
git checkout -b my-new-feature vmware/master
|
||||
git commit -a
|
||||
git push $USER my-new-feature
|
||||
> **Note:** If you are new to Git(hub) check out [Git rebase, squash...oh
|
||||
> my!](https://www.mgasch.com/2021/05/git-basics/) for more details on how to
|
||||
> successfully contribute to an open source project.
|
||||
|
||||
### Example 1 - Fix a Bug in `govmomi`
|
||||
|
||||
```bash
|
||||
git checkout -b issue-<number> main
|
||||
git add <files>
|
||||
git commit -m "fix: ..." -m "Closes: #<issue-number>"
|
||||
git push $USER issue-<number>
|
||||
```
|
||||
|
||||
### Stay in sync with upstream
|
||||
### Example 2 - Add a new (non-breaking) API to `govmomi`
|
||||
|
||||
When your branch gets out of sync with the vmware/master branch, use the following to update:
|
||||
```bash
|
||||
git checkout -b issue-<number> main
|
||||
git add <files>
|
||||
git commit -m "Add API ..." -m "Closes: #<issue-number>"
|
||||
git push $USER issue-<number>
|
||||
```
|
||||
|
||||
``` shell
|
||||
git checkout my-new-feature
|
||||
### Example 3 - Add a Feature to `govc`
|
||||
|
||||
```bash
|
||||
git checkout -b issue-<number> main
|
||||
git add <files>
|
||||
git commit -m "govc: Add feature ..." -m "Closes: #<issue-number>"
|
||||
git push $USER issue-<number>
|
||||
```
|
||||
**Note**:
|
||||
To register the new `govc` command package, add a blank `_` import to `govmomi/govc/main.go`.
|
||||
|
||||
### Example 4 - Fix a Bug in `vcsim`
|
||||
|
||||
```bash
|
||||
git checkout -b issue-<number> main
|
||||
git add <files>
|
||||
git commit -m "vcsim: Fix ..." -m "Closes: #<issue-number>"
|
||||
git push $USER issue-<number>
|
||||
```
|
||||
|
||||
### Example 5 - Document Breaking (API) Changes
|
||||
|
||||
Breaking changes, e.g. to the `govmomi` APIs, are highlighted in the `CHANGELOG`
|
||||
and release notes when the keyword `BREAKING:` is used in the commit message
|
||||
body.
|
||||
|
||||
The text after `BREAKING:` is used in the corresponding highlighted section.
|
||||
Thus these details should be stated at the body of the commit message.
|
||||
Multi-line strings are supported.
|
||||
|
||||
```bash
|
||||
git checkout -b issue-<number> main
|
||||
git add <files>
|
||||
cat << EOF | git commit -F -
|
||||
Add ctx to funcXYZ
|
||||
|
||||
This commit introduces context.Context to function XYZ
|
||||
Closes: #1234
|
||||
|
||||
BREAKING: Add ctx to funcXYZ()
|
||||
EOF
|
||||
|
||||
git push $USER issue-<number>
|
||||
```
|
||||
|
||||
### Stay in sync with Upstream
|
||||
|
||||
When your branch gets out of sync with the main branch, use the
|
||||
following to update (rebase):
|
||||
|
||||
```bash
|
||||
git checkout issue-<number>
|
||||
git fetch -a
|
||||
git rebase vmware/master
|
||||
git push --force-with-lease $USER my-new-feature
|
||||
git rebase main
|
||||
git push --force-with-lease $USER issue-<number>
|
||||
```
|
||||
|
||||
### Updating pull requests
|
||||
### Updating Pull Requests
|
||||
|
||||
If your PR fails to pass CI or needs changes based on code review, you'll most likely want to squash these changes into
|
||||
existing commits.
|
||||
If your PR fails to pass CI or needs changes based on code review, it's ok to
|
||||
add more commits stating the changes made, e.g. "Address review comments". This
|
||||
is to assist the reviewer(s) to easily detect and review the recent changes.
|
||||
|
||||
If your pull request contains a single commit or your changes are related to the most recent commit, you can simply
|
||||
amend the commit.
|
||||
In case of small PRs, it's ok to squash and force-push (see further below)
|
||||
directly instead.
|
||||
|
||||
``` shell
|
||||
```bash
|
||||
# incorporate review feedback
|
||||
git add .
|
||||
git commit --amend
|
||||
git push --force-with-lease $USER my-new-feature
|
||||
```
|
||||
|
||||
If you need to squash changes into an earlier commit, you can use:
|
||||
|
||||
``` shell
|
||||
git add .
|
||||
# create a fixup commit which will be merged into your (original) <commit>
|
||||
git commit --fixup <commit>
|
||||
git rebase -i --autosquash vmware/master
|
||||
git push --force-with-lease $USER my-new-feature
|
||||
git push $USER issue-<number>
|
||||
```
|
||||
|
||||
Be sure to add a comment to the PR indicating your new changes are ready to review, as github does not generate a
|
||||
notification when you git push.
|
||||
Be sure to add a comment to the PR indicating your new changes are ready to
|
||||
review, as Github does not generate a notification when you git push.
|
||||
|
||||
### Code style
|
||||
Once the review is complete, squash and push your final commit(s):
|
||||
|
||||
The coding style suggested by the Golang community is used in govmomi. See the
|
||||
```bash
|
||||
# squash all commits into one
|
||||
# --autosquash will automatically detect and merge fixup commits
|
||||
git rebase -i --autosquash main
|
||||
git push --force-with-lease $USER issue-<number>
|
||||
```
|
||||
|
||||
### Code Style
|
||||
|
||||
The coding style suggested by the Go community is used in `govmomi`. See the
|
||||
[style doc](https://github.com/golang/go/wiki/CodeReviewComments) for details.
|
||||
|
||||
Try to limit column width to 120 characters for both code and markdown documents such as this one.
|
||||
Try to limit column width to 120 characters for both code and markdown documents
|
||||
such as this one.
|
||||
|
||||
### Format of the Commit Message
|
||||
|
||||
We follow the conventions on [How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/).
|
||||
We follow the conventions described in [How to Write a Git Commit
|
||||
Message](http://chris.beams.io/posts/git-commit/).
|
||||
|
||||
Be sure to include any related GitHub issue references in the commit message.
|
||||
Be sure to include any related GitHub issue references in the commit message,
|
||||
e.g. `Closes: #<number>`.
|
||||
|
||||
The [`CHANGELOG.md`](./CHANGELOG.md) and release page uses **commit message
|
||||
prefixes** for grouping and highlighting. A commit message that
|
||||
starts with `[prefix:] ` will place this commit under the respective
|
||||
section in the `CHANGELOG`.
|
||||
|
||||
The following example creates a commit referencing the `issue: 1234` and puts
|
||||
the commit message in the `govc` `CHANGELOG` section:
|
||||
|
||||
```bash
|
||||
git commit -s -m "govc: Add CLI command X" -m "Closes: #1234"
|
||||
```
|
||||
|
||||
Currently the following prefixes are used:
|
||||
|
||||
- `api:` - Use for API-related changes
|
||||
- `govc:` - Use for changes to `govc` CLI
|
||||
- `vcsim:` - Use for changes to vCenter Simulator
|
||||
- `chore:` - Use for repository related activities
|
||||
- `fix:` - Use for bug fixes
|
||||
- `docs:` - Use for changes to the documentation
|
||||
- `examples:` - Use for changes to examples
|
||||
|
||||
If your contribution falls into multiple categories, e.g. `api` and `vcsim` it
|
||||
is recommended to break up your commits using distinct prefixes.
|
||||
|
||||
### Running CI Checks and Tests
|
||||
You can run both `make check` and `make test` from the top level of the
|
||||
repository.
|
||||
|
||||
While `make check` will catch formatting and import errors, it will not apply
|
||||
any fixes. The developer is expected to do that.
|
||||
|
||||
## Reporting Bugs and Creating Issues
|
||||
|
||||
When opening a new issue, try to roughly follow the commit message format conventions above.
|
||||
When opening a new issue, try to roughly follow the commit message format
|
||||
conventions above.
|
||||
|
||||
+181
-6
@@ -5,104 +5,279 @@
|
||||
|
||||
Abhijeet Kasurde <akasurde@redhat.com>
|
||||
abrarshivani <abrarshivani@users.noreply.github.com>
|
||||
Adam Chalkley <atc0005@users.noreply.github.com>
|
||||
Adam Fowler <adam@adamfowler.org>
|
||||
Adam Shannon <adamkshannon@gmail.com>
|
||||
Akanksha Panse <pansea@vmware.com>
|
||||
Al Biheiri <abiheiri@apple.com>
|
||||
Alessandro Cortiana <alessandro.cortiana@gmail.com>
|
||||
Alex <puzo2002@gmail.com>
|
||||
Alex Bozhenko <alexbozhenko@fb.com>
|
||||
Alex Ellis (VMware) <alexellis2@gmail.com>
|
||||
Aligator <8278538+yet-another-aligator@users.noreply.github.com>
|
||||
Alvaro Miranda <kikitux@gmail.com>
|
||||
Amanda H. L. de Andrade <amanda.andrade@serpro.gov.br>
|
||||
amanpaha <amanpahariya@microsoft.com>
|
||||
Amit Bathla <abathla@.vmware.com>
|
||||
amit bezalel <amit.bezalel@hpe.com>
|
||||
Andrew <AndrewDi@users.noreply.github.com>
|
||||
Andrew Chin <andrew@andrewtchin.com>
|
||||
Andrew Kutz <akutz@vmware.com>
|
||||
Andrey Klimentyev <andrey.klimentyev@flant.com>
|
||||
Anfernee Yongkun Gui <agui@vmware.com>
|
||||
angystardust <angystardust@users.noreply.github.com>
|
||||
aniketGslab <aniket.shinde@gslab.com>
|
||||
Ankit Gohil <gohilankit@gmail.com>
|
||||
Ankit Vaidya <vaidyaa@vmware.com>
|
||||
Ankur Huralikoppi <huralikoppia@vmware.com>
|
||||
Anna Carrigan <anna.carrigan@hpe.com>
|
||||
Antony Saba <awsaba@gmail.com>
|
||||
Ariel Chinn <arielchinn@gmail.com>
|
||||
Arran Walker <arran.walker@zopa.com>
|
||||
Artem Anisimov <aanisimov@inbox.ru>
|
||||
Arunesh Pandey <parunesh@vmware.com>
|
||||
Aryeh Weinreb <aryehweinreb@gmail.com>
|
||||
Augy StClair <augy@google.com>
|
||||
Austin Parker <aparker@apprenda.com>
|
||||
Balu Dontu <bdontu@vmware.com>
|
||||
bastienbc <bastien.barbe.creuly@gmail.com>
|
||||
Ben Corrie <bcorrie@vmware.com>
|
||||
Ben Vickers <bvickers@pivotal.io>
|
||||
Benjamin Davini <davinib@vmware.com>
|
||||
Benjamin Peterson <benjamin@python.org>
|
||||
Benjamin Vickers <bvickers@vmware.com>
|
||||
Bhavya Choudhary <bhavyac@vmware.com>
|
||||
Bob Killen <killen.bob@gmail.com>
|
||||
Brad Fitzpatrick <bradfitz@golang.org>
|
||||
Brian McClain <brianmmcclain@gmail.com>
|
||||
Brian Rak <brak@vmware.com>
|
||||
brian57860 <brian57860@users.noreply.github.com>
|
||||
Bruce Downs <bruceadowns@gmail.com>
|
||||
Cédric Blomart <cblomart@gmail.com>
|
||||
Bruno Meneguello <1322552+bkmeneguello@users.noreply.github.com>
|
||||
Bryan Venteicher <bryanventeicher@gmail.com>
|
||||
C S P Nanda <cspn@google.com>
|
||||
Carsten Grohmann <mail@carstengrohmann.de>
|
||||
Cheng Cheng <chengch@vmware.com>
|
||||
Chethan Venkatesh <chethanv@vmware.com>
|
||||
Choudhury Sarada Prasanna Nanda <cspn@google.com>
|
||||
Chris Marchesi <chrism@vancluevertech.com>
|
||||
Christian Höltje <docwhat@gerf.org>
|
||||
Christian Schlotter <christi.schlotter@gmail.com>
|
||||
Clint Greenwood <cgreenwood@vmware.com>
|
||||
cpiment <pimentel.carlos@gmail.com>
|
||||
CuiHaozhi <cuihaozhi@chinacloud.com.cn>
|
||||
Cédric Blomart <cblomart@gmail.com>
|
||||
Dan Ilan <danilan@google.com>
|
||||
Dan Norris <protochron@users.noreply.github.com>
|
||||
Daniel Frederick Crisman <daniel@crisman.org>
|
||||
Daniel Mueller <deso@posteo.net>
|
||||
Danielle Barda <dbarda@redhat.com>
|
||||
Danny Lockard <danny.lockard@banno.com>
|
||||
Dave Gress <gressd@vmware.com>
|
||||
Dave Smith-Uchida <dsmithuchida@vmware.com>
|
||||
Dave Tucker <dave@dtucker.co.uk>
|
||||
Davide Agnello <dagnello@hp.com>
|
||||
David Gress <gressd@vmware.com>
|
||||
David Stark <dave@davidstark.name>
|
||||
Davide Agnello <dagnello@hp.com>
|
||||
Davinder Kumar <davinderk@vmware.com>
|
||||
Defa <zhoudefa666@163.com>
|
||||
demarey <christophe.demarey@inria.fr>
|
||||
Deric Crago <deric.crago@gmail.com>
|
||||
Deyan Popov <deyan.popov@gmail.com>
|
||||
Dinesh Bhat <35480850+dbhat-arkin@users.noreply.github.com>
|
||||
ditsuke <ditsuke@protonmail.com>
|
||||
Divyen Patel <divyenp@vmware.com>
|
||||
Dnyanesh Gate <dnyanesh.gate@druva.com>
|
||||
Doug MacEachern <dougm@vmware.com>
|
||||
East <60801291+houfangdong@users.noreply.github.com>
|
||||
Eloy Coto <eloy.coto@gmail.com>
|
||||
Eng Zer Jun <engzerjun@gmail.com>
|
||||
Eric Edens <ericedens@google.com>
|
||||
Eric Graham <16710890+Pheric@users.noreply.github.com>
|
||||
Eric Gray <egray@vmware.com>
|
||||
Eric Yutao <eric.yutao@gmail.com>
|
||||
Erik Hollensbe <github@hollensbe.org>
|
||||
Erik Lund <info@erikjensen.it>
|
||||
Essodjolo KAHANAM <essodjolo@kahanam.com>
|
||||
Ethan Kaley <ethan.kaley@emc.com>
|
||||
Evan Chu <echu@vmware.com>
|
||||
Fabio Rapposelli <fabio@vmware.com>
|
||||
Faiyaz Ahmed <ahmedf@vmware.com>
|
||||
fabriziopandini <fpandini@vmware.com>
|
||||
Faiyaz Ahmed <faiyaza@vmware.com>
|
||||
Federico Pellegatta <12744504+federico-pellegatta@users.noreply.github.com>
|
||||
forkbomber <forkbomber@users.noreply.github.com>
|
||||
François Rigault <rigault.francois@gmail.com>
|
||||
freebsdly <qinhuajun@outlook.com>
|
||||
Gavin Gray <gavin@infinio.com>
|
||||
Gavrie Philipson <gavrie.philipson@elastifile.com>
|
||||
George Hicken <ghicken@vmware.com>
|
||||
George Hicken <hickeng@users.noreply.github.com>
|
||||
Gerrit Renker <Gerrit.Renker@ctl.io>
|
||||
gthombare <gthombare@vmware.com>
|
||||
guoguangwu <guoguangwu@magic-shield.com>
|
||||
Hakan Halil <hhalil@vmware.com>
|
||||
HakanSunay <hakansunay@abv.bg>
|
||||
Hasan Mahmood <mahmoodh@vmware.com>
|
||||
Haydon Ryan <haydon.ryan@gmail.com>
|
||||
Heiko Reese <hreese@users.noreply.github.com>
|
||||
Henrik Hodne <henrik@travis-ci.com>
|
||||
hkumar <hkumar@vmware.com>
|
||||
Hrabur Stoyanov <hstoyanov@vmware.com>
|
||||
hui luo <luoh@vmware.com>
|
||||
Ian Eyberg <ian@deferpanic.com>
|
||||
Isaac Rodman <isaac@eyz.us>
|
||||
Ivan Mikushin <imikushin@vmware.com>
|
||||
Ivan Porto Carrero <icarrero@vmware.com>
|
||||
James King <james.king@emc.com>
|
||||
James Peach <jpeach@vmware.com>
|
||||
Jason Kincl <jkincl@gmail.com>
|
||||
Jeremy Canady <jcanady@jackhenry.com>
|
||||
jeremy-clerc <jeremy@clerc.io>
|
||||
Jiatong Wang <wjiatong@vmware.com>
|
||||
João Pereira <joaodrp@gmail.com>
|
||||
jingyizPensando <jingyiz@pensando.io>
|
||||
Jonas Ausevicius <jonas.ausevicius@virtustream.com>
|
||||
Jorge Sevilla <jorge.sevilla@rstor.io>
|
||||
Joseph LeBlanc <jsleblanc@users.noreply.github.com>
|
||||
João Pereira <joaodrp@gmail.com>
|
||||
Julien PILLON <jpillon@lesalternatives.org>
|
||||
Justin J. Novack <jnovack@users.noreply.github.com>
|
||||
kayrus <kay.diam@gmail.com>
|
||||
Keenan Brock <keenan@thebrocks.net>
|
||||
Kevin George <georgek@vmware.com>
|
||||
Kiril Karaatanassov <kkaraatanassov@vmware.com>
|
||||
Knappek <andy.knapp.ak@gmail.com>
|
||||
Kristian Alvestad <kristian.alvestad@statnett.no>
|
||||
Lars Lehtonen <lars.lehtonen@gmail.com>
|
||||
Leslie Wang <qiwa@pensando.io>
|
||||
leslie-qiwa <leslie.qiwa@gmail.com>
|
||||
Lintong Jiang <lintongj@vmware.com>
|
||||
Liping Xue <lipingx@vmware.com>
|
||||
liron.levin <liron.levin@wiz.io>
|
||||
Louie Jiang <jiangl@vmware.com>
|
||||
Lubron Zhan <lzhan@vmware.com>
|
||||
Luther Monson <luther.monson@gmail.com>
|
||||
Madanagopal Arunachalam <marunachalam@vmware.com>
|
||||
makelarisjr <8687447+makelarisjr@users.noreply.github.com>
|
||||
Manuel Grandeit <m.grandeit@gmail.com>
|
||||
maplain <fangyuanl@vmware.com>
|
||||
Marc Carmier <mcarmier@gmail.com>
|
||||
Marcus Tan <marcus.tan@rubrik.com>
|
||||
Maria Ntalla <maria.ntalla@gmail.com>
|
||||
Marin Atanasov Nikolov <mnikolov@vmware.com>
|
||||
Mario Trangoni <mjtrangoni@gmail.com>
|
||||
Mark Dechiaro <mdechiaro@users.noreply.github.com>
|
||||
Mark Peek <markpeek@vmware.com>
|
||||
Mark Rexwinkel <Mark.Rexwinkel@elekta.com>
|
||||
martin <martin@catai.org>
|
||||
Matt Clay <matt@mystile.com>
|
||||
Matthew Cosgrove <matthew.cosgrove@dell.com>
|
||||
Matt Moore <mattmoor@vmware.com>
|
||||
Matt Moriarity <matt@mattmoriarity.com>
|
||||
Matthew Cosgrove <matthew.cosgrove@dell.com>
|
||||
Mayank Bhatt <bmayank@vmware.com>
|
||||
mbhadale <mbhadale@vmware.com>
|
||||
Merlijn Sebrechts <merlijn.sebrechts@gmail.com>
|
||||
Mevan Samaratunga <mevansam@gmail.com>
|
||||
Michael Gasch <mgasch@vmware.com>
|
||||
Michal Jankowski <mjankowski@vmware.com>
|
||||
Mike Schinkel <mike@newclarity.net>
|
||||
Mincho Tonev <mtonev@vmware.com>
|
||||
mingwei <mingwei@smartx.com>
|
||||
Nicolas Lamirault <nicolas.lamirault@gmail.com>
|
||||
nikhaild <84156354+nikhaild@users.noreply.github.com>
|
||||
Nikhil Kathare <nikhil.kathare@netapp.com>
|
||||
Nikhil R Deshpande <ndeshpande@vmware.com>
|
||||
Nikolas Grottendieck <git@nikolasgrottendieck.com>
|
||||
Nils Elde <nils.elde@sscinc.com>
|
||||
nirbhay <nirbhay.bagmar@nutanix.com>
|
||||
Nobuhiro MIKI <nmiki@yahoo-corp.jp>
|
||||
Om Kumar <om.kumar@hpe.com>
|
||||
Omar Kohl <omarkohl@gmail.com>
|
||||
Parham Alvani <parham.alvani@gmail.com>
|
||||
Parveen Chahal <parkuma@microsoft.com>
|
||||
Paul Martin <25058109+rawstorage@users.noreply.github.com>
|
||||
Pierre Gronlier <pierre.gronlier@corp.ovh.com>
|
||||
Pieter Noordhuis <pnoordhuis@vmware.com>
|
||||
pradeepj <50135054+pradeep288@users.noreply.github.com>
|
||||
Pranshu Jain <jpranshu@vmware.com>
|
||||
prydin <prydin@vmware.com>
|
||||
rconde01 <rconde01@hotmail.com>
|
||||
rHermes <teodor_spaeren@riseup.net>
|
||||
Rianto Wahyudi <rwahyudi@gmail.com>
|
||||
Ricardo Katz <rkatz@vmware.com>
|
||||
rmcqueen <rmcqueen@vmware.com>
|
||||
Robin Watkins <robwatkins@gmail.com>
|
||||
Rowan Jacobs <rojacobs@pivotal.io>
|
||||
Roy Ling <royling0024@gmail.com>
|
||||
rsikdar <rsikdar@berkeley.edu>
|
||||
runner.mei <runner.mei@gmail.com>
|
||||
Ryan Johnson <johnsonryan@vmware.com>
|
||||
S R Ashrith <sashrith@vmware.com>
|
||||
S.Çağlar Onur <conur@vmware.com>
|
||||
Saad Malik <saad@spectrocloud.com>
|
||||
Sam Zhu <zhusa@zhusa-a02.vmware.com>
|
||||
samzhu333 <45263849+samzhu333@users.noreply.github.com>
|
||||
Sandeep Pissay Srinivasa Rao <ssrinivas@vmware.com>
|
||||
schmikei <keith.schmitt@bluemedora.com>
|
||||
Scott Holden <scott@nullops.io>
|
||||
Sergey Ignatov <sergey.ignatov@jetbrains.com>
|
||||
serokles <timbo.alexander@gmail.com>
|
||||
shahra <shahra@vmware.com>
|
||||
Shalini Bhaskara <sbhaskara@vmware.com>
|
||||
Shaozhen Ding <dsz0111@gmail.com>
|
||||
Shawn Neal <sneal@sneal.net>
|
||||
shylasrinivas <sshyla@vmware.com>
|
||||
sky-joker <sky.jokerxx@gmail.com>
|
||||
smaftoul <samuel.maftoul@gmail.com>
|
||||
smahadik <smahadik@vmware.com>
|
||||
Sten Feldman <exile@chamber.ee>
|
||||
Stepan Mazurov <smazurov@gmail.com>
|
||||
Steve Purcell <steve@sanityinc.com>
|
||||
Stoyan Zhelyazkov <stoyan.zhelyazkov@broadcom.com>
|
||||
Sudhindra Aithal <sudhiaithal@pensando.io>
|
||||
SUMIT AGRAWAL <asumit@vmware.com>
|
||||
Sunny Carter <sunny.carter@metaswitch.com>
|
||||
syuparn <s.hello.spagetti@gmail.com>
|
||||
Takaaki Furukawa <takaaki.frkw@gmail.com>
|
||||
Tamas Eger <tamas.eger@bitrise.io>
|
||||
Tanay Kothari <tkothari@vmware.com>
|
||||
tanishi <tanishi503@gmail.com>
|
||||
Ted Zlatanov <tzz@lifelogs.com>
|
||||
Thad Craft <tcraft@pivotal.io>
|
||||
Thibaut Ackermann <thibaut.ackermann@alcatel-lucent.com>
|
||||
Tim McNamara <tim.mcnamara@canonical.com>
|
||||
Tjeu Kayim <15987676+TjeuKayim@users.noreply.github.com>
|
||||
Toomas Pelberg <toomas.pelberg@playtech.com>
|
||||
Trevor Dawe <trevor.dawe@gmail.com>
|
||||
tshihad <tshihad9@gmail.com>
|
||||
Ueli Banholzer <ueli@whatwedo.ch>
|
||||
Uwe Bessle <Uwe.Bessle@iteratec.de>
|
||||
Vadim Egorov <vegorov@vmware.com>
|
||||
Vamshik Shetty <svamshik@vmware.com>
|
||||
Vikram Krishnamurthy <vikramkrishnamu@vmware.com>
|
||||
Vipul Kotkar <vkotkar@vmware.com>
|
||||
volanja <volaaanja@gmail.com>
|
||||
Volodymyr Bobyr <pupsua@gmail.com>
|
||||
Waldek Maleska <w.maleska@gmail.com>
|
||||
William Lam <wlam@vmware.com>
|
||||
Witold Krecicki <wpk@culm.net>
|
||||
xing-yang <xingyang105@gmail.com>
|
||||
xinyanw409 <wxinyan@vmware.com>
|
||||
Yang Yang <yangy@vmware.com>
|
||||
yangxi <yangxi@vmware.com>
|
||||
Yann Hodique <yhodique@google.com>
|
||||
Yash Nitin Desai <desaiy@vmware.com>
|
||||
Yassine TIJANI <ytijani@vmware.com>
|
||||
Yi Jiang <yijiang@vmware.com>
|
||||
yiyingy <yiyingy@vmware.com>
|
||||
ykakarap <yuva2811@gmail.com>
|
||||
Yogesh Sobale <6104071+ysobale@users.noreply.github.com>
|
||||
Your Name <you@example.com>
|
||||
Yue Yin <yueyin@yuyin-a01.vmware.com>
|
||||
Yun Zhou <yunz@vmware.com>
|
||||
Yuya Kusakabe <yuya.kusakabe@gmail.com>
|
||||
Zacharias Taubert <zacharias.taubert@gmail.com>
|
||||
Zach G <zguan@vmware.com>
|
||||
Zach Tucker <ztucker@vmware.com>
|
||||
Zacharias Taubert <zacharias.taubert@gmail.com>
|
||||
Zee Yang <zeey@vmware.com>
|
||||
zyuxin <zyuxin@vmware.com>
|
||||
Кузаков Евгений <kuzakov@satel.org>
|
||||
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
FROM scratch
|
||||
LABEL maintainer="fabio@vmware.com"
|
||||
COPY govc /
|
||||
ENTRYPOINT [ "/govc" ]
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
# Create a builder container
|
||||
# golang:1.18.0-buster amd64
|
||||
FROM golang@sha256:7d39537344486528f8cdb3bd8adb98ab7f0f4236044b6944fed8631da35a4ce5 AS build
|
||||
WORKDIR /go/src/app
|
||||
|
||||
# Create appuser to isolate potential vulnerabilities
|
||||
# See https://stackoverflow.com/a/55757473/12429735
|
||||
ENV USER=appuser
|
||||
ENV UID=10001
|
||||
RUN adduser \
|
||||
--disabled-password \
|
||||
--gecos "" \
|
||||
--shell "/sbin/nologin" \
|
||||
--no-create-home \
|
||||
--uid "${UID}" \
|
||||
"${USER}"
|
||||
|
||||
# Create a new tmp directory so no bad actors can manipulate it
|
||||
RUN mkdir /temporary-tmp-directory && chmod 777 /temporary-tmp-directory
|
||||
|
||||
###############################################################################
|
||||
# Final stage
|
||||
FROM scratch
|
||||
|
||||
# Allow container to use latest TLS certificates
|
||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
|
||||
# Copy over appuser to run as non-root
|
||||
COPY --from=build /etc/passwd /etc/passwd
|
||||
COPY --from=build /etc/group /etc/group
|
||||
|
||||
# Copy over the /tmp directory for golang/os.TmpDir
|
||||
COPY --chown=appuser --from=build /temporary-tmp-directory /tmp
|
||||
|
||||
# Copy application from external build
|
||||
COPY govc /govc
|
||||
|
||||
# Run all commands as non-root
|
||||
USER appuser:appuser
|
||||
|
||||
# session cache, etc
|
||||
ENV GOVMOMI_HOME=/tmp
|
||||
|
||||
# Set CMD to application with container defaults
|
||||
CMD ["/govc"]
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
# Create a builder container
|
||||
# golang:1.18.0-buster amd64
|
||||
FROM golang@sha256:7d39537344486528f8cdb3bd8adb98ab7f0f4236044b6944fed8631da35a4ce5 AS build
|
||||
WORKDIR /go/src/app
|
||||
|
||||
# Create appuser to isolate potential vulnerabilities
|
||||
# See https://stackoverflow.com/a/55757473/12429735
|
||||
ENV USER=appuser
|
||||
ENV UID=10001
|
||||
RUN adduser \
|
||||
--disabled-password \
|
||||
--gecos "" \
|
||||
--home "/nonexistent" \
|
||||
--shell "/sbin/nologin" \
|
||||
--no-create-home \
|
||||
--uid "${UID}" \
|
||||
"${USER}"
|
||||
|
||||
# Create a new tmp directory so no bad actors can manipulate it
|
||||
RUN mkdir /temporary-tmp-directory && chmod 777 /temporary-tmp-directory
|
||||
|
||||
###############################################################################
|
||||
# Final stage
|
||||
FROM scratch
|
||||
|
||||
# Run all commands as non-root
|
||||
USER appuser:appuser
|
||||
|
||||
# Allow container to use latest TLS certificates
|
||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
|
||||
# Copy over appuser to run as non-root
|
||||
COPY --from=build /etc/passwd /etc/passwd
|
||||
COPY --from=build /etc/group /etc/group
|
||||
|
||||
# Copy over the /tmp directory for golang/os.TmpDir
|
||||
COPY --chown=appuser --from=build /temporary-tmp-directory /tmp
|
||||
|
||||
# Expose application port
|
||||
EXPOSE 8989
|
||||
|
||||
# Copy application from external build
|
||||
COPY vcsim /vcsim
|
||||
|
||||
# Set entrypoint to application with container defaults
|
||||
ENTRYPOINT [ "/vcsim" ]
|
||||
CMD ["-l", "0.0.0.0:8989"]
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "improvements"
|
||||
digest = "1:b183578c34fabccaf65f1a57d2efeec2086abdce1446978d69ab3a0016cb750c"
|
||||
name = "github.com/davecgh/go-xdr"
|
||||
packages = ["xdr2"]
|
||||
pruneopts = "NUT"
|
||||
revision = "4930550ba2e22f87187498acfd78348b15f4e7a8"
|
||||
source = "https://github.com/rasky/go-xdr"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:1ab18cf8c2084968d6dca0dd46fbda9efba08664ecd7957b63c7ca57bb2455df"
|
||||
name = "github.com/google/uuid"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "6a5e28554805e78ea6141142aba763936c4761c0"
|
||||
|
||||
[[projects]]
|
||||
branch = "govmomi"
|
||||
digest = "1:f49ed6cb2129e9a3ce9dde5037cb243b5849c0ec0c7973b9d1e987872d8b8cc6"
|
||||
name = "github.com/kr/pretty"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "2ee9d7453c02ef7fa518a83ae23644eb8872186a"
|
||||
source = "https://github.com/dougm/pretty"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:c3a7836b5904db0f8b609595b619916a6831cb35b8b714aec39f96d00c6155d8"
|
||||
name = "github.com/kr/text"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "7cafcd837844e784b526369c9bce262804aebc60"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:4bea31865971675c482ed875caeabe7d2182dcb47d52900b7da5236d66dc9970"
|
||||
name = "github.com/vmware/vmw-guestinfo"
|
||||
packages = [
|
||||
"bdoor",
|
||||
"message",
|
||||
"vmcheck",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "25eff159a728be87e103a0b8045e08273f4dbec4"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
input-imports = [
|
||||
"github.com/davecgh/go-xdr/xdr2",
|
||||
"github.com/google/uuid",
|
||||
"github.com/kr/pretty",
|
||||
"github.com/vmware/vmw-guestinfo/message",
|
||||
"github.com/vmware/vmw-guestinfo/vmcheck",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# Refer to https://github.com/toml-lang/toml for detailed TOML docs.
|
||||
|
||||
[prune]
|
||||
non-go = true
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
|
||||
[[constraint]]
|
||||
branch = "improvements"
|
||||
name = "github.com/davecgh/go-xdr"
|
||||
source = "https://github.com/rasky/go-xdr"
|
||||
|
||||
[[constraint]]
|
||||
branch = "govmomi"
|
||||
name = "github.com/kr/pretty"
|
||||
source = "https://github.com/dougm/pretty"
|
||||
+140
-16
@@ -1,29 +1,153 @@
|
||||
.PHONY: test
|
||||
# Copyright (c) 2021 VMware, Inc. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
all: check test
|
||||
# If you update this file, please follow
|
||||
# https://www.thapaliya.com/en/writings/well-documented-makefiles/
|
||||
|
||||
check: goimports govet
|
||||
# Ensure Make is run with bash shell as some syntax below is bash-specific
|
||||
SHELL := /usr/bin/env bash
|
||||
|
||||
goimports:
|
||||
@echo checking go imports...
|
||||
@command -v goimports >/dev/null 2>&1 || go get golang.org/x/tools/cmd/goimports
|
||||
@! goimports -d . 2>&1 | egrep -v '^$$'
|
||||
# Print the help/usage when make is executed without any other arguments
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
govet:
|
||||
@echo checking go vet...
|
||||
@go tool vet -structtags=false -methods=false $$(find . -mindepth 1 -maxdepth 1 -type d -not -name vendor)
|
||||
|
||||
install:
|
||||
## --------------------------------------
|
||||
## Help
|
||||
## --------------------------------------
|
||||
|
||||
.PHONY: help
|
||||
help: ## Display usage
|
||||
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make [target] \033[36m\033[0m\n\nTargets:\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
|
||||
|
||||
|
||||
## --------------------------------------
|
||||
## Locations and programs
|
||||
## --------------------------------------
|
||||
|
||||
# Directories
|
||||
BIN_DIR := bin
|
||||
TOOLS_DIR := hack/tools
|
||||
TOOLS_BIN_DIR := $(TOOLS_DIR)/bin
|
||||
|
||||
# Tooling binaries
|
||||
GO ?= $(shell command -v go 2>/dev/null)
|
||||
GOLANGCI_LINT := $(TOOLS_BIN_DIR)/golangci-lint
|
||||
|
||||
|
||||
## --------------------------------------
|
||||
## Prerequisites
|
||||
## --------------------------------------
|
||||
|
||||
# Do not proceed unless the go binary is present.
|
||||
ifeq (,$(strip $(GO)))
|
||||
$(error The "go" program cannot be found)
|
||||
endif
|
||||
|
||||
|
||||
## --------------------------------------
|
||||
## Linting and fixing linter errors
|
||||
## --------------------------------------
|
||||
|
||||
.PHONY: lint
|
||||
lint: ## Run all the lint targets
|
||||
$(MAKE) lint-go-full
|
||||
|
||||
GOLANGCI_LINT_FLAGS ?= --fast=true
|
||||
.PHONY: lint-go
|
||||
lint-go: $(GOLANGCI_LINT) ## Lint codebase
|
||||
$(GOLANGCI_LINT) run -v $(GOLANGCI_LINT_FLAGS)
|
||||
|
||||
.PHONY: lint-go-full
|
||||
lint-go-full: GOLANGCI_LINT_FLAGS = --fast=false --max-same-issues=200
|
||||
lint-go-full: lint-go ## Run slower linters to detect possible issues
|
||||
|
||||
.PHONY: fix
|
||||
fix: GOLANGCI_LINT_FLAGS = --fast=false --fix
|
||||
fix: lint-go ## Tries to fix errors reported by lint-go-full target
|
||||
|
||||
.PHONY: check
|
||||
check: lint-go-full
|
||||
check: ## Run linters
|
||||
|
||||
|
||||
## --------------------------------------
|
||||
## Tooling Binaries
|
||||
## --------------------------------------
|
||||
|
||||
TOOLING_BINARIES := $(GOLANGCI_LINT)
|
||||
tools: $(TOOLING_BINARIES) ## Build tooling binaries
|
||||
.PHONY: $(TOOLING_BINARIES)
|
||||
$(TOOLING_BINARIES):
|
||||
cd $(TOOLS_DIR); make $(@F)
|
||||
|
||||
|
||||
## --------------------------------------
|
||||
## Build / Install
|
||||
## --------------------------------------
|
||||
.PHONY: install
|
||||
install: ## Install govc and vcsim
|
||||
$(MAKE) -C govc install
|
||||
$(MAKE) -C vcsim install
|
||||
|
||||
go-test:
|
||||
GORACE=history_size=5 go test -timeout 5m -count 1 -race -v $(TEST_OPTS) ./...
|
||||
|
||||
govc-test: install
|
||||
(cd govc/test && ./vendor/github.com/sstephenson/bats/libexec/bats -t .)
|
||||
## --------------------------------------
|
||||
## Generate
|
||||
## --------------------------------------
|
||||
|
||||
test: go-test govc-test
|
||||
.PHONY: mod
|
||||
mod: ## Runs go mod tidy to validate modules
|
||||
go mod tidy -v
|
||||
|
||||
.PHONY: mod-get
|
||||
mod-get: ## Downloads and caches the modules
|
||||
go mod download
|
||||
|
||||
.PHONY: doc
|
||||
doc: install
|
||||
doc: ## Generates govc USAGE.md
|
||||
./govc/usage.sh > ./govc/USAGE.md
|
||||
|
||||
.PHONY: generate-types
|
||||
generate-types: ## Generate the types
|
||||
$(MAKE) -C ./gen/ $@
|
||||
|
||||
|
||||
## --------------------------------------
|
||||
## Tests
|
||||
## --------------------------------------
|
||||
|
||||
# Test options
|
||||
TEST_COUNT ?= 1
|
||||
TEST_TIMEOUT ?= 5m
|
||||
TEST_RACE_HISTORY_SIZE ?= 5
|
||||
GORACE ?= history_size=$(TEST_RACE_HISTORY_SIZE)
|
||||
|
||||
ifeq (-count,$(findstring -count,$(TEST_OPTS)))
|
||||
$(error Use TEST_COUNT to override this option)
|
||||
endif
|
||||
|
||||
ifeq (-race,$(findstring -race,$(TEST_OPTS)))
|
||||
$(error The -race flag is enabled by default & cannot be specified in TEST_OPTS)
|
||||
endif
|
||||
|
||||
ifeq (-timeout,$(findstring -timeout,$(TEST_OPTS)))
|
||||
$(error Use TEST_TIMEOUT to override this option)
|
||||
endif
|
||||
|
||||
.PHONY: go-test
|
||||
go-test: ## Runs go unit tests with race detector enabled
|
||||
GORACE=$(GORACE) CGO_ENABLED=1 $(GO) test \
|
||||
-count $(TEST_COUNT) \
|
||||
-race \
|
||||
-timeout $(TEST_TIMEOUT) \
|
||||
-v $(TEST_OPTS) \
|
||||
./...
|
||||
|
||||
.PHONY: govc-test
|
||||
govc-test: install
|
||||
govc-test: ## Runs govc bats tests
|
||||
./govc/test/images/update.sh
|
||||
(cd govc/test && ./vendor/github.com/bats-core/bats-core/bin/bats -t .)
|
||||
|
||||
.PHONY: test
|
||||
test: go-test govc-test ## Runs go-test and govc-test
|
||||
|
||||
+96
-63
@@ -1,94 +1,127 @@
|
||||
[](https://travis-ci.org/vmware/govmomi)
|
||||
[](https://goreportcard.com/report/github.com/vmware/govmomi)
|
||||
<!-- markdownlint-disable first-line-h1 no-inline-html -->
|
||||
|
||||
[][ci-build]
|
||||
[][ci-tests]
|
||||
[][go-report-card]
|
||||
[][latest-release]
|
||||
[][go-reference]
|
||||
[][go-version]
|
||||
|
||||
# govmomi
|
||||
|
||||
A Go library for interacting with VMware vSphere APIs (ESXi and/or vCenter).
|
||||
A Go library for interacting with VMware vSphere APIs (ESXi and/or vCenter Server).
|
||||
|
||||
In addition to the vSphere API client, this repository includes:
|
||||
|
||||
* [govc](./govc) - vSphere CLI
|
||||
|
||||
* [vcsim](./vcsim) - vSphere API mock framework
|
||||
|
||||
* [toolbox](./toolbox) - VM guest tools framework
|
||||
* [govc][govc] - vSphere CLI
|
||||
* [vcsim][vcsim] - vSphere API mock framework
|
||||
* [toolbox][toolbox] - VM guest tools framework
|
||||
|
||||
## Compatibility
|
||||
|
||||
This library is built for and tested against ESXi and vCenter 6.0, 6.5 and 6.7.
|
||||
This library supports vCenter Server and ESXi versions following the [VMware Product Lifecycle Matrix][reference-lifecycle].
|
||||
|
||||
It may work with versions 5.5 and 5.1, but neither are officially supported.
|
||||
Product versions that are end of support may work, but are not officially supported.
|
||||
|
||||
## Documentation
|
||||
|
||||
The APIs exposed by this library very closely follow the API described in the [VMware vSphere API Reference Documentation][apiref].
|
||||
Refer to this document to become familiar with the upstream API.
|
||||
The APIs exposed by this library closely follow the API described in the [VMware vSphere API Reference Documentation][reference-api]. Refer to the documentation to become familiar with the upstream API.
|
||||
|
||||
The code in the `govmomi` package is a wrapper for the code that is generated from the vSphere API description.
|
||||
It primarily provides convenience functions for working with the vSphere API.
|
||||
See [godoc.org][godoc] for documentation.
|
||||
|
||||
[apiref]:http://pubs.vmware.com/vsphere-6-5/index.jsp#com.vmware.wssdk.apiref.doc/right-pane.html
|
||||
[godoc]:http://godoc.org/github.com/vmware/govmomi
|
||||
The code in the `govmomi` package is a wrapper for the code that is generated from the vSphere API description. It primarily provides convenience functions for working with the vSphere API. See [godoc.org][reference-godoc] for documentation.
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
go get -u github.com/vmware/govmomi
|
||||
```
|
||||
### Binaries and Docker Images for `govc` and `vcsim`
|
||||
|
||||
Installation instructions, released binaries, and Docker images are documented in the respective README files of [`govc`][govc] and [`vcsim`][vcsim].
|
||||
|
||||
## Discussion
|
||||
|
||||
Contributors and users are encouraged to collaborate using GitHub issues and/or
|
||||
[Slack](https://vmwarecode.slack.com/messages/govmomi).
|
||||
Access to Slack requires a [VMware {code} membership](https://code.vmware.com/join/).
|
||||
The project encourages the community to collaborate using GitHub [issues][govmomi-github-issues], GitHub [discussions][govmomi-github-discussions], and [Slack][slack-channel].
|
||||
|
||||
> **Note**
|
||||
> Access to Slack requires a free [VMware {code}][slack-join] developer program membership.
|
||||
|
||||
## Status
|
||||
|
||||
Changes to the API are subject to [semantic versioning](http://semver.org).
|
||||
Changes to the API are subject to [semantic versioning][reference-semver].
|
||||
|
||||
Refer to the [CHANGELOG](CHANGELOG.md) for version to version changes.
|
||||
Refer to the [CHANGELOG][govmomi-changelog] for version to version changes.
|
||||
|
||||
## Projects using govmomi
|
||||
## Notable Projects Using govmomi
|
||||
|
||||
* [Docker Machine](https://github.com/docker/machine/tree/master/drivers/vmwarevsphere)
|
||||
* [collectd-vsphere][project-travisci-collectd-vsphere]
|
||||
* [Docker LinuxKit][project-docker-linuxKit]
|
||||
* [Elastic Agent VMware vSphere integration][project-elastic-agent]
|
||||
* [Gru][project-gru]
|
||||
* [Juju][project-juju]
|
||||
* [Jupiter Brain][project-travisci-jupiter-brain]
|
||||
* [Kubernetes vSphere Cloud Provider][project-k8s-cloud-provider]
|
||||
* [Kubernetes Cluster API][project-k8s-cluster-api]
|
||||
* [OPS][project-nanovms-ops]
|
||||
* [OpenTelemetry Collector Contrib][opentelemetry-collector-contrib]
|
||||
* [Packer Plugin for VMware vSphere][project-hashicorp-packer-plugin-vsphere]
|
||||
* [Rancher][project-rancher]
|
||||
* [Terraform Provider for VMware vSphere][project-hashicorp-terraform-provider-vsphere]
|
||||
* [Telegraf][project-influxdata-telegraf]
|
||||
* [VMware Event Broker Appliance][project-vmware-veba]
|
||||
* [VMware vSphere Integrated Containers Engine][project-vmware-vic]
|
||||
* [VMware vSphere 7.0][project-vmware-vsphere]
|
||||
|
||||
* [Docker InfraKit](https://github.com/docker/infrakit/tree/master/pkg/provider/vsphere)
|
||||
## Related Projects
|
||||
|
||||
* [Docker LinuxKit](https://github.com/linuxkit/linuxkit/tree/master/src/cmd/linuxkit)
|
||||
|
||||
* [Kubernetes](https://github.com/kubernetes/kubernetes/tree/master/pkg/cloudprovider/providers/vsphere)
|
||||
|
||||
* [Kubernetes Cloud Provider](https://github.com/kubernetes/cloud-provider-vsphere)
|
||||
|
||||
* [Kubernetes Cluster API](https://github.com/kubernetes-sigs/cluster-api-provider-vsphere)
|
||||
|
||||
* [Kubernetes kops](https://github.com/kubernetes/kops/tree/master/upup/pkg/fi/cloudup/vsphere)
|
||||
|
||||
* [Terraform](https://github.com/terraform-providers/terraform-provider-vsphere)
|
||||
|
||||
* [Packer](https://github.com/jetbrains-infra/packer-builder-vsphere)
|
||||
|
||||
* [VMware VIC Engine](https://github.com/vmware/vic)
|
||||
|
||||
* [Travis CI](https://github.com/travis-ci/jupiter-brain)
|
||||
|
||||
* [collectd-vsphere](https://github.com/travis-ci/collectd-vsphere)
|
||||
|
||||
* [Gru](https://github.com/dnaeon/gru)
|
||||
|
||||
* [Libretto](https://github.com/apcera/libretto/tree/master/virtualmachine/vsphere)
|
||||
|
||||
* [Telegraf](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/vsphere)
|
||||
|
||||
* [Open Storage](https://github.com/libopenstorage/openstorage/tree/master/pkg/storageops/vsphere)
|
||||
|
||||
## Related projects
|
||||
|
||||
* [rbvmomi](https://github.com/vmware/rbvmomi)
|
||||
|
||||
* [pyvmomi](https://github.com/vmware/pyvmomi)
|
||||
* [go-vmware-nsxt][reference-go-vmware-nsxt]
|
||||
* [pyvmomi][reference-pyvmomi]
|
||||
* [rbvmomi][reference-rbvmomi]
|
||||
|
||||
## License
|
||||
|
||||
govmomi is available under the [Apache 2 license](LICENSE.txt).
|
||||
govmomi is available under the [Apache 2 License][govmomi-license].
|
||||
|
||||
## Name
|
||||
|
||||
Pronounced: _go·v·mom·ie_
|
||||
|
||||
Follows pyvmomi and rbvmomi: language prefix + the vSphere acronym "VM Object Management Infrastructure".
|
||||
|
||||
[//]: Links
|
||||
|
||||
[ci-build]: https://github.com/vmware/govmomi/actions/workflows/govmomi-build.yaml
|
||||
[ci-tests]: https://github.com/vmware/govmomi/actions/workflows/govmomi-go-tests.yaml
|
||||
[latest-release]: https://github.com/vmware/govmomi/releases/latest
|
||||
[govc]: govc/README.md
|
||||
[govmomi-github-issues]: https://github.com/vmware/govmomi/issues
|
||||
[govmomi-github-discussions]: https://github.com/vmware/govmomi/discussions
|
||||
[govmomi-changelog]: CHANGELOG.md
|
||||
[govmomi-license]: LICENSE.txt
|
||||
[go-reference]: https://pkg.go.dev/github.com/vmware/govmomi
|
||||
[go-report-card]: https://goreportcard.com/report/github.com/vmware/govmomi
|
||||
[go-version]: https://github.com/vmware/govmomi
|
||||
[opentelemetry-collector-contrib]: https://github.com/open-telemetry/opentelemetry-collector-contrib
|
||||
[project-docker-linuxKit]: https://github.com/linuxkit/linuxkit/tree/master/src/cmd/linuxkit
|
||||
[project-elastic-agent]: https://github.com/elastic/integrations/tree/main/packages/vsphere
|
||||
[project-gru]: https://github.com/dnaeon/gru
|
||||
[project-hashicorp-packer-plugin-vsphere]: https://github.com/hashicorp/packer-plugin-vsphere
|
||||
[project-hashicorp-terraform-provider-vsphere]: https://github.com/hashicorp/terraform-provider-vsphere
|
||||
[project-influxdata-telegraf]: https://github.com/influxdata/telegraf/tree/master/plugins/inputs/vsphere
|
||||
[project-juju]: https://github.com/juju/juju
|
||||
[project-k8s-cloud-provider]: https://github.com/kubernetes/cloud-provider-vsphere
|
||||
[project-k8s-cluster-api]: https://github.com/kubernetes-sigs/cluster-api-provider-vsphere
|
||||
[project-nanovms-ops]: https://github.com/nanovms/ops
|
||||
[project-rancher]: https://github.com/rancher/rancher/blob/master/pkg/api/norman/customization/vsphere/listers.go
|
||||
[project-travisci-collectd-vsphere]: https://github.com/travis-ci/collectd-vsphere
|
||||
[project-travisci-jupiter-brain]: https://github.com/travis-ci/jupiter-brain
|
||||
[project-vmware-veba]: https://github.com/vmware-samples/vcenter-event-broker-appliance/tree/development/vmware-event-router
|
||||
[project-vmware-vic]: https://github.com/vmware/vic
|
||||
[project-vmware-vsphere]: https://docs.vmware.com/en/VMware-vSphere/7.0/rn/vsphere-esxi-vcenter-server-7-vsphere-with-kubernetes-release-notes.html
|
||||
[reference-api]: https://developer.vmware.com/apis/968/vsphere
|
||||
[reference-godoc]: http://godoc.org/github.com/vmware/govmomi
|
||||
[reference-go-vmware-nsxt]: https://github.com/vmware/go-vmware-nsxt
|
||||
[reference-lifecycle]: https://lifecycle.vmware.com
|
||||
[reference-pyvmomi]: https://github.com/vmware/pyvmomi
|
||||
[reference-rbvmomi]: https://github.com/vmware/rbvmomi
|
||||
[reference-semver]: http://semver.org
|
||||
[slack-join]: https://developer.vmware.com/join/
|
||||
[slack-channel]: https://vmwarecode.slack.com/messages/govmomi
|
||||
[toolbox]: toolbox/README.md
|
||||
[vcsim]: vcsim/README.md
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
# How to create a `govmomi` Release on Github
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> The steps outlined in this document can only be performed by maintainers or
|
||||
> administrators of this project.
|
||||
|
||||
The release automation is based on Github
|
||||
[Actions](https://github.com/features/actions) and has been improved over time
|
||||
to simplify the experience for creating `govmomi` releases.
|
||||
|
||||
The Github Actions release [workflow](.github/workflows/govmomi-release.yaml)
|
||||
uses [`goreleaser`](http://goreleaser.com/) and automatically creates/pushes:
|
||||
|
||||
- Release artifacts for `govc` and `vcsim` to the
|
||||
[release](https://github.com/vmware/govmomi/releases) page, including
|
||||
`LICENSE.txt`, `README` and `CHANGELOG`
|
||||
- Docker images for `vmware/govc` and `vmware/vcsim` to Docker Hub
|
||||
- Source code
|
||||
|
||||
Releases are not tagged on the `main` branch, but a dedicated release branch, for example `release-0.35`.
|
||||
|
||||
### Verify `main` branch is up to date with the remote
|
||||
|
||||
```console
|
||||
git checkout main
|
||||
git fetch -avp
|
||||
git diff main origin/main
|
||||
|
||||
# if your local and remote branches diverge run
|
||||
git pull origin/main
|
||||
```
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> These steps assume `origin` to point to the remote
|
||||
> `https://github.com/vmware/govmomi`, respectively
|
||||
> `git@github.com:vmware/govmomi`.
|
||||
|
||||
### Create a release branch
|
||||
|
||||
For new releases, create a release branch from the most recent commit in
|
||||
`main`, e.g. `release-0.35`.
|
||||
|
||||
```console
|
||||
export RELEASE_BRANCH=release-0.35
|
||||
git checkout -b ${RELEASE_BRANCH}
|
||||
```
|
||||
|
||||
For maintenance/patch releases on **existing** release branches, simply checkout the existing
|
||||
release branch and add commits to the existing release branch.
|
||||
|
||||
### Verify `make docs` and `CONTRIBUTORS` are up to date
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> Run the following commands and commit any changes to the release branch before
|
||||
> proceeding with the release.
|
||||
|
||||
```console
|
||||
make doc
|
||||
./scripts/contributors.sh
|
||||
if [ -z "$(git status --porcelain)" ]; then
|
||||
echo "working directory clean: proceed with release"
|
||||
else
|
||||
echo "working directory dirty: please commit changes"
|
||||
fi
|
||||
|
||||
# perform git add && git commit ... in case there were changes
|
||||
```
|
||||
|
||||
### Push the release branch
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> Do not create a tag as this will be done by the release automation.
|
||||
|
||||
The final step is pushing the new/updated release branch.
|
||||
|
||||
```console
|
||||
git push origin ${RELEASE_BRANCH}
|
||||
```
|
||||
|
||||
### Create a release in the Github UI
|
||||
|
||||
Open the `govmomi` Github [repository](https://github.com/vmware/govmomi) and
|
||||
navigate to `Actions -> Workflows -> Release`.
|
||||
|
||||
Click `Run Workflow` which opens a dropdown list.
|
||||
|
||||
Select the new/updated branch, e.g. `release-0.35`, i.e. **not** the `main`
|
||||
branch.
|
||||
|
||||
Specify a semantic `tag` to associate with the release, e.g. `v0.35.0`.
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> This tag **must not** exist or the release will fail during the validation
|
||||
> phase.
|
||||
|
||||
By default, a dry-run is performed to rule out most (but not all) errors during
|
||||
a release. If you do not want to perform a dry-run, e.g. to finally create a
|
||||
release, deselect the `Verify release workflow ...` checkbox.
|
||||
|
||||
Click `Run Workflow` to kick off the workflow.
|
||||
|
||||
After successful completion and if the newly created `tag` is the **latest**
|
||||
(semantic version sorted) tag in the repository, a PR is automatically opened
|
||||
against the `main` branch to update the `CHANGELOG`.
|
||||
+7
-7
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
Copyright (c) 2014-2016 VMware, Inc. All Rights Reserved.
|
||||
Copyright (c) 2014-2023 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -19,7 +19,7 @@ This package is the root package of the govmomi library.
|
||||
|
||||
The library is structured as follows:
|
||||
|
||||
Package vim25
|
||||
# Package vim25
|
||||
|
||||
The minimal usable functionality is available through the vim25 package.
|
||||
It contains subpackages that contain generated types, managed objects, and all
|
||||
@@ -30,25 +30,25 @@ The vim25 package itself contains a client structure that is
|
||||
passed around throughout the entire library. It abstracts a session and its
|
||||
immutable state. See the vim25 package for more information.
|
||||
|
||||
Package session
|
||||
# Package session
|
||||
|
||||
The session package contains an abstraction for the session manager that allows
|
||||
a user to login and logout. It also provides access to the current session
|
||||
(i.e. to determine if the user is in fact logged in)
|
||||
|
||||
Package object
|
||||
# Package object
|
||||
|
||||
The object package contains wrappers for a selection of managed objects. The
|
||||
constructors of these objects all take a *vim25.Client, which they pass along
|
||||
to derived objects, if applicable.
|
||||
|
||||
Package govc
|
||||
# Package govc
|
||||
|
||||
The govc package contains the govc CLI. The code in this tree is not intended
|
||||
to be used as a library. Any functionality that govc contains that _could_ be
|
||||
used as a library function but isn't, _should_ live in a root level package.
|
||||
|
||||
Other packages
|
||||
# Other packages
|
||||
|
||||
Other packages, such as "event", "guest", or "license", provide wrappers for
|
||||
the respective subsystems. They are typically not needed in normal workflows so
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache 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://www.apache.org/licenses/LICENSE-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.
|
||||
*/
|
||||
|
||||
package history
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/vmware/govmomi/property"
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type Collector struct {
|
||||
r types.ManagedObjectReference
|
||||
c *vim25.Client
|
||||
}
|
||||
|
||||
func NewCollector(c *vim25.Client, ref types.ManagedObjectReference) *Collector {
|
||||
return &Collector{
|
||||
r: ref,
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
// Reference returns the managed object reference of this collector
|
||||
func (c Collector) Reference() types.ManagedObjectReference {
|
||||
return c.r
|
||||
}
|
||||
|
||||
// Client returns the vim25 client used by this collector
|
||||
func (c Collector) Client() *vim25.Client {
|
||||
return c.c
|
||||
}
|
||||
|
||||
// Properties wraps property.DefaultCollector().RetrieveOne() and returns
|
||||
// properties for the specified managed object reference
|
||||
func (c Collector) Properties(ctx context.Context, r types.ManagedObjectReference, ps []string, dst interface{}) error {
|
||||
return property.DefaultCollector(c.c).RetrieveOne(ctx, r, ps, dst)
|
||||
}
|
||||
|
||||
func (c Collector) Destroy(ctx context.Context) error {
|
||||
req := types.DestroyCollector{
|
||||
This: c.r,
|
||||
}
|
||||
|
||||
_, err := methods.DestroyCollector(ctx, c.c, &req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c Collector) Reset(ctx context.Context) error {
|
||||
req := types.ResetCollector{
|
||||
This: c.r,
|
||||
}
|
||||
|
||||
_, err := methods.ResetCollector(ctx, c.c, &req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c Collector) Rewind(ctx context.Context) error {
|
||||
req := types.RewindCollector{
|
||||
This: c.r,
|
||||
}
|
||||
|
||||
_, err := methods.RewindCollector(ctx, c.c, &req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c Collector) SetPageSize(ctx context.Context, maxCount int32) error {
|
||||
req := types.SetCollectorPageSize{
|
||||
This: c.r,
|
||||
MaxCount: maxCount,
|
||||
}
|
||||
|
||||
_, err := methods.SetCollectorPageSize(ctx, c.c, &req)
|
||||
return err
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
Copyright (c) 2020-2023 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache 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://www.apache.org/licenses/LICENSE-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.
|
||||
*/
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
const (
|
||||
vCenterHostGatewaySocket = "/var/run/envoy-hgw/hgw-pipe"
|
||||
vCenterHostGatewaySocketEnv = "VCENTER_ENVOY_HOST_GATEWAY"
|
||||
)
|
||||
|
||||
// InventoryPath composed of entities by Name
|
||||
func InventoryPath(entities []mo.ManagedEntity) string {
|
||||
val := "/"
|
||||
|
||||
for _, entity := range entities {
|
||||
// Skip root folder in building inventory path.
|
||||
if entity.Parent == nil {
|
||||
continue
|
||||
}
|
||||
val = path.Join(val, entity.Name)
|
||||
}
|
||||
|
||||
return val
|
||||
}
|
||||
|
||||
func HostSystemManagementIPs(config []types.VirtualNicManagerNetConfig) []net.IP {
|
||||
var ips []net.IP
|
||||
|
||||
for _, nc := range config {
|
||||
if nc.NicType != string(types.HostVirtualNicManagerNicTypeManagement) {
|
||||
continue
|
||||
}
|
||||
for ix := range nc.CandidateVnic {
|
||||
for _, selectedVnicKey := range nc.SelectedVnic {
|
||||
if nc.CandidateVnic[ix].Key != selectedVnicKey {
|
||||
continue
|
||||
}
|
||||
ip := net.ParseIP(nc.CandidateVnic[ix].Spec.Ip.IpAddress)
|
||||
if ip != nil {
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ips
|
||||
}
|
||||
|
||||
// UsingEnvoySidecar determines if the given *vim25.Client is using vCenter's
|
||||
// local Envoy sidecar (as opposed to using the HTTPS port.)
|
||||
// Returns a boolean indicating whether to use the sidecar or not.
|
||||
func UsingEnvoySidecar(c *vim25.Client) bool {
|
||||
envoySidecarPort := os.Getenv("GOVMOMI_ENVOY_SIDECAR_PORT")
|
||||
if envoySidecarPort == "" {
|
||||
envoySidecarPort = "1080"
|
||||
}
|
||||
envoySidecarHost := os.Getenv("GOVMOMI_ENVOY_SIDECAR_HOST")
|
||||
if envoySidecarHost == "" {
|
||||
envoySidecarHost = "localhost"
|
||||
}
|
||||
return c.URL().Hostname() == envoySidecarHost && c.URL().Scheme == "http" && c.URL().Port() == envoySidecarPort
|
||||
}
|
||||
|
||||
// ClientWithEnvoyHostGateway clones the provided soap.Client and returns a new
|
||||
// one that uses a Unix socket to leverage vCenter's local Envoy host
|
||||
// gateway.
|
||||
// This should be used to construct clients that talk to ESX.
|
||||
// This method returns a new *vim25.Client and does not modify the original input.
|
||||
// This client disables HTTP keep alives and is intended for a single round
|
||||
// trip. (eg. guest file transfer, datastore file transfer)
|
||||
func ClientWithEnvoyHostGateway(vc *vim25.Client) *vim25.Client {
|
||||
// Override the vim client with a new one that wraps a Unix socket transport.
|
||||
// Using HTTP here so secure means nothing.
|
||||
sc := soap.NewClient(vc.URL(), true)
|
||||
// Clone the underlying HTTP transport, only replacing the dialer logic.
|
||||
transport := sc.DefaultTransport().Clone()
|
||||
hostGatewaySocketPath := os.Getenv(vCenterHostGatewaySocketEnv)
|
||||
if hostGatewaySocketPath == "" {
|
||||
hostGatewaySocketPath = vCenterHostGatewaySocket
|
||||
}
|
||||
transport.DialContext = func(_ context.Context, _, _ string) (net.Conn, error) {
|
||||
return net.Dial("unix", hostGatewaySocketPath)
|
||||
}
|
||||
// We use this client for a single request, so we don't require keepalives.
|
||||
transport.DisableKeepAlives = true
|
||||
sc.Client = http.Client{
|
||||
Transport: transport,
|
||||
}
|
||||
newVC := &vim25.Client{
|
||||
Client: sc,
|
||||
}
|
||||
return newVC
|
||||
}
|
||||
|
||||
// HostGatewayTransferURL rewrites the provided URL to be suitable for use
|
||||
// with the Envoy host gateway on vCenter.
|
||||
// It returns a copy of the provided URL with the host, scheme rewritten as needed.
|
||||
// Receivers of such URLs must typically also use ClientWithEnvoyHostGateway to
|
||||
// use the appropriate http.Transport to be able to make use of the host
|
||||
// gateway.
|
||||
// nil input yields an uninitialized struct.
|
||||
func HostGatewayTransferURL(u *url.URL, hostMoref types.ManagedObjectReference) *url.URL {
|
||||
if u == nil {
|
||||
return &url.URL{}
|
||||
}
|
||||
// Make a copy of the provided URL.
|
||||
turl := *u
|
||||
turl.Host = "localhost"
|
||||
turl.Scheme = "http"
|
||||
oldPath := turl.Path
|
||||
turl.Path = fmt.Sprintf("/hgw/%s%s", hostMoref.Value, oldPath)
|
||||
return &turl
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache 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://www.apache.org/licenses/LICENSE-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.
|
||||
*/
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
)
|
||||
|
||||
type RetrieveDynamicTypeManagerBody struct {
|
||||
Req *RetrieveDynamicTypeManagerRequest `xml:"urn:vim25 RetrieveDynamicTypeManager"`
|
||||
Res *RetrieveDynamicTypeManagerResponse `xml:"urn:vim25 RetrieveDynamicTypeManagerResponse"`
|
||||
Fault_ *soap.Fault
|
||||
}
|
||||
|
||||
func (b *RetrieveDynamicTypeManagerBody) Fault() *soap.Fault { return b.Fault_ }
|
||||
|
||||
func RetrieveDynamicTypeManager(ctx context.Context, r soap.RoundTripper, req *RetrieveDynamicTypeManagerRequest) (*RetrieveDynamicTypeManagerResponse, error) {
|
||||
var reqBody, resBody RetrieveDynamicTypeManagerBody
|
||||
|
||||
reqBody.Req = req
|
||||
|
||||
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resBody.Res, nil
|
||||
}
|
||||
|
||||
type RetrieveManagedMethodExecuterBody struct {
|
||||
Req *RetrieveManagedMethodExecuterRequest `xml:"urn:vim25 RetrieveManagedMethodExecuter"`
|
||||
Res *RetrieveManagedMethodExecuterResponse `xml:"urn:vim25 RetrieveManagedMethodExecuterResponse"`
|
||||
Fault_ *soap.Fault
|
||||
}
|
||||
|
||||
func (b *RetrieveManagedMethodExecuterBody) Fault() *soap.Fault { return b.Fault_ }
|
||||
|
||||
func RetrieveManagedMethodExecuter(ctx context.Context, r soap.RoundTripper, req *RetrieveManagedMethodExecuterRequest) (*RetrieveManagedMethodExecuterResponse, error) {
|
||||
var reqBody, resBody RetrieveManagedMethodExecuterBody
|
||||
|
||||
reqBody.Req = req
|
||||
|
||||
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resBody.Res, nil
|
||||
}
|
||||
|
||||
type DynamicTypeMgrQueryMoInstancesBody struct {
|
||||
Req *DynamicTypeMgrQueryMoInstancesRequest `xml:"urn:vim25 DynamicTypeMgrQueryMoInstances"`
|
||||
Res *DynamicTypeMgrQueryMoInstancesResponse `xml:"urn:vim25 DynamicTypeMgrQueryMoInstancesResponse"`
|
||||
Fault_ *soap.Fault
|
||||
}
|
||||
|
||||
func (b *DynamicTypeMgrQueryMoInstancesBody) Fault() *soap.Fault { return b.Fault_ }
|
||||
|
||||
func DynamicTypeMgrQueryMoInstances(ctx context.Context, r soap.RoundTripper, req *DynamicTypeMgrQueryMoInstancesRequest) (*DynamicTypeMgrQueryMoInstancesResponse, error) {
|
||||
var reqBody, resBody DynamicTypeMgrQueryMoInstancesBody
|
||||
|
||||
reqBody.Req = req
|
||||
|
||||
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resBody.Res, nil
|
||||
}
|
||||
|
||||
type DynamicTypeMgrQueryTypeInfoBody struct {
|
||||
Req *DynamicTypeMgrQueryTypeInfoRequest `xml:"urn:vim25 DynamicTypeMgrQueryTypeInfo"`
|
||||
Res *DynamicTypeMgrQueryTypeInfoResponse `xml:"urn:vim25 DynamicTypeMgrQueryTypeInfoResponse"`
|
||||
Fault_ *soap.Fault
|
||||
}
|
||||
|
||||
func (b *DynamicTypeMgrQueryTypeInfoBody) Fault() *soap.Fault { return b.Fault_ }
|
||||
|
||||
func DynamicTypeMgrQueryTypeInfo(ctx context.Context, r soap.RoundTripper, req *DynamicTypeMgrQueryTypeInfoRequest) (*DynamicTypeMgrQueryTypeInfoResponse, error) {
|
||||
var reqBody, resBody DynamicTypeMgrQueryTypeInfoBody
|
||||
|
||||
reqBody.Req = req
|
||||
|
||||
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resBody.Res, nil
|
||||
}
|
||||
|
||||
type ExecuteSoapBody struct {
|
||||
Req *ExecuteSoapRequest `xml:"urn:vim25 ExecuteSoap"`
|
||||
Res *ExecuteSoapResponse `xml:"urn:vim25 ExecuteSoapResponse"`
|
||||
Fault_ *soap.Fault
|
||||
}
|
||||
|
||||
func (b *ExecuteSoapBody) Fault() *soap.Fault { return b.Fault_ }
|
||||
|
||||
func ExecuteSoap(ctx context.Context, r soap.RoundTripper, req *ExecuteSoapRequest) (*ExecuteSoapResponse, error) {
|
||||
var reqBody, resBody ExecuteSoapBody
|
||||
|
||||
reqBody.Req = req
|
||||
|
||||
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resBody.Res, nil
|
||||
}
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
Copyright (c) 2014 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache 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://www.apache.org/licenses/LICENSE-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.
|
||||
*/
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type DynamicTypeMgrQueryMoInstancesRequest struct {
|
||||
This types.ManagedObjectReference `xml:"_this"`
|
||||
FilterSpec BaseDynamicTypeMgrFilterSpec `xml:"filterSpec,omitempty,typeattr"`
|
||||
}
|
||||
|
||||
type DynamicTypeMgrQueryMoInstancesResponse struct {
|
||||
Returnval []DynamicTypeMgrMoInstance `xml:"urn:vim25 returnval"`
|
||||
}
|
||||
|
||||
type DynamicTypeEnumTypeInfo struct {
|
||||
types.DynamicData
|
||||
|
||||
Name string `xml:"name"`
|
||||
WsdlName string `xml:"wsdlName"`
|
||||
Version string `xml:"version"`
|
||||
Value []string `xml:"value,omitempty"`
|
||||
Annotation []DynamicTypeMgrAnnotation `xml:"annotation,omitempty"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
types.Add("DynamicTypeEnumTypeInfo", reflect.TypeOf((*DynamicTypeEnumTypeInfo)(nil)).Elem())
|
||||
}
|
||||
|
||||
type DynamicTypeMgrAllTypeInfoRequest struct {
|
||||
types.DynamicData
|
||||
|
||||
ManagedTypeInfo []DynamicTypeMgrManagedTypeInfo `xml:"managedTypeInfo,omitempty"`
|
||||
EnumTypeInfo []DynamicTypeEnumTypeInfo `xml:"enumTypeInfo,omitempty"`
|
||||
DataTypeInfo []DynamicTypeMgrDataTypeInfo `xml:"dataTypeInfo,omitempty"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
types.Add("DynamicTypeMgrAllTypeInfo", reflect.TypeOf((*DynamicTypeMgrAllTypeInfoRequest)(nil)).Elem())
|
||||
}
|
||||
|
||||
type DynamicTypeMgrAnnotation struct {
|
||||
types.DynamicData
|
||||
|
||||
Name string `xml:"name"`
|
||||
Parameter []string `xml:"parameter,omitempty"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
types.Add("DynamicTypeMgrAnnotation", reflect.TypeOf((*DynamicTypeMgrAnnotation)(nil)).Elem())
|
||||
}
|
||||
|
||||
type DynamicTypeMgrDataTypeInfo struct {
|
||||
types.DynamicData
|
||||
|
||||
Name string `xml:"name"`
|
||||
WsdlName string `xml:"wsdlName"`
|
||||
Version string `xml:"version"`
|
||||
Base []string `xml:"base,omitempty"`
|
||||
Property []DynamicTypeMgrPropertyTypeInfo `xml:"property,omitempty"`
|
||||
Annotation []DynamicTypeMgrAnnotation `xml:"annotation,omitempty"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
types.Add("DynamicTypeMgrDataTypeInfo", reflect.TypeOf((*DynamicTypeMgrDataTypeInfo)(nil)).Elem())
|
||||
}
|
||||
|
||||
func (b *DynamicTypeMgrFilterSpec) GetDynamicTypeMgrFilterSpec() *DynamicTypeMgrFilterSpec { return b }
|
||||
|
||||
type BaseDynamicTypeMgrFilterSpec interface {
|
||||
GetDynamicTypeMgrFilterSpec() *DynamicTypeMgrFilterSpec
|
||||
}
|
||||
|
||||
type DynamicTypeMgrFilterSpec struct {
|
||||
types.DynamicData
|
||||
}
|
||||
|
||||
func init() {
|
||||
types.Add("DynamicTypeMgrFilterSpec", reflect.TypeOf((*DynamicTypeMgrFilterSpec)(nil)).Elem())
|
||||
}
|
||||
|
||||
type DynamicTypeMgrManagedTypeInfo struct {
|
||||
types.DynamicData
|
||||
|
||||
Name string `xml:"name"`
|
||||
WsdlName string `xml:"wsdlName"`
|
||||
Version string `xml:"version"`
|
||||
Base []string `xml:"base,omitempty"`
|
||||
Property []DynamicTypeMgrPropertyTypeInfo `xml:"property,omitempty"`
|
||||
Method []DynamicTypeMgrMethodTypeInfo `xml:"method,omitempty"`
|
||||
Annotation []DynamicTypeMgrAnnotation `xml:"annotation,omitempty"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
types.Add("DynamicTypeMgrManagedTypeInfo", reflect.TypeOf((*DynamicTypeMgrManagedTypeInfo)(nil)).Elem())
|
||||
}
|
||||
|
||||
type DynamicTypeMgrMethodTypeInfo struct {
|
||||
types.DynamicData
|
||||
|
||||
Name string `xml:"name"`
|
||||
WsdlName string `xml:"wsdlName"`
|
||||
Version string `xml:"version"`
|
||||
ParamTypeInfo []DynamicTypeMgrParamTypeInfo `xml:"paramTypeInfo,omitempty"`
|
||||
ReturnTypeInfo *DynamicTypeMgrParamTypeInfo `xml:"returnTypeInfo,omitempty"`
|
||||
Fault []string `xml:"fault,omitempty"`
|
||||
PrivId string `xml:"privId,omitempty"`
|
||||
Annotation []DynamicTypeMgrAnnotation `xml:"annotation,omitempty"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
types.Add("DynamicTypeMgrMethodTypeInfo", reflect.TypeOf((*DynamicTypeMgrMethodTypeInfo)(nil)).Elem())
|
||||
}
|
||||
|
||||
type DynamicTypeMgrMoFilterSpec struct {
|
||||
DynamicTypeMgrFilterSpec
|
||||
|
||||
Id string `xml:"id,omitempty"`
|
||||
TypeSubstr string `xml:"typeSubstr,omitempty"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
types.Add("DynamicTypeMgrMoFilterSpec", reflect.TypeOf((*DynamicTypeMgrMoFilterSpec)(nil)).Elem())
|
||||
}
|
||||
|
||||
type DynamicTypeMgrMoInstance struct {
|
||||
types.DynamicData
|
||||
|
||||
Id string `xml:"id"`
|
||||
MoType string `xml:"moType"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
types.Add("DynamicTypeMgrMoInstance", reflect.TypeOf((*DynamicTypeMgrMoInstance)(nil)).Elem())
|
||||
}
|
||||
|
||||
type DynamicTypeMgrParamTypeInfo struct {
|
||||
types.DynamicData
|
||||
|
||||
Name string `xml:"name"`
|
||||
Version string `xml:"version"`
|
||||
Type string `xml:"type"`
|
||||
PrivId string `xml:"privId,omitempty"`
|
||||
Annotation []DynamicTypeMgrAnnotation `xml:"annotation,omitempty"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
types.Add("DynamicTypeMgrParamTypeInfo", reflect.TypeOf((*DynamicTypeMgrParamTypeInfo)(nil)).Elem())
|
||||
}
|
||||
|
||||
type DynamicTypeMgrPropertyTypeInfo struct {
|
||||
types.DynamicData
|
||||
|
||||
Name string `xml:"name"`
|
||||
Version string `xml:"version"`
|
||||
Type string `xml:"type"`
|
||||
PrivId string `xml:"privId,omitempty"`
|
||||
MsgIdFormat string `xml:"msgIdFormat,omitempty"`
|
||||
Annotation []DynamicTypeMgrAnnotation `xml:"annotation,omitempty"`
|
||||
}
|
||||
|
||||
type DynamicTypeMgrQueryTypeInfoRequest struct {
|
||||
This types.ManagedObjectReference `xml:"_this"`
|
||||
FilterSpec BaseDynamicTypeMgrFilterSpec `xml:"filterSpec,omitempty,typeattr"`
|
||||
}
|
||||
|
||||
type DynamicTypeMgrQueryTypeInfoResponse struct {
|
||||
Returnval DynamicTypeMgrAllTypeInfoRequest `xml:"urn:vim25 returnval"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
types.Add("DynamicTypeMgrPropertyTypeInfo", reflect.TypeOf((*DynamicTypeMgrPropertyTypeInfo)(nil)).Elem())
|
||||
}
|
||||
|
||||
type DynamicTypeMgrTypeFilterSpec struct {
|
||||
DynamicTypeMgrFilterSpec
|
||||
|
||||
TypeSubstr string `xml:"typeSubstr,omitempty"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
types.Add("DynamicTypeMgrTypeFilterSpec", reflect.TypeOf((*DynamicTypeMgrTypeFilterSpec)(nil)).Elem())
|
||||
}
|
||||
|
||||
type ReflectManagedMethodExecuterSoapArgument struct {
|
||||
types.DynamicData
|
||||
|
||||
Name string `xml:"name"`
|
||||
Val string `xml:"val"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
types.Add("ReflectManagedMethodExecuterSoapArgument", reflect.TypeOf((*ReflectManagedMethodExecuterSoapArgument)(nil)).Elem())
|
||||
}
|
||||
|
||||
type ReflectManagedMethodExecuterSoapFault struct {
|
||||
types.DynamicData
|
||||
|
||||
FaultMsg string `xml:"faultMsg"`
|
||||
FaultDetail string `xml:"faultDetail,omitempty"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
types.Add("ReflectManagedMethodExecuterSoapFault", reflect.TypeOf((*ReflectManagedMethodExecuterSoapFault)(nil)).Elem())
|
||||
}
|
||||
|
||||
type ReflectManagedMethodExecuterSoapResult struct {
|
||||
types.DynamicData
|
||||
|
||||
Response string `xml:"response,omitempty"`
|
||||
Fault *ReflectManagedMethodExecuterSoapFault `xml:"fault,omitempty"`
|
||||
}
|
||||
|
||||
type RetrieveDynamicTypeManagerRequest struct {
|
||||
This types.ManagedObjectReference `xml:"_this"`
|
||||
}
|
||||
|
||||
type RetrieveDynamicTypeManagerResponse struct {
|
||||
Returnval *InternalDynamicTypeManager `xml:"urn:vim25 returnval"`
|
||||
}
|
||||
|
||||
type RetrieveManagedMethodExecuterRequest struct {
|
||||
This types.ManagedObjectReference `xml:"_this"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
types.Add("RetrieveManagedMethodExecuter", reflect.TypeOf((*RetrieveManagedMethodExecuterRequest)(nil)).Elem())
|
||||
}
|
||||
|
||||
type RetrieveManagedMethodExecuterResponse struct {
|
||||
Returnval *ReflectManagedMethodExecuter `xml:"urn:vim25 returnval"`
|
||||
}
|
||||
|
||||
type InternalDynamicTypeManager struct {
|
||||
types.ManagedObjectReference
|
||||
}
|
||||
|
||||
type ReflectManagedMethodExecuter struct {
|
||||
types.ManagedObjectReference
|
||||
}
|
||||
|
||||
type ExecuteSoapRequest struct {
|
||||
This types.ManagedObjectReference `xml:"_this"`
|
||||
Moid string `xml:"moid"`
|
||||
Version string `xml:"version"`
|
||||
Method string `xml:"method"`
|
||||
Argument []ReflectManagedMethodExecuterSoapArgument `xml:"argument,omitempty"`
|
||||
}
|
||||
|
||||
type ExecuteSoapResponse struct {
|
||||
Returnval *ReflectManagedMethodExecuterSoapResult `xml:"urn:vim25 returnval"`
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
Copyright (c) 2014-2022 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache 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://www.apache.org/licenses/LICENSE-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.
|
||||
*/
|
||||
|
||||
package version
|
||||
|
||||
const (
|
||||
// ClientName is the name of this SDK
|
||||
ClientName = "govmomi"
|
||||
|
||||
// ClientVersion is the version of this SDK
|
||||
ClientVersion = "0.37.1"
|
||||
)
|
||||
+6
-11
@@ -18,16 +18,15 @@ package nfc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
|
||||
"github.com/vmware/govmomi/property"
|
||||
"github.com/vmware/govmomi/task"
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/progress"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
@@ -72,17 +71,17 @@ func (l *Lease) Complete(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// GetManifest wraps methods.GetManifest
|
||||
func (l *Lease) GetManifest(ctx context.Context) error {
|
||||
func (l *Lease) GetManifest(ctx context.Context) ([]types.HttpNfcLeaseManifestEntry, error) {
|
||||
req := types.HttpNfcLeaseGetManifest{
|
||||
This: l.Reference(),
|
||||
}
|
||||
|
||||
_, err := methods.HttpNfcLeaseGetManifest(ctx, l.c, &req)
|
||||
res, err := methods.HttpNfcLeaseGetManifest(ctx, l.c, &req)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil
|
||||
return res.Returnval, nil
|
||||
}
|
||||
|
||||
// Progress wraps methods.Progress
|
||||
@@ -195,7 +194,7 @@ func (l *Lease) Wait(ctx context.Context, items []types.OvfFileItem) (*LeaseInfo
|
||||
}
|
||||
|
||||
if lease.Error != nil {
|
||||
return nil, errors.New(lease.Error.LocalizedMessage)
|
||||
return nil, &task.Error{LocalizedMethodFault: lease.Error}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unexpected nfc lease state: %s", lease.State)
|
||||
@@ -208,8 +207,6 @@ func (l *Lease) StartUpdater(ctx context.Context, info *LeaseInfo) *LeaseUpdater
|
||||
func (l *Lease) Upload(ctx context.Context, item FileItem, f io.Reader, opts soap.Upload) error {
|
||||
if opts.Progress == nil {
|
||||
opts.Progress = item
|
||||
} else {
|
||||
opts.Progress = progress.Tee(item, opts.Progress)
|
||||
}
|
||||
|
||||
// Non-disk files (such as .iso) use the PUT method.
|
||||
@@ -230,8 +227,6 @@ func (l *Lease) Upload(ctx context.Context, item FileItem, f io.Reader, opts soa
|
||||
func (l *Lease) DownloadFile(ctx context.Context, file string, item FileItem, opts soap.Download) error {
|
||||
if opts.Progress == nil {
|
||||
opts.Progress = item
|
||||
} else {
|
||||
opts.Progress = progress.Tee(item, opts.Progress)
|
||||
}
|
||||
|
||||
return l.c.DownloadFile(ctx, file, item.URL, &opts)
|
||||
|
||||
+2
-2
@@ -57,8 +57,8 @@ func (o FileItem) File() types.OvfFile {
|
||||
}
|
||||
|
||||
type LeaseUpdater struct {
|
||||
pos int64 // Number of bytes (keep first to ensure 64 bit aligment)
|
||||
total int64 // Total number of bytes (keep first to ensure 64 bit aligment)
|
||||
pos int64 // Number of bytes (keep first to ensure 64 bit alignment)
|
||||
total int64 // Total number of bytes (keep first to ensure 64 bit alignment)
|
||||
|
||||
lease *Lease
|
||||
|
||||
|
||||
+47
@@ -172,3 +172,50 @@ func (m AuthorizationManager) UpdateRole(ctx context.Context, id int32, name str
|
||||
_, err := methods.UpdateAuthorizationRole(ctx, m.Client(), &req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m AuthorizationManager) HasUserPrivilegeOnEntities(ctx context.Context, entities []types.ManagedObjectReference, userName string, privID []string) ([]types.EntityPrivilege, error) {
|
||||
req := types.HasUserPrivilegeOnEntities{
|
||||
This: m.Reference(),
|
||||
Entities: entities,
|
||||
UserName: userName,
|
||||
PrivId: privID,
|
||||
}
|
||||
|
||||
res, err := methods.HasUserPrivilegeOnEntities(ctx, m.Client(), &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Returnval, nil
|
||||
}
|
||||
|
||||
func (m AuthorizationManager) HasPrivilegeOnEntity(ctx context.Context, entity types.ManagedObjectReference, sessionID string, privID []string) ([]bool, error) {
|
||||
req := types.HasPrivilegeOnEntity{
|
||||
This: m.Reference(),
|
||||
Entity: entity,
|
||||
SessionId: sessionID,
|
||||
PrivId: privID,
|
||||
}
|
||||
|
||||
res, err := methods.HasPrivilegeOnEntity(ctx, m.Client(), &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Returnval, nil
|
||||
}
|
||||
|
||||
func (m AuthorizationManager) FetchUserPrivilegeOnEntities(ctx context.Context, entities []types.ManagedObjectReference, userName string) ([]types.UserPrivilegeResult, error) {
|
||||
req := types.FetchUserPrivilegeOnEntities{
|
||||
This: m.Reference(),
|
||||
Entities: entities,
|
||||
UserName: userName,
|
||||
}
|
||||
|
||||
res, err := methods.FetchUserPrivilegeOnEntities(ctx, m.Client(), &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Returnval, nil
|
||||
}
|
||||
|
||||
+14
@@ -87,3 +87,17 @@ func (c ClusterComputeResource) MoveInto(ctx context.Context, hosts ...*HostSyst
|
||||
|
||||
return NewTask(c.c, res.Returnval), nil
|
||||
}
|
||||
|
||||
func (c ClusterComputeResource) PlaceVm(ctx context.Context, spec types.PlacementSpec) (*types.PlacementResult, error) {
|
||||
req := types.PlaceVm{
|
||||
This: c.Reference(),
|
||||
PlacementSpec: spec,
|
||||
}
|
||||
|
||||
res, err := methods.PlaceVm(ctx, c.c, &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &res.Returnval, nil
|
||||
}
|
||||
|
||||
+19
-15
@@ -75,29 +75,22 @@ func (c *Common) SetInventoryPath(p string) {
|
||||
c.InventoryPath = p
|
||||
}
|
||||
|
||||
// ObjectName returns the base name of the InventoryPath field if set,
|
||||
// otherwise fetches the mo.ManagedEntity.Name field via the property collector.
|
||||
// ObjectName fetches the mo.ManagedEntity.Name field via the property collector.
|
||||
func (c Common) ObjectName(ctx context.Context) (string, error) {
|
||||
var o mo.ManagedEntity
|
||||
var content []types.ObjectContent
|
||||
|
||||
err := c.Properties(ctx, c.Reference(), []string{"name"}, &o)
|
||||
err := c.Properties(ctx, c.Reference(), []string{"name"}, &content)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if o.Name != "" {
|
||||
return o.Name, nil
|
||||
for i := range content {
|
||||
for _, prop := range content[i].PropSet {
|
||||
return prop.Val.(string), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Network has its own "name" field...
|
||||
var n mo.Network
|
||||
|
||||
err = c.Properties(ctx, c.Reference(), []string{"name"}, &n)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return n.Name, nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Properties is a wrapper for property.DefaultCollector().RetrieveOne()
|
||||
@@ -142,3 +135,14 @@ func (c Common) SetCustomValue(ctx context.Context, key string, value string) er
|
||||
_, err := methods.SetCustomValue(ctx, c.c, &req)
|
||||
return err
|
||||
}
|
||||
|
||||
func ReferenceFromString(s string) *types.ManagedObjectReference {
|
||||
var ref types.ManagedObjectReference
|
||||
if !ref.FromString(s) {
|
||||
return nil
|
||||
}
|
||||
if mo.IsManagedObjectType(ref.Type) {
|
||||
return &ref
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+18
-2
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
|
||||
Copyright (c) 2015-2023 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -18,6 +18,7 @@ package object
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/vmware/govmomi/property"
|
||||
@@ -84,6 +85,21 @@ func (c ComputeResource) Datastores(ctx context.Context) ([]*Datastore, error) {
|
||||
return dss, nil
|
||||
}
|
||||
|
||||
func (c ComputeResource) EnvironmentBrowser(ctx context.Context) (*EnvironmentBrowser, error) {
|
||||
var cr mo.ComputeResource
|
||||
|
||||
err := c.Properties(ctx, c.Reference(), []string{"environmentBrowser"}, &cr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cr.EnvironmentBrowser == nil {
|
||||
return nil, fmt.Errorf("%s: nil environmentBrowser", c.Reference())
|
||||
}
|
||||
|
||||
return NewEnvironmentBrowser(c.c, *cr.EnvironmentBrowser), nil
|
||||
}
|
||||
|
||||
func (c ComputeResource) ResourcePool(ctx context.Context) (*ResourcePool, error) {
|
||||
var cr mo.ComputeResource
|
||||
|
||||
|
||||
+7
@@ -21,6 +21,7 @@ import (
|
||||
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
@@ -36,6 +37,12 @@ func NewCustomizationSpecManager(c *vim25.Client) *CustomizationSpecManager {
|
||||
return &cs
|
||||
}
|
||||
|
||||
func (cs CustomizationSpecManager) Info(ctx context.Context) ([]types.CustomizationSpecInfo, error) {
|
||||
var m mo.CustomizationSpecManager
|
||||
err := cs.Properties(ctx, cs.Reference(), []string{"info"}, &m)
|
||||
return m.Info, err
|
||||
}
|
||||
|
||||
func (cs CustomizationSpecManager) DoesCustomizationSpecExist(ctx context.Context, name string) (bool, error) {
|
||||
req := types.DoesCustomizationSpecExist{
|
||||
This: cs.Reference(),
|
||||
|
||||
+75
-11
@@ -27,6 +27,7 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/govmomi/internal"
|
||||
"github.com/vmware/govmomi/property"
|
||||
"github.com/vmware/govmomi/session"
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
@@ -67,7 +68,37 @@ func NewDatastore(c *vim25.Client, ref types.ManagedObjectReference) *Datastore
|
||||
}
|
||||
}
|
||||
|
||||
// FindInventoryPath sets InventoryPath and DatacenterPath,
|
||||
// needed by NewURL() to compose an upload/download endpoint URL
|
||||
func (d *Datastore) FindInventoryPath(ctx context.Context) error {
|
||||
entities, err := mo.Ancestors(ctx, d.c, d.c.ServiceContent.PropertyCollector, d.r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
val := "/"
|
||||
|
||||
for _, entity := range entities {
|
||||
if entity.Parent == nil {
|
||||
continue // root folder
|
||||
}
|
||||
val = path.Join(val, entity.Name)
|
||||
if entity.Self.Type == "Datacenter" {
|
||||
d.DatacenterPath = val
|
||||
}
|
||||
}
|
||||
|
||||
d.InventoryPath = val
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d Datastore) Path(path string) string {
|
||||
var p DatastorePath
|
||||
if p.FromString(path) {
|
||||
return p.String() // already in "[datastore] path" format
|
||||
}
|
||||
|
||||
return (&DatastorePath{
|
||||
Datastore: d.Name(),
|
||||
Path: path,
|
||||
@@ -78,8 +109,14 @@ func (d Datastore) Path(path string) string {
|
||||
func (d Datastore) NewURL(path string) *url.URL {
|
||||
u := d.c.URL()
|
||||
|
||||
scheme := u.Scheme
|
||||
// In rare cases where vCenter and ESX are accessed using different schemes.
|
||||
if overrideScheme := os.Getenv("GOVMOMI_DATASTORE_ACCESS_SCHEME"); overrideScheme != "" {
|
||||
scheme = overrideScheme
|
||||
}
|
||||
|
||||
return &url.URL{
|
||||
Scheme: u.Scheme,
|
||||
Scheme: scheme,
|
||||
Host: u.Host,
|
||||
Path: fmt.Sprintf("/folder/%s", path),
|
||||
RawQuery: url.Values{
|
||||
@@ -89,11 +126,6 @@ func (d Datastore) NewURL(path string) *url.URL {
|
||||
}
|
||||
}
|
||||
|
||||
// URL is deprecated, use NewURL instead.
|
||||
func (d Datastore) URL(ctx context.Context, dc *Datacenter, path string) (*url.URL, error) {
|
||||
return d.NewURL(path), nil
|
||||
}
|
||||
|
||||
func (d Datastore) Browser(ctx context.Context) (*HostDatastoreBrowser, error) {
|
||||
var do mo.Datastore
|
||||
|
||||
@@ -174,6 +206,10 @@ func (d Datastore) HostContext(ctx context.Context, host *HostSystem) context.Co
|
||||
// that can be used along with the ticket cookie to access the given path. An host is chosen at random unless the
|
||||
// the given Context was created with a specific host via the HostContext method.
|
||||
func (d Datastore) ServiceTicket(ctx context.Context, path string, method string) (*url.URL, *http.Cookie, error) {
|
||||
if d.InventoryPath == "" {
|
||||
_ = d.FindInventoryPath(ctx)
|
||||
}
|
||||
|
||||
u := d.NewURL(path)
|
||||
|
||||
host, ok := ctx.Value(datastoreServiceTicketHostKey{}).(*HostSystem)
|
||||
@@ -218,8 +254,18 @@ func (d Datastore) ServiceTicket(ctx context.Context, path string, method string
|
||||
delete(q, "dcPath")
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
// Now that we have a host selected, take a copy of the URL.
|
||||
transferURL := *u
|
||||
|
||||
if internal.UsingEnvoySidecar(d.Client()) {
|
||||
// Rewrite the host URL to go through the Envoy sidecar on VC.
|
||||
// Reciever must use a custom dialer.
|
||||
u = internal.HostGatewayTransferURL(u, host.Reference())
|
||||
}
|
||||
|
||||
spec := types.SessionManagerHttpServiceRequestSpec{
|
||||
Url: u.String(),
|
||||
// Use the original URL (without rewrites) for the session ticket.
|
||||
Url: transferURL.String(),
|
||||
// See SessionManagerHttpServiceRequestSpecMethod enum
|
||||
Method: fmt.Sprintf("http%s%s", method[0:1], strings.ToLower(method[1:])),
|
||||
}
|
||||
@@ -256,7 +302,10 @@ func (d Datastore) uploadTicket(ctx context.Context, path string, param *soap.Up
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
p.Ticket = ticket
|
||||
if ticket != nil {
|
||||
p.Ticket = ticket
|
||||
p.Close = true // disable Keep-Alive connection to ESX
|
||||
}
|
||||
|
||||
return u, &p, nil
|
||||
}
|
||||
@@ -272,7 +321,10 @@ func (d Datastore) downloadTicket(ctx context.Context, path string, param *soap.
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
p.Ticket = ticket
|
||||
if ticket != nil {
|
||||
p.Ticket = ticket
|
||||
p.Close = true // disable Keep-Alive connection to ESX
|
||||
}
|
||||
|
||||
return u, &p, nil
|
||||
}
|
||||
@@ -292,7 +344,13 @@ func (d Datastore) UploadFile(ctx context.Context, file string, path string, par
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return d.Client().UploadFile(ctx, file, u, p)
|
||||
vc := d.Client()
|
||||
if internal.UsingEnvoySidecar(vc) {
|
||||
// Override the vim client with a new one that wraps a Unix socket transport.
|
||||
// Using HTTP here so secure means nothing.
|
||||
vc = internal.ClientWithEnvoyHostGateway(vc)
|
||||
}
|
||||
return vc.UploadFile(ctx, file, u, p)
|
||||
}
|
||||
|
||||
// Download via soap.Download with an http service ticket
|
||||
@@ -310,7 +368,13 @@ func (d Datastore) DownloadFile(ctx context.Context, path string, file string, p
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return d.Client().DownloadFile(ctx, file, u, p)
|
||||
vc := d.Client()
|
||||
if internal.UsingEnvoySidecar(vc) {
|
||||
// Override the vim client with a new one that wraps a Unix socket transport.
|
||||
// Using HTTP here so secure means nothing.
|
||||
vc = internal.ClientWithEnvoyHostGateway(vc)
|
||||
}
|
||||
return vc.DownloadFile(ctx, file, u, p)
|
||||
}
|
||||
|
||||
// AttachedHosts returns hosts that have this Datastore attached, accessible and writable.
|
||||
|
||||
+2
-4
@@ -297,10 +297,8 @@ func (f *DatastoreFile) TailFunc(lines int, include func(line int, message strin
|
||||
|
||||
nread = bsize + remain
|
||||
eof = true
|
||||
} else {
|
||||
if pos, err = f.Seek(offset, io.SeekEnd); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if pos, err = f.Seek(offset, io.SeekEnd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = io.CopyN(buf, f, nread); err != nil {
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ type DatastorePath struct {
|
||||
// FromString parses a datastore path.
|
||||
// Returns true if the path could be parsed, false otherwise.
|
||||
func (p *DatastorePath) FromString(s string) bool {
|
||||
if len(s) == 0 {
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -71,10 +71,8 @@ func (m DiagnosticManager) GenerateLogBundles(ctx context.Context, includeDefaul
|
||||
IncludeDefault: includeDefault,
|
||||
}
|
||||
|
||||
if host != nil {
|
||||
for _, h := range host {
|
||||
req.Host = append(req.Host, h.Reference())
|
||||
}
|
||||
for _, h := range host {
|
||||
req.Host = append(req.Host, h.Reference())
|
||||
}
|
||||
|
||||
res, err := methods.GenerateLogBundles_Task(ctx, m.c, &req)
|
||||
|
||||
+11
-1
@@ -36,6 +36,10 @@ func NewDistributedVirtualPortgroup(c *vim25.Client, ref types.ManagedObjectRefe
|
||||
}
|
||||
}
|
||||
|
||||
func (p DistributedVirtualPortgroup) GetInventoryPath() string {
|
||||
return p.InventoryPath
|
||||
}
|
||||
|
||||
// EthernetCardBackingInfo returns the VirtualDeviceBackingInfo for this DistributedVirtualPortgroup
|
||||
func (p DistributedVirtualPortgroup) EthernetCardBackingInfo(ctx context.Context) (types.BaseVirtualDeviceBackingInfo, error) {
|
||||
var dvp mo.DistributedVirtualPortgroup
|
||||
@@ -46,9 +50,15 @@ func (p DistributedVirtualPortgroup) EthernetCardBackingInfo(ctx context.Context
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// From the docs at https://code.vmware.com/apis/196/vsphere/doc/vim.dvs.DistributedVirtualPortgroup.ConfigInfo.html:
|
||||
// "This property should always be set unless the user's setting does not have System.Read privilege on the object referred to by this property."
|
||||
// Note that "the object" refers to the Switch, not the PortGroup.
|
||||
if dvp.Config.DistributedVirtualSwitch == nil {
|
||||
return nil, fmt.Errorf("no System.Read privilege on: %s.%s", p.Reference(), prop)
|
||||
name := p.InventoryPath
|
||||
if name == "" {
|
||||
name = p.Reference().String()
|
||||
}
|
||||
return nil, fmt.Errorf("failed to create EthernetCardBackingInfo for %s: System.Read privilege required for %s", name, prop)
|
||||
}
|
||||
|
||||
if err := p.Properties(ctx, *dvp.Config.DistributedVirtualSwitch, []string{"uuid"}, &dvs); err != nil {
|
||||
|
||||
+39
-1
@@ -18,6 +18,7 @@ package object
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
@@ -34,8 +35,17 @@ func NewDistributedVirtualSwitch(c *vim25.Client, ref types.ManagedObjectReferen
|
||||
}
|
||||
}
|
||||
|
||||
func (s DistributedVirtualSwitch) GetInventoryPath() string {
|
||||
return s.InventoryPath
|
||||
}
|
||||
|
||||
func (s DistributedVirtualSwitch) EthernetCardBackingInfo(ctx context.Context) (types.BaseVirtualDeviceBackingInfo, error) {
|
||||
return nil, ErrNotSupported // TODO: just to satisfy NetworkReference interface for the finder
|
||||
ref := s.Reference()
|
||||
name := s.InventoryPath
|
||||
if name == "" {
|
||||
name = ref.String()
|
||||
}
|
||||
return nil, fmt.Errorf("type %s (%s) cannot be used for EthernetCardBackingInfo", ref.Type, name)
|
||||
}
|
||||
|
||||
func (s DistributedVirtualSwitch) Reconfigure(ctx context.Context, spec types.BaseDVSConfigSpec) (*Task, error) {
|
||||
@@ -78,3 +88,31 @@ func (s DistributedVirtualSwitch) FetchDVPorts(ctx context.Context, criteria *ty
|
||||
}
|
||||
return res.Returnval, nil
|
||||
}
|
||||
|
||||
func (s DistributedVirtualSwitch) ReconfigureDVPort(ctx context.Context, spec []types.DVPortConfigSpec) (*Task, error) {
|
||||
req := types.ReconfigureDVPort_Task{
|
||||
This: s.Reference(),
|
||||
Port: spec,
|
||||
}
|
||||
|
||||
res, err := methods.ReconfigureDVPort_Task(ctx, s.Client(), &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewTask(s.Client(), res.Returnval), nil
|
||||
}
|
||||
|
||||
func (s DistributedVirtualSwitch) ReconfigureLACP(ctx context.Context, spec []types.VMwareDvsLacpGroupSpec) (*Task, error) {
|
||||
req := types.UpdateDVSLacpGroupConfig_Task{
|
||||
This: s.Reference(),
|
||||
LacpGroupSpec: spec,
|
||||
}
|
||||
|
||||
res, err := methods.UpdateDVSLacpGroupConfig_Task(ctx, s.Client(), &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewTask(s.Client(), res.Returnval), nil
|
||||
}
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
Copyright (c) 2023-2023 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache 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://www.apache.org/licenses/LICENSE-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.
|
||||
*/
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type EnvironmentBrowser struct {
|
||||
Common
|
||||
}
|
||||
|
||||
func NewEnvironmentBrowser(c *vim25.Client, ref types.ManagedObjectReference) *EnvironmentBrowser {
|
||||
return &EnvironmentBrowser{
|
||||
Common: NewCommon(c, ref),
|
||||
}
|
||||
}
|
||||
|
||||
func (b EnvironmentBrowser) QueryConfigTarget(ctx context.Context, host *HostSystem) (*types.ConfigTarget, error) {
|
||||
req := types.QueryConfigTarget{
|
||||
This: b.Reference(),
|
||||
}
|
||||
|
||||
if host != nil {
|
||||
ref := host.Reference()
|
||||
req.Host = &ref
|
||||
}
|
||||
|
||||
res, err := methods.QueryConfigTarget(ctx, b.Client(), &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Returnval, nil
|
||||
}
|
||||
|
||||
func (b EnvironmentBrowser) QueryTargetCapabilities(ctx context.Context, host *HostSystem) (*types.HostCapability, error) {
|
||||
req := types.QueryTargetCapabilities{
|
||||
This: b.Reference(),
|
||||
}
|
||||
|
||||
if host != nil {
|
||||
ref := host.Reference()
|
||||
req.Host = &ref
|
||||
}
|
||||
|
||||
res, err := methods.QueryTargetCapabilities(ctx, b.Client(), &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Returnval, nil
|
||||
}
|
||||
|
||||
func (b EnvironmentBrowser) QueryConfigOption(ctx context.Context, spec *types.EnvironmentBrowserConfigOptionQuerySpec) (*types.VirtualMachineConfigOption, error) {
|
||||
req := types.QueryConfigOptionEx{
|
||||
This: b.Reference(),
|
||||
Spec: spec,
|
||||
}
|
||||
|
||||
res, err := methods.QueryConfigOptionEx(ctx, b.Client(), &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Returnval, nil
|
||||
}
|
||||
|
||||
func (b EnvironmentBrowser) QueryConfigOptionDescriptor(ctx context.Context) ([]types.VirtualMachineConfigOptionDescriptor, error) {
|
||||
req := types.QueryConfigOptionDescriptor{
|
||||
This: b.Reference(),
|
||||
}
|
||||
|
||||
res, err := methods.QueryConfigOptionDescriptor(ctx, b.Client(), &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Returnval, nil
|
||||
}
|
||||
+14
@@ -225,3 +225,17 @@ func (f Folder) MoveInto(ctx context.Context, list []types.ManagedObjectReferenc
|
||||
|
||||
return NewTask(f.c, res.Returnval), nil
|
||||
}
|
||||
|
||||
func (f Folder) PlaceVmsXCluster(ctx context.Context, spec types.PlaceVmsXClusterSpec) (*types.PlaceVmsXClusterResult, error) {
|
||||
req := types.PlaceVmsXCluster{
|
||||
This: f.Reference(),
|
||||
PlacementSpec: spec,
|
||||
}
|
||||
|
||||
res, err := methods.PlaceVmsXCluster(ctx, f.c, &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &res.Returnval, nil
|
||||
}
|
||||
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache 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://www.apache.org/licenses/LICENSE-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.
|
||||
*/
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type HistoryCollector struct {
|
||||
Common
|
||||
}
|
||||
|
||||
func NewHistoryCollector(c *vim25.Client, ref types.ManagedObjectReference) *HistoryCollector {
|
||||
return &HistoryCollector{
|
||||
Common: NewCommon(c, ref),
|
||||
}
|
||||
}
|
||||
|
||||
func (h HistoryCollector) Destroy(ctx context.Context) error {
|
||||
req := types.DestroyCollector{
|
||||
This: h.Reference(),
|
||||
}
|
||||
|
||||
_, err := methods.DestroyCollector(ctx, h.c, &req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (h HistoryCollector) Reset(ctx context.Context) error {
|
||||
req := types.ResetCollector{
|
||||
This: h.Reference(),
|
||||
}
|
||||
|
||||
_, err := methods.ResetCollector(ctx, h.c, &req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (h HistoryCollector) Rewind(ctx context.Context) error {
|
||||
req := types.RewindCollector{
|
||||
This: h.Reference(),
|
||||
}
|
||||
|
||||
_, err := methods.RewindCollector(ctx, h.c, &req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (h HistoryCollector) SetPageSize(ctx context.Context, maxCount int32) error {
|
||||
req := types.SetCollectorPageSize{
|
||||
This: h.Reference(),
|
||||
MaxCount: maxCount,
|
||||
}
|
||||
|
||||
_, err := methods.SetCollectorPageSize(ctx, h.c, &req)
|
||||
return err
|
||||
}
|
||||
+7
-18
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
|
||||
Copyright (c) 2016-2023 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
package object
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
@@ -36,10 +35,10 @@ import (
|
||||
type HostCertificateInfo struct {
|
||||
types.HostCertificateManagerCertificateInfo
|
||||
|
||||
ThumbprintSHA1 string
|
||||
ThumbprintSHA256 string
|
||||
ThumbprintSHA1 string `json:"thumbprintSHA1"`
|
||||
ThumbprintSHA256 string `json:"thumbprintSHA256"`
|
||||
|
||||
Err error
|
||||
Err error `json:"err"`
|
||||
Certificate *x509.Certificate `json:"-"`
|
||||
|
||||
subjectName *pkix.Name
|
||||
@@ -58,14 +57,7 @@ func (info *HostCertificateInfo) FromCertificate(cert *x509.Certificate) *HostCe
|
||||
info.Subject = info.fromName(info.subjectName)
|
||||
|
||||
info.ThumbprintSHA1 = soap.ThumbprintSHA1(cert)
|
||||
|
||||
// SHA-256 for info purposes only, API fields all use SHA-1
|
||||
sum := sha256.Sum256(cert.Raw)
|
||||
hex := make([]string, len(sum))
|
||||
for i, b := range sum {
|
||||
hex[i] = fmt.Sprintf("%02X", b)
|
||||
}
|
||||
info.ThumbprintSHA256 = strings.Join(hex, ":")
|
||||
info.ThumbprintSHA256 = soap.ThumbprintSHA256(cert)
|
||||
|
||||
if info.Status == "" {
|
||||
info.Status = string(types.HostCertificateManagerCertificateInfoCertificateStatusUnknown)
|
||||
@@ -86,10 +78,7 @@ func (info *HostCertificateInfo) FromURL(u *url.URL, config *tls.Config) error {
|
||||
|
||||
conn, err := tls.Dial("tcp", addr, config)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case x509.UnknownAuthorityError:
|
||||
case x509.HostnameError:
|
||||
default:
|
||||
if !soap.IsCertificateUntrusted(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
+63
-88
@@ -18,9 +18,9 @@ package object
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
@@ -34,163 +34,138 @@ func NewHostConfigManager(c *vim25.Client, ref types.ManagedObjectReference) *Ho
|
||||
}
|
||||
}
|
||||
|
||||
func (m HostConfigManager) DatastoreSystem(ctx context.Context) (*HostDatastoreSystem, error) {
|
||||
var h mo.HostSystem
|
||||
// reference returns the ManagedObjectReference for the given HostConfigManager property name.
|
||||
// An error is returned if the field is nil, of type ErrNotSupported if versioned is true.
|
||||
func (m HostConfigManager) reference(ctx context.Context, name string, versioned ...bool) (types.ManagedObjectReference, error) {
|
||||
prop := "configManager." + name
|
||||
var content []types.ObjectContent
|
||||
|
||||
err := m.Properties(ctx, m.Reference(), []string{"configManager.datastoreSystem"}, &h)
|
||||
err := m.Properties(ctx, m.Reference(), []string{prop}, &content)
|
||||
if err != nil {
|
||||
return types.ManagedObjectReference{}, err
|
||||
}
|
||||
|
||||
for _, c := range content {
|
||||
for _, p := range c.PropSet {
|
||||
if p.Name != prop {
|
||||
continue
|
||||
}
|
||||
if ref, ok := p.Val.(types.ManagedObjectReference); ok {
|
||||
return ref, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = fmt.Errorf("%s %s is nil", m.Reference(), prop)
|
||||
if len(versioned) == 1 && versioned[0] {
|
||||
err = ErrNotSupported
|
||||
}
|
||||
return types.ManagedObjectReference{}, err
|
||||
}
|
||||
|
||||
func (m HostConfigManager) DatastoreSystem(ctx context.Context) (*HostDatastoreSystem, error) {
|
||||
ref, err := m.reference(ctx, "datastoreSystem")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewHostDatastoreSystem(m.c, *h.ConfigManager.DatastoreSystem), nil
|
||||
return NewHostDatastoreSystem(m.c, ref), nil
|
||||
}
|
||||
|
||||
func (m HostConfigManager) NetworkSystem(ctx context.Context) (*HostNetworkSystem, error) {
|
||||
var h mo.HostSystem
|
||||
|
||||
err := m.Properties(ctx, m.Reference(), []string{"configManager.networkSystem"}, &h)
|
||||
ref, err := m.reference(ctx, "networkSystem")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewHostNetworkSystem(m.c, *h.ConfigManager.NetworkSystem), nil
|
||||
return NewHostNetworkSystem(m.c, ref), nil
|
||||
}
|
||||
|
||||
func (m HostConfigManager) FirewallSystem(ctx context.Context) (*HostFirewallSystem, error) {
|
||||
var h mo.HostSystem
|
||||
|
||||
err := m.Properties(ctx, m.Reference(), []string{"configManager.firewallSystem"}, &h)
|
||||
ref, err := m.reference(ctx, "firewallSystem")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewHostFirewallSystem(m.c, *h.ConfigManager.FirewallSystem), nil
|
||||
return NewHostFirewallSystem(m.c, ref), nil
|
||||
}
|
||||
|
||||
func (m HostConfigManager) StorageSystem(ctx context.Context) (*HostStorageSystem, error) {
|
||||
var h mo.HostSystem
|
||||
|
||||
err := m.Properties(ctx, m.Reference(), []string{"configManager.storageSystem"}, &h)
|
||||
ref, err := m.reference(ctx, "storageSystem")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewHostStorageSystem(m.c, *h.ConfigManager.StorageSystem), nil
|
||||
return NewHostStorageSystem(m.c, ref), nil
|
||||
}
|
||||
|
||||
func (m HostConfigManager) VirtualNicManager(ctx context.Context) (*HostVirtualNicManager, error) {
|
||||
var h mo.HostSystem
|
||||
|
||||
err := m.Properties(ctx, m.Reference(), []string{"configManager.virtualNicManager"}, &h)
|
||||
ref, err := m.reference(ctx, "virtualNicManager")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewHostVirtualNicManager(m.c, *h.ConfigManager.VirtualNicManager, m.Reference()), nil
|
||||
return NewHostVirtualNicManager(m.c, ref, m.Reference()), nil
|
||||
}
|
||||
|
||||
func (m HostConfigManager) VsanSystem(ctx context.Context) (*HostVsanSystem, error) {
|
||||
var h mo.HostSystem
|
||||
|
||||
err := m.Properties(ctx, m.Reference(), []string{"configManager.vsanSystem"}, &h)
|
||||
ref, err := m.reference(ctx, "vsanSystem", true) // Added in 5.5
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Added in 5.5
|
||||
if h.ConfigManager.VsanSystem == nil {
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
return NewHostVsanSystem(m.c, *h.ConfigManager.VsanSystem), nil
|
||||
return NewHostVsanSystem(m.c, ref), nil
|
||||
}
|
||||
|
||||
func (m HostConfigManager) VsanInternalSystem(ctx context.Context) (*HostVsanInternalSystem, error) {
|
||||
var h mo.HostSystem
|
||||
|
||||
err := m.Properties(ctx, m.Reference(), []string{"configManager.vsanInternalSystem"}, &h)
|
||||
ref, err := m.reference(ctx, "vsanInternalSystem", true) // Added in 5.5
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Added in 5.5
|
||||
if h.ConfigManager.VsanInternalSystem == nil {
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
return NewHostVsanInternalSystem(m.c, *h.ConfigManager.VsanInternalSystem), nil
|
||||
return NewHostVsanInternalSystem(m.c, ref), nil
|
||||
}
|
||||
|
||||
func (m HostConfigManager) AccountManager(ctx context.Context) (*HostAccountManager, error) {
|
||||
var h mo.HostSystem
|
||||
|
||||
err := m.Properties(ctx, m.Reference(), []string{"configManager.accountManager"}, &h)
|
||||
ref, err := m.reference(ctx, "accountManager", true) // Added in 5.5
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ref := h.ConfigManager.AccountManager // Added in 6.0
|
||||
if ref == nil {
|
||||
// Versions < 5.5 can use the ServiceContent ref,
|
||||
// but we can only use it when connected directly to ESX.
|
||||
c := m.Client()
|
||||
if !c.IsVC() {
|
||||
ref = c.ServiceContent.AccountManager
|
||||
}
|
||||
|
||||
if ref == nil {
|
||||
return nil, ErrNotSupported
|
||||
if err == ErrNotSupported {
|
||||
// Versions < 5.5 can use the ServiceContent ref,
|
||||
// but only when connected directly to ESX.
|
||||
if m.c.ServiceContent.AccountManager == nil {
|
||||
return nil, err
|
||||
}
|
||||
ref = *m.c.ServiceContent.AccountManager
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return NewHostAccountManager(m.c, *ref), nil
|
||||
return NewHostAccountManager(m.c, ref), nil
|
||||
}
|
||||
|
||||
func (m HostConfigManager) OptionManager(ctx context.Context) (*OptionManager, error) {
|
||||
var h mo.HostSystem
|
||||
|
||||
err := m.Properties(ctx, m.Reference(), []string{"configManager.advancedOption"}, &h)
|
||||
ref, err := m.reference(ctx, "advancedOption")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewOptionManager(m.c, *h.ConfigManager.AdvancedOption), nil
|
||||
return NewOptionManager(m.c, ref), nil
|
||||
}
|
||||
|
||||
func (m HostConfigManager) ServiceSystem(ctx context.Context) (*HostServiceSystem, error) {
|
||||
var h mo.HostSystem
|
||||
|
||||
err := m.Properties(ctx, m.Reference(), []string{"configManager.serviceSystem"}, &h)
|
||||
ref, err := m.reference(ctx, "serviceSystem")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewHostServiceSystem(m.c, *h.ConfigManager.ServiceSystem), nil
|
||||
return NewHostServiceSystem(m.c, ref), nil
|
||||
}
|
||||
|
||||
func (m HostConfigManager) CertificateManager(ctx context.Context) (*HostCertificateManager, error) {
|
||||
var h mo.HostSystem
|
||||
|
||||
err := m.Properties(ctx, m.Reference(), []string{"configManager.certificateManager"}, &h)
|
||||
ref, err := m.reference(ctx, "certificateManager", true) // Added in 6.0
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Added in 6.0
|
||||
if h.ConfigManager.CertificateManager == nil {
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
return NewHostCertificateManager(m.c, *h.ConfigManager.CertificateManager, m.Reference()), nil
|
||||
return NewHostCertificateManager(m.c, ref, m.Reference()), nil
|
||||
}
|
||||
|
||||
func (m HostConfigManager) DateTimeSystem(ctx context.Context) (*HostDateTimeSystem, error) {
|
||||
var h mo.HostSystem
|
||||
|
||||
err := m.Properties(ctx, m.Reference(), []string{"configManager.dateTimeSystem"}, &h)
|
||||
ref, err := m.reference(ctx, "dateTimeSystem")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewHostDateTimeSystem(m.c, *h.ConfigManager.DateTimeSystem), nil
|
||||
return NewHostDateTimeSystem(m.c, ref), nil
|
||||
}
|
||||
|
||||
+16
@@ -117,3 +117,19 @@ func (s HostDatastoreSystem) QueryVmfsDatastoreCreateOptions(ctx context.Context
|
||||
|
||||
return res.Returnval, nil
|
||||
}
|
||||
|
||||
func (s HostDatastoreSystem) ResignatureUnresolvedVmfsVolumes(ctx context.Context, devicePaths []string) (*Task, error) {
|
||||
req := &types.ResignatureUnresolvedVmfsVolume_Task{
|
||||
This: s.Reference(),
|
||||
ResolutionSpec: types.HostUnresolvedVmfsResignatureSpec{
|
||||
ExtentDevicePath: devicePaths,
|
||||
},
|
||||
}
|
||||
|
||||
res, err := methods.ResignatureUnresolvedVmfsVolume_Task(ctx, s.Client(), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewTask(s.c, res.Returnval), nil
|
||||
}
|
||||
|
||||
+4
-4
@@ -98,18 +98,18 @@ func (o HostNetworkSystem) AddVirtualSwitch(ctx context.Context, vswitchName str
|
||||
}
|
||||
|
||||
// QueryNetworkHint wraps methods.QueryNetworkHint
|
||||
func (o HostNetworkSystem) QueryNetworkHint(ctx context.Context, device []string) error {
|
||||
func (o HostNetworkSystem) QueryNetworkHint(ctx context.Context, device []string) ([]types.PhysicalNicHintInfo, error) {
|
||||
req := types.QueryNetworkHint{
|
||||
This: o.Reference(),
|
||||
Device: device,
|
||||
}
|
||||
|
||||
_, err := methods.QueryNetworkHint(ctx, o.c, &req)
|
||||
res, err := methods.QueryNetworkHint(ctx, o.c, &req)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil
|
||||
return res.Returnval, err
|
||||
}
|
||||
|
||||
// RefreshNetworkSystem wraps methods.RefreshNetworkSystem
|
||||
|
||||
+26
@@ -172,3 +172,29 @@ func (s HostStorageSystem) AttachScsiLun(ctx context.Context, uuid string) error
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s HostStorageSystem) QueryUnresolvedVmfsVolumes(ctx context.Context) ([]types.HostUnresolvedVmfsVolume, error) {
|
||||
req := &types.QueryUnresolvedVmfsVolume{
|
||||
This: s.Reference(),
|
||||
}
|
||||
|
||||
res, err := methods.QueryUnresolvedVmfsVolume(ctx, s.Client(), req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res.Returnval, nil
|
||||
}
|
||||
|
||||
func (s HostStorageSystem) UnmountVmfsVolume(ctx context.Context, vmfsUuid string) error {
|
||||
req := &types.UnmountVmfsVolume{
|
||||
This: s.Reference(),
|
||||
VmfsUuid: vmfsUuid,
|
||||
}
|
||||
|
||||
_, err := methods.UnmountVmfsVolume(ctx, s.Client(), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+10
-9
@@ -21,6 +21,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/vmware/govmomi/internal"
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
@@ -81,17 +82,17 @@ func (h HostSystem) ManagementIPs(ctx context.Context) ([]net.IP, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ips []net.IP
|
||||
for _, nc := range mh.Config.VirtualNicManagerInfo.NetConfig {
|
||||
if nc.NicType == "management" && len(nc.CandidateVnic) > 0 {
|
||||
ip := net.ParseIP(nc.CandidateVnic[0].Spec.Ip.IpAddress)
|
||||
if ip != nil {
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
config := mh.Config
|
||||
if config == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return ips, nil
|
||||
info := config.VirtualNicManagerInfo
|
||||
if info == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return internal.HostSystemManagementIPs(info.NetConfig), nil
|
||||
}
|
||||
|
||||
func (h HostSystem) Disconnect(ctx context.Context) (*Task, error) {
|
||||
|
||||
+6
-8
@@ -20,7 +20,6 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
@@ -34,21 +33,20 @@ func NewNetwork(c *vim25.Client, ref types.ManagedObjectReference) *Network {
|
||||
}
|
||||
}
|
||||
|
||||
func (n Network) GetInventoryPath() string {
|
||||
return n.InventoryPath
|
||||
}
|
||||
|
||||
// EthernetCardBackingInfo returns the VirtualDeviceBackingInfo for this Network
|
||||
func (n Network) EthernetCardBackingInfo(ctx context.Context) (types.BaseVirtualDeviceBackingInfo, error) {
|
||||
var e mo.Network
|
||||
|
||||
// Use Network.Name rather than Common.Name as the latter does not return the complete name if it contains a '/'
|
||||
// We can't use Common.ObjectName here either as we need the ManagedEntity.Name field is not set since mo.Network
|
||||
// has its own Name field.
|
||||
err := n.Properties(ctx, n.Reference(), []string{"name"}, &e)
|
||||
name, err := n.ObjectName(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
backing := &types.VirtualEthernetCardNetworkBackingInfo{
|
||||
VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
|
||||
DeviceName: e.Name,
|
||||
DeviceName: name,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -26,6 +26,6 @@ import (
|
||||
// which can be used as the backing for a VirtualEthernetCard.
|
||||
type NetworkReference interface {
|
||||
Reference
|
||||
|
||||
GetInventoryPath() string
|
||||
EthernetCardBackingInfo(ctx context.Context) (types.BaseVirtualDeviceBackingInfo, error)
|
||||
}
|
||||
|
||||
+23
-8
@@ -35,19 +35,17 @@ func NewOpaqueNetwork(c *vim25.Client, ref types.ManagedObjectReference) *Opaque
|
||||
}
|
||||
}
|
||||
|
||||
func (n OpaqueNetwork) GetInventoryPath() string {
|
||||
return n.InventoryPath
|
||||
}
|
||||
|
||||
// EthernetCardBackingInfo returns the VirtualDeviceBackingInfo for this Network
|
||||
func (n OpaqueNetwork) EthernetCardBackingInfo(ctx context.Context) (types.BaseVirtualDeviceBackingInfo, error) {
|
||||
var net mo.OpaqueNetwork
|
||||
|
||||
if err := n.Properties(ctx, n.Reference(), []string{"summary"}, &net); err != nil {
|
||||
summary, err := n.Summary(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
summary, ok := net.Summary.(*types.OpaqueNetworkSummary)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s unsupported network type: %T", n, net.Summary)
|
||||
}
|
||||
|
||||
backing := &types.VirtualEthernetCardOpaqueNetworkBackingInfo{
|
||||
OpaqueNetworkId: summary.OpaqueNetworkId,
|
||||
OpaqueNetworkType: summary.OpaqueNetworkType,
|
||||
@@ -55,3 +53,20 @@ func (n OpaqueNetwork) EthernetCardBackingInfo(ctx context.Context) (types.BaseV
|
||||
|
||||
return backing, nil
|
||||
}
|
||||
|
||||
// Summary returns the mo.OpaqueNetwork.Summary property
|
||||
func (n OpaqueNetwork) Summary(ctx context.Context) (*types.OpaqueNetworkSummary, error) {
|
||||
var props mo.OpaqueNetwork
|
||||
|
||||
err := n.Properties(ctx, n.Reference(), []string{"summary"}, &props)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
summary, ok := props.Summary.(*types.OpaqueNetworkSummary)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s unsupported network summary type: %T", n, props.Summary)
|
||||
}
|
||||
|
||||
return summary, nil
|
||||
}
|
||||
|
||||
+13
@@ -22,6 +22,7 @@ import (
|
||||
"github.com/vmware/govmomi/nfc"
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
@@ -35,6 +36,18 @@ func NewResourcePool(c *vim25.Client, ref types.ManagedObjectReference) *Resourc
|
||||
}
|
||||
}
|
||||
|
||||
// Owner returns the ResourcePool owner as a ClusterComputeResource or ComputeResource.
|
||||
func (p ResourcePool) Owner(ctx context.Context) (Reference, error) {
|
||||
var pool mo.ResourcePool
|
||||
|
||||
err := p.Properties(ctx, p.Reference(), []string{"owner"}, &pool)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewReference(p.Client(), pool.Owner), nil
|
||||
}
|
||||
|
||||
func (p ResourcePool) ImportVApp(ctx context.Context, spec types.BaseImportSpec, folder *Folder, host *HostSystem) (*nfc.Lease, error) {
|
||||
req := types.ImportVApp{
|
||||
This: p.Reference(),
|
||||
|
||||
+97
-1
@@ -93,7 +93,18 @@ func (s SearchIndex) FindByInventoryPath(ctx context.Context, path string) (Refe
|
||||
if res.Returnval == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return NewReference(s.c, *res.Returnval), nil
|
||||
|
||||
r := NewReference(s.c, *res.Returnval)
|
||||
|
||||
type common interface {
|
||||
SetInventoryPath(string)
|
||||
}
|
||||
|
||||
if c, ok := r.(common); ok {
|
||||
c.SetInventoryPath(path)
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// FindByIp finds a virtual machine or host by IP address.
|
||||
@@ -161,3 +172,88 @@ func (s SearchIndex) FindChild(ctx context.Context, entity Reference, name strin
|
||||
}
|
||||
return NewReference(s.c, *res.Returnval), nil
|
||||
}
|
||||
|
||||
// FindAllByDnsName finds all virtual machines or hosts by DNS name.
|
||||
func (s SearchIndex) FindAllByDnsName(ctx context.Context, dc *Datacenter, dnsName string, vmSearch bool) ([]Reference, error) {
|
||||
req := types.FindAllByDnsName{
|
||||
This: s.Reference(),
|
||||
DnsName: dnsName,
|
||||
VmSearch: vmSearch,
|
||||
}
|
||||
if dc != nil {
|
||||
ref := dc.Reference()
|
||||
req.Datacenter = &ref
|
||||
}
|
||||
|
||||
res, err := methods.FindAllByDnsName(ctx, s.c, &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(res.Returnval) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var references []Reference
|
||||
for _, returnval := range res.Returnval {
|
||||
references = append(references, NewReference(s.c, returnval))
|
||||
}
|
||||
return references, nil
|
||||
}
|
||||
|
||||
// FindAllByIp finds all virtual machines or hosts by IP address.
|
||||
func (s SearchIndex) FindAllByIp(ctx context.Context, dc *Datacenter, ip string, vmSearch bool) ([]Reference, error) {
|
||||
req := types.FindAllByIp{
|
||||
This: s.Reference(),
|
||||
Ip: ip,
|
||||
VmSearch: vmSearch,
|
||||
}
|
||||
if dc != nil {
|
||||
ref := dc.Reference()
|
||||
req.Datacenter = &ref
|
||||
}
|
||||
|
||||
res, err := methods.FindAllByIp(ctx, s.c, &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(res.Returnval) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var references []Reference
|
||||
for _, returnval := range res.Returnval {
|
||||
references = append(references, NewReference(s.c, returnval))
|
||||
}
|
||||
return references, nil
|
||||
}
|
||||
|
||||
// FindAllByUuid finds all virtual machines or hosts by UUID.
|
||||
func (s SearchIndex) FindAllByUuid(ctx context.Context, dc *Datacenter, uuid string, vmSearch bool, instanceUuid *bool) ([]Reference, error) {
|
||||
req := types.FindAllByUuid{
|
||||
This: s.Reference(),
|
||||
Uuid: uuid,
|
||||
VmSearch: vmSearch,
|
||||
InstanceUuid: instanceUuid,
|
||||
}
|
||||
if dc != nil {
|
||||
ref := dc.Reference()
|
||||
req.Datacenter = &ref
|
||||
}
|
||||
|
||||
res, err := methods.FindAllByUuid(ctx, s.c, &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(res.Returnval) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var references []Reference
|
||||
for _, returnval := range res.Returnval {
|
||||
references = append(references, NewReference(s.c, returnval))
|
||||
}
|
||||
return references, nil
|
||||
}
|
||||
|
||||
+88
-4
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
|
||||
Copyright (c) 2015-2024 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -18,6 +18,7 @@ package object
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/vmware/govmomi/property"
|
||||
"github.com/vmware/govmomi/task"
|
||||
@@ -43,14 +44,63 @@ func NewTask(c *vim25.Client, ref types.ManagedObjectReference) *Task {
|
||||
return &t
|
||||
}
|
||||
|
||||
// Wait waits for a task to complete.
|
||||
// NOTE: This method create a thread-safe PropertyCollector instance per-call, so it is thread safe.
|
||||
// The downside of this approach is the additional resource usage on the vCenter side for each call.
|
||||
func (t *Task) Wait(ctx context.Context) error {
|
||||
_, err := t.WaitForResult(ctx, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *Task) WaitForResult(ctx context.Context, s progress.Sinker) (*types.TaskInfo, error) {
|
||||
// WaitForResult wait for a task to complete.
|
||||
// NOTE: This method create a thread-safe PropertyCollector instance per-call, so it is thread safe.
|
||||
// The downside of this approach is the additional resource usage on the vCenter side for each call.
|
||||
func (t *Task) WaitForResult(ctx context.Context, s ...progress.Sinker) (taskInfo *types.TaskInfo, result error) {
|
||||
var pr progress.Sinker
|
||||
if len(s) == 1 {
|
||||
pr = s[0]
|
||||
}
|
||||
p, err := property.DefaultCollector(t.c).Create(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Attempt to destroy the collector using the background context, as the
|
||||
// specified context may have timed out or have been canceled.
|
||||
defer func() {
|
||||
if err := p.Destroy(context.Background()); err != nil {
|
||||
if result == nil {
|
||||
result = err
|
||||
} else {
|
||||
result = fmt.Errorf(
|
||||
"destroy property collector failed with %s after failing to wait for updates: %w",
|
||||
err,
|
||||
result)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return task.WaitEx(ctx, t.Reference(), p, pr)
|
||||
}
|
||||
|
||||
// WaitEx waits for a task to complete.
|
||||
// NOTE: This method use the same PropertyCollector instance in each call, thus reducing resource usage on the vCenter side.
|
||||
// The downside of this approach is that this method is not thread safe.
|
||||
func (t *Task) WaitEx(ctx context.Context) error {
|
||||
_, err := t.WaitForResultEx(ctx, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// WaitForResultEx waits for a task to complete.
|
||||
// NOTE: This method use the same PropertyCollector instance in each call, thus reducing resource usage on the vCenter side.
|
||||
// The downside of this approach is that this method is not thread safe.
|
||||
func (t *Task) WaitForResultEx(ctx context.Context, s ...progress.Sinker) (*types.TaskInfo, error) {
|
||||
var pr progress.Sinker
|
||||
if len(s) == 1 {
|
||||
pr = s[0]
|
||||
}
|
||||
p := property.DefaultCollector(t.c)
|
||||
return task.Wait(ctx, t.Reference(), p, s)
|
||||
return task.WaitEx(ctx, t.Reference(), p, pr)
|
||||
}
|
||||
|
||||
func (t *Task) Cancel(ctx context.Context) error {
|
||||
@@ -60,3 +110,37 @@ func (t *Task) Cancel(ctx context.Context) error {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// SetState sets task state and optionally sets results or fault, as appropriate for state.
|
||||
func (t *Task) SetState(ctx context.Context, state types.TaskInfoState, result types.AnyType, fault *types.LocalizedMethodFault) error {
|
||||
req := types.SetTaskState{
|
||||
This: t.Reference(),
|
||||
State: state,
|
||||
Result: result,
|
||||
Fault: fault,
|
||||
}
|
||||
_, err := methods.SetTaskState(ctx, t.Common.Client(), &req)
|
||||
return err
|
||||
}
|
||||
|
||||
// SetDescription updates task description to describe the current phase of the task.
|
||||
func (t *Task) SetDescription(ctx context.Context, description types.LocalizableMessage) error {
|
||||
req := types.SetTaskDescription{
|
||||
This: t.Reference(),
|
||||
Description: description,
|
||||
}
|
||||
_, err := methods.SetTaskDescription(ctx, t.Common.Client(), &req)
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateProgress Sets percentage done for this task and recalculates overall percentage done.
|
||||
// If a percentDone value of less than zero or greater than 100 is specified,
|
||||
// a value of zero or 100 respectively is used.
|
||||
func (t *Task) UpdateProgress(ctx context.Context, percentDone int) error {
|
||||
req := types.UpdateProgress{
|
||||
This: t.Reference(),
|
||||
PercentDone: int32(percentDone),
|
||||
}
|
||||
_, err := methods.UpdateProgress(ctx, t.Common.Client(), &req)
|
||||
return err
|
||||
}
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
Copyright (c) 2021 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache 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://www.apache.org/licenses/LICENSE-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.
|
||||
*/
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type TenantManager struct {
|
||||
Common
|
||||
}
|
||||
|
||||
func NewTenantManager(c *vim25.Client) *TenantManager {
|
||||
t := TenantManager{
|
||||
Common: NewCommon(c, *c.ServiceContent.TenantManager),
|
||||
}
|
||||
|
||||
return &t
|
||||
}
|
||||
|
||||
func (t TenantManager) MarkServiceProviderEntities(ctx context.Context, entities []types.ManagedObjectReference) error {
|
||||
req := types.MarkServiceProviderEntities{
|
||||
This: t.Reference(),
|
||||
Entity: entities,
|
||||
}
|
||||
|
||||
_, err := methods.MarkServiceProviderEntities(ctx, t.Client(), &req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t TenantManager) UnmarkServiceProviderEntities(ctx context.Context, entities []types.ManagedObjectReference) error {
|
||||
req := types.UnmarkServiceProviderEntities{
|
||||
This: t.Reference(),
|
||||
Entity: entities,
|
||||
}
|
||||
|
||||
_, err := methods.UnmarkServiceProviderEntities(ctx, t.Client(), &req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t TenantManager) RetrieveServiceProviderEntities(ctx context.Context) ([]types.ManagedObjectReference, error) {
|
||||
req := types.RetrieveServiceProviderEntities{
|
||||
This: t.Reference(),
|
||||
}
|
||||
|
||||
res, err := methods.RetrieveServiceProviderEntities(ctx, t.Client(), &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Returnval, nil
|
||||
}
|
||||
+16
@@ -103,3 +103,19 @@ func (p VirtualApp) Suspend(ctx context.Context) (*Task, error) {
|
||||
|
||||
return NewTask(p.c, res.Returnval), nil
|
||||
}
|
||||
|
||||
func (p VirtualApp) Clone(ctx context.Context, name string, target types.ManagedObjectReference, spec types.VAppCloneSpec) (*Task, error) {
|
||||
req := types.CloneVApp_Task{
|
||||
This: p.Reference(),
|
||||
Name: name,
|
||||
Target: target,
|
||||
Spec: spec,
|
||||
}
|
||||
|
||||
res, err := methods.CloneVApp_Task(ctx, p.c, &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewTask(p.c, res.Returnval), nil
|
||||
}
|
||||
|
||||
+67
-26
@@ -19,6 +19,7 @@ package object
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
@@ -63,11 +64,12 @@ func EthernetCardTypes() VirtualDeviceList {
|
||||
&types.VirtualE1000e{},
|
||||
&types.VirtualVmxnet2{},
|
||||
&types.VirtualVmxnet3{},
|
||||
&types.VirtualVmxnet3Vrdma{},
|
||||
&types.VirtualPCNet32{},
|
||||
&types.VirtualSriovEthernetCard{},
|
||||
}).Select(func(device types.BaseVirtualDevice) bool {
|
||||
c := device.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard()
|
||||
c.GetVirtualDevice().Key = -1
|
||||
c.GetVirtualDevice().Key = VirtualDeviceList{}.newRandomKey()
|
||||
return true
|
||||
})
|
||||
}
|
||||
@@ -134,6 +136,9 @@ func (l VirtualDeviceList) SelectByBackingInfo(backing types.BaseVirtualDeviceBa
|
||||
b := backing.(*types.VirtualEthernetCardDistributedVirtualPortBackingInfo)
|
||||
return a.Port.SwitchUuid == b.Port.SwitchUuid &&
|
||||
a.Port.PortgroupKey == b.Port.PortgroupKey
|
||||
case *types.VirtualEthernetCardOpaqueNetworkBackingInfo:
|
||||
b := backing.(*types.VirtualEthernetCardOpaqueNetworkBackingInfo)
|
||||
return a.OpaqueNetworkId == b.OpaqueNetworkId
|
||||
case *types.VirtualDiskFlatVer2BackingInfo:
|
||||
b := backing.(*types.VirtualDiskFlatVer2BackingInfo)
|
||||
if a.Parent != nil && b.Parent != nil {
|
||||
@@ -146,6 +151,25 @@ func (l VirtualDeviceList) SelectByBackingInfo(backing types.BaseVirtualDeviceBa
|
||||
case types.BaseVirtualDeviceFileBackingInfo:
|
||||
b := backing.(types.BaseVirtualDeviceFileBackingInfo)
|
||||
return a.GetVirtualDeviceFileBackingInfo().FileName == b.GetVirtualDeviceFileBackingInfo().FileName
|
||||
case *types.VirtualPCIPassthroughVmiopBackingInfo:
|
||||
b := backing.(*types.VirtualPCIPassthroughVmiopBackingInfo)
|
||||
return a.Vgpu == b.Vgpu
|
||||
case *types.VirtualPCIPassthroughDynamicBackingInfo:
|
||||
b := backing.(*types.VirtualPCIPassthroughDynamicBackingInfo)
|
||||
if b.CustomLabel != "" && b.CustomLabel != a.CustomLabel {
|
||||
return false
|
||||
}
|
||||
if len(b.AllowedDevice) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, x := range a.AllowedDevice {
|
||||
for _, y := range b.AllowedDevice {
|
||||
if x.DeviceId == y.DeviceId && x.VendorId == y.VendorId {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
@@ -229,8 +253,10 @@ func (l VirtualDeviceList) FindSCSIController(name string) (*types.VirtualSCSICo
|
||||
func (l VirtualDeviceList) CreateSCSIController(name string) (types.BaseVirtualDevice, error) {
|
||||
ctypes := SCSIControllerTypes()
|
||||
|
||||
if name == "scsi" || name == "" {
|
||||
if name == "" || name == "scsi" {
|
||||
name = ctypes.Type(ctypes[0])
|
||||
} else if name == "virtualscsi" {
|
||||
name = "pvscsi" // ovf VirtualSCSI mapping
|
||||
}
|
||||
|
||||
found := ctypes.Select(func(device types.BaseVirtualDevice) bool {
|
||||
@@ -378,9 +404,13 @@ func (l VirtualDeviceList) PickController(kind types.BaseVirtualController) type
|
||||
}
|
||||
|
||||
// newUnitNumber returns the unit number to use for attaching a new device to the given controller.
|
||||
func (l VirtualDeviceList) newUnitNumber(c types.BaseVirtualController) int32 {
|
||||
func (l VirtualDeviceList) newUnitNumber(c types.BaseVirtualController, offset int) int32 {
|
||||
units := make([]bool, 30)
|
||||
|
||||
for i := 0; i < offset; i++ {
|
||||
units[i] = true
|
||||
}
|
||||
|
||||
switch sc := c.(type) {
|
||||
case types.BaseVirtualSCSIController:
|
||||
// The SCSI controller sits on its own bus
|
||||
@@ -429,10 +459,29 @@ func (l VirtualDeviceList) AssignController(device types.BaseVirtualDevice, c ty
|
||||
d := device.GetVirtualDevice()
|
||||
d.ControllerKey = c.GetVirtualController().Key
|
||||
d.UnitNumber = new(int32)
|
||||
*d.UnitNumber = l.newUnitNumber(c)
|
||||
if d.Key == 0 {
|
||||
d.Key = -1
|
||||
|
||||
offset := 0
|
||||
switch device.(type) {
|
||||
case types.BaseVirtualEthernetCard:
|
||||
offset = 7
|
||||
}
|
||||
*d.UnitNumber = l.newUnitNumber(c, offset)
|
||||
|
||||
if d.Key == 0 {
|
||||
d.Key = l.newRandomKey()
|
||||
}
|
||||
}
|
||||
|
||||
// newRandomKey returns a random negative device key.
|
||||
// The generated key can be used for devices you want to add so that it does not collide with existing ones.
|
||||
func (l VirtualDeviceList) newRandomKey() int32 {
|
||||
// NOTE: rand.Uint32 cannot be used here because conversion from uint32 to int32 may change the sign
|
||||
key := rand.Int31() * -1
|
||||
if key == 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
// CreateDisk creates a new VirtualDisk device which can be added to a VM.
|
||||
@@ -862,6 +911,8 @@ func (l VirtualDeviceList) Type(device types.BaseVirtualDevice) string {
|
||||
return "lsilogic-sas"
|
||||
case *types.VirtualNVMEController:
|
||||
return "nvme"
|
||||
case *types.VirtualPrecisionClock:
|
||||
return "clock"
|
||||
default:
|
||||
return l.deviceName(device)
|
||||
}
|
||||
@@ -879,7 +930,13 @@ func (l VirtualDeviceList) Name(device types.BaseVirtualDevice) string {
|
||||
dtype := l.Type(device)
|
||||
switch dtype {
|
||||
case DeviceTypeEthernet:
|
||||
key = fmt.Sprintf("%d", UnitNumber-7)
|
||||
// Ethernet devices of UnitNumber 7-19 are non-SRIOV. Ethernet devices of
|
||||
// UnitNumber 45-36 descending are SRIOV
|
||||
if UnitNumber <= 45 && UnitNumber >= 36 {
|
||||
key = fmt.Sprintf("sriov-%d", 45-UnitNumber)
|
||||
} else {
|
||||
key = fmt.Sprintf("%d", UnitNumber-7)
|
||||
}
|
||||
case DeviceTypeDisk:
|
||||
key = fmt.Sprintf("%d-%d", d.ControllerKey, UnitNumber)
|
||||
default:
|
||||
@@ -907,25 +964,9 @@ func (l VirtualDeviceList) ConfigSpec(op types.VirtualDeviceConfigSpecOperation)
|
||||
var res []types.BaseVirtualDeviceConfigSpec
|
||||
for _, device := range l {
|
||||
config := &types.VirtualDeviceConfigSpec{
|
||||
Device: device,
|
||||
Operation: op,
|
||||
}
|
||||
|
||||
if disk, ok := device.(*types.VirtualDisk); ok {
|
||||
config.FileOperation = fop
|
||||
|
||||
// Special case to attach an existing disk
|
||||
if op == types.VirtualDeviceConfigSpecOperationAdd && disk.CapacityInKB == 0 {
|
||||
childDisk := false
|
||||
if b, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok {
|
||||
childDisk = b.Parent != nil
|
||||
}
|
||||
|
||||
if !childDisk {
|
||||
// Existing disk, clear file operation
|
||||
config.FileOperation = ""
|
||||
}
|
||||
}
|
||||
Device: device,
|
||||
Operation: op,
|
||||
FileOperation: diskFileOperation(op, fop, device),
|
||||
}
|
||||
|
||||
res = append(res, config)
|
||||
|
||||
+291
-47
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
Copyright (c) 2015-2017 VMware, Inc. All Rights Reserved.
|
||||
Copyright (c) 2015-2023 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/govmomi/nfc"
|
||||
"github.com/vmware/govmomi/property"
|
||||
@@ -33,12 +34,41 @@ import (
|
||||
|
||||
const (
|
||||
PropRuntimePowerState = "summary.runtime.powerState"
|
||||
PropConfigTemplate = "summary.config.template"
|
||||
)
|
||||
|
||||
type VirtualMachine struct {
|
||||
Common
|
||||
}
|
||||
|
||||
// extractDiskLayoutFiles is a helper function used to extract file keys for
|
||||
// all disk files attached to the virtual machine at the current point of
|
||||
// running.
|
||||
func extractDiskLayoutFiles(diskLayoutList []types.VirtualMachineFileLayoutExDiskLayout) []int {
|
||||
var result []int
|
||||
|
||||
for _, layoutExDisk := range diskLayoutList {
|
||||
for _, link := range layoutExDisk.Chain {
|
||||
for i := range link.FileKey { // diskDescriptor, diskExtent pairs
|
||||
result = append(result, int(link.FileKey[i]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// removeKey is a helper function for removing a specific file key from a list
|
||||
// of keys associated with disks attached to a virtual machine.
|
||||
func removeKey(l *[]int, key int) {
|
||||
for i, k := range *l {
|
||||
if k == key {
|
||||
*l = append((*l)[:i], (*l)[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewVirtualMachine(c *vim25.Client, ref types.ManagedObjectReference) *VirtualMachine {
|
||||
return &VirtualMachine{
|
||||
Common: NewCommon(c, ref),
|
||||
@@ -56,6 +86,17 @@ func (v VirtualMachine) PowerState(ctx context.Context) (types.VirtualMachinePow
|
||||
return o.Summary.Runtime.PowerState, nil
|
||||
}
|
||||
|
||||
func (v VirtualMachine) IsTemplate(ctx context.Context) (bool, error) {
|
||||
var o mo.VirtualMachine
|
||||
|
||||
err := v.Properties(ctx, v.Reference(), []string{PropConfigTemplate}, &o)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return o.Summary.Config.Template, nil
|
||||
}
|
||||
|
||||
func (v VirtualMachine) PowerOn(ctx context.Context) (*Task, error) {
|
||||
req := types.PowerOnVM_Task{
|
||||
This: v.Reference(),
|
||||
@@ -131,6 +172,15 @@ func (v VirtualMachine) ShutdownGuest(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (v VirtualMachine) StandbyGuest(ctx context.Context) error {
|
||||
req := types.StandbyGuest{
|
||||
This: v.Reference(),
|
||||
}
|
||||
|
||||
_, err := methods.StandbyGuest(ctx, v.c, &req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (v VirtualMachine) RebootGuest(ctx context.Context) error {
|
||||
req := types.RebootGuest{
|
||||
This: v.Reference(),
|
||||
@@ -169,6 +219,20 @@ func (v VirtualMachine) Clone(ctx context.Context, folder *Folder, name string,
|
||||
return NewTask(v.c, res.Returnval), nil
|
||||
}
|
||||
|
||||
func (v VirtualMachine) InstantClone(ctx context.Context, config types.VirtualMachineInstantCloneSpec) (*Task, error) {
|
||||
req := types.InstantClone_Task{
|
||||
This: v.Reference(),
|
||||
Spec: config,
|
||||
}
|
||||
|
||||
res, err := methods.InstantClone_Task(ctx, v.c, &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewTask(v.c, res.Returnval), nil
|
||||
}
|
||||
|
||||
func (v VirtualMachine) Customize(ctx context.Context, spec types.CustomizationSpec) (*Task, error) {
|
||||
req := types.CustomizeVM_Task{
|
||||
This: v.Reference(),
|
||||
@@ -221,7 +285,9 @@ func (v VirtualMachine) RefreshStorageInfo(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (v VirtualMachine) WaitForIP(ctx context.Context) (string, error) {
|
||||
// WaitForIP waits for the VM guest.ipAddress property to report an IP address.
|
||||
// Waits for an IPv4 address if the v4 param is true.
|
||||
func (v VirtualMachine) WaitForIP(ctx context.Context, v4 ...bool) (string, error) {
|
||||
var ip string
|
||||
|
||||
p := property.DefaultCollector(v.c)
|
||||
@@ -238,6 +304,11 @@ func (v VirtualMachine) WaitForIP(ctx context.Context) (string, error) {
|
||||
}
|
||||
|
||||
ip = c.Val.(string)
|
||||
if len(v4) == 1 && v4[0] {
|
||||
if net.ParseIP(ip).To4() == nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -272,7 +343,9 @@ func (v VirtualMachine) WaitForNetIP(ctx context.Context, v4 bool, device ...str
|
||||
devices := VirtualDeviceList(c.Val.(types.ArrayOfVirtualDevice).VirtualDevice)
|
||||
for _, d := range devices {
|
||||
if nic, ok := d.(types.BaseVirtualEthernetCard); ok {
|
||||
mac := nic.GetVirtualEthernetCard().MacAddress
|
||||
// Convert to lower so that e.g. 00:50:56:83:3A:5D is treated the
|
||||
// same as 00:50:56:83:3a:5d
|
||||
mac := strings.ToLower(nic.GetVirtualEthernetCard().MacAddress)
|
||||
if mac == "" {
|
||||
return false
|
||||
}
|
||||
@@ -285,6 +358,10 @@ func (v VirtualMachine) WaitForNetIP(ctx context.Context, v4 bool, device ...str
|
||||
return true
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(device) != 0 {
|
||||
// Only wait for specific NIC(s)
|
||||
macs = make(map[string][]string)
|
||||
@@ -304,7 +381,9 @@ func (v VirtualMachine) WaitForNetIP(ctx context.Context, v4 bool, device ...str
|
||||
|
||||
nics := c.Val.(types.ArrayOfGuestNicInfo).GuestNicInfo
|
||||
for _, nic := range nics {
|
||||
mac := nic.MacAddress
|
||||
// Convert to lower so that e.g. 00:50:56:83:3A:5D is treated the
|
||||
// same as 00:50:56:83:3a:5d
|
||||
mac := strings.ToLower(nic.MacAddress)
|
||||
if mac == "" || nic.IpConfig == nil {
|
||||
continue
|
||||
}
|
||||
@@ -359,6 +438,17 @@ func (v VirtualMachine) Device(ctx context.Context) (VirtualDeviceList, error) {
|
||||
return VirtualDeviceList(o.Config.Hardware.Device), nil
|
||||
}
|
||||
|
||||
func (v VirtualMachine) EnvironmentBrowser(ctx context.Context) (*EnvironmentBrowser, error) {
|
||||
var vm mo.VirtualMachine
|
||||
|
||||
err := v.Properties(ctx, v.Reference(), []string{"environmentBrowser"}, &vm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewEnvironmentBrowser(v.c, vm.EnvironmentBrowser), nil
|
||||
}
|
||||
|
||||
func (v VirtualMachine) HostSystem(ctx context.Context) (*HostSystem, error) {
|
||||
var o mo.VirtualMachine
|
||||
|
||||
@@ -391,29 +481,33 @@ func (v VirtualMachine) ResourcePool(ctx context.Context) (*ResourcePool, error)
|
||||
return NewResourcePool(v.c, *rp), nil
|
||||
}
|
||||
|
||||
func diskFileOperation(op types.VirtualDeviceConfigSpecOperation, fop types.VirtualDeviceConfigSpecFileOperation, device types.BaseVirtualDevice) types.VirtualDeviceConfigSpecFileOperation {
|
||||
if disk, ok := device.(*types.VirtualDisk); ok {
|
||||
// Special case to attach an existing disk
|
||||
if op == types.VirtualDeviceConfigSpecOperationAdd && disk.CapacityInKB == 0 && disk.CapacityInBytes == 0 {
|
||||
childDisk := false
|
||||
if b, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok {
|
||||
childDisk = b.Parent != nil
|
||||
}
|
||||
|
||||
if !childDisk {
|
||||
fop = "" // existing disk
|
||||
}
|
||||
}
|
||||
return fop
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (v VirtualMachine) configureDevice(ctx context.Context, op types.VirtualDeviceConfigSpecOperation, fop types.VirtualDeviceConfigSpecFileOperation, devices ...types.BaseVirtualDevice) error {
|
||||
spec := types.VirtualMachineConfigSpec{}
|
||||
|
||||
for _, device := range devices {
|
||||
config := &types.VirtualDeviceConfigSpec{
|
||||
Device: device,
|
||||
Operation: op,
|
||||
}
|
||||
|
||||
if disk, ok := device.(*types.VirtualDisk); ok {
|
||||
config.FileOperation = fop
|
||||
|
||||
// Special case to attach an existing disk
|
||||
if op == types.VirtualDeviceConfigSpecOperationAdd && disk.CapacityInKB == 0 {
|
||||
childDisk := false
|
||||
if b, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok {
|
||||
childDisk = b.Parent != nil
|
||||
}
|
||||
|
||||
if !childDisk {
|
||||
config.FileOperation = "" // existing disk
|
||||
}
|
||||
}
|
||||
Device: device,
|
||||
Operation: op,
|
||||
FileOperation: diskFileOperation(op, fop, device),
|
||||
}
|
||||
|
||||
spec.DeviceChange = append(spec.DeviceChange, config)
|
||||
@@ -446,6 +540,41 @@ func (v VirtualMachine) RemoveDevice(ctx context.Context, keepFiles bool, device
|
||||
return v.configureDevice(ctx, types.VirtualDeviceConfigSpecOperationRemove, fop, device...)
|
||||
}
|
||||
|
||||
// AttachDisk attaches the given disk to the VirtualMachine
|
||||
func (v VirtualMachine) AttachDisk(ctx context.Context, id string, datastore *Datastore, controllerKey int32, unitNumber int32) error {
|
||||
req := types.AttachDisk_Task{
|
||||
This: v.Reference(),
|
||||
DiskId: types.ID{Id: id},
|
||||
Datastore: datastore.Reference(),
|
||||
ControllerKey: controllerKey,
|
||||
UnitNumber: &unitNumber,
|
||||
}
|
||||
|
||||
res, err := methods.AttachDisk_Task(ctx, v.c, &req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
task := NewTask(v.c, res.Returnval)
|
||||
return task.Wait(ctx)
|
||||
}
|
||||
|
||||
// DetachDisk detaches the given disk from the VirtualMachine
|
||||
func (v VirtualMachine) DetachDisk(ctx context.Context, id string) error {
|
||||
req := types.DetachDisk_Task{
|
||||
This: v.Reference(),
|
||||
DiskId: types.ID{Id: id},
|
||||
}
|
||||
|
||||
res, err := methods.DetachDisk_Task(ctx, v.c, &req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
task := NewTask(v.c, res.Returnval)
|
||||
return task.Wait(ctx)
|
||||
}
|
||||
|
||||
// BootOptions returns the VirtualMachine's config.bootOptions property.
|
||||
func (v VirtualMachine) BootOptions(ctx context.Context) (*types.VirtualMachineBootOptions, error) {
|
||||
var o mo.VirtualMachine
|
||||
@@ -556,6 +685,63 @@ func (m snapshotMap) add(parent string, tree []types.VirtualMachineSnapshotTree)
|
||||
}
|
||||
}
|
||||
|
||||
// SnapshotSize calculates the size of a given snapshot in bytes. If the
|
||||
// snapshot is current, disk files not associated with any parent snapshot are
|
||||
// included in size calculations. This allows for measuring and including the
|
||||
// growth from the last fixed snapshot to the present state.
|
||||
func SnapshotSize(info types.ManagedObjectReference, parent *types.ManagedObjectReference, vmlayout *types.VirtualMachineFileLayoutEx, isCurrent bool) int {
|
||||
var fileKeyList []int
|
||||
var parentFiles []int
|
||||
var allSnapshotFiles []int
|
||||
|
||||
diskFiles := extractDiskLayoutFiles(vmlayout.Disk)
|
||||
|
||||
for _, layout := range vmlayout.Snapshot {
|
||||
diskLayout := extractDiskLayoutFiles(layout.Disk)
|
||||
allSnapshotFiles = append(allSnapshotFiles, diskLayout...)
|
||||
|
||||
if layout.Key.Value == info.Value {
|
||||
fileKeyList = append(fileKeyList, int(layout.DataKey)) // The .vmsn file
|
||||
fileKeyList = append(fileKeyList, diskLayout...) // The .vmdk files
|
||||
} else if parent != nil && layout.Key.Value == parent.Value {
|
||||
parentFiles = append(parentFiles, diskLayout...)
|
||||
}
|
||||
}
|
||||
|
||||
for _, parentFile := range parentFiles {
|
||||
removeKey(&fileKeyList, parentFile)
|
||||
}
|
||||
|
||||
for _, file := range allSnapshotFiles {
|
||||
removeKey(&diskFiles, file)
|
||||
}
|
||||
|
||||
fileKeyMap := make(map[int]types.VirtualMachineFileLayoutExFileInfo)
|
||||
for _, file := range vmlayout.File {
|
||||
fileKeyMap[int(file.Key)] = file
|
||||
}
|
||||
|
||||
size := 0
|
||||
|
||||
for _, fileKey := range fileKeyList {
|
||||
file := fileKeyMap[fileKey]
|
||||
if parent != nil ||
|
||||
(file.Type != string(types.VirtualMachineFileLayoutExFileTypeDiskDescriptor) &&
|
||||
file.Type != string(types.VirtualMachineFileLayoutExFileTypeDiskExtent)) {
|
||||
size += int(file.Size)
|
||||
}
|
||||
}
|
||||
|
||||
if isCurrent {
|
||||
for _, diskFile := range diskFiles {
|
||||
file := fileKeyMap[diskFile]
|
||||
size += int(file.Size)
|
||||
}
|
||||
}
|
||||
|
||||
return size
|
||||
}
|
||||
|
||||
// FindSnapshot supports snapshot lookup by name, where name can be:
|
||||
// 1) snapshot ManagedObjectReference.Value (unique)
|
||||
// 2) snapshot name (may not be unique)
|
||||
@@ -569,7 +755,7 @@ func (v VirtualMachine) FindSnapshot(ctx context.Context, name string) (*types.M
|
||||
}
|
||||
|
||||
if o.Snapshot == nil || len(o.Snapshot.RootSnapshotList) == 0 {
|
||||
return nil, errors.New("No snapshots for this VM")
|
||||
return nil, errors.New("no snapshots for this VM")
|
||||
}
|
||||
|
||||
m := make(snapshotMap)
|
||||
@@ -743,27 +929,6 @@ func (v VirtualMachine) Unregister(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// QueryEnvironmentBrowser is a helper to get the environmentBrowser property.
|
||||
func (v VirtualMachine) QueryConfigTarget(ctx context.Context) (*types.ConfigTarget, error) {
|
||||
var vm mo.VirtualMachine
|
||||
|
||||
err := v.Properties(ctx, v.Reference(), []string{"environmentBrowser"}, &vm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := types.QueryConfigTarget{
|
||||
This: vm.EnvironmentBrowser,
|
||||
}
|
||||
|
||||
res, err := methods.QueryConfigTarget(ctx, v.Client(), &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Returnval, nil
|
||||
}
|
||||
|
||||
func (v VirtualMachine) MountToolsInstaller(ctx context.Context) error {
|
||||
req := types.MountToolsInstaller{
|
||||
This: v.Reference(),
|
||||
@@ -832,6 +997,85 @@ func (v VirtualMachine) UUID(ctx context.Context) string {
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return o.Config.Uuid
|
||||
if o.Config != nil {
|
||||
return o.Config.Uuid
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (v VirtualMachine) QueryChangedDiskAreas(ctx context.Context, baseSnapshot, curSnapshot *types.ManagedObjectReference, disk *types.VirtualDisk, offset int64) (types.DiskChangeInfo, error) {
|
||||
var noChange types.DiskChangeInfo
|
||||
var err error
|
||||
|
||||
if offset > disk.CapacityInBytes {
|
||||
return noChange, fmt.Errorf("offset is greater than the disk size (%#x and %#x)", offset, disk.CapacityInBytes)
|
||||
} else if offset == disk.CapacityInBytes {
|
||||
return types.DiskChangeInfo{StartOffset: offset, Length: 0}, nil
|
||||
}
|
||||
|
||||
var b mo.VirtualMachineSnapshot
|
||||
err = v.Properties(ctx, baseSnapshot.Reference(), []string{"config.hardware"}, &b)
|
||||
if err != nil {
|
||||
return noChange, fmt.Errorf("failed to fetch config.hardware of snapshot %s: %s", baseSnapshot, err)
|
||||
}
|
||||
|
||||
var changeId *string
|
||||
for _, vd := range b.Config.Hardware.Device {
|
||||
d := vd.GetVirtualDevice()
|
||||
if d.Key != disk.Key {
|
||||
continue
|
||||
}
|
||||
|
||||
// As per VDDK programming guide, these are the four types of disks
|
||||
// that support CBT, see "Gathering Changed Block Information".
|
||||
if b, ok := d.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok {
|
||||
changeId = &b.ChangeId
|
||||
break
|
||||
}
|
||||
if b, ok := d.Backing.(*types.VirtualDiskSparseVer2BackingInfo); ok {
|
||||
changeId = &b.ChangeId
|
||||
break
|
||||
}
|
||||
if b, ok := d.Backing.(*types.VirtualDiskRawDiskMappingVer1BackingInfo); ok {
|
||||
changeId = &b.ChangeId
|
||||
break
|
||||
}
|
||||
if b, ok := d.Backing.(*types.VirtualDiskRawDiskVer2BackingInfo); ok {
|
||||
changeId = &b.ChangeId
|
||||
break
|
||||
}
|
||||
|
||||
return noChange, fmt.Errorf("disk %d has backing info without .ChangeId: %t", disk.Key, d.Backing)
|
||||
}
|
||||
if changeId == nil || *changeId == "" {
|
||||
return noChange, fmt.Errorf("CBT is not enabled on disk %d", disk.Key)
|
||||
}
|
||||
|
||||
req := types.QueryChangedDiskAreas{
|
||||
This: v.Reference(),
|
||||
Snapshot: curSnapshot,
|
||||
DeviceKey: disk.Key,
|
||||
StartOffset: offset,
|
||||
ChangeId: *changeId,
|
||||
}
|
||||
|
||||
res, err := methods.QueryChangedDiskAreas(ctx, v.Client(), &req)
|
||||
if err != nil {
|
||||
return noChange, err
|
||||
}
|
||||
|
||||
return res.Returnval, nil
|
||||
}
|
||||
|
||||
// ExportSnapshot exports all VMDK-files up to (but not including) a specified snapshot. This
|
||||
// is useful when exporting a running VM.
|
||||
func (v *VirtualMachine) ExportSnapshot(ctx context.Context, snapshot *types.ManagedObjectReference) (*nfc.Lease, error) {
|
||||
req := types.ExportSnapshot{
|
||||
This: *snapshot,
|
||||
}
|
||||
resp, err := methods.ExportSnapshot(ctx, v.Client(), &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nfc.NewLease(v.c, resp.Returnval), nil
|
||||
}
|
||||
|
||||
+4
@@ -19,3 +19,7 @@ package object
|
||||
type VmwareDistributedVirtualSwitch struct {
|
||||
DistributedVirtualSwitch
|
||||
}
|
||||
|
||||
func (s VmwareDistributedVirtualSwitch) GetInventoryPath() string {
|
||||
return s.InventoryPath
|
||||
}
|
||||
|
||||
+50
@@ -16,6 +16,10 @@ limitations under the License.
|
||||
|
||||
package ovf
|
||||
|
||||
import (
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
/*
|
||||
Source: http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.24.0/CIM_VirtualSystemSettingData.xsd
|
||||
*/
|
||||
@@ -76,3 +80,49 @@ type CIMResourceAllocationSettingData struct {
|
||||
VirtualQuantityUnits *string `xml:"VirtualQuantityUnits"`
|
||||
Weight *uint `xml:"Weight"`
|
||||
}
|
||||
|
||||
/*
|
||||
Source: http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.24.0/CIM_StorageAllocationSettingData.xsd
|
||||
*/
|
||||
type CIMStorageAllocationSettingData struct {
|
||||
ElementName string `xml:"ElementName"`
|
||||
InstanceID string `xml:"InstanceID"`
|
||||
|
||||
ResourceType *uint16 `xml:"ResourceType"`
|
||||
OtherResourceType *string `xml:"OtherResourceType"`
|
||||
ResourceSubType *string `xml:"ResourceSubType"`
|
||||
|
||||
Access *uint16 `xml:"Access"`
|
||||
Address *string `xml:"Address"`
|
||||
AddressOnParent *string `xml:"AddressOnParent"`
|
||||
AllocationUnits *string `xml:"AllocationUnits"`
|
||||
AutomaticAllocation *bool `xml:"AutomaticAllocation"`
|
||||
AutomaticDeallocation *bool `xml:"AutomaticDeallocation"`
|
||||
Caption *string `xml:"Caption"`
|
||||
ChangeableType *uint16 `xml:"ChangeableType"`
|
||||
ComponentSetting []types.AnyType `xml:"ComponentSetting"`
|
||||
ConfigurationName *string `xml:"ConfigurationName"`
|
||||
Connection []string `xml:"Connection"`
|
||||
ConsumerVisibility *uint16 `xml:"ConsumerVisibility"`
|
||||
Description *string `xml:"Description"`
|
||||
Generation *uint64 `xml:"Generation"`
|
||||
HostExtentName *string `xml:"HostExtentName"`
|
||||
HostExtentNameFormat *uint16 `xml:"HostExtentNameFormat"`
|
||||
HostExtentNameNamespace *uint16 `xml:"HostExtentNameNamespace"`
|
||||
HostExtentStartingAddress *uint64 `xml:"HostExtentStartingAddress"`
|
||||
HostResource []string `xml:"HostResource"`
|
||||
HostResourceBlockSize *uint64 `xml:"HostResourceBlockSize"`
|
||||
Limit *uint64 `xml:"Limit"`
|
||||
MappingBehavior *uint `xml:"MappingBehavior"`
|
||||
OtherHostExtentNameFormat *string `xml:"OtherHostExtentNameFormat"`
|
||||
OtherHostExtentNameNamespace *string `xml:"OtherHostExtentNameNamespace"`
|
||||
Parent *string `xml:"Parent"`
|
||||
PoolID *string `xml:"PoolID"`
|
||||
Reservation *uint64 `xml:"Reservation"`
|
||||
SoID *string `xml:"SoID"`
|
||||
SoOrgID *string `xml:"SoOrgID"`
|
||||
VirtualQuantity *uint `xml:"VirtualQuantity"`
|
||||
VirtualQuantityUnits *string `xml:"VirtualQuantityUnits"`
|
||||
VirtualResourceBlockSize *uint64 `xml:"VirtualResourceBlockSize"`
|
||||
Weight *uint `xml:"Weight"`
|
||||
}
|
||||
|
||||
+3
-3
@@ -87,13 +87,13 @@ func (e Env) MarshalManual() string {
|
||||
buffer.WriteString(fmt.Sprintf(ovfEnvHeader, e.EsxID))
|
||||
buffer.WriteString(fmt.Sprintf(ovfEnvPlatformSection, e.Platform.Kind, e.Platform.Version, e.Platform.Vendor, e.Platform.Locale))
|
||||
|
||||
buffer.WriteString(fmt.Sprintf(ovfEnvPropertyHeader))
|
||||
buffer.WriteString(fmt.Sprint(ovfEnvPropertyHeader))
|
||||
for _, p := range e.Property.Properties {
|
||||
buffer.WriteString(fmt.Sprintf(ovfEnvPropertyEntry, p.Key, p.Value))
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(ovfEnvPropertyFooter))
|
||||
buffer.WriteString(fmt.Sprint(ovfEnvPropertyFooter))
|
||||
|
||||
buffer.WriteString(fmt.Sprintf(ovfEnvFooter))
|
||||
buffer.WriteString(fmt.Sprint(ovfEnvFooter))
|
||||
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
+40
-4
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
|
||||
Copyright (c) 2015-2023 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -16,6 +16,10 @@ limitations under the License.
|
||||
|
||||
package ovf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Envelope struct {
|
||||
References []File `xml:"References>File"`
|
||||
|
||||
@@ -61,6 +65,7 @@ type Content struct {
|
||||
type Section struct {
|
||||
Required *bool `xml:"required,attr"`
|
||||
Info string `xml:"Info"`
|
||||
Category string `xml:"Category"`
|
||||
}
|
||||
|
||||
type AnnotationSection struct {
|
||||
@@ -85,6 +90,20 @@ type ProductSection struct {
|
||||
Property []Property `xml:"Property"`
|
||||
}
|
||||
|
||||
func (p ProductSection) Key(prop Property) string {
|
||||
// From OVF spec, section 9.5.1:
|
||||
// key-value-env = [class-value "."] key-value-prod ["." instance-value]
|
||||
|
||||
k := prop.Key
|
||||
if p.Class != nil {
|
||||
k = fmt.Sprintf("%s.%s", *p.Class, k)
|
||||
}
|
||||
if p.Instance != nil {
|
||||
k = fmt.Sprintf("%s.%s", k, *p.Instance)
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
type Property struct {
|
||||
Key string `xml:"key,attr"`
|
||||
Type string `xml:"type,attr"`
|
||||
@@ -148,14 +167,23 @@ type EulaSection struct {
|
||||
License string `xml:"License"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Required *bool `xml:"required,attr"`
|
||||
Key string `xml:"key,attr"`
|
||||
Value string `xml:"value,attr"`
|
||||
}
|
||||
|
||||
type VirtualHardwareSection struct {
|
||||
Section
|
||||
|
||||
ID *string `xml:"id,attr"`
|
||||
Transport *string `xml:"transport,attr"`
|
||||
|
||||
System *VirtualSystemSettingData `xml:"System"`
|
||||
Item []ResourceAllocationSettingData `xml:"Item"`
|
||||
System *VirtualSystemSettingData `xml:"System"`
|
||||
Item []ResourceAllocationSettingData `xml:"Item"`
|
||||
StorageItem []StorageAllocationSettingData `xml:"StorageItem"`
|
||||
Config []Config `xml:"Config"`
|
||||
ExtraConfig []Config `xml:"ExtraConfig"`
|
||||
}
|
||||
|
||||
type VirtualSystemSettingData struct {
|
||||
@@ -170,6 +198,14 @@ type ResourceAllocationSettingData struct {
|
||||
Bound *string `xml:"bound,attr"`
|
||||
}
|
||||
|
||||
type StorageAllocationSettingData struct {
|
||||
CIMStorageAllocationSettingData
|
||||
|
||||
Required *bool `xml:"required,attr"`
|
||||
Configuration *string `xml:"configuration,attr"`
|
||||
Bound *string `xml:"bound,attr"`
|
||||
}
|
||||
|
||||
type ResourceAllocationSection struct {
|
||||
Section
|
||||
|
||||
|
||||
+90
-13
@@ -32,6 +32,17 @@ import (
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// Intervals maps name to seconds for the built-in historical intervals
|
||||
Intervals = map[string]int32{
|
||||
"real": 0, // 0 == default 20s interval
|
||||
"day": 300,
|
||||
"week": 1800,
|
||||
"month": 7200,
|
||||
"year": 86400,
|
||||
}
|
||||
)
|
||||
|
||||
// Manager wraps mo.PerformanceManager.
|
||||
type Manager struct {
|
||||
object.Common
|
||||
@@ -194,10 +205,18 @@ func (d groupPerfCounterInfo) Len() int {
|
||||
func (d groupPerfCounterInfo) Less(i, j int) bool {
|
||||
ci := d.ids[i].CounterId
|
||||
cj := d.ids[j].CounterId
|
||||
gi := d.info[ci].GroupInfo.GetElementDescription()
|
||||
gj := d.info[cj].GroupInfo.GetElementDescription()
|
||||
|
||||
return gi.Key < gj.Key
|
||||
giKey := "-"
|
||||
gjKey := "-"
|
||||
|
||||
if gi, ok := d.info[ci]; ok {
|
||||
giKey = gi.GroupInfo.GetElementDescription().Key
|
||||
}
|
||||
if gj, ok := d.info[cj]; ok {
|
||||
gjKey = gj.GroupInfo.GetElementDescription().Key
|
||||
}
|
||||
|
||||
return giKey < gjKey
|
||||
}
|
||||
|
||||
func (d groupPerfCounterInfo) Swap(i, j int) {
|
||||
@@ -260,6 +279,21 @@ func (m *Manager) Query(ctx context.Context, spec []types.PerfQuerySpec) ([]type
|
||||
return res.Returnval, nil
|
||||
}
|
||||
|
||||
// QueryCounter wraps the QueryPerfCounter method.
|
||||
func (m *Manager) QueryCounter(ctx context.Context, ids []int32) ([]types.PerfCounterInfo, error) {
|
||||
req := types.QueryPerfCounter{
|
||||
This: m.Reference(),
|
||||
CounterId: ids,
|
||||
}
|
||||
|
||||
res, err := methods.QueryPerfCounter(ctx, m.Client(), &req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Returnval, nil
|
||||
}
|
||||
|
||||
// SampleByName uses the spec param as a template, constructing a []types.PerfQuerySpec for the given metrics and entities
|
||||
// and invoking the Query method.
|
||||
// The spec template can specify instances using the MetricId.Instance field, by default all instances are collected.
|
||||
@@ -297,7 +331,10 @@ func (m *Manager) SampleByName(ctx context.Context, spec types.PerfQuerySpec, me
|
||||
spec.MaxSample = 1
|
||||
}
|
||||
|
||||
truncate := false
|
||||
|
||||
if spec.IntervalId >= 60 && spec.StartTime == nil {
|
||||
truncate = true
|
||||
// Need a StartTime to make use of history
|
||||
now, err := methods.GetCurrentTime(ctx, m.Client())
|
||||
if err != nil {
|
||||
@@ -305,7 +342,7 @@ func (m *Manager) SampleByName(ctx context.Context, spec types.PerfQuerySpec, me
|
||||
}
|
||||
|
||||
// Go back in time
|
||||
x := spec.IntervalId * -1 * spec.MaxSample
|
||||
x := spec.IntervalId * -1 * (spec.MaxSample * 2)
|
||||
t := now.Add(time.Duration(x) * time.Second)
|
||||
spec.StartTime = &t
|
||||
}
|
||||
@@ -317,15 +354,51 @@ func (m *Manager) SampleByName(ctx context.Context, spec types.PerfQuerySpec, me
|
||||
query = append(query, spec)
|
||||
}
|
||||
|
||||
return m.Query(ctx, query)
|
||||
series, err := m.Query(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if truncate {
|
||||
// Going back in time with IntervalId * MaxSample isn't always far enough,
|
||||
// depending on when the historical data is saved in vCenter.
|
||||
// So we go back twice as far and truncate here if needed.
|
||||
for i := range series {
|
||||
s, ok := series[i].(*types.PerfEntityMetric)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
n := len(s.SampleInfo)
|
||||
diff := n - int(spec.MaxSample)
|
||||
if diff > 0 {
|
||||
s.SampleInfo = s.SampleInfo[diff:]
|
||||
}
|
||||
|
||||
for j := range s.Value {
|
||||
v, ok := s.Value[j].(*types.PerfMetricIntSeries)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
n = len(v.Value)
|
||||
diff = n - int(spec.MaxSample)
|
||||
if diff > 0 {
|
||||
v.Value = v.Value[diff:]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return series, nil
|
||||
}
|
||||
|
||||
// MetricSeries contains the same data as types.PerfMetricIntSeries, but with the CounterId converted to Name.
|
||||
type MetricSeries struct {
|
||||
Name string
|
||||
Name string `json:"name"`
|
||||
unit string
|
||||
Instance string
|
||||
Value []int64
|
||||
Instance string `json:"instance"`
|
||||
Value []int64 `json:"value"`
|
||||
}
|
||||
|
||||
func (s *MetricSeries) Format(val int64) string {
|
||||
@@ -350,10 +423,10 @@ func (s *MetricSeries) ValueCSV() string {
|
||||
|
||||
// EntityMetric contains the same data as types.PerfEntityMetric, but with MetricSeries type for the Value field.
|
||||
type EntityMetric struct {
|
||||
Entity types.ManagedObjectReference
|
||||
Entity types.ManagedObjectReference `json:"entity"`
|
||||
|
||||
SampleInfo []types.PerfSampleInfo
|
||||
Value []MetricSeries
|
||||
SampleInfo []types.PerfSampleInfo `json:"sampleInfo"`
|
||||
Value []MetricSeries `json:"value"`
|
||||
}
|
||||
|
||||
// SampleInfoCSV converts the SampleInfo field to a CSV string
|
||||
@@ -390,10 +463,14 @@ func (m *Manager) ToMetricSeries(ctx context.Context, series []types.BasePerfEnt
|
||||
|
||||
for j := range s.Value {
|
||||
v := s.Value[j].(*types.PerfMetricIntSeries)
|
||||
info, ok := counters[v.Id.CounterId]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
values = append(values, MetricSeries{
|
||||
Name: counters[v.Id.CounterId].Name(),
|
||||
unit: counters[v.Id.CounterId].UnitInfo.GetElementDescription().Key,
|
||||
Name: info.Name(),
|
||||
unit: info.UnitInfo.GetElementDescription().Key,
|
||||
Instance: v.Id.Instance,
|
||||
Value: v.Value,
|
||||
})
|
||||
|
||||
+4
-1
@@ -1,3 +1,6 @@
|
||||
# Copyright (c) 2021 VMware, Inc. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
ifneq (,$(strip $(GOOS)))
|
||||
ifeq (,$(strip $(GOARCH)))
|
||||
GOARCH := $(shell go env | grep GOARCH | awk -F= '{print $$2}' | tr -d '"')
|
||||
@@ -30,7 +33,7 @@ $(PROGRAM):
|
||||
CGO_ENABLED=0 go build -a $(BUILD_ARGS) -o $@
|
||||
|
||||
install:
|
||||
CGO_ENABLED=0 go install $(BUILD_ARGS)
|
||||
CGO_ENABLED=0 go install -v $(BUILD_ARGS)
|
||||
|
||||
ifneq (,$(strip $(BUILD_OS)))
|
||||
ifneq (,$(strip $(BUILD_ARCH)))
|
||||
|
||||
+138
-18
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
|
||||
Copyright (c) 2015-2024 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -19,6 +19,8 @@ package property
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
@@ -27,12 +29,19 @@ import (
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
// ErrConcurrentCollector is returned from WaitForUpdates, WaitForUpdatesEx,
|
||||
// or CheckForUpdates if any of those calls are unable to obtain an exclusive
|
||||
// lock for the property collector.
|
||||
var ErrConcurrentCollector = fmt.Errorf(
|
||||
"only one goroutine may invoke WaitForUpdates, WaitForUpdatesEx, " +
|
||||
"or CheckForUpdates on a given PropertyCollector")
|
||||
|
||||
// Collector models the PropertyCollector managed object.
|
||||
//
|
||||
// For more information, see:
|
||||
// http://pubs.vmware.com/vsphere-60/index.jsp?topic=%2Fcom.vmware.wssdk.apiref.doc%2Fvmodl.query.PropertyCollector.html
|
||||
//
|
||||
type Collector struct {
|
||||
mu sync.Mutex
|
||||
roundTripper soap.RoundTripper
|
||||
reference types.ManagedObjectReference
|
||||
}
|
||||
@@ -47,7 +56,7 @@ func DefaultCollector(c *vim25.Client) *Collector {
|
||||
return &p
|
||||
}
|
||||
|
||||
func (p Collector) Reference() types.ManagedObjectReference {
|
||||
func (p *Collector) Reference() types.ManagedObjectReference {
|
||||
return p.reference
|
||||
}
|
||||
|
||||
@@ -86,21 +95,37 @@ func (p *Collector) Destroy(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Collector) CreateFilter(ctx context.Context, req types.CreateFilter) error {
|
||||
func (p *Collector) CreateFilter(ctx context.Context, req types.CreateFilter) (*Filter, error) {
|
||||
req.This = p.Reference()
|
||||
|
||||
_, err := methods.CreateFilter(ctx, p.roundTripper, &req)
|
||||
resp, err := methods.CreateFilter(ctx, p.roundTripper, &req)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil
|
||||
return &Filter{roundTripper: p.roundTripper, reference: resp.Returnval}, nil
|
||||
}
|
||||
|
||||
func (p *Collector) WaitForUpdates(ctx context.Context, v string) (*types.UpdateSet, error) {
|
||||
// Deprecated: Please use WaitForUpdatesEx instead.
|
||||
func (p *Collector) WaitForUpdates(
|
||||
ctx context.Context,
|
||||
version string,
|
||||
opts ...*types.WaitOptions) (*types.UpdateSet, error) {
|
||||
|
||||
if !p.mu.TryLock() {
|
||||
return nil, ErrConcurrentCollector
|
||||
}
|
||||
defer p.mu.Unlock()
|
||||
|
||||
req := types.WaitForUpdatesEx{
|
||||
This: p.Reference(),
|
||||
Version: v,
|
||||
Version: version,
|
||||
}
|
||||
|
||||
if len(opts) == 1 {
|
||||
req.Options = opts[0]
|
||||
} else if len(opts) > 1 {
|
||||
panic("only one option may be specified")
|
||||
}
|
||||
|
||||
res, err := methods.WaitForUpdatesEx(ctx, p.roundTripper, &req)
|
||||
@@ -117,9 +142,29 @@ func (p *Collector) CancelWaitForUpdates(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *Collector) RetrieveProperties(ctx context.Context, req types.RetrieveProperties) (*types.RetrievePropertiesResponse, error) {
|
||||
req.This = p.Reference()
|
||||
return methods.RetrieveProperties(ctx, p.roundTripper, &req)
|
||||
// RetrieveProperties wraps RetrievePropertiesEx and ContinueRetrievePropertiesEx to collect properties in batches.
|
||||
func (p *Collector) RetrieveProperties(
|
||||
ctx context.Context,
|
||||
req types.RetrieveProperties,
|
||||
maxObjectsArgs ...int32) (*types.RetrievePropertiesResponse, error) {
|
||||
|
||||
var opts types.RetrieveOptions
|
||||
if l := len(maxObjectsArgs); l > 1 {
|
||||
return nil, fmt.Errorf("maxObjectsArgs accepts a single value")
|
||||
} else if l == 1 {
|
||||
opts.MaxObjects = maxObjectsArgs[0]
|
||||
}
|
||||
|
||||
objects, err := mo.RetrievePropertiesEx(ctx, p.roundTripper, types.RetrievePropertiesEx{
|
||||
This: p.Reference(),
|
||||
SpecSet: req.SpecSet,
|
||||
Options: opts,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.RetrievePropertiesResponse{Returnval: objects}, nil
|
||||
}
|
||||
|
||||
// Retrieve loads properties for a slice of managed objects. The dst argument
|
||||
@@ -143,7 +188,7 @@ func (p *Collector) Retrieve(ctx context.Context, objs []types.ManagedObjectRefe
|
||||
spec := types.PropertySpec{
|
||||
Type: obj.Type,
|
||||
}
|
||||
if ps == nil {
|
||||
if len(ps) == 0 {
|
||||
spec.All = types.NewBool(true)
|
||||
} else {
|
||||
spec.PathSet = ps
|
||||
@@ -179,11 +224,18 @@ func (p *Collector) Retrieve(ctx context.Context, objs []types.ManagedObjectRefe
|
||||
return nil
|
||||
}
|
||||
|
||||
return mo.LoadRetrievePropertiesResponse(res, dst)
|
||||
return mo.LoadObjectContent(res.Returnval, dst)
|
||||
}
|
||||
|
||||
// RetrieveWithFilter populates dst as Retrieve does, but only for entities matching the given filter.
|
||||
func (p *Collector) RetrieveWithFilter(ctx context.Context, objs []types.ManagedObjectReference, ps []string, dst interface{}, filter Filter) error {
|
||||
// RetrieveWithFilter populates dst as Retrieve does, but only for entities
|
||||
// that match the specified filter.
|
||||
func (p *Collector) RetrieveWithFilter(
|
||||
ctx context.Context,
|
||||
objs []types.ManagedObjectReference,
|
||||
ps []string,
|
||||
dst interface{},
|
||||
filter Match) error {
|
||||
|
||||
if len(filter) == 0 {
|
||||
return p.Retrieve(ctx, objs, ps, dst)
|
||||
}
|
||||
@@ -195,7 +247,7 @@ func (p *Collector) RetrieveWithFilter(ctx context.Context, objs []types.Managed
|
||||
return err
|
||||
}
|
||||
|
||||
objs = filter.MatchObjectContent(content)
|
||||
objs = filter.ObjectContent(content)
|
||||
|
||||
if len(objs) == 0 {
|
||||
return nil
|
||||
@@ -209,3 +261,71 @@ func (p *Collector) RetrieveOne(ctx context.Context, obj types.ManagedObjectRefe
|
||||
var objs = []types.ManagedObjectReference{obj}
|
||||
return p.Retrieve(ctx, objs, ps, dst)
|
||||
}
|
||||
|
||||
// WaitForUpdatesEx waits for any of the specified properties of the specified
|
||||
// managed object to change. It calls the specified function for every update it
|
||||
// receives. If this function returns false, it continues waiting for
|
||||
// subsequent updates. If this function returns true, it stops waiting and
|
||||
// returns.
|
||||
//
|
||||
// If the Context is canceled, a call to CancelWaitForUpdates() is made and its
|
||||
// error value is returned.
|
||||
//
|
||||
// By default, ObjectUpdate.MissingSet faults are not propagated to the returned
|
||||
// error, set WaitFilter.PropagateMissing=true to enable MissingSet fault
|
||||
// propagation.
|
||||
func (p *Collector) WaitForUpdatesEx(
|
||||
ctx context.Context,
|
||||
opts WaitOptions,
|
||||
onUpdatesFn func([]types.ObjectUpdate) bool) error {
|
||||
|
||||
if !p.mu.TryLock() {
|
||||
return ErrConcurrentCollector
|
||||
}
|
||||
defer p.mu.Unlock()
|
||||
|
||||
req := types.WaitForUpdatesEx{
|
||||
This: p.Reference(),
|
||||
Options: opts.Options,
|
||||
}
|
||||
|
||||
for {
|
||||
res, err := methods.WaitForUpdatesEx(ctx, p.roundTripper, &req)
|
||||
if err != nil {
|
||||
if ctx.Err() == context.Canceled {
|
||||
return p.CancelWaitForUpdates(context.Background())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
set := res.Returnval
|
||||
if set == nil {
|
||||
if req.Options != nil && req.Options.MaxWaitSeconds != nil {
|
||||
return nil // WaitOptions.MaxWaitSeconds exceeded
|
||||
}
|
||||
// Retry if the result came back empty
|
||||
continue
|
||||
}
|
||||
|
||||
req.Version = set.Version
|
||||
opts.Truncated = false
|
||||
if set.Truncated != nil {
|
||||
opts.Truncated = *set.Truncated
|
||||
}
|
||||
|
||||
for _, fs := range set.FilterSet {
|
||||
if opts.PropagateMissing {
|
||||
for i := range fs.ObjectSet {
|
||||
for _, p := range fs.ObjectSet[i].MissingSet {
|
||||
// Same behavior as mo.ObjectContentToType()
|
||||
return soap.WrapVimFault(p.Fault.Fault)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if onUpdatesFn(fs.ObjectSet) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+26
-111
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||
Copyright (c) 2017-2024 VMware, Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -17,123 +17,38 @@ limitations under the License.
|
||||
package property
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"context"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
// Filter provides methods for matching against types.DynamicProperty
|
||||
type Filter map[string]types.AnyType
|
||||
|
||||
// Keys returns the Filter map keys as a []string
|
||||
func (f Filter) Keys() []string {
|
||||
keys := make([]string, 0, len(f))
|
||||
|
||||
for key := range f {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
return keys
|
||||
// Filter models the Filter managed object.
|
||||
//
|
||||
// For more information, see:
|
||||
// https://vdc-download.vmware.com/vmwb-repository/dcr-public/184bb3ba-6fa8-4574-a767-d0c96e2a38f4/ba9422ef-405c-47dd-8553-e11b619185b2/SDK/vsphere-ws/docs/ReferenceGuide/vmodl.query.PropertyCollector.Filter.html.
|
||||
type Filter struct {
|
||||
roundTripper soap.RoundTripper
|
||||
reference types.ManagedObjectReference
|
||||
}
|
||||
|
||||
// MatchProperty returns true if a Filter entry matches the given prop.
|
||||
func (f Filter) MatchProperty(prop types.DynamicProperty) bool {
|
||||
match, ok := f[prop.Name]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if match == prop.Val {
|
||||
return true
|
||||
}
|
||||
|
||||
ptype := reflect.TypeOf(prop.Val)
|
||||
|
||||
if strings.HasPrefix(ptype.Name(), "ArrayOf") {
|
||||
pval := reflect.ValueOf(prop.Val).Field(0)
|
||||
|
||||
for i := 0; i < pval.Len(); i++ {
|
||||
prop.Val = pval.Index(i).Interface()
|
||||
|
||||
if f.MatchProperty(prop) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
if reflect.TypeOf(match) != ptype {
|
||||
s, ok := match.(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// convert if we can
|
||||
switch prop.Val.(type) {
|
||||
case bool:
|
||||
match, _ = strconv.ParseBool(s)
|
||||
case int16:
|
||||
x, _ := strconv.ParseInt(s, 10, 16)
|
||||
match = int16(x)
|
||||
case int32:
|
||||
x, _ := strconv.ParseInt(s, 10, 32)
|
||||
match = int32(x)
|
||||
case int64:
|
||||
match, _ = strconv.ParseInt(s, 10, 64)
|
||||
case float32:
|
||||
x, _ := strconv.ParseFloat(s, 32)
|
||||
match = float32(x)
|
||||
case float64:
|
||||
match, _ = strconv.ParseFloat(s, 64)
|
||||
case fmt.Stringer:
|
||||
prop.Val = prop.Val.(fmt.Stringer).String()
|
||||
default:
|
||||
if ptype.Kind() != reflect.String {
|
||||
return false
|
||||
}
|
||||
// An enum type we can convert to a string type
|
||||
prop.Val = reflect.ValueOf(prop.Val).String()
|
||||
}
|
||||
}
|
||||
|
||||
switch pval := prop.Val.(type) {
|
||||
case string:
|
||||
s := match.(string)
|
||||
if s == "*" {
|
||||
return true // TODO: path.Match fails if s contains a '/'
|
||||
}
|
||||
m, _ := path.Match(s, pval)
|
||||
return m
|
||||
default:
|
||||
return reflect.DeepEqual(match, pval)
|
||||
}
|
||||
func (f Filter) Reference() types.ManagedObjectReference {
|
||||
return f.reference
|
||||
}
|
||||
|
||||
// MatchPropertyList returns true if all given props match the Filter.
|
||||
func (f Filter) MatchPropertyList(props []types.DynamicProperty) bool {
|
||||
for _, p := range props {
|
||||
if !f.MatchProperty(p) {
|
||||
return false
|
||||
}
|
||||
// Destroy destroys this filter.
|
||||
//
|
||||
// This operation can be called explicitly, or it can take place implicitly when
|
||||
// the session that created the filter is closed.
|
||||
func (f *Filter) Destroy(ctx context.Context) error {
|
||||
if _, err := methods.DestroyPropertyFilter(
|
||||
ctx,
|
||||
f.roundTripper,
|
||||
&types.DestroyPropertyFilter{This: f.Reference()}); err != nil {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return len(f) == len(props) // false if a property such as VM "guest" is unset
|
||||
}
|
||||
|
||||
// MatchObjectContent returns a list of ObjectContent.Obj where the ObjectContent.PropSet matches the Filter.
|
||||
func (f Filter) MatchObjectContent(objects []types.ObjectContent) []types.ManagedObjectReference {
|
||||
var refs []types.ManagedObjectReference
|
||||
|
||||
for _, o := range objects {
|
||||
if f.MatchPropertyList(o.PropSet) {
|
||||
refs = append(refs, o.Obj)
|
||||
}
|
||||
}
|
||||
|
||||
return refs
|
||||
f.reference = types.ManagedObjectReference{}
|
||||
return nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user