Skip to content

Commit

Permalink
feat: Introduce .kraftignore
Browse files Browse the repository at this point in the history
Signed-off-by: Md Sahil <[email protected]>
  • Loading branch information
MdSahil-oss committed Jan 20, 2024
1 parent 2bd20dc commit af27038
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 3 deletions.
26 changes: 26 additions & 0 deletions initrd/directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ func (initrd *directory) Build(ctx context.Context) (string, error) {
}
}

if _, err := os.Stat(initrd.opts.output); err == nil {
if err = os.Remove(initrd.opts.output); err != nil {
return "", err
}
}

f, err := os.OpenFile(initrd.opts.output, os.O_RDWR|os.O_CREATE, 0o644)
if err != nil {
return "", fmt.Errorf("could not open initramfs file: %w", err)
Expand All @@ -75,6 +81,11 @@ func (initrd *directory) Build(ctx context.Context) (string, error) {
writer := cpio.NewWriter(f)
defer writer.Close()

ignoringItems, err := getKraftignoreItems(ctx, initrd)
if err != nil {
return "", err
}

if err := filepath.WalkDir(initrd.path, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return fmt.Errorf("received error before parsing path: %w", err)
Expand All @@ -86,6 +97,21 @@ func (initrd *directory) Build(ctx context.Context) (string, error) {
}
internal = "." + filepath.ToSlash(internal)

if len(ignoringItems) > 0 && path != initrd.path {
switch isExistInKraftignoreFile(internal, d, ignoringItems) {
case SkipDir:
log.G(ctx).
WithField("directory", internal).
Trace("ignoring from archiving")
return filepath.SkipDir
case Exist:
log.G(ctx).
WithField("file", internal).
Trace("ignoring from archiving")
return nil
}
}

info, err := d.Info()
if err != nil {
return fmt.Errorf("could not get directory entry info: %w", err)
Expand Down
159 changes: 159 additions & 0 deletions initrd/kraftignore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2022, Unikraft GmbH and The KraftKit Authors.
// Licensed under the BSD-3-Clause License (the "License").
// You may not use this file except in compliance with the License.
package initrd

import (
"bufio"
"context"
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"

"kraftkit.sh/log"
)

// kraftignore filename
const KraftignoreFileName = ".kraftignore"

type IgnoringFileType string

const (
Exist = IgnoringFileType("Exist")
NotExist = IgnoringFileType("NotExist")
SkipDir = IgnoringFileType("SkipDir")
)

// returns file and directory names specified in .kraftignore
func getKraftignoreItems(ctx context.Context, initrd *directory) ([]string, error) {
if initrd.opts.kraftignorePath == "" {
cwd, err := os.Getwd()
if err != nil {
return []string{}, err
}
initrd.opts.kraftignorePath = filepath.Join(cwd, KraftignoreFileName)
}

if _, err := os.Stat(initrd.opts.kraftignorePath); errors.Is(err, os.ErrNotExist) {
return []string{}, nil
} else if err != nil {
return []string{}, err
}

kraftignoreFile, err := os.Open(initrd.opts.kraftignorePath)
if err != nil {
return []string{}, err
}

defer func() {
kraftIgnoreErr := kraftignoreFile.Close()
if kraftIgnoreErr != nil {
if err != nil {
err = fmt.Errorf("%w: %w", err, kraftIgnoreErr)
} else {
err = kraftIgnoreErr
}
}
}()

kraftignoreScanner := bufio.NewScanner(kraftignoreFile)
kraftignoreScanner.Split(bufio.ScanLines)
var kraftignoreFileLines, ignoringItems []string

for kraftignoreScanner.Scan() {
kraftignoreFileLines = append(kraftignoreFileLines, kraftignoreScanner.Text())
}

for lineNum, line := range kraftignoreFileLines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
items := findLineItems(line)
for _, item := range items {
if item == "" || item == "#" {
continue
}

if hasGlobPatterns(item) {
log.G(ctx).
WithField("file", initrd.opts.kraftignorePath).
Warn("contains a glob pattern ", item,
" at line ", lineNum,
" which is not supported by Kraftkit")
continue
}

if _, err := os.Stat(filepath.Join(initrd.path, item)); os.IsNotExist(err) {
log.G(ctx).
WithField("file", initrd.opts.kraftignorePath).
Warn("contains ", item,
" at line ", lineNum,
" which does not exist in the provided rootfs directory")
continue
}

ignoringItems = append(ignoringItems, item)
}
}

return ignoringItems, err
}

// checks if the path exist in .kraftignore
func isExistInKraftignoreFile(internal string, pathInfo fs.DirEntry, kraftignoreItems []string) IgnoringFileType {
for _, ignoringItem := range kraftignoreItems {
if internal == ignoringItem {
if pathInfo.IsDir() {
return SkipDir
}
return Exist
}
}
return NotExist
}

// checks if the item contains glob pattern
func hasGlobPatterns(item string) bool {
return strings.ContainsAny(item, "*?![{")
}

// finds items in a line of .kraftignore
func findLineItems(line string) []string {
items := strings.Split(line, " ")
for index := 0; index < len(items); index++ {
charToFind := ""
if strings.HasPrefix(items[index], `"`) && !strings.HasSuffix(items[index], `"`) {
charToFind = `"`
} else if strings.HasPrefix(items[index], `'`) && !strings.HasSuffix(items[index], `'`) {
charToFind = `'`
}

if len(charToFind) > 0 {
i := index + 1
for ; i < len(items) && !strings.HasSuffix(items[i], charToFind); i++ {
items[index] += " " + items[i]
items = append(items[:i], items[i+1:]...)
i--
}
items[index] += " " + items[i]
items = append(items[:i], items[i+1:]...)
}
items[index] = strings.Trim(items[index], `"`)
items[index] = strings.Trim(items[index], `'`)
items[index] = strings.TrimSpace(items[index])
items[index] = strings.TrimPrefix(items[index], "../")
if !strings.HasPrefix(items[index], "./") {
if !strings.HasPrefix(items[index], "/") {
items[index] = "/" + items[index]
}
items[index] = "." + items[index]
}
items[index] = strings.TrimSuffix(items[index], string(filepath.Separator))
}
return items
}
19 changes: 16 additions & 3 deletions initrd/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@
// You may not use this file except in compliance with the License.
package initrd

import (
"path/filepath"
)

type InitrdOptions struct {
output string
cacheDir string
arch string
output string
cacheDir string
arch string
kraftignorePath string
}

type InitrdOption func(*InitrdOptions) error
Expand Down Expand Up @@ -41,3 +46,11 @@ func WithArchitecture(arch string) InitrdOption {
return nil
}
}

// WithKraftignorePath sets the path for .kraftignore.
func WithKraftignorePath(dir string) InitrdOption {
return func(opts *InitrdOptions) error {
opts.kraftignorePath = filepath.Join(dir, KraftignoreFileName)
return nil
}
}

0 comments on commit af27038

Please sign in to comment.