Skip to content

Commit

Permalink
*: Apply filters on start
Browse files Browse the repository at this point in the history
This changes the way filters are applied. Prior to this change, filters
were applied in various places. With this change, all filters are
applied at start by modifying the loaded definition.

Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
  • Loading branch information
monstermunchkin committed Jul 10, 2023
1 parent d6b105a commit 74e4071
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 141 deletions.
50 changes: 32 additions & 18 deletions distrobuilder/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,38 @@ func main() {
return
}

// Get the image definition
globalCmd.definition, err = getDefinition(args[0], globalCmd.flagOptions)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed reading definition: %s\n", err)
os.Exit(1)
}

// Set VM target type if we're building or packing a VM.
isTargetVM, _ := cmd.Flags().GetBool("vm")
if isTargetVM {
globalCmd.definition.Targets.Type = shared.DefinitionFilterTypeVM
}

// Get image targets depending on the subcommand and flags.
var imageTargets shared.ImageTarget

if strings.HasPrefix(cmd.CalledAs(), "build-") {
imageTargets = shared.ImageTargetUndefined
}

if cmd.CalledAs() != "build-dir" {
imageTargets |= shared.ImageTargetAll
}

if isTargetVM {
imageTargets |= shared.ImageTargetVM
} else {
imageTargets |= shared.ImageTargetContainer
}

globalCmd.definition.ApplyFilters(imageTargets)

// Create temp directory if the cache directory isn't explicitly set
if globalCmd.flagCacheDir == "" {
dir, err := os.MkdirTemp("/var/cache", "distrobuilder.")
Expand All @@ -179,12 +211,6 @@ func main() {

globalCmd.flagCacheDir = dir
}

// Set VM target type if we're building or packing a VM.
isTargetVM, _ := cmd.Flags().GetBool("vm")
if isTargetVM {
globalCmd.definition.Targets.Type = shared.DefinitionFilterTypeVM
}
},
PersistentPostRunE: globalCmd.postRun,
CompletionOptions: cobra.CompletionOptions{DisableDefaultCmd: true},
Expand Down Expand Up @@ -306,12 +332,6 @@ func (c *cmdGlobal) preRunBuild(cmd *cobra.Command, args []string) error {
return fmt.Errorf("Failed to create directory %q: %w", c.sourceDir, err)
}

// Get the image definition
c.definition, err = getDefinition(args[0], c.flagOptions)
if err != nil {
return fmt.Errorf("Failed to get definition: %w", err)
}

// Create cache directory if we also plan on creating LXC or LXD images
if !isRunningBuildDir {
err = os.MkdirAll(c.flagCacheDir, 0755)
Expand Down Expand Up @@ -467,12 +487,6 @@ func (c *cmdGlobal) preRunPack(cmd *cobra.Command, args []string) error {
c.targetDir = args[2]
}

// Get the image definition
c.definition, err = getDefinition(args[0], c.flagOptions)
if err != nil {
return fmt.Errorf("Failed to get definition: %w", err)
}

return nil
}

