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: uds dev deploy #536

Merged
merged 23 commits into from
Apr 4, 2024
Merged
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
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
1. [Bundle Overrides](docs/overrides.md)
1. [Bundle Anatomy](docs/anatomy.md)
1. [Runner](docs/runner.md)
1. [Dev Mode](#dev-mode)

## Install
Recommended installation method is with Brew:
Expand Down Expand Up @@ -130,7 +131,7 @@ As an example: `uds remove uds-bundle-<name>.tar.zst --packages init,nginx`

### Logs

> [!NOTE]
> Note:
> Only works with `uds deploy` for now, may work for other operations but isn't guaranteed.

The `uds logs` command can be used to view the most recent logs of a bundle operation. Note that depending on your OS temporary directory and file settings, recent logs are purged after a certain amount of time, so this command may return an error if the logs are no longer available.
Expand Down Expand Up @@ -223,3 +224,8 @@ That is to say, variables set using the `--set` flag take precedence over all ot

## Zarf Integration
UDS CLI includes a vendored version of Zarf inside of its binary. To use Zarf, simply run `uds zarf <command>`. For example, to create a Zarf package, run `uds zarf create <dir>`, or to use the [airgap tooling](https://docs.zarf.dev/docs/the-zarf-cli/cli-commands/zarf_tools) that Zarf provides, run `uds zarf tools <cmd>`.

## Dev Mode
Dev mode allows you to speed up dev cycles when developing and testing bundles. `uds dev deploy` allows you to deploy a UDS bundle in dev mode. If you are missing a local zarf package, this command will create that zarf package for you assuming that your `zarf.yaml` file and zarf package are expected in the same directory. It will then create your bundle and deploy your zarf packages in [YOLO](https://docs.zarf.dev/docs/faq#what-is-yolo-mode-and-why-would-i-use-it) mode, eliminating the need to do a `zarf init`

> Note: currently dev mode only works with local bundles
101 changes: 101 additions & 0 deletions src/cmd/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023-Present The UDS Authors

// Package cmd contains the CLI commands for UDS.
package cmd

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

tea "github.com/charmbracelet/bubbletea"
"github.com/defenseunicorns/uds-cli/src/config"
"github.com/defenseunicorns/uds-cli/src/config/lang"
"github.com/defenseunicorns/uds-cli/src/pkg/bundle"
"github.com/defenseunicorns/uds-cli/src/pkg/bundle/tui/deploy"
"github.com/defenseunicorns/uds-cli/src/pkg/utils"
zarfConfig "github.com/defenseunicorns/zarf/src/config"
"github.com/defenseunicorns/zarf/src/pkg/message"
"github.com/defenseunicorns/zarf/src/pkg/utils/helpers"
zarfTypes "github.com/defenseunicorns/zarf/src/types"
"github.com/spf13/cobra"
)

// configureZarf copies configs from UDS-CLI to Zarf
func configureZarf() {
zarfConfig.CommonOptions = zarfTypes.ZarfCommonOptions{
Insecure: config.CommonOptions.Insecure,
TempDirectory: config.CommonOptions.TempDirectory,
OCIConcurrency: config.CommonOptions.OCIConcurrency,
Confirm: config.CommonOptions.Confirm,
CachePath: config.CommonOptions.CachePath, // use uds-cache instead of zarf-cache
}
}

func deployWithoutTea(bndlClient *bundle.Bundle) {
_, _, _, err := bndlClient.PreDeployValidation()
if err != nil {
message.Fatalf(err, "Failed to validate bundle: %s", err.Error())
}
// confirm deployment
if ok := bndlClient.ConfirmBundleDeploy(); !ok {
message.Fatal(nil, "bundle deployment cancelled")
}
// create an empty program and kill it, this makes Program.Send a no-op
deploy.Program = tea.NewProgram(nil)
deploy.Program.Kill()

// deploy the bundle
if err := bndlClient.Deploy(); err != nil {
bndlClient.ClearPaths()
message.Fatalf(err, "Failed to deploy bundle: %s", err.Error())
}
}

func setBundleFile(args []string) {
pathToBundleFile := ""
if len(args) > 0 {
if !helpers.IsDir(args[0]) {
message.Fatalf(nil, "(%q) is not a valid path to a directory", args[0])
}
pathToBundleFile = filepath.Join(args[0])
}
// Handle .yaml or .yml
bundleYml := strings.Replace(config.BundleYAML, ".yaml", ".yml", 1)
if _, err := os.Stat(filepath.Join(pathToBundleFile, config.BundleYAML)); err == nil {
bundleCfg.CreateOpts.BundleFile = config.BundleYAML
} else if _, err = os.Stat(filepath.Join(pathToBundleFile, bundleYml)); err == nil {
bundleCfg.CreateOpts.BundleFile = bundleYml
} else {
message.Fatalf(err, "Neither %s or %s found", config.BundleYAML, bundleYml)
}
}

func cliSetup(cmd *cobra.Command) {
match := map[string]message.LogLevel{
"warn": message.WarnLevel,
"info": message.InfoLevel,
"debug": message.DebugLevel,
"trace": message.TraceLevel,
}

printViperConfigUsed()

// No log level set, so use the default
if logLevel != "" {
if lvl, ok := match[logLevel]; ok {
message.SetLogLevel(lvl)
message.Debug("Log level set to " + logLevel)
} else {
message.Warn(lang.RootCmdErrInvalidLogLevel)
}
}

if !config.SkipLogFile && !config.ListTasks {
err := utils.ConfigureLogs(cmd)
if err != nil {
message.Fatalf(err, "Error configuring logs")
}
}
}
81 changes: 81 additions & 0 deletions src/cmd/dev.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023-Present The UDS Authors

// Package cmd contains the CLI commands for UDS.
package cmd

import (
"os"

"github.com/defenseunicorns/uds-cli/src/config"
"github.com/defenseunicorns/uds-cli/src/config/lang"
"github.com/defenseunicorns/zarf/src/pkg/message"

"github.com/defenseunicorns/uds-cli/src/pkg/bundle"
"github.com/spf13/cobra"
)

var devCmd = &cobra.Command{
decleaver marked this conversation as resolved.
Show resolved Hide resolved
Use: "dev",
Short: lang.CmdDevShort,
}

var devDeployCmd = &cobra.Command{
Use: "deploy",
Args: cobra.MaximumNArgs(1),
Short: lang.CmdDevDeployShort,
PreRun: func(_ *cobra.Command, args []string) {
setBundleFile(args)
},
Run: func(_ *cobra.Command, args []string) {

// Create Bundle
srcDir, err := os.Getwd()
if err != nil {
message.Fatalf(err, "error reading the current working directory")
}
if len(args) > 0 {
srcDir = args[0]
}

if len(srcDir) != 0 && srcDir[len(srcDir)-1] != '/' {
srcDir = srcDir + "/"
}

config.CommonOptions.Confirm = true
bundleCfg.CreateOpts.SourceDirectory = srcDir
configureZarf()

// load uds-config if it exists
if v.ConfigFileUsed() != "" {
if err := loadViperConfig(); err != nil {
message.Fatalf(err, "Failed to load uds-config: %s", err.Error())
return
}
}

bndlClient := bundle.NewOrDie(&bundleCfg)
defer bndlClient.ClearPaths()

// Check if local zarf packages need to be created
bndlClient.CreateZarfPkgs()

// Create dev bundle
config.Dev = true
if err := bndlClient.Create(); err != nil {
decleaver marked this conversation as resolved.
Show resolved Hide resolved
bndlClient.ClearPaths()
message.Fatalf(err, "Failed to create bundle: %s", err.Error())
}

// Deploy dev bundle
bndlClient.SetDevSource(srcDir)

deployWithoutTea(bndlClient)
},
}

func init() {
initViper()
rootCmd.AddCommand(devCmd)
devCmd.AddCommand(devDeployCmd)
}
31 changes: 1 addition & 30 deletions src/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (

"github.com/defenseunicorns/uds-cli/src/config"
"github.com/defenseunicorns/uds-cli/src/config/lang"
"github.com/defenseunicorns/uds-cli/src/pkg/utils"
"github.com/defenseunicorns/uds-cli/src/types"
"github.com/defenseunicorns/zarf/src/cmd/common"
zarfCommon "github.com/defenseunicorns/zarf/src/cmd/common"
Expand Down Expand Up @@ -42,7 +41,7 @@ var rootCmd = &cobra.Command{

// don't load log configs for the logs command
if cmd.Use != "logs" {
cliSetup(cmd.Use)
cliSetup(cmd)
}
},
Short: lang.RootCmdShort,
Expand Down Expand Up @@ -91,31 +90,3 @@ func init() {
rootCmd.PersistentFlags().IntVar(&config.CommonOptions.OCIConcurrency, "oci-concurrency", v.GetInt(V_BNDL_OCI_CONCURRENCY), lang.CmdBundleFlagConcurrency)
rootCmd.PersistentFlags().BoolVar(&config.CommonOptions.NoTea, "no-tea", v.GetBool(V_NO_TEA), lang.RootCmdNoTea)
}

func cliSetup(op string) {
match := map[string]message.LogLevel{
"warn": message.WarnLevel,
"info": message.InfoLevel,
"debug": message.DebugLevel,
"trace": message.TraceLevel,
}

printViperConfigUsed()

// No log level set, so use the default
if logLevel != "" {
if lvl, ok := match[logLevel]; ok {
message.SetLogLevel(lvl)
message.Debug("Log level set to " + logLevel)
} else {
message.Warn(lang.RootCmdErrInvalidLogLevel)
}
}

if !config.SkipLogFile && !config.ListTasks {
err := utils.ConfigureLogs(op)
if err != nil {
message.Fatalf(err, "Error configuring logs")
}
}
}
51 changes: 1 addition & 50 deletions src/cmd/uds.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@ import (
"github.com/defenseunicorns/uds-cli/src/config/lang"
"github.com/defenseunicorns/uds-cli/src/pkg/bundle"
"github.com/defenseunicorns/uds-cli/src/pkg/bundle/tui/deploy"
zarfConfig "github.com/defenseunicorns/zarf/src/config"
"github.com/defenseunicorns/zarf/src/pkg/message"
"github.com/defenseunicorns/zarf/src/pkg/utils/helpers"
zarfTypes "github.com/defenseunicorns/zarf/src/types"
goyaml "github.com/goccy/go-yaml"
"github.com/spf13/cobra"
"golang.org/x/term"
Expand All @@ -33,22 +30,7 @@ var createCmd = &cobra.Command{
Args: cobra.MaximumNArgs(1),
Short: lang.CmdBundleCreateShort,
PreRun: func(_ *cobra.Command, args []string) {
pathToBundleFile := ""
if len(args) > 0 {
if !helpers.IsDir(args[0]) {
message.Fatalf(nil, "(%q) is not a valid path to a directory", args[0])
}
pathToBundleFile = filepath.Join(args[0])
}
// Handle .yaml or .yml
bundleYml := strings.Replace(config.BundleYAML, ".yaml", ".yml", 1)
if _, err := os.Stat(filepath.Join(pathToBundleFile, config.BundleYAML)); err == nil {
bundleCfg.CreateOpts.BundleFile = config.BundleYAML
} else if _, err = os.Stat(filepath.Join(pathToBundleFile, bundleYml)); err == nil {
bundleCfg.CreateOpts.BundleFile = bundleYml
} else {
message.Fatalf(err, "Neither %s or %s found", config.BundleYAML, bundleYml)
}
setBundleFile(args)
},
Run: func(_ *cobra.Command, args []string) {
srcDir, err := os.Getwd()
Expand Down Expand Up @@ -301,17 +283,6 @@ func init() {
rootCmd.AddCommand(logsCmd)
}

// configureZarf copies configs from UDS-CLI to Zarf
func configureZarf() {
zarfConfig.CommonOptions = zarfTypes.ZarfCommonOptions{
Insecure: config.CommonOptions.Insecure,
TempDirectory: config.CommonOptions.TempDirectory,
OCIConcurrency: config.CommonOptions.OCIConcurrency,
Confirm: config.CommonOptions.Confirm,
CachePath: config.CommonOptions.CachePath, // use uds-cache instead of zarf-cache
}
}

// chooseBundle provides a file picker when users don't specify a file
func chooseBundle(args []string) string {
if len(args) > 0 {
Expand All @@ -337,23 +308,3 @@ func chooseBundle(args []string) string {

return path
}

func deployWithoutTea(bndlClient *bundle.Bundle) {
_, _, _, err := bndlClient.PreDeployValidation()
if err != nil {
message.Fatalf(err, "Failed to validate bundle: %s", err.Error())
}
// confirm deployment
if ok := bndlClient.ConfirmBundleDeploy(); !ok {
message.Fatal(nil, "bundle deployment cancelled")
}
// create an empty program and kill it, this makes Program.Send a no-op
deploy.Program = tea.NewProgram(nil)
deploy.Program.Kill()

// deploy the bundle
if err := bndlClient.Deploy(); err != nil {
bndlClient.ClearPaths()
message.Fatalf(err, "Failed to deploy bundle: %s", err.Error())
}
}
4 changes: 2 additions & 2 deletions src/cmd/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import (
var versionCmd = &cobra.Command{
Use: "version",
Aliases: []string{"v"},
PersistentPreRun: func(_ *cobra.Command, _ []string) {
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
config.SkipLogFile = true
cliSetup("")
cliSetup(cmd)
},
Short: lang.CmdVersionShort,
Long: lang.CmdVersionLong,
Expand Down
3 changes: 3 additions & 0 deletions src/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ var (

// HelmTimeout is the default timeout for helm deploys
HelmTimeout = 15 * time.Minute

// Dev specifies if we are running in dev mode
Dev = false
)

// GetArch returns the arch based on a priority list with options for overriding.
Expand Down
4 changes: 4 additions & 0 deletions src/config/lang/lang.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,8 @@ const (

// uds zarf
CmdZarfShort = "Run a zarf command"

// uds dev
CmdDevShort = "Commands useful for developing bundles"
CmdDevDeployShort = "Creates and deploys a dev UDS bundle from a given directory"
)
Loading
Loading