Skip to content

Commit da30d89

Browse files
committed
Tool to create image with a given kernel version.
Bug: b/446172232
1 parent 12587f8 commit da30d89

File tree

2 files changed

+190
-0
lines changed

2 files changed

+190
-0
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
// Copyright (C) 2025 The Android Open Source Project
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package main
16+
17+
import (
18+
"flag"
19+
"fmt"
20+
"log"
21+
22+
"github.com/google/android-cuttlefish/tools/baseimage/pkg/gce"
23+
"github.com/google/android-cuttlefish/tools/baseimage/pkg/gce/scripts"
24+
)
25+
26+
// Cuttlefish base images are based on debian images.
27+
const (
28+
debianSourceImageProject = "debian-cloud"
29+
debianSourceImage = "debian-12-bookworm-v20250610"
30+
)
31+
32+
const (
33+
outImageName = "cuttlefish-fixed-kernel-image"
34+
)
35+
36+
var (
37+
project = flag.String("project", "", "GCE project whose resources will be used for creating the image")
38+
zone = flag.String("zone", "us-central1-a", "GCE zone used for creating relevant resources")
39+
linuxImageDeb = flag.String("linux-image-deb", "", "linux-image-* package name. E.g. linux-image-6.1.0-40-cloud-amd64")
40+
)
41+
42+
func createImageMain(project, zone, linuxImageDeb string) error {
43+
h, err := gce.NewGceHelper(project, zone)
44+
if err != nil {
45+
return fmt.Errorf("failed to create GCE helper: %w", err)
46+
}
47+
insName := outImageName
48+
attachedDiskName := fmt.Sprintf("%s-attached-disk", insName)
49+
defer func() {
50+
// DetachDisk
51+
log.Printf("cleanup: detaching disk %q from instance %q...", attachedDiskName, insName)
52+
if err := h.DetachDisk(insName, attachedDiskName); err != nil {
53+
log.Printf("cleanup: error detaching disk: %v", err)
54+
} else {
55+
log.Println("cleanup: disk detached")
56+
}
57+
// Delete Instance
58+
log.Printf("cleanup: deleting instance %q...", insName)
59+
if err := h.DeleteInstance(insName); err != nil {
60+
log.Printf("cleanup: error deleting instance: %v", err)
61+
} else {
62+
log.Println("cleanup: instance deleted")
63+
}
64+
// Delete Disk
65+
log.Printf("cleanup: deleting disk %q...", attachedDiskName)
66+
if err := h.DeleteDisk(attachedDiskName); err != nil {
67+
log.Printf("cleanup: error deleting disk: %v", err)
68+
} else {
69+
log.Println("cleanup: disk deleted")
70+
}
71+
}()
72+
log.Println("creating disk...")
73+
disk, err := h.CreateDisk(debianSourceImageProject, debianSourceImage, attachedDiskName)
74+
if err != nil {
75+
return fmt.Errorf("failed to create disk: %w", err)
76+
}
77+
log.Printf("disk created: %q", attachedDiskName)
78+
log.Println("creating instance...")
79+
ins, err := h.CreateInstance(insName)
80+
if err != nil {
81+
return fmt.Errorf("failed to create instance: %w", err)
82+
}
83+
log.Printf("instance created: %q", insName)
84+
log.Println("attaching disk...")
85+
if err := h.AttachDisk(insName, attachedDiskName); err != nil {
86+
log.Fatalf("failed to attach disk %q to instance %q: %v", disk.Name, ins.Name, err)
87+
}
88+
log.Println("disk attached")
89+
90+
if err := gce.WaitForInstance(project, zone, insName); err != nil {
91+
return fmt.Errorf("waiting for instance error: %v", err)
92+
}
93+
// Upload Scripts
94+
list := []struct {
95+
dstname string
96+
content string
97+
}{
98+
{"mount_attached_disk.sh", scripts.MountAttachedDisk},
99+
{"install_kernel_main.sh", scripts.InstallKernelMain},
100+
}
101+
for _, s := range list {
102+
if err := gce.UploadBashScript(project, zone, insName, s.dstname, s.content); err != nil {
103+
return fmt.Errorf("error uploading script: %v", err)
104+
}
105+
}
106+
// Execute Scripts
107+
if err := gce.RunCmd(project, zone, insName, "./install_kernel_main.sh "+linuxImageDeb); err != nil {
108+
return err
109+
}
110+
log.Printf("stopping instance %q...", insName)
111+
if err := h.StopInstance(insName); err != nil {
112+
return fmt.Errorf("error stopping instance: %v", err)
113+
}
114+
if err := h.CreateImage(insName, attachedDiskName, outImageName); err != nil {
115+
return fmt.Errorf("failed to create image: %w", err)
116+
}
117+
return nil
118+
}
119+
120+
func main() {
121+
flag.Parse()
122+
123+
if *project == "" {
124+
log.Fatal("usage: `-project` must not be empty")
125+
}
126+
if *zone == "" {
127+
log.Fatal("usage: `-zone` must not be empty")
128+
}
129+
if *linuxImageDeb == "" {
130+
log.Fatal("usage: `-linux-image-deb` must not be empty")
131+
}
132+
133+
if err := createImageMain(*project, *zone, *linuxImageDeb); err != nil {
134+
log.Fatal(err)
135+
}
136+
log.Printf("image %q was created successfully", outImageName)
137+
fmt.Printf(`Copy the image somewhere else:
138+
gcloud compute images create \
139+
--source-image-project=%s \
140+
--source-image=%s \
141+
--project=[DEST_PROJECT] \
142+
--family=[DEST_IMAGE_FAMILY] [DEST_IMAGE_NAME]
143+
`,
144+
*project,
145+
outImageName,
146+
)
147+
}

