blue-build / cli

BlueBuild's command line program that builds custom Fedora Atomic images based on your recipe.yml
https://blue-build.org/
Apache License 2.0
66 stars 6 forks source link

arriving at a stable, functional, and sensical recipe standard #180

Open xynydev opened 3 months ago

xynydev commented 3 months ago

(this issue is filed under CLI, because this is the only place changes would have to be made to implement changes to the recipe format)

I had an epiphany. But let me preface a bit; if the purpose of the recipe is to be a versatile standard that describes the build and publish process of a native container image, is it as good as that as can be? And if not, aren't we in the broad ideal moment of time to evolve it?

Here's how it could look:

base-image:
    url: ghcr.io/ublue-os/silverblue-main
    tag: 40
    public-key: https://... # unimplemented, needed for base image verification, would be optional, and automatically supplied for ublue etc.
    type: ostree # unimplemented, might be needed for effectively supporting different base images, which might need things like running `ostree container commit` 
result-image:
    name: weird-os
    description: This is my personal OS image.
    tags: # replaces the "latest" and timestamp tags
       - gts
stages:
    ...
modules:
    ...

That's a sketch, but IMO it's already better than what we have now. Ideas and discussion appreciated here, though.

Multiple versions of the recipe standard could be supported in a way similar to other projects, say, docker-compose.yml do. I'd opt for using the $schema: key to specify the version; that would force us to supply jsonschema for all supported versions, and the user to update both the autocomplete source and the actual version in their recipes at the same time. This would set a solid foundation for breaking recipe changes in the future. If rust can do jsonchema validation, maybe we could even get multi-version support in CLI with not much extra effort? #173

@blue-build/members

fiftydinar commented 3 months ago

Generally looks good.

    tags: # replaces the "latest" and timestamp tags
       - gts

I'm interested in this part.

It would be possible to provide multiple tags, like gts & latest, right?

What about other images, like VanillaOS, which have different release schedules? I guess that only latest would be supplied short-term, while gts is more of a Fedora/Universal Blue thing. Maybe the equivalent for it would be lts, but not sure. Just thinking around.

xynydev commented 3 months ago

Yes, that's an already implemented feature, and can be used to set any set of custom tags on any image. latest is added automatically if tags isn't set, but after that, it's up to the user.

gmpinder commented 3 months ago

I think this structure makes way more sense TBH. I do think that result-image: could be something else like final-image or main-image. The public key property is a good addition too. I was envisioning adding our own labels onto images that would be a link to the public key or something like that. This way we would be able to pull that information dynamically on build. But this property would be perfect for images that aren't built by bluebuild.

gmpinder commented 3 months ago

Something else that could probably be added later for the user's image is labels: which would just be a map

gmpinder commented 3 months ago

As for the "schema" for the recipe, the struct definitions and the use of serde already act like a schema enforcer. However, I think using schema's for the configuration of modules would be a really good idea to be able to validate the input before building.

gmpinder commented 3 months ago

So there is a jsonschema crate that we can use that is being actively maintained it would seem. Also we could include a tool to convert user's recipes using struct-convert. I used this at work for helping to create a helm values migration tool for deploying our services.

gmpinder commented 3 months ago
base-image:
    url: ghcr.io/ublue-os/silverblue-main

Just realized. I don't think this should be url:. Image refs are their own separate spec from URL. Maybe name, from, image-name, ref. I just think URL is misleading a bit.

xynydev commented 3 months ago
base-image:
    url: ghcr.io/ublue-os/silverblue-main

Just realized. I don't think this should be url:. Image refs are their own separate spec from URL. Maybe name, from, image-name, ref. I just think URL is misleading a bit.

Yeah I was a bit iffy on that too, but don't know what would be better. from isn't that clear either IMO. ref could be? Will have to see how it's referred to in the spec.

xynydev commented 3 months ago

I do think that result-image: could be something else like final-image or main-image.

Maybe final-image then, definetly not main-image, that's an overloaded term. metadata would be another possible key name for it.

gmpinder commented 3 months ago

Maybe final-image then, definetly not main-image, that's an overloaded term. metadata would be another possible key name for it.

I like metadata. Feels k8s-ish

fiftydinar commented 3 months ago

final-image is better imo, metadata can be referred to almost anything that contains information.

xynydev commented 3 months ago

To spin that argument around: basically everything in the recipe concerns 'the final image', while metadata would quite literally be the final images metadata, as in it's name, description, labels, etc.

xynydev commented 3 months ago

