Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
github.com/anchore/stereoscope v0.3.0
github.com/anchore/syft v1.51.0
github.com/avast/retry-go/v4 v4.7.0
github.com/containerd/containerd/v2 v2.3.3
github.com/defenseunicorns/pkg/helpers/v2 v2.0.4
github.com/defenseunicorns/pkg/oci v1.3.2
github.com/derailed/k9s v0.51.0
Expand All @@ -31,6 +32,7 @@ require (
github.com/golang-cz/devslog v0.0.17
github.com/google/go-containerregistry v0.21.9
github.com/invopop/jsonschema v0.14.0
github.com/klauspost/compress v1.19.1
github.com/mholt/archives v0.1.5
github.com/moby/moby/client v0.5.1
github.com/opencontainers/image-spec v1.1.1
Expand Down Expand Up @@ -110,7 +112,6 @@ require (
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect
github.com/containerd/cgroups/v3 v3.1.3 // indirect
github.com/containerd/containerd/api v1.11.1 // indirect
github.com/containerd/containerd/v2 v2.3.3 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/platforms v1.0.0-rc.4 // indirect
Expand Down Expand Up @@ -485,7 +486,6 @@ require (
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.19.1 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f // indirect
github.com/knqyf263/go-deb-version v0.0.0-20241115132648-6f4aee6ccd23 // indirect
Expand Down
1 change: 1 addition & 0 deletions site/src/content/docs/commands/zarf_dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Commands useful for developing packages
* [zarf dev generate](/commands/zarf_dev_generate/) - Creates a zarf.yaml automatically from a given remote (git) Helm chart
* [zarf dev generate-config](/commands/zarf_dev_generate-config/) - Generates a config file for Zarf
* [zarf dev generate-schema](/commands/zarf_dev_generate-schema/) - Generates a JSON schema for Zarf values based on the package definition, chart defaults, and chart schemas
* [zarf dev image-volume-archive](/commands/zarf_dev_image-volume-archive/) - Creates an image archive from a given directory with a given image reference
* [zarf dev inspect](/commands/zarf_dev_inspect/) - Commands to gather information about a Zarf package using its package definition
* [zarf dev lint](/commands/zarf_dev_lint/) - Lints the given package for valid schema and recommended practices
* [zarf dev patch-git](/commands/zarf_dev_patch-git/) - Converts all .git URLs to the specified Zarf HOST and with the Zarf URL pattern in a given FILE. NOTE:
Expand Down
44 changes: 44 additions & 0 deletions site/src/content/docs/commands/zarf_dev_image-volume-archive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
title: zarf dev image-volume-archive
description: Zarf CLI command reference for <code>zarf dev image-volume-archive</code>.
tableOfContents: false
---

<!-- Page generated by Zarf; DO NOT EDIT -->

## zarf dev image-volume-archive

Creates an image archive from a given directory with a given image reference

```
zarf dev image-volume-archive [ DIRECTORY ] [ IMAGE-REFERENCE ] [flags]
```

### Options

```
-h, --help help for image-volume-archive
-c, --layer-compression string Compression algorthm used on the individual layers of the image volume (default "gzip")
-m, --max-layers uint8 Cap on the number of layers in the resulting image; files are batched across fewer layers as needed to stay under it. 0 disables the cap. (default 127)
-O, --output string Path to write the resulting tar archive to (default derived from IMAGE-REFERENCE)
-o, --platform-os string Operating system of the image volume (default "linux")
```

### Options inherited from parent commands

```
-a, --architecture string Architecture for OCI images and Zarf packages
--cache string Specify the location of the Zarf cache directory (default "~/.zarf-cache")
--features stringToString Provide a comma-separated list of feature names to bools to enable or disable. Ex. --features "foo=true,bar=false,baz=true" (default [])
--insecure-skip-tls-verify Skip checking server's certificate for validity. This flag should only be used if you have a specific reason and accept the reduced security posture.
--log-format string Select a logging format. Defaults to 'console'. Valid options are: 'console', 'json', 'dev'. (default "console")
-l, --log-level string Log level when running Zarf. Valid options are: warn, info, debug, trace (default "info")
--no-color Disable terminal color codes in logging and stdout prints.
--plain-http Allow OCI registry connections over HTTP instead of HTTPS. This flag should only be used if you have a specific reason and accept the reduced security posture.
--tmpdir string Specify the temporary directory to use for intermediate files
```

### SEE ALSO

* [zarf dev](/commands/zarf_dev/) - Commands useful for developing packages

66 changes: 66 additions & 0 deletions src/cmd/cobra/dev.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2021-Present The Zarf Authors

package cobra

import (
"fmt"

"github.com/zarf-dev/zarf/src/pkg/zoci/image"
)

// gzipCobraDesc, zstdCobraDesc, and uncompressedCobraDesc are the shell
// completion descriptions shown alongside each VolumeCompression value.
const (
gzipCobraDesc = "gzip-compressed layers"
zstdCobraDesc = "zstd-compressed layers"
uncompressedCobraDesc = "uncompressed layers"
)

// GetDevIVACobraCompression returns the valid VolumeCompression values as
// "value\tdescription" pairs, ready to return from a cobra
// RegisterFlagCompletionFunc callback for shell tab completion.
func GetDevIVACobraCompression() []string {
return []string{
fmt.Sprintf("%s\t%s", string(image.VolumeCompressionGzip), gzipCobraDesc),
fmt.Sprintf("%s\t%s", string(image.VolumeCompressionZstd), zstdCobraDesc),
fmt.Sprintf("%s\t%s", string(image.VolumeCompressionUncompressed), uncompressedCobraDesc),
}
}

// unlimitedMaxLayersDesc and defaultMaxLayersDesc are the shell completion
// descriptions shown alongside the suggested MaxLayers values.
const (
unlimitedMaxLayersDesc = "unlimited (disables the cap)"
defaultMaxLayersDesc = "default cap"
)

// GetDevIVACobraMaxLayers returns a short list of sensible MaxLayers values as
// "value\tdescription" pairs, ready to return from a cobra
// RegisterFlagCompletionFunc callback for shell tab completion. Unlike
// GetCobraCompression/GetCobraPlatformOS this isn't an exhaustive set of
// valid values - MaxLayers accepts any uint8 - just the ones worth
// suggesting.
func GetDevIVACobraMaxLayers() []string {
return []string{
fmt.Sprintf("%d\t%s", image.UnlimiteLayers, unlimitedMaxLayersDesc),
fmt.Sprintf("%d\t%s", image.DefaultMaxLayers, defaultMaxLayersDesc),
}
}

// osLinuxCobraDesc and osWindowCobraDesc are the shell completion
// descriptions shown alongside each PlatformOS value.
const (
osLinuxCobraDesc = "linux image volume"
osWindowCobraDesc = "windows image volume"
)

// GetDevIVACobraPlatformOS returns the valid PlatformOS values as
// "value\tdescription" pairs, ready to return from a cobra
// RegisterFlagCompletionFunc callback for shell tab completion.
func GetDevIVACobraPlatformOS() []string {
return []string{
fmt.Sprintf("%s\t%s", string(image.PlatformOSLinux), osLinuxCobraDesc),
fmt.Sprintf("%s\t%s", string(image.PlatformOSWindows), osWindowCobraDesc),
}
}
30 changes: 30 additions & 0 deletions src/cmd/cobra/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2021-Present The Zarf Authors

// Package cobra holds shell tab-completion helpers shared by Zarf's cobra
// commands, returning "value\tdescription" pairs for use in
// cobra.RegisterFlagCompletionFunc callbacks.
package cobra

import (
"fmt"

"github.com/zarf-dev/zarf/src/config"
)

// osArchAMD64Desc and osArchARM64Desc are the shell completion descriptions
// shown alongside each supported --architecture value.
const (
osArchAMD64Desc = "the x86-64, 64-bit AMD, architecture"
osArchARM64Desc = "the 64-bit ARM architecture"
)

// GetRootArchitectureCobraCompression returns the valid --architecture
// values as "value\tdescription" pairs, ready to return from a cobra
// RegisterFlagCompletionFunc callback for shell tab completion.
func GetRootArchitectureCobraCompression() []string {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Realized that this is a bad function name.... I will update it soon

return []string{
fmt.Sprintf("%s\t%s", string(config.OSArchAMD64), osArchAMD64Desc),
fmt.Sprintf("%s\t%s", string(config.OSArchARM64), osArchARM64Desc),
}
}
2 changes: 2 additions & 0 deletions src/cmd/dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/spf13/viper"
"github.com/zarf-dev/zarf/src/api/v1alpha1"
"github.com/zarf-dev/zarf/src/api/v1beta1"
"github.com/zarf-dev/zarf/src/cmd/dev"
"github.com/zarf-dev/zarf/src/config"
"github.com/zarf-dev/zarf/src/config/lang"
"github.com/zarf-dev/zarf/src/internal/packager/helm"
Expand Down Expand Up @@ -82,6 +83,7 @@ func newDevCommand() *cobra.Command {
cmd.AddCommand(newDevLintCommand(v))
cmd.AddCommand(newDevUpgradeSchemaCommand())
cmd.AddCommand(newDevTemplateCommand(v))
cmd.AddCommand(dev.NewImageVolumeCommand())

return cmd
}
Expand Down
145 changes: 145 additions & 0 deletions src/cmd/dev/iva.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2021-Present The Zarf Authors

// Package dev contains dev Zarf CLI commands.
package dev

import (
"errors"
"os"

"github.com/spf13/cobra"
zcobra "github.com/zarf-dev/zarf/src/cmd/cobra"
"github.com/zarf-dev/zarf/src/config"
"github.com/zarf-dev/zarf/src/config/lang"
"github.com/zarf-dev/zarf/src/pkg/logger"
"github.com/zarf-dev/zarf/src/pkg/utils"
"github.com/zarf-dev/zarf/src/pkg/zoci/archive"
"github.com/zarf-dev/zarf/src/pkg/zoci/image"
)

const (
flagLayerCompression = "layer-compression"
flagPlatformOS = "platform-os"
flagMaxLayer = "max-layers"
)

type imageVolumeOptions struct {
compression image.VolumeCompression
os image.PlatformOS
output string
maxLayers uint8
}

// NewImageVolumeCommand creates the command to build an image volume
// archive from a directory of files.
func NewImageVolumeCommand() *cobra.Command {
o := &imageVolumeOptions{}

cmd := &cobra.Command{
Use: lang.CmdDevImageVolumeArchiveUsage,
Aliases: []string{"iva"},
Short: lang.CmdDevImageVolumeArchiveShort,
Args: cobra.ExactArgs(2),
PreRunE: o.prerun,
RunE: o.run,
}

cmd.Flags().StringVarP((*string)(&o.compression), flagLayerCompression, "c", string(image.VolumeCompressionGzip), lang.CmdDevImageVolumeArchiveFlagCompression)
cmd.Flags().StringVarP((*string)(&o.os), flagPlatformOS, "o", string(image.PlatformOSLinux), lang.CmdDevImageVolumeArchiveFlagPlatformOS)
cmd.Flags().StringVarP(&o.output, "output", "O", "", lang.CmdDevImageVolumeArchiveFlagOutput)
cmd.Flags().Uint8VarP(&o.maxLayers, flagMaxLayer, "m", image.DefaultMaxLayers, lang.CmdDevImageVolumeArchiveFlagMaxLayer)

if err := cmd.RegisterFlagCompletionFunc(flagLayerCompression, func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return zcobra.GetDevIVACobraCompression(), cobra.ShellCompDirectiveNoFileComp
}); err != nil {
logger.From(cmd.Context()).Warn("failed to register out-complete", "error", err)
panic(err)
}

if err := cmd.RegisterFlagCompletionFunc(flagPlatformOS, func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return zcobra.GetDevIVACobraPlatformOS(), cobra.ShellCompDirectiveNoFileComp
}); err != nil {
logger.From(cmd.Context()).Warn("failed to register out-complete", "error", err)
panic(err)
}

if err := cmd.RegisterFlagCompletionFunc(flagMaxLayer, func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return zcobra.GetDevIVACobraMaxLayers(), cobra.ShellCompDirectiveNoFileComp
}); err != nil {
logger.From(cmd.Context()).Warn("failed to register out-complete", "error", err)
panic(err)
}

return cmd
}