tools/baseimage/pkg/gce/scripts/scripts.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,46 @@ fi
214214
# Sometimes systemd starts, making it hard to unmount
215215
# In any case we'll unmount cleanly when the instance shuts down
216216
`
217+
218+
const InstallKernelMain = `#!/usr/bin/env bash
219+
set -o errexit -o nounset -o pipefail
220+
221+
if [[ $# -eq 0 ]] ; then
222+
echo "usage: $0 <linux-image-deb>"
223+
exit 1
224+
fi
225+
226+
linux_image_deb=$1
227+
228+
sudo apt-get update
229+
sudo apt-get upgrade -y
230+
231+
./mount_attached_disk.sh
232+
233+
version=$(sudo chroot /mnt/image/ /usr/bin/dpkg -s linux-image-cloud-amd64 | grep ^Depends: | \
234+
cut -d: -f2 | cut -d" " -f2 )
235+
echo "START VERSION: ${version}"
236+
237+
sudo chroot /mnt/image /usr/bin/apt-get update
238+
sudo chroot /mnt/image /usr/bin/apt-get upgrade -y
239+
240+
version=$(sudo chroot /mnt/image/ /usr/bin/dpkg -s linux-image-cloud-amd64 | grep ^Depends: | \
241+
cut -d: -f2 | cut -d" " -f2 )
242+
echo "AFTER UPGRADE VERSION: ${version}"
243+
244+
sudo chroot /mnt/image /usr/bin/apt-get install -y ${linux_image_deb}
245+
246+
version=$(sudo chroot /mnt/image/ /usr/bin/dpkg -s linux-image-cloud-amd64 | grep ^Depends: | \
247+
cut -d: -f2 | cut -d" " -f2)
248+
echo "END VERSION: ${version}"
249+
250+
if [ "${version}" != "${linux_image_deb}" ]; then
251+
echo "CREATE IMAGE FAILED!!!"
252+
echo "Expected ${linux_image_deb}, got: ${version}"
253+
exit 1
254+
fi
255+
256+
# Skip unmounting:
257+
# Sometimes systemd starts, making it hard to unmount
258+
# In any case we'll unmount cleanly when the instance shuts down
259+
`

0 commit comments

Comments
 (0)