github.com/matrixorigin/matrixone@v1.2.0/pkg/backup/fs.go (about) 1 // Copyright 2023 Matrix Origin 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 backup 16 17 import ( 18 "context" 19 "crypto/sha256" 20 "encoding/csv" 21 "fmt" 22 "github.com/matrixorigin/matrixone/pkg/common/moerr" 23 "github.com/matrixorigin/matrixone/pkg/fileservice" 24 "github.com/matrixorigin/matrixone/pkg/logutil" 25 "strconv" 26 "strings" 27 ) 28 29 // setupFilesystem returns a FileService for ETL which the reader outside the matrixone 30 // can read the content. a FileService for Backup which only the matrixone 31 // can read the content. 32 func setupFilesystem(ctx context.Context, path string, forETL bool) (res fileservice.FileService, readPath string, err error) { 33 return setupFileservice(ctx, &pathConfig{ 34 isS3: false, 35 forETL: forETL, 36 filesystemConfig: filesystemConfig{path: path}, 37 }) 38 } 39 40 // setupS3 returns a FileService for ETL which the reader outside the matrixone 41 // can read the content.a FileService for Backup which only the matrixone 42 // can read the content. 43 func setupS3(ctx context.Context, s3 *s3Config, forETL bool) (res fileservice.FileService, readPath string, err error) { 44 return setupFileservice(ctx, &pathConfig{ 45 isS3: true, 46 forETL: forETL, 47 s3Config: *s3, 48 }) 49 } 50 51 func setupFileservice(ctx context.Context, conf *pathConfig) (res fileservice.FileService, readPath string, err error) { 52 var s3opts string 53 if conf.isS3 { 54 s3opts, err = makeS3Opts(&conf.s3Config) 55 if err != nil { 56 return nil, "", err 57 } 58 if conf.forETL { 59 s3path := fileservice.JoinPath(s3opts, etlFSDir(conf.filepath)) 60 //TODO:remove debug 61 logutil.Debugf("==>s3path: %s", s3path) 62 res, readPath, err = fileservice.GetForETL(ctx, nil, s3path) 63 if err != nil { 64 return nil, "", err 65 } 66 } else { 67 s3path := fileservice.JoinPath(s3opts, conf.filepath) 68 res, err = fileservice.GetForBackup(ctx, s3path) 69 if err != nil { 70 return nil, "", err 71 } 72 } 73 res = fileservice.SubPath(res, conf.filepath) 74 } else { 75 if conf.forETL { 76 res, readPath, err = fileservice.GetForETL(ctx, nil, etlFSDir(conf.path)) 77 if err != nil { 78 return nil, "", err 79 } 80 } else { 81 res, err = fileservice.GetForBackup(ctx, conf.path) 82 if err != nil { 83 return nil, "", err 84 } 85 } 86 } 87 88 return res, readPath, err 89 } 90 91 func makeS3Opts(s3 *s3Config) (string, error) { 92 var err error 93 buf := new(strings.Builder) 94 w := csv.NewWriter(buf) 95 opts := []string{ 96 "s3-opts", 97 "endpoint=" + s3.endpoint, 98 "region=" + s3.region, 99 "key=" + s3.accessKeyId, 100 "secret=" + s3.secretAccessKey, 101 "bucket=" + s3.bucket, 102 "role-arn=" + s3.roleArn, 103 "is-minio=" + strconv.FormatBool(s3.isMinio), 104 //"external-id=" /*+ param.S3Param.ExternalId*/, 105 } 106 if err = w.Write(opts); err != nil { 107 return "", err 108 } 109 w.Flush() 110 return buf.String(), nil 111 } 112 113 func etlFSDir(filepath string) string { 114 return filepath + "/_" 115 } 116 117 func writeFile(ctx context.Context, fs fileservice.FileService, path string, data []byte) error { 118 var err error 119 //write file 120 _, err = fileservice.DoWithRetry( 121 "BackupWrite", 122 func() (int, error) { 123 return 0, fs.Write(ctx, fileservice.IOVector{ 124 FilePath: path, 125 Entries: []fileservice.IOEntry{ 126 { 127 Offset: 0, 128 Size: int64(len(data)), 129 Data: data, 130 }, 131 }, 132 }) 133 }, 134 64, 135 fileservice.IsRetryableError, 136 ) 137 if err != nil { 138 return err 139 } 140 141 checksum := sha256.Sum256(data) 142 143 //write checksum file for the file 144 checksumFile := path + ".sha256" 145 _, err = fileservice.DoWithRetry( 146 "BackupWrite", 147 func() (int, error) { 148 return 0, fs.Write(ctx, fileservice.IOVector{ 149 FilePath: checksumFile, 150 Entries: []fileservice.IOEntry{ 151 { 152 Offset: 0, 153 Size: int64(len(checksum)), 154 Data: checksum[:], 155 }, 156 }, 157 }) 158 }, 159 64, 160 fileservice.IsRetryableError, 161 ) 162 return err 163 } 164 165 func readFile(ctx context.Context, fs fileservice.FileService, path string) ([]byte, error) { 166 var ( 167 err error 168 ) 169 iov := &fileservice.IOVector{ 170 FilePath: path, 171 Entries: []fileservice.IOEntry{ 172 { 173 Offset: 0, 174 Size: -1, 175 }, 176 }, 177 } 178 err = fs.Read(ctx, iov) 179 if err != nil { 180 return nil, err 181 } 182 return iov.Entries[0].Data, err 183 } 184 185 func hexStr(d []byte) string { 186 return fmt.Sprintf("%x", d) 187 } 188 189 // readFileAndCheck reads data and compare the checksum with the one in checksum file. 190 // if the checksum is equal, it returns the data of the file. 191 func readFileAndCheck(ctx context.Context, fs fileservice.FileService, path string) ([]byte, error) { 192 var ( 193 err error 194 data []byte 195 savedChecksumData []byte 196 newChecksumData []byte 197 savedChecksum string 198 newChecksum string 199 ) 200 data, err = readFile(ctx, fs, path) 201 if err != nil { 202 return nil, err 203 } 204 205 //calculate the checksum 206 hash := sha256.New() 207 hash.Write(data) 208 newChecksumData = hash.Sum(nil) 209 newChecksum = hexStr(newChecksumData) 210 211 checksumFile := path + ".sha256" 212 savedChecksumData, err = readFile(ctx, fs, checksumFile) 213 if err != nil { 214 return nil, err 215 } 216 savedChecksum = hexStr(savedChecksumData) 217 //3. compare the checksum 218 if strings.Compare(savedChecksum, newChecksum) != 0 { 219 return nil, moerr.NewInternalError(ctx, checksumErrorInfo(newChecksum, savedChecksum, path)) 220 } 221 return data, err 222 } 223 224 func checksumErrorInfo(newChecksum, savedChecksum, path string) string { 225 return fmt.Sprintf("checksum %s of %s is not equal to %s ", newChecksum, path, savedChecksum) 226 }