Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bib: switch to use bootc install to-filesystem (HMS-3453) #304

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
102 changes: 14 additions & 88 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,32 +23,35 @@ $ podman machine start

## 🚀 Examples

The following example builds a [Fedora ELN](https://docs.fedoraproject.org/en-US/eln/) bootable container into a QCOW2 image for the architecture you're running
the command on.

The `fedora-bootc:eln` base image does not include a default user. This example injects a [user configuration file](#-build-config)
by adding a volume-mount for the local file as well as the `--config` flag to the bootc-image-builder container.

The following command will create a QCOW2 disk image. First, create `./config.json` as described above to configure user access.
The following example builds a derrived [Fedora ELN](https://docs.fedoraproject.org/en-US/eln/) bootable container into a QCOW2 image for the architecture you're running the command on.

```bash
cat > Containerfile <<'EOF'
FROM quay.io/centos-bootc/fedora-bootc:eln
RUN useradd alice --password "$(openssl passwd -6 "bob")" --groups wheel
EOF
sudo podman build -f Containerfile -t my-bootc
sudo podman run \
--rm \
-it \
--privileged \
--pull=newer \
--security-opt label=type:unconfined_t \
-v $(pwd)/config.json:/config.json \
-v $(pwd)/output:/output \
-v /var/lib/containers/storage:/var/lib/containers/storage \
quay.io/centos-bootc/bootc-image-builder:latest \
--type qcow2 \
--config /config.json \
quay.io/centos-bootc/fedora-bootc:eln
--local \
localhost/my-bootc
```

There is more in-depth discussion how to customize the image and the users in the [bootc documentation](https://bootc-org.gitlab.io/documentation/guide/building.html#logins-and-users).

### Using local containers

To use containers from local container's storage rather than a registry, we need to ensure two things:
By default (without the `--local` flag) bootc-image-builder will use
the podman registry. To use containers from local container's storage,
we need to ensure two things:
- the container exists in local storage
- mount the local container storage

Expand All @@ -61,12 +64,10 @@ sudo podman run \
--privileged \
--pull=newer \
--security-opt label=type:unconfined_t \
-v $(pwd)/config.json:/config.json \
-v $(pwd)/output:/output \
-v /var/lib/containers/storage:/var/lib/containers/storage \
quay.io/centos-bootc/bootc-image-builder:latest \
--type qcow2 \
--config /config.json \
--local \
localhost/bootc:eln
```
Expand Down Expand Up @@ -134,7 +135,6 @@ Usage:

Flags:
--chown string chown the ouput directory to match the specified UID:GID
--config string build config file
--tls-verify require HTTPS and verify certificates when contacting registries (default true)
--type string image type to build [qcow2, ami] (default "qcow2")
```
Expand All @@ -144,7 +144,6 @@ Flags:
| Argument | Description | Default Value |
|------------------|------------------------------------------------------------------|:-------------:|
| **--chown** | chown the ouput directory to match the specified UID:GID | ❌ |
| **--config** | Path to a [build config](#-build-config) | ❌ |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing is this is used elsewhere so I think we may be partially committed short term.

(There is some things related to this, like the fact I don't quite understand why it's JSON and not TOML as seems would be expected for blueprints, but that's an aside)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite understand why it's JSON and not TOML

No real (good) reason other than the initial version BIB was heavily based on https://github.com/osbuild/images/blob/main/cmd/build/main.go, which is used for test image builds and it was simpler to use the json formatting for config files.

Since we're moving towards making it more explicit that this config is an instance of an Image Builder blueprint, I agree that we should make it a toml to make the connection clearer.

| **--tls-verify** | Require HTTPS and verify certificates when contacting registries | `true` |
| **--type** | [Image type](#-image-types) to build | `qcow2` |

Expand Down Expand Up @@ -262,79 +261,6 @@ The following volumes can be mounted inside the container:
| `/store` | Used for the [osbuild store](https://www.osbuild.org/) | No |
| `/rpmmd` | Used for the DNF cache | No |

## 📝 Build config

A build config is a JSON file with customizations for the resulting image. A path to the file is passed via the `--config` argument. The customizations are specified under a `blueprint.customizations` object.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be helpful to have a realtime sync on this? I sent an invite.


As an example, let's show how you can add a user to the image:

Firstly create a file `./config.json` and put the following content into it:

```json
{
"blueprint": {
"customizations": {
"user": [
{
"name": "alice",
"password": "bob",
"key": "ssh-rsa AAA ... user@email.com",
"groups": [
"wheel"
]
}
]
}
}
}
```

Then, run `bootc-image-builder` with the following arguments:

```bash
sudo podman run \
--rm \
-it \
--privileged \
--pull=newer \
--security-opt label=type:unconfined_t \
-v $(pwd)/config.json:/config.json \
-v $(pwd)/output:/output \
quay.io/centos-bootc/bootc-image-builder:latest \
--type qcow2 \
--config /config.json \
quay.io/centos-bootc/fedora-bootc:eln
```

### Users (`user`, array)

Possible fields:

| Field | Use | Required |
|------------|--------------------------------------------|:--------:|
| `name` | Name of the user | ✅ |
| `password` | Unencrypted password | No |
| `key` | Public SSH key contents | No |
| `groups` | An array of secondary to put the user into | No |

Example:

```json
{
"user": [
{
"name": "alice",
"password": "bob",
"key": "ssh-rsa AAA ... user@email.com",
"groups": [
"wheel",
"admins"
]
}
]
}
```

## Building

To build the container locally you can run
Expand Down
6 changes: 0 additions & 6 deletions bib/cmd/bootc-image-builder/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@ func manifestForDiskImage(c *ManifestConfig, rng *rand.Rand) (*manifest.Manifest

img := image.NewBootcDiskImage(containerSource)
img.Users = users.UsersFromBP(customizations.GetUsers())
img.Groups = users.GroupsFromBP(customizations.GetGroups())

img.KernelOptionsAppend = []string{
"rw",
// TODO: Drop this as we expect kargs to come from the container image,
Expand All @@ -88,8 +86,6 @@ func manifestForDiskImage(c *ManifestConfig, rng *rand.Rand) (*manifest.Manifest
"console=ttyS0",
}

img.SysrootReadOnly = true

switch c.Architecture {
case arch.ARCH_X86_64:
img.Platform = &platform.X86{
Expand Down Expand Up @@ -265,8 +261,6 @@ func manifestForISO(c *ManifestConfig, rng *rand.Rand) (*manifest.Manifest, erro
},
}

img.ISOLabelTmpl = "Container-Installer-%s"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want something like img.ISOLabel = fmt.Sprintf("Container-Installer-%s", c.Architecture.String()) here instead. I'm not deeply familiar with ISOs, but I think we should keep the label.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! I fixed this in #342


var customizations *blueprint.Customizations
if c.Config != nil && c.Config.Blueprint != nil {
customizations = c.Config.Blueprint.Customizations
Expand Down
42 changes: 31 additions & 11 deletions bib/cmd/bootc-image-builder/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import (
"strconv"
"strings"

"github.com/osbuild/bootc-image-builder/bib/internal/setup"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"golang.org/x/exp/slices"

"github.com/osbuild/images/pkg/arch"
"github.com/osbuild/images/pkg/blueprint"
"github.com/osbuild/images/pkg/cloud/awscloud"
Expand All @@ -20,9 +23,8 @@ import (
"github.com/osbuild/images/pkg/manifest"
"github.com/osbuild/images/pkg/osbuild"
"github.com/osbuild/images/pkg/rpmmd"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"golang.org/x/exp/slices"

"github.com/osbuild/bootc-image-builder/bib/internal/setup"
)

//go:embed fedora-eln.json
Expand Down Expand Up @@ -233,12 +235,13 @@ func manifestFromCobra(cmd *cobra.Command, args []string) ([]byte, error) {
}

imgref := args[0]
configFile, _ := cmd.Flags().GetString("config")
isoConfigFile, _ := cmd.Flags().GetString("iso-config")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought the config actually didn't apply to the ISO at all? Or am I missing something?

imgTypes, _ := cmd.Flags().GetStringArray("type")
rpmCacheRoot, _ := cmd.Flags().GetString("rpmmd")
targetArch, _ := cmd.Flags().GetString("target-arch")
tlsVerify, _ := cmd.Flags().GetBool("tls-verify")
localStorage, _ := cmd.Flags().GetBool("local")
rootSSHKey, _ := cmd.Flags().GetString("experimental-root-ssh-authorized-key")

if targetArch != "" {
// TODO: detect if binfmt_misc for target arch is
Expand All @@ -259,16 +262,31 @@ func manifestFromCobra(cmd *cobra.Command, args []string) ([]byte, error) {
return nil, err
}

// bootc does not yet support arbitray blueprint customizations
if buildType != BuildTypeISO && isoConfigFile != "" {
return nil, fmt.Errorf("the --iso-config switch is only supported for ISO images")
}

var config *BuildConfig
if configFile != "" {
config, err = loadConfig(configFile)
if isoConfigFile != "" {
config, err = loadConfig(isoConfigFile)
if err != nil {
return nil, err
return nil, fmt.Errorf("cannot load config: %w", err)
}
} else {
config = &BuildConfig{}
}

if rootSSHKey != "" {
config.Blueprint = &blueprint.Blueprint{
Customizations: &blueprint.Customizations{
User: []blueprint.UserCustomization{
{Name: "root", Key: &rootSSHKey},
},
},
}
}

manifestConfig := &ManifestConfig{
Architecture: buildArch,
Config: config,
Expand Down Expand Up @@ -338,7 +356,7 @@ func cmdBuild(cmd *cobra.Command, args []string) error {
fmt.Printf("Generating manifest %s\n", manifest_fname)
mf, err := manifestFromCobra(cmd, args)
if err != nil {
panic(err)
return err
}
fmt.Print("DONE\n")

Expand Down Expand Up @@ -455,11 +473,13 @@ func run() error {
}
rootCmd.AddCommand(manifestCmd)
manifestCmd.Flags().Bool("tls-verify", true, "require HTTPS and verify certificates when contacting registries")
manifestCmd.Flags().String("config", "", "build config file")
manifestCmd.Flags().String("iso-config", "", "build config file for the iso")
manifestCmd.Flags().String("rpmmd", "/rpmmd", "rpm metadata cache directory")
manifestCmd.Flags().String("target-arch", "", "build for the given target architecture (experimental)")
manifestCmd.Flags().StringArray("type", []string{"qcow2"}, fmt.Sprintf("image types to build [%s]", allImageTypesString()))
manifestCmd.Flags().Bool("local", false, "use a local container rather than a container from a registry")
// XXX: hide from help?
manifestCmd.Flags().String("experimental-root-ssh-authorized-key", "", "authorized ssh key for root as string")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not opposed to this, but the more I've been thinking about this (you probably saw) the more I feel we need to build up the story of using blueprints/kickstarts in container builds and align that with disk image generation and not emphasize one-offs like this.

And yes I know I added it to bootc install, but the use case is different there in a way because the bootc install is really low level and I was thinking of it more like a generic low level escape hatch.

But here we can be more opinionated I think.


logrus.SetLevel(logrus.ErrorLevel)
buildCmd.Flags().AddFlagSet(manifestCmd.Flags())
Expand All @@ -477,7 +497,7 @@ func run() error {
return err
}
}
if err := buildCmd.MarkFlagFilename("config"); err != nil {
if err := buildCmd.MarkFlagFilename("iso-config"); err != nil {
return err
}
buildCmd.MarkFlagsRequiredTogether("aws-region", "aws-bucket", "aws-ami-name")
Expand Down