Skip to content

Commit

Permalink
refactor: Installer
Browse files Browse the repository at this point in the history
Creating a single installer for all the components, including the
"template" subcommand using dry-run mode.
  • Loading branch information
otaviof committed Jun 18, 2024
1 parent 20cbbd3 commit b7045cb
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 126 deletions.
140 changes: 137 additions & 3 deletions pkg/installer/installer.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,151 @@
package installer

import (
"context"
"fmt"
"log/slog"
"os"

"github.com/redhat-appstudio/rhtap-cli/pkg/chartfs"
"github.com/redhat-appstudio/rhtap-cli/pkg/config"
"github.com/redhat-appstudio/rhtap-cli/pkg/deployer"
"github.com/redhat-appstudio/rhtap-cli/pkg/engine"
"github.com/redhat-appstudio/rhtap-cli/pkg/flags"
"github.com/redhat-appstudio/rhtap-cli/pkg/hooks"
"github.com/redhat-appstudio/rhtap-cli/pkg/k8s"
"github.com/redhat-appstudio/rhtap-cli/pkg/printer"
"helm.sh/helm/v3/pkg/chartutil"
)

type Installer struct {
cfg *config.Spec // installer configuration
logger *slog.Logger // application logger
flags *flags.Flags // global flags
kube *k8s.Kube // kubernetes client
dep *config.Dependency // dependency to install
cfs *chartfs.ChartFS // chart file system

valuesBytes []byte // rendered values
values chartutil.Values // helm chart values
}

// prepareHelmClient prepares the Helm client for the given dependency, which also
// specifies the default namespace for the Helm Chart.
func (i *Installer) prepareHelmClient() (*deployer.Helm, error) {
i.logger.Debug("Loading dependency Helm chart (from CFS)")
chart, err := i.cfs.GetChartForDep(i.dep)
if err != nil {
return nil, err
}

i.logger.Debug("Loading Helm client for dependency and namespace")
return deployer.NewHelm(i.logger, i.flags, i.kube, i.dep.Namespace, chart)
}

// SetValues prepares the values template for the Helm chart installation.
func (i *Installer) SetValues(
ctx context.Context,
cfg *config.Spec,
valuesTmpl string,
) error {
i.logger.Debug("Preparing values template context")
variables := engine.NewVariables()
err := variables.SetInstaller(cfg)
if err != nil {
return err
}
if err = variables.SetOpenShift(ctx, i.kube); err != nil {
return err
}

i.logger.Debug("Rendering values template")
i.valuesBytes, err = engine.NewEngine(i.kube, valuesTmpl).Render(variables)
return err
}

// PrintRawValues prints the raw values template to the console.
func (i *Installer) PrintRawValues() {
i.logger.Debug("Showing raw results of rendered values template")
fmt.Printf("#\n# Values (Raw)\n#\n\n%s\n", i.valuesBytes)
}

// RenderValues parses the values template and prepares the Helm chart values.
func (i *Installer) RenderValues() error {
if i.valuesBytes == nil {
return fmt.Errorf("values not set")
}

i.logger.Debug("Preparing rendered values for Helm installation")
var err error
i.values, err = chartutil.ReadValues(i.valuesBytes)
return err
}

// PrintValues prints the parsed values to the console.
func (i *Installer) PrintValues() {
i.logger.Debug("Showing parsed values")
printer.ValuesPrinter("Values", i.values)
}

// Install performs the installation of the Helm chart, including the pre and post
// hooks execution.
func (i *Installer) Install() error {
if i.values == nil {
return fmt.Errorf("values not set")
}
hc, err := i.prepareHelmClient()
if err != nil {
return err
}

hook := hooks.NewHooks(i.cfs, i.dep, os.Stdout, os.Stderr)
if !i.flags.DryRun {
i.logger.Debug("Running pre-deploy hook script...")
if err = hook.PreDeploy(i.values); err != nil {
return err
}
} else {
i.logger.Debug("Skipping pre-deploy hook script (dry-run)")
}

// Performing the installation, or upgrade, of the Helm chart dependency,
// using the values rendered before hand.
i.logger.Debug("Installing the Helm chart")
if err = hc.Install(i.values); err != nil {
return err
}
// Verifying if the instaltion was successful, by running the Helm chart
// tests interactively.
i.logger.Debug("Verifying the Helm chart release")
if err = hc.Verify(); err != nil {
return err
}

if !i.flags.DryRun {
i.logger.Debug("Running post-deploy hook script...")
if err = hook.PostDeploy(i.values); err != nil {
return err
}
} else {
i.logger.Debug("Skipping post-deploy hook script (dry-run)")
}

i.logger.Info("Helm chart installed!")
return nil
}

