diff --git a/go.mod b/go.mod index 57009e6..1524488 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/google/btree v1.0.1 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 github.com/hashicorp/terraform-config-inspect v0.0.0-20210318070130-9a80970d6b34 + github.com/natefinch/atomic v1.0.1 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 ) diff --git a/go.sum b/go.sum index 44010cb..e19cca3 100644 --- a/go.sum +++ b/go.sum @@ -36,6 +36,8 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A= +github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -54,10 +56,8 @@ github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6Ac github.com/zclconf/go-cty v1.1.0 h1:uJwc9HiBOCpoKIObTQaLR+tsEXx1HBHnOsOOpcdhZgw= github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/LemDnOc1GiZL5FCVlORJ5zo= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/internal/releaseapi/client.go b/internal/releaseapi/client.go index 280097f..35eee6d 100644 --- a/internal/releaseapi/client.go +++ b/internal/releaseapi/client.go @@ -14,6 +14,7 @@ import ( "github.com/Masterminds/semver/v3" "github.com/gregjones/httpcache" "github.com/gregjones/httpcache/diskcache" + "github.com/natefinch/atomic" "github.com/pkg/errors" ) @@ -122,22 +123,14 @@ func (c *Client) downloadBuild(build Build) (string, error) { return "", err } + defer zipFile.Close() + zipReader, err := zip.NewReader(zipFile, zipLength) if err != nil { return "", errors.Wrap(err, "could not unzip release archive") } - destination, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0755) - - defer destination.Close() - - if err != nil { - return "", errors.Wrap(err, "could not create destination file for executable") - } - - var found bool - for _, f := range zipReader.File { if filepath.Base(f.Name) != "terraform" { continue @@ -145,24 +138,24 @@ func (c *Client) downloadBuild(build Build) (string, error) { source, err := f.Open() - defer source.Close() - if err != nil { - return "", errors.Wrap(err, "could not read executable in release archive") + return "", errors.Wrap(err, "could not read binary in release archive") } - if _, err := io.Copy(destination, source); err != nil { - return "", errors.Wrap(err, "could not copy executable to destination") + defer source.Close() + + if err := atomic.WriteFile(path, source); err != nil { + return "", errors.Wrap(err, "could not write binary to the cache directory") } - found = true - } + if err := os.Chmod(path, 0700); err != nil { + return "", errors.Wrap(err, "could not make binary executable") + } - if !found { - return "", errors.New("could not find executable named 'terraform' in release archive") + return path, nil } - return path, nil + return "", errors.New("could not find executable named 'terraform' in release archive") } func (c *Client) downloadReleaseArchive(build Build) (*os.File, int64, error) { @@ -176,11 +169,13 @@ func (c *Client) downloadReleaseArchive(build Build) (*os.File, int64, error) { response, err := c.httpClient.Do(request) - defer response.Body.Close() - if err != nil { return nil, 0, errors.Wrap(err, "could not download release archive") - } else if response.StatusCode != http.StatusOK { + } + + defer response.Body.Close() + + if response.StatusCode != http.StatusOK { return nil, 0, errors.Errorf("unexpected status code '%s' in response", response.StatusCode) } @@ -197,10 +192,6 @@ func (c *Client) downloadReleaseArchive(build Build) (*os.File, int64, error) { return tmp, response.ContentLength, nil } -func cachedReleaseListPath(cacheDir string) string { - return filepath.Join(cacheDir, "terraform-releases") -} - func cachedExecutablePath(cacheDir string, b Build) string { return filepath.Join(cacheDir, executableName(b)) }