k8s.io/kubernetes@v1.29.3/pkg/kubelet/util/store/filestore.go (about)

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package store
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"path/filepath"
    23  	"strings"
    24  
    25  	utilfs "k8s.io/kubernetes/pkg/util/filesystem"
    26  )
    27  
    28  const (
    29  	// Name prefix for the temporary files.
    30  	tmpPrefix = "."
    31  )
    32  
    33  // FileStore is an implementation of the Store interface which stores data in files.
    34  type FileStore struct {
    35  	// Absolute path to the base directory for storing data files.
    36  	directoryPath string
    37  
    38  	// filesystem to use.
    39  	filesystem utilfs.Filesystem
    40  }
    41  
    42  // NewFileStore returns an instance of FileStore.
    43  func NewFileStore(path string, fs utilfs.Filesystem) (Store, error) {
    44  	if err := fs.MkdirAll(path, 0755); err != nil {
    45  		return nil, err
    46  	}
    47  	return &FileStore{directoryPath: path, filesystem: fs}, nil
    48  }
    49  
    50  // Write writes the given data to a file named key.
    51  func (f *FileStore) Write(key string, data []byte) error {
    52  	if err := ValidateKey(key); err != nil {
    53  		return err
    54  	}
    55  	if err := f.filesystem.MkdirAll(f.directoryPath, 0755); err != nil {
    56  		return err
    57  	}
    58  
    59  	return writeFile(f.filesystem, f.getPathByKey(key), data)
    60  }
    61  
    62  // Read reads the data from the file named key.
    63  func (f *FileStore) Read(key string) ([]byte, error) {
    64  	if err := ValidateKey(key); err != nil {
    65  		return nil, err
    66  	}
    67  	bytes, err := f.filesystem.ReadFile(f.getPathByKey(key))
    68  	if os.IsNotExist(err) {
    69  		return bytes, ErrKeyNotFound
    70  	}
    71  	return bytes, err
    72  }
    73  
    74  // Delete deletes the key file.
    75  func (f *FileStore) Delete(key string) error {
    76  	if err := ValidateKey(key); err != nil {
    77  		return err
    78  	}
    79  	return removePath(f.filesystem, f.getPathByKey(key))
    80  }
    81  
    82  // List returns all keys in the store.
    83  func (f *FileStore) List() ([]string, error) {
    84  	keys := make([]string, 0)
    85  	files, err := f.filesystem.ReadDir(f.directoryPath)
    86  	if err != nil {
    87  		return keys, err
    88  	}
    89  	for _, f := range files {
    90  		if !strings.HasPrefix(f.Name(), tmpPrefix) {
    91  			keys = append(keys, f.Name())
    92  		}
    93  	}
    94  	return keys, nil
    95  }
    96  
    97  // getPathByKey returns the full path of the file for the key.
    98  func (f *FileStore) getPathByKey(key string) string {
    99  	return filepath.Join(f.directoryPath, key)
   100  }
   101  
   102  // writeFile writes data to path in a single transaction.
   103  func writeFile(fs utilfs.Filesystem, path string, data []byte) (retErr error) {
   104  	// Create a temporary file in the base directory of `path` with a prefix.
   105  	tmpFile, err := fs.TempFile(filepath.Dir(path), tmpPrefix)
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	tmpPath := tmpFile.Name()
   111  	shouldClose := true
   112  
   113  	defer func() {
   114  		// Close the file.
   115  		if shouldClose {
   116  			if err := tmpFile.Close(); err != nil {
   117  				if retErr == nil {
   118  					retErr = fmt.Errorf("close error: %v", err)
   119  				} else {
   120  					retErr = fmt.Errorf("failed to close temp file after error %v; close error: %v", retErr, err)
   121  				}
   122  			}
   123  		}
   124  
   125  		// Clean up the temp file on error.
   126  		if retErr != nil && tmpPath != "" {
   127  			if err := removePath(fs, tmpPath); err != nil {
   128  				retErr = fmt.Errorf("failed to remove the temporary file (%q) after error %v; remove error: %v", tmpPath, retErr, err)
   129  			}
   130  		}
   131  	}()
   132  
   133  	// Write data.
   134  	if _, err := tmpFile.Write(data); err != nil {
   135  		return err
   136  	}
   137  
   138  	// Sync file.
   139  	if err := tmpFile.Sync(); err != nil {
   140  		return err
   141  	}
   142  
   143  	// Closing the file before renaming.
   144  	err = tmpFile.Close()
   145  	shouldClose = false
   146  	if err != nil {
   147  		return err
   148  	}
   149  
   150  	return fs.Rename(tmpPath, path)
   151  }
   152  
   153  func removePath(fs utilfs.Filesystem, path string) error {
   154  	if err := fs.Remove(path); err != nil && !os.IsNotExist(err) {
   155  		return err
   156  	}
   157  	return nil
   158  }