Merge lp:~mvo/goget-ubuntu-touch/minimal-first-boot into lp:goget-ubuntu-touch

Proposed by Michael Vogt
Status: Needs review
Proposed branch: lp:~mvo/goget-ubuntu-touch/minimal-first-boot
Merge into: lp:goget-ubuntu-touch
Diff against target: 1819 lines (+1145/-164) (has conflicts)
17 files modified
debian/changelog (+15/-0)
debian/control (+14/-0)
dependencies.tsv (+6/-1)
diskimage/bootloader.go (+21/-12)
diskimage/common.go (+99/-80)
diskimage/common_test.go (+14/-14)
diskimage/core_grub.go (+31/-27)
diskimage/core_uboot.go (+32/-16)
diskimage/partition.go (+1/-0)
snapcraft.yaml (+35/-0)
ubuntu-device-flash/common.go (+3/-3)
ubuntu-device-flash/core.go (+12/-5)
ubuntu-device-flash/helpers.go (+89/-0)
ubuntu-device-flash/helpers_test.go (+135/-0)
ubuntu-device-flash/main.go (+1/-1)
ubuntu-device-flash/snappy.go (+513/-5)
ubuntu-device-flash/snappy_compat_yaml.go (+124/-0)
Text conflict in debian/changelog
Text conflict in debian/control
Text conflict in dependencies.tsv
Text conflict in diskimage/bootloader.go
Text conflict in diskimage/common.go
Text conflict in diskimage/core_grub.go
Text conflict in diskimage/core_uboot.go
Text conflict in ubuntu-device-flash/core.go
Text conflict in ubuntu-device-flash/snappy.go
To merge this branch: bzr merge lp:~mvo/goget-ubuntu-touch/minimal-first-boot
Reviewer Review Type Date Requested Status
Ubuntu Phablet Team Pending
Review via email: mp+296646@code.launchpad.net

Description of the change

This implements creating a ubuntu core 16 image with minimal support from snapd (grub only for now). Most importantly the snap install part is dropped and the code only downloads and drops the files into the right location.

This is the basis for the `firstboot` part of snapd. Also note that this is only a temporary tree that will be replaced with ubuntu-image. However ubuntu-image can still use this code (and layout) as a blueprint how to generate the basic dir layout. Plus for ubuntu-image we will need something like "snap-bootstrap" as the python-code will not be able to call into the go code.

To post a comment you must log in.
283. By Michael Vogt

prefix first-boot snaps with "first-boot"

284. By Michael Vogt

use .sideinfo instead of .meta

285. By Michael Vogt

make u-d-f install the snap under exactly the same name as snappy would expect it

286. By Michael Vogt

only create .sideinfo if we actually have sideinfo

287. By Michael Vogt

use `snap boostrap` for os,kernel,gadget install

288. By Michael Vogt

add channel and store-id to boostrap.yaml

289. By Michael Vogt

add extra installs

290. By Michael Vogt

remove dead code

291. By Michael Vogt

use new `snap weld`

292. By Michael Vogt

update to latest create-image api

293. By Michael Vogt

cherry pick r290 from lp:~mvo/goget-ubuntu-touch/minimal-first-boot-no-prepare-image

294. By Michael Vogt

fix copy

295. By Michael Vogt

take model assertion as input instead of of --kernel/--os

296. By Michael Vogt

ubuntu-device-flash/main.go: fix cmdline parsing

297. By Michael Vogt

add snapcraft.yaml that includes the "snap" command

298. By Michael Vogt

remove some dead code

299. By Michael Vogt

remove boot-assets/uEnv.txt from compat yaml

Unmerged revisions

299. By Michael Vogt

remove boot-assets/uEnv.txt from compat yaml

298. By Michael Vogt

remove some dead code

297. By Michael Vogt

add snapcraft.yaml that includes the "snap" command

296. By Michael Vogt

ubuntu-device-flash/main.go: fix cmdline parsing

295. By Michael Vogt

take model assertion as input instead of of --kernel/--os

294. By Michael Vogt

fix copy

293. By Michael Vogt

cherry pick r290 from lp:~mvo/goget-ubuntu-touch/minimal-first-boot-no-prepare-image

292. By Michael Vogt

update to latest create-image api

291. By Michael Vogt

use new `snap weld`

290. By Michael Vogt

