File: cmd_model.go

package info (click to toggle)
snapd 2.72-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 80,412 kB
  • sloc: sh: 16,506; ansic: 16,211; python: 11,213; makefile: 1,919; exp: 190; awk: 58; xml: 22
file content (175 lines) | stat: -rw-r--r-- 5,082 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
 * Copyright (C) 2019 Canonical Ltd
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package main

import (
	"errors"
	"fmt"

	"github.com/jessevdk/go-flags"

	"github.com/snapcore/snapd/client"
	"github.com/snapcore/snapd/client/clientutil"
	"github.com/snapcore/snapd/i18n"
)

var (
	shortModelHelp = i18n.G("Get the active model for this device")
	longModelHelp  = i18n.G(`
The model command returns the active model assertion information for this
device.

By default, only the essential model identification information is
included in the output, but this can be expanded to include all of an
assertion's non-meta headers.

The verbose output is presented in a structured, yaml-like format.

Similarly, the active serial assertion can be used for the output instead of the
model assertion.
`)

	errNoMainAssertion    = errors.New(i18n.G("device not ready yet (no assertions found)"))
	errNoSerial           = errors.New(i18n.G("device not registered yet (no serial assertion found)"))
	errNoVerboseAssertion = errors.New(i18n.G("cannot use --verbose with --assertion"))
)

// cmdModelFormatter implements the interface required by clientutil.Print*
// functions, as it formats the output it requires some extra information from
// environment its called from.
type cmdModelFormatter struct {
	client *client.Client
	esc    *escapes
}

func (mf cmdModelFormatter) GetEscapedDash() string {
	return mf.esc.dash
}

func (mf cmdModelFormatter) LongPublisher(storeAccountID string) string {
	storeAccount, err := mf.client.StoreAccount(storeAccountID)
	if err != nil {
		return ""
	}
	// use the longPublisher helper to format the brand store account
	// like we do in `snap info`
	return longPublisher(mf.esc, storeAccount)
}

type cmdModel struct {
	clientMixin
	timeMixin
	colorMixin

	Serial    bool `long:"serial"`
	Verbose   bool `long:"verbose"`
	Assertion bool `long:"assertion"`
}

func init() {
	addCommand("model",
		shortModelHelp,
		longModelHelp,
		func() flags.Commander {
			return &cmdModel{}
		}, colorDescs.also(timeDescs).also(map[string]string{
			"assertion": i18n.G("Print the raw assertion."),
			"verbose":   i18n.G("Print all specific assertion fields."),
			"serial": i18n.G(
				"Print the serial assertion instead of the model assertion."),
		}),
		[]argDesc{},
	)
}

func (x *cmdModel) Execute(args []string) error {
	if x.Verbose && x.Assertion {
		// can't do a verbose mode for the assertion
		return errNoVerboseAssertion
	}

	serialAssertion, serialErr := x.client.CurrentSerialAssertion()
	modelAssertion, modelErr := x.client.CurrentModelAssertion()

	// if we didn't get a model assertion bail early
	if modelErr != nil {
		if client.IsAssertionNotFoundError(modelErr) {
			// device is not registered yet - use specific error message
			return errNoMainAssertion
		}
		return modelErr
	}

	// if the serial assertion error is anything other than not found, also
	// bail early
	// the serial assertion not being found may not be fatal
	if serialErr != nil && !client.IsAssertionNotFoundError(serialErr) {
		return serialErr
	}

	if x.Assertion {
		// if we are using the serial assertion and we specifically didn't find the
		// serial assertion, bail with specific error
		if x.Serial && client.IsAssertionNotFoundError(serialErr) {
			return errNoMainAssertion
		}
	}

	termWidth, _ := termSize()
	termWidth -= 3
	if termWidth > 100 {
		// any wider than this and it gets hard to read
		termWidth = 100
	}

	w := tabWriter()

	if x.Serial && client.IsAssertionNotFoundError(serialErr) {
		// for serial assertion, the primary keys are output (model and
		// brand-id), but if we didn't find the serial assertion then we still
		// output the brand-id and model from the model assertion, but also
		// return a devNotReady error
		fmt.Fprintf(w, "brand-id:\t%s\n", modelAssertion.HeaderString("brand-id"))
		fmt.Fprintf(w, "model:\t%s\n", modelAssertion.HeaderString("model"))
		w.Flush()
		return errNoSerial
	}

	modelFormatter := cmdModelFormatter{
		esc:    x.getEscapes(),
		client: x.client,
	}
	opts := clientutil.PrintModelAssertionOptions{
		TermWidth: termWidth,
		AbsTime:   x.AbsTime,
		Verbose:   x.Verbose,
		Assertion: x.Assertion,
	}
	if x.Serial {
		if err := clientutil.PrintSerialAssertionYAML(w, *serialAssertion, modelFormatter, opts); err != nil {
			return err
		}
	} else {
		if err := clientutil.PrintModelAssertion(w, *modelAssertion, serialAssertion, modelFormatter, opts); err != nil {
			return err
		}
	}
	return w.Flush()
}