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 }