github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/events/file_backed_proc.go (about) 1 // Copyright 2019 Dolthub, Inc. 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 events 16 17 import ( 18 "crypto/md5" 19 "encoding/base64" 20 "errors" 21 "os" 22 "path/filepath" 23 "runtime" 24 "strings" 25 26 "github.com/golang/protobuf/proto" 27 28 eventsapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi/v1alpha1" 29 filesys "github.com/dolthub/dolt/go/libraries/utils/filesys" 30 "github.com/dolthub/dolt/go/libraries/utils/iohelp" 31 ) 32 33 const ( 34 eventsDir = "eventsData" 35 localPath = "temp" 36 evtDataExt = ".devts" 37 doltLockFile = "dolt.lock" 38 ) 39 40 // fileNamingFunc is the signature used for functions used to create 41 // dynamic file names in the FileBackedProc 42 type fileNamingFunc func(bytes []byte) string 43 44 // fileChecingFunc is the signature used for functions used to authenticate 45 // filenames created by the fileNamingFunc 46 type fileCheckingFunc func(data []byte, path string) (bool, error) 47 48 // MD5Str returns the standard base64 encoding of the md5 hash with padding 49 func MD5Str(bytes []byte) string { 50 md5Bytes := md5.Sum(bytes) 51 str := base64.StdEncoding.EncodeToString(md5Bytes[:]) 52 return str 53 } 54 55 // MD5StrUrl returns the base64 url encoding of the md5 hash with the padding removed 56 func MD5StrUrl(bytes []byte) string { 57 md5Bytes := md5.Sum(bytes) 58 str := base64.URLEncoding.EncodeToString(md5Bytes[:]) 59 return str[:22] 60 } 61 62 // MD5FileNamer names files after the base64 url encoding of the md5 hash of the contents of the file 63 func MD5FileNamer(bytes []byte) string { 64 return MD5StrUrl(bytes) + evtDataExt 65 } 66 67 // CheckFilenameMD5 is the fileNamingFunc used when instantiating a FileBackedProc 68 func CheckFilenameMD5(data []byte, path string) (bool, error) { 69 filename := filepath.Base(path) 70 ext := filepath.Ext(filename) 71 72 if ext != evtDataExt { 73 return false, nil 74 } 75 76 md5FromFilename := filename[:len(filename)-len(ext)] 77 md5FromData := MD5StrUrl(data) 78 79 if md5FromFilename != md5FromData { 80 return false, nil 81 } 82 83 return true, nil 84 } 85 86 var errEventsDirNotExists = errors.New("no events data directory exists") 87 88 // eventsDataDir is the directory used to store the events requests files 89 type eventsDataDir struct { 90 fs filesys.Filesys 91 path string 92 } 93 94 // newEventsDataDir creates a new eventsDataDir 95 func newEventsDataDir(fs filesys.Filesys, homeDir string, doltDir string) *eventsDataDir { 96 path := filepath.Join(homeDir, doltDir, eventsDir) 97 98 return &eventsDataDir{fs: fs, path: path} 99 } 100 101 // MakeEventsDir creates a new events data dir in the main dolt dir 102 func (evd *eventsDataDir) MakeEventsDir() error { 103 if exists, _ := evd.fs.Exists(evd.path); !exists { 104 if err := evd.fs.MkDirs(evd.path); err != nil { 105 return err 106 } 107 return nil 108 } 109 return nil 110 } 111 112 func (evd *eventsDataDir) getPath() string { 113 return evd.path 114 } 115 116 // FileBackedProc writes events requests to files in an events data dir 117 type FileBackedProc struct { 118 ed *eventsDataDir 119 namingFunc fileNamingFunc 120 CheckingFunc fileCheckingFunc 121 LockPath string 122 } 123 124 // NewFileBackedProc creates a new FileBackedProc 125 func NewFileBackedProc(fs filesys.Filesys, userHomeDir string, doltDir string, nf fileNamingFunc, cf fileCheckingFunc) *FileBackedProc { 126 eventsDataDir := newEventsDataDir(fs, userHomeDir, doltDir) 127 128 if err := eventsDataDir.MakeEventsDir(); err != nil { 129 panic(err) 130 } 131 132 lp := filepath.Join(eventsDataDir.getPath(), doltLockFile) 133 134 exists, _ := fs.Exists(lp) 135 136 if !exists { 137 if err := fs.WriteFile(lp, []byte("lockfile for dolt \n")); err != nil { 138 panic(err) 139 } 140 } 141 142 return &FileBackedProc{ed: eventsDataDir, namingFunc: nf, CheckingFunc: cf, LockPath: lp} 143 } 144 145 // renameFile renames the request events file using the namingFunc 146 func (fbp *FileBackedProc) renameFile(dir string, oldName string) error { 147 oldPath := filepath.Join(dir, oldName) 148 149 data, err := fbp.ed.fs.ReadFile(oldPath) 150 if err != nil { 151 return err 152 } 153 154 filename := fbp.namingFunc(data) 155 newPath := filepath.Join(dir, filename) 156 157 if err := fbp.ed.fs.MoveFile(oldPath, newPath); err != nil { 158 return nil 159 } 160 161 return nil 162 } 163 164 // EventsDirExists returns true iff the events data dir exists 165 func (fbp *FileBackedProc) EventsDirExists() bool { 166 if exists, _ := fbp.ed.fs.Exists(fbp.ed.getPath()); exists { 167 return true 168 } 169 return false 170 } 171 172 // GetEventsDirPath returns the path to the events data dir 173 func (fbp *FileBackedProc) GetEventsDirPath() string { 174 return fbp.ed.getPath() 175 } 176 177 // GetFileSys returns the current filesys being used 178 func (fbp *FileBackedProc) GetFileSys() filesys.Filesys { 179 return fbp.ed.fs 180 } 181 182 // WriteEvents writes events requests to the events data dir 183 func (fbp *FileBackedProc) WriteEvents(version string, evts []*eventsapi.ClientEvent) error { 184 if len(evts) < 1 { 185 return nil 186 } 187 188 var plat eventsapi.Platform 189 switch strings.ToLower(runtime.GOOS) { 190 case "darwin": 191 plat = eventsapi.Platform_DARWIN 192 case "linux": 193 plat = eventsapi.Platform_LINUX 194 case "windows": 195 plat = eventsapi.Platform_WINDOWS 196 } 197 198 if dirExists := fbp.EventsDirExists(); dirExists { 199 eventsPath := fbp.ed.getPath() 200 tempFilename := filepath.Join(eventsPath, localPath) 201 202 f, err := fbp.ed.fs.OpenForWrite(tempFilename, os.ModePerm) 203 204 if err != nil { 205 return err 206 } 207 208 req := &eventsapi.LogEventsRequest{ 209 MachineId: getMachineID(), 210 Version: version, 211 Platform: plat, 212 Events: evts, 213 App: eventsapi.AppID_APP_DOLT, 214 } 215 216 data, err := proto.Marshal(req) 217 if err != nil { 218 return err 219 } 220 221 if err := iohelp.WriteAll(f, data); err != nil { 222 return err 223 } 224 225 if err := f.Close(); err != nil { 226 return err 227 } 228 229 if err = fbp.renameFile(eventsPath, localPath); err != nil { 230 return err 231 } 232 233 return nil 234 } 235 236 return errEventsDirNotExists 237 }