Skip to content

Commit

Permalink
feat: Local and Embedded ChartFS
Browse files Browse the repository at this point in the history
Supports local and embedded files via `ChartFS`, the component looks for
the data in the local filesystem first, and if not found, it looks for
the data in the embedded filesystem.

The flag `--embedded` is removed.
  • Loading branch information
otaviof authored and Roming22 committed Dec 18, 2024
1 parent d30d0e3 commit 62a3dfc
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 89 deletions.
2 changes: 1 addition & 1 deletion integration-tests/scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ install_rhtap() {
# for debugging purpose
echo "[INFO] Print out the content of values.yaml.tpl"
cat "$tpl_file"
./bin/rhtap-cli deploy --timeout 30m --embedded false --config "$config_file" --values-template "$tpl_file" --kube-config "$KUBECONFIG" --debug --log-level=debug
./bin/rhtap-cli deploy --timeout 30m --config "$config_file" --values-template "$tpl_file" --kube-config "$KUBECONFIG" --debug --log-level=debug

homepage_url=https://$(kubectl -n rhtap get route backstage-developer-hub -o 'jsonpath={.spec.host}')
callback_url=https://$(kubectl -n rhtap get route backstage-developer-hub -o 'jsonpath={.spec.host}')/api/auth/github/handler/frame
Expand Down
75 changes: 49 additions & 26 deletions pkg/chartfs/chartfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package chartfs

import (
"bytes"
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
Expand All @@ -19,63 +21,84 @@ import (
const installerDir = "installer"

// ChartFS represents a file system abstraction which provides the Helm charts
// payload, and as well the "values.yaml.tpl" file.
// payload, and as well the "values.yaml.tpl" file. It uses the embedded tarball
// as data source, and as well, the local file system.
type ChartFS struct {
fs fs.FS // file system
baseDir string // base directory path
embeddedFS fs.FS // embedded file system
localFS fs.FS // local file system
baseDir string // base directory path
}

// ErrFailedToReadEmbeddedFiles returned when the tarball is not readable.
var ErrFailedToReadEmbeddedFiles = errors.New("failed to read embedded files")

// relativePath returns the relative path for the given file name.
func (c *ChartFS) relativePath(name string) (string, error) {
func (c *ChartFS) relativePath(baseDir, name string) (string, error) {
// If the file name does not start with the base directory, it means the path
// is already relative.
if !strings.HasPrefix(name, c.baseDir) {
if !strings.HasPrefix(name, baseDir) {
return name, nil
}

relPath, err := filepath.Rel(c.baseDir, name)
relPath, err := filepath.Rel(baseDir, name)
if err != nil {
return "", err
}
return relPath, nil
}

// ReadFile reads the file from the file system.
func (c *ChartFS) ReadFile(name string) ([]byte, error) {
relPath, err := c.relativePath(name)
// readFileFromLocalFS reads the file from the local file system, so using the
// base diretory configured.
func (c *ChartFS) readFileFromLocalFS(name string) ([]byte, error) {
relPath, err := c.relativePath(c.baseDir, name)
if err != nil {
return nil, err
}
return fs.ReadFile(c.localFS, relPath)
}

// readFileFromEmbeddedFS reads the file from the embedded file system, using the
// known base direcotry for embedded files.
func (c *ChartFS) readFileFromEmbeddedFS(name string) ([]byte, error) {
relPath, err := c.relativePath(installerDir, name)
if err != nil {
return nil, err
}
return fs.ReadFile(c.fs, relPath)
return fs.ReadFile(c.embeddedFS, relPath)
}

// ReadFile reads the file from the file system.
func (c *ChartFS) ReadFile(name string) ([]byte, error) {
payload, err := c.readFileFromLocalFS(name)
if err == nil {
return payload, nil
}
if errors.Is(err, fs.ErrNotExist) {
return c.readFileFromEmbeddedFS(name)
}
return nil, err
}

// GetChartForDep returns the Helm chart for the given dependency. It uses
// BufferredFiles to walk through the filesytem and collect the chart files.
func (c *ChartFS) GetChartForDep(dep *config.Dependency) (*chart.Chart, error) {
bf := NewBufferedFiles(c.fs, dep.Chart)
if err := fs.WalkDir(c.fs, dep.Chart, bf.Walk); err != nil {
bf := NewBufferedFiles(c.embeddedFS, dep.Chart)
if err := fs.WalkDir(c.embeddedFS, dep.Chart, bf.Walk); err != nil {
return nil, err
}
return loader.LoadFiles(bf.Files())
}

// NewChartFS instantiates a new ChartFS instance for the given base directory.
func NewChartFS(baseDir string) *ChartFS {
return &ChartFS{
fs: os.DirFS(baseDir),
baseDir: baseDir,
}
}

// NewChartFSEmbedded instantiates a new ChartFS instance for the embedded files,
// using a tarball to load the files and thus the base directory is a constant.
func NewChartFSEmbedded() (*ChartFS, error) {
// NewChartFS instantiates a ChartFS instance using the embedded tarball,
// and the local base directory.
func NewChartFS(baseDir string) (*ChartFS, error) {
tfs, err := tarfs.New(bytes.NewReader(installer.InstallerTarball))
if err != nil {
return nil, err
return nil, fmt.Errorf("%w: %v", ErrFailedToReadEmbeddedFiles, err)
}
return &ChartFS{
fs: tfs,
baseDir: installerDir,
embeddedFS: tfs,
baseDir: installerDir,
localFS: os.DirFS(baseDir),
}, nil
}
3 changes: 2 additions & 1 deletion pkg/chartfs/chartfs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import (
func TestNewChartFS(t *testing.T) {
g := o.NewWithT(t)

c := NewChartFS("../../installer")
c, err := NewChartFS("../../installer")
g.Expect(err).To(o.Succeed())
g.Expect(c).ToNot(o.BeNil())

t.Run("ReadFile", func(t *testing.T) {
Expand Down
8 changes: 0 additions & 8 deletions pkg/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
type Flags struct {
Debug bool // debug mode
DryRun bool // dry-run mode
Embedded bool // embedded mode
KubeConfigPath string // path to the kubeconfig file
LogLevel *slog.Level // log verbosity level
Timeout time.Duration // helm client timeout
Expand All @@ -26,12 +25,6 @@ type Flags struct {
func (f *Flags) PersistentFlags(p *pflag.FlagSet) {
p.BoolVar(&f.Debug, "debug", f.Debug, "enable debug mode")
p.BoolVar(&f.DryRun, "dry-run", f.DryRun, "enable dry-run mode")
p.BoolVar(
&f.Embedded,
"embedded",
f.Embedded,
"installer resources embedded on the executable",
)
p.StringVar(
&f.KubeConfigPath,
"kube-config",
Expand Down Expand Up @@ -80,7 +73,6 @@ func NewFlags() *Flags {
return &Flags{
Debug: false,
DryRun: false,
Embedded: true,
KubeConfigPath: path.Join(usr.HomeDir, ".kube", "config"),
LogLevel: &defaultLogLevel,
Timeout: 15 * time.Minute,
Expand Down
4 changes: 3 additions & 1 deletion pkg/hooks/hooks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ func TestNewHooks(t *testing.T) {

var stdout bytes.Buffer
var stderr bytes.Buffer
cfs, err := chartfs.NewChartFS("../../test")
g.Expect(err).To(o.Succeed())
h := NewHooks(
chartfs.NewChartFS("../../test"),
cfs,
&config.Dependency{
Chart: "charts/testing",
Namespace: "rhtap",
Expand Down
33 changes: 0 additions & 33 deletions pkg/subcmd/chartfs.go

This file was deleted.

18 changes: 10 additions & 8 deletions pkg/subcmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ 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/flags"
"github.com/redhat-appstudio/rhtap-cli/pkg/installer"
Expand Down Expand Up @@ -37,7 +39,8 @@ The platform configuration is rendered from the values template file
(--values-template), this configuration payload is given to all Helm charts.
The installer resources are embedded in the executable, these resources are
employed by default, to use local files, set the '--embedded' flag to false.
employed by default, to use local files just point the "config.yaml" file to
find the dependencies in the local filesystem.
`

// Cmd exposes the cobra instance.
Expand All @@ -58,12 +61,6 @@ func (d *Deploy) Complete(_ []string) error {

// Validate asserts the requirements to start the deployment are in place.
func (d *Deploy) Validate() error {
if d.flags.Embedded && d.valuesTemplatePath == "" {
return fmt.Errorf(
"flag --%s is ignored when using embedded resources",
flags.ValuesTemplateFlag,
)
}
return k8s.EnsureOpenShiftProject(
d.cmd.Context(),
d.log(),
Expand All @@ -74,7 +71,12 @@ func (d *Deploy) Validate() error {

// Run deploys the enabled dependencies listed on the configuration.
func (d *Deploy) Run() error {
cfs, err := newChartFS(d.logger, d.flags, d.cfg)
cwd, err := os.Getwd()
if err != nil {
return err
}

cfs, err := chartfs.NewChartFS(cwd)
if err != nil {
return err
}
Expand Down
3 changes: 0 additions & 3 deletions pkg/subcmd/installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,6 @@ func (i *Installer) Complete(_ []string) error {

// Validate validates the informed flags are correct, and the conditions are met.
func (i *Installer) Validate() error {
if !i.flags.Embedded {
return fmt.Errorf("embedded must be enabled")
}
if i.list && i.extract != "" {
return fmt.Errorf("list and extract are mutually exclusive")
}
Expand Down
18 changes: 10 additions & 8 deletions pkg/subcmd/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ 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/flags"
"github.com/redhat-appstudio/rhtap-cli/pkg/installer"
Expand Down Expand Up @@ -44,7 +46,8 @@ Additionally, the '--debug' flag should be used to display rendered global value
passed into every Helm Chart installed, as key-value pairs.
The installer resources are embedded in the executable, these resources are
employed by default, to use local files, set the '--embedded' flag to false.
employed by default, to use local files just use the last argument with the path
to the local Helm Chart.
Examples:
Expand Down Expand Up @@ -99,18 +102,17 @@ func (t *Template) Validate() error {
if t.dep.Chart == "" {
return fmt.Errorf("missing chart path")
}
if t.flags.Embedded && t.valuesTemplatePath == "" {
return fmt.Errorf(
"flag --%s is ignored when using embedded resources",
flags.ValuesTemplateFlag,
)
}
return nil
}

// Run Renders the templates.
func (t *Template) Run() error {
cfs, err := newChartFS(t.logger, t.flags, t.cfg)
cwd, err := os.Getwd()
if err != nil {
return err
}

cfs, err := chartfs.NewChartFS(cwd)
if err != nil {
return err
}
Expand Down

0 comments on commit 62a3dfc

Please sign in to comment.