Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: package generation ALPHA #2269

Merged
merged 61 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
32e7db3
super barebones package generation
corang Oct 14, 2022
4871fee
skeleton of generate command
corang Oct 18, 2022
4ee13c0
file rename
corang Oct 18, 2022
1f30a6b
minimal package creation and editing
corang Oct 18, 2022
7a17721
refactor of current generation
corang Oct 19, 2022
96ecdd8
barebones component generation
corang Oct 19, 2022
58a8f06
going to try this selector route
corang Oct 19, 2022
364b439
better description
corang Oct 20, 2022
4d66c22
initial cleanup and refactor
corang Oct 20, 2022
c43b1a5
improving zarf's decision making abilities
corang Oct 20, 2022
25a1a6f
should be able to tell what kind of "from" it is
corang Oct 21, 2022
87dd8eb
working localchart generation
corang Oct 21, 2022
95ca528
manifest generation!
corang Oct 21, 2022
88f1ec9
better manifest/kustomize support
corang Oct 21, 2022
20ad09f
minor cleanup and localFiles
corang Nov 2, 2022
b44f1e9
refactor/cleanup
corang Nov 2, 2022
7ed8997
sloppy but working git chart support
corang Nov 2, 2022
6cbcb60
helm repo chart generation done
corang Nov 5, 2022
13be236
prettying up and minor refactors, feature complete!
corang Nov 5, 2022
5fab2a2
refactoring and bug fixing
corang Nov 5, 2022
1ff816e
make sure package name is valid
corang Nov 14, 2022
605870c
logic around naming and writing zarf.yaml fixed
corang Nov 14, 2022
08be44d
some image gen working
corang Nov 14, 2022
dc5ed69
make --assume fully assume everything again
corang Nov 14, 2022
f0c2988
I wish you could nest spinners...
corang Nov 14, 2022
ae07bf6
chore: move dev generate command
andrewg-xyz Feb 9, 2024
ccda556
validate generate flags
andrewg-xyz Feb 9, 2024
3695cf9
fix: unit tests
andrewg-xyz Feb 9, 2024
68404be
generated docs
andrewg-xyz Feb 9, 2024
30274ca
basic generate
andrewg-xyz Feb 10, 2024
ba776a3
unused file
andrewg-xyz Feb 10, 2024
099f916
updates
andrewg-xyz Feb 23, 2024
18caa7a
docs: update
andrewg-xyz Feb 23, 2024
e71df3c
remove unused
andrewg-xyz Feb 23, 2024
7a8c81b
cleanup logic errors
andrewg-xyz Feb 27, 2024
d4928fb
docs update
andrewg-xyz Feb 27, 2024
904ec67
add kube-version override flag
andrewg-xyz Feb 27, 2024
3f66ee6
refactor code
andrewg-xyz Feb 27, 2024
93e07fe
update lang/english
andrewg-xyz Feb 27, 2024
e15a7fb
update test
andrewg-xyz Feb 27, 2024
7db9d03
update docs
andrewg-xyz Feb 27, 2024
a7b29ef
remove extra debug messages
andrewg-xyz Feb 27, 2024
956d7d8
typo
andrewg-xyz Feb 27, 2024
185780e
you just HAD to think you were smart
andrewg-xyz Feb 27, 2024
ffe3b45
refactor directory operations
andrewg-xyz Feb 27, 2024
21359eb
linting errors
andrewg-xyz Feb 28, 2024
4f84b61
replace validate with cobra utility
andrewg-xyz Feb 28, 2024
d5decac
remove unused objects
andrewg-xyz Feb 28, 2024
c9d727d
cleanup
andrewg-xyz Feb 28, 2024
0c3f666
remove OBE tests
andrewg-xyz Feb 28, 2024
ca906af
cleanup
andrewg-xyz Feb 29, 2024
768c0cd
docs update
andrewg-xyz Feb 29, 2024
5260a37
fix imports after merge
andrewg-xyz Mar 8, 2024
8697da4
address comments
andrewg-xyz Mar 8, 2024
2313b30
a little simpler me thinks
Noxsios Mar 13, 2024
929780f
lint
Noxsios Mar 13, 2024
b4fe761
more dry
Noxsios Mar 13, 2024
99361d6
tiny nit fix
Noxsios Mar 13, 2024
021f106
Merge branch 'main' into 821-package-generation
Noxsios Mar 13, 2024
4f99d91
docs and schema
Noxsios Mar 13, 2024
c2d07d0
make output-directory required
andrewg-xyz Mar 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/2-the-zarf-cli/100-cli-commands/zarf_dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Commands useful for developing packages
* [zarf](zarf.md) - DevSecOps for Airgap
* [zarf dev deploy](zarf_dev_deploy.md) - [beta] Creates and deploys a Zarf package from a given directory
* [zarf dev find-images](zarf_dev_find-images.md) - Evaluates components in a Zarf file to identify images specified in their helm charts and manifests
* [zarf dev generate](zarf_dev_generate.md) - [alpha] Creates a zarf.yaml automatically from a given remote (git) Helm chart
* [zarf dev generate-config](zarf_dev_generate-config.md) - Generates a config file for Zarf
* [zarf dev lint](zarf_dev_lint.md) - Lints the given package for valid schema and recommended practices
* [zarf dev patch-git](zarf_dev_patch-git.md) - Converts all .git URLs to the specified Zarf HOST and with the Zarf URL pattern in a given FILE. NOTE:
Expand Down
42 changes: 42 additions & 0 deletions docs/2-the-zarf-cli/100-cli-commands/zarf_dev_generate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# zarf dev generate
<!-- Auto-generated by hack/gen-cli-docs.sh -->

