github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/cmd/check.go (about) 1 package cmd 2 3 import ( 4 "bufio" 5 "encoding/json" 6 "fmt" 7 "net/url" 8 "os" 9 "strconv" 10 11 "github.com/cozy/cozy-stack/client/request" 12 "github.com/spf13/cobra" 13 ) 14 15 var flagCheckFSIndexIntegrity bool 16 var flagCheckFSFilesConsistensy bool 17 var flagCheckFSFailFast bool 18 var flagCheckSharingsFast bool 19 20 var checkCmdGroup = &cobra.Command{ 21 Use: "check <command>", 22 Short: "A set of tools to check that instances are in the expected state.", 23 RunE: func(cmd *cobra.Command, args []string) error { 24 return cmd.Usage() 25 }, 26 } 27 28 var checkFSCmd = &cobra.Command{ 29 Use: "fs <domain>", 30 Short: "Check a vfs", 31 Long: ` 32 This command checks that the files in the VFS are not desynchronized, ie a file 33 present in CouchDB but not swift/localfs, or present in swift/localfs but not 34 couchdb. 35 36 There are 2 steps: 37 38 - index integrity checks that there are nothing wrong in the index (CouchDB), 39 like a file present in a directory that has been deleted 40 - files consistency checks that the files are the same in the index (CouchDB) 41 and the storage (Swift or localfs). 42 43 By default, both operations are done, but you can choose one or the other via 44 the flags. 45 `, 46 RunE: func(cmd *cobra.Command, args []string) error { 47 if len(args) == 0 { 48 return cmd.Usage() 49 } 50 return fsck(args[0]) 51 }, 52 } 53 54 func fsck(domain string) error { 55 if flagCheckFSFilesConsistensy && flagCheckFSIndexIntegrity { 56 flagCheckFSIndexIntegrity = false 57 flagCheckFSFilesConsistensy = false 58 } 59 60 ac := newAdminClient() 61 res, err := ac.Req(&request.Options{ 62 Method: "GET", 63 Path: "/instances/" + url.PathEscape(domain) + "/fsck", 64 Queries: url.Values{ 65 "IndexIntegrity": {strconv.FormatBool(flagCheckFSIndexIntegrity)}, 66 "FilesConsistency": {strconv.FormatBool(flagCheckFSFilesConsistensy)}, 67 "FailFast": {strconv.FormatBool(flagCheckFSFailFast)}, 68 }, 69 }) 70 if err != nil { 71 return err 72 } 73 74 hasLogs := false 75 scanner := bufio.NewScanner(res.Body) 76 buf := make([]byte, 512*1024) // The default buffer can be too short for some lines 77 scanner.Buffer(buf, len(buf)) 78 79 for scanner.Scan() { 80 hasLogs = true 81 fmt.Println(string(scanner.Bytes())) 82 } 83 if err := scanner.Err(); err != nil { 84 _ = res.Body.Close() 85 return err 86 } 87 88 if hasLogs { 89 // Needs to handle manually the body close because os.Exit bypass all the 90 // defer functions. 91 _ = res.Body.Close() 92 os.Exit(1) 93 } 94 95 _ = res.Body.Close() 96 return nil 97 } 98 99 var checkTriggers = &cobra.Command{ 100 Use: "triggers <domain>", 101 Short: "Check the triggers", 102 Long: ` 103 This command checks that the instance doesn't have duplicate triggers: several 104 triggers of the same type, for the same worker, and with the same arguments. 105 `, 106 RunE: func(cmd *cobra.Command, args []string) error { 107 if len(args) == 0 { 108 return cmd.Usage() 109 } 110 domain := args[0] 111 112 ac := newAdminClient() 113 res, err := ac.Req(&request.Options{ 114 Method: "POST", 115 Path: "/instances/" + url.PathEscape(domain) + "/checks/triggers", 116 }) 117 if err != nil { 118 return err 119 } 120 121 var result []map[string]interface{} 122 err = json.NewDecoder(res.Body).Decode(&result) 123 if err != nil { 124 return err 125 } 126 127 if len(result) > 0 { 128 for _, r := range result { 129 j, _ := json.Marshal(r) 130 fmt.Fprintf(os.Stdout, "%s\n", j) 131 } 132 os.Exit(1) 133 } 134 return nil 135 }, 136 } 137 138 var checkSharedCmd = &cobra.Command{ 139 Use: "shared <domain>", 140 Short: "Check the io.cozy.shared documents", 141 Long: ` 142 The io.cozy.shared documents have a tree of revisions. This command will check 143 that all revisions in this tree are either the root or their parent have a 144 generation smaller than their generation. 145 `, 146 RunE: func(cmd *cobra.Command, args []string) error { 147 if len(args) == 0 { 148 return cmd.Usage() 149 } 150 domain := args[0] 151 152 ac := newAdminClient() 153 res, err := ac.Req(&request.Options{ 154 Method: "POST", 155 Path: "/instances/" + url.PathEscape(domain) + "/checks/shared", 156 }) 157 if err != nil { 158 return err 159 } 160 161 var result []map[string]interface{} 162 err = json.NewDecoder(res.Body).Decode(&result) 163 if err != nil { 164 return err 165 } 166 167 if len(result) > 0 { 168 for _, r := range result { 169 j, _ := json.Marshal(r) 170 fmt.Fprintf(os.Stdout, "%s\n", j) 171 } 172 os.Exit(1) 173 } 174 return nil 175 }, 176 } 177 178 var checkSharingsCmd = &cobra.Command{ 179 Use: "sharings <domain>", 180 Short: "Check the io.cozy.sharings documents", 181 Long: ` 182 This command checks that the io.cozy.sharings have no inconsistencies. It can 183 be triggers that are missing on an active sharing, or missing credentials for 184 an active member. 185 186 There are 2 steps: 187 188 - setup integrity checks that there are nothing wrong in the configuration like 189 a missing trigger 190 - files and folders consistency checks that the shared documents are the same 191 for all members 192 193 By default, both operations are done, but you can choose to skip the consistency 194 check via the flags. 195 `, 196 RunE: func(cmd *cobra.Command, args []string) error { 197 if len(args) == 0 { 198 return cmd.Usage() 199 } 200 domain := args[0] 201 202 ac := newAdminClient() 203 res, err := ac.Req(&request.Options{ 204 Method: "POST", 205 Path: "/instances/" + url.PathEscape(domain) + "/checks/sharings", 206 Queries: url.Values{ 207 "SkipFSConsistency": {strconv.FormatBool(flagCheckSharingsFast)}, 208 }, 209 }) 210 if err != nil { 211 return err 212 } 213 214 var result []map[string]interface{} 215 err = json.NewDecoder(res.Body).Decode(&result) 216 if err != nil { 217 return err 218 } 219 220 if len(result) > 0 { 221 for _, r := range result { 222 j, _ := json.Marshal(r) 223 fmt.Fprintf(os.Stdout, "%s\n", j) 224 } 225 os.Exit(1) 226 } 227 return nil 228 }, 229 } 230 231 func init() { 232 checkCmdGroup.AddCommand(checkFSCmd) 233 checkCmdGroup.AddCommand(checkTriggers) 234 checkCmdGroup.AddCommand(checkSharedCmd) 235 checkCmdGroup.AddCommand(checkSharingsCmd) 236 checkFSCmd.Flags().BoolVar(&flagCheckFSIndexIntegrity, "index-integrity", false, "Check the index integrity only") 237 checkFSCmd.Flags().BoolVar(&flagCheckFSFilesConsistensy, "files-consistency", false, "Check the files consistency only (between CouchDB and Swift)") 238 checkFSCmd.Flags().BoolVar(&flagCheckFSFailFast, "fail-fast", false, "Stop the FSCK on the first error") 239 checkSharingsCmd.Flags().BoolVar(&flagCheckSharingsFast, "fast", false, "Skip the sharings FS consistency check") 240 241 RootCmd.AddCommand(checkCmdGroup) 242 }