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 }