Files
snapd/client/clientutil/modelinfo_test.go
Philip Meulengracht 48fb0e2b97 client/clientutil: review feedback
improve docstrings, add missing docstrings, use nil for serial when the assertion is not provided
2022-05-12 10:33:32 +02:00

503 lines
13 KiB
Go

// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2022 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 clientutil_test
import (
"bytes"
"fmt"
"strings"
"text/tabwriter"
"time"
. "gopkg.in/check.v1"
"github.com/snapcore/snapd/asserts"
"github.com/snapcore/snapd/asserts/assertstest"
"github.com/snapcore/snapd/client/clientutil"
"github.com/snapcore/snapd/strutil"
"github.com/snapcore/snapd/testutil"
"github.com/snapcore/snapd/timeutil"
)
const (
modelExample = "type: model\n" +
"authority-id: brand-id1\n" +
"series: 16\n" +
"brand-id: brand-id1\n" +
"model: baz-3000\n" +
"display-name: Baz 3000\n" +
"architecture: amd64\n" +
"gadget: brand-gadget\n" +
"base: core18\n" +
"kernel: baz-linux\n" +
"store: brand-store\n" +
"serial-authority:\n - generic\n" +
"system-user-authority: *\n" +
"required-snaps:\n - foo\n - bar\n" +
"TSLINE" +
"body-length: 0\n" +
"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" +
"\n\n" +
"AXNpZw=="
core20ModelExample = `type: model
authority-id: brand-id1
series: 16
brand-id: brand-id1
model: baz-3000
display-name: Baz 3000
architecture: amd64
system-user-authority:
- partner
- brand-id1
base: core20
store: brand-store
grade: dangerous
storage-safety: prefer-unencrypted
snaps:
-
name: baz-linux
id: bazlinuxidididididididididididid
type: kernel
default-channel: 20
-
name: brand-gadget
id: brandgadgetdidididididididididid
type: gadget
-
name: other-base
id: otherbasedididididididididididid
type: base
modes:
- run
presence: required
-
name: nm
id: nmididididididididididididididid
modes:
- ephemeral
- run
default-channel: 1.0
-
name: myapp
id: myappdididididididididididididid
type: app
default-channel: 2.0
-
name: myappopt
id: myappoptidididididididididididid
type: app
presence: optional
grade: secured
storage-safety: encrypted
` + "TSLINE" +
"body-length: 0\n" +
"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" +
"\n\n" +
"AXNpZw=="
serialExample = "type: serial\n" +
"authority-id: brand-id1\n" +
"brand-id: brand-id1\n" +
"model: baz-3000\n" +
"serial: 2700\n" +
"device-key:\n DEVICEKEY\n" +
"device-key-sha3-384: KEYID\n" +
"TSLINE" +
"body-length: 2\n" +
"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" +
"HW" +
"\n\n" +
"AXNpZw=="
)
type testFormatter struct {
}
func (tf testFormatter) GetEscapedDash() string {
return "--"
}
func (tf testFormatter) LongPublisher(storeAccountID string) string {
return storeAccountID
}
type modelInfoSuite struct {
testutil.BaseTest
ts time.Time
tsLine string
deviceKey asserts.PrivateKey
encodedDevKey string
formatter testFormatter
}
var testPrivKey2, _ = assertstest.GenerateKey(752)
var _ = Suite(&modelInfoSuite{})
func (s *modelInfoSuite) SetUpTest(c *C) {
s.BaseTest.SetUpTest(c)
s.ts = time.Now().Truncate(time.Second).UTC()
s.tsLine = "timestamp: " + s.ts.Format(time.RFC3339) + "\n"
s.deviceKey = testPrivKey2
encodedPubKey, err := asserts.EncodePublicKey(s.deviceKey.PublicKey())
c.Assert(err, IsNil)
s.encodedDevKey = string(encodedPubKey)
}
func (s *modelInfoSuite) getModel(c *C, modelText string) asserts.Model {
encoded := strings.Replace(modelText, "TSLINE", s.tsLine, 1)
a, err := asserts.Decode([]byte(encoded))
c.Assert(err, IsNil)
c.Check(a.Type(), Equals, asserts.ModelType)
model := a.(*asserts.Model)
return *model
}
func (s *modelInfoSuite) getDeviceKey(padding string, termWidth int) string {
var buffer bytes.Buffer
w := tabwriter.NewWriter(&buffer, 0, 5, 4, ' ', 0)
key := strings.Join(strings.Split(s.encodedDevKey, "\n"), "")
strutil.WordWrapPadded(w, []rune(key), padding, termWidth)
return buffer.String()
}
func (s *modelInfoSuite) getSerial(c *C, serialText string) asserts.Serial {
encoded := strings.Replace(serialText, "TSLINE", s.tsLine, 1)
encoded = strings.Replace(encoded, "DEVICEKEY", strings.Replace(s.encodedDevKey, "\n", "\n ", -1), 1)
encoded = strings.Replace(encoded, "KEYID", s.deviceKey.PublicKey().ID(), 1)
a, err := asserts.Decode([]byte(encoded))
c.Assert(err, IsNil)
c.Check(a.Type(), Equals, asserts.SerialType)
serial := a.(*asserts.Serial)
return *serial
}
func (s *modelInfoSuite) TestPrintModelYAML(c *C) {
var buffer bytes.Buffer
w := tabwriter.NewWriter(&buffer, 0, 5, 4, ' ', 0)
model := s.getModel(c, modelExample)
options := clientutil.PrintModelAssertionOptions{
TermWidth: 80,
Verbose: false,
AbsTime: false,
Assertion: false,
}
err := clientutil.PrintModelAssertion(w, model, nil, s.formatter, options)
c.Assert(err, IsNil)
c.Check(buffer.String(), Equals, `brand brand-id1
model Baz 3000 (baz-3000)
serial - (device not registered yet)
`)
}
func (s *modelInfoSuite) TestPrintModelWithSerialYAML(c *C) {
var buffer bytes.Buffer
w := tabwriter.NewWriter(&buffer, 0, 5, 4, ' ', 0)
model := s.getModel(c, modelExample)
serial := s.getSerial(c, serialExample)
options := clientutil.PrintModelAssertionOptions{
TermWidth: 80,
Verbose: false,
AbsTime: false,
Assertion: false,
}
err := clientutil.PrintModelAssertion(w, model, &serial, s.formatter, options)
c.Assert(err, IsNil)
c.Check(buffer.String(), Equals, `brand brand-id1
model Baz 3000 (baz-3000)
serial 2700
`)
}
func (s *modelInfoSuite) TestPrintModelYAMLVerbose(c *C) {
var buffer bytes.Buffer
w := tabwriter.NewWriter(&buffer, 0, 5, 4, ' ', 0)
model := s.getModel(c, core20ModelExample)
options := clientutil.PrintModelAssertionOptions{
TermWidth: 80,
Verbose: true,
AbsTime: false,
Assertion: false,
}
err := clientutil.PrintModelAssertion(w, model, nil, s.formatter, options)
c.Assert(err, IsNil)
c.Check(buffer.String(), Equals, fmt.Sprintf(`brand-id: brand-id1
model: baz-3000
grade: dangerous
storage-safety: prefer-unencrypted
serial: -- (device not registered yet)
architecture: amd64
base: core20
display-name: Baz 3000
store: brand-store
system-user-authority:
- partner
- brand-id1
timestamp: %s
snaps:
- name: baz-linux
id: bazlinuxidididididididididididid
type: kernel
default-channel: 20
- name: brand-gadget
id: brandgadgetdidididididididididid
type: gadget
- name: other-base
id: otherbasedididididididididididid
type: base
presence: required
modes: [run]
- name: nm
id: nmididididididididididididididid
default-channel: 1.0
modes: [ephemeral, run]
- name: myapp
id: myappdididididididididididididid
type: app
default-channel: 2.0
- name: myappopt
id: myappoptidididididididididididid
type: app
presence: optional
`, timeutil.Human(s.ts)))
}
func (s *modelInfoSuite) TestPrintModelAssertion(c *C) {
var buffer bytes.Buffer
w := tabwriter.NewWriter(&buffer, 0, 5, 4, ' ', 0)
model := s.getModel(c, modelExample)
options := clientutil.PrintModelAssertionOptions{
TermWidth: 80,
Verbose: false,
AbsTime: false,
Assertion: true,
}
err := clientutil.PrintModelAssertion(w, model, nil, s.formatter, options)
c.Assert(err, IsNil)
c.Check(buffer.String(), Equals, fmt.Sprintf(`type: model
authority-id: brand-id1
series: 16
brand-id: brand-id1
model: baz-3000
display-name: Baz 3000
architecture: amd64
gadget: brand-gadget
base: core18
kernel: baz-linux
store: brand-store
serial-authority:
- generic
system-user-authority: *
required-snaps:
- foo
- bar
timestamp: %s
body-length: 0
sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij
`, s.ts.Format(time.RFC3339)))
}
func (s *modelInfoSuite) TestPrintSerialYAML(c *C) {
var buffer bytes.Buffer
w := tabwriter.NewWriter(&buffer, 0, 5, 4, ' ', 0)
serial := s.getSerial(c, serialExample)
options := clientutil.PrintModelAssertionOptions{
TermWidth: 80,
Verbose: false,
AbsTime: false,
Assertion: false,
}
err := clientutil.PrintSerialAssertionYAML(w, serial, s.formatter, options)
c.Assert(err, IsNil)
c.Check(buffer.String(), Equals, `brand-id: brand-id1
model: baz-3000
serial: 2700
`)
}
func (s *modelInfoSuite) TestPrintSerialAssertion(c *C) {
var buffer bytes.Buffer
w := tabwriter.NewWriter(&buffer, 0, 5, 4, ' ', 0)
serial := s.getSerial(c, serialExample)
options := clientutil.PrintModelAssertionOptions{
TermWidth: 80,
Verbose: false,
AbsTime: false,
Assertion: true,
}
err := clientutil.PrintSerialAssertionYAML(w, serial, s.formatter, options)
c.Assert(err, IsNil)
c.Check(buffer.String(), Equals, fmt.Sprintf(`type: serial
authority-id: brand-id1
brand-id: brand-id1
model: baz-3000
serial: 2700
device-key:
%sdevice-key-sha3-384: %s
timestamp: %s
body-length: 2
sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij
HW
`, s.getDeviceKey(" ", options.TermWidth), s.deviceKey.PublicKey().ID(), s.ts.Format(time.RFC3339)))
}
func (s *modelInfoSuite) TestPrintSerialVerboseYAML(c *C) {
var buffer bytes.Buffer
w := tabwriter.NewWriter(&buffer, 0, 5, 4, ' ', 0)
serial := s.getSerial(c, serialExample)
options := clientutil.PrintModelAssertionOptions{
TermWidth: 80,
Verbose: true,
AbsTime: false,
Assertion: false,
}
err := clientutil.PrintSerialAssertionYAML(w, serial, s.formatter, options)
c.Assert(err, IsNil)
c.Check(buffer.String(), Equals, fmt.Sprintf(`brand-id: brand-id1
model: baz-3000
serial: 2700
timestamp: %s
device-key-sha3-384: |
%s
device-key: |
%s`, timeutil.Human(s.ts), s.deviceKey.PublicKey().ID(), s.getDeviceKey(" ", options.TermWidth)))
}
func (s *modelInfoSuite) TestPrintModelJSON(c *C) {
var buffer bytes.Buffer
w := tabwriter.NewWriter(&buffer, 0, 5, 4, ' ', 0)
model := s.getModel(c, modelExample)
// For JSON verbose is always implictly true
options := clientutil.PrintModelAssertionOptions{
TermWidth: 80,
Verbose: true,
AbsTime: false,
Assertion: false,
}
err := clientutil.PrintModelAssertionJSON(w, model, nil, options)
c.Assert(err, IsNil)
c.Check(buffer.String(), Equals, fmt.Sprintf(`{
"architecture": "amd64",
"base": "core18",
"brand-id": "brand-id1",
"display-name": "Baz 3000",
"gadget": "brand-gadget",
"kernel": "baz-linux",
"model": "baz-3000",
"required-snaps": [
"foo",
"bar"
],
"serial": null,
"store": "brand-store",
"system-user-authority": "*",
"timestamp": "%s"
}`, s.ts.Format(time.RFC3339)))
}
func (s *modelInfoSuite) TestPrintModelWithSerialJSON(c *C) {
var buffer bytes.Buffer
w := tabwriter.NewWriter(&buffer, 0, 5, 4, ' ', 0)
model := s.getModel(c, modelExample)
serial := s.getSerial(c, serialExample)
// For JSON verbose is always implictly true
options := clientutil.PrintModelAssertionOptions{
TermWidth: 80,
Verbose: true,
AbsTime: false,
Assertion: false,
}
err := clientutil.PrintModelAssertionJSON(w, model, &serial, options)
c.Assert(err, IsNil)
c.Check(buffer.String(), Equals, fmt.Sprintf(`{
"architecture": "amd64",
"base": "core18",
"brand-id": "brand-id1",
"display-name": "Baz 3000",
"gadget": "brand-gadget",
"kernel": "baz-linux",
"model": "baz-3000",
"required-snaps": [
"foo",
"bar"
],
"serial": "2700",
"store": "brand-store",
"system-user-authority": "*",
"timestamp": "%s"
}`, s.ts.Format(time.RFC3339)))
}
func (s *modelInfoSuite) TestPrintModelJSONAssertion(c *C) {
var buffer bytes.Buffer
w := tabwriter.NewWriter(&buffer, 0, 5, 4, ' ', 0)
model := s.getModel(c, modelExample)
// For JSON verbose is always implictly true
options := clientutil.PrintModelAssertionOptions{
TermWidth: 80,
Verbose: true,
AbsTime: false,
Assertion: true,
}
err := clientutil.PrintModelAssertionJSON(w, model, nil, options)
c.Assert(err, IsNil)
c.Check(buffer.String(), Equals, fmt.Sprintf(`{
"headers": {
"architecture": "amd64",
"authority-id": "brand-id1",
"base": "core18",
"body-length": "0",
"brand-id": "brand-id1",
"display-name": "Baz 3000",
"gadget": "brand-gadget",
"kernel": "baz-linux",
"model": "baz-3000",
"required-snaps": [
"foo",
"bar"
],
"serial-authority": [
"generic"
],
"series": "16",
"sign-key-sha3-384": "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
"store": "brand-store",
"system-user-authority": "*",
"timestamp": "%s",
"type": "model"
}
}`, s.ts.Format(time.RFC3339)))
}