ChangeWorkingDirectory function

Add a WORKDIR instruction to the containerfile

Parameters:

  • workdir string
  • containerfile *os.File

Returns:

  • error
Show/Hide Function Body
{
	if workdir != "" {
		_, err := containerfile.WriteString(
			fmt.Sprintf("WORKDIR %s\n", workdir),
		)
		if err != nil {
			return err
		}
	}
	return nil
}

RestoreWorkingDirectory function

Add a WORKDIR instruction to reset to the root directory

Parameters:

  • workdir string
  • containerfile *os.File

Returns:

  • error
Show/Hide Function Body
{
	if workdir != "" {
		_, err := containerfile.WriteString(
			fmt.Sprintf("WORKDIR %s\n", "/"),
		)
		if err != nil {
			return err
		}
	}

	return nil
}

BuildRecipe function

Load and build a Containerfile from the specified recipe

Parameters:

  • recipePath string

Returns:

  • api.Recipe
  • error
Show/Hide Function Body
{
	// load the recipe
	recipe, err := LoadRecipe(recipePath)
	if err != nil {
		return api.Recipe{}, err
	}

	fmt.Printf("Building recipe %s\n", recipe.Name)

	// build the Containerfile
	err = BuildContainerfile(recipe)
	if err != nil {
		return api.Recipe{}, err
	}

	modules := 0
	for _, stage := range recipe.Stages {
		modules += len(stage.Modules)
	}

	fmt.Printf("Recipe %s built successfully\n", recipe.Name)
	fmt.Printf("Processed %d stages\n", len(recipe.Stages))
	fmt.Printf("Processed %d modules\n", modules)

	return *recipe, nil
}

BuildContainerfile function

Generate a Containerfile from the recipe

Parameters:

  • recipe *api.Recipe

Returns:

  • error
