github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/cmd/snap/cmd_validate.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2020 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package main 21 22 import ( 23 "fmt" 24 "strconv" 25 "strings" 26 27 "github.com/jessevdk/go-flags" 28 29 "github.com/snapcore/snapd/asserts" 30 "github.com/snapcore/snapd/client" 31 "github.com/snapcore/snapd/i18n" 32 ) 33 34 type cmdValidate struct { 35 clientMixin 36 Monitor bool `long:"monitor"` 37 // XXX: enforce mode is not supported yet 38 Enforce bool `long:"enforce" hidden:"yes"` 39 Forget bool `long:"forget"` 40 Positional struct { 41 ValidationSet string `positional-arg-name:"<validation-set>"` 42 } `positional-args:"yes"` 43 colorMixin 44 } 45 46 var shortValidateHelp = i18n.G("List or apply validation sets") 47 var longValidateHelp = i18n.G(` 48 The validate command lists or applies validations sets 49 `) 50 51 func init() { 52 cmd := addCommand("validate", shortValidateHelp, longValidateHelp, func() flags.Commander { return &cmdValidate{} }, colorDescs.also(map[string]string{ 53 // TRANSLATORS: This should not start with a lowercase letter. 54 "monitor": i18n.G("Monitor the given validations set"), 55 // TRANSLATORS: This should not start with a lowercase letter. 56 "enforce": i18n.G("Enforce the given validation set"), 57 // TRANSLATORS: This should not start with a lowercase letter. 58 "forget": i18n.G("Forget the given validation set"), 59 }), []argDesc{{ 60 // TRANSLATORS: This needs to begin with < and end with > 61 name: i18n.G("<validation-set>"), 62 // TRANSLATORS: This should not start with a lowercase letter. 63 desc: i18n.G("Validation set with an optional pinned sequence point, i.e. account-id/name[=seq]"), 64 }}) 65 // XXX: remove once api has landed 66 cmd.hidden = true 67 } 68 69 func splitValidationSetArg(arg string) (account, name string, seq int, err error) { 70 parts := strings.Split(arg, "=") 71 if len(parts) > 2 { 72 return "", "", 0, fmt.Errorf("cannot parse validation set, expected account/name=seq") 73 } 74 if len(parts) == 2 { 75 seq, err = strconv.Atoi(parts[1]) 76 if err != nil { 77 return "", "", 0, err 78 } 79 } 80 81 parts = strings.Split(parts[0], "/") 82 if len(parts) != 2 { 83 return "", "", 0, fmt.Errorf("expected a single account/name") 84 } 85 86 account = parts[0] 87 name = parts[1] 88 if !asserts.IsValidAccountID(account) { 89 return "", "", 0, fmt.Errorf("invalid account ID %q", account) 90 } 91 if !asserts.IsValidValidationSetName(name) { 92 return "", "", 0, fmt.Errorf("invalid validation set name %q", name) 93 } 94 95 return account, name, seq, nil 96 } 97 98 func fmtValid(res *client.ValidationSetResult) string { 99 if res.Valid { 100 return fmt.Sprint("valid") 101 } 102 return fmt.Sprint("invalid") 103 } 104 105 func fmtValidationSet(res *client.ValidationSetResult) string { 106 if res.PinnedAt == 0 { 107 return fmt.Sprintf("%s/%s", res.AccountID, res.Name) 108 } 109 return fmt.Sprintf("%s/%s=%d", res.AccountID, res.Name, res.PinnedAt) 110 } 111 112 func (cmd *cmdValidate) Execute(args []string) error { 113 // check that only one action is used at a time 114 var action string 115 for _, a := range []struct { 116 name string 117 set bool 118 }{ 119 {"monitor", cmd.Monitor}, 120 {"enforce", cmd.Enforce}, 121 {"forget", cmd.Forget}, 122 } { 123 if a.set { 124 if action != "" { 125 return fmt.Errorf("cannot use --%s and --%s together", action, a.name) 126 } 127 action = a.name 128 } 129 } 130 131 if cmd.Positional.ValidationSet == "" && action != "" { 132 return fmt.Errorf("missing validation set argument") 133 } 134 135 var accountID, name string 136 var seq int 137 var err error 138 if cmd.Positional.ValidationSet != "" { 139 accountID, name, seq, err = splitValidationSetArg(cmd.Positional.ValidationSet) 140 if err != nil { 141 return fmt.Errorf("cannot parse validation set %q: %v", cmd.Positional.ValidationSet, err) 142 } 143 } 144 145 if action != "" { 146 // forget 147 if cmd.Forget { 148 return cmd.client.ForgetValidationSet(accountID, name, seq) 149 } 150 // apply 151 opts := &client.ValidateApplyOptions{ 152 Mode: action, 153 Sequence: seq, 154 } 155 return cmd.client.ApplyValidationSet(accountID, name, opts) 156 } 157 158 // no validation set argument, print list with extended info 159 if cmd.Positional.ValidationSet == "" { 160 vsets, err := cmd.client.ListValidationsSets() 161 if err != nil { 162 return err 163 } 164 if len(vsets) == 0 { 165 fmt.Fprintln(Stderr, i18n.G("No validations are available")) 166 return nil 167 } 168 169 esc := cmd.getEscapes() 170 w := tabWriter() 171 172 // TRANSLATORS: the %s is to insert a filler escape sequence (please keep it flush to the column header, with no extra spaces) 173 fmt.Fprintf(w, i18n.G("Validation\tMode\tSeq\tCurrent\t%s\tNotes\n"), fillerPublisher(esc)) 174 for _, res := range vsets { 175 // TODO: fill notes when've clarity about them 176 var notes string 177 // doing it this way because otherwise it's a sea of %s\t%s\t%s 178 line := []string{ 179 fmtValidationSet(res), 180 res.Mode, 181 fmt.Sprintf("%d", res.Sequence), 182 fmtValid(res), 183 notes, 184 } 185 fmt.Fprintln(w, strings.Join(line, "\t")) 186 } 187 w.Flush() 188 } else { 189 vset, err := cmd.client.ValidationSet(accountID, name, seq) 190 if err != nil { 191 return err 192 } 193 fmt.Fprintf(Stdout, fmtValid(vset)) 194 // XXX: exit status 1 if invalid? 195 } 196 197 return nil 198 }