Skip to content

Commit a4f32b1

Browse files
committed
Add new build-cimage command
This is a big step towards #2685 I know there's a lot going on with the pipeline, and I don't want to conflict with all that work - but at the same time, in my opinion we are just too dependent on complex Jenkins flows and our bespoke "meta.json in S3". The core of CoreOS *is a container image* now. This new command adds an opinionated flow where one can do: ``` $ cosa init $ cosa build-cimage quay.io/cgwalters/ostest ``` And *that's it* - we do proper change detection, reading and writing from the remote container image. We don't do silly things like storing an `.ociarchive` in S3 when we have native registries available. Later, we can build on this and rework our disk images to derive from that container image, as #2685 calls for. Also in the near term future, I think we can rework `cmd-build` such that it reuses this flow, but outputs to an `.ociarchive` instead. However, this code is going to need a bit more work to run in supermin.
1 parent 7a7190a commit a4f32b1

File tree

5 files changed

+192
-0
lines changed

5 files changed

+192
-0
lines changed

cmd/buildcimage.go

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// See usage below
2+
package main
3+
4+
// build-cimage is a wrapper for `rpm-ostree compose image`; for more
5+
// on that, see https://coreos.github.io/rpm-ostree/container/
6+
//
7+
// A key motivation here is to sever the dependency on S3 (and meta.json) for
8+
// our container image builds. As part of the ostree native container work,
9+
// the core of CoreOS becomes a container image. Our disk images
10+
// have always been derivatives of the container, and this is pushing things
11+
// farther in that direction.
12+
// See https://github.com/coreos/coreos-assembler/issues/2685
13+
//
14+
// This command is opinionated on reading and writing to a remote registry,
15+
// whereas the underlying `rpm-ostree compose image` defaults to
16+
// an ociarchive.
17+
18+
import (
19+
"fmt"
20+
"os"
21+
22+
"github.com/coreos/coreos-assembler/internal/pkg/cmdrun"
23+
"github.com/coreos/coreos-assembler/internal/pkg/cosash"
24+
"github.com/spf13/cobra"
25+
)
26+
27+
type BuildCImageOptions struct {
28+
authfile string
29+
initialize bool
30+
}
31+
32+
var (
33+
BuildCImageOpts BuildCImageOptions
34+
35+
cmdBuildCImage = &cobra.Command{
36+
Use: "build-cimage",
37+
Short: "cosa build-cimage [repository]",
38+
Args: cobra.ExactArgs(1),
39+
Long: "Initialize directory for ostree container image build",
40+
RunE: implRunBuildCImage,
41+
}
42+
)
43+
44+
func init() {
45+
cmdBuildCImage.Flags().BoolVarP(
46+
&BuildCImageOpts.initialize, "initialize", "i", false,
47+
"Assume target image does not exist")
48+
cmdBuildCImage.Flags().StringVar(
49+
&BuildCImageOpts.authfile, "authfile", "",
50+
"Path to container authentication file")
51+
}
52+
53+
func runBuildCImage(argv []string) error {
54+
cmdBuildCImage.SetArgs(argv)
55+
return cmdBuildCImage.Execute()
56+
}
57+
58+
// This is a Go reipmlementation of pick_yaml_or_else_json() from cmdlib.sh
59+
func pickConfigFileYamlOrJson(name string, preferJson bool) (string, error) {
60+
jsonPath := fmt.Sprintf("src/config/%s.json", name)
61+
yamlPath := fmt.Sprintf("src/config/%s.yaml", name)
62+
if _, err := os.Stat(jsonPath); err != nil {
63+
if !os.IsNotExist(err) {
64+
return "", err
65+
}
66+
jsonPath = ""
67+
}
68+
if _, err := os.Stat(yamlPath); err != nil {
69+
if !os.IsNotExist(err) {
70+
return "", err
71+
}
72+
yamlPath = ""
73+
}
74+
if jsonPath != "" && yamlPath != "" {
75+
return "", fmt.Errorf("found both %s and %s", jsonPath, yamlPath)
76+
}
77+
if jsonPath != "" {
78+
return jsonPath, nil
79+
}
80+
return yamlPath, nil
81+
}
82+
83+
func implRunBuildCImage(c *cobra.Command, args []string) error {
84+
if err := cmdrun.RunCmdSyncV("cosa", "build", "--prepare-only"); err != nil {
85+
return err
86+
}
87+
88+
csh, err := cosash.NewCosaSh()
89+
if err != nil {
90+
return err
91+
}
92+
93+
basearch, err := csh.BaseArch()
94+
if err != nil {
95+
return err
96+
}
97+
repository := args[0]
98+
99+
buildArgs := []string{"compose", "image", "--format", "registry", "--layer-repo", "tmp/repo"}
100+
if BuildCImageOpts.initialize {
101+
buildArgs = append(buildArgs, "--initialize")
102+
}
103+
if BuildCImageOpts.authfile != "" {
104+
buildArgs = append(buildArgs, "--authfile", BuildCImageOpts.authfile)
105+
}
106+
if _, err := os.Stat("tmp/cosa-transient"); err != nil {
107+
if !os.IsNotExist(err) {
108+
return err
109+
}
110+
cachedir := "cache/buildcimage-cache"
111+
if err := os.MkdirAll(cachedir, 0o755); err != nil {
112+
return err
113+
}
114+
buildArgs = append(buildArgs, "--cachedir", cachedir)
115+
}
116+
manifestLock, err := pickConfigFileYamlOrJson(fmt.Sprintf("manifest-lock.%s", basearch), true)
117+
if err != nil {
118+
return err
119+
}
120+
manifestLockOverrides, err := pickConfigFileYamlOrJson("manifest-lock.overrides", false)
121+
if err != nil {
122+
return err
123+
}
124+
manifestLockArchOverrides, err := pickConfigFileYamlOrJson(fmt.Sprintf("manifest-lock.overrides.%s", basearch), false)
125+
if err != nil {
126+
return err
127+
}
128+
for _, lock := range []string{manifestLock, manifestLockOverrides, manifestLockArchOverrides} {
129+
if lock != "" {
130+
buildArgs = append(buildArgs, "--lockfile", lock)
131+
}
132+
}
133+
buildArgs = append(buildArgs, "src/config/manifest.yaml")
134+
buildArgs = append(buildArgs, repository)
135+
136+
argv0 := "rpm-ostree"
137+
priv, err := csh.HasPrivileges()
138+
if err != nil {
139+
return err
140+
}
141+
if priv {
142+
argv0 = "sudo"
143+
buildArgs = append([]string{"rpm-ostree"}, buildArgs...)
144+
} else {
145+
return fmt.Errorf("this command currently requires the ability to create nested containers")
146+
}
147+
148+
if err := cmdrun.RunCmdSyncV(argv0, buildArgs...); err != nil {
149+
return err
150+
}
151+
152+
return nil
153+
}

