1// Copyright 2018 The Prometheus Authors 2// Licensed under the Apache License, Version 2.0 (the "License"); 3// you may not use this file except in compliance with the License. 4// You may obtain a copy of the License at 5// 6// http://www.apache.org/licenses/LICENSE-2.0 7// 8// Unless required by applicable law or agreed to in writing, software 9// distributed under the License is distributed on an "AS IS" BASIS, 10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11// See the License for the specific language governing permissions and 12// limitations under the License. 13 14package tsdb 15 16import ( 17 "encoding/json" 18 "io" 19 "io/ioutil" 20 "os" 21 "path/filepath" 22 23 "github.com/go-kit/kit/log" 24 "github.com/go-kit/kit/log/level" 25 "github.com/pkg/errors" 26 "github.com/prometheus/tsdb/fileutil" 27) 28 29// repairBadIndexVersion repairs an issue in index and meta.json persistence introduced in 30// commit 129773b41a565fde5156301e37f9a87158030443. 31func repairBadIndexVersion(logger log.Logger, dir string) error { 32 // All blocks written by Prometheus 2.1 with a meta.json version of 2 are affected. 33 // We must actually set the index file version to 2 and revert the meta.json version back to 1. 34 dirs, err := blockDirs(dir) 35 if err != nil { 36 return errors.Wrapf(err, "list block dirs in %q", dir) 37 } 38 39 wrapErr := func(err error, d string) error { 40 return errors.Wrapf(err, "block dir: %q", d) 41 } 42 for _, d := range dirs { 43 meta, err := readBogusMetaFile(d) 44 if err != nil { 45 return wrapErr(err, d) 46 } 47 if meta.Version == 1 { 48 level.Info(logger).Log( 49 "msg", "found healthy block", 50 "mint", meta.MinTime, 51 "maxt", meta.MaxTime, 52 "ulid", meta.ULID, 53 ) 54 continue 55 } 56 level.Info(logger).Log( 57 "msg", "fixing broken block", 58 "mint", meta.MinTime, 59 "maxt", meta.MaxTime, 60 "ulid", meta.ULID, 61 ) 62 63 repl, err := os.Create(filepath.Join(d, "index.repaired")) 64 if err != nil { 65 return wrapErr(err, d) 66 } 67 broken, err := os.Open(filepath.Join(d, "index")) 68 if err != nil { 69 return wrapErr(err, d) 70 } 71 if _, err := io.Copy(repl, broken); err != nil { 72 return wrapErr(err, d) 73 } 74 // Set the 5th byte to 2 to indicate the correct file format version. 75 if _, err := repl.WriteAt([]byte{2}, 4); err != nil { 76 return wrapErr(err, d) 77 } 78 if err := fileutil.Fsync(repl); err != nil { 79 return wrapErr(err, d) 80 } 81 if err := repl.Close(); err != nil { 82 return wrapErr(err, d) 83 } 84 if err := broken.Close(); err != nil { 85 return wrapErr(err, d) 86 } 87 if err := renameFile(repl.Name(), broken.Name()); err != nil { 88 return wrapErr(err, d) 89 } 90 // Reset version of meta.json to 1. 91 meta.Version = 1 92 if err := writeMetaFile(d, meta); err != nil { 93 return wrapErr(err, d) 94 } 95 } 96 return nil 97} 98 99func readBogusMetaFile(dir string) (*BlockMeta, error) { 100 b, err := ioutil.ReadFile(filepath.Join(dir, metaFilename)) 101 if err != nil { 102 return nil, err 103 } 104 var m BlockMeta 105 106 if err := json.Unmarshal(b, &m); err != nil { 107 return nil, err 108 } 109 if m.Version != 1 && m.Version != 2 { 110 return nil, errors.Errorf("unexpected meta file version %d", m.Version) 111 } 112 return &m, nil 113} 114