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: dev deploy --ref and --flavor flags #638

Merged
merged 37 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
4c18547
flavor and ref updates
decleaver May 21, 2024
31e6d7c
Merge branch 'main' into 537-dev-deploy-refs
decleaver May 22, 2024
8d36284
check refs and flavors on create, for local bundles
decleaver May 26, 2024
c2ff9b1
add test yamls
decleaver May 26, 2024
b0e7afe
schema update and fix flavor tag typo in test
decleaver May 26, 2024
7b56d29
delete debug ci yml
decleaver May 27, 2024
1ed528d
Merge branch 'main' into 537-dev-deploy-refs
decleaver May 29, 2024
f5aa28b
Merge branch 'main' into 537-dev-deploy-refs
decleaver May 29, 2024
ae513bb
moves ref check from create to deploy
decleaver May 29, 2024
f8e4d23
Merge branch 'main' into 537-dev-deploy-refs
decleaver May 29, 2024
65bfb5a
testing updates
decleaver May 30, 2024
fed1aec
fix flavor test
decleaver May 30, 2024
7089551
update documentation
decleaver May 31, 2024
13cbccc
Merge branch 'main' into 537-dev-deploy-refs
decleaver Jun 3, 2024
672df73
fix error messaging
decleaver Jun 3, 2024
5273a77
addressing pr comments
decleaver Jun 3, 2024
6951177
consolidate flavor and flavor-all flags
decleaver Jun 4, 2024
fdfa4cb
fix flavor bug
decleaver Jun 4, 2024
17b7724
refactor to remove DevDeployRefs from config
decleaver Jun 4, 2024
a365356
fix bug regarding RemoveOpts.source
decleaver Jun 4, 2024
50c4c24
adding unit tests and confirmation on all bundle types
decleaver Jun 4, 2024
4f05eff
review updates
decleaver Jun 4, 2024
858463c
add test fixes this time
decleaver Jun 5, 2024
7982151
test fix
decleaver Jun 5, 2024
ebedfac
remove extra space causing test to fail... smh
decleaver Jun 5, 2024
68ba0f1
Merge branch 'main' into 537-dev-deploy-refs
decleaver Jun 5, 2024
ee0b2e9
clarity is kindness
decleaver Jun 5, 2024
35e5b9f
fullsend no more confirm, update hack/push-test-artifacts.sh
decleaver Jun 6, 2024
5c9b865
fullersend
decleaver Jun 6, 2024
9d72026
add error messaging for ref and flavor flags on incompatible sources
decleaver Jun 6, 2024
102e099
split up bundles and packages for flavor tests
decleaver Jun 6, 2024
72a0d38
Merge branch 'main' into 537-dev-deploy-refs
decleaver Jun 6, 2024
c7564a6
better docs?
decleaver Jun 7, 2024
e2db54e
Update README.md
decleaver Jun 7, 2024
f56e628
Update README.md
decleaver Jun 7, 2024
d7abcf6
fix docs
decleaver Jun 7, 2024
97b44e0
Update README.md
decleaver Jun 7, 2024
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: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,9 @@ The `dev deploy` command performs the following operations