cmd/coreos-assembler.go

+2
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ func run(argv []string) error {
8888
switch cmd {
8989
case "clean":
9090
return runClean(argv)
91+
case "build-cimage":
92+
return runBuildCImage(argv)
9193
case "update-variant":
9294
return runUpdateVariant(argv)
9395
case "remote-session":

internal/pkg/cmdrun/cmdrun.go

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package cmdrun
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
"strings"
8+
"syscall"
9+
)
10+
11+
// Synchronously invoke a command, logging the command arguments
12+
// to stdout.
13+
func RunCmdSyncV(cmdName string, args ...string) error {
14+
fmt.Printf("Running: %s %s\n", cmdName, strings.Join(args, " "))
15+
return RunCmdSync(cmdName, args...)
16+
}
17+
18+
// Synchronously invoke a command, passing both stdout and stderr.
19+
func RunCmdSync(cmdName string, args ...string) error {
20+
cmd := exec.Command(cmdName, args...)
21+
cmd.SysProcAttr = &syscall.SysProcAttr{
22+
Pdeathsig: syscall.SIGTERM,
23+
}
24+
cmd.Stdout = os.Stdout
25+
cmd.Stderr = os.Stderr
26+
if err := cmd.Run(); err != nil {
27+
return fmt.Errorf("error running %s %s: %w", cmdName, strings.Join(args, " "), err)
28+
}
29+
30+
return nil
31+
}

internal/pkg/cosash/cosash.go

+5
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ pwd >&3
170170
`)
171171
}
172172

173+
// BaseArch returns the base architecture
174+
func (sh *CosaSh) BaseArch() (string, error) {
175+
return sh.ProcessWithReply(`echo $basearch >&3`)
176+
}
177+
173178
// HasPrivileges checks if we can use sudo
174179
func (sh *CosaSh) HasPrivileges() (bool, error) {
175180
r, err := sh.ProcessWithReply(`

src/cmd-fetch

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ fi
8585

8686
prepare_build
8787

88+
# NOTE: Keep this logic in sync with buildcimage.go
8889
args=
8990
if [ -n "${UPDATE_LOCKFILE}" ]; then
9091
# Put this under tmprepo so it gets automatically chown'ed if needed

0 commit comments

Comments
 (0)