Skip to content

Commit

Permalink
feat: uds dev deploy (#536)
Browse files Browse the repository at this point in the history
Co-authored-by: UncleGedd <42304551+UncleGedd@users.noreply.github.com>
  • Loading branch information
decleaver and UncleGedd committed Apr 4, 2024
1 parent 393954a commit 217d029
Show file tree
Hide file tree
Showing 15 changed files with 373 additions and 87 deletions.
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{
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 {
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

0 comments on commit 217d029

Please sign in to comment.