- If local bundle: Creates Zarf packages for all local packages in a bundle
- Creates the Zarf tarball in the same directory as the `zarf.yaml`
- Will only create the Zarf tarball if one does not already exist
- Can use `--flavor` flag to specify what flavor of package you want to create
- Will only create the Zarf tarball if one does not already exist or can use `--force-create` to force the creation of a new zarf package even if one currently exists
- Ignores any `kind: ZarfInitConfig` packages in the bundle
- Creates a bundle from the newly created Zarf packages
- Deploys the bundle in [YOLO](https://docs.zarf.dev/faq/#what-is-yolo-mode-and-why-would-i-use-it) mode, eliminating the need to do a `zarf init`
- For remote packages you can specify what package ref you want to deploy by using the `--ref` flag and specifying the package name and desired ref. (example: `--ref podinfo=0.2.0`)
67 changes: 59 additions & 8 deletions src/cmd/dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
package cmd

import (
"fmt"
"strings"

"github.com/defenseunicorns/pkg/helpers/v2"
"github.com/defenseunicorns/uds-cli/src/config"
"github.com/defenseunicorns/uds-cli/src/config/lang"
Expand Down Expand Up @@ -34,13 +37,24 @@ var devDeployCmd = &cobra.Command{
}

// Check if source is a local bundle
localBundle := helpers.IsDir(src)
isLocalBundle := isLocalBundle(src)

// Validate flags
err := validateDevDeployFlags(isLocalBundle)
if err != nil {
message.Fatalf(err, "Failed to validate flags: %s", err.Error())
}

if isLocalBundle {
// Populate flavor map
err = populateFlavorMap()
if err != nil {
message.Fatalf(err, "Failed to populate flavor map: %s", err.Error())
}

if localBundle {
// Create Bundle
setBundleFile(args)

config.CommonOptions.Confirm = true
bundleCfg.CreateOpts.SourceDirectory = src
}

Expand All @@ -58,17 +72,13 @@ var devDeployCmd = &cobra.Command{
defer bndlClient.ClearPaths()

// Create dev bundle
if localBundle {
if isLocalBundle {
// Check if local zarf packages need to be created
bndlClient.CreateZarfPkgs()

if err := bndlClient.Create(); err != nil {
message.Fatalf(err, "Failed to create bundle: %s", err.Error())
}
}

// Set dev source
if localBundle {
bndlClient.SetDeploySource(src)
} else {
bundleCfg.DeployOpts.Source = src
Expand All @@ -79,11 +89,52 @@ var devDeployCmd = &cobra.Command{
},
}

// isLocalBundle checks if the bundle source is a local bundle
func isLocalBundle(src string) bool {
return helpers.IsDir(src) || strings.Contains(src, ".tar.zst")
}

// validateDevDeployFlags validates the flags for dev deploy
func validateDevDeployFlags(isLocalBundle bool) error {
if !isLocalBundle {
//Throw error if trying to run with --flavor or --force-create flag with remote bundle
if len(bundleCfg.DevDeployOpts.Flavor) > 0 || bundleCfg.DevDeployOpts.ForceCreate {
return fmt.Errorf("Cannot use --flavor or --force-create flags with remote bundle")
}
}
return nil
}

// populateFlavorMap populates the flavor map based on the string input to the --flavor flag
func populateFlavorMap() error {
if bundleCfg.DevDeployOpts.FlavorInput != "" {
bundleCfg.DevDeployOpts.Flavor = make(map[string]string)
flavorEntries := strings.Split(bundleCfg.DevDeployOpts.FlavorInput, ",")
for i, entry := range flavorEntries {
entrySplit := strings.Split(entry, "=")
if len(entrySplit) != 2 {
// check i==0 to check for invalid input (ex. key=value1,value2)
if len(entrySplit) == 1 && i == 0 {
bundleCfg.DevDeployOpts.Flavor = map[string]string{"": bundleCfg.DevDeployOpts.FlavorInput}
} else {
return fmt.Errorf("Invalid flavor entry: %s", entry)
}
} else {
bundleCfg.DevDeployOpts.Flavor[entrySplit[0]] = entrySplit[1]
}
}
}
return nil
}

func init() {
initViper()
rootCmd.AddCommand(devCmd)
devCmd.AddCommand(devDeployCmd)
devDeployCmd.Flags().StringArrayVarP(&bundleCfg.DeployOpts.Packages, "packages", "p", []string{}, lang.CmdBundleDeployFlagPackages)
devDeployCmd.Flags().StringToStringVarP(&bundleCfg.DevDeployOpts.Ref, "ref", "r", map[string]string{}, lang.CmdBundleDeployFlagRef)
devDeployCmd.Flags().StringVarP(&bundleCfg.DevDeployOpts.FlavorInput, "flavor", "f", "", lang.CmdBundleCreateFlagFlavor)
devDeployCmd.Flags().BoolVar(&bundleCfg.DevDeployOpts.ForceCreate, "force-create", false, lang.CmdBundleCreateForceCreate)
devDeployCmd.Flags().BoolVarP(&config.CommonOptions.Confirm, "confirm", "c", false, lang.CmdBundleDeployFlagConfirm)
devDeployCmd.Flags().StringToStringVar(&bundleCfg.DeployOpts.SetVariables, "set", nil, lang.CmdBundleDeployFlagSet)
}
142 changes: 142 additions & 0 deletions src/cmd/dev_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package cmd

import (
"testing"

"github.com/defenseunicorns/uds-cli/src/types"
"github.com/stretchr/testify/require"
)

func TestValidateDevDeployFlags(t *testing.T) {
testCases := []struct {
name string
localBundle bool
DevDeployOpts types.BundleDevDeployOptions
expectError bool
}{
{
name: "Local bundle with --ref flag",
localBundle: true,
DevDeployOpts: types.BundleDevDeployOptions{
Ref: map[string]string{"some-key": "some-ref"},
},
expectError: true,
},
{
name: "Remote bundle with --ref flag",
localBundle: false,
DevDeployOpts: types.BundleDevDeployOptions{
Ref: map[string]string{"some-key": "some-ref"},
},
expectError: false,
},
{
name: "Local bundle with --flavor flag",
localBundle: true,
DevDeployOpts: types.BundleDevDeployOptions{
Flavor: map[string]string{"some-key": "some-flavor"},
},
expectError: false,
},
{
name: "Remote bundle with --flavor flag",
localBundle: false,
DevDeployOpts: types.BundleDevDeployOptions{
Flavor: map[string]string{"some-key": "some-flavor"},
},
expectError: true,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
bundleCfg.DevDeployOpts = tc.DevDeployOpts

err := validateDevDeployFlags(tc.localBundle)
if tc.expectError {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}

func TestIsLocalBundle(t *testing.T) {
testCases := []struct {
name string
src string
want bool
}{
{
name: "Test with directory",
src: "../cmd/",
want: true,
},
{
name: "Test with .tar.zst file",
src: "/path/to/file.tar.zst",
want: true,
},
{
name: "Test with other file",
src: "/path/to/file.txt",
want: false,
},
{
name: "Test with registry",
src: "ghcr.io/defenseunicorns/uds-cli/nginx",
want: false,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got := isLocalBundle(tc.src)
require.Equal(t, tc.want, got)
})
}
}

func TestPopulateFlavorMap(t *testing.T) {
testCases := []struct {
name string
FlavorInput string
expect map[string]string
expectError bool
}{
{
name: "Test with valid flavor input",
FlavorInput: "key1=value1,key2=value2",
expect: map[string]string{"key1": "value1", "key2": "value2"},
},
{
name: "Test with single value",
FlavorInput: "value1",
expect: map[string]string{"": "value1"},
},
{
name: "Test with invalid flavor input",
FlavorInput: "key1=value1,key2",
expectError: true,
},
{
name: "Test with empty flavor input",
FlavorInput: "",
expect: nil,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
bundleCfg.DevDeployOpts.FlavorInput = tc.FlavorInput
bundleCfg.DevDeployOpts.Flavor = nil
err := populateFlavorMap()
if tc.expectError {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, tc.expect, bundleCfg.DevDeployOpts.Flavor)
}
})
}
}
9 changes: 6 additions & 3 deletions src/config/lang/lang.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const (
CmdBundleCreateFlagOutput = "Specify the output (an oci:// URL) for the created bundle"
CmdBundleCreateFlagSigningKey = "Path to private key file for signing bundles"
CmdBundleCreateFlagSigningKeyPassword = "Password to the private key file used for signing bundles"
CmdBundleCreateFlagFlavor = "Specify which zarf package flavor you want to use."

// bundle deploy
CmdBundleDeployShort = "Deploy a bundle from a local tarball or oci:// URL"
Expand All @@ -37,6 +38,7 @@ const (
CmdBundleDeployFlagResume = "Only deploys packages from the bundle which haven't already been deployed"
CmdBundleDeployFlagSet = "Specify deployment variables to set on the command line (KEY=value)"
CmdBundleDeployFlagRetries = "Specify the number of retries for package deployments (applies to all pkgs in a bundle)"
CmdBundleDeployFlagRef = "Specify which zarf package ref you want to deploy. By default the ref set in the bundle yaml is used."

// bundle inspect
CmdBundleInspectShort = "Display the metadata of a bundle"
Expand Down Expand Up @@ -81,7 +83,8 @@ const (
CmdZarfShort = "Run a zarf command"

// uds dev
CmdDevShort = "Commands useful for developing bundles"
CmdDevDeployShort = "[beta] Creates and deploys a UDS bundle from a given directory in dev mode"
CmdDevDeployLong = "[beta] Creates and deploys a UDS bundle from a given directory in dev mode, setting package options like YOLO mode for faster iteration."
CmdDevShort = "Commands useful for developing bundles"
CmdDevDeployShort = "[beta] Creates and deploys a UDS bundle from a given directory in dev mode"
CmdDevDeployLong = "[beta] Creates and deploys a UDS bundle from a given directory in dev mode, setting package options like YOLO mode for faster iteration."
CmdBundleCreateForceCreate = "For local bundles with local packages, specify whether to create a zarf package even if it already exists."
)
29 changes: 29 additions & 0 deletions src/pkg/bundle/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,3 +344,32 @@ func validateBundleVars(packages []types.Package) error {
}
return nil
}

// setPackageRef sets the package reference
func (b *Bundle) setPackageRef(pkg types.Package) types.Package {
if ref, ok := b.cfg.DevDeployOpts.Ref[pkg.Name]; ok {
errMsg := fmt.Sprintf("Unable to access %s:%s", pkg.Repository, ref)

// Get SHA from registry
url := fmt.Sprintf("%s:%s", pkg.Repository, ref)

platform := ocispec.Platform{
Architecture: config.GetArch(),
OS: oci.MultiOS,
}
remote, err := zoci.NewRemote(url, platform)
if err != nil {
message.Fatalf(err, errMsg)
}
if err := remote.Repo().Reference.ValidateReferenceAsDigest(); err != nil {
manifestDesc, err := remote.ResolveRoot(context.TODO())
if err != nil {
message.Fatalf(err, errMsg)
}
pkg.Ref = ref + "@sha256:" + manifestDesc.Digest.Encoded()
} else {
message.Fatalf(err, errMsg)
}
}
return pkg
}
14 changes: 9 additions & 5 deletions src/pkg/bundle/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,6 @@ func (b *Bundle) Create() error {
return err
}

// Populate values from valuesFiles if provided
if err := b.processValuesFiles(); err != nil {
return err
}

// confirm creation
if ok := b.confirmBundleCreation(); !ok {
return fmt.Errorf("bundle creation cancelled")
Expand Down Expand Up @@ -86,13 +81,22 @@ func (b *Bundle) Create() error {
}
}

// for dev mode update package ref for local bundles, refs for remote bundles updated on deploy
decleaver marked this conversation as resolved.
Show resolved Hide resolved
if config.Dev && len(b.cfg.DevDeployOpts.Ref) != 0 {
for i, pkg := range b.bundle.Packages {
pkg = b.setPackageRef(pkg)
b.bundle.Packages[i] = pkg
}
}

opts := bundler.Options{
Bundle: &b.bundle,
Output: b.cfg.CreateOpts.Output,
TmpDstDir: b.tmp,
SourceDir: b.cfg.CreateOpts.SourceDirectory,
}
bundlerClient := bundler.NewBundler(&opts)

return bundlerClient.Create()
}

Expand Down
15 changes: 10 additions & 5 deletions src/pkg/bundle/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ func (b *Bundle) Deploy() error {
if len(userSpecifiedPackages) != len(packagesToDeploy) {
return fmt.Errorf("invalid zarf packages specified by --packages")
}
return deployPackages(packagesToDeploy, resume, b)
} else {
packagesToDeploy = b.bundle.Packages
UncleGedd marked this conversation as resolved.
Show resolved Hide resolved
}

return deployPackages(b.bundle.Packages, resume, b)
return deployPackages(packagesToDeploy, resume, b)
}

func deployPackages(packages []types.Package, resume bool, b *Bundle) error {
Expand All @@ -78,7 +78,12 @@ func deployPackages(packages []types.Package, resume bool, b *Bundle) error {
}

// deploy each package
for _, pkg := range packagesToDeploy {
for i, pkg := range packagesToDeploy {
// for dev mode update package ref for remote bundles, refs for local bundles updated on create
if config.Dev && !strings.Contains(b.cfg.DeployOpts.Source, "tar.zst") {
pkg = b.setPackageRef(pkg)
b.bundle.Packages[i] = pkg
}
sha := strings.Split(pkg.Ref, "@sha256:")[1] // using appended SHA from create!
pkgTmp, err := utils.MakeTempDir(config.CommonOptions.TempDirectory)
if err != nil {
Expand Down Expand Up @@ -125,7 +130,7 @@ func deployPackages(packages []types.Package, resume bool, b *Bundle) error {
// Automatically confirm the package deployment
zarfConfig.CommonOptions.Confirm = true

source, err := sources.New(b.cfg.DeployOpts.Source, pkg, opts, sha, nsOverrides)
source, err := sources.New(*b.cfg, pkg, opts, sha, nsOverrides)
if err != nil {
return err
}
Expand Down
Loading
Loading