mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Merge pull request #5172 from hashicorp/tpounds-fix-go-vet-warnings
Fix go vet warnings
This commit is contained in:
commit
21c8814390
4
Godeps/Godeps.json
generated
4
Godeps/Godeps.json
generated
@ -599,6 +599,10 @@
|
||||
"ImportPath": "github.com/mitchellh/go-linereader",
|
||||
"Rev": "07bab5fdd9580500aea6ada0e09df4aa28e68abd"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mitchellh/hashstructure",
|
||||
"Rev": "6b17d669fac5e2f71c16658d781ec3fdd3802b69"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mitchellh/mapstructure",
|
||||
"Rev": "281073eb9eb092240d33ef253c404f1cca550309"
|
||||
|
4
Makefile
4
Makefile
@ -1,5 +1,5 @@
|
||||
TEST?=$$(GO15VENDOREXPERIMENT=1 go list ./... | grep -v /vendor/)
|
||||
VETARGS?=-asmdecl -atomic -bool -buildtags -copylocks -methods -nilfunc -printf -rangeloops -shift -structtags -unsafeptr
|
||||
VETARGS?=-asmdecl -atomic -bool -buildtags -composites -copylocks -methods -nilfunc -printf -rangeloops -shift -structtags -unreachable -unsafeptr -unusedresult
|
||||
|
||||
default: test
|
||||
|
||||
@ -75,7 +75,7 @@ vet:
|
||||
go get golang.org/x/tools/cmd/vet; \
|
||||
fi
|
||||
@echo "go tool vet $(VETARGS) ."
|
||||
@GO15VENDOREXPERIMENT=1 go tool vet $(VETARGS) . ; if [ $$? -eq 1 ]; then \
|
||||
@GO15VENDOREXPERIMENT=1 go tool vet $(VETARGS) $$(ls -d */ | grep -v vendor) ; if [ $$? -eq 1 ]; then \
|
||||
echo ""; \
|
||||
echo "Vet found suspicious constructs. Please check the reported constructs"; \
|
||||
echo "and fix them if necessary before submitting the code for review."; \
|
||||
|
@ -266,7 +266,7 @@ func testAccCheckCloudTrailLogValidationEnabled(n string, desired bool, trail *c
|
||||
|
||||
if *trail.LogFileValidationEnabled != desired {
|
||||
return fmt.Errorf("Expected log validation status %t, given %t", desired,
|
||||
trail.LogFileValidationEnabled)
|
||||
*trail.LogFileValidationEnabled)
|
||||
}
|
||||
|
||||
// local state comparison
|
||||
@ -277,8 +277,7 @@ func testAccCheckCloudTrailLogValidationEnabled(n string, desired bool, trail *c
|
||||
}
|
||||
desiredInString := fmt.Sprintf("%t", desired)
|
||||
if enabled != desiredInString {
|
||||
return fmt.Errorf("Expected log validation status %t, saved %t", desiredInString,
|
||||
enabled)
|
||||
return fmt.Errorf("Expected log validation status %s, saved %s", desiredInString, enabled)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -18,7 +18,7 @@ func TestAccAWSGroupMembership_basic(t *testing.T) {
|
||||
|
||||
rString := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
|
||||
configBase := fmt.Sprintf(testAccAWSGroupMemberConfig, rString, rString, rString)
|
||||
configUpdate := fmt.Sprintf(testAccAWSGroupMemberConfigUpdate, rString, rString, rString, rString)
|
||||
configUpdate := fmt.Sprintf(testAccAWSGroupMemberConfigUpdate, rString, rString, rString, rString, rString)
|
||||
configUpdateDown := fmt.Sprintf(testAccAWSGroupMemberConfigUpdateDown, rString, rString, rString)
|
||||
|
||||
testUser := fmt.Sprintf("test-user-%s", rString)
|
||||
@ -115,7 +115,7 @@ func testAccCheckAWSGroupMembershipExists(n string, g *iam.GetGroupOutput) resou
|
||||
func testAccCheckAWSGroupMembershipAttributes(group *iam.GetGroupOutput, users []string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
if !strings.Contains(*group.Group.GroupName, "test-group") {
|
||||
return fmt.Errorf("Bad group membership: expected %d, got %d", "test-group", *group.Group.GroupName)
|
||||
return fmt.Errorf("Bad group membership: expected %s, got %s", "test-group", *group.Group.GroupName)
|
||||
}
|
||||
|
||||
uc := len(users)
|
||||
|
@ -81,7 +81,7 @@ func testAccCheckAzureSqlDatabaseServerDeleted(s *terraform.State) error {
|
||||
|
||||
for _, srv := range servers.DatabaseServers {
|
||||
if srv.Name == resource.Primary.ID {
|
||||
fmt.Errorf("SQL Server %s still exists.", resource.Primary.ID)
|
||||
return fmt.Errorf("SQL Server %s still exists.", resource.Primary.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ func testAccCheckAzureSqlDatabaseServiceDeleted(s *terraform.State) error {
|
||||
|
||||
for _, srv := range dbs.ServiceResources {
|
||||
if srv.Name == resource.Primary.ID {
|
||||
fmt.Errorf("SQL Service %s still exists.", resource.Primary.ID)
|
||||
return fmt.Errorf("SQL Service %s still exists.", resource.Primary.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ func testAccCheckDigitalOceanFloatingIPDestroy(s *terraform.State) error {
|
||||
_, _, err := client.FloatingIPs.Get(rs.Primary.ID)
|
||||
|
||||
if err == nil {
|
||||
fmt.Errorf("Floating IP still exists")
|
||||
return fmt.Errorf("Floating IP still exists")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ func testAccCheckDigitalOceanSSHKeyDestroy(s *terraform.State) error {
|
||||
_, _, err = client.Keys.GetByID(id)
|
||||
|
||||
if err == nil {
|
||||
fmt.Errorf("SSH key still exists")
|
||||
return fmt.Errorf("SSH key still exists")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ func testAccCheckPubsubSubscriptionDestroy(s *terraform.State) error {
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
_, err := config.clientPubsub.Projects.Subscriptions.Get(rs.Primary.ID).Do()
|
||||
if err != nil {
|
||||
fmt.Errorf("Subscription still present")
|
||||
return fmt.Errorf("Subscription still present")
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ func testAccPubsubSubscriptionExists(n string) resource.TestCheckFunc {
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
_, err := config.clientPubsub.Projects.Subscriptions.Get(rs.Primary.ID).Do()
|
||||
if err != nil {
|
||||
fmt.Errorf("Subscription still present")
|
||||
return fmt.Errorf("Subscription still present")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -36,7 +36,7 @@ func testAccCheckPubsubTopicDestroy(s *terraform.State) error {
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
_, err := config.clientPubsub.Projects.Topics.Get(rs.Primary.ID).Do()
|
||||
if err != nil {
|
||||
fmt.Errorf("Topic still present")
|
||||
return fmt.Errorf("Topic still present")
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ func testAccPubsubTopicExists(n string) resource.TestCheckFunc {
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
_, err := config.clientPubsub.Projects.Topics.Get(rs.Primary.ID).Do()
|
||||
if err != nil {
|
||||
fmt.Errorf("Topic still present")
|
||||
return fmt.Errorf("Topic still present")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -43,7 +43,7 @@ func resourceHerokuDomainCreate(d *schema.ResourceData, meta interface{}) error
|
||||
|
||||
log.Printf("[DEBUG] Domain create configuration: %#v, %#v", app, hostname)
|
||||
|
||||
do, err := client.DomainCreate(app, heroku.DomainCreateOpts{hostname})
|
||||
do, err := client.DomainCreate(app, heroku.DomainCreateOpts{Hostname: hostname})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ func resourceHerokuDrainCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
var dr *heroku.LogDrain
|
||||
err := resource.Retry(2*time.Minute, func() error {
|
||||
d, err := client.LogDrainCreate(app, heroku.LogDrainCreateOpts{url})
|
||||
d, err := client.LogDrainCreate(app, heroku.LogDrainCreateOpts{URL: url})
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), retryableError) {
|
||||
return err
|
||||
|
@ -374,8 +374,8 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e
|
||||
|
||||
if keyName, ok := d.Get("key_pair").(string); ok && keyName != "" {
|
||||
createOpts = &keypairs.CreateOptsExt{
|
||||
createOpts,
|
||||
keyName,
|
||||
CreateOptsBuilder: createOpts,
|
||||
KeyName: keyName,
|
||||
}
|
||||
}
|
||||
|
||||
@ -384,8 +384,8 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e
|
||||
blockDeviceRaw := v.(map[string]interface{})
|
||||
blockDevice := resourceInstanceBlockDeviceV2(d, blockDeviceRaw)
|
||||
createOpts = &bootfromvolume.CreateOptsExt{
|
||||
createOpts,
|
||||
blockDevice,
|
||||
CreateOptsBuilder: createOpts,
|
||||
BlockDevice: blockDevice,
|
||||
}
|
||||
log.Printf("[DEBUG] Create BFV Options: %+v", createOpts)
|
||||
}
|
||||
@ -396,8 +396,8 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e
|
||||
log.Printf("[DEBUG] schedulerhints: %+v", schedulerHintsRaw)
|
||||
schedulerHints := resourceInstanceSchedulerHintsV2(d, schedulerHintsRaw[0].(map[string]interface{}))
|
||||
createOpts = &schedulerhints.CreateOptsExt{
|
||||
createOpts,
|
||||
schedulerHints,
|
||||
CreateOptsBuilder: createOpts,
|
||||
SchedulerHints: schedulerHints,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,6 @@ func testAccTestCheckDestroy(test *statuscake.Test) resource.TestCheckFunc {
|
||||
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const testAccTestConfig_basic = `
|
||||
|
@ -107,7 +107,7 @@ func CreateCertRequest(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
certReqBytes, err := x509.CreateCertificateRequest(rand.Reader, &certReq, key)
|
||||
if err != nil {
|
||||
fmt.Errorf("Error creating certificate request: %s", err)
|
||||
return fmt.Errorf("Error creating certificate request: %s", err)
|
||||
}
|
||||
certReqPem := string(pem.EncodeToMemory(&pem.Block{Type: pemCertReqType, Bytes: certReqBytes}))
|
||||
|
||||
|
@ -159,7 +159,7 @@ func createCertificate(d *schema.ResourceData, template, parent *x509.Certificat
|
||||
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, template, parent, pub, priv)
|
||||
if err != nil {
|
||||
fmt.Errorf("error creating certificate: %s", err)
|
||||
return fmt.Errorf("error creating certificate: %s", err)
|
||||
}
|
||||
certPem := string(pem.EncodeToMemory(&pem.Block{Type: pemCertType, Bytes: certBytes}))
|
||||
|
||||
|
@ -648,8 +648,8 @@ func buildNetworkDevice(f *find.Finder, label, adapterType string) (*types.Virtu
|
||||
return &types.VirtualDeviceConfigSpec{
|
||||
Operation: types.VirtualDeviceConfigSpecOperationAdd,
|
||||
Device: &types.VirtualVmxnet3{
|
||||
types.VirtualVmxnet{
|
||||
types.VirtualEthernetCard{
|
||||
VirtualVmxnet: types.VirtualVmxnet{
|
||||
VirtualEthernetCard: types.VirtualEthernetCard{
|
||||
VirtualDevice: types.VirtualDevice{
|
||||
Key: -1,
|
||||
Backing: backing,
|
||||
@ -663,7 +663,7 @@ func buildNetworkDevice(f *find.Finder, label, adapterType string) (*types.Virtu
|
||||
return &types.VirtualDeviceConfigSpec{
|
||||
Operation: types.VirtualDeviceConfigSpecOperationAdd,
|
||||
Device: &types.VirtualE1000{
|
||||
types.VirtualEthernetCard{
|
||||
VirtualEthernetCard: types.VirtualEthernetCard{
|
||||
VirtualDevice: types.VirtualDevice{
|
||||
Key: -1,
|
||||
Backing: backing,
|
||||
@ -922,7 +922,7 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
|
||||
|
||||
if d.Type == "StoragePod" {
|
||||
sp := object.StoragePod{
|
||||
object.NewFolder(c.Client, d),
|
||||
Folder: object.NewFolder(c.Client, d),
|
||||
}
|
||||
sps := buildStoragePlacementSpecCreate(dcFolders, resourcePool, sp, configSpec)
|
||||
datastore, err = findDatastore(c, sps)
|
||||
@ -1054,7 +1054,7 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
|
||||
|
||||
if d.Type == "StoragePod" {
|
||||
sp := object.StoragePod{
|
||||
object.NewFolder(c.Client, d),
|
||||
Folder: object.NewFolder(c.Client, d),
|
||||
}
|
||||
sps := buildStoragePlacementSpecClone(c, dcFolders, template, resourcePool, sp)
|
||||
datastore, err = findDatastore(c, sps)
|
||||
|
@ -217,8 +217,6 @@ func (c *AtlasClient) Delete() error {
|
||||
"HTTP error: %d\n\nBody: %s",
|
||||
resp.StatusCode, c.readBody(resp.Body))
|
||||
}
|
||||
|
||||
return fmt.Errorf("Unexpected HTTP response code %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
func (c *AtlasClient) readBody(b io.Reader) string {
|
||||
|
@ -33,7 +33,10 @@ func consulFactory(conf map[string]string) (Client, error) {
|
||||
} else {
|
||||
username = auth
|
||||
}
|
||||
config.HttpAuth = &consulapi.HttpBasicAuth{username, password}
|
||||
config.HttpAuth = &consulapi.HttpBasicAuth{
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
}
|
||||
|
||||
client, err := consulapi.NewClient(config)
|
||||
|
@ -795,9 +795,6 @@ func TestReadWriteState(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
// Checksum before the write
|
||||
chksum := checksumStruct(t, state)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := WriteState(state, buf); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
@ -808,12 +805,6 @@ func TestReadWriteState(t *testing.T) {
|
||||
t.Fatalf("bad version number: %d", state.Version)
|
||||
}
|
||||
|
||||
// Checksum after the write
|
||||
chksumAfter := checksumStruct(t, state)
|
||||
if chksumAfter != chksum {
|
||||
t.Fatalf("structure changed during serialization!")
|
||||
}
|
||||
|
||||
actual, err := ReadState(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
|
@ -8,6 +8,8 @@ import (
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/mitchellh/hashstructure"
|
||||
)
|
||||
|
||||
func TestReadWriteStateV1(t *testing.T) {
|
||||
@ -25,7 +27,10 @@ func TestReadWriteStateV1(t *testing.T) {
|
||||
}
|
||||
|
||||
// Checksum before the write
|
||||
chksum := checksumStruct(t, state)
|
||||
chksum, err := hashstructure.Hash(state, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("hash: %s", err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := testWriteStateV1(state, buf); err != nil {
|
||||
@ -33,7 +38,11 @@ func TestReadWriteStateV1(t *testing.T) {
|
||||
}
|
||||
|
||||
// Checksum after the write
|
||||
chksumAfter := checksumStruct(t, state)
|
||||
chksumAfter, err := hashstructure.Hash(state, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("hash: %s", err)
|
||||
}
|
||||
|
||||
if chksumAfter != chksum {
|
||||
t.Fatalf("structure changed during serialization!")
|
||||
}
|
||||
|
@ -1,10 +1,6 @@
|
||||
package terraform
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/gob"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@ -21,21 +17,6 @@ import (
|
||||
// This is the directory where our test fixtures are.
|
||||
const fixtureDir = "./test-fixtures"
|
||||
|
||||
func checksumStruct(t *testing.T, i interface{}) string {
|
||||
// TODO(mitchellh): write a library to do this because gob is not
|
||||
// deterministic in order
|
||||
return "foo"
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
enc := gob.NewEncoder(buf)
|
||||
if err := enc.Encode(i); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
sum := sha1.Sum(buf.Bytes())
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func tempDir(t *testing.T) string {
|
||||
dir, err := ioutil.TempDir("", "tf")
|
||||
if err != nil {
|
||||
|
21
vendor/github.com/mitchellh/hashstructure/LICENSE
generated
vendored
Normal file
21
vendor/github.com/mitchellh/hashstructure/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Mitchell Hashimoto
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
61
vendor/github.com/mitchellh/hashstructure/README.md
generated
vendored
Normal file
61
vendor/github.com/mitchellh/hashstructure/README.md
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
# hashstructure
|
||||
|
||||
hashstructure is a Go library for creating a unique hash value
|
||||
for arbitrary values in Go.
|
||||
|
||||
This can be used to key values in a hash (for use in a map, set, etc.)
|
||||
that are complex. The most common use case is comparing two values without
|
||||
sending data across the network, caching values locally (de-dup), and so on.
|
||||
|
||||
## Features
|
||||
|
||||
* Hash any arbitrary Go value, including complex types.
|
||||
|
||||
* Tag a struct field to ignore it and not affect the hash value.
|
||||
|
||||
* Tag a slice type struct field to treat it as a set where ordering
|
||||
doesn't affect the hash code but the field itself is still taken into
|
||||
account to create the hash value.
|
||||
|
||||
* Optionally specify a custom hash function to optimize for speed, collision
|
||||
avoidance for your data set, etc.
|
||||
|
||||
## Installation
|
||||
|
||||
Standard `go get`:
|
||||
|
||||
```
|
||||
$ go get github.com/mitchellh/hashstructure
|
||||
```
|
||||
|
||||
## Usage & Example
|
||||
|
||||
For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/hashstructure).
|
||||
|
||||
A quick code example is shown below:
|
||||
|
||||
|
||||
type ComplexStruct struct {
|
||||
Name string
|
||||
Age uint
|
||||
Metadata map[string]interface{}
|
||||
}
|
||||
|
||||
v := ComplexStruct{
|
||||
Name: "mitchellh",
|
||||
Age: 64,
|
||||
Metadata: map[string]interface{}{
|
||||
"car": true,
|
||||
"location": "California",
|
||||
"siblings": []string{"Bob", "John"},
|
||||
},
|
||||
}
|
||||
|
||||
hash, err := hashstructure.Hash(v, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("%d", hash)
|
||||
// Output:
|
||||
// 2307517237273902113
|
323
vendor/github.com/mitchellh/hashstructure/hashstructure.go
generated
vendored
Normal file
323
vendor/github.com/mitchellh/hashstructure/hashstructure.go
generated
vendored
Normal file
@ -0,0 +1,323 @@
|
||||
package hashstructure
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash"
|
||||
"hash/fnv"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// HashOptions are options that are available for hashing.
|
||||
type HashOptions struct {
|
||||
// Hasher is the hash function to use. If this isn't set, it will
|
||||
// default to FNV.
|
||||
Hasher hash.Hash64
|
||||
|
||||
// TagName is the struct tag to look at when hashing the structure.
|
||||
// By default this is "hash".
|
||||
TagName string
|
||||
}
|
||||
|
||||
// Hash returns the hash value of an arbitrary value.
|
||||
//
|
||||
// If opts is nil, then default options will be used. See HashOptions
|
||||
// for the default values.
|
||||
//
|
||||
// Notes on the value:
|
||||
//
|
||||
// * Unexported fields on structs are ignored and do not affect the
|
||||
// hash value.
|
||||
//
|
||||
// * Adding an exported field to a struct with the zero value will change
|
||||
// the hash value.
|
||||
//
|
||||
// For structs, the hashing can be controlled using tags. For example:
|
||||
//
|
||||
// struct {
|
||||
// Name string
|
||||
// UUID string `hash:"ignore"`
|
||||
// }
|
||||
//
|
||||
// The available tag values are:
|
||||
//
|
||||
// * "ignore" - The field will be ignored and not affect the hash code.
|
||||
//
|
||||
// * "set" - The field will be treated as a set, where ordering doesn't
|
||||
// affect the hash code. This only works for slices.
|
||||
//
|
||||
func Hash(v interface{}, opts *HashOptions) (uint64, error) {
|
||||
// Create default options
|
||||
if opts == nil {
|
||||
opts = &HashOptions{}
|
||||
}
|
||||
if opts.Hasher == nil {
|
||||
opts.Hasher = fnv.New64()
|
||||
}
|
||||
if opts.TagName == "" {
|
||||
opts.TagName = "hash"
|
||||
}
|
||||
|
||||
// Reset the hash
|
||||
opts.Hasher.Reset()
|
||||
|
||||
// Create our walker and walk the structure
|
||||
w := &walker{
|
||||
h: opts.Hasher,
|
||||
tag: opts.TagName,
|
||||
}
|
||||
return w.visit(reflect.ValueOf(v), nil)
|
||||
}
|
||||
|
||||
type walker struct {
|
||||
h hash.Hash64
|
||||
tag string
|
||||
}
|
||||
|
||||
type visitOpts struct {
|
||||
// Flags are a bitmask of flags to affect behavior of this visit
|
||||
Flags visitFlag
|
||||
|
||||
// Information about the struct containing this field
|
||||
Struct interface{}
|
||||
StructField string
|
||||
}
|
||||
|
||||
func (w *walker) visit(v reflect.Value, opts *visitOpts) (uint64, error) {
|
||||
// Loop since these can be wrapped in multiple layers of pointers
|
||||
// and interfaces.
|
||||
for {
|
||||
// If we have an interface, dereference it. We have to do this up
|
||||
// here because it might be a nil in there and the check below must
|
||||
// catch that.
|
||||
if v.Kind() == reflect.Interface {
|
||||
v = v.Elem()
|
||||
continue
|
||||
}
|
||||
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = reflect.Indirect(v)
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
// If it is nil, treat it like a zero.
|
||||
if !v.IsValid() {
|
||||
var tmp int8
|
||||
v = reflect.ValueOf(tmp)
|
||||
}
|
||||
|
||||
// Binary writing can use raw ints, we have to convert to
|
||||
// a sized-int, we'll choose the largest...
|
||||
switch v.Kind() {
|
||||
case reflect.Int:
|
||||
v = reflect.ValueOf(int64(v.Int()))
|
||||
case reflect.Uint:
|
||||
v = reflect.ValueOf(uint64(v.Uint()))
|
||||
case reflect.Bool:
|
||||
var tmp int8
|
||||
if v.Bool() {
|
||||
tmp = 1
|
||||
}
|
||||
v = reflect.ValueOf(tmp)
|
||||
}
|
||||
|
||||
k := v.Kind()
|
||||
|
||||
// We can shortcut numeric values by directly binary writing them
|
||||
if k >= reflect.Int && k <= reflect.Complex64 {
|
||||
// A direct hash calculation
|
||||
w.h.Reset()
|
||||
err := binary.Write(w.h, binary.LittleEndian, v.Interface())
|
||||
return w.h.Sum64(), err
|
||||
}
|
||||
|
||||
switch k {
|
||||
case reflect.Array:
|
||||
var h uint64
|
||||
l := v.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
current, err := w.visit(v.Index(i), nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
h = hashUpdateOrdered(w.h, h, current)
|
||||
}
|
||||
|
||||
return h, nil
|
||||
|
||||
case reflect.Map:
|
||||
var includeMap IncludableMap
|
||||
if opts != nil && opts.Struct != nil {
|
||||
if v, ok := opts.Struct.(IncludableMap); ok {
|
||||
includeMap = v
|
||||
}
|
||||
}
|
||||
|
||||
// Build the hash for the map. We do this by XOR-ing all the key
|
||||
// and value hashes. This makes it deterministic despite ordering.
|
||||
var h uint64
|
||||
for _, k := range v.MapKeys() {
|
||||
v := v.MapIndex(k)
|
||||
if includeMap != nil {
|
||||
incl, err := includeMap.HashIncludeMap(
|
||||
opts.StructField, k.Interface(), v.Interface())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !incl {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
kh, err := w.visit(k, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
vh, err := w.visit(v, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
fieldHash := hashUpdateOrdered(w.h, kh, vh)
|
||||
h = hashUpdateUnordered(h, fieldHash)
|
||||
}
|
||||
|
||||
return h, nil
|
||||
|
||||
case reflect.Struct:
|
||||
var include Includable
|
||||
parent := v.Interface()
|
||||
if impl, ok := parent.(Includable); ok {
|
||||
include = impl
|
||||
}
|
||||
|
||||
t := v.Type()
|
||||
h, err := w.visit(reflect.ValueOf(t.Name()), nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
l := v.NumField()
|
||||
for i := 0; i < l; i++ {
|
||||
if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" {
|
||||
var f visitFlag
|
||||
fieldType := t.Field(i)
|
||||
if fieldType.PkgPath != "" {
|
||||
// Unexported
|
||||
continue
|
||||
}
|
||||
|
||||
tag := fieldType.Tag.Get(w.tag)
|
||||
if tag == "ignore" {
|
||||
// Ignore this field
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if we implement includable and check it
|
||||
if include != nil {
|
||||
incl, err := include.HashInclude(fieldType.Name, v)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !incl {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case "set":
|
||||
f |= visitFlagSet
|
||||
}
|
||||
|
||||
kh, err := w.visit(reflect.ValueOf(fieldType.Name), nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
vh, err := w.visit(v, &visitOpts{
|
||||
Flags: f,
|
||||
Struct: parent,
|
||||
StructField: fieldType.Name,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
fieldHash := hashUpdateOrdered(w.h, kh, vh)
|
||||
h = hashUpdateUnordered(h, fieldHash)
|
||||
}
|
||||
}
|
||||
|
||||
return h, nil
|
||||
|
||||
case reflect.Slice:
|
||||
// We have two behaviors here. If it isn't a set, then we just
|
||||
// visit all the elements. If it is a set, then we do a deterministic
|
||||
// hash code.
|
||||
var h uint64
|
||||
var set bool
|
||||
if opts != nil {
|
||||
set = (opts.Flags & visitFlagSet) != 0
|
||||
}
|
||||
l := v.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
current, err := w.visit(v.Index(i), nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if set {
|
||||
h = hashUpdateUnordered(h, current)
|
||||
} else {
|
||||
h = hashUpdateOrdered(w.h, h, current)
|
||||
}
|
||||
}
|
||||
|
||||
return h, nil
|
||||
|
||||
case reflect.String:
|
||||
// Directly hash
|
||||
w.h.Reset()
|
||||
_, err := w.h.Write([]byte(v.String()))
|
||||
return w.h.Sum64(), err
|
||||
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown kind to hash: %s", k)
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func hashUpdateOrdered(h hash.Hash64, a, b uint64) uint64 {
|
||||
// For ordered updates, use a real hash function
|
||||
h.Reset()
|
||||
|
||||
// We just panic if the binary writes fail because we are writing
|
||||
// an int64 which should never be fail-able.
|
||||
e1 := binary.Write(h, binary.LittleEndian, a)
|
||||
e2 := binary.Write(h, binary.LittleEndian, b)
|
||||
if e1 != nil {
|
||||
panic(e1)
|
||||
}
|
||||
if e2 != nil {
|
||||
panic(e2)
|
||||
}
|
||||
|
||||
return h.Sum64()
|
||||
}
|
||||
|
||||
func hashUpdateUnordered(a, b uint64) uint64 {
|
||||
return a ^ b
|
||||
}
|
||||
|
||||
// visitFlag is used as a bitmask for affecting visit behavior
|
||||
type visitFlag uint
|
||||
|
||||
const (
|
||||
visitFlagInvalid visitFlag = iota
|
||||
visitFlagSet = iota << 1
|
||||
)
|
15
vendor/github.com/mitchellh/hashstructure/include.go
generated
vendored
Normal file
15
vendor/github.com/mitchellh/hashstructure/include.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
package hashstructure
|
||||
|
||||
// Includable is an interface that can optionally be implemented by
|
||||
// a struct. It will be called for each field in the struct to check whether
|
||||
// it should be included in the hash.
|
||||
type Includable interface {
|
||||
HashInclude(field string, v interface{}) (bool, error)
|
||||
}
|
||||
|
||||
// IncludableMap is an interface that can optionally be implemented by
|
||||
// a struct. It will be called when a map-type field is found to ask the
|
||||
// struct if the map item should be included in the hash.
|
||||
type IncludableMap interface {
|
||||
HashIncludeMap(field string, k, v interface{}) (bool, error)
|
||||
}
|
Loading…
Reference in New Issue
Block a user