func NewInstaller() *Installer {
return &Installer{}
// NewInstaller instantiates a new installer for the given dependency.
func NewInstaller(
logger *slog.Logger,
f *flags.Flags,
kube *k8s.Kube,
cfs *chartfs.ChartFS,
dep *config.Dependency,
) *Installer {
return &Installer{
logger: dep.LoggerWith(logger),
flags: f,
kube: kube,
cfs: cfs,
dep: dep,
}
}
86 changes: 20 additions & 66 deletions pkg/subcmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,14 @@ package subcmd
import (
"fmt"
"log/slog"
"os"

"github.com/redhat-appstudio/rhtap-cli/pkg/chartfs"
"github.com/redhat-appstudio/rhtap-cli/pkg/config"
"github.com/redhat-appstudio/rhtap-cli/pkg/deployer"
"github.com/redhat-appstudio/rhtap-cli/pkg/engine"
"github.com/redhat-appstudio/rhtap-cli/pkg/flags"
"github.com/redhat-appstudio/rhtap-cli/pkg/hooks"
"github.com/redhat-appstudio/rhtap-cli/pkg/installer"
"github.com/redhat-appstudio/rhtap-cli/pkg/k8s"

"github.com/spf13/cobra"
"helm.sh/helm/v3/pkg/chartutil"
)

// Deploy is the deploy subcommand.
Expand All @@ -25,7 +21,7 @@ type Deploy struct {
cfg *config.Spec // installer configuration
kube *k8s.Kube // kubernetes client

valuesTemplatePath string // path to the values template file
valuesTmplPath string // path to the values template file
}

var _ Interface = &Deploy{}
Expand All @@ -50,17 +46,11 @@ func (d *Deploy) Cmd() *cobra.Command {
// log logger with contextual information.
func (d *Deploy) log() *slog.Logger {
return d.flags.LoggerWith(
d.logger.With("values-template", d.valuesTemplatePath))
d.logger.With("values-template", d.valuesTmplPath))
}

// Complete verifies the object is complete.
func (d *Deploy) Complete(_ []string) error {
if d.cfg == nil {
return fmt.Errorf("configuration is not informed")
}
if d.kube == nil {
return fmt.Errorf("kubernetes client is not informed")
}
return nil
}

Expand All @@ -74,78 +64,42 @@ func (d *Deploy) Validate() error {
)
}

// Run deploys the dependencies listed on the configuration.
func (d *Deploy) Run() error {
cfs := chartfs.NewChartFSForCWD()

d.log().Debug("Loading values template file (from CFS)")
valuesTemplatePayload, err := cfs.ReadFile(d.valuesTemplatePath)
d.log().Debug("Reading values template file")
valuesTmpl, err := cfs.ReadFile(d.valuesTmplPath)
if err != nil {
return err
}

d.log().Debug("Preparing values template context")
variables := engine.NewVariables()
if err := variables.SetInstaller(d.cfg); err != nil {
return err
}
if err := variables.SetOpenShift(d.cmd.Context(), d.kube); err != nil {
return err
return fmt.Errorf("failed to read values template file: %w", err)
}

d.log().Debug("Searching Helm charts from the current directory")
eng := engine.NewEngine(d.kube, string(valuesTemplatePayload))

// Installing each Helm Chart dependency from the configuration.
d.log().Debug("Installing dependencies...")
for _, dep := range d.cfg.Dependencies {
logger := dep.LoggerWith(d.log())

logger.Debug("Loading dependency Helm chart (from CFS)")
chart, err := cfs.GetChartForDep(&dep)
if err != nil {
return err
}

hc, err := deployer.NewHelm(logger, d.flags, d.kube, dep.Namespace, chart)
if err != nil {
return err
}
i := installer.NewInstaller(d.log(), d.flags, d.kube, cfs, &dep)

logger.Debug("Rendering values from template")
valuesBytes, err := eng.Render(variables)
err := i.SetValues(d.cmd.Context(), d.cfg, string(valuesTmpl))
if err != nil {
return err
}

logger.Debug("Preparing rendered values for Helm installation")
values, err := chartutil.ReadValues(valuesBytes)
if err != nil {
return err
if d.flags.Debug {
i.PrintRawValues()
}

hook := hooks.NewHooks(cfs, &dep, os.Stdout, os.Stderr)
logger.Debug("Running pre-deploy hook script...")
if err = hook.PreDeploy(values); err != nil {
if err := i.RenderValues(); err != nil {
return err
}

// Performing the installation, or upgrade, of the Helm chart dependency,
// using the values rendered before hand.
logger.Debug("Installing the Helm chart")
if err = hc.Install(values); err != nil {
return err
}
// Verifying if the instaltion was successful, by running the Helm chart
// tests interactively.
logger.Debug("Verifying the Helm chart release")
if err = hc.Verify(); err != nil {
return err
if d.flags.Debug {
i.PrintValues()
}

logger.Debug("Running post-deploy hook script...")
if err = hook.PostDeploy(values); err != nil {
if err = i.Install(); err != nil {
return err
}
logger.Info("Helm chart installed!")
}

d.log().Info("Deployment complete!")
return nil
}

Expand All @@ -167,6 +121,6 @@ func NewDeploy(
cfg: cfg,
kube: kube,
}
flags.SetValuesTmplFlag(d.cmd.PersistentFlags(), &d.valuesTemplatePath)
flags.SetValuesTmplFlag(d.cmd.PersistentFlags(), &d.valuesTmplPath)
return d
}
Loading

0 comments on commit b7045cb

Please sign in to comment.