Show/Hide Function Body
{
	containerfile, err := os.Create(recipe.Containerfile)
	if err != nil {
		return err
	}

	defer containerfile.Close()

	for _, stage := range recipe.Stages {
		// build the modules*
		// * actually just build the commands that will be used
		//   in the Containerfile to build the modules
		cmds, err := BuildModules(recipe, stage.Modules)
		if err != nil {
			return err
		}

		// FROM
		if stage.Id != "" {
			_, err = containerfile.WriteString(
				fmt.Sprintf("# Stage: %s\n", stage.Id),
			)
			if err != nil {
				return err
			}
			_, err = containerfile.WriteString(
				fmt.Sprintf("FROM %s AS %s\n", stage.Base, stage.Id),
			)
			if err != nil {
				return err
			}
		} else {
			_, err = containerfile.WriteString(
				fmt.Sprintf("FROM %s\n", stage.Base),
			)
			if err != nil {
				return err
			}
		}

		// COPY
		if len(stage.Copy) > 0 {
			for _, copy := range stage.Copy {
				if len(copy.SrcDst) > 0 {
					err = ChangeWorkingDirectory(copy.Workdir, containerfile)
					if err != nil {
						return err
					}

					for src, dst := range copy.SrcDst {
						if copy.From != "" {
							_, err = containerfile.WriteString(
								fmt.Sprintf("COPY --from=%s %s %s\n", copy.From, src, dst),
							)
							if err != nil {
								return err
							}
						} else {
							_, err = containerfile.WriteString(
								fmt.Sprintf("COPY %s %s\n", src, dst),
							)
							if err != nil {
								return err
							}
						}
					}

					err = RestoreWorkingDirectory(copy.Workdir, containerfile)
					if err != nil {
						return err
					}
				}
			}
		}

		// LABELS
		for key, value := range stage.Labels {
			_, err = containerfile.WriteString(
				fmt.Sprintf("LABEL %s='%s'\n", key, value),
			)
			if err != nil {
				return err
			}
		}

		// ENV
		for key, value := range stage.Env {
			_, err = containerfile.WriteString(
				fmt.Sprintf("ENV %s=%s\n", key, value),
			)
			if err != nil {
				return err
			}
		}

		// ARGS
		for key, value := range stage.Args {
			_, err = containerfile.WriteString(
				fmt.Sprintf("ARG %s=%s\n", key, value),
			)
			if err != nil {
				return err
			}
		}

		// RUN(S)
		if !stage.SingleLayer {
			if len(stage.Runs.Commands) > 0 {
				err = ChangeWorkingDirectory(stage.Runs.Workdir, containerfile)
				if err != nil {
					return err
				}

				for _, cmd := range stage.Runs.Commands {
					_, err = containerfile.WriteString(
						fmt.Sprintf("RUN %s\n", cmd),
					)
					if err != nil {
						return err
					}
				}

				err = RestoreWorkingDirectory(stage.Runs.Workdir, containerfile)
				if err != nil {
					return err
				}
			}
		}

		// EXPOSE
		for key, value := range stage.Expose {
			_, err = containerfile.WriteString(
				fmt.Sprintf("EXPOSE %s/%s\n", key, value),
			)
			if err != nil {
				return err
			}
		}

		// ADDS
		if len(stage.Adds) > 0 {
			for _, add := range stage.Adds {
				if len(add.SrcDst) > 0 {
					err = ChangeWorkingDirectory(add.Workdir, containerfile)
					if err != nil {
						return err
					}

					for src, dst := range add.SrcDst {
						_, err = containerfile.WriteString(
							fmt.Sprintf("ADD %s %s\n", src, dst),
						)
						if err != nil {
							return err
						}
					}
				}

				err = RestoreWorkingDirectory(add.Workdir, containerfile)
				if err != nil {
					return err
				}
			}
		}

		// INCLUDES.CONTAINER
		_, err = containerfile.WriteString("ADD includes.container /\n")
		if err != nil {
			return err
		}

		// SOURCES
		_, err = containerfile.WriteString("ADD sources /sources\n")
		if err != nil {
			return err
		}

		// MODULES RUN(S)
		if !stage.SingleLayer {
			for _, cmd := range cmds {
				if cmd.Command == "" {
					continue
				}

				err = ChangeWorkingDirectory(cmd.Workdir, containerfile)
				if err != nil {
					return err
				}

				_, err = containerfile.WriteString(
					fmt.Sprintf("RUN %s\n", cmd.Command),
				)
				if err != nil {
					return err
				}

				err = RestoreWorkingDirectory(cmd.Workdir, containerfile)
				if err != nil {
					return err
				}
			}
		}

		// SINGLE LAYER
		if stage.SingleLayer {
			if len(stage.Runs.Commands) > 0 {
				err = ChangeWorkingDirectory(stage.Runs.Workdir, containerfile)
				if err != nil {
					return err
				}

				unifiedCmd := "RUN "

				for i, cmd := range stage.Runs.Commands {
					unifiedCmd += cmd
					if i != len(stage.Runs.Commands)-1 {
						unifiedCmd += " && "
					}
				}

				if len(cmds) > 0 {
					unifiedCmd += " && "
				}

				for i, cmd := range cmds {
					if cmd.Workdir != stage.Runs.Workdir {
						return errors.New("Workdir mismatch")
					}
					unifiedCmd += cmd.Command
					if i != len(cmds)-1 {
						unifiedCmd += " && "
					}
				}

				if len(unifiedCmd) > 4 {
					_, err = containerfile.WriteString(fmt.Sprintf("%s\n", unifiedCmd))
					if err != nil {
						return err
					}
				}

				err = RestoreWorkingDirectory(stage.Runs.Workdir, containerfile)
				if err != nil {
					return err
				}
			}
		}

		// CMD
		err = ChangeWorkingDirectory(stage.Cmd.Workdir, containerfile)
		if err != nil {
			return err
		}

		if len(stage.Cmd.Exec) > 0 {
			_, err = containerfile.WriteString(
				fmt.Sprintf("CMD [\"%s\"]\n", strings.Join(stage.Cmd.Exec, "\",\"")),
			)
			if err != nil {
				return err
			}

			err = RestoreWorkingDirectory(stage.Cmd.Workdir, containerfile)
			if err != nil {
				return err
			}
		}

		// ENTRYPOINT
		err = ChangeWorkingDirectory(stage.Entrypoint.Workdir, containerfile)
		if err != nil {
			return err
		}

		if len(stage.Entrypoint.Exec) > 0 {
			_, err = containerfile.WriteString(
				fmt.Sprintf("ENTRYPOINT [\"%s\"]\n", strings.Join(stage.Entrypoint.Exec, "\",\"")),
			)
			if err != nil {
				return err
			}

			err = RestoreWorkingDirectory(stage.Entrypoint.Workdir, containerfile)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

BuildModules function

Build commands for each module in the recipe

Parameters:

  • recipe *api.Recipe
  • modules []interface{}

Returns:

  • []ModuleCommand
  • error
Show/Hide Function Body
{
	cmds := []ModuleCommand{}
	for _, moduleInterface := range modules {
		var module Module
		err := mapstructure.Decode(moduleInterface, &module)
		if err != nil {
			return nil, err
		}

		cmd, err := BuildModule(recipe, moduleInterface)
		if err != nil {
			return nil, err
		}

		cmds = append(cmds, ModuleCommand{
			Name:    module.Name,
			Command: cmd,
			Workdir: module.Workdir,
		})
	}

	return cmds, nil
}

BuildModule function

Build a command string for the given module in the recipe

Parameters:

  • recipe *api.Recipe
  • moduleInterface interface{}

Returns:

  • string
  • error
Show/Hide Function Body
{
	var module Module
	err := mapstructure.Decode(moduleInterface, &module)
	if err != nil {
		return "", err
	}

	fmt.Printf("Building module [%s] of type [%s]\n", module.Name, module.Type)

	var commands string
	if len(module.Modules) > 0 {
		for _, nestedModule := range module.Modules {
			buildModule, err := BuildModule(recipe, nestedModule)
			if err != nil {
				return "", err
			}
			commands = commands + " && " + buildModule
		}
	}

	moduleBuilders := map[string]func(interface{}, *api.Recipe) (string, error){
		"shell":    BuildShellModule,
		"includes": func(interface{}, *api.Recipe) (string, error) { return "", nil },
	}

	if moduleBuilder, ok := moduleBuilders[module.Type]; ok {
		command, err := moduleBuilder(moduleInterface, recipe)
		if err != nil {
			return "", err
		}
		commands = commands + " && " + command
	} else {
		command, err := LoadBuildPlugin(module.Type, moduleInterface, recipe)
		if err != nil {
			return "", err
		}
		commands = commands + " && " + command
	}

	fmt.Printf("Module [%s] built successfully\n", module.Name)
	result := strings.TrimPrefix(commands, " && ")

	if result == "&&" {
		return "", nil
	}

	return result, nil
}

CompileRecipe function

Compile and build the recipe using the specified runtime

Parameters:

  • recipePath string
  • runtime string
  • isRoot bool
  • origGid int
  • origUid int

Returns:

  • error
Show/Hide Function Body
{
	recipe, err := BuildRecipe(recipePath)
	if err != nil {
		return err
	}

	syscall.Seteuid(0)
	syscall.Setegid(0)
	switch runtime {
	case "docker":
		err = compileDocker(recipe, origGid, origUid)
		if err != nil {
			return err
		}
	case "podman":
		err = compilePodman(recipe, origGid, origUid)
		if err != nil {
			return err
		}
	case "buildah":
		return fmt.Errorf("buildah not implemented yet")
	default:
		return fmt.Errorf("no runtime specified and the prometheus library is not implemented yet")
	}
	syscall.Seteuid(origUid)
	syscall.Setegid(origGid)

	for _, finalizeInterface := range recipe.Finalize {
		var module Finalize

		err := mapstructure.Decode(finalizeInterface, &module)
		if err != nil {
			return err
		}
		err = LoadFinalizePlugin(module.Type, finalizeInterface, &recipe, runtime, isRoot, origGid, origUid)
		if err != nil {
			return err
		}
	}

	fmt.Printf("Image %s built successfully using %s\n", recipe.Id, runtime)

	return nil
}

compileDocker function

Build an OCI image using the specified recipe through Docker

Parameters:

  • recipe api.Recipe
  • gid int
  • uid int

Returns:

  • error
Show/Hide Function Body
{
	docker, err := exec.LookPath("docker")
	if err != nil {
		return err
	}

	cmd := exec.Command(
		docker, "build",
		"-t", fmt.Sprintf("localhost/%s", recipe.Id),
		"-f", recipe.Containerfile,
		".",
	)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Dir = recipe.ParentPath

	return cmd.Run()
}

compilePodman function

Build an OCI image using the specified recipe through Podman

Parameters:

  • recipe api.Recipe
  • gid int
  • uid int

Returns:

  • error
Show/Hide Function Body
{
	podman, err := exec.LookPath("podman")
	if err != nil {
		return err
	}

	cmd := exec.Command(
		podman, "build",
		"-t", fmt.Sprintf("localhost/%s", recipe.Id),
		"-f", recipe.Containerfile,
		".",
	)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Dir = recipe.ParentPath

	return cmd.Run()
}

StorageConf struct

Configuration for storage drivers

Fields:

  • Driver (string)
  • Runroot (string)
  • Graphroot (string)

GetContainerStorage function

Retrieve the container storage configuration based on the runtime

Parameters:

  • runtime string

Returns:

  • cstorage.Store
  • error
Show/Hide Function Body
{
	storageconfig := &StorageConf{}
	if runtime == "podman" {
		podmanPath, err := exec.LookPath("podman")
		out, err := exec.Command(
			podmanPath, "info", "-f json").Output()
		if err != nil {
			fmt.Println("Failed to get podman info")
		} else {
			driver := strings.Split(strings.Split(string(out), "\"graphDriverName\": \"")[1], "\",")[0]
			storageconfig.Driver = driver

			graphRoot := strings.Split(strings.Split(string(out), "\"graphRoot\": \"")[1], "\",")[0]
			storageconfig.Graphroot = graphRoot

			runRoot := strings.Split(strings.Split(string(out), "\"runRoot\": \"")[1], "\",")[0]
			storageconfig.Runroot = runRoot
		}

	}
	if storageconfig.Runroot == "" {
		storageconfig.Runroot = "/var/lib/vib/runroot"
		storageconfig.Graphroot = "/var/lib/vib/graphroot"
		storageconfig.Driver = "overlay"
	}
	store, err := cstorage.GetStore(cstorage.StoreOptions{
		RunRoot:         storageconfig.Runroot,
		GraphRoot:       storageconfig.Graphroot,
		GraphDriverName: storageconfig.Driver,
	})
	if err != nil {
		return store, err
	}

	return store, err
}

GetImageID function

Retrieve the image ID for a given image name from the storage

Parameters:

  • name string
  • store cstorage.Store

Returns:

  • string
  • error
Show/Hide Function Body
{
	images, err := store.Images()
	if err != nil {
		return "", err
	}
	for _, img := range images {
		for _, imgname := range img.Names {
			if imgname == name {
				return img.ID, nil
			}
		}
	}
	return "", fmt.Errorf("image not found")
}

GetTopLayerID function

Retrieve the top layer ID for a given image ID from the storage

Parameters:

  • imageid string
  • store cstorage.Store

Returns:

  • string
  • error
Show/Hide Function Body
{
	images, err := store.Images()
	if err != nil {
		return "", err
	}
	for _, img := range images {
		if img.ID == imageid {
			return img.TopLayer, nil
		}
	}
	return "", fmt.Errorf("no top layer for id %s found", imageid)
}

MountImage function

Mount the image and return the mount directory

Parameters:

  • imagename string
  • imageid string
  • runtime string

Returns:

  • string
  • error
Show/Hide Function Body
{
	store, err := GetContainerStorage(runtime)
	if err != nil {
		return "", err
	}
	topLayerID, err := GetTopLayerID(imageid, store)
	if err != nil {
		return "", err
	}
	mountDir, err := store.Mount(topLayerID, "")
	if err != nil {
		return "", err
	}
	return mountDir, err
}

LoadRecipe function

LoadRecipe loads a recipe from a file and returns a Recipe

Does not validate the recipe but it will catch some errors

a proper validation will be done in the future

Parameters:

  • path string

Returns:

  • *api.Recipe
  • error
Show/Hide Function Body
{
	recipe := &api.Recipe{}

	// we use the absolute path to the recipe file as the
	// root path for the recipe and all its files
	recipePath, err := filepath.Abs(path)
	if err != nil {
		return nil, err
	}

	// here we open the recipe file and unmarshal it into
	// the Recipe struct, this is not a full validation
	// but it will catch some errors
	recipeFile, err := os.Open(recipePath)
	if err != nil {
		return nil, err
	}
	defer recipeFile.Close()

	recipeYAML, err := io.ReadAll(recipeFile)
	if err != nil {
		return nil, err
	}

	err = yaml.Unmarshal(recipeYAML, recipe)
	if err != nil {
		return nil, err
	}

	// the recipe path is stored in the recipe itself
	// for convenience
	recipe.Path = recipePath
	recipe.ParentPath = filepath.Dir(recipePath)

	// assuming the Containerfile location is relative
	recipe.Containerfile = filepath.Join(filepath.Dir(recipePath), "Containerfile")
	err = os.RemoveAll(recipe.Containerfile)
	if err != nil {
		return nil, err
	}

	// we create the sources directory which is the place where
	// all the sources will be stored and be available to all
	// the modules
	recipe.SourcesPath = filepath.Join(filepath.Dir(recipePath), "sources")
	err = os.RemoveAll(recipe.SourcesPath)
	if err != nil {
		return nil, err
	}
	err = os.MkdirAll(recipe.SourcesPath, 0755)
	if err != nil {
		return nil, err
	}

	// the downloads directory is a transient directory, here all
	// the downloaded sources will be stored before being moved
	// to the sources directory. This is useful since some sources
	// types need to be extracted, this way we can extract them
	// directly to the sources directory after downloading them
	recipe.DownloadsPath = filepath.Join(filepath.Dir(recipePath), "downloads")
	err = os.RemoveAll(recipe.DownloadsPath)
	if err != nil {
		return nil, err
	}
	err = os.MkdirAll(recipe.DownloadsPath, 0755)
	if err != nil {
		return nil, err
	}

	// the plugins directory contains all plugins that vib can load
	// and use for unknown modules in the recipe
	recipe.PluginPath = filepath.Join(filepath.Dir(recipePath), "plugins")

	// the includes directory is the place where we store all the
	// files to be included in the container, this is useful for
	// example to include configuration files. Each file must follow
	// the File Hierarchy Standard (FHS) and be placed in the correct
	// directory. For example, if you want to include a file in
	// /etc/nginx/nginx.conf you must place it in includes/etc/nginx/nginx.conf
	// so it will be copied to the correct location in the container
	includesContainerPath := filepath.Join(filepath.Dir(recipePath), "includes.container")
	_, err = os.Stat(includesContainerPath)
	if os.IsNotExist(err) {
		err := os.MkdirAll(includesContainerPath, 0755)
		if err != nil {
			return nil, err
		}
	}

	for i, stage := range recipe.Stages {
		// here we check if the extra Adds path exists
		for _, add := range stage.Adds {
			for src := range add.SrcDst {
				fullPath := filepath.Join(filepath.Dir(recipePath), src)
				_, err = os.Stat(fullPath)
				if os.IsNotExist(err) {
					return nil, err
				}
			}
		}

		// here we expand modules of type "includes"
		var newRecipeModules []interface{}

		for _, moduleInterface := range stage.Modules {

			var module Module
			err := mapstructure.Decode(moduleInterface, &module)
			if err != nil {
				return nil, err
			}

			if module.Type == "includes" {
				var include IncludesModule
				err := mapstructure.Decode(moduleInterface, &include)
				if err != nil {
					return nil, err
				}

				if len(include.Includes) == 0 {
					return nil, errors.New("includes module must have at least one module to include")
				}

				for _, include := range include.Includes {
					var modulePath string

					// in case of a remote include, we need to download the
					// recipe before including it
					if include[:4] == "http" {
						fmt.Printf("Downloading recipe from %s\n", include)
						modulePath, err = downloadRecipe(include)
						if err != nil {
							return nil, err
						}
					} else if followsGhPattern(include) {
						// if the include follows the github pattern, we need to
						// download the recipe from the github repository
						fmt.Printf("Downloading recipe from %s\n", include)
						modulePath, err = downloadGhRecipe(include)
						if err != nil {
							return nil, err
						}
					} else {
						modulePath = filepath.Join(recipe.ParentPath, include)
					}

					includeModule, err := GenModule(modulePath)
					if err != nil {
						return nil, err
					}

					newRecipeModules = append(newRecipeModules, includeModule)
				}

				continue
			}

			newRecipeModules = append(newRecipeModules, moduleInterface)
		}

		stage.Modules = newRecipeModules
		recipe.Stages[i] = stage
	}

	return recipe, nil
}

downloadRecipe function

downloadRecipe downloads a recipe from a remote URL and stores it to

a temporary file

Parameters:

  • url string

Returns:

  • path string
  • err error
Show/Hide Function Body
{
	resp, err := http.Get(url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	tmpFile, err := os.CreateTemp("", "vib-recipe-")
	if err != nil {
		return "", err
	}
	defer tmpFile.Close()

	_, err = io.Copy(tmpFile, resp.Body)
	if err != nil {
		return "", err
	}

	return tmpFile.Name(), nil
}

followsGhPattern function

followsGhPattern checks if a given path follows the pattern:

gh:org/repo:branch:path

Parameters:

  • s string

Returns:

  • bool
Show/Hide Function Body
{
	parts := strings.Split(s, ":")
	if len(parts) != 4 {
		return false
	}

	if parts[0] != "gh" {
		return false
	}

	return true
}

downloadGhRecipe function

downloadGhRecipe downloads a recipe from a github repository and stores it to

a temporary file

Parameters:

  • gh string

Returns:

  • path string
  • err error
Show/Hide Function Body
{
	parts := strings.Split(gh, ":")
	repo := parts[1]
	branch := parts[2]
	file := parts[3]

	url := fmt.Sprintf("https://raw.githubusercontent.com/%s/%s/%s", repo, branch, file)
	return downloadRecipe(url)
}

GenModule function

GenModule generate a Module struct from a module path

Parameters:

  • modulePath string

Returns:

  • map[string]interface{}
  • error
Show/Hide Function Body
{
	var module map[string]interface{}

	moduleFile, err := os.Open(modulePath)
	if err != nil {
		return module, err
	}
	defer moduleFile.Close()

	moduleYAML, err := io.ReadAll(moduleFile)
	if err != nil {
		return module, err
	}

	err = yaml.Unmarshal(moduleYAML, &module)
	if err != nil {
		return module, err
	}

	return module, nil
}

TestRecipe function

TestRecipe validates a recipe by loading it and checking for errors

Parameters:

  • path string

Returns:

  • *api.Recipe
  • error
Show/Hide Function Body
{
	recipe, err := LoadRecipe(path)
	if err != nil {
		fmt.Printf("Error validating recipe: %s\n", err)
		return nil, err
	}

	modules := 0
	for _, stage := range recipe.Stages {
		modules += len(stage.Modules)
	}

	fmt.Printf("Recipe %s validated successfully\n", recipe.Id)
	fmt.Printf("Found %d stages\n", len(recipe.Stages))
	fmt.Printf("Found %d modules\n", modules)
	return recipe, nil
}

ShellModule struct

Configuration for shell modules

Fields:

  • Name (string) - json:"name"
  • Type (string) - json:"type"
  • Sources ([]api.Source)
  • Commands ([]string)

BuildShellModule function

Build shell module commands and return them as a single string

Returns: Concatenated shell commands or an error if any step fails

Parameters:

  • moduleInterface interface{}
  • recipe *api.Recipe

Returns:

  • string
  • error
Show/Hide Function Body
{
	var module ShellModule
	err := mapstructure.Decode(moduleInterface, &module)
	if err != nil {
		return "", err
	}

	for _, source := range module.Sources {
		if strings.TrimSpace(source.Type) != "" {
			err := api.DownloadSource(recipe.DownloadsPath, source, module.Name)
			if err != nil {
				return "", err
			}
			err = api.MoveSource(recipe.DownloadsPath, recipe.SourcesPath, source, module.Name)
			if err != nil {
				return "", err
			}
		}
	}

	if len(module.Commands) == 0 {
		return "", errors.New("no commands specified")
	}

	cmd := ""
	for i, command := range module.Commands {
		cmd += command
		if i < len(module.Commands)-1 {
			cmd += " && "
		}
	}

	return cmd, nil
}

Module struct

Configuration for a module

Fields:

  • Name (string) - json:"name"
  • Workdir (string)
  • Type (string) - json:"type"
  • Modules ([]map[string]interface{})
  • Content ([]byte)

Finalize struct

Configuration for finalization steps

Fields:

  • Name (string) - json:"name"
  • Type (string) - json:"type"
  • Content ([]byte)

IncludesModule struct

Configuration for including other modules or recipes

Fields:

  • Name (string) - json:"name"
  • Type (string) - json:"type"
  • Includes ([]string) - json:"includes"

ModuleCommand struct

Information for building a module

Fields:

  • Name (string)
  • Command (string)
  • Workdir (string)

Plugin struct

Configuration for a plugin

Fields:

  • Name (string)
  • BuildFunc (func(*C.char, *C.char) string)
  • LoadedPlugin (uintptr)

errors import

Import example:

import "errors"

fmt import

Import example:

import "fmt"

os import

Import example:

import "os"

strings import

Import example:

import "strings"

github.com/mitchellh/mapstructure import

Import example:

import "github.com/mitchellh/mapstructure"

github.com/vanilla-os/vib/api import

Import example:

import "github.com/vanilla-os/vib/api"

fmt import

Import example:

import "fmt"

os import

Import example:

import "os"

os/exec import

Import example:

import "os/exec"

syscall import

Import example:

import "syscall"

github.com/mitchellh/mapstructure import

Import example:

import "github.com/mitchellh/mapstructure"

github.com/vanilla-os/vib/api import

Import example:

import "github.com/vanilla-os/vib/api"

fmt import

Import example:

import "fmt"

github.com/containers/storage import

Import example:

import "github.com/containers/storage"

Imported as:

cstorage

os/exec import

Import example:

import "os/exec"

strings import

Import example:

import "strings"

errors import

Import example:

import "errors"

fmt import

Import example:

import "fmt"

io import

Import example:

import "io"

net/http import

Import example:

import "net/http"

os import

Import example:

import "os"

path/filepath import

Import example:

import "path/filepath"

strings import

Import example:

import "strings"

github.com/mitchellh/mapstructure import

Import example:

import "github.com/mitchellh/mapstructure"

github.com/vanilla-os/vib/api import

Import example:

import "github.com/vanilla-os/vib/api"

gopkg.in/yaml.v3 import

Import example:

import "gopkg.in/yaml.v3"

errors import

Import example:

import "errors"

strings import

Import example:

import "strings"

github.com/mitchellh/mapstructure import

Import example:

import "github.com/mitchellh/mapstructure"

github.com/vanilla-os/vib/api import

Import example:

import "github.com/vanilla-os/vib/api"

C import

Import example:

import "C"