// prerun validates the compression format in the image volume options,
// defaulting to uncompressed if not specified.
//
// _ *cobra.Command, args []string.
// error.
func (o *imageVolumeOptions) prerun(_ *cobra.Command, args []string) error {
if o.output == "" {
o.output = archive.ImageRefToTar(args[1])
}
return errors.Join(
archive.ValidateFileEndsWithTar(o.output),
image.ValidateCompression(o.compression),
image.ValidatePlatformOS(o.os),
image.ValidatePlatformArch(image.PlatformArch(config.GetArch())),
)
}

// run builds an image volume from args[0] (the source directory) tagged as
// args[1] (the image reference), then writes it to o.output as a
// Docker/OCI-compatible tar archive.
//
// cmd *cobra.Command, args []string.
// error.
func (o *imageVolumeOptions) run(cmd *cobra.Command, args []string) error {
dir := args[0]
ref := args[1]

tmpDir, err := utils.MakeTempDir(config.CommonOptions.TempDirectory)
if err != nil {
return err
}

iv, err := image.New(tmpDir, string(o.os), config.GetArch())
if err != nil {
return err
}
defer func() {
if err := iv.Clean(); err != nil {
logger.From(cmd.Context()).Debug("failed to clean image volume workspace", "error", err)
}
if err := os.RemoveAll(tmpDir); err != nil {
logger.From(cmd.Context()).Debug("failed to remove staging directory", "error", err)
}
}()

iv.Compression = o.compression
iv.MaxLayers = o.maxLayers

if err := iv.AddDirectory(cmd.Context(), dir, ref); err != nil {
return err
}

out, err := os.Create(o.output)
if err != nil {
return err
}
defer func() {
if closeErr := out.Close(); closeErr != nil {
logger.From(cmd.Context()).Debug("failed to close image volume archive", "error", closeErr)
}
}()

if err := iv.WriteTar(cmd.Context(), ref, out); err != nil {
return err
}

logger.From(cmd.Context()).Info("wrote image volume archive", "path", o.output)
return nil
}
Loading