Merge lp:~snappy-dev/goget-ubuntu-touch/all-snaps into lp:goget-ubuntu-touch
- all-snaps
- Merge into trunk
Status: | Needs review |
---|---|
Proposed branch: | lp:~snappy-dev/goget-ubuntu-touch/all-snaps |
Merge into: | lp:goget-ubuntu-touch |
Diff against target: |
1882 lines (+1243/-150) (has conflicts) 14 files modified
debian/changelog (+15/-0) debian/control (+14/-0) dependencies.tsv (+6/-1) diskimage/bootloader.go (+21/-12) diskimage/common.go (+99/-69) diskimage/common_test.go (+14/-14) diskimage/core_grub.go (+31/-27) diskimage/core_uboot.go (+32/-16) diskimage/partition.go (+1/-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/snappy.go (+771/-3) 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:~snappy-dev/goget-ubuntu-touch/all-snaps |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Sergio Schvezov | Approve | ||
Review via email: mp+275273@code.launchpad.net |
Commit message
Add support to build all-snaps images.
Description of the change
Add support to build all-snaps images.
Sergio Schvezov (sergiusens) wrote : | # |
On Oct 22, 2015 4:28 AM, "Michael Vogt" <email address hidden> wrote:
>
> Just added some comments so that we don't forget them :)
>
> Diff comments:
>
> > === modified file 'diskimage/
> > --- diskimage/common.go 2015-09-09 14:36:46 +0000
> > +++ diskimage/common.go 2015-10-22 07:16:55 +0000
> > @@ -190,16 +190,19 @@
> >
> > // BaseImage implements the basic primitives to manage images.
> > type BaseImage struct {
> > - baseMount string
> > - hardware HardwareDescription
> > - location string
> > - oem OemDescription
> > - parts []partition
> > - partCount int
> > - size int64
> > - rootSize int
> > + baseMount string
> > + bindMounts []string
> > + hardware HardwareDescription
> > + location string
> > + oem OemDescription
> > + parts []partition
> > + partCount int
> > + size int64
> > + rootSize int
> > }
> >
> > +var bindMounts = []string{"dev", "sys", "proc", filepath.
"firmware")}
>
> It looks like we are doing this twice (once here, once in snappy).
You will see a bunch of these little things which could go away when
migrating the provisioner into snappy itself.
>
> > +
> > // Mount mounts the image. This also maps the loop device.
> > func (img *BaseImage) Mount() error {
> > if err := img.doMap(); err != nil {
> >
> > === modified file 'ubuntu-
> > --- ubuntu-
> > +++ ubuntu-
> > @@ -339,7 +343,21 @@
> > return hw, err
> > }
> >
> > -func (s *Snapper) setup(filePathChan <-chan string, fileCount int)
error {
> > +func (s *Snapper) bindMount(d string) (string, error) {
>
> I gues the name could be better, something like bindMountInWritable or so
>
> > + src := filepath.
> > + dst := filepath.
> > +
> > + if err := os.MkdirAll(src, 0755); err != nil {
> > + return "", err
> > + }
> > + cmd := exec.Command(
> > + if o, err := cmd.CombinedOut
> > + return "", fmt.Errorf("os snap mount failed with: %s %v
", err, string(o))
> > + }
> > + return dst, nil
> > +}
> > +
> > +func (s *Snapper) setup(systemIma
> > printOut(
> > if err := s.img.Mount(); err != nil {
> > return err
> > @@ -362,14 +379,82 @@
> >
> > systemPath := s.img.System()
> >
> > + // setup a fake system
> > + if s.oem.Partition
> > + if err := os.MkdirAll(
> > + return err
> > + }
> > + // mount os snap
> > + cmd := exec.Command(
> > + if o, err := cmd.CombinedOut
> > + return fmt.Errorf("os snap mount failed with: %s
%v ", err, string(o))
> > + }
> > + defer exec.Command(
> > +
> > + //...
Sergio Schvezov (sergiusens) wrote : | # |
Thanks for this, it removes a bunch of legacy. I added a couple of inline comments, nothing major.
MPs like this https:/
Sergio Schvezov (sergiusens) : | # |
Michael Vogt (mvo) wrote : | # |
This is updated and all comments are addressed now.
Snappy Tarmac (snappydevtarmac) wrote : | # |
The attempt to merge lp:~snappy-dev/goget-ubuntu-touch/all-snaps into lp:goget-ubuntu-touch failed. Below is the output from the failed tests.
Checking formatting
Installing godeps
Install golint
Obtaining dependencies
update launchpad.
launchpad.
update github.
github.
update github.
github.
update github.
github.
update github.
github.
update github.
github.
update github.
github.
update gopkg.in/yaml.v2 failed; trying to fetch newer version
gopkg.in/yaml.v2 now at 7ad95dd0798a40d
Building
# we always run in a fresh dir in tarmac
export GOPATH=$(mktemp -d)
trap 'rm -rf "$GOPATH"' EXIT
# this is a hack, but not sure tarmac is golang friendly
mkdir -p $GOPATH/
cp -a . $GOPATH/
cd $GOPATH/
./run-checks
launchpad.
launchpad.
launchpad.
launchpad.
launchpad.
github.
github.
launchpad.
github.
github.
launchpad.
github.
github.
github.
github.
github.
github.
github.
gopkg.in/yaml.v2
github.
github.
github.
github.
github.
github.
github.
launchpad.
github.
github.
github.
github.
github.
github.
launchpad.
# launchpad.
ubuntu-
ubuntu-
- 264. By Sergio Schvezov
-
Update to latest snappy code
- 265. By Michael Vogt
-
ppa uploads
- 266. By Michael Vogt
-
fix bind mounts
- 267. By Michael Vogt
-
update to the latest api
- 268. By Michael Vogt
-
manually enable all snaps on the system
- 269. By Michael Vogt
-
remove debug code
- 270. By Michael Vogt
-
update for latest snappy
- 271. By Michael Vogt
-
add check for having systemctl
- 272. By Michael Vogt
-
set snappy_
{,good} _{kernel, os} to fix LP:#1534137 - 273. By Michael Vogt
-
fix squashfs format issue
- 274. By Michael Vogt
-
use gadget snap whitelist in preparation for the rework of the gadget yaml
- 275. By Michael Vogt
-
merged lp:~pedronis/goget-ubuntu-touch/use-read-info-from-snapfile
- 276. By Michael Vogt
-
fix for latest api change
Unmerged revisions
- 276. By Michael Vogt
-
fix for latest api change
- 275. By Michael Vogt
-
merged lp:~pedronis/goget-ubuntu-touch/use-read-info-from-snapfile
- 274. By Michael Vogt
-
use gadget snap whitelist in preparation for the rework of the gadget yaml
- 273. By Michael Vogt
-
fix squashfs format issue
- 272. By Michael Vogt
-
set snappy_
{,good} _{kernel, os} to fix LP:#1534137 - 271. By Michael Vogt
-
add check for having systemctl
- 270. By Michael Vogt
-
update for latest snappy
- 269. By Michael Vogt
-
remove debug code
- 268. By Michael Vogt
-
manually enable all snaps on the system
- 267. By Michael Vogt
-
update to the latest api
Preview Diff
1 | === modified file 'debian/changelog' |
2 | --- debian/changelog 2016-04-05 10:22:02 +0000 |
3 | +++ debian/changelog 2016-05-10 18:32:26 +0000 |
4 | @@ -1,3 +1,4 @@ |
5 | +<<<<<<< TREE |
6 | goget-ubuntu-touch (0.34-0ubuntu1) xenial; urgency=medium |
7 | |
8 | [ Ondrej Kubik ] |
9 | @@ -19,6 +20,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-05-10 18:32:26 +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-05-10 18:32:26 +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-05-10 18:32:26 +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-05-10 18:32:26 +0000 |
137 | @@ -17,8 +17,6 @@ |
138 | "path/filepath" |
139 | "strings" |
140 | "syscall" |
141 | - |
142 | - "launchpad.net/goget-ubuntu-touch/sysutils" |
143 | ) |
144 | |
145 | // This program is free software: you can redistribute it and/or modify it |
146 | @@ -47,10 +45,6 @@ |
147 | initrdFileName = "initrd.img" |
148 | ) |
149 | |
150 | -const ( |
151 | - partLayoutSystemAB = "system-AB" |
152 | -) |
153 | - |
154 | var ( |
155 | syscallSync = syscall.Sync |
156 | ) |
157 | @@ -88,9 +82,16 @@ |
158 | } |
159 | |
160 | type BootAssetRawPartitions struct { |
161 | +<<<<<<< TREE |
162 | Name string `yaml:"name"` |
163 | Size string `yaml:"size"` |
164 | Type string `yaml:"type"` |
165 | +======= |
166 | + Name string `yaml:"name"` |
167 | + Size int `yaml:"size"` |
168 | + Pos int `yaml:"pos"` |
169 | + Type string `yaml:"type"` |
170 | +>>>>>>> MERGE-SOURCE |
171 | } |
172 | |
173 | type BootAssetFiles struct { |
174 | @@ -107,11 +108,11 @@ |
175 | RawPartitions []BootAssetRawPartitions `yaml:"raw-partitions,omitempty"` |
176 | } |
177 | |
178 | -type OemDescription struct { |
179 | +type GadgetDescription struct { |
180 | Name string `yaml:"name"` |
181 | Version string `yaml:"version"` |
182 | |
183 | - OEM struct { |
184 | + Gadget struct { |
185 | Hardware struct { |
186 | Bootloader string `yaml:"bootloader"` |
187 | PartitionLayout string `yaml:"partition-layout"` |
188 | @@ -129,7 +130,7 @@ |
189 | Store *struct { |
190 | ID string `yaml:"id,omitempty"` |
191 | } |
192 | - } `yaml:"oem,omitempty"` |
193 | + } `yaml:"gadget,omitempty"` |
194 | |
195 | Config struct { |
196 | UbuntuCore struct { |
197 | @@ -140,53 +141,52 @@ |
198 | rootDir string |
199 | } |
200 | |
201 | -func (o *OemDescription) SetRoot(rootDir string) { |
202 | +func (o *GadgetDescription) SetRoot(rootDir string) { |
203 | o.rootDir = rootDir |
204 | } |
205 | |
206 | // SystemParts returns the system labels depending on the partition layout. |
207 | // |
208 | // The default is to return a flat structure for any unknown layout. |
209 | -func (o *OemDescription) SystemParts() []string { |
210 | - switch o.OEM.Hardware.PartitionLayout { |
211 | - case partLayoutSystemAB: |
212 | - return []string{"a", "b"} |
213 | +func (o *GadgetDescription) SystemParts() []string { |
214 | + switch o.Gadget.Hardware.PartitionLayout { |
215 | default: |
216 | return []string{""} |
217 | } |
218 | } |
219 | |
220 | -func (o OemDescription) InstallPath() (string, error) { |
221 | - glob, err := filepath.Glob(fmt.Sprintf("%s/oem/%s/current", o.rootDir, o.Name)) |
222 | +func (o GadgetDescription) InstallPath() (string, error) { |
223 | + |
224 | + glob, err := filepath.Glob(fmt.Sprintf("%s/gadget/*/*", o.rootDir)) |
225 | if err != nil { |
226 | return "", err |
227 | } |
228 | |
229 | if len(glob) != 1 { |
230 | - return "", errors.New("oem package not installed") |
231 | + return "", errors.New("gadget package not installed") |
232 | } |
233 | |
234 | return glob[0], nil |
235 | } |
236 | |
237 | -func (o OemDescription) Architecture() string { |
238 | - return o.OEM.Hardware.Architecture |
239 | -} |
240 | - |
241 | -func (o *OemDescription) SetArchitecture(architecture string) { |
242 | - o.OEM.Hardware.Architecture = architecture |
243 | -} |
244 | - |
245 | -func (o OemDescription) PartitionLayout() string { |
246 | - return o.OEM.Hardware.PartitionLayout |
247 | -} |
248 | - |
249 | -func (o OemDescription) Platform() string { |
250 | - return o.OEM.Hardware.Platform |
251 | -} |
252 | - |
253 | -func (o *OemDescription) SetPlatform(platform string) { |
254 | - o.OEM.Hardware.Platform = platform |
255 | +func (o GadgetDescription) Architecture() string { |
256 | + return o.Gadget.Hardware.Architecture |
257 | +} |
258 | + |
259 | +func (o *GadgetDescription) SetArchitecture(architecture string) { |
260 | + o.Gadget.Hardware.Architecture = architecture |
261 | +} |
262 | + |
263 | +func (o GadgetDescription) PartitionLayout() string { |
264 | + return o.Gadget.Hardware.PartitionLayout |
265 | +} |
266 | + |
267 | +func (o GadgetDescription) Platform() string { |
268 | + return o.Gadget.Hardware.Platform |
269 | +} |
270 | + |
271 | +func (o *GadgetDescription) SetPlatform(platform string) { |
272 | + o.Gadget.Hardware.Platform = platform |
273 | } |
274 | |
275 | func sectorSize(dev string) (string, error) { |
276 | @@ -200,6 +200,7 @@ |
277 | |
278 | // BaseImage implements the basic primitives to manage images. |
279 | type BaseImage struct { |
280 | +<<<<<<< TREE |
281 | baseMount string |
282 | hardware HardwareDescription |
283 | location string |
284 | @@ -209,8 +210,22 @@ |
285 | size int64 |
286 | rootSize int |
287 | label string |
288 | +======= |
289 | + baseMount string |
290 | + bindMounts []string |
291 | + hardware HardwareDescription |
292 | + location string |
293 | + gadget GadgetDescription |
294 | + parts []partition |
295 | + partCount int |
296 | + size int64 |
297 | + rootSize int |
298 | + label string |
299 | +>>>>>>> MERGE-SOURCE |
300 | } |
301 | |
302 | +var bindMounts = []string{"dev", "sys", "proc", filepath.Join("sys", "firmware")} |
303 | + |
304 | // Mount mounts the image. This also maps the loop device. |
305 | func (img *BaseImage) Mount() error { |
306 | if err := img.doMap(); err != nil { |
307 | @@ -267,8 +282,27 @@ |
308 | } |
309 | img.baseMount = baseMount |
310 | |
311 | + mountpoints := make([]string, 0, len(bindMounts)) |
312 | + if img.gadget.PartitionLayout() == "minimal" { |
313 | + mountpoints = bindMounts |
314 | + |
315 | + for _, d := range mountpoints { |
316 | + p := filepath.Join(baseMount, d) |
317 | + |
318 | + if err := os.MkdirAll(p, 0755); err != nil { |
319 | + return err |
320 | + } |
321 | + |
322 | + printOut("Bind mounting", d, "to", p) |
323 | + if err := bindMount(filepath.Join("/", d), p); err != nil { |
324 | + return err |
325 | + } |
326 | + |
327 | + img.bindMounts = append(img.bindMounts, p) |
328 | + } |
329 | + } |
330 | + |
331 | return nil |
332 | - |
333 | } |
334 | |
335 | // Unmount unmounts the image. This also unmaps the loop device. |
336 | @@ -283,6 +317,13 @@ |
337 | panic("No base mountpoint set") |
338 | } |
339 | |
340 | + for i := len(img.bindMounts) - 1; i >= 0; i-- { |
341 | + if err := unmount(img.bindMounts[i]); err != nil { |
342 | + return err |
343 | + } |
344 | + } |
345 | + img.bindMounts = nil |
346 | + |
347 | syscallSync() |
348 | |
349 | for _, part := range img.parts { |
350 | @@ -466,55 +507,28 @@ |
351 | } |
352 | |
353 | func (img *BaseImage) GenericBootSetup(bootPath string) error { |
354 | - // origins |
355 | - hardwareYamlPath := filepath.Join(img.baseMount, hardwareFileName) |
356 | - kernelPath := filepath.Join(img.baseMount, img.hardware.Kernel) |
357 | - initrdPath := filepath.Join(img.baseMount, img.hardware.Initrd) |
358 | - |
359 | - // populate both A/B |
360 | - for _, part := range img.oem.SystemParts() { |
361 | - path := filepath.Join(bootPath, part) |
362 | - |
363 | - printOut("Setting up", path) |
364 | - |
365 | - if err := os.MkdirAll(path, 0755); err != nil { |
366 | - return err |
367 | - } |
368 | - |
369 | - if err := sysutils.CopyFile(hardwareYamlPath, filepath.Join(path, hardwareFileName)); err != nil { |
370 | - return err |
371 | - } |
372 | - |
373 | - if err := sysutils.CopyFile(kernelPath, filepath.Join(path, kernelFileName)); err != nil { |
374 | - return err |
375 | - } |
376 | - |
377 | - if err := sysutils.CopyFile(initrdPath, filepath.Join(path, initrdFileName)); err != nil { |
378 | - return err |
379 | - } |
380 | - } |
381 | - |
382 | - oemRoot, err := img.oem.InstallPath() |
383 | + gadgetRoot, err := img.gadget.InstallPath() |
384 | if err != nil { |
385 | return err |
386 | } |
387 | |
388 | - return setupBootAssetFiles(img.Boot(), bootPath, oemRoot, img.oem.OEM.Hardware.BootAssets.Files) |
389 | + return setupBootAssetFiles(img.Boot(), bootPath, gadgetRoot, img.gadget.Gadget.Hardware.BootAssets.Files) |
390 | } |
391 | |
392 | func (img *BaseImage) FlashExtra() error { |
393 | - oemRoot, err := img.oem.InstallPath() |
394 | + gadgetRoot, err := img.gadget.InstallPath() |
395 | if err != nil { |
396 | return err |
397 | } |
398 | |
399 | - if bootAssets := img.oem.OEM.Hardware.BootAssets; bootAssets != nil { |
400 | + if bootAssets := img.gadget.Gadget.Hardware.BootAssets; bootAssets != nil { |
401 | if bootAssets.RawPartitions != nil { |
402 | if err := setupBootAssetRawPartitions(img.location, img.partCount, bootAssets.RawPartitions); err != nil { |
403 | return err |
404 | } |
405 | } |
406 | - return setupBootAssetRawFiles(img.location, oemRoot, bootAssets.RawFiles) |
407 | + |
408 | + return setupBootAssetRawFiles(img.location, gadgetRoot, bootAssets.RawFiles) |
409 | } |
410 | |
411 | return nil |
412 | @@ -525,3 +539,19 @@ |
413 | fmt.Println(args...) |
414 | } |
415 | } |
416 | + |
417 | +func bindMount(src, dst string) error { |
418 | + if out, err := exec.Command("mount", "--bind", src, dst).CombinedOutput(); err != nil { |
419 | + return fmt.Errorf("issues while bind mounting: %s", out) |
420 | + } |
421 | + |
422 | + return nil |
423 | +} |
424 | + |
425 | +func unmount(dst string) error { |
426 | + if out, err := exec.Command("umount", dst).CombinedOutput(); err != nil { |
427 | + return fmt.Errorf("issues while unmounting: %s", out) |
428 | + } |
429 | + |
430 | + return nil |
431 | +} |
432 | |
433 | === modified file 'diskimage/common_test.go' |
434 | --- diskimage/common_test.go 2015-09-09 16:00:01 +0000 |
435 | +++ diskimage/common_test.go 2016-05-10 18:32:26 +0000 |
436 | @@ -28,7 +28,7 @@ |
437 | |
438 | type CommonTestSuite struct { |
439 | tmpdir string |
440 | - oem OemDescription |
441 | + gadget GadgetDescription |
442 | packageInst string |
443 | } |
444 | |
445 | @@ -36,27 +36,27 @@ |
446 | |
447 | func (s *CommonTestSuite) SetUpTest(c *C) { |
448 | s.tmpdir = c.MkDir() |
449 | - s.oem = OemDescription{Name: "packagename", Version: "42"} |
450 | - s.packageInst = s.oem.Name |
451 | + s.gadget = GadgetDescription{Name: "packagename", Version: "42"} |
452 | + s.packageInst = s.gadget.Name |
453 | } |
454 | |
455 | func (s *CommonTestSuite) TestOemInstallPath(c *C) { |
456 | - err := os.MkdirAll(filepath.Join(s.tmpdir, "oem", s.packageInst, "current"), 0755) |
457 | - c.Assert(err, IsNil) |
458 | - |
459 | - s.oem.SetRoot(s.tmpdir) |
460 | - installPath, err := s.oem.InstallPath() |
461 | - |
462 | - c.Assert(err, IsNil) |
463 | - c.Assert(installPath, Equals, filepath.Join(s.tmpdir, "oem/packagename/current")) |
464 | + err := os.MkdirAll(filepath.Join(s.tmpdir, "gadget", s.packageInst, "current"), 0755) |
465 | + c.Assert(err, IsNil) |
466 | + |
467 | + s.gadget.SetRoot(s.tmpdir) |
468 | + installPath, err := s.gadget.InstallPath() |
469 | + |
470 | + c.Assert(err, IsNil) |
471 | + c.Assert(installPath, Equals, filepath.Join(s.tmpdir, "gadget/packagename/current")) |
472 | } |
473 | |
474 | func (s *CommonTestSuite) TestOemInstallPathNoOem(c *C) { |
475 | - err := os.MkdirAll(filepath.Join(s.tmpdir, "oem", s.packageInst), 0755) |
476 | + err := os.MkdirAll(filepath.Join(s.tmpdir, "gadget", s.packageInst), 0755) |
477 | c.Assert(err, IsNil) |
478 | |
479 | - s.oem.SetRoot(s.tmpdir) |
480 | - installPath, err := s.oem.InstallPath() |
481 | + s.gadget.SetRoot(s.tmpdir) |
482 | + installPath, err := s.gadget.InstallPath() |
483 | |
484 | c.Assert(err, NotNil) |
485 | c.Assert(installPath, Equals, "") |
486 | |
487 | === modified file 'diskimage/core_grub.go' |
488 | --- diskimage/core_grub.go 2016-04-05 09:32:48 +0000 |
489 | +++ diskimage/core_grub.go 2016-05-10 18:32:26 +0000 |
490 | @@ -5,6 +5,7 @@ |
491 | // |
492 | // Written by Sergio Schvezov <sergio.schvezov@canonical.com> |
493 | // |
494 | + |
495 | package diskimage |
496 | |
497 | import ( |
498 | @@ -31,25 +32,40 @@ |
499 | // You should have received a copy of the GNU General Public License along |
500 | // with this program. If not, see <http://www.gnu.org/licenses/>. |
501 | |
502 | +// CoreGrubImage holds the logic to create a core image using grub. |
503 | type CoreGrubImage struct { |
504 | BaseImage |
505 | |
506 | legacyGrub bool |
507 | } |
508 | |
509 | -func NewCoreGrubImage(location string, size int64, rootSize int, hw HardwareDescription, oem OemDescription, updateGrub bool, label string) *CoreGrubImage { |
510 | +// NewCoreGrubImage creates a new instance of CoreGrubImage |
511 | +func NewCoreGrubImage(location string, size int64, rootSize int, hw HardwareDescription, gadget GadgetDescription, updateGrub bool, label string) *CoreGrubImage { |
512 | + var partCount int |
513 | + switch gadget.PartitionLayout() { |
514 | + case "minimal": |
515 | + partCount = 3 |
516 | + } |
517 | + |
518 | return &CoreGrubImage{ |
519 | BaseImage: BaseImage{ |
520 | location: location, |
521 | size: size, |
522 | rootSize: rootSize, |
523 | hardware: hw, |
524 | +<<<<<<< TREE |
525 | oem: oem, |
526 | partCount: 5, |
527 | label: label, |
528 | +======= |
529 | + gadget: gadget, |
530 | + partCount: partCount, |
531 | + label: label, |
532 | +>>>>>>> MERGE-SOURCE |
533 | }, |
534 | legacyGrub: updateGrub, |
535 | } |
536 | + |
537 | } |
538 | |
539 | const grubCfgContent = `# console only, no graphics/vga |
540 | @@ -75,9 +91,10 @@ |
541 | } |
542 | |
543 | parted.addPart(grubLabel, "", fsNone, 4) |
544 | - parted.addPart(bootLabel, bootDir, fsFat32, 128) |
545 | - parted.addPart(systemALabel, systemADir, fsExt4, img.rootSize) |
546 | - parted.addPart(systemBLabel, systemBDir, fsExt4, img.rootSize) |
547 | + switch img.gadget.PartitionLayout() { |
548 | + case "minimal": |
549 | + parted.addPart(bootLabel, bootDir, fsFat32, 64) |
550 | + } |
551 | parted.addPart(writableLabel, writableDir, fsExt4, -1) |
552 | |
553 | parted.setBoot(2) |
554 | @@ -88,6 +105,7 @@ |
555 | return parted.create(img.location) |
556 | } |
557 | |
558 | +// SetupBoot sets up the bootloader logic for the image. |
559 | func (img *CoreGrubImage) SetupBoot() error { |
560 | if !img.legacyGrub { |
561 | // destinations |
562 | @@ -121,14 +139,14 @@ |
563 | return errors.New("cannot determined absolute path for output image") |
564 | } |
565 | |
566 | - rootDevPath := filepath.Join(img.System(), "root_dev") |
567 | + rootDevPath := filepath.Join(img.System(), "tmp", "root_dev") |
568 | |
569 | - if f, err := os.Create(rootDevPath); err != nil { |
570 | + f, err := os.Create(rootDevPath) |
571 | + if err != nil { |
572 | return err |
573 | - } else { |
574 | - f.Close() |
575 | - defer os.Remove(rootDevPath) |
576 | } |
577 | + f.Close() |
578 | + defer os.Remove(rootDevPath) |
579 | |
580 | if err := bindMount(outputPath, rootDevPath); err != nil { |
581 | return err |
582 | @@ -165,7 +183,7 @@ |
583 | |
584 | var grubTarget string |
585 | |
586 | - arch := img.oem.Architecture() |
587 | + arch := img.gadget.Architecture() |
588 | |
589 | switch arch { |
590 | case "armhf": |
591 | @@ -174,13 +192,15 @@ |
592 | grubTarget = "x86_64-efi" |
593 | case "i386": |
594 | grubTarget = "i386-efi" |
595 | + case "arm64": |
596 | + grubTarget = "arm64-efi" |
597 | default: |
598 | return fmt.Errorf("unsupported architecture for GRUB on EFI: %s", arch) |
599 | } |
600 | |
601 | if arch == "amd64" || arch == "i386" { |
602 | // install grub BIOS support |
603 | - if out, err := exec.Command("chroot", img.System(), "grub-install", "/root_dev").CombinedOutput(); err != nil { |
604 | + if out, err := exec.Command("chroot", img.System(), "grub-install", "tmp/root_dev").CombinedOutput(); err != nil { |
605 | return fmt.Errorf("unable to install grub (BIOS): %s", out) |
606 | } |
607 | } |
608 | @@ -233,19 +253,3 @@ |
609 | |
610 | return nil |
611 | } |
612 | - |
613 | -func bindMount(src, dst string) error { |
614 | - if out, err := exec.Command("mount", "--bind", src, dst).CombinedOutput(); err != nil { |
615 | - return fmt.Errorf("issues while bind mounting: %s", out) |
616 | - } |
617 | - |
618 | - return nil |
619 | -} |
620 | - |
621 | -func unmount(dst string) error { |
622 | - if out, err := exec.Command("umount", dst).CombinedOutput(); err != nil { |
623 | - return fmt.Errorf("issues while unmounting: %s", out) |
624 | - } |
625 | - |
626 | - return nil |
627 | -} |
628 | |
629 | === modified file 'diskimage/core_uboot.go' |
630 | --- diskimage/core_uboot.go 2016-04-05 09:32:48 +0000 |
631 | +++ diskimage/core_uboot.go 2016-05-10 18:32:26 +0000 |
632 | @@ -66,16 +66,27 @@ |
633 | Bootloader []string `yaml:"bootloader"` |
634 | } |
635 | |
636 | -func NewCoreUBootImage(location string, size int64, rootSize int, hw HardwareDescription, oem OemDescription, label string) *CoreUBootImage { |
637 | +func NewCoreUBootImage(location string, size int64, rootSize int, hw HardwareDescription, gadget GadgetDescription, label string) *CoreUBootImage { |
638 | + var partCount int |
639 | + switch gadget.PartitionLayout() { |
640 | + case "minimal": |
641 | + partCount = 2 |
642 | + } |
643 | + |
644 | return &CoreUBootImage{ |
645 | BaseImage{ |
646 | hardware: hw, |
647 | - oem: oem, |
648 | + gadget: gadget, |
649 | location: location, |
650 | size: size, |
651 | rootSize: rootSize, |
652 | +<<<<<<< TREE |
653 | partCount: 4, |
654 | label: label, |
655 | +======= |
656 | + partCount: partCount, |
657 | + label: label, |
658 | +>>>>>>> MERGE-SOURCE |
659 | }, |
660 | } |
661 | } |
662 | @@ -95,9 +106,10 @@ |
663 | return err |
664 | } |
665 | |
666 | - parted.addPart(bootLabel, bootDir, fsFat32, 128) |
667 | - parted.addPart(systemALabel, systemADir, fsExt4, 1024) |
668 | - parted.addPart(systemBLabel, systemBDir, fsExt4, 1024) |
669 | + switch img.gadget.PartitionLayout() { |
670 | + case "minimal": |
671 | + parted.addPart(bootLabel, bootDir, fsFat32, 128) |
672 | + } |
673 | parted.addPart(writableLabel, writableDir, fsExt4, -1) |
674 | |
675 | parted.setBoot(1) |
676 | @@ -117,7 +129,7 @@ |
677 | } |
678 | |
679 | // populate both A/B |
680 | - for _, part := range img.oem.SystemParts() { |
681 | + for _, part := range img.gadget.SystemParts() { |
682 | bootDtbPath := filepath.Join(bootPath, part, "dtbs") |
683 | if err := img.provisionDtbs(bootDtbPath); err != nil { |
684 | return err |
685 | @@ -131,7 +143,7 @@ |
686 | defer snappySystemFile.Close() |
687 | |
688 | var fdtfile string |
689 | - if platform := img.oem.Platform(); platform != "" { |
690 | + if platform := img.gadget.Platform(); platform != "" { |
691 | fdtfile = fmt.Sprintf("fdtfile=%s.dtb", platform) |
692 | } |
693 | |
694 | @@ -154,28 +166,32 @@ |
695 | return err |
696 | } |
697 | |
698 | - dtbFis, err := ioutil.ReadDir(dtbsPath) |
699 | - if err != nil { |
700 | - return err |
701 | + var dtbFis []os.FileInfo |
702 | + if img.hardware.Dtbs != "" { |
703 | + var err error |
704 | + dtbFis, err = ioutil.ReadDir(dtbsPath) |
705 | + if err != nil { |
706 | + return err |
707 | + } |
708 | } |
709 | |
710 | if err := os.MkdirAll(bootDtbPath, 0755); err != nil { |
711 | return err |
712 | } |
713 | |
714 | - dtb := filepath.Join(dtbsPath, fmt.Sprintf("%s.dtb", img.oem.Platform())) |
715 | + dtb := filepath.Join(dtbsPath, fmt.Sprintf("%s.dtb", img.gadget.Platform())) |
716 | |
717 | // if there is a specific dtb for the platform, copy it. |
718 | - // First look in oem and then in device. |
719 | - if oemDtb := img.oem.OEM.Hardware.Dtb; oemDtb != "" && img.oem.Platform() != "" { |
720 | - oemRoot, err := img.oem.InstallPath() |
721 | + // First look in gadget and then in device. |
722 | + if gadgetDtb := img.gadget.Gadget.Hardware.Dtb; gadgetDtb != "" && img.gadget.Platform() != "" { |
723 | + gadgetRoot, err := img.gadget.InstallPath() |
724 | if err != nil { |
725 | return err |
726 | } |
727 | |
728 | - oemDtb := filepath.Join(oemRoot, oemDtb) |
729 | + gadgetDtb := filepath.Join(gadgetRoot, gadgetDtb) |
730 | dst := filepath.Join(bootDtbPath, filepath.Base(dtb)) |
731 | - if err := sysutils.CopyFile(oemDtb, dst); err != nil { |
732 | + if err := sysutils.CopyFile(gadgetDtb, dst); err != nil { |
733 | return err |
734 | } |
735 | } else if _, err := os.Stat(dtb); err == nil { |
736 | |
737 | === modified file 'diskimage/partition.go' |
738 | --- diskimage/partition.go 2015-01-15 21:05:01 +0000 |
739 | +++ diskimage/partition.go 2016-05-10 18:32:26 +0000 |
740 | @@ -5,6 +5,7 @@ |
741 | // |
742 | // Written by Sergio Schvezov <sergio.schvezov@canonical.com> |
743 | // |
744 | + |
745 | package diskimage |
746 | |
747 | import ( |
748 | |
749 | === modified file 'ubuntu-device-flash/common.go' |
750 | --- ubuntu-device-flash/common.go 2015-03-30 03:28:28 +0000 |
751 | +++ ubuntu-device-flash/common.go 2016-05-10 18:32:26 +0000 |
752 | @@ -61,11 +61,11 @@ |
753 | |
754 | // expandFile checks for file existence, correct permissions and returns the absolute path. |
755 | func expandFile(path string) (abspath string, err error) { |
756 | - if p, err := filepath.Abs(path); err != nil { |
757 | + p, err := filepath.Abs(path) |
758 | + if err != nil { |
759 | return "", err |
760 | - } else { |
761 | - abspath = p |
762 | } |
763 | + abspath = p |
764 | |
765 | fi, err := os.Lstat(abspath) |
766 | if err != nil { |
767 | |
768 | === modified file 'ubuntu-device-flash/core.go' |
769 | --- ubuntu-device-flash/core.go 2016-04-04 15:54:40 +0000 |
770 | +++ ubuntu-device-flash/core.go 2016-05-10 18:32:26 +0000 |
771 | @@ -77,21 +77,20 @@ |
772 | |
773 | if !coreCmd.Deprecated.Cloud { |
774 | coreCmd.customizationFunc = append(coreCmd.customizationFunc, coreCmd.setupCloudInit) |
775 | - coreCmd.customizationFunc = append(coreCmd.customizationFunc, coreCmd.setupOemConfigs) |
776 | + coreCmd.customizationFunc = append(coreCmd.customizationFunc, coreCmd.setupGadgetConfigs) |
777 | } |
778 | |
779 | return coreCmd.create() |
780 | } |
781 | |
782 | // this is a hackish way to get the config in place |
783 | -func (coreCmd *CoreCmd) setupOemConfigs() error { |
784 | - modprobeDContent := coreCmd.oem.Config.UbuntuCore.Modprobe |
785 | +func (coreCmd *CoreCmd) setupGadgetConfigs() error { |
786 | + modprobeDContent := coreCmd.gadget.Config.UbuntuCore.Modprobe |
787 | if modprobeDContent == nil { |
788 | - printOut("no modprobe") |
789 | return nil |
790 | } |
791 | |
792 | - fmt.Println("Setting up oem hooks...") |
793 | + fmt.Println("Setting up gadget hooks...") |
794 | |
795 | writablePath := coreCmd.img.Writable() |
796 | |
797 | @@ -101,12 +100,20 @@ |
798 | } |
799 | |
800 | // first we need to copy all the files in modprobe.d |
801 | +<<<<<<< TREE |
802 | /* |
803 | systemModprobeDir := filepath.Join(coreCmd.img.System(), "etc", "modprobe.d") |
804 | if err := helpers.RSyncWithDelete(systemModprobeDir, modprobeDir); err != nil { |
805 | return err |
806 | } |
807 | */ |
808 | +======= |
809 | + systemModprobeDir := filepath.Join(coreCmd.img.System(), "etc", "modprobe.d") |
810 | + // FIXME: can we do "cp -a" here? |
811 | + if err := RSyncWithDelete(systemModprobeDir, modprobeDir); err != nil { |
812 | + return err |
813 | + } |
814 | +>>>>>>> MERGE-SOURCE |
815 | |
816 | modprobeD := filepath.Join(modprobeDir, "ubuntu-core.conf") |
817 | modprobeDFile, err := os.Create(modprobeD) |
818 | |
819 | === added file 'ubuntu-device-flash/helpers.go' |
820 | --- ubuntu-device-flash/helpers.go 1970-01-01 00:00:00 +0000 |
821 | +++ ubuntu-device-flash/helpers.go 2016-05-10 18:32:26 +0000 |
822 | @@ -0,0 +1,89 @@ |
823 | +// -*- Mode: Go; indent-tabs-mode: t -*- |
824 | + |
825 | +/* |
826 | + * Copyright (C) 2014-2015 Canonical Ltd |
827 | + * |
828 | + * This program is free software: you can redistribute it and/or modify |
829 | + * it under the terms of the GNU General Public License version 3 as |
830 | + * published by the Free Software Foundation. |
831 | + * |
832 | + * This program is distributed in the hope that it will be useful, |
833 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
834 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
835 | + * GNU General Public License for more details. |
836 | + * |
837 | + * You should have received a copy of the GNU General Public License |
838 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
839 | + * |
840 | + */ |
841 | + |
842 | +package main |
843 | + |
844 | +import ( |
845 | + "fmt" |
846 | + "os" |
847 | + "os/exec" |
848 | + "path/filepath" |
849 | + "syscall" |
850 | + |
851 | + "github.com/ubuntu-core/snappy/osutil" |
852 | +) |
853 | + |
854 | +// RSyncWithDelete syncs srcDir to destDir |
855 | +func RSyncWithDelete(srcDirName, destDirName string) error { |
856 | + // first remove everything thats not in srcdir |
857 | + err := filepath.Walk(destDirName, func(path string, info os.FileInfo, err error) error { |
858 | + if err != nil { |
859 | + return err |
860 | + } |
861 | + |
862 | + // relative to the root "destDirName" |
863 | + relPath := path[len(destDirName):] |
864 | + if !osutil.FileExists(filepath.Join(srcDirName, relPath)) { |
865 | + if err := os.RemoveAll(path); err != nil { |
866 | + return err |
867 | + } |
868 | + if info.IsDir() { |
869 | + return filepath.SkipDir |
870 | + } |
871 | + } |
872 | + return nil |
873 | + }) |
874 | + if err != nil { |
875 | + return err |
876 | + } |
877 | + |
878 | + // then copy or update the data from srcdir to destdir |
879 | + err = filepath.Walk(srcDirName, func(src string, info os.FileInfo, err error) error { |
880 | + if err != nil { |
881 | + return err |
882 | + } |
883 | + |
884 | + // relative to the root "srcDirName" |
885 | + relPath := src[len(srcDirName):] |
886 | + dst := filepath.Join(destDirName, relPath) |
887 | + if info.IsDir() { |
888 | + if err := os.MkdirAll(dst, info.Mode()); err != nil { |
889 | + return err |
890 | + } |
891 | + |
892 | + // this can panic. The alternative would be to use the "st, ok" pattern, and then if !ok... panic? |
893 | + st := info.Sys().(*syscall.Stat_t) |
894 | + ts := []syscall.Timespec{st.Atim, st.Mtim} |
895 | + |
896 | + return syscall.UtimesNano(dst, ts) |
897 | + } |
898 | + if !osutil.FilesAreEqual(src, dst) { |
899 | + // XXX: we should (eventually) use CopyFile here, |
900 | + // but we need to teach it about preserving |
901 | + // of atime/mtime and permissions |
902 | + output, err := exec.Command("cp", "-va", src, dst).CombinedOutput() |
903 | + if err != nil { |
904 | + return fmt.Errorf("Failed to copy %s to %s (%s)", src, dst, output) |
905 | + } |
906 | + } |
907 | + return nil |
908 | + }) |
909 | + |
910 | + return err |
911 | +} |
912 | |
913 | === added file 'ubuntu-device-flash/helpers_test.go' |
914 | --- ubuntu-device-flash/helpers_test.go 1970-01-01 00:00:00 +0000 |
915 | +++ ubuntu-device-flash/helpers_test.go 2016-05-10 18:32:26 +0000 |
916 | @@ -0,0 +1,135 @@ |
917 | +// -*- Mode: Go; indent-tabs-mode: t -*- |
918 | + |
919 | +/* |
920 | + * Copyright (C) 2014-2015 Canonical Ltd |
921 | + * |
922 | + * This program is free software: you can redistribute it and/or modify |
923 | + * it under the terms of the GNU General Public License version 3 as |
924 | + * published by the Free Software Foundation. |
925 | + * |
926 | + * This program is distributed in the hope that it will be useful, |
927 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
928 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
929 | + * GNU General Public License for more details. |
930 | + * |
931 | + * You should have received a copy of the GNU General Public License |
932 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
933 | + * |
934 | + */ |
935 | + |
936 | +package main |
937 | + |
938 | +import ( |
939 | + "fmt" |
940 | + "io/ioutil" |
941 | + "os" |
942 | + "os/exec" |
943 | + "path/filepath" |
944 | + |
945 | + . "launchpad.net/gocheck" |
946 | +) |
947 | + |
948 | +type HTestSuite struct{} |
949 | + |
950 | +var _ = Suite(&HTestSuite{}) |
951 | + |
952 | +func makeTestFiles(c *C, srcDir, destDir string) { |
953 | + // a new file |
954 | + err := ioutil.WriteFile(filepath.Join(srcDir, "new"), []byte(nil), 0644) |
955 | + c.Assert(err, IsNil) |
956 | + |
957 | + // a existing file that needs update |
958 | + err = ioutil.WriteFile(filepath.Join(destDir, "existing-update"), []byte("old-content"), 0644) |
959 | + c.Assert(err, IsNil) |
960 | + err = ioutil.WriteFile(filepath.Join(srcDir, "existing-update"), []byte("some-new-content"), 0644) |
961 | + c.Assert(err, IsNil) |
962 | + |
963 | + // existing file that needs no update |
964 | + err = ioutil.WriteFile(filepath.Join(srcDir, "existing-unchanged"), []byte(nil), 0644) |
965 | + c.Assert(err, IsNil) |
966 | + err = exec.Command("cp", "-a", filepath.Join(srcDir, "existing-unchanged"), filepath.Join(destDir, "existing-unchanged")).Run() |
967 | + c.Assert(err, IsNil) |
968 | + |
969 | + // a file that needs removal |
970 | + err = ioutil.WriteFile(filepath.Join(destDir, "to-be-deleted"), []byte(nil), 0644) |
971 | + c.Assert(err, IsNil) |
972 | +} |
973 | + |
974 | +func compareDirs(c *C, srcDir, destDir string) { |
975 | + d1, err := exec.Command("ls", "-al", srcDir).CombinedOutput() |
976 | + c.Assert(err, IsNil) |
977 | + d2, err := exec.Command("ls", "-al", destDir).CombinedOutput() |
978 | + c.Assert(err, IsNil) |
979 | + c.Assert(string(d1), Equals, string(d2)) |
980 | + // ensure content got updated |
981 | + c1, err := exec.Command("sh", "-c", fmt.Sprintf("find %s -type f |xargs cat", srcDir)).CombinedOutput() |
982 | + c.Assert(err, IsNil) |
983 | + c2, err := exec.Command("sh", "-c", fmt.Sprintf("find %s -type f |xargs cat", destDir)).CombinedOutput() |
984 | + c.Assert(err, IsNil) |
985 | + c.Assert(string(c1), Equals, string(c2)) |
986 | +} |
987 | + |
988 | +func (ts *HTestSuite) TestSyncDirs(c *C) { |
989 | + |
990 | + for _, l := range [][2]string{ |
991 | + [2]string{"src-short", "dst-loooooooooooong"}, |
992 | + [2]string{"src-loooooooooooong", "dst-short"}, |
993 | + [2]string{"src-eq", "dst-eq"}, |
994 | + } { |
995 | + |
996 | + // ensure we have src, dest dirs with different length |
997 | + srcDir := filepath.Join(c.MkDir(), l[0]) |
998 | + err := os.MkdirAll(srcDir, 0755) |
999 | + c.Assert(err, IsNil) |
1000 | + destDir := filepath.Join(c.MkDir(), l[1]) |
1001 | + err = os.MkdirAll(destDir, 0755) |
1002 | + c.Assert(err, IsNil) |
1003 | + |
1004 | + // add a src subdir |
1005 | + subdir := filepath.Join(srcDir, "subdir") |
1006 | + err = os.Mkdir(subdir, 0755) |
1007 | + c.Assert(err, IsNil) |
1008 | + makeTestFiles(c, subdir, destDir) |
1009 | + |
1010 | + // add a dst subdir that needs to get deleted |
1011 | + subdir2 := filepath.Join(destDir, "to-be-deleted-subdir") |
1012 | + err = os.Mkdir(subdir2, 0755) |
1013 | + subdir3 := filepath.Join(subdir2, "to-be-deleted-sub-subdir") |
1014 | + err = os.Mkdir(subdir3, 0755) |
1015 | + |
1016 | + // and a toplevel |
1017 | + makeTestFiles(c, srcDir, destDir) |
1018 | + |
1019 | + // do it |
1020 | + err = RSyncWithDelete(srcDir, destDir) |
1021 | + c.Assert(err, IsNil) |
1022 | + |
1023 | + // ensure meta-data is identical |
1024 | + compareDirs(c, srcDir, destDir) |
1025 | + compareDirs(c, filepath.Join(srcDir, "subdir"), filepath.Join(destDir, "subdir")) |
1026 | + } |
1027 | +} |
1028 | + |
1029 | +func (ts *HTestSuite) TestSyncDirFails(c *C) { |
1030 | + srcDir := c.MkDir() |
1031 | + err := os.MkdirAll(srcDir, 0755) |
1032 | + c.Assert(err, IsNil) |
1033 | + |
1034 | + destDir := c.MkDir() |
1035 | + err = os.MkdirAll(destDir, 0755) |
1036 | + c.Assert(err, IsNil) |
1037 | + |
1038 | + err = ioutil.WriteFile(filepath.Join(destDir, "meep"), []byte(nil), 0644) |
1039 | + c.Assert(err, IsNil) |
1040 | + |
1041 | + // ensure remove fails |
1042 | + err = os.Chmod(destDir, 0100) |
1043 | + c.Assert(err, IsNil) |
1044 | + // make tempdir cleanup work again |
1045 | + defer os.Chmod(destDir, 0755) |
1046 | + |
1047 | + // do it |
1048 | + err = RSyncWithDelete(srcDir, destDir) |
1049 | + c.Check(err, NotNil) |
1050 | + c.Check(err, ErrorMatches, ".*permission denied.*") |
1051 | +} |
1052 | |
1053 | === modified file 'ubuntu-device-flash/snappy.go' |
1054 | --- ubuntu-device-flash/snappy.go 2016-04-05 09:39:49 +0000 |
1055 | +++ ubuntu-device-flash/snappy.go 2016-05-10 18:32:26 +0000 |
1056 | @@ -11,6 +11,22 @@ |
1057 | import ( |
1058 | "fmt" |
1059 | |
1060 | +<<<<<<< TREE |
1061 | +======= |
1062 | + "github.com/ubuntu-core/snappy/arch" |
1063 | + "github.com/ubuntu-core/snappy/dirs" |
1064 | + "github.com/ubuntu-core/snappy/osutil" |
1065 | + "github.com/ubuntu-core/snappy/partition" |
1066 | + "github.com/ubuntu-core/snappy/progress" |
1067 | + "github.com/ubuntu-core/snappy/provisioning" |
1068 | + "github.com/ubuntu-core/snappy/release" |
1069 | + "github.com/ubuntu-core/snappy/snap" |
1070 | + // needed so that we register the squashfs format |
1071 | + _ "github.com/ubuntu-core/snappy/snap/squashfs" |
1072 | + "github.com/ubuntu-core/snappy/snappy" |
1073 | + |
1074 | + "gopkg.in/yaml.v2" |
1075 | +>>>>>>> MERGE-SOURCE |
1076 | "launchpad.net/goget-ubuntu-touch/diskimage" |
1077 | "launchpad.net/goget-ubuntu-touch/ubuntuimage" |
1078 | ) |
1079 | @@ -58,11 +74,14 @@ |
1080 | } |
1081 | } |
1082 | |
1083 | +// Snapper holds common options applicable to snappy based images. |
1084 | type Snapper struct { |
1085 | Channel string `long:"channel" description:"Specify the channel to use" default:"stable"` |
1086 | Output string `long:"output" short:"o" description:"Name of the image file to create" required:"true"` |
1087 | - Oem string `long:"oem" description:"The snappy oem package to base the image out of" default:"generic-amd64"` |
1088 | + Gadget string `long:"gadget" description:"The snappy gadget package to base the image out of" default:"generic-amd64"` |
1089 | StoreID string `long:"store" description:"Set an alternate store id."` |
1090 | + OS string `long:"os" description:"path to the OS snap."` |
1091 | + Kernel string `long:"kernel" description:"path to the kernel snap."` |
1092 | |
1093 | Development struct { |
1094 | Install []string `long:"install" description:"Install additional packages (can be called multiple times)"` |
1095 | @@ -71,12 +90,19 @@ |
1096 | } `group:"Development"` |
1097 | |
1098 | Positional struct { |
1099 | - Release string `positional-arg-name:"release" description:"The release to base the image out of (15.04 or rolling)" required:"true"` |
1100 | + Release string `positional-arg-name:"release" description:"The release to base the image out of (16 or rolling)" required:"true"` |
1101 | } `positional-args:"yes" required:"yes"` |
1102 | |
1103 | +<<<<<<< TREE |
1104 | img diskimage.CoreImage |
1105 | hardware diskimage.HardwareDescription |
1106 | oem diskimage.OemDescription |
1107 | +======= |
1108 | + img diskimage.CoreImage |
1109 | + hardware diskimage.HardwareDescription |
1110 | + gadget diskimage.GadgetDescription |
1111 | + stagingRootPath string |
1112 | +>>>>>>> MERGE-SOURCE |
1113 | |
1114 | size int64 |
1115 | |
1116 | @@ -85,23 +111,765 @@ |
1117 | customizationFunc []func() error |
1118 | } |
1119 | |
1120 | +<<<<<<< TREE |
1121 | +======= |
1122 | +func (s Snapper) sanityCheck() error { |
1123 | + // we don't want to overwrite the output, people might get angry :-) |
1124 | + if osutil.FileExists(s.Output) { |
1125 | + return fmt.Errorf("Giving up, the desired target output file %#v already exists", s.Output) |
1126 | + } |
1127 | + |
1128 | + if s.size < s.flavor.minSize() { |
1129 | + return fmt.Errorf("minimum size for %s is %d", s.flavor, s.flavor.minSize()) |
1130 | + } |
1131 | + |
1132 | + if syscall.Getuid() != 0 { |
1133 | + return errors.New("command requires sudo/pkexec (root)") |
1134 | + } |
1135 | + if s.Positional.Release == "15.04" { |
1136 | + 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") |
1137 | + } |
1138 | + |
1139 | + // ensure we error when running on e.g. 14.04 with a sensible |
1140 | + // error message instead of super strange error later |
1141 | + if !osutil.FileExists("/bin/systemctl") { |
1142 | + return errors.New("need '/bin/systemctl to work") |
1143 | + } |
1144 | + |
1145 | + // only allow whitelisted gadget names for now |
1146 | + if os.Getenv("UBUNTU_DEVICE_FLASH_IGNORE_UNSTABLE_GADGET_DEFINITION") == "" { |
1147 | + contains := func(haystack []string, needle string) bool { |
1148 | + for _, elm := range haystack { |
1149 | + if elm == needle { |
1150 | + return true |
1151 | + } |
1152 | + } |
1153 | + return false |
1154 | + } |
1155 | + whitelist := []string{"canonical-i386", "canonical-pc", "canonical-pi2", "canonical-dragon", "beagleblack"} |
1156 | + if !contains(whitelist, s.Gadget) { |
1157 | + return fmt.Errorf("cannot use %q, must be one of: %q", s.Gadget, whitelist) |
1158 | + } |
1159 | + } |
1160 | + |
1161 | + return nil |
1162 | +} |
1163 | + |
1164 | +>>>>>>> MERGE-SOURCE |
1165 | func (s *Snapper) systemImage() (*ubuntuimage.Image, error) { |
1166 | +<<<<<<< TREE |
1167 | return nil, nil |
1168 | +======= |
1169 | + channels, err := ubuntuimage.NewChannels(globalArgs.Server) |
1170 | + if err != nil { |
1171 | + return nil, err |
1172 | + } |
1173 | + |
1174 | + channel := systemImageChannel(s.flavor.Channel(), s.Positional.Release, s.Channel) |
1175 | + // TODO: remove once azure channel is gone |
1176 | + if s.device == "" { |
1177 | + s.device = systemImageDeviceChannel(s.gadget.Architecture()) |
1178 | + } |
1179 | + |
1180 | + deviceChannel, err := channels.GetDeviceChannel(globalArgs.Server, channel, s.device) |
1181 | + if err != nil { |
1182 | + return nil, err |
1183 | + } |
1184 | + |
1185 | + systemImage, err := getImage(deviceChannel) |
1186 | + if err != nil { |
1187 | + return nil, err |
1188 | + } |
1189 | + |
1190 | + // avoid passing more args to setup() |
1191 | + globalArgs.Revision = systemImage.Version |
1192 | + |
1193 | + return &systemImage, nil |
1194 | +} |
1195 | + |
1196 | +func systemdEnable(serviceName string) error { |
1197 | + fmt.Printf("Enabling systemd unit %s\n", serviceName) |
1198 | + |
1199 | + servicesSystemdTarget := "multi-user.target" |
1200 | + snapServicesDir := "/etc/systemd/system" |
1201 | + |
1202 | + enableSymlink := filepath.Join(dirs.GlobalRootDir, snapServicesDir, servicesSystemdTarget+".wants", serviceName) |
1203 | + |
1204 | + serviceFilename := filepath.Join(dirs.GlobalRootDir, snapServicesDir, serviceName) |
1205 | + |
1206 | + return os.Symlink(serviceFilename[len(dirs.GlobalRootDir):], enableSymlink) |
1207 | + |
1208 | +} |
1209 | + |
1210 | +func (s *Snapper) installFlags() snappy.InstallFlags { |
1211 | + flags := snappy.InhibitHooks | snappy.AllowGadget |
1212 | + |
1213 | + if s.Development.DeveloperMode { |
1214 | + flags |= snappy.AllowUnauthenticated |
1215 | + } |
1216 | + |
1217 | + return flags |
1218 | +>>>>>>> MERGE-SOURCE |
1219 | } |
1220 | |
1221 | func (s *Snapper) install(systemPath string) error { |
1222 | - return nil |
1223 | +<<<<<<< TREE |
1224 | + return nil |
1225 | +======= |
1226 | + dirs.SetRootDir(systemPath) |
1227 | + defer dirs.SetRootDir("/") |
1228 | + |
1229 | + flags := s.installFlags() |
1230 | + gadgetSoftware := s.gadget.Gadget.Software |
1231 | + packageCount := len(s.Development.Install) + len(gadgetSoftware.BuiltIn) + len(gadgetSoftware.Preinstalled) + 3 |
1232 | + if s.Gadget != "" { |
1233 | + packageCount++ |
1234 | + } |
1235 | + |
1236 | + packageQueue := make([]string, 0, packageCount) |
1237 | + if s.Gadget != "" { |
1238 | + packageQueue = append(packageQueue, s.Gadget) |
1239 | + } |
1240 | + if s.OS != "" && s.Kernel != "" { |
1241 | + packageQueue = append(packageQueue, s.Kernel) |
1242 | + packageQueue = append(packageQueue, s.OS) |
1243 | + } |
1244 | + packageQueue = append(packageQueue, gadgetSoftware.BuiltIn...) |
1245 | + packageQueue = append(packageQueue, gadgetSoftware.Preinstalled...) |
1246 | + packageQueue = append(packageQueue, s.Development.Install...) |
1247 | + |
1248 | + for _, snap := range packageQueue { |
1249 | + fmt.Println("Installing", snap) |
1250 | + |
1251 | + pb := progress.NewTextProgress() |
1252 | + name := snap |
1253 | + if _, err := snappy.Install(name, s.Channel, flags, pb); err != nil { |
1254 | + return fmt.Errorf("failed to install %q from %q: %s", name, s.Channel, err) |
1255 | + } |
1256 | + } |
1257 | + |
1258 | + // set the bootvars for kernel/os snaps, the latest snappy is |
1259 | + // not activating the snaps on install anymore (with inhibit) |
1260 | + // so we need to work around that here (only on first boot) |
1261 | + // |
1262 | + // there is also no mounted os/kernel snap in the systemPath |
1263 | + // all we have here is the blobs |
1264 | + if s.OS != "" && s.Kernel != "" { |
1265 | + bootloader, err := partition.FindBootloader() |
1266 | + if err != nil { |
1267 | + return fmt.Errorf("can not set kernel/os bootvars: %s", err) |
1268 | + } |
1269 | + |
1270 | + snaps, _ := filepath.Glob(filepath.Join(dirs.SnapBlobDir, "*.snap")) |
1271 | + for _, fullname := range snaps { |
1272 | + bootvar := "" |
1273 | + bootvar2 := "" |
1274 | + |
1275 | + // detect type |
1276 | + snapFile, err := snap.Open(fullname) |
1277 | + if err != nil { |
1278 | + return fmt.Errorf("can not read %v", fullname) |
1279 | + } |
1280 | + info, err := snap.ReadInfoFromSnapFile(snapFile, nil) |
1281 | + if err != nil { |
1282 | + return fmt.Errorf("can not get info for %v", fullname) |
1283 | + } |
1284 | + switch info.Type { |
1285 | + case snap.TypeOS: |
1286 | + bootvar = "snappy_os" |
1287 | + bootvar2 = "snappy_good_os" |
1288 | + case snap.TypeKernel: |
1289 | + bootvar = "snappy_kernel" |
1290 | + bootvar2 = "snappy_good_kernel" |
1291 | + } |
1292 | + |
1293 | + name := filepath.Base(fullname) |
1294 | + for _, b := range []string{bootvar, bootvar2} { |
1295 | + if b != "" { |
1296 | + if err := bootloader.SetBootVar(b, name); err != nil { |
1297 | + return err |
1298 | + } |
1299 | + } |
1300 | + } |
1301 | + } |
1302 | + |
1303 | + // HORRIBLE, snappy.Install() will check if running |
1304 | + // on a grub system based on the gadget snap and if |
1305 | + // it is grub it will not extract the kernel/os |
1306 | + // |
1307 | + // HOWEVER this won't work in u-d-f because there |
1308 | + // is no current symlink so kernel.go always unpacks |
1309 | + // the kernel. undo this here |
1310 | + if s.gadget.Gadget.Hardware.Bootloader == "grub" { |
1311 | + dirs, _ := filepath.Glob(filepath.Join(s.img.Boot(), "/EFI/ubuntu/grub/*.snap")) |
1312 | + for _, d := range dirs { |
1313 | + fmt.Printf("Removing unneeded: %s\n", d) |
1314 | + if err := os.RemoveAll(d); err != nil { |
1315 | + return err |
1316 | + } |
1317 | + } |
1318 | + } |
1319 | + } |
1320 | + |
1321 | + return nil |
1322 | +} |
1323 | + |
1324 | +func (s *Snapper) extractGadget(gadgetPackage string) error { |
1325 | + if gadgetPackage == "" { |
1326 | + return nil |
1327 | + } |
1328 | + |
1329 | + tempDir, err := ioutil.TempDir("", "gadget") |
1330 | + if err != nil { |
1331 | + return err |
1332 | + } |
1333 | + |
1334 | + // we need to fix the permissions for tempdir to be seteuid friendly |
1335 | + if err := os.Chmod(tempDir, 0755); err != nil { |
1336 | + return err |
1337 | + } |
1338 | + |
1339 | + s.stagingRootPath = tempDir |
1340 | + os.MkdirAll(filepath.Join(tempDir, "/snap"), 0755) |
1341 | + |
1342 | + dirs.SetRootDir(tempDir) |
1343 | + defer dirs.SetRootDir("/") |
1344 | + release.Series = s.Positional.Release |
1345 | + |
1346 | + // we need to download and extract the squashfs snap |
1347 | + downloadedSnap := gadgetPackage |
1348 | + if !osutil.FileExists(gadgetPackage) { |
1349 | + repo := snappy.NewConfiguredUbuntuStoreSnapRepository() |
1350 | + snap, err := repo.Snap(gadgetPackage, s.Channel, nil) |
1351 | + if err != nil { |
1352 | + return fmt.Errorf("expected a gadget snaps: %s", err) |
1353 | + } |
1354 | + |
1355 | + pb := progress.NewTextProgress() |
1356 | + downloadedSnap, err = repo.Download(snap, pb, nil) |
1357 | + if err != nil { |
1358 | + return err |
1359 | + } |
1360 | + } |
1361 | + |
1362 | + // the fake snap needs to be in an expected location so that |
1363 | + // s.loadGadget() is happy |
1364 | + fakeGadgetDir := filepath.Join(tempDir, "/gadget/fake-gadget/1.0-fake/") |
1365 | + if err := os.MkdirAll(fakeGadgetDir, 0755); err != nil { |
1366 | + return err |
1367 | + } |
1368 | + cmd := exec.Command("unsquashfs", "-i", "-f", "-d", fakeGadgetDir, downloadedSnap) |
1369 | + if output, err := cmd.CombinedOutput(); err != nil { |
1370 | + return fmt.Errorf("snap unpack failed with: %v (%v)", err, string(output)) |
1371 | + } else { |
1372 | + println(string(output)) |
1373 | + } |
1374 | + |
1375 | + if err := s.loadGadget(tempDir); err != nil { |
1376 | + return err |
1377 | + } |
1378 | + |
1379 | + return nil |
1380 | +} |
1381 | + |
1382 | +func (s *Snapper) loadGadget(systemPath string) error { |
1383 | + pkgs, err := filepath.Glob(filepath.Join(systemPath, "/gadget/*/*/meta/snap.yaml")) |
1384 | + if err != nil { |
1385 | + return err |
1386 | + } |
1387 | + |
1388 | + // checking for len(pkgs) > 2 due to the 'current' symlink |
1389 | + if len(pkgs) == 0 { |
1390 | + return errors.New("no gadget package found") |
1391 | + } else if len(pkgs) > 2 || err != nil { |
1392 | + return errors.New("too many gadget packages installed") |
1393 | + } |
1394 | + |
1395 | + f, err := ioutil.ReadFile(pkgs[0]) |
1396 | + if err != nil { |
1397 | + return errors.New("failed to read gadget yaml") |
1398 | + } |
1399 | + |
1400 | + var gadget diskimage.GadgetDescription |
1401 | + if err := yaml.Unmarshal([]byte(f), &gadget); err != nil { |
1402 | + return errors.New("cannot decode gadget yaml") |
1403 | + } |
1404 | + s.gadget = gadget |
1405 | + s.gadget.SetRoot(systemPath) |
1406 | + |
1407 | + // ensure we can download and install snaps |
1408 | + arch.SetArchitecture(arch.ArchitectureType(s.gadget.Architecture())) |
1409 | + |
1410 | + return nil |
1411 | +} |
1412 | + |
1413 | +// Creates a YAML file inside the image that contains metadata relating |
1414 | +// to the installation. |
1415 | +func (s Snapper) writeInstallYaml(bootMountpoint string) error { |
1416 | + selfPath, err := exec.LookPath(os.Args[0]) |
1417 | + if err != nil { |
1418 | + return err |
1419 | + } |
1420 | + |
1421 | + bootDir := "" |
1422 | + |
1423 | + switch s.gadget.Gadget.Hardware.Bootloader { |
1424 | + // Running systems use a bindmount for /boot/grub, but |
1425 | + // since the system isn't booted, create the file in the |
1426 | + // real location. |
1427 | + case "grub": |
1428 | + bootDir = "/EFI/ubuntu/grub" |
1429 | + } |
1430 | + |
1431 | + installYamlFilePath := filepath.Join(bootMountpoint, bootDir, provisioning.InstallYamlFile) |
1432 | + |
1433 | + i := provisioning.InstallYaml{ |
1434 | + InstallMeta: provisioning.InstallMeta{ |
1435 | + Timestamp: time.Now(), |
1436 | + }, |
1437 | + InstallTool: provisioning.InstallTool{ |
1438 | + Name: filepath.Base(selfPath), |
1439 | + Path: selfPath, |
1440 | + // FIXME: we don't know our own version yet :) |
1441 | + // Version: "???", |
1442 | + }, |
1443 | + InstallOptions: provisioning.InstallOptions{ |
1444 | + Size: s.size, |
1445 | + SizeUnit: "GB", |
1446 | + Output: s.Output, |
1447 | + Channel: s.Channel, |
1448 | + DevicePart: s.Development.DevicePart, |
1449 | + Gadget: s.Gadget, |
1450 | + OS: s.OS, |
1451 | + Kernel: s.Kernel, |
1452 | + DeveloperMode: s.Development.DeveloperMode, |
1453 | + }, |
1454 | + } |
1455 | + |
1456 | + data, err := yaml.Marshal(&i) |
1457 | + if err != nil { |
1458 | + return err |
1459 | + } |
1460 | + |
1461 | + // the file isn't supposed to be modified, hence r/o. |
1462 | + return ioutil.WriteFile(installYamlFilePath, data, 0444) |
1463 | +} |
1464 | + |
1465 | +func extractHWDescription(path string) (hw diskimage.HardwareDescription, err error) { |
1466 | + // hack to circumvent https://code.google.com/p/go/issues/detail?id=1435 |
1467 | + if syscall.Getuid() == 0 { |
1468 | + runtime.GOMAXPROCS(1) |
1469 | + runtime.LockOSThread() |
1470 | + |
1471 | + if err := sysutils.DropPrivs(); err != nil { |
1472 | + return hw, err |
1473 | + } |
1474 | + } |
1475 | + |
1476 | + printOut("Searching for hardware.yaml in device part") |
1477 | + tmpdir, err := ioutil.TempDir("", "hardware") |
1478 | + if err != nil { |
1479 | + return hw, errors.New("cannot create tempdir to extract hardware.yaml from device part") |
1480 | + } |
1481 | + defer os.RemoveAll(tmpdir) |
1482 | + |
1483 | + if out, err := exec.Command("tar", "xf", path, "-C", tmpdir, "hardware.yaml").CombinedOutput(); err != nil { |
1484 | + return hw, fmt.Errorf("failed to extract a hardware.yaml from the device part: %s", out) |
1485 | + } |
1486 | + |
1487 | + data, err := ioutil.ReadFile(filepath.Join(tmpdir, "hardware.yaml")) |
1488 | + if err != nil { |
1489 | + return hw, err |
1490 | + } |
1491 | + |
1492 | + err = yaml.Unmarshal([]byte(data), &hw) |
1493 | + |
1494 | + return hw, err |
1495 | +} |
1496 | + |
1497 | +func (s *Snapper) bindMount(d string) (string, error) { |
1498 | + src := filepath.Join(s.img.Writable(), "system-data", d) |
1499 | + dst := filepath.Join(s.img.System(), d) |
1500 | + |
1501 | + if err := os.MkdirAll(src, 0755); err != nil { |
1502 | + return "", err |
1503 | + } |
1504 | + cmd := exec.Command("mount", "--bind", src, dst) |
1505 | + if o, err := cmd.CombinedOutput(); err != nil { |
1506 | + return "", fmt.Errorf("bind mount failed for %s to %s with: %s %v ", src, dst, err, string(o)) |
1507 | + } |
1508 | + return dst, nil |
1509 | +} |
1510 | + |
1511 | +func (s *Snapper) downloadOS(osPackage string) (string, error) { |
1512 | + if osPackage == "" { |
1513 | + return "", nil |
1514 | + } |
1515 | + // if its pointing to a local file, just return that |
1516 | + if _, err := os.Stat(osPackage); err == nil { |
1517 | + return osPackage, nil |
1518 | + } |
1519 | + release.Series = s.Positional.Release |
1520 | + |
1521 | + m := snappy.NewConfiguredUbuntuStoreSnapRepository() |
1522 | + snap, err := m.Snap(osPackage, s.Channel, nil) |
1523 | + if err != nil { |
1524 | + return "", fmt.Errorf("failed to find os snap: %s", err) |
1525 | + } |
1526 | + pb := progress.NewTextProgress() |
1527 | + path, err := m.Download(snap, pb, nil) |
1528 | + if err != nil { |
1529 | + return "", err |
1530 | + } |
1531 | + |
1532 | + return path, nil |
1533 | +} |
1534 | + |
1535 | +func (s *Snapper) setup(systemImageFiles []Files) error { |
1536 | + printOut("Mounting...") |
1537 | + if err := s.img.Mount(); err != nil { |
1538 | + return err |
1539 | + } |
1540 | + defer func() { |
1541 | + printOut("Unmounting...") |
1542 | + if err := s.img.Unmount(); err != nil { |
1543 | + fmt.Println("WARNING: unexpected issue:", err) |
1544 | + } |
1545 | + }() |
1546 | + |
1547 | + printOut("Provisioning...") |
1548 | + for i := range systemImageFiles { |
1549 | + if out, err := exec.Command("fakeroot", "tar", "--numeric-owner", "-axvf", systemImageFiles[i].FilePath, "-C", s.img.BaseMount()).CombinedOutput(); err != nil { |
1550 | + printOut(string(out)) |
1551 | + return fmt.Errorf("issues while extracting: %s", out) |
1552 | + } |
1553 | + } |
1554 | + |
1555 | + systemPath := s.img.System() |
1556 | + |
1557 | + // setup a fake system |
1558 | + if s.gadget.PartitionLayout() == "minimal" { |
1559 | + if err := os.MkdirAll(systemPath, 0755); err != nil { |
1560 | + return err |
1561 | + } |
1562 | + |
1563 | + // this is a bit terrible, we need to download the OS |
1564 | + // mount it, "sync dirs" (see below) and then we |
1565 | + // will need to download it again to install it properly |
1566 | + osSnap, err := s.downloadOS(s.OS) |
1567 | + if err != nil { |
1568 | + return err |
1569 | + } |
1570 | + |
1571 | + // mount os snap |
1572 | + cmd := exec.Command("mount", osSnap, systemPath) |
1573 | + if o, err := cmd.CombinedOutput(); err != nil { |
1574 | + return fmt.Errorf("os snap mount failed with: %s %v ", err, string(o)) |
1575 | + } |
1576 | + defer exec.Command("umount", systemPath).Run() |
1577 | + |
1578 | + // we need to do what "writable-paths" normally does on |
1579 | + // boot for etc/systemd/system, i.e. copy all the stuff |
1580 | + // from the os into the writable partition. normally |
1581 | + // this is the job of the initrd, however it won't touch |
1582 | + // the dir if there are files in there already. and a |
1583 | + // kernel/os install will create auto-mount units in there |
1584 | + src := filepath.Join(systemPath, "etc", "systemd", "system") |
1585 | + dst := filepath.Join(s.img.Writable(), "system-data", "etc", "systemd") |
1586 | + if err := os.MkdirAll(dst, 0755); err != nil { |
1587 | + return err |
1588 | + } |
1589 | + cmd = exec.Command("cp", "-a", src, dst) |
1590 | + if o, err := cmd.CombinedOutput(); err != nil { |
1591 | + return fmt.Errorf("copy failed: %s %s", err, o) |
1592 | + } |
1593 | + |
1594 | + // bind mount all relevant dirs |
1595 | + for _, d := range []string{"snap", "var/snap", "var/lib/snapd", "etc/systemd/system/", "tmp"} { |
1596 | + dst, err := s.bindMount(d) |
1597 | + if err != nil { |
1598 | + return err |
1599 | + } |
1600 | + defer exec.Command("umount", dst).Run() |
1601 | + } |
1602 | + |
1603 | + // bind mount /boot/efi |
1604 | + dst = filepath.Join(systemPath, "/boot/efi") |
1605 | + cmd = exec.Command("mount", "--bind", s.img.Boot(), dst) |
1606 | + if o, err := cmd.CombinedOutput(); err != nil { |
1607 | + return fmt.Errorf("boot bind mount failed with: %s %v ", err, string(o)) |
1608 | + } |
1609 | + defer exec.Command("umount", dst).Run() |
1610 | + switch s.gadget.Gadget.Hardware.Bootloader { |
1611 | + case "grub": |
1612 | + // grub needs this |
1613 | + grubUbuntu := filepath.Join(s.img.Boot(), "EFI/ubuntu/grub") |
1614 | + os.MkdirAll(grubUbuntu, 0755) |
1615 | + |
1616 | + // and /boot/grub |
1617 | + src = grubUbuntu |
1618 | + dst = filepath.Join(systemPath, "/boot/grub") |
1619 | + cmd = exec.Command("mount", "--bind", src, dst) |
1620 | + if o, err := cmd.CombinedOutput(); err != nil { |
1621 | + return fmt.Errorf("boot/ubuntu bind mount failed with: %s %v ", err, string(o)) |
1622 | + } |
1623 | + defer exec.Command("umount", dst).Run() |
1624 | + |
1625 | + // TERRIBLE but we need a /boot/grub/grub.cfg so that |
1626 | + // the kernel and os snap can be installed |
1627 | + glob, err := filepath.Glob(filepath.Join(s.stagingRootPath, "gadget", "*", "*", "grub.cfg")) |
1628 | + if err != nil { |
1629 | + return fmt.Errorf("grub.cfg glob failed: %s", err) |
1630 | + } |
1631 | + if len(glob) != 1 { |
1632 | + return fmt.Errorf("can not find a valid grub.cfg, found %v instead", len(glob)) |
1633 | + } |
1634 | + gadgetGrubCfg := glob[0] |
1635 | + cmd = exec.Command("cp", gadgetGrubCfg, grubUbuntu) |
1636 | + o, err := cmd.CombinedOutput() |
1637 | + if err != nil { |
1638 | + return fmt.Errorf("failed to copy %s %s", err, o) |
1639 | + } |
1640 | + case "u-boot": |
1641 | + src = s.img.Boot() |
1642 | + dst = filepath.Join(systemPath, "/boot/uboot") |
1643 | + cmd = exec.Command("mount", "--bind", src, dst) |
1644 | + if o, err := cmd.CombinedOutput(); err != nil { |
1645 | + return fmt.Errorf("boot/ubuntu bind mount failed with: %s %v ", err, string(o)) |
1646 | + } |
1647 | + defer exec.Command("umount", dst).Run() |
1648 | + } |
1649 | + } |
1650 | + |
1651 | + if err := s.img.SetupBoot(); err != nil { |
1652 | + return err |
1653 | + } |
1654 | + |
1655 | + if err := s.install(systemPath); err != nil { |
1656 | + return err |
1657 | + } |
1658 | + |
1659 | + for i := range s.customizationFunc { |
1660 | + if err := s.customizationFunc[i](); err != nil { |
1661 | + return err |
1662 | + } |
1663 | + } |
1664 | + |
1665 | + return s.writeInstallYaml(s.img.Boot()) |
1666 | +>>>>>>> MERGE-SOURCE |
1667 | } |
1668 | |
1669 | // deploy orchestrates the priviledged part of the setup |
1670 | +<<<<<<< TREE |
1671 | func (s *Snapper) deploy(systemImage *ubuntuimage.Image, filePathChan <-chan string) error { |
1672 | +======= |
1673 | +func (s *Snapper) deploy(systemImageFiles []Files) error { |
1674 | + // hack to circumvent https://code.google.com/p/go/issues/detail?id=1435 |
1675 | + runtime.GOMAXPROCS(1) |
1676 | + runtime.LockOSThread() |
1677 | + if err := sysutils.EscalatePrivs(); err != nil { |
1678 | + return err |
1679 | + } |
1680 | + defer sysutils.DropPrivs() |
1681 | + |
1682 | + printOut("Formatting...") |
1683 | + if err := s.img.Format(); err != nil { |
1684 | + return err |
1685 | + } |
1686 | + |
1687 | + if err := s.setup(systemImageFiles); err != nil { |
1688 | + return err |
1689 | + } |
1690 | + |
1691 | +>>>>>>> MERGE-SOURCE |
1692 | return nil |
1693 | } |
1694 | |
1695 | +<<<<<<< TREE |
1696 | func (s *Snapper) create() error { |
1697 | return fmt.Errorf(`Building core images is currently not supported. |
1698 | |
1699 | Images for ubuntu-core 15.04 can be build with the ppa:snappy-dev/tools. |
1700 | Building images for ubuntu-core 16.04 will be supported by this tool soon. |
1701 | `) |
1702 | +======= |
1703 | +func (s Snapper) printSummary() { |
1704 | + fmt.Println("New image complete") |
1705 | + fmt.Println("Summary:") |
1706 | + fmt.Println(" Output:", s.Output) |
1707 | + fmt.Println(" Architecture:", s.gadget.Architecture()) |
1708 | + fmt.Println(" Channel:", s.Channel) |
1709 | + fmt.Println(" Version:", globalArgs.Revision) |
1710 | +} |
1711 | + |
1712 | +func (s *Snapper) getSystemImage() ([]Files, error) { |
1713 | + var devicePart string |
1714 | + if s.Development.DevicePart != "" { |
1715 | + p, err := expandFile(s.Development.DevicePart) |
1716 | + if err != nil { |
1717 | + return nil, err |
1718 | + } |
1719 | + |
1720 | + fmt.Println("Using a custom OS or Kernel part will prevent updates for these components") |
1721 | + |
1722 | + devicePart = p |
1723 | + } |
1724 | + |
1725 | + fmt.Println("Fetching information from server...") |
1726 | + systemImage, err := s.systemImage() |
1727 | + if err != nil { |
1728 | + return nil, err |
1729 | + } |
1730 | + |
1731 | + filesChan := make(chan Files, len(systemImage.Files)) |
1732 | + sigFiles := ubuntuimage.GetGPGFiles() |
1733 | + |
1734 | + fmt.Println("Downloading and setting up...") |
1735 | + |
1736 | + go func() { |
1737 | + sigFilesChan := make(chan Files, len(sigFiles)) |
1738 | + defer close(sigFilesChan) |
1739 | + |
1740 | + for _, f := range sigFiles { |
1741 | + bitDownloader(f, sigFilesChan, globalArgs.Server, cacheDir) |
1742 | + } |
1743 | + }() |
1744 | + |
1745 | + filePaths := make([]Files, 0, len(systemImage.Files)) |
1746 | + hwChan := make(chan diskimage.HardwareDescription) |
1747 | + |
1748 | + go func() { |
1749 | + for i := 0; i < len(systemImage.Files); i++ { |
1750 | + f := <-filesChan |
1751 | + |
1752 | + if isDevicePart(f.FilePath) { |
1753 | + devicePart = f.FilePath |
1754 | + |
1755 | + if hardware, err := extractHWDescription(f.FilePath); err != nil { |
1756 | + fmt.Println("Failed to read harware.yaml from device part, provisioning may fail:", err) |
1757 | + } else { |
1758 | + hwChan <- hardware |
1759 | + } |
1760 | + } |
1761 | + |
1762 | + printOut("Download finished for", f.FilePath) |
1763 | + filePaths = append(filePaths, f) |
1764 | + } |
1765 | + close(hwChan) |
1766 | + close(filesChan) |
1767 | + }() |
1768 | + |
1769 | + for _, f := range systemImage.Files { |
1770 | + if devicePart != "" && isDevicePart(f.Path) { |
1771 | + printOut("Using a custom device tarball") |
1772 | + filesChan <- Files{FilePath: devicePart} |
1773 | + } else { |
1774 | + go bitDownloader(f, filesChan, globalArgs.Server, cacheDir) |
1775 | + } |
1776 | + } |
1777 | + |
1778 | + s.hardware = <-hwChan |
1779 | + |
1780 | + return filePaths, nil |
1781 | +} |
1782 | + |
1783 | +func (s *Snapper) create() (err error) { |
1784 | + if err := s.sanityCheck(); err != nil { |
1785 | + return err |
1786 | + } |
1787 | + |
1788 | + if s.StoreID != "" { |
1789 | + fmt.Println("Setting store id to", s.StoreID) |
1790 | + os.Setenv("UBUNTU_STORE_ID", s.StoreID) |
1791 | + } |
1792 | + |
1793 | + fmt.Println("Determining gadget configuration") |
1794 | + if err := s.extractGadget(s.Gadget); err != nil { |
1795 | + return err |
1796 | + } |
1797 | + defer os.RemoveAll(s.stagingRootPath) |
1798 | + |
1799 | + // hack to circumvent https://code.google.com/p/go/issues/detail?id=1435 |
1800 | + runtime.GOMAXPROCS(1) |
1801 | + runtime.LockOSThread() |
1802 | + if err := sysutils.DropPrivs(); err != nil { |
1803 | + return err |
1804 | + } |
1805 | + |
1806 | + systemImageFiles := []Files{} |
1807 | + switch s.gadget.Gadget.Hardware.PartitionLayout { |
1808 | + case "minimal": |
1809 | + if s.OS == "" && s.Kernel == "" { |
1810 | + return errors.New("kernel and os have to be specified to support partition-layout: minimal") |
1811 | + } |
1812 | + } |
1813 | + |
1814 | + switch s.gadget.Gadget.Hardware.Bootloader { |
1815 | + case "grub": |
1816 | + legacy := isLegacy(s.Positional.Release, s.Channel, globalArgs.Revision) |
1817 | + if legacy { |
1818 | + printOut("Using legacy setup") |
1819 | + } |
1820 | + |
1821 | + s.img = diskimage.NewCoreGrubImage(s.Output, s.size, s.flavor.rootSize(), s.hardware, s.gadget, legacy, "gpt") |
1822 | + case "u-boot": |
1823 | + label := "msdos" |
1824 | + if s.gadget.Architecture() == archArm64 { |
1825 | + label = "gpt" |
1826 | + } |
1827 | + s.img = diskimage.NewCoreUBootImage(s.Output, s.size, s.flavor.rootSize(), s.hardware, s.gadget, label) |
1828 | + default: |
1829 | + return errors.New("no hardware description in Gadget snap") |
1830 | + } |
1831 | + |
1832 | + printOut("Partitioning...") |
1833 | + if err := s.img.Partition(); err != nil { |
1834 | + return err |
1835 | + } |
1836 | + defer func() { |
1837 | + if err != nil { |
1838 | + os.Remove(s.Output) |
1839 | + } |
1840 | + }() |
1841 | + |
1842 | + // Handle SIGINT and SIGTERM. |
1843 | + go func() { |
1844 | + ch := make(chan os.Signal) |
1845 | + signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) |
1846 | + |
1847 | + for sig := range ch { |
1848 | + printOut("Received", sig, "... ignoring") |
1849 | + } |
1850 | + }() |
1851 | + |
1852 | + // Execute the following code with escalated privs and drop them when done |
1853 | + if err := s.deploy(systemImageFiles); err != nil { |
1854 | + return err |
1855 | + } |
1856 | + |
1857 | + if err := s.img.FlashExtra(); err != nil { |
1858 | + return err |
1859 | + } |
1860 | + |
1861 | + s.printSummary() |
1862 | + |
1863 | + return nil |
1864 | +} |
1865 | + |
1866 | +func isLegacy(release, channel string, revision int) bool { |
1867 | + if release != "15.04" { |
1868 | + return false |
1869 | + } |
1870 | + |
1871 | + switch channel { |
1872 | + case "edge": |
1873 | + return revision <= 149 |
1874 | + case "alpha": |
1875 | + return revision <= 9 |
1876 | + case "stable": |
1877 | + return revision <= 4 |
1878 | + } |
1879 | + |
1880 | + return false |
1881 | +>>>>>>> MERGE-SOURCE |
1882 | } |
Just added some comments so that we don't forget them :)