Skip to content

Commit

Permalink
REMOVE ME: FIX ARGS
Browse files Browse the repository at this point in the history
Signed-off-by: Cezar Craciunoiu <[email protected]>
  • Loading branch information
craciunoiuc committed Aug 22, 2023
1 parent ad78cba commit fbd7955
Show file tree
Hide file tree
Showing 5 changed files with 299 additions and 20 deletions.
121 changes: 102 additions & 19 deletions cmd/kraft/pkg/pkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package pkg

import (
"context"
"errors"
"fmt"
"os"

Expand Down Expand Up @@ -51,7 +52,7 @@ type Pkg struct {
func New() *cobra.Command {
cmd, err := cmdfactory.New(&Pkg{}, cobra.Command{
Short: "Package and distribute Unikraft unikernels and their dependencies",
Use: "pkg [FLAGS] [SUBCOMMAND|DIR]",
Use: "pkg [FLAGS] [SUBCOMMAND|DIR|PACKAGE]",
Args: cmdfactory.MaxDirArgs(1),
Long: heredoc.Docf(`
Package and distribute Unikraft unikernels and their dependencies.
Expand All @@ -66,7 +67,14 @@ func New() *cobra.Command {
`, "`"),
Example: heredoc.Doc(`
# Package a project as an OCI archive and embed the target's KConfig.
$ kraft pkg --as oci --name unikraft.org/nginx:latest --with-kconfig`),
$ kraft pkg --as oci --name unikraft.org/nginx:latest --with-kconfig
# Package a project as an OCI archive and embed an initrd and arguments.
$ kraft pkg --as oci --name unikraft.org/nginx:latest --initrd ./fs0 --args "-h"
# Package an existing OCI archive and embed a different initrd.
$ kraft pkg --as oci --initrd ./fs0 unikraft.org/nginx:latest
`),
Annotations: map[string]string{
cmdfactory.AnnotationHelpGroup: "pkg",
},
Expand Down Expand Up @@ -105,37 +113,112 @@ func (opts *Pkg) Pre(cmd *cobra.Command, _ []string) error {

func (opts *Pkg) Run(cmd *cobra.Command, args []string) error {
var err error
var workdir string
var source string
var sourcePackage pack.Package
popts := []app.ProjectOption{}

ctx := cmd.Context()
var pmananger packmanager.PackageManager
if opts.Format != "auto" {
pmananger = packmanager.PackageManagers()[pack.PackageFormat(opts.Format)]
if pmananger == nil {
return errors.New("invalid package format specified")
}
} else {
pmananger = packmanager.G(ctx)
}

if len(args) == 0 {
workdir, err = os.Getwd()
source, err = os.Getwd()
if err != nil {
return err
}
popts = append(popts, app.WithProjectWorkdir(source))

fmt.Println("Empty ARGS")
} else {
workdir = args[0]
source = args[0]

fmt.Println(source)

// The provided argument is either a directory or a Kraftfile
if fi, err := os.Stat(args[0]); err == nil && fi.IsDir() {
popts = append(popts, app.WithProjectWorkdir(source))
} else {
fmt.Println(source)
if pm, compatible, err := pmananger.IsCompatible(ctx, source); err == nil && compatible {
packages, err := pm.Catalog(ctx,
packmanager.WithCache(true),
packmanager.WithName(source),
)
if err != nil {
return err
}

if len(packages) == 0 {
return fmt.Errorf("no package found for %s", source)
} else if len(packages) > 1 {
return fmt.Errorf("multiple packages found for %s", source)
}

sourcePackage = packages[0]
}
}
}

ctx := cmd.Context()
var tree []*processtree.ProcessTreeItem
var project app.Application

popts := []app.ProjectOption{
app.WithProjectWorkdir(workdir),
}
if sourcePackage != nil {
cmdShellArgs, err := shellwords.Parse(opts.Args)
if err != nil {
return err
}

if len(opts.Kraftfile) > 0 {
popts = append(popts, app.WithProjectKraftfile(opts.Kraftfile))
tree = append(tree, processtree.NewProcessTreeItem(
sourcePackage.Name(),
sourcePackage.Version(),
func(ctx context.Context) error {
var err error
pm := packmanager.G(ctx)

// Switch the package manager the desired format for this target
if sourcePackage.Format().String() != "auto" {
pm, err = pm.From(sourcePackage.Format())
if err != nil {
return err
}
}

popts := []packmanager.PackOption{
packmanager.PackArgs(cmdShellArgs...),
packmanager.PackInitrd(opts.Initrd),
packmanager.PackKConfig(opts.WithKConfig),
packmanager.PackName(opts.Name),
packmanager.PackOutput(opts.Output),
}

if _, err := pm.Pack(ctx, nil, popts...); err != nil {
return err
}

return nil
},
))
} else {
popts = append(popts, app.WithProjectDefaultKraftfiles())
}
if len(opts.Kraftfile) > 0 {
popts = append(popts, app.WithProjectKraftfile(opts.Kraftfile))
} else {
popts = append(popts, app.WithProjectDefaultKraftfiles())
}

// Interpret the project directory
project, err := app.NewProjectFromOptions(ctx, popts...)
if err != nil {
return err
// Interpret the project directory
project, err = app.NewProjectFromOptions(ctx, popts...)
if err != nil {
return err
}
}

var tree []*processtree.ProcessTreeItem

parallel := !config.G[config.KraftKit](ctx).NoParallel
norender := log.LoggerTypeFromString(config.G[config.KraftKit](ctx).Log.Type) != log.FANCY

Expand Down
2 changes: 1 addition & 1 deletion cmdfactory/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ func New(obj Runnable, cmd cobra.Command) (*cobra.Command, error) {
c.RunE = obj.Run

// Parse the attributes of this object into addressable flags for this command
if err := AttributeFlags(&c, obj); err != nil {
if err := AttributeFlags(&c, obj, c.ValidArgs...); err != nil {
return nil, err
}
}
Expand Down
20 changes: 20 additions & 0 deletions oci/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,26 @@ func (manager *ociManager) Update(ctx context.Context) error {

// Pack implements packmanager.PackageManager
func (manager *ociManager) Pack(ctx context.Context, entity component.Component, opts ...packmanager.PackOption) ([]pack.Package, error) {

// If the provided entity is empty, then check the options
if entity == nil {
popts := &packmanager.PackOptions{}
for _, opt := range opts {
opt(popts)
}

if popts.Source() != nil {
pkg, err := NewPackageFromOCIPackage(ctx, opts...)
if err != nil {
return nil, err
}

return []pack.Package{pkg}, nil
} else {
return nil, fmt.Errorf("cannot repack OCI package without source")
}
}

targ, ok := entity.(target.Target)
if !ok {
return nil, fmt.Errorf("entity is not Unikraft target")
Expand Down
161 changes: 161 additions & 0 deletions oci/pack.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,167 @@ func NewPackageFromTarget(ctx context.Context, targ target.Target, opts ...packm
return &ocipack, nil
}

// NewPackageFromOCIPackage generates a package from a supplied OCI image package.
func NewPackageFromOCIPackage(ctx context.Context, opts ...packmanager.PackOption) (pack.Package, error) {
var err error

popts := &packmanager.PackOptions{}
for _, opt := range opts {
opt(popts)
}

ocipack := popts.Source().(*ociPackage)

if popts.Args() != nil {
ocipack.command = popts.Args()
}

if popts.Output() != "" {
ocipack.ref, err = name.ParseReference(
popts.Output(),
name.WithDefaultRegistry(DefaultRegistry),
)
} else {
if popts.Name() != "" {
ocipack.ref, err = name.ParseReference(
popts.Name(),
name.WithDefaultRegistry(DefaultRegistry),
)
}
}
if err != nil {
return nil, err
}

if contAddr := config.G[config.KraftKit](ctx).ContainerdAddr; len(contAddr) > 0 {
namespace := DefaultNamespace
if n := os.Getenv("CONTAINERD_NAMESPACE"); n != "" {
namespace = n
}

log.G(ctx).WithFields(logrus.Fields{
"addr": contAddr,
"namespace": namespace,
}).Debug("oci: packaging via containerd")

ctx, ocipack.handle, err = handler.NewContainerdHandler(ctx, contAddr, namespace)
} else {
if gerr := os.MkdirAll(config.G[config.KraftKit](ctx).RuntimeDir, fs.ModeSetgid|0o775); gerr != nil {
return nil, fmt.Errorf("could not create local oci cache directory: %w", gerr)
}

ociDir := filepath.Join(config.G[config.KraftKit](ctx).RuntimeDir, "oci")

log.G(ctx).WithFields(logrus.Fields{
"path": ociDir,
}).Trace("oci: directory handler")

ocipack.handle, err = handler.NewDirectoryHandler(ociDir)
}
if err != nil {
return nil, err
}

// TODO: Remove the existing reference if a --force-remove|--overwrite flag is
// provided (which should then translate into an PackOptions attribute).
// existingDesc, err := handle.Resolve(ctx, ocipack.Name())
// if err == nil && popts.Overwrite() && existingDesc.MediaType == ocispec.MediaTypeImageManifest {
// reader, err := handle.Fetch(ctx, existingDesc)
// if err != nil {
// return nil, err
// }
//
// log.G(ctx).WithFields(logrus.Fields{
// "tag": ocipack.Name(),
// }).Warn("removing existing reference")
//
// // TODO: Remove the manifest descriptor
//
// }

// If the new kernel version is not empty it means that the user wants a new kernel so refuse
// to continue repackaging
var image *Image
if popts.KernelVersion() == "" {
image = ocipack.image

// If an initrd exists and needs to be replaced, then we need to remove the old one
// We remove it directly because we want to keep the old layers intact
if popts.Initrd() != "" && len(image.Layers()) > 1 {
for i, layer := range image.Layers() {
if _, ok := layer.blob.desc.Annotations[AnnotationKernelInitrdPath]; ok {
image.layers = append(image.Layers()[:i], image.Layers()[i+1:]...)
break
}
}
}
} else {
return nil, fmt.Errorf("can't replace kernel when repackaging")
}

// If a new initrd is provided we need to remove the old one if it exists and then replace it
// Otherwise we don't need to do anything
if popts.Initrd() != "" {
log.G(ctx).Debug("oci: including initrd")
initRdPath := popts.Initrd()
if f, err := os.Stat(initRdPath); err == nil && f.IsDir() {
cwd, err2 := os.Getwd()
if err2 != nil {
return nil, err2
}

file, err := os.CreateTemp("", "kraftkit-oci-archive-*")
if err != nil {
return nil, err
}
defer os.Remove(file.Name())

cfg, err2 := initrd.NewFromMapping(cwd,
file.Name(),
fmt.Sprintf("%s:/", initRdPath))
if err2 != nil {
return nil, err2
}

initRdPath = cfg.Output
}
layer, err := NewLayerFromFile(ctx,
ocispec.MediaTypeImageLayer,
initRdPath,
WellKnownInitrdPath,
WithLayerAnnotation(AnnotationKernelInitrdPath, WellKnownInitrdPath),
)
if err != nil {
return nil, err
}

if _, err := image.AddLayer(ctx, layer); err != nil {
return nil, err
}
}

image.SetAnnotation(ctx, AnnotationName, ocipack.Name())
image.SetAnnotation(ctx, AnnotationVersion, ocipack.ref.Identifier())
image.SetAnnotation(ctx, AnnotationKraftKitVersion, kraftkitversion.Version())

image.SetCmd(ctx, ocipack.Command())
image.SetOS(ctx, ocipack.Platform().Name())
image.SetArchitecture(ctx, ocipack.Architecture().Name())

log.G(ctx).WithFields(logrus.Fields{
"tag": ocipack.Name(),
}).Debug("oci: saving image")

_, err = image.Save(ctx, ocipack.imageRef(), nil)
if err != nil {
return nil, err
}

ocipack.image = image

return ocipack, nil
}

// NewPackageFromOCIManifestSpec generates a package from a supplied OCI image
// manifest specification.
func NewPackageFromOCIManifestSpec(ctx context.Context, handle handler.Handler, ref string, manifest ocispec.Manifest) (pack.Package, error) {
Expand Down
Loading

0 comments on commit fbd7955

Please sign in to comment.