From 00de9141a8b92eb36d6b583e796ab57122a54e5b Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Fri, 12 Jul 2024 22:13:22 +0000 Subject: [PATCH] Use strong type for package constraints (#306) This allows us to add more functionality to package constraints, including support for constraining by arch. Signed-off-by: Brian Goff --- docker-bake.hcl | 4 +- docs/spec.schema.json | 58 ++++++++++++++------------ frontend/rpm/template.go | 36 ++++++++++++----- frontend/rpm/template_test.go | 76 ++++++++++++++++++++++++++++++----- image.go | 2 +- spec.go | 28 +++++++++---- test/azlinux_test.go | 14 ++++--- test/fixtures/frontend.yml | 2 +- test/fixtures/moby-runc.yml | 5 ++- test/windows_test.go | 6 +-- 10 files changed, 162 insertions(+), 69 deletions(-) diff --git a/docker-bake.hcl b/docker-bake.hcl index ab40003a2..120097e3c 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -173,8 +173,8 @@ target "deps-only" { dockerfile-inline = <= 1.0", + Version: []string{ + "< 2.0", + ">= 1.0", + }, + }, + "d-lib-single-arch-constraints": { + Arch: []string{"arm64"}, + }, + "e-lib-multi-arch-constraints": { + Arch: []string{"amd64", "arm64"}, + }, + "f-lib-multi-arch-multi-version-constraints": { + Arch: []string{"amd64", "arm64"}, + Version: []string{"< 2.0", ">= 1.0"}, }, }, - Runtime: map[string][]string{ + Runtime: map[string]dalec.PackageConstraints{ "a-no-constraints": {}, "b-one-constraints": { - "< 2.0", + Version: []string{"< 2.0"}, }, "c-multiple-constraints": { - "< 2.0", - ">= 1.0", + Version: []string{ + "< 2.0", + ">= 1.0", + }, + }, + "d-single-arch-constraints": { + Arch: []string{"arm64"}, + }, + "e-multi-arch-constraints": { + Arch: []string{"amd64", "arm64"}, + }, + "f-multi-arch-multi-version-constraints": { + Arch: []string{"amd64", "arm64"}, + Version: []string{"< 2.0", ">= 1.0"}, }, }, }, @@ -554,11 +579,45 @@ func TestTemplate_Requires(t *testing.T) { BuildRequires: b-lib-one-constraints < 2.0 BuildRequires: c-lib-multiple-constraints < 2.0 BuildRequires: c-lib-multiple-constraints >= 1.0 +%ifarch arm64 +BuildRequires: d-lib-single-arch-constraints +%endif +%ifarch amd64 +BuildRequires: e-lib-multi-arch-constraints +%endif +%ifarch arm64 +BuildRequires: e-lib-multi-arch-constraints +%endif +%ifarch amd64 +BuildRequires: f-lib-multi-arch-multi-version-constraints < 2.0 +BuildRequires: f-lib-multi-arch-multi-version-constraints >= 1.0 +%endif +%ifarch arm64 +BuildRequires: f-lib-multi-arch-multi-version-constraints < 2.0 +BuildRequires: f-lib-multi-arch-multi-version-constraints >= 1.0 +%endif Requires: a-no-constraints Requires: b-one-constraints < 2.0 Requires: c-multiple-constraints < 2.0 Requires: c-multiple-constraints >= 1.0 +%ifarch arm64 +Requires: d-single-arch-constraints +%endif +%ifarch amd64 +Requires: e-multi-arch-constraints +%endif +%ifarch arm64 +Requires: e-multi-arch-constraints +%endif +%ifarch amd64 +Requires: f-multi-arch-multi-version-constraints < 2.0 +Requires: f-multi-arch-multi-version-constraints >= 1.0 +%endif +%ifarch arm64 +Requires: f-multi-arch-multi-version-constraints < 2.0 +Requires: f-multi-arch-multi-version-constraints >= 1.0 +%endif ` @@ -593,7 +652,6 @@ A helpful tool %install %files - `) assert.Equal(t, expect, actual) diff --git a/image.go b/image.go index c89283f17..df92c0948 100644 --- a/image.go +++ b/image.go @@ -39,7 +39,7 @@ type ImageConfig struct { // Post is the post install configuration for the image. // This allows making additional modifications to the container rootfs after the package(s) are installed. // - // Use this to perform actions that would otherwise require additional tooling inside the container that is not relavent to + // Use this to perform actions that would otherwise require additional tooling inside the container that is not relevant to // the resulting container and makes a post-install script as part of the package unnecessary. Post *PostInstall `yaml:"post,omitempty" json:"post,omitempty"` diff --git a/spec.go b/spec.go index 17df45c07..e2db6f90e 100644 --- a/spec.go +++ b/spec.go @@ -34,21 +34,21 @@ type Spec struct { // Conflicts is the list of packages that conflict with the generated package. // This will prevent the package from being installed if any of these packages are already installed or vice versa. - Conflicts map[string][]string `yaml:"conflicts,omitempty" json:"conflicts,omitempty"` + Conflicts map[string]PackageConstraints `yaml:"conflicts,omitempty" json:"conflicts,omitempty"` // Replaces is the list of packages that are replaced by the generated package. - Replaces map[string][]string `yaml:"replaces,omitempty" json:"replaces,omitempty"` + Replaces map[string]PackageConstraints `yaml:"replaces,omitempty" json:"replaces,omitempty"` // Provides is the list of things that the generated package provides. // This can be used to satisfy dependencies of other packages. // As an example, the moby-runc package provides "runc", other packages could depend on "runc" and be satisfied by moby-runc. // This is an advanced use case and consideration should be taken to ensure that the package actually provides the thing it claims to provide. - Provides []string `yaml:"provides,omitempty" json:"provides,omitempty"` + Provides map[string]PackageConstraints `yaml:"provides,omitempty" json:"provides,omitempty"` // Sources is the list of sources to use to build the artifact(s). // The map key is the name of the source and the value is the source configuration. // The source configuration is used to fetch the source and filter the files to include/exclude. // This can be mounted into the build using the "Mounts" field in the StepGroup. // - // Sources can be embedded in the main spec as here or overriden in a build request. + // Sources can be embedded in the main spec as here or overridden in a build request. Sources map[string]Source `yaml:"sources,omitempty" json:"sources,omitempty"` // Patches is the list of patches to apply to the sources. @@ -426,17 +426,29 @@ type SourceGenerator struct { Gomod *GeneratorGomod `yaml:"gomod" json:"gomod"` } +// PackageConstraints is used to specify complex constraints for a package dependency. +type PackageConstraints struct { + // Version is a list of version constraints for the package. + // The format of these strings is depenendent on the package manager of the target system. + // Examples: + // [">=1.0.0", "<2.0.0"] + Version []string `yaml:"version,omitempty" json:"version,omitempty"` + // Arch is a list of architecture constraints for the package. + // Use this to specify that a package constraint only applies to certain architectures. + Arch []string `yaml:"arch,omitempty" json:"arch,omitempty"` +} + // PackageDependencies is a list of dependencies for a package. // This will be included in the package metadata so that the package manager can install the dependencies. // It also includes build-time dedendencies, which we'll install before running any build steps. type PackageDependencies struct { // Build is the list of packagese required to build the package. - Build map[string][]string `yaml:"build,omitempty" json:"build,omitempty"` + Build map[string]PackageConstraints `yaml:"build,omitempty" json:"build,omitempty"` // Runtime is the list of packages required to install/run the package. - Runtime map[string][]string `yaml:"runtime,omitempty" json:"runtime,omitempty"` + Runtime map[string]PackageConstraints `yaml:"runtime,omitempty" json:"runtime,omitempty"` // Recommends is the list of packages recommended to install with the generated package. // Note: Not all package managers support this (e.g. rpm) - Recommends map[string][]string `yaml:"recommends,omitempty" json:"recommends,omitempty"` + Recommends map[string]PackageConstraints `yaml:"recommends,omitempty" json:"recommends,omitempty"` // Test lists any extra packages required for running tests // These packages are only installed for tests which have steps that require @@ -518,7 +530,7 @@ type Target struct { Image *ImageConfig `yaml:"image,omitempty" json:"image,omitempty"` // Frontend is the frontend configuration to use for the target. - // This is used to forward the build to a different, dalec-compatabile frontend. + // This is used to forward the build to a different, dalec-compatible frontend. // This can be useful when testing out new distros or using a different version of the frontend for a given distro. Frontend *Frontend `yaml:"frontend,omitempty" json:"frontend,omitempty"` diff --git a/test/azlinux_test.go b/test/azlinux_test.go index 01cfd6634..5d6cdfbda 100644 --- a/test/azlinux_test.go +++ b/test/azlinux_test.go @@ -105,7 +105,7 @@ func testLinuxDistro(ctx context.Context, t *testing.T, testConfig testLinuxConf Packager: "Dalec", Description: "Should not have internet access during build", Dependencies: &dalec.PackageDependencies{ - Build: map[string][]string{"curl": {}}, + Build: map[string]dalec.PackageConstraints{"curl": {}}, }, Build: dalec.ArtifactBuild{ Steps: []dalec.BuildStep{ @@ -224,7 +224,7 @@ index 0000000..5260cb1 }, Dependencies: &dalec.PackageDependencies{ - Runtime: map[string][]string{ + Runtime: map[string]dalec.PackageConstraints{ "bash": {}, "coreutils": {}, }, @@ -770,7 +770,7 @@ Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/boot }, }, Dependencies: &dalec.PackageDependencies{ - Build: map[string][]string{ + Build: map[string]dalec.PackageConstraints{ // TODO: This works at least for now, but is distro specific and // could break on new distros (though that is still unlikely). "golang": {}, @@ -806,7 +806,9 @@ Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/boot Packager: "Dalec", Description: "Should Create Specified Directories", Dependencies: &dalec.PackageDependencies{ - Runtime: map[string][]string{"curl": {}}, + Runtime: map[string]dalec.PackageConstraints{ + "curl": {}, + }, }, Sources: map[string]dalec.Source{ "src1": { @@ -962,7 +964,7 @@ Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/boot Packager: "Dalec", Description: "Should Create Specified Directories", Dependencies: &dalec.PackageDependencies{ - Runtime: map[string][]string{"curl": {}}, + Runtime: map[string]dalec.PackageConstraints{"curl": {}}, }, Sources: map[string]dalec.Source{ "src1": { @@ -1101,7 +1103,7 @@ Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/boot Description: "meta test", License: "MIT", Dependencies: &dalec.PackageDependencies{ - Runtime: map[string][]string{ + Runtime: map[string]dalec.PackageConstraints{ "curl": {}, }, }, diff --git a/test/fixtures/frontend.yml b/test/fixtures/frontend.yml index 586c7a357..eae61e7b9 100644 --- a/test/fixtures/frontend.yml +++ b/test/fixtures/frontend.yml @@ -17,7 +17,7 @@ sources: dependencies: build: - golang: [] + golang: build: env: diff --git a/test/fixtures/moby-runc.yml b/test/fixtures/moby-runc.yml index 0b0b0fa1b..d4ab62813 100644 --- a/test/fixtures/moby-runc.yml +++ b/test/fixtures/moby-runc.yml @@ -34,7 +34,8 @@ x-azl: &azl tar: runtime: libseccomp: - - ">= 2.3" + version: + - ">= 2.3" test: - /bin/sh tests: @@ -64,7 +65,7 @@ conflicts: runc-io: containerd.io: provides: - - runc + runc: {} sources: src: git: diff --git a/test/windows_test.go b/test/windows_test.go index 84255bad7..40fe508ad 100644 --- a/test/windows_test.go +++ b/test/windows_test.go @@ -304,7 +304,7 @@ echo "$BAR" > bar.txt zipperSpec := fillMetadata("bar", &dalec.Spec{ Dependencies: &dalec.PackageDependencies{ - Runtime: map[string][]string{ + Runtime: map[string]dalec.PackageConstraints{ "unzip": {}, }, }, @@ -381,7 +381,7 @@ echo "$BAR" > bar.txt zipperSpec := fillMetadata("bar", &dalec.Spec{ Dependencies: &dalec.PackageDependencies{ - Runtime: map[string][]string{ + Runtime: map[string]dalec.PackageConstraints{ "unzip": {}, }, }, @@ -447,7 +447,7 @@ echo "$BAR" > bar.txt }, }, Dependencies: &dalec.PackageDependencies{ - Build: map[string][]string{ + Build: map[string]dalec.PackageConstraints{ // TODO: This works at least for now, but is distro specific and // could break on new distros (though that is still unlikely). "golang": {},