remove dead code

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/changelog'
2--- debian/changelog 2016-08-05 13:21:10 +0000
3+++ debian/changelog 2016-09-04 15:15:47 +0000
4@@ -1,3 +1,4 @@
5+<<<<<<< TREE
6 goget-ubuntu-touch (0.34+16.10.20160805-0ubuntu1) yakkety; urgency=medium
7
8 [ Andrea Bernabei ]
9@@ -34,6 +35,20 @@
10
11 -- Sergio Schvezov <sergio.schvezov@canonical.com> Wed, 18 Nov 2015 09:32:39 -0300
12
13+=======
14+goget-ubuntu-touch (0.34-0ubuntu1+ppa2) xenial; urgency=medium
15+
16+ * add missing golang-golang-x-crypto-dev b-d
17+
18+ -- Michael Vogt <michael.vogt@ubuntu.com> Wed, 06 Apr 2016 18:06:31 +0200
19+
20+goget-ubuntu-touch (0.34-0ubuntu1+ppa1) xenial; urgency=medium
21+
22+ * ppa upload of the all-snaps branch
23+
24+ -- Michael Vogt <michael.vogt@ubuntu.com> Wed, 06 Apr 2016 17:58:42 +0200
25+
26+>>>>>>> MERGE-SOURCE
27 goget-ubuntu-touch (0.33-0ubuntu1) xenial; urgency=medium
28
29 * Exit 1 on parameter errors (LP: #1473333)
30
31=== modified file 'debian/control'
32--- debian/control 2016-04-05 07:51:47 +0000
33+++ debian/control 2016-09-04 15:15:47 +0000
34@@ -9,9 +9,23 @@
35 golang-gettext-dev,
36 golang-go-flags-dev,
37 golang-gocheck-dev,
38+<<<<<<< TREE
39+=======
40+ golang-github-mvo5-goconfigparser-dev,
41+>>>>>>> MERGE-SOURCE
42 golang-juju-loggo-dev,
43 golang-pb-dev,
44+<<<<<<< TREE
45+=======
46+ golang-github-ubuntu-core-snappy-dev,
47+ golang-github-mvo5-uboot-go-dev,
48+>>>>>>> MERGE-SOURCE
49 golang-yaml.v2-dev,
50+<<<<<<< TREE
51+=======
52+ golang-golang-x-crypto-dev,
53+ ubuntu-snappy-cli,
54+>>>>>>> MERGE-SOURCE
55 Standards-Version: 3.9.5
56 Homepage: https://launchpad.net/goget-ubuntu-touch
57 Vcs-Browser: http://bazaar.launchpad.net/~phablet-team/goget-ubuntu-touch/trunk/files
58
59=== modified file 'dependencies.tsv'
60--- dependencies.tsv 2016-04-04 15:54:40 +0000
61+++ dependencies.tsv 2016-09-04 15:15:47 +0000
62@@ -1,6 +1,11 @@
63-github.com/blakesmith/ar git 8bd4349a67f2533b078dbc524689d15dba0f4659 2015-03-11T14:59:44Z
64 github.com/cheggaaa/pb git da1f27ad1d9509b16f65f52fd9d8138b0f2dc7b2 2015-08-13T11:06:09Z
65 github.com/gosexy/gettext git 98b7b91596d20b96909e6b60d57411547dd9959c 2013-02-21T11:21:43Z
66 github.com/jessevdk/go-flags git 4047bd797dd935ae2b557a79cc43f223066c9659 2015-10-18T21:15:10Z
67+<<<<<<< TREE
68+=======
69+github.com/mvo5/goconfigparser git 26426272dda20cc76aa1fa44286dc743d2972fe8 2015-02-12T09:37:50Z
70+github.com/mvo5/uboot-go git 69978a3e4b05cca9d7cfee489b3453dfed45e72c 2015-07-23T08:17:10Z
71+github.com/ubuntu-core/snappy git a6e683cb4eca7027b331713c2f10e26a31050f20 2016-01-14T16:39:06Z
72+>>>>>>> MERGE-SOURCE
73 gopkg.in/yaml.v2 git 7ad95dd0798a40da1ccdff6dff35fd177b5edf40 2015-06-24T10:29:02Z
74 launchpad.net/gocheck bzr gustavo@niemeyer.net-20140225173054-xu9zlkf9kxhvow02 87
75
76=== modified file 'diskimage/bootloader.go'
77--- diskimage/bootloader.go 2016-04-05 09:32:48 +0000
78+++ diskimage/bootloader.go 2016-09-04 15:15:47 +0000
79@@ -89,21 +89,31 @@
80 printOut("Setting up raw boot asset partitions for", imagePath, "...")
81 var part int = partCount
82
83- for _, asset := range rawPartitions {
84+<<<<<<< TREE
85+ for _, asset := range rawPartitions {
86+=======
87+ pos := 0
88+ for _, asset := range rawPartitions {
89+>>>>>>> MERGE-SOURCE
90 part += 1
91
92- size, err := strconv.Atoi(asset.Size)
93- if err != nil {
94- return err
95- }
96- size = size * 2
97-
98 printOut("creating partition:", asset.Name)
99-
100- opts := fmt.Sprintf("0:0:+%d", size)
101- if err := exec.Command("sgdisk", "-a", "1", "-n", opts, imagePath).Run(); err != nil {
102+ // override position if specified, otherwise use the
103+ // previous position
104+ if asset.Pos > 0 {
105+ pos = asset.Pos
106+ }
107+
108+ // why * 2?
109+ size := asset.Size * 2
110+
111+ opts := fmt.Sprintf("%d:%d:+%d", part, pos, size)
112+ if output, err := exec.Command("sgdisk", "-a", "1", "-n", opts, imagePath).CombinedOutput(); err != nil {
113+ println(string(output))
114 return err
115 }
116+ // move postition forward
117+ pos += size
118
119 opts = fmt.Sprintf("%d:%s", part, asset.Name)
120 if err := exec.Command("sgdisk", "-c", opts, imagePath).Run(); err != nil {
121@@ -114,10 +124,9 @@
122 if err := exec.Command("sgdisk", "-t", opts, imagePath).Run(); err != nil {
123 return err
124 }
125-
126 }
127
128- printOut("sorting partitions")
129+ printOut("aligning partitions")
130 if err := exec.Command("sgdisk", "-s", imagePath).Run(); err != nil {
131 return err
132 }
133
134=== modified file 'diskimage/common.go'
135--- diskimage/common.go 2016-04-05 09:32:48 +0000
136+++ diskimage/common.go 2016-09-04 15:15:47 +0000
137@@ -9,7 +9,6 @@
138
139 import (
140 "bufio"
141- "errors"
142 "fmt"
143 "io/ioutil"
144 "os"
145@@ -17,8 +16,6 @@
146 "path/filepath"
147 "strings"
148 "syscall"
149-
150- "launchpad.net/goget-ubuntu-touch/sysutils"
151 )
152
153 // This program is free software: you can redistribute it and/or modify it
154@@ -47,10 +44,6 @@
155 initrdFileName = "initrd.img"
156 )
157
158-const (
159- partLayoutSystemAB = "system-AB"
160-)
161-
162 var (
163 syscallSync = syscall.Sync
164 )
165@@ -88,9 +81,16 @@
166 }
167
168 type BootAssetRawPartitions struct {
169+<<<<<<< TREE
170 Name string `yaml:"name"`
171 Size string `yaml:"size"`
172 Type string `yaml:"type"`
173+=======
174+ Name string `yaml:"name"`
175+ Size int `yaml:"size"`
176+ Pos int `yaml:"pos"`
177+ Type string `yaml:"type"`
178+>>>>>>> MERGE-SOURCE
179 }
180
181 type BootAssetFiles struct {
182@@ -107,11 +107,11 @@
183 RawPartitions []BootAssetRawPartitions `yaml:"raw-partitions,omitempty"`
184 }
185
186-type OemDescription struct {
187+type GadgetDescription struct {
188 Name string `yaml:"name"`
189 Version string `yaml:"version"`
190
191- OEM struct {
192+ Gadget struct {
193 Hardware struct {
194 Bootloader string `yaml:"bootloader"`
195 PartitionLayout string `yaml:"partition-layout"`
196@@ -129,7 +129,7 @@
197 Store *struct {
198 ID string `yaml:"id,omitempty"`
199 }
200- } `yaml:"oem,omitempty"`
201+ } `yaml:"gadget,omitempty"`
202
203 Config struct {
204 UbuntuCore struct {
205@@ -140,53 +140,42 @@
206 rootDir string
207 }
208
209-func (o *OemDescription) SetRoot(rootDir string) {
210+func (o *GadgetDescription) SetRoot(rootDir string) {
211 o.rootDir = rootDir
212 }
213
214 // SystemParts returns the system labels depending on the partition layout.
215 //
216 // The default is to return a flat structure for any unknown layout.
217-func (o *OemDescription) SystemParts() []string {
218- switch o.OEM.Hardware.PartitionLayout {
219- case partLayoutSystemAB:
220- return []string{"a", "b"}
221+func (o *GadgetDescription) SystemParts() []string {
222+ switch o.Gadget.Hardware.PartitionLayout {
223 default:
224 return []string{""}
225 }
226 }
227
228-func (o OemDescription) InstallPath() (string, error) {
229- glob, err := filepath.Glob(fmt.Sprintf("%s/oem/%s/current", o.rootDir, o.Name))
230- if err != nil {
231- return "", err
232- }
233-
234- if len(glob) != 1 {
235- return "", errors.New("oem package not installed")
236- }
237-
238- return glob[0], nil
239-}
240-
241-func (o OemDescription) Architecture() string {
242- return o.OEM.Hardware.Architecture
243-}
244-
245-func (o *OemDescription) SetArchitecture(architecture string) {
246- o.OEM.Hardware.Architecture = architecture
247-}
248-
249-func (o OemDescription) PartitionLayout() string {
250- return o.OEM.Hardware.PartitionLayout
251-}
252-
253-func (o OemDescription) Platform() string {
254- return o.OEM.Hardware.Platform
255-}
256-
257-func (o *OemDescription) SetPlatform(platform string) {
258- o.OEM.Hardware.Platform = platform
259+func (o GadgetDescription) InstallPath() (string, error) {
260+ return o.rootDir, nil
261+}
262+
263+func (o GadgetDescription) Architecture() string {
264+ return o.Gadget.Hardware.Architecture
265+}
266+
267+func (o *GadgetDescription) SetArchitecture(architecture string) {
268+ o.Gadget.Hardware.Architecture = architecture
269+}
270+
271+func (o GadgetDescription) PartitionLayout() string {
272+ return o.Gadget.Hardware.PartitionLayout
273+}
274+
275+func (o GadgetDescription) Platform() string {
276+ return o.Gadget.Hardware.Platform
277+}
278+
279+func (o *GadgetDescription) SetPlatform(platform string) {
280+ o.Gadget.Hardware.Platform = platform
281 }
282
283 func sectorSize(dev string) (string, error) {
284@@ -200,6 +189,7 @@
285
286 // BaseImage implements the basic primitives to manage images.
287 type BaseImage struct {
288+<<<<<<< TREE
289 baseMount string
290 hardware HardwareDescription
291 location string
292@@ -209,8 +199,22 @@
293 size int64
294 rootSize int
295 label string
296+=======
297+ baseMount string
298+ bindMounts []string
299+ hardware HardwareDescription
300+ location string
301+ gadget GadgetDescription
302+ parts []partition
303+ partCount int
304+ size int64
305+ rootSize int
306+ label string
307+>>>>>>> MERGE-SOURCE
308 }
309
310+var bindMounts = []string{"dev", "sys", "proc", filepath.Join("sys", "firmware")}
311+
312 // Mount mounts the image. This also maps the loop device.
313 func (img *BaseImage) Mount() error {
314 if err := img.doMap(); err != nil {
315@@ -267,8 +271,27 @@
316 }
317 img.baseMount = baseMount
318
319+ mountpoints := make([]string, 0, len(bindMounts))
320+ if img.gadget.PartitionLayout() == "minimal" {
321+ mountpoints = bindMounts
322+
323+ for _, d := range mountpoints {
324+ p := filepath.Join(baseMount, d)
325+
326+ if err := os.MkdirAll(p, 0755); err != nil {
327+ return err
328+ }
329+
330+ printOut("Bind mounting", d, "to", p)
331+ if err := bindMount(filepath.Join("/", d), p); err != nil {
332+ return err
333+ }
334+
335+ img.bindMounts = append(img.bindMounts, p)
336+ }
337+ }
338+
339 return nil
340-
341 }
342
343 // Unmount unmounts the image. This also unmaps the loop device.
344@@ -283,6 +306,13 @@
345 panic("No base mountpoint set")
346 }
347
348+ for i := len(img.bindMounts) - 1; i >= 0; i-- {
349+ if err := unmount(img.bindMounts[i]); err != nil {
350+ return err
351+ }
352+ }
353+ img.bindMounts = nil
354+
355 syscallSync()
356
357 for _, part := range img.parts {
358@@ -466,55 +496,28 @@
359 }
360
361 func (img *BaseImage) GenericBootSetup(bootPath string) error {
362- // origins
363- hardwareYamlPath := filepath.Join(img.baseMount, hardwareFileName)
364- kernelPath := filepath.Join(img.baseMount, img.hardware.Kernel)
365- initrdPath := filepath.Join(img.baseMount, img.hardware.Initrd)
366-
367- // populate both A/B
368- for _, part := range img.oem.SystemParts() {
369- path := filepath.Join(bootPath, part)
370-
371- printOut("Setting up", path)
372-
373- if err := os.MkdirAll(path, 0755); err != nil {
374- return err
375- }
376-
377- if err := sysutils.CopyFile(hardwareYamlPath, filepath.Join(path, hardwareFileName)); err != nil {
378- return err
379- }
380-
381- if err := sysutils.CopyFile(kernelPath, filepath.Join(path, kernelFileName)); err != nil {
382- return err
383- }
384-
385- if err := sysutils.CopyFile(initrdPath, filepath.Join(path, initrdFileName)); err != nil {
386- return err
387- }
388- }
389-
390- oemRoot, err := img.oem.InstallPath()
391+ gadgetRoot, err := img.gadget.InstallPath()
392 if err != nil {
393 return err
394 }
395
396- return setupBootAssetFiles(img.Boot(), bootPath, oemRoot, img.oem.OEM.Hardware.BootAssets.Files)
397+ return setupBootAssetFiles(img.Boot(), bootPath, gadgetRoot, img.gadget.Gadget.Hardware.BootAssets.Files)
398 }
399
400 func (img *BaseImage) FlashExtra() error {
401- oemRoot, err := img.oem.InstallPath()
402+ gadgetRoot, err := img.gadget.InstallPath()
403 if err != nil {
404 return err
405 }
406
407- if bootAssets := img.oem.OEM.Hardware.BootAssets; bootAssets != nil {
408+ if bootAssets := img.gadget.Gadget.Hardware.BootAssets; bootAssets != nil {
409 if bootAssets.RawPartitions != nil {
410 if err := setupBootAssetRawPartitions(img.location, img.partCount, bootAssets.RawPartitions); err != nil {
411 return err
412 }
413 }
414- return setupBootAssetRawFiles(img.location, oemRoot, bootAssets.RawFiles)
415+
416+ return setupBootAssetRawFiles(img.location, gadgetRoot, bootAssets.RawFiles)
417 }
418
419 return nil
420@@ -525,3 +528,19 @@
421 fmt.Println(args...)
422 }
423 }
424+
425+func bindMount(src, dst string) error {
426+ if out, err := exec.Command("mount", "--bind", src, dst).CombinedOutput(); err != nil {
427+ return fmt.Errorf("issues while bind mounting: %s", out)
428+ }
429+
430+ return nil
431+}
432+
433+func unmount(dst string) error {
434+ if out, err := exec.Command("umount", dst).CombinedOutput(); err != nil {
435+ return fmt.Errorf("issues while unmounting: %s", out)
436+ }
437+
438+ return nil
439+}
440
441=== modified file 'diskimage/common_test.go'
442--- diskimage/common_test.go 2015-09-09 16:00:01 +0000
443+++ diskimage/common_test.go 2016-09-04 15:15:47 +0000
444@@ -28,7 +28,7 @@
445
446 type CommonTestSuite struct {
447 tmpdir string
448- oem OemDescription
449+ gadget GadgetDescription
450 packageInst string
451 }
452
453@@ -36,27 +36,27 @@
454
455 func (s *CommonTestSuite) SetUpTest(c *C) {
456 s.tmpdir = c.MkDir()
457- s.oem = OemDescription{Name: "packagename", Version: "42"}
458- s.packageInst = s.oem.Name
459+ s.gadget = GadgetDescription{Name: "packagename", Version: "42"}
460+ s.packageInst = s.gadget.Name
461 }
462
463 func (s *CommonTestSuite) TestOemInstallPath(c *C) {
464- err := os.MkdirAll(filepath.Join(s.tmpdir, "oem", s.packageInst, "current"), 0755)
465- c.Assert(err, IsNil)
466-
467- s.oem.SetRoot(s.tmpdir)
468- installPath, err := s.oem.InstallPath()
469-
470- c.Assert(err, IsNil)
471- c.Assert(installPath, Equals, filepath.Join(s.tmpdir, "oem/packagename/current"))
472+ err := os.MkdirAll(filepath.Join(s.tmpdir, "gadget", s.packageInst, "current"), 0755)
473+ c.Assert(err, IsNil)
474+
475+ s.gadget.SetRoot(s.tmpdir)
476+ installPath, err := s.gadget.InstallPath()
477+
478+ c.Assert(err, IsNil)
479+ c.Assert(installPath, Equals, filepath.Join(s.tmpdir, "gadget/packagename/current"))
480 }
481
482 func (s *CommonTestSuite) TestOemInstallPathNoOem(c *C) {
483- err := os.MkdirAll(filepath.Join(s.tmpdir, "oem", s.packageInst), 0755)
484+ err := os.MkdirAll(filepath.Join(s.tmpdir, "gadget", s.packageInst), 0755)
485 c.Assert(err, IsNil)
486
487- s.oem.SetRoot(s.tmpdir)
488- installPath, err := s.oem.InstallPath()
489+ s.gadget.SetRoot(s.tmpdir)
490+ installPath, err := s.gadget.InstallPath()
491
492 c.Assert(err, NotNil)
493 c.Assert(installPath, Equals, "")
494
495=== modified file 'diskimage/core_grub.go'
496--- diskimage/core_grub.go 2016-04-05 09:32:48 +0000
497+++ diskimage/core_grub.go 2016-09-04 15:15:47 +0000
498@@ -5,6 +5,7 @@
499 //
500 // Written by Sergio Schvezov <sergio.schvezov@canonical.com>
501 //
502+
503 package diskimage
504
505 import (
506@@ -31,25 +32,40 @@
507 // You should have received a copy of the GNU General Public License along
508 // with this program. If not, see <http://www.gnu.org/licenses/>.
509
510+// CoreGrubImage holds the logic to create a core image using grub.
511 type CoreGrubImage struct {
512 BaseImage
513
514 legacyGrub bool
515 }
516
517-func NewCoreGrubImage(location string, size int64, rootSize int, hw HardwareDescription, oem OemDescription, updateGrub bool, label string) *CoreGrubImage {
518+// NewCoreGrubImage creates a new instance of CoreGrubImage
519+func NewCoreGrubImage(location string, size int64, rootSize int, hw HardwareDescription, gadget GadgetDescription, updateGrub bool, label string) *CoreGrubImage {
520+ var partCount int
521+ switch gadget.PartitionLayout() {
522+ case "minimal":
523+ partCount = 3
524+ }
525+
526 return &CoreGrubImage{
527 BaseImage: BaseImage{
528 location: location,
529 size: size,
530 rootSize: rootSize,
531 hardware: hw,
532+<<<<<<< TREE
533 oem: oem,
534 partCount: 5,
535 label: label,
536+=======
537+ gadget: gadget,
538+ partCount: partCount,
539+ label: label,
540+>>>>>>> MERGE-SOURCE
541 },
542 legacyGrub: updateGrub,
543 }
544+
545 }
546
547 const grubCfgContent = `# console only, no graphics/vga
548@@ -75,9 +91,10 @@
549 }
550
551 parted.addPart(grubLabel, "", fsNone, 4)
552- parted.addPart(bootLabel, bootDir, fsFat32, 128)
553- parted.addPart(systemALabel, systemADir, fsExt4, img.rootSize)
554- parted.addPart(systemBLabel, systemBDir, fsExt4, img.rootSize)
555+ switch img.gadget.PartitionLayout() {
556+ case "minimal":
557+ parted.addPart(bootLabel, bootDir, fsFat32, 64)
558+ }
559 parted.addPart(writableLabel, writableDir, fsExt4, -1)
560
561 parted.setBoot(2)
562@@ -88,6 +105,7 @@
563 return parted.create(img.location)
564 }
565
566+// SetupBoot sets up the bootloader logic for the image.
567 func (img *CoreGrubImage) SetupBoot() error {
568 if !img.legacyGrub {
569 // destinations
570@@ -121,14 +139,14 @@
571 return errors.New("cannot determined absolute path for output image")
572 }
573
574- rootDevPath := filepath.Join(img.System(), "root_dev")
575+ rootDevPath := filepath.Join(img.System(), "tmp", "root_dev")
576
577- if f, err := os.Create(rootDevPath); err != nil {
578+ f, err := os.Create(rootDevPath)
579+ if err != nil {
580 return err
581- } else {
582- f.Close()
583- defer os.Remove(rootDevPath)
584 }
585+ f.Close()
586+ defer os.Remove(rootDevPath)
587
588 if err := bindMount(outputPath, rootDevPath); err != nil {
589 return err
590@@ -165,7 +183,7 @@
591
592 var grubTarget string
593
594- arch := img.oem.Architecture()
595+ arch := img.gadget.Architecture()
596
597 switch arch {
598 case "armhf":
599@@ -174,13 +192,15 @@
600 grubTarget = "x86_64-efi"
601 case "i386":
602 grubTarget = "i386-efi"
603+ case "arm64":
604+ grubTarget = "arm64-efi"
605 default:
606 return fmt.Errorf("unsupported architecture for GRUB on EFI: %s", arch)
607 }
608
609 if arch == "amd64" || arch == "i386" {
610 // install grub BIOS support
611- if out, err := exec.Command("chroot", img.System(), "grub-install", "/root_dev").CombinedOutput(); err != nil {
612+ if out, err := exec.Command("chroot", img.System(), "grub-install", "tmp/root_dev").CombinedOutput(); err != nil {
613 return fmt.Errorf("unable to install grub (BIOS): %s", out)
614 }
615 }
616@@ -233,19 +253,3 @@
617
618 return nil
619 }
620-
621-func bindMount(src, dst string) error {
622- if out, err := exec.Command("mount", "--bind", src, dst).CombinedOutput(); err != nil {
623- return fmt.Errorf("issues while bind mounting: %s", out)
624- }
625-
626- return nil
627-}
628-
629-func unmount(dst string) error {
630- if out, err := exec.Command("umount", dst).CombinedOutput(); err != nil {
631- return fmt.Errorf("issues while unmounting: %s", out)
632- }
633-
634- return nil
635-}
636
637=== modified file 'diskimage/core_uboot.go'
638--- diskimage/core_uboot.go 2016-04-05 09:32:48 +0000
639+++ diskimage/core_uboot.go 2016-09-04 15:15:47 +0000
640@@ -66,16 +66,27 @@
641 Bootloader []string `yaml:"bootloader"`
642 }
643
644-func NewCoreUBootImage(location string, size int64, rootSize int, hw HardwareDescription, oem OemDescription, label string) *CoreUBootImage {
645+func NewCoreUBootImage(location string, size int64, rootSize int, hw HardwareDescription, gadget GadgetDescription, label string) *CoreUBootImage {
646+ var partCount int
647+ switch gadget.PartitionLayout() {
648+ case "minimal":
649+ partCount = 2
650+ }
651+
652 return &CoreUBootImage{
653 BaseImage{
654 hardware: hw,
655- oem: oem,
656+ gadget: gadget,
657 location: location,
658 size: size,
659 rootSize: rootSize,
660+<<<<<<< TREE
661 partCount: 4,
662 label: label,
663+=======
664+ partCount: partCount,
665+ label: label,
666+>>>>>>> MERGE-SOURCE
667 },
668 }
669 }
670@@ -95,9 +106,10 @@
671 return err
672 }
673
674- parted.addPart(bootLabel, bootDir, fsFat32, 128)
675- parted.addPart(systemALabel, systemADir, fsExt4, 1024)
676- parted.addPart(systemBLabel, systemBDir, fsExt4, 1024)
677+ switch img.gadget.PartitionLayout() {
678+ case "minimal":
679+ parted.addPart(bootLabel, bootDir, fsFat32, 128)
680+ }
681 parted.addPart(writableLabel, writableDir, fsExt4, -1)
682
683 parted.setBoot(1)
684@@ -117,7 +129,7 @@
685 }
686
687 // populate both A/B
688- for _, part := range img.oem.SystemParts() {
689+ for _, part := range img.gadget.SystemParts() {
690 bootDtbPath := filepath.Join(bootPath, part, "dtbs")
691 if err := img.provisionDtbs(bootDtbPath); err != nil {
692 return err
693@@ -131,7 +143,7 @@
694 defer snappySystemFile.Close()
695
696 var fdtfile string
697- if platform := img.oem.Platform(); platform != "" {
698+ if platform := img.gadget.Platform(); platform != "" {
699 fdtfile = fmt.Sprintf("fdtfile=%s.dtb", platform)
700 }
701
702@@ -154,28 +166,32 @@
703 return err
704 }
705
706- dtbFis, err := ioutil.ReadDir(dtbsPath)
707- if err != nil {
708- return err
709+ var dtbFis []os.FileInfo
710+ if img.hardware.Dtbs != "" {
711+ var err error
712+ dtbFis, err = ioutil.ReadDir(dtbsPath)
713+ if err != nil {
714+ return err
715+ }
716 }
717
718 if err := os.MkdirAll(bootDtbPath, 0755); err != nil {
719 return err
720 }
721
722- dtb := filepath.Join(dtbsPath, fmt.Sprintf("%s.dtb", img.oem.Platform()))
723+ dtb := filepath.Join(dtbsPath, fmt.Sprintf("%s.dtb", img.gadget.Platform()))
724
725 // if there is a specific dtb for the platform, copy it.
726- // First look in oem and then in device.
727- if oemDtb := img.oem.OEM.Hardware.Dtb; oemDtb != "" && img.oem.Platform() != "" {
728- oemRoot, err := img.oem.InstallPath()
729+ // First look in gadget and then in device.
730+ if gadgetDtb := img.gadget.Gadget.Hardware.Dtb; gadgetDtb != "" && img.gadget.Platform() != "" {
731+ gadgetRoot, err := img.gadget.InstallPath()
732 if err != nil {
733 return err
734 }
735
736- oemDtb := filepath.Join(oemRoot, oemDtb)
737+ gadgetDtb := filepath.Join(gadgetRoot, gadgetDtb)
738 dst := filepath.Join(bootDtbPath, filepath.Base(dtb))
739- if err := sysutils.CopyFile(oemDtb, dst); err != nil {
740+ if err := sysutils.CopyFile(gadgetDtb, dst); err != nil {
741 return err
742 }
743 } else if _, err := os.Stat(dtb); err == nil {
744
745=== modified file 'diskimage/partition.go'
746--- diskimage/partition.go 2015-01-15 21:05:01 +0000
747+++ diskimage/partition.go 2016-09-04 15:15:47 +0000
748@@ -5,6 +5,7 @@
749 //
750 // Written by Sergio Schvezov <sergio.schvezov@canonical.com>
751 //
752+
753 package diskimage
754
755 import (
756
757=== added file 'snapcraft.yaml'
758--- snapcraft.yaml 1970-01-01 00:00:00 +0000
759+++ snapcraft.yaml 2016-09-04 15:15:47 +0000
760@@ -0,0 +1,35 @@
761+name: ubuntu-device-flash
762+summary: Flash supported devices with Ubuntu
763+description: |
764+ Use this tool to flash a suported device with Ubuntu. This works
765+ for different kinds of Ubuntu like Core or Touch.
766+version: 11
767+confinement: devmode
768+
769+apps:
770+ ubuntu-device-flash:
771+ command: bin/ubuntu-device-flash
772+
773+parts:
774+ goget-ubuntu-touch:
775+ plugin: go
776+ source: lp:~mvo/goget-ubuntu-touch/minimal-first-boot
777+ source-type: bzr
778+ go-importpath: launchpad.net/goget-ubuntu-touch
779+ organize:
780+ sbin/kpartx: bin/kpartx
781+ sbin/dmsetup: bin/dmsetup
782+ snap:
783+ - bin/ubuntu-device-flash
784+ - bin/kpartx
785+ - bin/dmsetup
786+ stage-packages:
787+ - kpartx
788+ - dmsetup
789+ snapd:
790+ plugin: go
791+ source: https://github.com/snapcore/snapd
792+ source-type: git
793+ go-importpath: github.com/snapcore/snapd
794+ snap:
795+ - bin/snap
796
797=== modified file 'ubuntu-device-flash/common.go'
798--- ubuntu-device-flash/common.go 2015-03-30 03:28:28 +0000
799+++ ubuntu-device-flash/common.go 2016-09-04 15:15:47 +0000
800@@ -61,11 +61,11 @@
801
802 // expandFile checks for file existence, correct permissions and returns the absolute path.
803 func expandFile(path string) (abspath string, err error) {
804- if p, err := filepath.Abs(path); err != nil {
805+ p, err := filepath.Abs(path)
806+ if err != nil {
807 return "", err
808- } else {
809- abspath = p
810 }
811+ abspath = p
812
813 fi, err := os.Lstat(abspath)
814 if err != nil {
815
816=== modified file 'ubuntu-device-flash/core.go'
817--- ubuntu-device-flash/core.go 2016-04-04 15:54:40 +0000
818+++ ubuntu-device-flash/core.go 2016-09-04 15:15:47 +0000
819@@ -77,21 +77,20 @@
820
821 if !coreCmd.Deprecated.Cloud {
822 coreCmd.customizationFunc = append(coreCmd.customizationFunc, coreCmd.setupCloudInit)
823- coreCmd.customizationFunc = append(coreCmd.customizationFunc, coreCmd.setupOemConfigs)
824+ coreCmd.customizationFunc = append(coreCmd.customizationFunc, coreCmd.setupGadgetConfigs)
825 }
826
827 return coreCmd.create()
828 }
829
830 // this is a hackish way to get the config in place
831-func (coreCmd *CoreCmd) setupOemConfigs() error {
832- modprobeDContent := coreCmd.oem.Config.UbuntuCore.Modprobe
833+func (coreCmd *CoreCmd) setupGadgetConfigs() error {
834+ modprobeDContent := coreCmd.gadget.Config.UbuntuCore.Modprobe
835 if modprobeDContent == nil {
836- printOut("no modprobe")
837 return nil
838 }
839
840- fmt.Println("Setting up oem hooks...")
841+ fmt.Println("Setting up gadget hooks...")
842
843 writablePath := coreCmd.img.Writable()
844
845@@ -101,12 +100,20 @@
846 }
847
848 // first we need to copy all the files in modprobe.d
849+<<<<<<< TREE
850 /*
851 systemModprobeDir := filepath.Join(coreCmd.img.System(), "etc", "modprobe.d")
852 if err := helpers.RSyncWithDelete(systemModprobeDir, modprobeDir); err != nil {
853 return err
854 }
855 */
856+=======
857+ systemModprobeDir := filepath.Join(coreCmd.img.System(), "etc", "modprobe.d")
858+ // FIXME: can we do "cp -a" here?
859+ if err := RSyncWithDelete(systemModprobeDir, modprobeDir); err != nil {
860+ return err
861+ }
862+>>>>>>> MERGE-SOURCE
863
864 modprobeD := filepath.Join(modprobeDir, "ubuntu-core.conf")
865 modprobeDFile, err := os.Create(modprobeD)
866
867=== added file 'ubuntu-device-flash/helpers.go'
868--- ubuntu-device-flash/helpers.go 1970-01-01 00:00:00 +0000
869+++ ubuntu-device-flash/helpers.go 2016-09-04 15:15:47 +0000
870@@ -0,0 +1,89 @@
871+// -*- Mode: Go; indent-tabs-mode: t -*-
872+
873+/*
874+ * Copyright (C) 2014-2015 Canonical Ltd
875+ *
876+ * This program is free software: you can redistribute it and/or modify
877+ * it under the terms of the GNU General Public License version 3 as
878+ * published by the Free Software Foundation.
879+ *
880+ * This program is distributed in the hope that it will be useful,
881+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
882+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
883+ * GNU General Public License for more details.
884+ *
885+ * You should have received a copy of the GNU General Public License
886+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
887+ *
888+ */
889+
890+package main
891+
892+import (
893+ "fmt"
894+ "os"
895+ "os/exec"
896+ "path/filepath"
897+ "syscall"
898+
899+ "github.com/snapcore/snapd/osutil"
900+)
901+
902+// RSyncWithDelete syncs srcDir to destDir
903+func RSyncWithDelete(srcDirName, destDirName string) error {
904+ // first remove everything thats not in srcdir
905+ err := filepath.Walk(destDirName, func(path string, info os.FileInfo, err error) error {
906+ if err != nil {
907+ return err
908+ }
909+
910+ // relative to the root "destDirName"
911+ relPath := path[len(destDirName):]
912+ if !osutil.FileExists(filepath.Join(srcDirName, relPath)) {
913+ if err := os.RemoveAll(path); err != nil {
914+ return err
915+ }
916+ if info.IsDir() {
917+ return filepath.SkipDir
918+ }
919+ }
920+ return nil
921+ })
922+ if err != nil {
923+ return err
924+ }
925+
926+ // then copy or update the data from srcdir to destdir
927+ err = filepath.Walk(srcDirName, func(src string, info os.FileInfo, err error) error {
928+ if err != nil {
929+ return err
930+ }
931+
932+ // relative to the root "srcDirName"
933+ relPath := src[len(srcDirName):]
934+ dst := filepath.Join(destDirName, relPath)
935+ if info.IsDir() {
936+ if err := os.MkdirAll(dst, info.Mode()); err != nil {
937+ return err
938+ }
939+
940+ // this can panic. The alternative would be to use the "st, ok" pattern, and then if !ok... panic?
941+ st := info.Sys().(*syscall.Stat_t)
942+ ts := []syscall.Timespec{st.Atim, st.Mtim}
943+
944+ return syscall.UtimesNano(dst, ts)
945+ }
946+ if !osutil.FilesAreEqual(src, dst) {
947+ // XXX: we should (eventually) use CopyFile here,
948+ // but we need to teach it about preserving
949+ // of atime/mtime and permissions
950+ output, err := exec.Command("cp", "-va", src, dst).CombinedOutput()
951+ if err != nil {
952+ return fmt.Errorf("Failed to copy %s to %s (%s)", src, dst, output)
953+ }
954+ }
955+ return nil
956+ })
957+
958+ return err
959+}
960
961=== added file 'ubuntu-device-flash/helpers_test.go'
962--- ubuntu-device-flash/helpers_test.go 1970-01-01 00:00:00 +0000
963+++ ubuntu-device-flash/helpers_test.go 2016-09-04 15:15:47 +0000
964@@ -0,0 +1,135 @@
965+// -*- Mode: Go; indent-tabs-mode: t -*-
966+
967+/*
968+ * Copyright (C) 2014-2015 Canonical Ltd
969+ *
970+ * This program is free software: you can redistribute it and/or modify
971+ * it under the terms of the GNU General Public License version 3 as
972+ * published by the Free Software Foundation.
973+ *
974+ * This program is distributed in the hope that it will be useful,
975+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
976+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
977+ * GNU General Public License for more details.
978+ *
979+ * You should have received a copy of the GNU General Public License
980+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
981+ *
982+ */
983+
984+package main
985+
986+import (
987+ "fmt"
988+ "io/ioutil"
989+ "os"
990+ "os/exec"
991+ "path/filepath"
992+
993+ . "launchpad.net/gocheck"
994+)
995+
996+type HTestSuite struct{}
997+
998+var _ = Suite(&HTestSuite{})
999+
1000+func makeTestFiles(c *C, srcDir, destDir string) {
1001+ // a new file
1002+ err := ioutil.WriteFile(filepath.Join(srcDir, "new"), []byte(nil), 0644)
1003+ c.Assert(err, IsNil)
1004+
1005+ // a existing file that needs update
1006+ err = ioutil.WriteFile(filepath.Join(destDir, "existing-update"), []byte("old-content"), 0644)
1007+ c.Assert(err, IsNil)
1008+ err = ioutil.WriteFile(filepath.Join(srcDir, "existing-update"), []byte("some-new-content"), 0644)
1009+ c.Assert(err, IsNil)
1010+
1011+ // existing file that needs no update
1012+ err = ioutil.WriteFile(filepath.Join(srcDir, "existing-unchanged"), []byte(nil), 0644)
1013+ c.Assert(err, IsNil)
1014+ err = exec.Command("cp", "-a", filepath.Join(srcDir, "existing-unchanged"), filepath.Join(destDir, "existing-unchanged")).Run()
1015+ c.Assert(err, IsNil)
1016+
1017+ // a file that needs removal
1018+ err = ioutil.WriteFile(filepath.Join(destDir, "to-be-deleted"), []byte(nil), 0644)
1019+ c.Assert(err, IsNil)
1020+}
1021+
1022+func compareDirs(c *C, srcDir, destDir string) {
1023+ d1, err := exec.Command("ls", "-al", srcDir).CombinedOutput()
1024+ c.Assert(err, IsNil)
1025+ d2, err := exec.Command("ls", "-al", destDir).CombinedOutput()
1026+ c.Assert(err, IsNil)
1027+ c.Assert(string(d1), Equals, string(d2))
1028+ // ensure content got updated
1029+ c1, err := exec.Command("sh", "-c", fmt.Sprintf("find %s -type f |xargs cat", srcDir)).CombinedOutput()
1030+ c.Assert(err, IsNil)
1031+ c2, err := exec.Command("sh", "-c", fmt.Sprintf("find %s -type f |xargs cat", destDir)).CombinedOutput()
1032+ c.Assert(err, IsNil)
1033+ c.Assert(string(c1), Equals, string(c2))
1034+}
1035+
1036+func (ts *HTestSuite) TestSyncDirs(c *C) {
1037+
1038+ for _, l := range [][2]string{
1039+ [2]string{"src-short", "dst-loooooooooooong"},
1040+ [2]string{"src-loooooooooooong", "dst-short"},
1041+ [2]string{"src-eq", "dst-eq"},
1042+ } {
1043+
1044+ // ensure we have src, dest dirs with different length
1045+ srcDir := filepath.Join(c.MkDir(), l[0])
1046+ err := os.MkdirAll(srcDir, 0755)
1047+ c.Assert(err, IsNil)
1048+ destDir := filepath.Join(c.MkDir(), l[1])
1049+ err = os.MkdirAll(destDir, 0755)
1050+ c.Assert(err, IsNil)
1051+
1052+ // add a src subdir
1053+ subdir := filepath.Join(srcDir, "subdir")
1054+ err = os.Mkdir(subdir, 0755)
1055+ c.Assert(err, IsNil)
1056+ makeTestFiles(c, subdir, destDir)
1057+
1058+ // add a dst subdir that needs to get deleted
1059+ subdir2 := filepath.Join(destDir, "to-be-deleted-subdir")
1060+ err = os.Mkdir(subdir2, 0755)
1061+ subdir3 := filepath.Join(subdir2, "to-be-deleted-sub-subdir")
1062+ err = os.Mkdir(subdir3, 0755)
1063+
1064+ // and a toplevel
1065+ makeTestFiles(c, srcDir, destDir)
1066+
1067+ // do it
1068+ err = RSyncWithDelete(srcDir, destDir)
1069+ c.Assert(err, IsNil)
1070+
1071+ // ensure meta-data is identical
1072+ compareDirs(c, srcDir, destDir)
1073+ compareDirs(c, filepath.Join(srcDir, "subdir"), filepath.Join(destDir, "subdir"))
1074+ }
1075+}
1076+
1077+func (ts *HTestSuite) TestSyncDirFails(c *C) {
1078+ srcDir := c.MkDir()
1079+ err := os.MkdirAll(srcDir, 0755)
1080+ c.Assert(err, IsNil)
1081+
1082+ destDir := c.MkDir()
1083+ err = os.MkdirAll(destDir, 0755)
1084+ c.Assert(err, IsNil)
1085+
1086+ err = ioutil.WriteFile(filepath.Join(destDir, "meep"), []byte(nil), 0644)
1087+ c.Assert(err, IsNil)
1088+
1089+ // ensure remove fails
1090+ err = os.Chmod(destDir, 0100)
1091+ c.Assert(err, IsNil)
1092+ // make tempdir cleanup work again
1093+ defer os.Chmod(destDir, 0755)
1094+
1095+ // do it
1096+ err = RSyncWithDelete(srcDir, destDir)
1097+ c.Check(err, NotNil)
1098+ c.Check(err, ErrorMatches, ".*permission denied.*")
1099+}
1100
1101=== modified file 'ubuntu-device-flash/main.go'
1102--- ubuntu-device-flash/main.go 2015-11-13 15:19:25 +0000
1103+++ ubuntu-device-flash/main.go 2016-09-04 15:15:47 +0000
1104@@ -43,7 +43,7 @@
1105 var cacheDir = ubuntuimage.GetCacheDir()
1106
1107 func main() {
1108- execute(os.Args)
1109+ execute(os.Args[1:])
1110 }
1111
1112 func execute(args []string) {
1113
1114=== modified file 'ubuntu-device-flash/snappy.go'
1115--- ubuntu-device-flash/snappy.go 2016-04-05 09:39:49 +0000
1116+++ ubuntu-device-flash/snappy.go 2016-09-04 15:15:47 +0000
1117@@ -9,10 +9,32 @@
1118 package main
1119
1120 import (
1121+<<<<<<< TREE
1122+=======
1123+ "encoding/json"
1124+ "errors"
1125+>>>>>>> MERGE-SOURCE
1126 "fmt"
1127
1128+<<<<<<< TREE
1129+=======
1130+ "github.com/snapcore/snapd/arch"
1131+ "github.com/snapcore/snapd/asserts"
1132+ "github.com/snapcore/snapd/osutil"
1133+ "github.com/snapcore/snapd/progress"
1134+ "github.com/snapcore/snapd/provisioning"
1135+ "github.com/snapcore/snapd/release"
1136+ "github.com/snapcore/snapd/snap"
1137+ "github.com/snapcore/snapd/store"
1138+
1139+ "gopkg.in/yaml.v2"
1140+>>>>>>> MERGE-SOURCE
1141 "launchpad.net/goget-ubuntu-touch/diskimage"
1142+<<<<<<< TREE
1143 "launchpad.net/goget-ubuntu-touch/ubuntuimage"
1144+=======
1145+ "launchpad.net/goget-ubuntu-touch/sysutils"
1146+>>>>>>> MERGE-SOURCE
1147 )
1148
1149 type imageFlavor string
1150@@ -58,25 +80,34 @@
1151 }
1152 }
1153
1154+// Snapper holds common options applicable to snappy based images.
1155 type Snapper struct {
1156 Channel string `long:"channel" description:"Specify the channel to use" default:"stable"`
1157 Output string `long:"output" short:"o" description:"Name of the image file to create" required:"true"`
1158- Oem string `long:"oem" description:"The snappy oem package to base the image out of" default:"generic-amd64"`
1159- StoreID string `long:"store" description:"Set an alternate store id."`
1160
1161 Development struct {
1162 Install []string `long:"install" description:"Install additional packages (can be called multiple times)"`
1163- DevicePart string `long:"device-part" description:"Specify a local device part to override the one from the server"`
1164 DeveloperMode bool `long:"developer-mode" description:"Finds the latest public key in your ~/.ssh and sets it up using cloud-init"`
1165 } `group:"Development"`
1166
1167 Positional struct {
1168- Release string `positional-arg-name:"release" description:"The release to base the image out of (15.04 or rolling)" required:"true"`
1169+ Release string `positional-arg-name:"release" description:"The release to base the image out of (16 or rolling)" required:"true"`
1170+ ModelAssertion string `positional-arg-name:"model-assertion" description:"The model assertion to use" required:"true"`
1171 } `positional-args:"yes" required:"yes"`
1172
1173+<<<<<<< TREE
1174 img diskimage.CoreImage
1175 hardware diskimage.HardwareDescription
1176 oem diskimage.OemDescription
1177+=======
1178+ img diskimage.CoreImage
1179+ hardware diskimage.HardwareDescription
1180+ gadget diskimage.GadgetDescription
1181+ stagingRootPath string
1182+>>>>>>> MERGE-SOURCE
1183+
1184+ prepareImagePath string
1185+ modelAssertion *asserts.Model
1186
1187 size int64
1188
1189@@ -85,23 +116,500 @@
1190 customizationFunc []func() error
1191 }
1192
1193+<<<<<<< TREE
1194 func (s *Snapper) systemImage() (*ubuntuimage.Image, error) {
1195 return nil, nil
1196+=======
1197+func (s Snapper) sanityCheck() error {
1198+ // we don't want to overwrite the output, people might get angry :-)
1199+ if osutil.FileExists(s.Output) {
1200+ return fmt.Errorf("Giving up, the desired target output file %#v already exists", s.Output)
1201+ }
1202+
1203+ if s.size < s.flavor.minSize() {
1204+ return fmt.Errorf("minimum size for %s is %d", s.flavor, s.flavor.minSize())
1205+ }
1206+
1207+ if syscall.Getuid() != 0 {
1208+ return errors.New("command requires sudo/pkexec (root)")
1209+ }
1210+ if s.Positional.Release == "15.04" {
1211+ return errors.New("building 15.04 core images is no longer supported. Please use the ppa:snappy-dev/tools 15.04 version of this tool")
1212+ }
1213+
1214+ // ensure we error when running on e.g. 14.04 with a sensible
1215+ // error message instead of super strange error later
1216+ if !osutil.FileExists("/bin/systemctl") {
1217+ return errors.New("need '/bin/systemctl to work")
1218+ }
1219+
1220+ return nil
1221+}
1222+
1223+func snapTargetPathFromSnapFile(src string) (string, error) {
1224+ snapFile, err := snap.Open(src)
1225+ if err != nil {
1226+ return "", err
1227+ }
1228+ var si snap.SideInfo
1229+ // XXX: copied from snapmgr.go
1230+ metafn := src + ".sideinfo"
1231+ if j, err := ioutil.ReadFile(metafn); err == nil {
1232+ if err := json.Unmarshal(j, &si); err != nil {
1233+ return "", fmt.Errorf("cannot read metadata: %s %s\n", metafn, err)
1234+ }
1235+ }
1236+ info, err := snap.ReadInfoFromSnapFile(snapFile, &si)
1237+ if err != nil {
1238+ return "", err
1239+ }
1240+
1241+ // local snap
1242+ if info.Revision.Unset() {
1243+ info.Revision = snap.R(-1)
1244+ }
1245+
1246+ return info.MountFile(), nil
1247+}
1248+
1249+func copyFile(src, dst string) error {
1250+ cmd := exec.Command("cp", "-va", src, dst)
1251+ if o, err := cmd.CombinedOutput(); err != nil {
1252+ return fmt.Errorf("copy failed: %s %s", err, o)
1253+ }
1254+ return nil
1255+}
1256+
1257+func (s *Snapper) prepareImage() error {
1258+ var err error
1259+ s.prepareImagePath, err = ioutil.TempDir("", "prepare-image")
1260+ if err != nil {
1261+ return err
1262+ }
1263+
1264+ raw, err := ioutil.ReadFile(s.Positional.ModelAssertion)
1265+ if err != nil {
1266+ return err
1267+ }
1268+ as, err := asserts.Decode(raw)
1269+ if err != nil {
1270+ return err
1271+ }
1272+ s.modelAssertion = as.(*asserts.Model)
1273+ arch.SetArchitecture(arch.ArchitectureType(s.modelAssertion.Architecture()))
1274+
1275+ os.Setenv("SNAP_REEXEC", "0")
1276+ cmdArgs := []string{
1277+ "snap",
1278+ "prepare-image",
1279+ "--channel", s.Channel,
1280+ s.Positional.ModelAssertion,
1281+ s.prepareImagePath,
1282+ }
1283+ for _, extra := range s.Development.Install {
1284+ cmdArgs = append(cmdArgs, "--extra-snaps="+extra)
1285+ }
1286+
1287+ cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
1288+ cmd.Stdin = os.Stdin
1289+ cmd.Stdout = os.Stdout
1290+ cmd.Stderr = os.Stderr
1291+ if err := cmd.Run(); err != nil {
1292+ return fmt.Errorf("cannot run snap bootstrap: %s", err)
1293+ }
1294+ return nil
1295+>>>>>>> MERGE-SOURCE
1296 }
1297
1298 func (s *Snapper) install(systemPath string) error {
1299- return nil
1300+<<<<<<< TREE
1301+ return nil
1302+=======
1303+ tmpSystemPath := filepath.Join(s.prepareImagePath, "image")
1304+
1305+ mv := exec.Command("sh", "-c", fmt.Sprintf("cp -rv %s/* %s", tmpSystemPath, systemPath))
1306+ mv.Stdin = os.Stdin
1307+ mv.Stdout = os.Stdout
1308+ mv.Stderr = os.Stderr
1309+ if err := mv.Run(); err != nil {
1310+ return fmt.Errorf("cannot run snap bootstrap: %s", err)
1311+ }
1312+
1313+ return nil
1314+}
1315+
1316+func (s *Snapper) extractGadget(gadgetPackage string) error {
1317+ fakeGadgetDir := filepath.Join(s.prepareImagePath, "gadget")
1318+
1319+ // HORRIBLE - there is always one more circle of hell, never assume
1320+ // you have reached the end of it yet
1321+ //
1322+ // the new gadget snaps do no have the old gadget specific stuff
1323+ // anymore - however we still need it to create images until we
1324+ // have the new stuff available
1325+ var gadgetMetaYaml string
1326+ switch gadgetPackage {
1327+ case "pc":
1328+ gadgetMetaYaml = compatCanonicalPCamd64
1329+ case "386":
1330+ gadgetMetaYaml = compatCanonicalPCi386
1331+ case "pi2":
1332+ gadgetMetaYaml = compatCanonicalPi2
1333+ case "dragon":
1334+ gadgetMetaYaml = compatCanonicalDragon
1335+
1336+ }
1337+ if gadgetMetaYaml != "" {
1338+ if err := ioutil.WriteFile(filepath.Join(fakeGadgetDir, "meta/snap.yaml"), []byte(gadgetMetaYaml), 0644); err != nil {
1339+ return err
1340+ }
1341+ }
1342+
1343+ f, err := ioutil.ReadFile(filepath.Join(fakeGadgetDir, "meta/snap.yaml"))
1344+ if err != nil {
1345+ return errors.New("failed to read gadget yaml")
1346+ }
1347+ var gadget diskimage.GadgetDescription
1348+ if err := yaml.Unmarshal([]byte(f), &gadget); err != nil {
1349+ return errors.New("cannot decode gadget yaml")
1350+ }
1351+
1352+ s.gadget = gadget
1353+ s.gadget.SetRoot(fakeGadgetDir)
1354+ return nil
1355+}
1356+
1357+// Creates a YAML file inside the image that contains metadata relating
1358+// to the installation.
1359+func (s Snapper) writeInstallYaml(bootMountpoint string) error {
1360+ selfPath, err := exec.LookPath(os.Args[0])
1361+ if err != nil {
1362+ return err
1363+ }
1364+
1365+ bootDir := ""
1366+
1367+ switch s.gadget.Gadget.Hardware.Bootloader {
1368+ // Running systems use a bindmount for /boot/grub, but
1369+ // since the system isn't booted, create the file in the
1370+ // real location.
1371+ case "grub":
1372+ bootDir = "/EFI/ubuntu/grub"
1373+ }
1374+
1375+ installYamlFilePath := filepath.Join(bootMountpoint, bootDir, provisioning.InstallYamlFile)
1376+
1377+ i := provisioning.InstallYaml{
1378+ InstallMeta: provisioning.InstallMeta{
1379+ Timestamp: time.Now(),
1380+ },
1381+ InstallTool: provisioning.InstallTool{
1382+ Name: filepath.Base(selfPath),
1383+ Path: selfPath,
1384+ // FIXME: we don't know our own version yet :)
1385+ // Version: "???",
1386+ },
1387+ InstallOptions: provisioning.InstallOptions{
1388+ Size: s.size,
1389+ SizeUnit: "GB",
1390+ Output: s.Output,
1391+ Channel: s.Channel,
1392+ DeveloperMode: s.Development.DeveloperMode,
1393+ },
1394+ }
1395+
1396+ data, err := yaml.Marshal(&i)
1397+ if err != nil {
1398+ return err
1399+ }
1400+
1401+ // the file isn't supposed to be modified, hence r/o.
1402+ return ioutil.WriteFile(installYamlFilePath, data, 0444)
1403+}
1404+
1405+func (s *Snapper) bindMount(d string) (string, error) {
1406+ src := filepath.Join(s.img.Writable(), "system-data", d)
1407+ dst := filepath.Join(s.img.System(), d)
1408+
1409+ if err := os.MkdirAll(src, 0755); err != nil {
1410+ return "", err
1411+ }
1412+ cmd := exec.Command("mount", "--bind", src, dst)
1413+ if o, err := cmd.CombinedOutput(); err != nil {
1414+ return "", fmt.Errorf("bind mount failed for %s to %s with: %s %v ", src, dst, err, string(o))
1415+ }
1416+ return dst, nil
1417+}
1418+
1419+func (s *Snapper) downloadSnap(snapName string) (string, error) {
1420+ if snapName == "" {
1421+ return "", nil
1422+ }
1423+ // if its pointing to a local file, just return that
1424+ if _, err := os.Stat(snapName); err == nil {
1425+ return snapName, nil
1426+ }
1427+ release.Series = s.Positional.Release
1428+
1429+ cfg := store.DefaultConfig()
1430+ cfg.StoreID = s.modelAssertion.Store()
1431+ m := store.New(cfg, nil)
1432+ snap, err := m.Snap(snapName, s.Channel, false, snap.R(0), nil)
1433+ if err != nil {
1434+ return "", fmt.Errorf("failed to find os snap: %s", err)
1435+ }
1436+ pb := progress.NewTextProgress()
1437+ tmpName, err := m.Download(snapName, &snap.DownloadInfo, pb, nil)
1438+ if err != nil {
1439+ return "", err
1440+ }
1441+ // rename to the snapid
1442+ baseName := fmt.Sprintf("%s_%s.snap", snap.SnapID, snap.Revision)
1443+ path := filepath.Join(filepath.Dir(tmpName), baseName)
1444+ if err := os.Rename(tmpName, path); err != nil {
1445+ return "", err
1446+ }
1447+
1448+ // write out metadata for first boot
1449+ out, err := json.Marshal(snap)
1450+ if err != nil {
1451+ return "", err
1452+ }
1453+ if err := ioutil.WriteFile(path+".sideinfo", []byte(out), 0644); err != nil {
1454+ return "", err
1455+ }
1456+
1457+ return path, nil
1458+}
1459+
1460+func (s *Snapper) setup() error {
1461+ if s.gadget.PartitionLayout() != "minimal" {
1462+ return fmt.Errorf("only supporting 'minimal' partition layout")
1463+ }
1464+
1465+ printOut("Mounting...")
1466+ if err := s.img.Mount(); err != nil {
1467+ return err
1468+ }
1469+ defer func() {
1470+ printOut("Unmounting...")
1471+ if err := s.img.Unmount(); err != nil {
1472+ fmt.Println("WARNING: unexpected issue:", err)
1473+ }
1474+ }()
1475+
1476+ printOut("Provisioning...")
1477+ systemPath := s.img.System()
1478+
1479+ // setup a fake system
1480+ if err := os.MkdirAll(systemPath, 0755); err != nil {
1481+ return err
1482+ }
1483+
1484+ // this is a bit terrible, we need to download the OS
1485+ // mount it, "sync dirs" (see below) and then we
1486+ // will need to download it again to install it properly
1487+ osSnap, err := s.downloadSnap("ubuntu-core")
1488+ if err != nil {
1489+ return err
1490+ }
1491+
1492+ // mount os snap
1493+ cmd := exec.Command("mount", osSnap, systemPath)
1494+ if o, err := cmd.CombinedOutput(); err != nil {
1495+ return fmt.Errorf("os snap mount failed with: %s %v ", err, string(o))
1496+ }
1497+ defer exec.Command("umount", systemPath).Run()
1498+
1499+ // bind mount all relevant dirs:
1500+ // - /snap so that we can temporarly mount the kernel
1501+ // - /var/lib/snapd to put the downloaded snaps in there
1502+ // - /tmp so that we can create /tmp/root_dev for grub
1503+ for _, d := range []string{"snap", "var/lib/snapd", "tmp"} {
1504+ dst, err := s.bindMount(d)
1505+ if err != nil {
1506+ return err
1507+ }
1508+ defer exec.Command("umount", dst).Run()
1509+ }
1510+
1511+ // bind mount /boot/efi
1512+ dst := filepath.Join(systemPath, "/boot/efi")
1513+ cmd = exec.Command("mount", "--bind", s.img.Boot(), dst)
1514+ if o, err := cmd.CombinedOutput(); err != nil {
1515+ return fmt.Errorf("boot bind mount failed with: %s %v ", err, string(o))
1516+ }
1517+ defer exec.Command("umount", dst).Run()
1518+ switch s.gadget.Gadget.Hardware.Bootloader {
1519+ case "grub":
1520+ // grub needs this
1521+ grubUbuntu := filepath.Join(s.img.Boot(), "EFI/ubuntu/grub")
1522+ os.MkdirAll(grubUbuntu, 0755)
1523+
1524+ // and /boot/grub
1525+ src := grubUbuntu
1526+ dst = filepath.Join(systemPath, "/boot/grub")
1527+ cmd = exec.Command("mount", "--bind", src, dst)
1528+ if o, err := cmd.CombinedOutput(); err != nil {
1529+ return fmt.Errorf("boot/ubuntu bind mount failed with: %s %v ", err, string(o))
1530+ }
1531+ defer exec.Command("umount", dst).Run()
1532+ case "u-boot":
1533+ src := s.img.Boot()
1534+ dst = filepath.Join(systemPath, "/boot/uboot")
1535+ cmd = exec.Command("mount", "--bind", src, dst)
1536+ if o, err := cmd.CombinedOutput(); err != nil {
1537+ return fmt.Errorf("boot/ubuntu bind mount failed with: %s %v ", err, string(o))
1538+ }
1539+ defer exec.Command("umount", dst).Run()
1540+ }
1541+
1542+ if err := s.img.SetupBoot(); err != nil {
1543+ return err
1544+ }
1545+
1546+ if err := s.install(systemPath); err != nil {
1547+ return err
1548+ }
1549+
1550+ for i := range s.customizationFunc {
1551+ if err := s.customizationFunc[i](); err != nil {
1552+ return err
1553+ }
1554+ }
1555+
1556+ return s.writeInstallYaml(s.img.Boot())
1557+>>>>>>> MERGE-SOURCE
1558 }
1559
1560 // deploy orchestrates the priviledged part of the setup
1561+<<<<<<< TREE
1562 func (s *Snapper) deploy(systemImage *ubuntuimage.Image, filePathChan <-chan string) error {
1563+=======
1564+func (s *Snapper) deploy() error {
1565+ // hack to circumvent https://code.google.com/p/go/issues/detail?id=1435
1566+ runtime.GOMAXPROCS(1)
1567+ runtime.LockOSThread()
1568+ if err := sysutils.EscalatePrivs(); err != nil {
1569+ return err
1570+ }
1571+ defer sysutils.DropPrivs()
1572+
1573+ printOut("Formatting...")
1574+ if err := s.img.Format(); err != nil {
1575+ return err
1576+ }
1577+
1578+ if err := s.setup(); err != nil {
1579+ return err
1580+ }
1581+
1582+>>>>>>> MERGE-SOURCE
1583 return nil
1584 }
1585
1586+<<<<<<< TREE
1587 func (s *Snapper) create() error {
1588 return fmt.Errorf(`Building core images is currently not supported.
1589
1590 Images for ubuntu-core 15.04 can be build with the ppa:snappy-dev/tools.
1591 Building images for ubuntu-core 16.04 will be supported by this tool soon.
1592 `)
1593+=======
1594+func (s Snapper) printSummary() {
1595+ fmt.Println("New image complete")
1596+ fmt.Println("Summary:")
1597+ fmt.Println(" Output:", s.Output)
1598+ fmt.Println(" Architecture:", s.gadget.Architecture())
1599+ fmt.Println(" Channel:", s.Channel)
1600+ fmt.Println(" Version:", globalArgs.Revision)
1601+}
1602+
1603+func (s *Snapper) create() (err error) {
1604+ if err := s.sanityCheck(); err != nil {
1605+ return err
1606+ }
1607+
1608+ fmt.Println("Calling prepare image")
1609+ if err := s.prepareImage(); err != nil {
1610+ return err
1611+ }
1612+
1613+ fmt.Println("Determining gadget configuration")
1614+ if err := s.extractGadget(s.modelAssertion.Gadget()); err != nil {
1615+ return err
1616+ }
1617+ defer os.RemoveAll(s.stagingRootPath)
1618+
1619+ // hack to circumvent https://code.google.com/p/go/issues/detail?id=1435
1620+ runtime.GOMAXPROCS(1)
1621+ runtime.LockOSThread()
1622+ if err := sysutils.DropPrivs(); err != nil {
1623+ return err
1624+ }
1625+
1626+ switch s.gadget.Gadget.Hardware.Bootloader {
1627+ case "grub":
1628+ legacy := false
1629+ s.img = diskimage.NewCoreGrubImage(s.Output, s.size, s.flavor.rootSize(), s.hardware, s.gadget, legacy, "gpt")
1630+ case "u-boot":
1631+ label := "msdos"
1632+ if s.gadget.Architecture() == archArm64 {
1633+ label = "gpt"
1634+ }
1635+ s.img = diskimage.NewCoreUBootImage(s.Output, s.size, s.flavor.rootSize(), s.hardware, s.gadget, label)
1636+ default:
1637+ return errors.New("no hardware description in Gadget snap")
1638+ }
1639+
1640+ printOut("Partitioning...")
1641+ if err := s.img.Partition(); err != nil {
1642+ return err
1643+ }
1644+ defer func() {
1645+ if err != nil {
1646+ os.Remove(s.Output)
1647+ }
1648+ }()
1649+
1650+ // Handle SIGINT and SIGTERM.
1651+ go func() {
1652+ ch := make(chan os.Signal)
1653+ signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
1654+
1655+ for sig := range ch {
1656+ printOut("Received", sig, "... ignoring")
1657+ }
1658+ }()
1659+
1660+ // Execute the following code with escalated privs and drop them when done
1661+ if err := s.deploy(); err != nil {
1662+ return err
1663+ }
1664+
1665+ if err := s.img.FlashExtra(); err != nil {
1666+ return err
1667+ }
1668+
1669+ s.printSummary()
1670+
1671+ return nil
1672+}
1673+
1674+func isLegacy(release, channel string, revision int) bool {
1675+ if release != "15.04" {
1676+ return false
1677+ }
1678+
1679+ switch channel {
1680+ case "edge":
1681+ return revision <= 149
1682+ case "alpha":
1683+ return revision <= 9
1684+ case "stable":
1685+ return revision <= 4
1686+ }
1687+
1688+ return false
1689+>>>>>>> MERGE-SOURCE
1690 }
1691
1692=== added file 'ubuntu-device-flash/snappy_compat_yaml.go'
1693--- ubuntu-device-flash/snappy_compat_yaml.go 1970-01-01 00:00:00 +0000
1694+++ ubuntu-device-flash/snappy_compat_yaml.go 2016-09-04 15:15:47 +0000
1695@@ -0,0 +1,124 @@
1696+//
1697+// ubuntu-device-flash - Tool to download and flash devices with an Ubuntu Image
1698+// based system
1699+//
1700+// Copyright (c) 2016 Canonical Ltd.
1701+//
1702+package main
1703+
1704+var compatCanonicalPCamd64 = `
1705+name: canonical-pc
1706+gadget:
1707+ branding:
1708+ name: amd64
1709+ subname: generic
1710+
1711+ hardware:
1712+ bootloader: grub
1713+ architecture: amd64
1714+ partition-layout: minimal
1715+ boot-assets:
1716+ files:
1717+ - path: grub.cfg
1718+`
1719+
1720+var compatCanonicalPCi386 = `
1721+name: canonical-i386
1722+gadget:
1723+ branding:
1724+ name: i386
1725+ subname: generic
1726+
1727+ hardware:
1728+ bootloader: grub
1729+ architecture: amd64
1730+ partition-layout: minimal
1731+ boot-assets:
1732+ files:
1733+ - path: grub.cfg
1734+`
1735+
1736+var compatCanonicalPi2 = `
1737+name: canonical-pi2
1738+gadget:
1739+ hardware:
1740+ platform: bcm2836-rpi-2-b
1741+ architecture: armhf
1742+ partition-layout: minimal
1743+ bootloader: u-boot
1744+ boot-assets:
1745+ files:
1746+ - path: boot-assets/config.txt
1747+ - path: boot-assets/cmdline.txt
1748+ - path: boot-assets/uboot.bin
1749+ - path: boot-assets/uboot.env
1750+ - path: boot-assets/bcm2708-rpi-b.dtb
1751+ - path: boot-assets/bcm2708-rpi-b-plus.dtb
1752+ - path: boot-assets/bcm2709-rpi-2-b.dtb
1753+ - path: boot-assets/bootcode.bin
1754+ - path: boot-assets/COPYING.linux
1755+ - path: boot-assets/fixup_cd.dat
1756+ - path: boot-assets/fixup.dat
1757+ - path: boot-assets/fixup_x.dat
1758+ - path: boot-assets/LICENCE.broadcom
1759+ - path: boot-assets/LICENSE.oracle
1760+ - path: boot-assets/start_cd.elf
1761+ - path: boot-assets/start.elf
1762+ - path: boot-assets/start_x.elf
1763+ - path: boot-assets/overlays.tgz
1764+`
1765+
1766+var compatCanonicalDragon = `
1767+name: canonical-dragon
1768+gadget:
1769+ branding:
1770+ name: Dragonboard
1771+ subname: Dragonboard
1772+
1773+ hardware:
1774+ platform: msm8916-mtp
1775+ architecture: arm64
1776+ partition-layout: minimal
1777+ bootloader: u-boot
1778+ boot-assets:
1779+ files:
1780+ - path: uboot.env
1781+ raw-files:
1782+ - path: sbl1.mbn
1783+ offset: 17408
1784+ - path: rpm.mbn
1785+ offset: 541696
1786+ - path: tz.mbn
1787+ offset: 1065984
1788+ - path: hyp.mbn
1789+ offset: 1590272
1790+ - path: sec.dat
1791+ offset: 2114560
1792+ - path: sd_appsboot.mbn
1793+ offset: 2130944
1794+ - path: u-boot.img
1795+ offset: 3179520
1796+ raw-partitions:
1797+ - name: sbl1
1798+ size: 512
1799+ pos: 34
1800+ type: DEA0BA2C-CBDD-4805-B4F9-F428251C3E98
1801+ - name: rpm
1802+ size: 512
1803+ type: 098DF793-D712-413D-9D4E-89D711772228
1804+ - name: tz
1805+ size: 512
1806+ type: A053AA7F-40B8-4B1C-BA08-2F68AC71A4F4
1807+ - name: hyp
1808+ size: 512
1809+ type: E1A6A689-0C8D-4CC6-B4E8-55A4320FBD8A
1810+ - name: sec
1811+ size: 16
1812+ type: 303E6AC3-AF15-4C54-9E9B-D9A8FBECF401
1813+ - name: aboot
1814+ size: 1024
1815+ type: 400FFDCD-22E0-47E7-9A23-F16ED9382388
1816+ - name: boot
1817+ size: 512
1818+ type: 20117F86-E985-4357-B9EE-374BC1D8487D
1819+`

Subscribers

People subscribed via source and target branches