Merge pull request #5172 from hashicorp/tpounds-fix-go-vet-warnings
Fix go vet warnings
This commit is contained in:
commit
21c8814390
|
@ -599,6 +599,10 @@
|
||||||
"ImportPath": "github.com/mitchellh/go-linereader",
|
"ImportPath": "github.com/mitchellh/go-linereader",
|
||||||
"Rev": "07bab5fdd9580500aea6ada0e09df4aa28e68abd"
|
"Rev": "07bab5fdd9580500aea6ada0e09df4aa28e68abd"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/mitchellh/hashstructure",
|
||||||
|
"Rev": "6b17d669fac5e2f71c16658d781ec3fdd3802b69"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mitchellh/mapstructure",
|
"ImportPath": "github.com/mitchellh/mapstructure",
|
||||||
"Rev": "281073eb9eb092240d33ef253c404f1cca550309"
|
"Rev": "281073eb9eb092240d33ef253c404f1cca550309"
|
||||||
|
|
4
Makefile
4
Makefile
|
@ -1,5 +1,5 @@
|
||||||
TEST?=$$(GO15VENDOREXPERIMENT=1 go list ./... | grep -v /vendor/)
|
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
|
default: test
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ vet:
|
||||||
go get golang.org/x/tools/cmd/vet; \
|
go get golang.org/x/tools/cmd/vet; \
|
||||||
fi
|
fi
|
||||||
@echo "go tool vet $(VETARGS) ."
|
@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 ""; \
|
||||||
echo "Vet found suspicious constructs. Please check the reported constructs"; \
|
echo "Vet found suspicious constructs. Please check the reported constructs"; \
|
||||||
echo "and fix them if necessary before submitting the code for review."; \
|
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 {
|
if *trail.LogFileValidationEnabled != desired {
|
||||||
return fmt.Errorf("Expected log validation status %t, given %t", desired,
|
return fmt.Errorf("Expected log validation status %t, given %t", desired,
|
||||||
trail.LogFileValidationEnabled)
|
*trail.LogFileValidationEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
// local state comparison
|
// local state comparison
|
||||||
|
@ -277,8 +277,7 @@ func testAccCheckCloudTrailLogValidationEnabled(n string, desired bool, trail *c
|
||||||
}
|
}
|
||||||
desiredInString := fmt.Sprintf("%t", desired)
|
desiredInString := fmt.Sprintf("%t", desired)
|
||||||
if enabled != desiredInString {
|
if enabled != desiredInString {
|
||||||
return fmt.Errorf("Expected log validation status %t, saved %t", desiredInString,
|
return fmt.Errorf("Expected log validation status %s, saved %s", desiredInString, enabled)
|
||||||
enabled)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -18,7 +18,7 @@ func TestAccAWSGroupMembership_basic(t *testing.T) {
|
||||||
|
|
||||||
rString := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
|
rString := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
|
||||||
configBase := fmt.Sprintf(testAccAWSGroupMemberConfig, rString, rString, rString)
|
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)
|
configUpdateDown := fmt.Sprintf(testAccAWSGroupMemberConfigUpdateDown, rString, rString, rString)
|
||||||
|
|
||||||
testUser := fmt.Sprintf("test-user-%s", 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 {
|
func testAccCheckAWSGroupMembershipAttributes(group *iam.GetGroupOutput, users []string) resource.TestCheckFunc {
|
||||||
return func(s *terraform.State) error {
|
return func(s *terraform.State) error {
|
||||||
if !strings.Contains(*group.Group.GroupName, "test-group") {
|
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)
|
uc := len(users)
|
||||||
|
|
|
@ -81,7 +81,7 @@ func testAccCheckAzureSqlDatabaseServerDeleted(s *terraform.State) error {
|
||||||
|
|
||||||
for _, srv := range servers.DatabaseServers {
|
for _, srv := range servers.DatabaseServers {
|
||||||
if srv.Name == resource.Primary.ID {
|
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 {
|
for _, srv := range dbs.ServiceResources {
|
||||||
if srv.Name == resource.Primary.ID {
|
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)
|
_, _, err := client.FloatingIPs.Get(rs.Primary.ID)
|
||||||
|
|
||||||
if err == nil {
|
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)
|
_, _, err = client.Keys.GetByID(id)
|
||||||
|
|
||||||
if err == nil {
|
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)
|
config := testAccProvider.Meta().(*Config)
|
||||||
_, err := config.clientPubsub.Projects.Subscriptions.Get(rs.Primary.ID).Do()
|
_, err := config.clientPubsub.Projects.Subscriptions.Get(rs.Primary.ID).Do()
|
||||||
if err != nil {
|
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)
|
config := testAccProvider.Meta().(*Config)
|
||||||
_, err := config.clientPubsub.Projects.Subscriptions.Get(rs.Primary.ID).Do()
|
_, err := config.clientPubsub.Projects.Subscriptions.Get(rs.Primary.ID).Do()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Errorf("Subscription still present")
|
return fmt.Errorf("Subscription still present")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -36,7 +36,7 @@ func testAccCheckPubsubTopicDestroy(s *terraform.State) error {
|
||||||
config := testAccProvider.Meta().(*Config)
|
config := testAccProvider.Meta().(*Config)
|
||||||
_, err := config.clientPubsub.Projects.Topics.Get(rs.Primary.ID).Do()
|
_, err := config.clientPubsub.Projects.Topics.Get(rs.Primary.ID).Do()
|
||||||
if err != nil {
|
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)
|
config := testAccProvider.Meta().(*Config)
|
||||||
_, err := config.clientPubsub.Projects.Topics.Get(rs.Primary.ID).Do()
|
_, err := config.clientPubsub.Projects.Topics.Get(rs.Primary.ID).Do()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Errorf("Topic still present")
|
return fmt.Errorf("Topic still present")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -43,7 +43,7 @@ func resourceHerokuDomainCreate(d *schema.ResourceData, meta interface{}) error
|
||||||
|
|
||||||
log.Printf("[DEBUG] Domain create configuration: %#v, %#v", app, hostname)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ func resourceHerokuDrainCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
||||||
var dr *heroku.LogDrain
|
var dr *heroku.LogDrain
|
||||||
err := resource.Retry(2*time.Minute, func() error {
|
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 err != nil {
|
||||||
if strings.Contains(err.Error(), retryableError) {
|
if strings.Contains(err.Error(), retryableError) {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -374,8 +374,8 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e
|
||||||
|
|
||||||
if keyName, ok := d.Get("key_pair").(string); ok && keyName != "" {
|
if keyName, ok := d.Get("key_pair").(string); ok && keyName != "" {
|
||||||
createOpts = &keypairs.CreateOptsExt{
|
createOpts = &keypairs.CreateOptsExt{
|
||||||
createOpts,
|
CreateOptsBuilder: createOpts,
|
||||||
keyName,
|
KeyName: keyName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,8 +384,8 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e
|
||||||
blockDeviceRaw := v.(map[string]interface{})
|
blockDeviceRaw := v.(map[string]interface{})
|
||||||
blockDevice := resourceInstanceBlockDeviceV2(d, blockDeviceRaw)
|
blockDevice := resourceInstanceBlockDeviceV2(d, blockDeviceRaw)
|
||||||
createOpts = &bootfromvolume.CreateOptsExt{
|
createOpts = &bootfromvolume.CreateOptsExt{
|
||||||
createOpts,
|
CreateOptsBuilder: createOpts,
|
||||||
blockDevice,
|
BlockDevice: blockDevice,
|
||||||
}
|
}
|
||||||
log.Printf("[DEBUG] Create BFV Options: %+v", createOpts)
|
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)
|
log.Printf("[DEBUG] schedulerhints: %+v", schedulerHintsRaw)
|
||||||
schedulerHints := resourceInstanceSchedulerHintsV2(d, schedulerHintsRaw[0].(map[string]interface{}))
|
schedulerHints := resourceInstanceSchedulerHintsV2(d, schedulerHintsRaw[0].(map[string]interface{}))
|
||||||
createOpts = &schedulerhints.CreateOptsExt{
|
createOpts = &schedulerhints.CreateOptsExt{
|
||||||
createOpts,
|
CreateOptsBuilder: createOpts,
|
||||||
schedulerHints,
|
SchedulerHints: schedulerHints,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,6 @@ func testAccTestCheckDestroy(test *statuscake.Test) resource.TestCheckFunc {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const testAccTestConfig_basic = `
|
const testAccTestConfig_basic = `
|
||||||
|
|
|
@ -107,7 +107,7 @@ func CreateCertRequest(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
||||||
certReqBytes, err := x509.CreateCertificateRequest(rand.Reader, &certReq, key)
|
certReqBytes, err := x509.CreateCertificateRequest(rand.Reader, &certReq, key)
|
||||||
if err != nil {
|
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}))
|
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)
|
certBytes, err := x509.CreateCertificate(rand.Reader, template, parent, pub, priv)
|
||||||
if err != nil {
|
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}))
|
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{
|
return &types.VirtualDeviceConfigSpec{
|
||||||
Operation: types.VirtualDeviceConfigSpecOperationAdd,
|
Operation: types.VirtualDeviceConfigSpecOperationAdd,
|
||||||
Device: &types.VirtualVmxnet3{
|
Device: &types.VirtualVmxnet3{
|
||||||
types.VirtualVmxnet{
|
VirtualVmxnet: types.VirtualVmxnet{
|
||||||
types.VirtualEthernetCard{
|
VirtualEthernetCard: types.VirtualEthernetCard{
|
||||||
VirtualDevice: types.VirtualDevice{
|
VirtualDevice: types.VirtualDevice{
|
||||||
Key: -1,
|
Key: -1,
|
||||||
Backing: backing,
|
Backing: backing,
|
||||||
|
@ -663,7 +663,7 @@ func buildNetworkDevice(f *find.Finder, label, adapterType string) (*types.Virtu
|
||||||
return &types.VirtualDeviceConfigSpec{
|
return &types.VirtualDeviceConfigSpec{
|
||||||
Operation: types.VirtualDeviceConfigSpecOperationAdd,
|
Operation: types.VirtualDeviceConfigSpecOperationAdd,
|
||||||
Device: &types.VirtualE1000{
|
Device: &types.VirtualE1000{
|
||||||
types.VirtualEthernetCard{
|
VirtualEthernetCard: types.VirtualEthernetCard{
|
||||||
VirtualDevice: types.VirtualDevice{
|
VirtualDevice: types.VirtualDevice{
|
||||||
Key: -1,
|
Key: -1,
|
||||||
Backing: backing,
|
Backing: backing,
|
||||||
|
@ -922,7 +922,7 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error {
|
||||||
|
|
||||||
if d.Type == "StoragePod" {
|
if d.Type == "StoragePod" {
|
||||||
sp := object.StoragePod{
|
sp := object.StoragePod{
|
||||||
object.NewFolder(c.Client, d),
|
Folder: object.NewFolder(c.Client, d),
|
||||||
}
|
}
|
||||||
sps := buildStoragePlacementSpecCreate(dcFolders, resourcePool, sp, configSpec)
|
sps := buildStoragePlacementSpecCreate(dcFolders, resourcePool, sp, configSpec)
|
||||||
datastore, err = findDatastore(c, sps)
|
datastore, err = findDatastore(c, sps)
|
||||||
|
@ -1054,7 +1054,7 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error {
|
||||||
|
|
||||||
if d.Type == "StoragePod" {
|
if d.Type == "StoragePod" {
|
||||||
sp := object.StoragePod{
|
sp := object.StoragePod{
|
||||||
object.NewFolder(c.Client, d),
|
Folder: object.NewFolder(c.Client, d),
|
||||||
}
|
}
|
||||||
sps := buildStoragePlacementSpecClone(c, dcFolders, template, resourcePool, sp)
|
sps := buildStoragePlacementSpecClone(c, dcFolders, template, resourcePool, sp)
|
||||||
datastore, err = findDatastore(c, sps)
|
datastore, err = findDatastore(c, sps)
|
||||||
|
|
|
@ -217,8 +217,6 @@ func (c *AtlasClient) Delete() error {
|
||||||
"HTTP error: %d\n\nBody: %s",
|
"HTTP error: %d\n\nBody: %s",
|
||||||
resp.StatusCode, c.readBody(resp.Body))
|
resp.StatusCode, c.readBody(resp.Body))
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("Unexpected HTTP response code %d", resp.StatusCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AtlasClient) readBody(b io.Reader) string {
|
func (c *AtlasClient) readBody(b io.Reader) string {
|
||||||
|
|
|
@ -33,7 +33,10 @@ func consulFactory(conf map[string]string) (Client, error) {
|
||||||
} else {
|
} else {
|
||||||
username = auth
|
username = auth
|
||||||
}
|
}
|
||||||
config.HttpAuth = &consulapi.HttpBasicAuth{username, password}
|
config.HttpAuth = &consulapi.HttpBasicAuth{
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := consulapi.NewClient(config)
|
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)
|
buf := new(bytes.Buffer)
|
||||||
if err := WriteState(state, buf); err != nil {
|
if err := WriteState(state, buf); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
|
@ -808,12 +805,6 @@ func TestReadWriteState(t *testing.T) {
|
||||||
t.Fatalf("bad version number: %d", state.Version)
|
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)
|
actual, err := ReadState(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
|
|
|
@ -8,6 +8,8 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mitchellh/hashstructure"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestReadWriteStateV1(t *testing.T) {
|
func TestReadWriteStateV1(t *testing.T) {
|
||||||
|
@ -25,7 +27,10 @@ func TestReadWriteStateV1(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checksum before the write
|
// 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)
|
buf := new(bytes.Buffer)
|
||||||
if err := testWriteStateV1(state, buf); err != nil {
|
if err := testWriteStateV1(state, buf); err != nil {
|
||||||
|
@ -33,7 +38,11 @@ func TestReadWriteStateV1(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checksum after the write
|
// 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 {
|
if chksumAfter != chksum {
|
||||||
t.Fatalf("structure changed during serialization!")
|
t.Fatalf("structure changed during serialization!")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/sha1"
|
|
||||||
"encoding/gob"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
@ -21,21 +17,6 @@ import (
|
||||||
// This is the directory where our test fixtures are.
|
// This is the directory where our test fixtures are.
|
||||||
const fixtureDir = "./test-fixtures"
|
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 {
|
func tempDir(t *testing.T) string {
|
||||||
dir, err := ioutil.TempDir("", "tf")
|
dir, err := ioutil.TempDir("", "tf")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -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
|
||||||
|
)
|
|
@ -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