github.com/openebs/api@v1.12.0/pkg/util/fileoperator.go (about)

     1  // Copyright © 2020 The OpenEBS Authors
     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 util
    16  
    17  import (
    18  	"io/ioutil"
    19  	"os"
    20  	"strings"
    21  
    22  	"path/filepath"
    23  
    24  	"github.com/pkg/errors"
    25  	"k8s.io/klog"
    26  )
    27  
    28  //FileOperator operates on files
    29  type FileOperator interface {
    30  	Write(filename string, data []byte, perm os.FileMode) error
    31  	Updatefile(fileName, updateVal, searchString string, perm os.FileMode) error
    32  	GetLineDetails(filename, searchString string) (int, string, error)
    33  	UpdateOrAppendMultipleLines(fileName string, keyUpdateValue map[string]string, perm os.FileMode) error
    34  }
    35  
    36  //RealFileOperator is used for writing the actual files without mocking
    37  type RealFileOperator struct{}
    38  
    39  // the real file operator for the actual program,
    40  func (r RealFileOperator) Write(filename string, data []byte, perm os.FileMode) error {
    41  	err := ioutil.WriteFile(filename, data, perm)
    42  	if err != nil {
    43  		klog.Errorf("Failed to write file: " + filename)
    44  	}
    45  	return err
    46  }
    47  
    48  // GetLineDetails return the line number and line content of matched string in file
    49  func (r RealFileOperator) GetLineDetails(filename, searchString string) (int, string, error) {
    50  	var line string
    51  	var i int
    52  	buffer, err := ioutil.ReadFile(filepath.Clean(filename))
    53  	if err != nil {
    54  		return -1, "", errors.Wrapf(err, "failed to read %s file", filename)
    55  	}
    56  	lines := strings.Split(string(buffer), "\n")
    57  	for i, line = range lines {
    58  		if strings.Contains(line, searchString) {
    59  			return i, line, nil
    60  		}
    61  	}
    62  	return -1, "", nil
    63  }
    64  
    65  // Updatefile updates the line number with the given string
    66  func (r RealFileOperator) Updatefile(fileName, updatedVal, searchString string, perm os.FileMode) error {
    67  	buffer, err := ioutil.ReadFile(filepath.Clean(fileName))
    68  	if err != nil {
    69  		return errors.Wrapf(err, "failed to read %s file", fileName)
    70  	}
    71  	lines := strings.Split(string(buffer), "\n")
    72  	for index, line := range lines {
    73  		// If searchString found then update and return
    74  		if strings.Contains(line, searchString) {
    75  			lines[index] = updatedVal
    76  			newbuffer := strings.Join(lines, "\n")
    77  			klog.V(4).Infof("content in a file %s\n", lines)
    78  			err = r.Write(fileName, []byte(newbuffer), perm)
    79  			return err
    80  		}
    81  	}
    82  	return errors.Errorf("failed to find %s in file %s", searchString, fileName)
    83  }
    84  
    85  // UpdateOrAppendMultipleLines will update or append multiple lines based on the
    86  // given string
    87  func (r RealFileOperator) UpdateOrAppendMultipleLines(fileName string,
    88  	keyUpdateValue map[string]string, perm os.FileMode) error {
    89  	var newLines []string
    90  	var index int
    91  	var line string
    92  	buffer, err := ioutil.ReadFile(filepath.Clean(fileName))
    93  	if err != nil {
    94  		return errors.Wrapf(err, "failed to read %s file", fileName)
    95  	}
    96  	lines := strings.Split(string(buffer), "\n")
    97  	// newLines pointing to lines reference
    98  	newLines = lines
    99  	if len(lines[len(lines)-1]) == 0 {
   100  		// For replacing the NUL in the file
   101  		newLines = lines[:len(lines)-1]
   102  	}
   103  
   104  	// TODO: We can split above read file into key value pairs and later we can
   105  	// append with \n and update file
   106  	// TODO: Use regular expresion to replace key value pairs
   107  	// will be doing after current blockers
   108  	for key, updatedValue := range keyUpdateValue {
   109  		found := false
   110  		for index, line = range newLines {
   111  			if strings.HasPrefix(line, key) {
   112  				newLines[index] = updatedValue
   113  				found = true
   114  				break
   115  			}
   116  		}
   117  		// To remove particular line that matched with key
   118  		if found && updatedValue == "" {
   119  			newLines = append(newLines[:index], newLines[index+1:]...)
   120  			continue
   121  		}
   122  		if found == false {
   123  			newLines = append(newLines, updatedValue)
   124  		}
   125  	}
   126  	newbuffer := strings.Join(newLines, "\n")
   127  	klog.V(4).Infof("content in a file %s\n", newLines)
   128  	err = r.Write(fileName, []byte(newbuffer), perm)
   129  	return err
   130  }
   131  
   132  //TestFileOperator is used as a dummy FileOperator
   133  type TestFileOperator struct{}
   134  
   135  //Write is to mock write operation for FileOperator interface
   136  func (r TestFileOperator) Write(filename string, data []byte, perm os.FileMode) error {
   137  	return nil
   138  }
   139  
   140  //Updatefile is to mock Updatefile operation for FileOperator interface
   141  func (r TestFileOperator) Updatefile(fileName, updateStorageVal, searchString string, perm os.FileMode) error {
   142  	return nil
   143  }
   144  
   145  //GetLineDetails is to mock operation for FileOperator interface
   146  func (r TestFileOperator) GetLineDetails(filename, searchString string) (int, string, error) {
   147  	return -1, "", nil
   148  }
   149  
   150  // UpdateOrAppendMultipleLines is to mock operation for FileOperator interface
   151  func (r TestFileOperator) UpdateOrAppendMultipleLines(
   152  	fileName string,
   153  	keyUpdateValue map[string]string,
   154  	perm os.FileMode) error {
   155  	return nil
   156  }