That is a bit "waffly" IMO yeah 😅

gmpinder commented 3 months ago

So it's sounding like it's going to be something like this?

base-image:
    ref: ghcr.io/ublue-os/silverblue-main:40
    public-key: https://... # unimplemented, needed for base image verification, would be optional, and automatically supplied for ublue etc.
    type: ostree # unimplemented, might be needed for effectively supporting different base images, which might need things like running `ostree container commit` 
final-image:
    name: weird-os
    description: This is my personal OS image.
    tags: # replaces the "latest" and timestamp tags
       - gts
stages:
    ...
modules:
    ...

I think combining the tag in with ref makes things a little clearer especially since we aren't building FROM multiple tagged images

xynydev commented 3 months ago

ref is still unclear IMO, not sure what it could be instead, though. I think the reason I did it like this is to (1) possibly support matrixing with a single recipe in the future and (2) that the diffs when changing the version would look nicer.

But yeah, I probably agree that this is better.

gmpinder commented 3 months ago

How about something like:

base-image:
    image: ghcr.io/ublue-os/silverblue-main:40
    public-key: https://... # unimplemented, needed for base image verification, would be optional, and automatically supplied for ublue etc.
    type: ostree # unimplemented, might be needed for effectively supporting different base images, which might need things like running `ostree container commit` 

It feels a bit redundant, but it follows convention from k8s and docker compose. Maybe if we want, we can find another name for base-image.

xynydev commented 3 months ago
base:
  image: ghcr.io/ublue-os/silverblue-main:40
  public-key: https://... # unimplemented, needed for base image verification, would be optional, and automatically supplied for ublue etc.
  type: ostree # unimplemented, might be needed for effectively supporting different base images, which might need things like running `ostree container commit` 
metadata:
  name: weird-os
  description: ...
  tags: [gts]
stages:
  ...
modules:
  ...
gmpinder commented 3 months ago

I'm liking that

gmpinder commented 2 months ago

Something that I think would be super useful. I've been looking through how the Bluefin Containerfile is laid out and it gave me the idea of having something like:

base:
  from-recipe: parent-recipe.yml
...

This could allow building tiered images like how bluefin and bluefin-dx are created.

FROM ${BASE_IMAGE}:${FEDORA_MAJOR_VERSION} AS base
# ...

FROM base AS dx
# ...

We could then build and push both in a single build process (once I figure that out).

xynydev commented 2 months ago

That is similar to what I laid out as the other way to solve building multiple similar images alongside modules. Could be interesting. I don't see the practical difference from just building multiple images, one that uses the other as a base, except that GHA matrixes are concurrent so the build might be based on yesterdays image if the workflows aren't set up correctly.

I'm also not sure how your solution would interplay with GHA matrixing, so we'll see.

gmpinder commented 2 months ago

except that GHA matrixes are concurrent so the build might be based on yesterdays image if the workflows aren't set up correctly.

Yeah that was my main reasoning for it, though with having the full recipe we probably don't need to push both images. I think it would be a good idea for inheriting the base image and any stages that were defined in the parent recipe. We can try it out later and see what works best.

xynydev commented 2 months ago

though with having the full recipe we probably don't need to push both images

Is there any point in this if both images aren't pushed?

gmpinder commented 2 months ago

Is there any point in this if both images aren't pushed?

Yeah, the whole system of inheriting from a recipe. Say you have a tiered set of images. One that we'll call basic, and another we'll call deluxe. The deluxe recipe will be based off of the basic recipe. If we then decide to change the basic recipe to a new version of the image or a different base image altogether, the deluxe recipe will also have this new base image. Any and all modules that are run for the basic recipe will also be there for the deluxe recipe without having to explicitly do a from-file for each of the stages and modules that the deluxe recipe should also have.

All in all it would just make it easier to keep certain images in sync with each other and prevent the issue of basing it off of yesterday's build of basic.

xynydev commented 2 months ago

If basic is not published, that is essentially the same as doing from-file from a file that contains multiple module definitions (AFAIK totally supported, as should be doing the same thing for stages). That basically just leaves the base-image and related properties to be inherited from the basic image. That feels like a really small improvement to basically re-implement a feature that already exists.

If there's some elegant solution that would push both basic and deluxe, I would 100% be for that. If not, this feels like a duplicate feature.

gmpinder commented 2 months ago

If there's some elegant solution that would push both basic and deluxe, I would 100% be for that. If not, this feels like a duplicate feature.

I can agree to this. This will require some research outside of this issue.