github.com/mgoltzsche/ctnr@v0.7.1-alpha/pkg/atomic/writer.go (about)

     1  // Copyright © 2017 Max Goltzsche
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package atomic
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/json"
    20  	"io"
    21  	"io/ioutil"
    22  	"os"
    23  	"path/filepath"
    24  
    25  	"github.com/pkg/errors"
    26  )
    27  
    28  // Writes a file atomically by first writing into a temp file before moving it to its final destination
    29  func WriteFile(dest string, reader io.Reader) (size int64, err error) {
    30  	// Create temp file to write blob to
    31  	tmpFile, err := ioutil.TempFile(filepath.Dir(dest), ".tmp-")
    32  	if err != nil {
    33  		return 0, errors.New(err.Error())
    34  	}
    35  	tmpPath := tmpFile.Name()
    36  	tmpRemoved := false
    37  	defer func() {
    38  		tmpFile.Close()
    39  		if !tmpRemoved {
    40  			os.Remove(tmpPath)
    41  		}
    42  	}()
    43  
    44  	// Write temp file
    45  	if size, err = io.Copy(tmpFile, reader); err != nil {
    46  		err = errors.New("write to temp file: " + err.Error())
    47  		return
    48  	}
    49  	if err = tmpFile.Sync(); err != nil {
    50  		err = errors.New("sync temp file: " + err.Error())
    51  		return
    52  	}
    53  	if err = tmpFile.Close(); err != nil {
    54  		err = errors.New("close temp file: " + err.Error())
    55  		return
    56  	}
    57  
    58  	// Rename temp file
    59  	if err = os.Rename(tmpPath, dest); err != nil {
    60  		return 0, errors.New("rename temp file: " + err.Error())
    61  	}
    62  	tmpRemoved = true
    63  	return
    64  }
    65  
    66  // Writes a JSON file atomically by first writing into a temp file before moving it to its final destination
    67  func WriteJson(dest string, o interface{}) (size int64, err error) {
    68  	var buf bytes.Buffer
    69  	if err = json.NewEncoder(&buf).Encode(o); err != nil {
    70  		return 0, errors.New("write json: " + err.Error())
    71  	}
    72  	return WriteFile(dest, &buf)
    73  }