Expand Down
4 changes: 0 additions & 4 deletions distrobuilder/main_build-dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@ func (c *cmdBuildDir) command() *cobra.Command {
PostRunE: func(cmd *cobra.Command, args []string) error {
// Run global generators
for _, file := range c.global.definition.Files {
if !shared.ApplyFilter(&file, c.global.definition.Image.Release, c.global.definition.Image.ArchitectureMapped, c.global.definition.Image.Variant, c.global.definition.Targets.Type, 0) {
continue
}

generator, err := generators.Load(file.Generator, c.global.logger, c.global.flagCacheDir, c.global.targetDir, file, *c.global.definition)
if err != nil {
return fmt.Errorf("Failed to load generator %q: %w", file.Generator, err)
Expand Down
6 changes: 0 additions & 6 deletions distrobuilder/main_lxc.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,12 +187,6 @@ func (c *cmdLXC) run(cmd *cobra.Command, args []string, overlayDir string) error
c.global.flagCacheDir, *c.global.definition)

for _, file := range c.global.definition.Files {
if !shared.ApplyFilter(&file, c.global.definition.Image.Release, c.global.definition.Image.ArchitectureMapped, c.global.definition.Image.Variant, c.global.definition.Targets.Type, shared.ImageTargetUndefined|shared.ImageTargetAll|shared.ImageTargetContainer) {
c.global.logger.WithField("generator", file.Generator).Info("Skipping generator")

continue
}

generator, err := generators.Load(file.Generator, c.global.logger, c.global.flagCacheDir, overlayDir, file, *c.global.definition)
if err != nil {
return fmt.Errorf("Failed to load generator %q: %w", file.Generator, err)
Expand Down
4 changes: 0 additions & 4 deletions distrobuilder/main_lxd.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,10 +262,6 @@ func (c *cmdLXD) run(cmd *cobra.Command, args []string, overlayDir string) error
}

for _, file := range c.global.definition.Files {
if !shared.ApplyFilter(&file, c.global.definition.Image.Release, c.global.definition.Image.ArchitectureMapped, c.global.definition.Image.Variant, c.global.definition.Targets.Type, imageTargets) {
continue
}

generator, err := generators.Load(file.Generator, c.global.logger, c.global.flagCacheDir, overlayDir, file, *c.global.definition)
if err != nil {
return fmt.Errorf("Failed to load generator %q: %w", file.Generator, err)
Expand Down
18 changes: 2 additions & 16 deletions managers/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,19 +94,9 @@ func Load(ctx context.Context, managerName string, logger *logrus.Logger, defini

// ManagePackages manages packages.
func (m *Manager) ManagePackages(imageTarget shared.ImageTarget) error {
var validSets []shared.DefinitionPackagesSet

for _, set := range m.def.Packages.Sets {
if !shared.ApplyFilter(&set, m.def.Image.Release, m.def.Image.ArchitectureMapped, m.def.Image.Variant, m.def.Targets.Type, imageTarget) {
continue
}

validSets = append(validSets, set)
}

// If there's nothing to install or remove, and no updates need to be performed,
// we can exit here.
if len(validSets) == 0 && !m.def.Packages.Update {
if len(m.def.Packages.Sets) == 0 && !m.def.Packages.Update {
return nil
}

Expand Down Expand Up @@ -137,7 +127,7 @@ func (m *Manager) ManagePackages(imageTarget shared.ImageTarget) error {
}
}

for _, set := range optimizePackageSets(validSets) {
for _, set := range optimizePackageSets(m.def.Packages.Sets) {
if set.Action == "install" {
err = m.mgr.install(set.Packages, set.Flags)
} else if set.Action == "remove" {
Expand Down Expand Up @@ -168,10 +158,6 @@ func (m *Manager) ManageRepositories(imageTarget shared.ImageTarget) error {
}

for _, repo := range m.def.Packages.Repositories {
if !shared.ApplyFilter(&repo, m.def.Image.Release, m.def.Image.ArchitectureMapped, m.def.Image.Variant, m.def.Targets.Type, imageTarget) {
continue
}

// Run template on repo.URL
repo.URL, err = shared.RenderTemplate(repo.URL, m.def)
if err != nil {
Expand Down
158 changes: 105 additions & 53 deletions shared/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -536,10 +536,6 @@ func (d *Definition) GetRunnableActions(trigger string, imageTarget ImageTarget)
continue
}

if !ApplyFilter(&action, d.Image.Release, d.Image.ArchitectureMapped, d.Image.Variant, d.Targets.Type, imageTarget) {
continue
}

out = append(out, action)
}

Expand All @@ -554,7 +550,7 @@ func (d *Definition) GetEarlyPackages(action string) []string {
normal := []DefinitionPackagesSet{}

for _, set := range d.Packages.Sets {
if set.Early && set.Action == action && ApplyFilter(&set, d.Image.Release, d.Image.ArchitectureMapped, d.Image.Variant, d.Targets.Type, 0) {
if set.Early && set.Action == action {
early = append(early, set.Packages...)
} else {
normal = append(normal, set)
Expand Down Expand Up @@ -592,58 +588,17 @@ func (d *Definition) getMappedArchitecture() (string, error) {
return arch, nil
}

func getFieldByTag(v reflect.Value, t reflect.Type, tag string) (reflect.Value, error) {
parts := strings.SplitN(tag, ".", 2)

if t.Kind() == reflect.Slice {
// Get index, e.g. '0' from tag 'foo.0'
value, err := strconv.Atoi(parts[0])
if err != nil {
return reflect.Value{}, err
}

if t.Elem().Kind() == reflect.Struct {
// Make sure we are in range, otherwise return error
if value < 0 || value >= v.Len() {
return reflect.Value{}, errors.New("Index out of range")
}

return getFieldByTag(v.Index(value), t.Elem(), parts[1])
}

// Primitive type
return v.Index(value), nil
}

if t.Kind() == reflect.Struct {
// Find struct field with correct tag
for i := 0; i < t.NumField(); i++ {
value := t.Field(i).Tag.Get("yaml")
if value != "" && strings.Split(value, ",")[0] == parts[0] {
if len(parts) == 1 {
return v.Field(i), nil
}

return getFieldByTag(v.Field(i), t.Field(i).Type, parts[1])
}
}
}

// Return its value if it's a primitive type
return v, nil
}

// ApplyFilter returns true if the filter matches.
func ApplyFilter(filter Filter, release string, architecture string, variant string, targetType DefinitionFilterType, acceptedImageTargets ImageTarget) bool {
if len(filter.GetReleases()) > 0 && !shared.StringInSlice(release, filter.GetReleases()) {
func (d *Definition) applyFilter(filter Filter, acceptedImageTargets ImageTarget) bool {
if len(filter.GetReleases()) > 0 && !shared.StringInSlice(d.Image.Release, filter.GetReleases()) {
return false
}

if len(filter.GetArchitectures()) > 0 && !shared.StringInSlice(architecture, filter.GetArchitectures()) {
if len(filter.GetArchitectures()) > 0 && !shared.StringInSlice(d.Image.ArchitectureMapped, filter.GetArchitectures()) {
return false
}

if len(filter.GetVariants()) > 0 && !shared.StringInSlice(variant, filter.GetVariants()) {
if len(filter.GetVariants()) > 0 && !shared.StringInSlice(d.Image.Variant, filter.GetVariants()) {
return false
}

Expand All @@ -664,22 +619,119 @@ func ApplyFilter(filter Filter, release string, architecture string, variant str
}

if acceptedImageTargets&ImageTargetAll > 0 {
if len(types) == 2 && hasTargetType(targetType) {
if len(types) == 2 && hasTargetType(d.Targets.Type) {
return true
}
}

if acceptedImageTargets&ImageTargetContainer > 0 {
if targetType == DefinitionFilterTypeContainer && hasTargetType(targetType) {
if d.Targets.Type == DefinitionFilterTypeContainer && hasTargetType(d.Targets.Type) {
return true
}
}

if acceptedImageTargets&ImageTargetVM > 0 {
if targetType == DefinitionFilterTypeVM && hasTargetType(targetType) {
if d.Targets.Type == DefinitionFilterTypeVM && hasTargetType(d.Targets.Type) {
return true
}
}

return false
}

// ApplyFilters removes those parts of the definition which are excluded by the filters.
func (d *Definition) ApplyFilters(imageTargets ImageTarget) {
newDefinition := Definition{
Actions: []DefinitionAction{},
Environment: d.Environment,
Files: []DefinitionFile{},
Image: d.Image,
Mappings: d.Mappings,
Packages: d.Packages,
Source: d.Source,
Targets: d.Targets,
}

// Filter files
for _, file := range d.Files {
if !d.applyFilter(&file, imageTargets) {
continue
}

newDefinition.Files = append(newDefinition.Files, file)
}

// Filter repositories
newDefinition.Packages.Repositories = []DefinitionPackagesRepository{}

for _, repo := range d.Packages.Repositories {
if !d.applyFilter(&repo, imageTargets) {
continue
}

newDefinition.Packages.Repositories = append(newDefinition.Packages.Repositories, repo)
}

// Filter package sets
newDefinition.Packages.Sets = []DefinitionPackagesSet{}

for _, set := range d.Packages.Sets {
if !d.applyFilter(&set, imageTargets) {
continue
}

newDefinition.Packages.Sets = append(newDefinition.Packages.Sets, set)
}

// Filter actions
for _, action := range d.Actions {
if !d.applyFilter(&action, imageTargets) {
continue
}

newDefinition.Actions = append(newDefinition.Actions, action)
}

*d = newDefinition
}

func getFieldByTag(v reflect.Value, t reflect.Type, tag string) (reflect.Value, error) {
parts := strings.SplitN(tag, ".", 2)

if t.Kind() == reflect.Slice {
// Get index, e.g. '0' from tag 'foo.0'
value, err := strconv.Atoi(parts[0])
if err != nil {
return reflect.Value{}, err
}

if t.Elem().Kind() == reflect.Struct {
// Make sure we are in range, otherwise return error
if value < 0 || value >= v.Len() {
return reflect.Value{}, errors.New("Index out of range")
}

return getFieldByTag(v.Index(value), t.Elem(), parts[1])
}

// Primitive type
return v.Index(value), nil
}

if t.Kind() == reflect.Struct {
// Find struct field with correct tag
for i := 0; i < t.NumField(); i++ {
value := t.Field(i).Tag.Get("yaml")
if value != "" && strings.Split(value, ",")[0] == parts[0] {
if len(parts) == 1 {
return v.Field(i), nil
}

return getFieldByTag(v.Field(i), t.Field(i).Type, parts[1])
}
}
}

// Return its value if it's a primitive type
return v, nil
}
Loading

0 comments on commit 74e4071

Please sign in to comment.