github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/core/paths/transientfile/create.go (about)

     1  // Copyright 2020 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package transientfile
     5  
     6  import (
     7  	"os"
     8  	"path/filepath"
     9  
    10  	"github.com/juju/errors"
    11  )
    12  
    13  // Create a transient file with the specified name inside transientDir. The
    14  // function will attempt to create any missing folders leading up to the
    15  // place where the transient file is to be created.
    16  //
    17  // For *nix targets, the caller is expected to provide a suitable transient
    18  // directory (e.g. a tmpfs mount) that will be automatically purged after a
    19  // reboot.
    20  //
    21  // For windows targets, any directory can be specified as transientDir but in
    22  // order to ensure that the file will get removed, the process must be able to
    23  // access the windows registry.
    24  func Create(transientDir, name string) (*os.File, error) {
    25  	if filepath.IsAbs(name) {
    26  		return nil, errors.New("transient file name contains an absolute path")
    27  	}
    28  
    29  	transientFilePath := filepath.Join(transientDir, name)
    30  
    31  	// Create any missing directories. The MkdirAll call below might fail
    32  	// if the base directory does not exist and multiple agents attempt to
    33  	// create it concurrently. To work around this potential race, we retry
    34  	// the attempt a few times before bailing out with an error.
    35  	//
    36  	// TODO(achilleasa) this retry block is only needed until we complete
    37  	// the unification of the machine/unit juju agents.
    38  	baseDir := filepath.Dir(transientFilePath)
    39  	for attempt := 0; ; attempt++ {
    40  		if err := os.MkdirAll(baseDir, os.ModePerm); err != nil {
    41  			if attempt == 10 {
    42  				return nil, errors.Annotatef(err, "unable to create directory path for transient file %q", transientFilePath)
    43  			}
    44  			continue
    45  		}
    46  
    47  		break
    48  	}
    49  
    50  	f, err := os.Create(transientFilePath)
    51  	if err != nil {
    52  		return nil, errors.Annotatef(err, "unable to create transient file %q", transientFilePath)
    53  	}
    54  
    55  	// Invoke platform-specific code to ensure that the file is removed
    56  	// after a reboot.
    57  	if err = ensureDeleteAfterReboot(transientFilePath); err != nil {
    58  		_ = f.Close()
    59  		_ = os.Remove(transientFilePath)
    60  		return nil, errors.Annotatef(err, "unable to schedule deletion of transient file %q after reboot", transientFilePath)
    61  	}
    62  
    63  	return f, nil
    64  }