[alpha] Creates a zarf.yaml automatically from a given remote (git) Helm chart

```
zarf dev generate NAME [flags]
```

## Examples

```
zarf dev generate podinfo --url https://github.com/stefanprodan/podinfo.git --version 6.4.0 --gitPath charts/podinfo
```

## Options

```
--gitPath string Relative path to the chart in the git repository
-h, --help help for generate
--kube-version string Override the default helm template KubeVersion when performing a package chart template
--output-directory string Output directory for the generated zarf.yaml
--url string URL to the source git repository
--version string The Version of the chart to use
```

## Options inherited from parent commands

```
-a, --architecture string Architecture for OCI images and Zarf packages
--insecure Allow access to insecure registries and disable other recommended security enforcements such as package checksum and signature validation. This flag should only be used if you have a specific reason and accept the reduced security posture.
-l, --log-level string Log level when running Zarf. Valid options are: warn, info, debug, trace (default "info")
--no-color Disable colors in output
--no-log-file Disable log file creation
--no-progress Disable fancy UI progress bars, spinners, logos, etc
--tmpdir string Specify the temporary directory to use for intermediate files
--zarf-cache string Specify the location of the Zarf cache directory (default "~/.zarf-cache")
```

## SEE ALSO

* [zarf dev](zarf_dev.md) - Commands useful for developing packages
38 changes: 37 additions & 1 deletion src/cmd/dev.go
andrewg-xyz marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,27 @@ var devDeployCmd = &cobra.Command{
},
}

var devGenerateCmd = &cobra.Command{
Use: "generate NAME",
Aliases: []string{"g"},
Args: cobra.ExactArgs(1),
Short: lang.CmdDevGenerateShort,
Example: lang.CmdDevGenerateExample,
Run: func(_ *cobra.Command, args []string) {
pkgConfig.GenerateOpts.Name = args[0]

pkgConfig.CreateOpts.BaseDir = "."
pkgConfig.FindImagesOpts.RepoHelmChartPath = pkgConfig.GenerateOpts.GitPath

pkgClient := packager.NewOrDie(&pkgConfig)
defer pkgClient.ClearTempPaths()

if err := pkgClient.Generate(); err != nil {
message.Fatalf(err, err.Error())
}
},
}

