8 "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
9 "git.curoverse.com/arvados.git/sdk/go/keepclient"
20 err := doMain(os.Args[1:])
26 func doMain(args []string) error {
27 flags := flag.NewFlagSet("keep-block-check", flag.ExitOnError)
29 configFile := flags.String(
32 "Configuration filename. May be either a pathname to a config file, or (for example) 'foo' as shorthand for $HOME/.config/arvados/foo.conf file. This file is expected to specify the values for ARVADOS_API_TOKEN, ARVADOS_API_HOST, ARVADOS_API_HOST_INSECURE, and ARVADOS_BLOB_SIGNING_KEY for the source.")
34 keepServicesJSON := flags.String(
37 "An optional list of available keepservices. "+
38 "If not provided, this list is obtained from api server configured in config-file.")
40 locatorFile := flags.String(
43 "Filename containing the block hashes to be checked. This is required. "+
44 "This file contains the block hashes one per line.")
46 prefix := flags.String(
49 "Block hash prefix. When a prefix is specified, only hashes listed in the file with this prefix will be checked.")
51 verbose := flags.Bool(
54 "Log progress of each block verification")
56 // Parse args; omit the first arg which is the command name
59 config, blobSigningKey, err := loadConfig(*configFile)
61 return fmt.Errorf("Error loading configuration from file: %s", err.Error())
64 // get list of block locators to be checked
65 blockLocators, err := getBlockLocators(*locatorFile, *prefix)
67 return fmt.Errorf("Error reading block hashes to be checked from file: %s", err.Error())
71 kc, err := setupKeepClient(config, *keepServicesJSON)
73 return fmt.Errorf("Error configuring keepclient: %s", err.Error())
76 return performKeepBlockCheck(kc, blobSigningKey, blockLocators, *verbose)
79 type apiConfig struct {
86 // Load config from given file
87 func loadConfig(configFile string) (config apiConfig, blobSigningKey string, err error) {
89 err = errors.New("Client config file not specified")
93 config, blobSigningKey, err = readConfigFromFile(configFile)
97 var matchTrue = regexp.MustCompile("^(?i:1|yes|true)$")
99 // Read config from file
100 func readConfigFromFile(filename string) (config apiConfig, blobSigningKey string, err error) {
101 if !strings.Contains(filename, "/") {
102 filename = os.Getenv("HOME") + "/.config/arvados/" + filename + ".conf"
105 content, err := ioutil.ReadFile(filename)
111 lines := strings.Split(string(content), "\n")
112 for _, line := range lines {
117 kv := strings.SplitN(line, "=", 2)
119 key := strings.TrimSpace(kv[0])
120 value := strings.TrimSpace(kv[1])
123 case "ARVADOS_API_TOKEN":
124 config.APIToken = value
125 case "ARVADOS_API_HOST":
126 config.APIHost = value
127 case "ARVADOS_API_HOST_INSECURE":
128 config.APIHostInsecure = matchTrue.MatchString(value)
129 case "ARVADOS_EXTERNAL_CLIENT":
130 config.ExternalClient = matchTrue.MatchString(value)
131 case "ARVADOS_BLOB_SIGNING_KEY":
132 blobSigningKey = value
140 // setup keepclient using the config provided
141 func setupKeepClient(config apiConfig, keepServicesJSON string) (kc *keepclient.KeepClient, err error) {
142 arv := arvadosclient.ArvadosClient{
143 ApiToken: config.APIToken,
144 ApiServer: config.APIHost,
145 ApiInsecure: config.APIHostInsecure,
146 Client: &http.Client{Transport: &http.Transport{
147 TLSClientConfig: &tls.Config{InsecureSkipVerify: config.APIHostInsecure}}},
148 External: config.ExternalClient,
151 // if keepServicesJSON is provided, use it to load services; else, use DiscoverKeepServers
152 if keepServicesJSON == "" {
153 kc, err = keepclient.MakeKeepClient(&arv)
158 kc = keepclient.New(&arv)
159 err = kc.LoadKeepServicesFromJSON(keepServicesJSON)
168 // Get list of unique block locators from the given file
169 func getBlockLocators(locatorFile, prefix string) (locators []string, err error) {
170 if locatorFile == "" {
171 err = errors.New("block-hash-file not specified")
175 content, err := ioutil.ReadFile(locatorFile)
180 locatorMap := make(map[string]bool)
181 for _, line := range strings.Split(string(content), "\n") {
182 line = strings.TrimSpace(line)
183 if line == "" || !strings.HasPrefix(line, prefix) || locatorMap[line] {
186 locators = append(locators, line)
187 locatorMap[line] = true
193 // Get block headers from keep. Log any errors.
194 func performKeepBlockCheck(kc *keepclient.KeepClient, blobSigningKey string, blockLocators []string, verbose bool) error {
195 totalBlocks := len(blockLocators)
198 for _, locator := range blockLocators {
201 log.Printf("Checking block %d of %d: %v", current, totalBlocks, locator)
203 getLocator := locator
204 if blobSigningKey != "" {
205 expiresAt := time.Now().AddDate(0, 0, 1)
206 getLocator = keepclient.SignLocator(locator, kc.Arvados.ApiToken, expiresAt, []byte(blobSigningKey))
209 _, _, err := kc.Ask(getLocator)
212 log.Printf("Error verifying block %v: %v", locator, err)
216 log.Printf("Verify block totals: %d attempts, %d successes, %d errors", totalBlocks, totalBlocks-notFoundBlocks, notFoundBlocks)
218 if notFoundBlocks > 0 {
219 return fmt.Errorf("Block verification failed for %d out of %d blocks with matching prefix.", notFoundBlocks, totalBlocks)