github.com/tompreston/snapd@v0.0.0-20210817193607-954edfcb9611/cmd/snap/cmd_debug_seeding.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 "bytes" 24 "encoding/json" 25 "fmt" 26 "time" 27 28 "github.com/jessevdk/go-flags" 29 30 "github.com/snapcore/snapd/interfaces" 31 ) 32 33 type cmdSeeding struct { 34 clientMixin 35 unicodeMixin 36 } 37 38 func init() { 39 cmd := addDebugCommand("seeding", 40 "(internal) obtain seeding and preseeding details", 41 "(internal) obtain seeding and preseeding details", 42 func() flags.Commander { 43 return &cmdSeeding{} 44 }, nil, nil) 45 cmd.hidden = true 46 } 47 48 func (x *cmdSeeding) Execute(args []string) error { 49 esc := x.getEscapes() 50 51 if len(args) > 0 { 52 return ErrExtraArgs 53 } 54 var resp struct { 55 Seeded bool `json:"seeded,omitempty"` 56 Preseeded bool `json:"preseeded,omitempty"` 57 PreseedStartTime *time.Time `json:"preseed-start-time,omitempty"` 58 PreseedTime *time.Time `json:"preseed-time,omitempty"` 59 SeedStartTime *time.Time `json:"seed-start-time,omitempty"` 60 SeedRestartTime *time.Time `json:"seed-restart-time,omitempty"` 61 SeedTime *time.Time `json:"seed-time,omitempty"` 62 // use json.RawMessage to delay unmarshal'ing to the interfaces pkg 63 PreseedSystemKey *json.RawMessage `json:"preseed-system-key,omitempty"` 64 SeedRestartSystemKey *json.RawMessage `json:"seed-restart-system-key,omitempty"` 65 66 SeedError string `json:"seed-error,omitempty"` 67 } 68 if err := x.client.DebugGet("seeding", &resp, nil); err != nil { 69 return err 70 } 71 72 w := tabWriter() 73 74 // show seeded and preseeded keys 75 fmt.Fprintf(w, "seeded:\t%v\n", resp.Seeded) 76 if resp.SeedError != "" { 77 // print seed-error 78 termWidth, _ := termSize() 79 termWidth -= 3 80 if termWidth > 100 { 81 // any wider than this and it gets hard to read 82 termWidth = 100 83 } 84 fmt.Fprintln(w, "seed-error: |") 85 // XXX: reuse/abuse 86 printDescr(w, resp.SeedError, termWidth) 87 } 88 89 fmt.Fprintf(w, "preseeded:\t%v\n", resp.Preseeded) 90 91 // calculate the time spent preseeding (if preseeded) and seeding 92 // for the preseeded case, we use the seed-restart-time as the start time 93 // to show how long we spent only after booting the preseeded image 94 95 // if we are missing time values, we will default to showing "-" for the 96 // duration 97 seedDuration := esc.dash 98 if resp.Preseeded { 99 if resp.PreseedTime != nil && resp.PreseedStartTime != nil { 100 preseedDuration := resp.PreseedTime.Sub(*resp.PreseedStartTime).Round(time.Millisecond) 101 fmt.Fprintf(w, "image-preseeding:\t%v\n", preseedDuration) 102 } else { 103 fmt.Fprintf(w, "image-preseeding:\t%s\n", esc.dash) 104 } 105 106 if resp.SeedTime != nil && resp.SeedRestartTime != nil { 107 seedDuration = fmt.Sprintf("%v", resp.SeedTime.Sub(*resp.SeedRestartTime).Round(time.Millisecond)) 108 } 109 } else if resp.SeedTime != nil && resp.SeedStartTime != nil { 110 seedDuration = fmt.Sprintf("%v", resp.SeedTime.Sub(*resp.SeedStartTime).Round(time.Millisecond)) 111 } 112 fmt.Fprintf(w, "seed-completion:\t%s\n", seedDuration) 113 114 // we flush the tabwriter now because if we have more output, it will be 115 // the system keys, which are JSON and thus will never display cleanly in 116 // line with the other keys we did above 117 w.Flush() 118 119 // only compare system-keys if preseeded and the system-keys exist 120 // they might not exist if this command is used on a system that was 121 // preseeded with an older version of snapd, i.e. while this feature is 122 // being rolled out, we may be preseeding images via old snapd deb, but with 123 // new snapd snap 124 if resp.Preseeded && resp.SeedRestartSystemKey != nil && resp.PreseedSystemKey != nil { 125 // only show them if they don't match, so first unmarshal them so we can 126 // properly compare them 127 128 // we use raw json messages here so that the interfaces pkg can do the 129 // real unmarshalling to a real systemKey interface{} that can be 130 // compared with SystemKeysMatch, if we had instead unmarshalled here, 131 // we would have to remarshal the map[string]interface{} we got above 132 // and then pass those bytes back to the interfaces pkg which is awkward 133 seedSk, err := interfaces.UnmarshalJSONSystemKey(bytes.NewReader(*resp.SeedRestartSystemKey)) 134 if err != nil { 135 return err 136 } 137 138 preseedSk, err := interfaces.UnmarshalJSONSystemKey(bytes.NewReader(*resp.PreseedSystemKey)) 139 if err != nil { 140 return err 141 } 142 143 match, err := interfaces.SystemKeysMatch(preseedSk, seedSk) 144 if err != nil { 145 return err 146 } 147 if !match { 148 // mismatch, display the different keys 149 var preseedSkJSON, seedRestartSkJSON bytes.Buffer 150 json.Indent(&preseedSkJSON, *resp.PreseedSystemKey, "", " ") 151 fmt.Fprintf(Stdout, "preseed-system-key: ") 152 preseedSkJSON.WriteTo(Stdout) 153 fmt.Fprintln(Stdout, "") 154 155 json.Indent(&seedRestartSkJSON, *resp.SeedRestartSystemKey, "", " ") 156 fmt.Fprintf(Stdout, "seed-restart-system-key: ") 157 seedRestartSkJSON.WriteTo(Stdout) 158 fmt.Fprintln(Stdout, "") 159 } 160 } 161 162 return nil 163 }