var devTransformGitLinksCmd = &cobra.Command{
Use: "patch-git HOST FILE",
Aliases: []string{"p"},
Expand Down Expand Up @@ -186,7 +207,6 @@ var devFindImagesCmd = &cobra.Command{
Short: lang.CmdDevFindImagesShort,
Long: lang.CmdDevFindImagesLong,
Run: func(_ *cobra.Command, args []string) {
// If a directory was provided, use that as the base directory
common.SetBaseDirectory(args, &pkgConfig)

// Ensure uppercase keys from viper
Expand Down Expand Up @@ -256,13 +276,15 @@ func init() {
rootCmd.AddCommand(devCmd)

devCmd.AddCommand(devDeployCmd)
devCmd.AddCommand(devGenerateCmd)
devCmd.AddCommand(devTransformGitLinksCmd)
devCmd.AddCommand(devSha256SumCmd)
devCmd.AddCommand(devFindImagesCmd)
devCmd.AddCommand(devGenConfigFileCmd)
devCmd.AddCommand(devLintCmd)

bindDevDeployFlags(v)
bindDevGenerateFlags(v)

devSha256SumCmd.Flags().StringVarP(&extractPath, "extract-path", "e", "", lang.CmdDevFlagExtractPath)

Expand Down Expand Up @@ -307,3 +329,17 @@ func bindDevDeployFlags(v *viper.Viper) {

devDeployFlags.BoolVar(&pkgConfig.CreateOpts.NoYOLO, "no-yolo", v.GetBool(common.VDevDeployNoYolo), lang.CmdDevDeployFlagNoYolo)
}

func bindDevGenerateFlags(_ *viper.Viper) {
generateFlags := devGenerateCmd.Flags()

generateFlags.StringVar(&pkgConfig.GenerateOpts.URL, "url", "", "URL to the source git repository")
generateFlags.StringVar(&pkgConfig.GenerateOpts.Version, "version", "", "The Version of the chart to use")
generateFlags.StringVar(&pkgConfig.GenerateOpts.GitPath, "gitPath", "", "Relative path to the chart in the git repository")
generateFlags.StringVar(&pkgConfig.GenerateOpts.Output, "output-directory", "", "Output directory for the generated zarf.yaml")
generateFlags.StringVar(&pkgConfig.FindImagesOpts.KubeVersionOverride, "kube-version", "", lang.CmdDevFlagKubeVersion)

devGenerateCmd.MarkFlagRequired("url")
devGenerateCmd.MarkFlagRequired("version")
devGenerateCmd.MarkFlagRequired("output-directory")
}
7 changes: 6 additions & 1 deletion src/config/lang/english.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
// Alternative languages can be created by duplicating this file and changing the build tag to "//go:build alt_language && <language>".
package lang

import "errors"
import (
"errors"
)

// All language strings should be in the form of a constant
// The constants should be grouped by the top level package they are used in (or common)
Expand Down Expand Up @@ -357,6 +359,9 @@ $ zarf package pull oci://ghcr.io/defenseunicorns/packages/dos-games:1.0.0 -a sk
CmdDevDeployFlagNoYolo = "Disable the YOLO mode default override and create / deploy the package as-defined"
CmdDevDeployErr = "Failed to dev deploy: %s"

CmdDevGenerateShort = "[alpha] Creates a zarf.yaml automatically from a given remote (git) Helm chart"
CmdDevGenerateExample = "zarf dev generate podinfo --url https://github.com/stefanprodan/podinfo.git --version 6.4.0 --gitPath charts/podinfo"

CmdDevPatchGitShort = "Converts all .git URLs to the specified Zarf HOST and with the Zarf URL pattern in a given FILE. NOTE:\n" +
"This should only be used for manifests that are not mutated by the Zarf Agent Mutating Webhook."
CmdDevPatchGitOverwritePrompt = "Overwrite the file %s with these changes?"
Expand Down
102 changes: 102 additions & 0 deletions src/pkg/packager/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2021-Present The Zarf Authors

// Package packager contains functions for interacting with, managing and deploying Zarf packages.
package packager

import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/defenseunicorns/zarf/src/config"
"github.com/defenseunicorns/zarf/src/internal/packager/validate"
"github.com/defenseunicorns/zarf/src/pkg/layout"
"github.com/defenseunicorns/zarf/src/pkg/message"
"github.com/defenseunicorns/zarf/src/pkg/utils"
"github.com/defenseunicorns/zarf/src/pkg/utils/helpers"
"github.com/defenseunicorns/zarf/src/types"
goyaml "github.com/goccy/go-yaml"
)

// Generate generates a Zarf package definition.
func (p *Packager) Generate() (err error) {
generatedZarfYAMLPath := filepath.Join(p.cfg.GenerateOpts.Output, layout.ZarfYAML)
spinner := message.NewProgressSpinner("Generating package for %q at %s", p.cfg.GenerateOpts.Name, generatedZarfYAMLPath)

if !utils.InvalidPath(generatedZarfYAMLPath) {
prefixed := filepath.Join(p.cfg.GenerateOpts.Output, fmt.Sprintf("%s-%s", p.cfg.GenerateOpts.Name, layout.ZarfYAML))

message.Warnf("%s already exists, writing to %s", generatedZarfYAMLPath, prefixed)

generatedZarfYAMLPath = prefixed

if !utils.InvalidPath(generatedZarfYAMLPath) {
return fmt.Errorf("unable to generate package, %s already exists", generatedZarfYAMLPath)
}
}

generatedComponent := types.ZarfComponent{
Name: p.cfg.GenerateOpts.Name,
Required: true,
Charts: []types.ZarfChart{
{
Name: p.cfg.GenerateOpts.Name,
Version: p.cfg.GenerateOpts.Version,
Namespace: p.cfg.GenerateOpts.Name,
URL: p.cfg.GenerateOpts.URL,
GitPath: p.cfg.GenerateOpts.GitPath,
},
},
}

p.cfg.Pkg = types.ZarfPackage{
Kind: types.ZarfPackageConfig,
Metadata: types.ZarfMetadata{
Name: p.cfg.GenerateOpts.Name,
Version: p.cfg.GenerateOpts.Version,
Description: "auto-generated using `zarf dev generate`",
},
Components: []types.ZarfComponent{
generatedComponent,
},
}
p.arch = config.GetArch()

images, err := p.findImages()
if err != nil {
// purposefully not returning error here, as we can still generate the package without images
message.Warnf("Unable to find images: %s", err.Error())
}

for i := range p.cfg.Pkg.Components {
name := p.cfg.Pkg.Components[i].Name
p.cfg.Pkg.Components[i].Images = images[name]
}

if err := validate.Run(p.cfg.Pkg); err != nil {
return err
}

if err := utils.CreateDirectory(p.cfg.GenerateOpts.Output, helpers.ReadExecuteAllWriteUser); err != nil {
return err
}

b, err := goyaml.MarshalWithOptions(p.cfg.Pkg, goyaml.IndentSequence(true), goyaml.UseSingleQuote(false))
if err != nil {
return err
}

schemaComment := fmt.Sprintf("# yaml-language-server: $schema=https://raw.githubusercontent.com/%s/%s/zarf.schema.json", config.GithubProject, config.CLIVersion)
content := schemaComment + "\n" + string(b)

// lets space things out a bit
content = strings.Replace(content, "kind:\n", "\nkind:\n", 1)
content = strings.Replace(content, "metadata:\n", "\nmetadata:\n", 1)
content = strings.Replace(content, "components:\n", "\ncomponents:\n", 1)

spinner.Successf("Generated package for %q at %s", p.cfg.GenerateOpts.Name, generatedZarfYAMLPath)

return os.WriteFile(generatedZarfYAMLPath, []byte(content), helpers.ReadAllWriteUser)
}
37 changes: 20 additions & 17 deletions src/pkg/packager/prepare.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,29 +38,38 @@ type imageMap map[string]bool

// FindImages iterates over a Zarf.yaml and attempts to parse any images.
func (p *Packager) FindImages() (imgMap map[string][]string, err error) {
repoHelmChartPath := p.cfg.FindImagesOpts.RepoHelmChartPath
kubeVersionOverride := p.cfg.FindImagesOpts.KubeVersionOverride
whyImage := p.cfg.FindImagesOpts.Why

imagesMap := make(map[string][]string)
erroredCharts := []string{}
erroredCosignLookups := []string{}
whyResources := []string{}

cwd, err := os.Getwd()
if err != nil {
return nil, err
}

defer func() {
// Return to the original working directory
if err := os.Chdir(cwd); err != nil {
message.Warnf("Unable to return to the original working directory: %s", err.Error())
}
}()
if err := os.Chdir(p.cfg.CreateOpts.BaseDir); err != nil {
andrewg-xyz marked this conversation as resolved.
Show resolved Hide resolved
return nil, fmt.Errorf("unable to access directory '%s': %w", p.cfg.CreateOpts.BaseDir, err)
return nil, fmt.Errorf("unable to access directory %q: %w", p.cfg.CreateOpts.BaseDir, err)
}
message.Note(fmt.Sprintf("Using build directory %s", p.cfg.CreateOpts.BaseDir))

if err = p.readZarfYAML(layout.ZarfYAML); err != nil {
return nil, fmt.Errorf("unable to read the zarf.yaml file: %w", err)
}

return p.findImages()
}

func (p *Packager) findImages() (imgMap map[string][]string, err error) {
repoHelmChartPath := p.cfg.FindImagesOpts.RepoHelmChartPath
kubeVersionOverride := p.cfg.FindImagesOpts.KubeVersionOverride
whyImage := p.cfg.FindImagesOpts.Why

imagesMap := make(map[string][]string)
erroredCharts := []string{}
erroredCosignLookups := []string{}
whyResources := []string{}

if err := p.composeComponents(); err != nil {
return nil, err
}
Expand Down Expand Up @@ -90,7 +99,6 @@ func (p *Packager) FindImages() (imgMap map[string][]string, err error) {
}

for _, component := range p.cfg.Pkg.Components {

if len(component.Charts)+len(component.Manifests)+len(component.Repos) < 1 {
// Skip if it doesn't have what we need
continue
Expand Down Expand Up @@ -342,11 +350,6 @@ func (p *Packager) FindImages() (imgMap map[string][]string, err error) {

fmt.Println(componentDefinition)

// Return to the original working directory
if err := os.Chdir(cwd); err != nil {
return nil, err
}

if len(erroredCharts) > 0 || len(erroredCosignLookups) > 0 {
errMsg := ""
if len(erroredCharts) > 0 {
Expand Down
39 changes: 39 additions & 0 deletions src/test/e2e/13_zarf_package_generate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2021-Present The Zarf Authors

// Package test provides e2e tests for Zarf.
package test

import (
"path/filepath"
"testing"

"github.com/defenseunicorns/zarf/src/pkg/layout"
"github.com/defenseunicorns/zarf/src/pkg/utils"
"github.com/defenseunicorns/zarf/src/types"
"github.com/stretchr/testify/require"
)

func TestZarfDevGenerate(t *testing.T) {
t.Log("E2E: Zarf Dev Generate")

t.Run("Test generate podinfo", func(t *testing.T) {
tmpDir := t.TempDir()

url := "https://github.com/stefanprodan/podinfo.git"
version := "6.4.0"
gitPath := "charts/podinfo"

stdOut, stdErr, err := e2e.Zarf("dev", "generate", "podinfo", "--url", url, "--version", version, "--gitPath", gitPath, "--output-directory", tmpDir)
require.NoError(t, err, stdOut, stdErr)

zarfPackage := types.ZarfPackage{}
packageLocation := filepath.Join(tmpDir, layout.ZarfYAML)
err = utils.ReadYaml(packageLocation, &zarfPackage)
require.NoError(t, err)
require.Equal(t, zarfPackage.Components[0].Charts[0].URL, url)
require.Equal(t, zarfPackage.Components[0].Charts[0].Version, version)
require.Equal(t, zarfPackage.Components[0].Charts[0].GitPath, gitPath)
require.NotEmpty(t, zarfPackage.Components[0].Images)
})
}
3 changes: 3 additions & 0 deletions src/types/packager.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ type PackagerConfig struct {
// FindImagesOpts tracks user-defined options used to find images
FindImagesOpts ZarfFindImagesOptions

// GenerateOpts tracks user-defined values for package generation.
GenerateOpts ZarfGenerateOptions

// The package data
Pkg ZarfPackage

Expand Down
9 changes: 9 additions & 0 deletions src/types/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@ type ZarfPullOptions struct {
OutputDirectory string `json:"outputDirectory" jsonschema:"description=Location where the pulled Zarf package will be placed"`
}

// ZarfGenerateOptions tracks the user-defined options during package generation.
type ZarfGenerateOptions struct {
Name string `json:"name" jsonschema:"description=Name of the package being generated"`
URL string `json:"url" jsonschema:"description=URL to the source git repository"`
Version string `json:"version" jsonschema:"description=Version of the chart to use"`
GitPath string `json:"gitPath" jsonschema:"description=Relative path to the chart in the git repository"`
Output string `json:"output" jsonschema:"description=Location where the finalized zarf.yaml will be placed"`
}

// ZarfInitOptions tracks the user-defined options during cluster initialization.
type ZarfInitOptions struct {
// Zarf init is installing the k3s component
Expand Down