github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/cmd/snap/cmd_known.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 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 "strings" 25 26 "github.com/jessevdk/go-flags" 27 "golang.org/x/xerrors" 28 29 "github.com/snapcore/snapd/asserts" 30 "github.com/snapcore/snapd/client" 31 "github.com/snapcore/snapd/i18n" 32 "github.com/snapcore/snapd/overlord/auth" 33 "github.com/snapcore/snapd/store" 34 ) 35 36 type cmdKnown struct { 37 clientMixin 38 KnownOptions struct { 39 // XXX: how to get a list of assert types for completion? 40 AssertTypeName assertTypeName `required:"true"` 41 HeaderFilters []string `required:"0"` 42 } `positional-args:"true" required:"true"` 43 44 Remote bool `long:"remote"` 45 Direct bool `long:"direct"` 46 } 47 48 var shortKnownHelp = i18n.G("Show known assertions of the provided type") 49 var longKnownHelp = i18n.G(` 50 The known command shows known assertions of the provided type. 51 If header=value pairs are provided after the assertion type, the assertions 52 shown must also have the specified headers matching the provided values. 53 `) 54 55 func init() { 56 addCommand("known", shortKnownHelp, longKnownHelp, func() flags.Commander { 57 return &cmdKnown{} 58 }, map[string]string{ 59 // TRANSLATORS: This should not start with a lowercase letter. 60 "remote": i18n.G("Query the store for the assertion, via snapd if possible"), 61 // TRANSLATORS: This should not start with a lowercase letter. 62 "direct": i18n.G("Query the store for the assertion, without attempting to go via snapd"), 63 }, []argDesc{ 64 { 65 // TRANSLATORS: This needs to begin with < and end with > 66 name: i18n.G("<assertion type>"), 67 // TRANSLATORS: This should not start with a lowercase letter. 68 desc: i18n.G("Assertion type name"), 69 }, { 70 // TRANSLATORS: This needs to begin with < and end with > 71 name: i18n.G("<header filter>"), 72 // TRANSLATORS: This should not start with a lowercase letter. 73 desc: i18n.G("Constrain listing to those matching header=value"), 74 }, 75 }) 76 } 77 78 var storeNew = store.New 79 80 func downloadAssertion(typeName string, headers map[string]string) ([]asserts.Assertion, error) { 81 var user *auth.UserState 82 83 // FIXME: set auth context 84 var storeCtx store.DeviceAndAuthContext 85 86 at := asserts.Type(typeName) 87 if at == nil { 88 return nil, fmt.Errorf("cannot find assertion type %q", typeName) 89 } 90 primaryKeys, err := asserts.PrimaryKeyFromHeaders(at, headers) 91 if err != nil { 92 return nil, fmt.Errorf("cannot query remote assertion: %v", err) 93 } 94 95 sto := storeNew(nil, storeCtx) 96 as, err := sto.Assertion(at, primaryKeys, user) 97 if err != nil { 98 return nil, err 99 } 100 101 return []asserts.Assertion{as}, nil 102 } 103 104 func (x *cmdKnown) Execute(args []string) error { 105 if len(args) > 0 { 106 return ErrExtraArgs 107 } 108 109 // TODO: share this kind of parsing once it's clearer how often is used in snap 110 headers := map[string]string{} 111 for _, headerFilter := range x.KnownOptions.HeaderFilters { 112 parts := strings.SplitN(headerFilter, "=", 2) 113 if len(parts) != 2 { 114 return fmt.Errorf(i18n.G("invalid header filter: %q (want key=value)"), headerFilter) 115 } 116 headers[parts[0]] = parts[1] 117 } 118 119 var assertions []asserts.Assertion 120 var err error 121 switch { 122 case x.Remote && !x.Direct: 123 // --remote will query snapd 124 assertions, err = x.client.Known(string(x.KnownOptions.AssertTypeName), headers, &client.KnownOptions{Remote: true}) 125 // if snapd is unavailable automatically fallback 126 var connErr client.ConnectionError 127 if xerrors.As(err, &connErr) { 128 assertions, err = downloadAssertion(string(x.KnownOptions.AssertTypeName), headers) 129 } 130 case x.Direct: 131 // --direct implies remote 132 assertions, err = downloadAssertion(string(x.KnownOptions.AssertTypeName), headers) 133 default: 134 // default is to look only local 135 assertions, err = x.client.Known(string(x.KnownOptions.AssertTypeName), headers, nil) 136 } 137 if err != nil { 138 return err 139 } 140 141 enc := asserts.NewEncoder(Stdout) 142 for _, a := range assertions { 143 enc.Encode(a) 144 } 145 146 return nil 147 }