Repository: iter8-tools/iter8
Pull request #1353 (2022-10-13 21:26:14)

github repo -> helm repo
Addresses: https://github.com/iter8-tools/iter8/issues/1352

Signed-off-by: Michael Kalantar <kalantar@us.ibm.com>

kalantar: This PR depends on https://github.com/iter8-tools/hub/pull/31.
It represents a breaking change.
Once the above PR is merged, `testdata/charts/iter8` should be updated using:

```shell
helm pull --repo https://iter8-tools.github.io/hub iter8 --untar
```

Patch:
diff --git a/.gitignore b/.gitignore
index f0903cdb..1eef9476 100644
--- a/.gitignore
+++ b/.gitignore
@@ -143,8 +143,8 @@ iter8
 **/ghz-call-data.bin
 **/ghz-call-metadata.json
 
-!charts
-!charts/iter8
+!testdata/charts
+!testdata/charts/iter8
 
 # iter8lib tgz
 iter8lib*.tgz
diff --git a/action/gen.go b/action/gen.go
index 1e0e9bdb..c74fb175 100644
--- a/action/gen.go
+++ b/action/gen.go
@@ -15,19 +15,10 @@ import (
 	"helm.sh/helm/v3/pkg/getter"
 )
 
-const (
-	// ChartsFolderName is the folder where Iter8 looks for Iter8 experiment charts
-	ChartsFolderName = "charts"
-	// DefaultChartName is the default name of the Iter8 chart
-	DefaultChartName = "iter8"
-)
-
 // GenOpts are the options used for generating experiment.yaml
 type GenOpts struct {
 	// Options provides the values to be combined with the experiment chart
 	values.Options
-	// ChartsParentDir is the directory where `charts` directory is located
-	ChartsParentDir string
 	// GenDir is the directory where the chart templates are rendered
 	GenDir string
 	// ChartName is the name of the chart
@@ -37,26 +28,15 @@ type GenOpts struct {
 // NewGenOpts initializes and returns gen opts
 func NewGenOpts() *GenOpts {
 	return &GenOpts{
-		ChartsParentDir: ".",
-		GenDir:          ".",
-		ChartName:       DefaultChartName,
+		GenDir:    ".",
+		ChartName: DefaultChartName,
 	}
 }
 
-// chartDir returns the path to chart directory
-func (gen *GenOpts) chartDir() string {
-	return path.Join(gen.ChartsParentDir, ChartsFolderName, gen.ChartName)
-}
-
 // LocalRun generates a local experiment.yaml file
 func (gen *GenOpts) LocalRun() error {
-	// update dependencies
-	if err := driver.UpdateChartDependencies(gen.chartDir(), nil); err != nil {
-		return err
-	}
-
 	// read in the experiment chart
-	c, err := loader.Load(gen.chartDir())
+	c, err := loader.Load(gen.ChartName)
 	if err != nil {
 		log.Logger.WithStackTrace(err.Error()).Error("unable to load experiment chart")
 		return err
diff --git a/action/hub.go b/action/hub.go
deleted file mode 100644
index 3be447d3..00000000
--- a/action/hub.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package action
-
-import (
-	"errors"
-	"strings"
-
-	"github.com/hashicorp/go-getter"
-	"github.com/iter8-tools/iter8/base"
-	"github.com/iter8-tools/iter8/base/log"
-)
-
-const defaultIter8Repo = "github.com/iter8-tools/iter8.git"
-
-// HubOpts are the options used for downloading Iter8 experiment charts
-type HubOpts struct {
-	// RemoteFolderURL is the URL of the remote Iter8 experiment charts folder
-	// Remote URLs can be any go-getter URLs like GitHub or GitLab URLs
-	// https://github.com/hashicorp/go-getter
-	RemoteFolderURL string
-	// ChartsDir is the full path to the `charts` dir
-	ChartsDir string
-}
-
-// DefaultRemoteFolderURL returns the default value of the remote Iter8 folder
-func DefaultRemoteFolderURL() string {
-	// parse version
-	v := strings.Split(base.Version, "-")
-	ref := v[0]
-	return defaultIter8Repo + "?ref=" + ref + "//" + ChartsFolderName
-}
-
-// NewHubOpts initializes and returns hub opts
-func NewHubOpts() *HubOpts {
-
-	return &HubOpts{
-		RemoteFolderURL: DefaultRemoteFolderURL(),
-		ChartsDir:       ChartsFolderName,
-	}
-}
-
-// LocalRun downloads an experiment chart to DestDir
-func (hub *HubOpts) LocalRun() error {
-	log.Logger.Infof("downloading %v into %v", hub.RemoteFolderURL, hub.ChartsDir)
-	if err := getter.Get(hub.ChartsDir, hub.RemoteFolderURL); err != nil {
-		e := errors.New("unable to download charts")
-		log.Logger.WithStackTrace(err.Error()).Error(e)
-		return e
-	}
-	return nil
-}
diff --git a/action/launch.go b/action/launch.go
index 0f6e32f7..fd9f43e4 100644
--- a/action/launch.go
+++ b/action/launch.go
@@ -1,26 +1,32 @@
 package action
 
 import (
+	"os"
 	"path"
+	"path/filepath"
+	"strings"
 
+	"github.com/iter8-tools/iter8/base"
 	"github.com/iter8-tools/iter8/base/log"
 	"github.com/iter8-tools/iter8/driver"
+	"helm.sh/helm/v3/pkg/action"
+	"helm.sh/helm/v3/pkg/cli"
 	"helm.sh/helm/v3/pkg/cli/values"
 )
 
+const (
+	// DefaultHelmRepository is the URL of the default Helm repository
+	DefaultHelmRepository = "https://iter8-tools.github.io/hub"
+	// DefaultChartName is the default name of the Iter8 chart
+	DefaultChartName = "iter8"
+)
+
 // LaunchOpts are the options used for launching experiments
 type LaunchOpts struct {
 	// DryRun enables simulating a launch
 	DryRun bool
-	// RemoteFolderURL is the URL of the remote Iter8 experiment charts folder
-	// Remote URLs can be any go-getter URLs like GitHub or GitLab URLs
-	// https://github.com/hashicorp/go-getter
-	RemoteFolderURL string
-	// ChartsParentDir is the directory where `charts` is to be downloaded or is located
-	ChartsParentDir string
-	// NoDownload disables charts download.
-	// With this option turned on, `charts` that are already present locally are reused
-	NoDownload bool
+	// ChartPathOptions
+	action.ChartPathOptions
 	// ChartName is the name of the chart
 	ChartName string
 	// Options provides the values to be combined with the experiment chart
@@ -29,46 +35,79 @@ type LaunchOpts struct {
 	RunDir string
 	// KubeDriver enables Kubernetes experiment run
 	*driver.KubeDriver
+	// LocalChart indicates the chart is on the local filesystem
+	LocalChart bool
 }
 
 // NewLaunchOpts initializes and returns launch opts
 func NewLaunchOpts(kd *driver.KubeDriver) *LaunchOpts {
 	return &LaunchOpts{
-		DryRun:          false,
-		RemoteFolderURL: DefaultRemoteFolderURL(),
-		ChartsParentDir: ".",
-		NoDownload:      false,
-		ChartName:       "",
-		Options:         values.Options{},
-		RunDir:          ".",
-		KubeDriver:      kd,
+		DryRun: false,
+		ChartPathOptions: action.ChartPathOptions{
+			RepoURL: DefaultHelmRepository,
+			Version: defaultChartVersion(),
+		},
+		ChartName:  DefaultChartName,
+		Options:    values.Options{},
+		RunDir:     ".",
+		KubeDriver: kd,
+		LocalChart: false,
 	}
 }
 
+func defaultChartVersion() string {
+	return strings.Replace(base.MajorMinor, "v", "", 1) + ".x"
+}
+
 // LocalRun launches a local experiment
 func (lOpts *LaunchOpts) LocalRun() error {
 	log.Logger.Debug("launch local run started...")
-	if !lOpts.NoDownload {
-		// download chart from Iter8 hub
-		hOpts := &HubOpts{
-			RemoteFolderURL: lOpts.RemoteFolderURL,
-			ChartsDir:       path.Join(lOpts.ChartsParentDir, ChartsFolderName),
+
+	var gOpts GenOpts
+	if lOpts.LocalChart {
+		// local charts
+		gOpts = GenOpts{
+			Options:   lOpts.Options,
+			GenDir:    lOpts.RunDir,
+			ChartName: path.Join(filepath.Dir(lOpts.ChartName), filepath.Base(lOpts.ChartName)),
 		}
-		if err := hOpts.LocalRun(); err != nil {
+	} else {
+		// non-local charts. download charts
+
+		// create temporary folder to store chart
+		chartsFolderName, err := os.MkdirTemp("", "iter8-")
+		if err != nil {
+			log.Logger.Error("failed to download chart")
+			return err
+		}
+		defer func() {
+			// ignore error value
+			_ = os.RemoveAll(chartsFolderName)
+		}()
+
+		// pull chart (and its dependencies)
+		client := action.NewPullWithOpts(action.WithConfig(&action.Configuration{}))
+		client.RepoURL = lOpts.RepoURL
+		client.Untar = true
+		client.UntarDir = chartsFolderName + "/charts"
+		client.Settings = cli.New()
+		log.Logger.Debug("client.UntarDir ", client.UntarDir)
+
+		_, err = client.Run(lOpts.ChartName)
+		if err != nil {
 			return err
 		}
-		log.Logger.Debug("hub complete")
-	} else {
-		log.Logger.Debug("using `charts` under ", lOpts.ChartsParentDir)
-	}
 
-	// gen experiment spec
-	gOpts := GenOpts{
-		Options:         lOpts.Options,
-		ChartsParentDir: lOpts.ChartsParentDir,
-		GenDir:          lOpts.RunDir,
-		ChartName:       lOpts.ChartName,
+		log.Logger.Trace("chart pulled")
+
+		// gen experiment spec
+		gOpts = GenOpts{
+			Options:   lOpts.Options,
+			GenDir:    lOpts.RunDir,
+			ChartName: path.Join(client.UntarDir, lOpts.ChartName),
+		}
 	}
+
 	if err := gOpts.LocalRun(); err != nil {
 		return err
 	}
@@ -96,30 +135,5 @@ func (lOpts *LaunchOpts) KubeRun() error {
 		return err
 	}
 
-	if !lOpts.NoDownload {
-		// download chart from Iter8 hub
-		hOpts := &HubOpts{
-			RemoteFolderURL: lOpts.RemoteFolderURL,
-			ChartsDir:       path.Join(lOpts.ChartsParentDir, ChartsFolderName),
-		}
-		if err := hOpts.LocalRun(); err != nil {
-			return err
-		}
-		log.Logger.Debug("hub complete")
-	} else {
-		log.Logger.Debug("using `charts` under ", lOpts.ChartsParentDir)
-	}
-
-	// update dependencies
-	gOpts := GenOpts{
-		Options:         lOpts.Options,
-		ChartsParentDir: lOpts.ChartsParentDir,
-		GenDir:          lOpts.RunDir,
-		ChartName:       lOpts.ChartName,
-	}
-	if err := driver.UpdateChartDependencies(gOpts.chartDir(), lOpts.EnvSettings); err != nil {
-		return err
-	}
-
-	return lOpts.KubeDriver.Launch(gOpts.chartDir(), lOpts.Options, lOpts.Group, lOpts.DryRun)
+	return lOpts.KubeDriver.Launch(lOpts.ChartPathOptions, lOpts.ChartName, lOpts.Options, lOpts.Group, lOpts.DryRun)
 }
diff --git a/base/util.go b/base/util.go
index 56cdd28b..64bef831 100644
--- a/base/util.go
+++ b/base/util.go
@@ -13,11 +13,11 @@ import (
 
 // MajorMinor is the minor version of Iter8
 // set this manually whenever the major or minor version changes
-var MajorMinor = "v0.11"
+var MajorMinor = "v0.12"
 
 // Version is the semantic version of Iter8 (with the `v` prefix)
 // Version is intended to be set using LDFLAGS at build time
-var Version = "v0.11.0"
+var Version = "v0.12.0"
 
 // int64Pointer takes an int64 as input, creates a new variable with the input value, and returns a pointer to the variable
 func int64Pointer(i int64) *int64 {
diff --git a/charts/iter8/README.md b/charts/iter8/README.md
deleted file mode 100644
index 379ebdf5..00000000
--- a/charts/iter8/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Iter8 experiment chart
-
-This chart provides reusable templates that enable Iter8 experiments.
diff --git a/cmd/gen.go b/cmd/gen.go
index b0a979da..11682c8f 100644
--- a/cmd/gen.go
+++ b/cmd/gen.go
@@ -26,21 +26,16 @@ func newGenCmd() *cobra.Command {
 		Short:        "Generate experiment.yaml file by combining an experiment chart with values",
 		Long:         genDesc,
 		SilenceUsage: true,
+		Hidden:       true,
 		RunE: func(_ *cobra.Command, _ []string) error {
 			return actor.LocalRun()
 		},
 	}
-	addChartsParentDirFlag(cmd, &actor.ChartsParentDir)
 	addChartNameFlag(cmd, &actor.ChartName)
 	addValueFlags(cmd.Flags(), &actor.Options)
 	return cmd
 }
 
-// addChartsParentDirFlag to the command
-func addChartsParentDirFlag(cmd *cobra.Command, chartsParentDirPtr *string) {
-	cmd.Flags().StringVar(chartsParentDirPtr, "chartsParentDir", ".", "directory under which the charts folder is located")
-}
-
 // addChartNameFlag to the command
 func addChartNameFlag(cmd *cobra.Command, chartNamePtr *string) {
 	cmd.Flags().StringVarP(chartNamePtr, "chartName", "c", ia.DefaultChartName, "name of the experiment chart")
diff --git a/cmd/hub.go b/cmd/hub.go
deleted file mode 100644
index d4e312f2..00000000
--- a/cmd/hub.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package cmd
-
-import (
-	ia "github.com/iter8-tools/iter8/action"
-
-	"github.com/spf13/cobra"
-)
-
-// hubDesc is the description of the hub command
-const hubDesc = `
-Download the Iter8 experiment chart from a go-getter URL.
-
-	iter8 hub
-
-This command is intended for development and testing of experiment charts. For production usage, the iter8 launch command is recommended.
-`
-
-// newHubCmd creates the hub command
-func newHubCmd() *cobra.Command {
-	actor := ia.NewHubOpts()
-
-	cmd := &cobra.Command{
-		Use:          "hub",
-		Short:        "Download Iter8 experiment chart",
-		Long:         hubDesc,
-		SilenceUsage: true,
-		RunE: func(_ *cobra.Command, _ []string) error {
-			return actor.LocalRun()
-		},
-	}
-	addRemoteFolderURLFlag(cmd, &actor.RemoteFolderURL)
-	return cmd
-}
-
-// add the remoteFolderURL flag to the command
-func addRemoteFolderURLFlag(cmd *cobra.Command, remoteFolderURLPtr *string) {
-	cmd.Flags().StringVar(remoteFolderURLPtr, "remoteFolderURL", ia.DefaultRemoteFolderURL(), "URL of the remote folder containing the Iter8 experiment chart. Accepts any URL supported by https://github.com/hashicorp/go-getter")
-}
diff --git a/cmd/klaunch.go b/cmd/klaunch.go
index bb02c784..f1801524 100644
--- a/cmd/klaunch.go
+++ b/cmd/klaunch.go
@@ -37,7 +37,7 @@ func newKLaunchCmd(kd *driver.KubeDriver, out io.Writer) *cobra.Command {
 		Long:         klaunchDesc,
 		SilenceUsage: true,
 		PreRunE: func(cmd *cobra.Command, _ []string) error {
-			return noDownloadIsRequired(actor, cmd.Flags())
+			return chartNameIsRequired(actor, cmd.Flags())
 		},
 		RunE: func(_ *cobra.Command, _ []string) error {
 			return actor.KubeRun()
@@ -49,11 +49,10 @@ func newKLaunchCmd(kd *driver.KubeDriver, out io.Writer) *cobra.Command {
 	actor.EnvSettings = settings
 
 	// flags shared with launch
-	addChartsParentDirFlag(cmd, &actor.ChartsParentDir)
-	addRemoteFolderURLFlag(cmd, &actor.RemoteFolderURL)
+	// addChartPathOptionsFlags(cmd, &actor.ChartPathOptions)
 	addChartNameFlag(cmd, &actor.ChartName)
 	addValueFlags(cmd.Flags(), &actor.Options)
-	addNoDownloadFlag(cmd, &actor.NoDownload)
+	addLocalChartFlag(cmd, &actor.LocalChart)
 
 	return cmd
 }
diff --git a/cmd/launch.go b/cmd/launch.go
index df115905..fee7e89c 100644
--- a/cmd/launch.go
+++ b/cmd/launch.go
@@ -2,13 +2,13 @@ package cmd
 
 import (
 	"errors"
-	"os"
-	"path"
 
 	ia "github.com/iter8-tools/iter8/action"
 	"github.com/iter8-tools/iter8/driver"
 	"github.com/spf13/cobra"
 	"github.com/spf13/pflag"
+	// "helm.sh/helm/v3/pkg/action"
+	// "k8s.io/client-go/util/homedir"
 )
 
 // launchDesc is the description of the launch command
@@ -39,31 +39,26 @@ func newLaunchCmd(kd *driver.KubeDriver) *cobra.Command {
 		Long:         launchDesc,
 		SilenceUsage: true,
 		PreRunE: func(cmd *cobra.Command, _ []string) error {
-			return noDownloadIsRequired(actor, cmd.Flags())
+			return chartNameIsRequired(actor, cmd.Flags())
 		},
 		RunE: func(_ *cobra.Command, _ []string) error {
 			return actor.LocalRun()
 		},
 	}
 	addDryRunFlag(cmd, &actor.DryRun)
-	addChartsParentDirFlag(cmd, &actor.ChartsParentDir)
-	addRemoteFolderURLFlag(cmd, &actor.RemoteFolderURL)
+	// addChartPathOptionsFlags(cmd, &actor.ChartPathOptions)
 	addChartNameFlag(cmd, &actor.ChartName)
 	addValueFlags(cmd.Flags(), &actor.Options)
 	addRunDirFlag(cmd, &actor.RunDir)
-	addNoDownloadFlag(cmd, &actor.NoDownload)
+	addLocalChartFlag(cmd, &actor.LocalChart)
 
 	return cmd
 }
 
-// noDownloadIsRequired make noDownload flag required, if 'charts' folder is found
-func noDownloadIsRequired(lOpts *ia.LaunchOpts, flags *pflag.FlagSet) error {
-	chartsFolderPath := path.Join(lOpts.ChartsParentDir, ia.ChartsFolderName)
-	if _, err := os.Stat(chartsFolderPath); !os.IsNotExist(err) {
-		if flags.Changed("noDownload") {
-			return nil
-		}
-		return errors.New("'charts' folder found; 'noDownload' flag is required")
+// chartNameIsRequired makes chartName required if localChart is set
+func chartNameIsRequired(lOpts *ia.LaunchOpts, flags *pflag.FlagSet) error {
+	if flags.Changed("localChart") && !flags.Changed("chartName") {
+		return errors.New("localChart specified; 'chartName' is required")
 	}
 	return nil
 }
@@ -74,8 +69,35 @@ func addDryRunFlag(cmd *cobra.Command, dryRunPtr *bool) {
 	cmd.Flags().Lookup("dry").NoOptDefVal = "true"
 }
 
-// addNoDownloadFlag adds noDownload flag to the launch command
-func addNoDownloadFlag(cmd *cobra.Command, noDownloadPtr *bool) {
-	cmd.Flags().BoolVar(noDownloadPtr, "noDownload", false, "reuse local charts dir - do not download from remoteFolderURL; if local charts are present, this flag is required - set it to true or false; if local charts are absent, do not use this flag")
-	cmd.Flags().Lookup("noDownload").NoOptDefVal = "true"
+// addLocalChartFlag adds the localChart flag to the launch command
+func addLocalChartFlag(cmd *cobra.Command, localChartPtr *bool) {
+	cmd.Flags().BoolVar(localChartPtr, "localChart", false, "use local charts")
+	cmd.Flags().Lookup("localChart").NoOptDefVal = "true"
 }
+
+// addChartPathOptionsFlags adds flags related to Helm chart repository
+// copied from
+// https://github.com/helm/helm/blob/ce66412a723e4d89555dc67217607c6579ffcb21/cmd/helm/flags.go
+// func addChartPathOptionsFlags(cmd *cobra.Command, c *action.ChartPathOptions) {
+// cmd.Flags().StringVar(&c.Version, "version", "", "specify a version constraint for the chart version to use. This constraint can be a specific tag (e.g. 1.1.1) or it may reference a valid range (e.g. ^2.0.0). If this is not specified, the latest version is used")
+// cmd.Flags().BoolVar(&c.Verify, "verify", false, "verify the package before using it")
+// cmd.Flags().StringVar(&c.Keyring, "keyring", defaultKeyring(), "location of public keys used for verification")
+// cmd.Flags().StringVar(&c.RepoURL, "repo", "https://iter8-tools.github.io/hub", "chart repository url where to locate the requested chart")
+// cmd.Flags().StringVar(&c.Username, "username", "", "chart repository username where to locate the requested chart")
+// cmd.Flags().StringVar(&c.Password, "password", "", "chart repository password where to locate the requested chart")
+// cmd.Flags().StringVar(&c.CertFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
+// cmd.Flags().StringVar(&c.KeyFile, "key-file", "", "identify HTTPS client using this SSL key file")
+// cmd.Flags().BoolVar(&c.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart download")
+// cmd.Flags().StringVar(&c.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
+// cmd.Flags().BoolVar(&c.PassCredentialsAll, "pass-credentials", false, "pass credentials to all domains")
+// }
+
+// // defaultKeyring returns the expanded path to the default keyring.
+// // copied from
+// // https://github.com/helm/helm/blob/ce66412a723e4d89555dc67217607c6579ffcb21/cmd/helm/dependency_build.go
+// func defaultKeyring() string {
+// 	if v, ok := os.LookupEnv("GNUPGHOME"); ok {
+// 		return filepath.Join(v, "pubring.gpg")
+// 	}
+// 	return filepath.Join(homedir.HomeDir(), ".gnupg", "pubring.gpg")
+// }
diff --git a/cmd/root.go b/cmd/root.go
index 3d13408e..e80e0faf 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -81,9 +81,6 @@ func init() {
 	// add gen
 	rootCmd.AddCommand(newGenCmd())
 
-	// add hub
-	rootCmd.AddCommand(newHubCmd())
-
 	// add k
 	rootCmd.AddCommand(kcmd)
 
diff --git a/cmd/run.go b/cmd/run.go
index 6485a26c..029d7bc5 100644
--- a/cmd/run.go
+++ b/cmd/run.go
@@ -26,6 +26,7 @@ func newRunCmd(kd *driver.KubeDriver, out io.Writer) *cobra.Command {
 		Short:        "Run an experiment",
 		Long:         runDesc,
 		SilenceUsage: true,
+		Hidden:       true,
 		RunE: func(_ *cobra.Command, _ []string) error {
 			return actor.LocalRun()
 		},
diff --git a/driver/kubedriver.go b/driver/kubedriver.go
index 569d39d2..19985e25 100644
--- a/driver/kubedriver.go
+++ b/driver/kubedriver.go
@@ -15,7 +15,6 @@ import (
 	// Import to initialize client auth plugins.
 	"helm.sh/helm/v3/pkg/chart"
 	"helm.sh/helm/v3/pkg/chart/loader"
-	"helm.sh/helm/v3/pkg/downloader"
 
 	// auth import enables automated authentication to various hosted clouds
 	_ "k8s.io/client-go/plugin/pkg/client/auth"
@@ -258,34 +257,6 @@ func (kd *KubeDriver) GetRevision() int {
 	return kd.revision
 }
 
-// UpdateChartDependencies for an Iter8 experiment chart
-// for now this function has one purpose ...
-// bring iter8lib dependency into other experiment charts like load-test-http
-func UpdateChartDependencies(chartDir string, settings *cli.EnvSettings) error {
-	// client and settings may not really be initialized with proper values
-	// should be ok considering iter8lib is a local file dependency
-	if settings == nil {
-		settings = cli.New()
-	}
-	client := action.NewDependency()
-	man := &downloader.Manager{
-		Out:              io.Discard,
-		ChartPath:        chartDir,
-		Keyring:          client.Keyring,
-		SkipUpdate:       client.SkipRefresh,
-		Getters:          getter.All(settings),
-		RepositoryConfig: settings.RepositoryConfig,
-		RepositoryCache:  settings.RepositoryCache,
-		Debug:            settings.Debug,
-	}
-	log.Logger.Debug("updating chart ", chartDir)
-	if err := man.Update(); err != nil {
-		log.Logger.WithStackTrace(err.Error()).Error("unable to update chart dependencies")
-		return err
-	}
-	return nil
-}
-
 // writeManifest writes the Kubernetes experiment manifest to a local file
 func writeManifest(rel *release.Release) error {
 	err := os.WriteFile(ManifestFile, []byte(rel.Manifest), 0600)
@@ -300,14 +271,33 @@ func writeManifest(rel *release.Release) error {
 // Credit: the logic for this function is sourced from Helm
 // https://github.com/helm/helm/blob/8ab18f7567cedffdfa5ba4d7f6abfb58efc313f8/cmd/helm/upgrade.go#L69
 // Upgrade a Kubernetes experiment to the next release
-func (kd *KubeDriver) upgrade(chartDir string, valueOpts values.Options, group string, dry bool) error {
+func (kd *KubeDriver) upgrade(chartPathOptions action.ChartPathOptions, chartName string, valueOpts values.Options, group string, dry bool) error {
 	client := action.NewUpgrade(kd.Configuration)
 	client.Namespace = kd.Namespace()
 	client.DryRun = dry
 
-	ch, vals, err := kd.getChartAndVals(chartDir, valueOpts)
+	// copy chartpathoptions
+	// client.ChartPathOptions.CaFile = chartPathOptions.CaFile
+	// client.ChartPathOptions.CertFile = chartPathOptions.CertFile
+	// client.ChartPathOptions.KeyFile = chartPathOptions.KeyFile
+	// client.ChartPathOptions.InsecureSkipTLSverify = chartPathOptions.InsecureSkipTLSverify
+	// client.ChartPathOptions.Keyring = chartPathOptions.Keyring
+	// client.ChartPathOptions.Password = chartPathOptions.Password
+	// client.ChartPathOptions.PassCredentialsAll = chartPathOptions.PassCredentialsAll
+	client.ChartPathOptions.RepoURL = chartPathOptions.RepoURL
+	// client.ChartPathOptions.Username = chartPathOptions.Username
+	// client.ChartPathOptions.Verify = chartPathOptions.Verify
+	client.ChartPathOptions.Version = chartPathOptions.Version
+
+	cp, err := client.ChartPathOptions.LocateChart(chartName, kd.EnvSettings)
+	if err != nil {
+		log.Logger.Error(err)
+		return err
+	}
+
+	chartRequested, vals, err := kd.getChartAndVals(cp, valueOpts)
 	if err != nil {
-		e := fmt.Errorf("unable to get chart and vals for %v", chartDir)
+		e := fmt.Errorf("unable to get chart and value for %v", cp)
 		log.Logger.WithStackTrace(err.Error()).Error(e)
 		return e
 	}
@@ -323,46 +313,54 @@ func (kd *KubeDriver) upgrade(chartDir string, valueOpts values.Options, group s
 	signal.Notify(cSignal, os.Interrupt, syscall.SIGTERM)
 	go func() {
 		<-cSignal
-		fmt.Printf("experiment for group %s has been cancelled.\n", group)
+		log.Logger.Warnf("experiment for group %s has been cancelled.\n", group)
 		cancel()
 	}()
 
-	rel, err := client.RunWithContext(ctx, group, ch, vals)
+	rel, err := client.RunWithContext(ctx, group, chartRequested, vals)
 	if err != nil {
 		e := fmt.Errorf("experiment launch failed")
 		log.Logger.WithStackTrace(err.Error()).Error(e)
 		return e
 	}
 
-	// upgrading revision info
-	kd.revision = rel.Version
-
-	// write manifest if dry
-	if dry {
-		err := writeManifest(rel)
-		if err != nil {
-			return err
-		}
-		log.Logger.Info("dry run complete")
-	} else {
-		log.Logger.Info("experiment launched. Happy Iter8ing!")
-	}
-
-	return nil
+	return kd.updateRevision(rel, dry)
 }
 
 // install a Kubernetes experiment
 // Credit: the logic for this function is sourced from Helm
 // https://github.com/helm/helm/blob/8ab18f7567cedffdfa5ba4d7f6abfb58efc313f8/cmd/helm/install.go#L177
-func (kd *KubeDriver) install(chartDir string, valueOpts values.Options, group string, dry bool) error {
+func (kd *KubeDriver) install(chartPathOptions action.ChartPathOptions, chartName string, valueOpts values.Options, group string, dry bool) error {
+
+	// buf := new(bytes.Buffer)
 	client := action.NewInstall(kd.Configuration)
 	client.Namespace = kd.Namespace()
+	client.RepoURL = chartPathOptions.RepoURL
 	client.DryRun = dry
 	client.ReleaseName = group
 
-	ch, vals, err := kd.getChartAndVals(chartDir, valueOpts)
+	// copy chartPathOptions to client
+	// client.ChartPathOptions.CaFile = chartPathOptions.CaFile
+	// client.ChartPathOptions.CertFile = chartPathOptions.CertFile
+	// client.ChartPathOptions.KeyFile = chartPathOptions.KeyFile
+	// client.ChartPathOptions.InsecureSkipTLSverify = chartPathOptions.InsecureSkipTLSverify
+	// client.ChartPathOptions.Keyring = chartPathOptions.Keyring
+	// client.ChartPathOptions.Password = chartPathOptions.Password
+	// client.ChartPathOptions.PassCredentialsAll = chartPathOptions.PassCredentialsAll
+	client.ChartPathOptions.RepoURL = chartPathOptions.RepoURL
+	// client.ChartPathOptions.Username = chartPathOptions.Username
+	// client.ChartPathOptions.Verify = chartPathOptions.Verify
+	client.ChartPathOptions.Version = chartPathOptions.Version
+
+	cp, err := client.ChartPathOptions.LocateChart(chartName, kd.EnvSettings)
 	if err != nil {
-		e := fmt.Errorf("unable to get chart and vals for %v", chartDir)
+		log.Logger.Error(err)
+		return err
+	}
+
+	chartRequested, vals, err := kd.getChartAndVals(cp, valueOpts)
+	if err != nil {
+		e := fmt.Errorf("unable to get chart and value for %v", cp)
 		log.Logger.WithStackTrace(err.Error()).Error(e)
 		return e
 	}
@@ -378,17 +376,21 @@ func (kd *KubeDriver) install(chartDir string, valueOpts values.Options, group s
 	signal.Notify(cSignal, os.Interrupt, syscall.SIGTERM)
 	go func() {
 		<-cSignal
-		fmt.Printf("experiment for group %s has been cancelled.\n", group)
+		log.Logger.Warnf("experiment for group %s has been cancelled.\n", group)
 		cancel()
 	}()
 
-	rel, err := client.RunWithContext(ctx, ch, vals)
+	rel, err := client.RunWithContext(ctx, chartRequested, vals)
 	if err != nil {
 		e := fmt.Errorf("experiment launch failed")
 		log.Logger.WithStackTrace(err.Error()).Error(e)
 		return e
 	}
 
+	return kd.updateRevision(rel, dry)
+}
+
+func (kd *KubeDriver) updateRevision(rel *release.Release, dry bool) error {
 	// upgrading revision info
 	kd.revision = rel.Version
 
@@ -402,16 +404,15 @@ func (kd *KubeDriver) install(chartDir string, valueOpts values.Options, group s
 	} else {
 		log.Logger.Info("experiment launched. Happy Iter8ing!")
 	}
-
 	return nil
 }
 
 // Launch a Kubernetes experiment
-func (kd *KubeDriver) Launch(chartDir string, valueOpts values.Options, group string, dry bool) error {
+func (kd *KubeDriver) Launch(chartPathOptions action.ChartPathOptions, chartName string, valueOpts values.Options, group string, dry bool) error {
 	if kd.revision <= 0 {
-		return kd.install(chartDir, valueOpts, group, dry)
+		return kd.install(chartPathOptions, chartName, valueOpts, group, dry)
 	}
-	return kd.upgrade(chartDir, valueOpts, group, dry)
+	return kd.upgrade(chartPathOptions, chartName, valueOpts, group, dry)
 }
 
 // Delete a Kubernetes experiment group
@@ -431,11 +432,6 @@ func (kd *KubeDriver) Delete() error {
 // Credit: the logic for this function is sourced from Helm
 // https://github.com/helm/helm/blob/8ab18f7567cedffdfa5ba4d7f6abfb58efc313f8/cmd/helm/install.go#L177
 func (kd *KubeDriver) getChartAndVals(chartDir string, valueOpts values.Options) (*chart.Chart, map[string]interface{}, error) {
-	// update dependencies for the chart
-	if err := UpdateChartDependencies(chartDir, kd.EnvSettings); err != nil {
-		return nil, nil, err
-	}
-
 	// form chart values
 	p := getter.All(kd.EnvSettings)
 	vals, err := valueOpts.MergeValues(p)
diff --git a/go.mod b/go.mod
index 8101928d..9e5b99bf 100644
--- a/go.mod
+++ b/go.mod
@@ -26,7 +26,6 @@ require (
 	github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490 // indirect
 	github.com/envoyproxy/go-control-plane v0.10.1 // indirect
 	github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect
-	github.com/hashicorp/go-getter v1.6.1
 	github.com/imdario/mergo v0.3.12
 	github.com/itchyny/gojq v0.12.7
 	github.com/jarcoal/httpmock v1.1.0
@@ -39,8 +38,8 @@ require (
 	github.com/spf13/pflag v1.0.5
 	github.com/stretchr/testify v1.7.2
 	golang.org/x/net v0.0.0-20220420153159-1850ba15e1be
+	golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e // indirect
 	golang.org/x/text v0.3.7
-	google.golang.org/api v0.62.0 // indirect
 	google.golang.org/grpc v1.45.0
 	google.golang.org/protobuf v1.28.0
 	helm.sh/helm/v3 v3.9.4
diff --git a/go.sum b/go.sum
index 36298be0..9d7a1c2f 100644
--- a/go.sum
+++ b/go.sum
@@ -28,7 +28,6 @@ cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aD
 cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
 cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
 cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
-cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM=
 cloud.google.com/go v0.99.0 h1:y/cM2iqGgGi5D5DQZl6D9STN/3dR/Vx5Mp8s752oJTY=
 cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
 cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
@@ -48,7 +47,6 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy
 cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
 cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
 cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
-cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA=
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 fortio.org/fortio v1.27.0 h1:XGvympOz+mLQ8wvTZh812pF3mmBU9DlIs4A6ZjcwJXk=
@@ -172,8 +170,6 @@ github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:l
 github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY=
 github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
 github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
-github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
-github.com/aws/aws-sdk-go v1.43.16 h1:Y7wBby44f+tINqJjw5fLH3vA+gFq4uMITIKqditwM14=
 github.com/aws/aws-sdk-go v1.43.16/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
 github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
 github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
@@ -182,8 +178,6 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
-github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
 github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
 github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
 github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
@@ -225,7 +219,6 @@ github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1
 github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
 github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
 github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
-github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -667,11 +660,9 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
 github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
 github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
 github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
-github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ=
 github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -700,7 +691,6 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
-github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU=
 github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
 github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
 github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
@@ -739,24 +729,16 @@ github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FK
 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
-github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
-github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
-github.com/hashicorp/go-getter v1.6.1 h1:NASsgP4q6tL94WH6nJxKWj8As2H/2kop/bB1d8JMyRY=
-github.com/hashicorp/go-getter v1.6.1/go.mod h1:IZCrswsZPeWv9IkVnLElzRU/gz/QPi6pZHn4tv6vbwA=
 github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
 github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
 github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
 github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
 github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
 github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
-github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
-github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
 github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
 github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
 github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
 github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
-github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@@ -800,9 +782,7 @@ github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/
 github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
 github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
-github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
 github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
-github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
 github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
 github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
 github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
@@ -833,7 +813,6 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
-github.com/klauspost/compress v1.11.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
@@ -941,10 +920,8 @@ github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFW
 github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
 github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
 github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
-github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
 github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
 github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
 github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
@@ -1258,8 +1235,6 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1
 github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig=
 github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
-github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ=
-github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
 github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
 github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
 github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
@@ -1645,7 +1620,6 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1810,8 +1784,6 @@ google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqiv
 google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
 google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
 google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
-google.golang.org/api v0.62.0 h1:PhGymJMXfGBzc4lBRmrx9+1w4w2wEzURHNGF/sD/xGc=
-google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1886,8 +1858,6 @@ google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEc
 google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
 google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368 h1:Et6SkiuvnBn+SgrSYXs/BrUpGB4mbdwt4R3vaPIlicA=
@@ -1922,7 +1892,6 @@ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ
 google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
 google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
 google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
 google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
 google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
 google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
@@ -1955,7 +1924,6 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
-gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
diff --git a/charts/iter8/.helmignore b/testdata/charts/iter8/.helmignore
similarity index 100%
rename from charts/iter8/.helmignore
rename to testdata/charts/iter8/.helmignore
diff --git a/charts/iter8/Chart.yaml b/testdata/charts/iter8/Chart.yaml
similarity index 63%
rename from charts/iter8/Chart.yaml
rename to testdata/charts/iter8/Chart.yaml
index 890b0304..88af422c 100644
--- a/charts/iter8/Chart.yaml
+++ b/testdata/charts/iter8/Chart.yaml
@@ -1,13 +1,15 @@
 apiVersion: v2
-name: iter8
-version: 0.11.5
 description: Iter8 experiment chart
-type: application
 home: https://iter8.tools
-sources:
-- https://github.com/iter8-tools/iter8
+icon: https://github.com/iter8-tools/iter8/raw/master/mkdocs/docs/images/favicon.png
 maintainers:
-- name: Srinivasan Parthasarathy
-  email: spartha@us.ibm.com
+- email: spartha@us.ibm.com
+  name: Srinivasan Parthasarathy
   url: https://researcher.watson.ibm.com/researcher/view.php?person=us-spartha
-icon: https://github.com/iter8-tools/iter8/raw/master/mkdocs/docs/images/favicon.png
+- email: kalantar@us.ibm.com
+  name: Michael Kalantar
+name: iter8
+sources:
+- https://github.com/iter8-tools/hub
+type: application
+version: 0.12.1
diff --git a/charts/iter8/templates/_experiment.tpl b/testdata/charts/iter8/templates/_experiment.tpl
similarity index 100%
rename from charts/iter8/templates/_experiment.tpl
rename to testdata/charts/iter8/templates/_experiment.tpl
diff --git a/charts/iter8/templates/_k-cronjob.tpl b/testdata/charts/iter8/templates/_k-cronjob.tpl
similarity index 100%
rename from charts/iter8/templates/_k-cronjob.tpl
rename to testdata/charts/iter8/templates/_k-cronjob.tpl
diff --git a/charts/iter8/templates/_k-job.tpl b/testdata/charts/iter8/templates/_k-job.tpl
similarity index 100%
rename from charts/iter8/templates/_k-job.tpl
rename to testdata/charts/iter8/templates/_k-job.tpl
diff --git a/charts/iter8/templates/_k-role.tpl b/testdata/charts/iter8/templates/_k-role.tpl
similarity index 100%
rename from charts/iter8/templates/_k-role.tpl
rename to testdata/charts/iter8/templates/_k-role.tpl
diff --git a/charts/iter8/templates/_k-rolebinding.tpl b/testdata/charts/iter8/templates/_k-rolebinding.tpl
similarity index 100%
rename from charts/iter8/templates/_k-rolebinding.tpl
rename to testdata/charts/iter8/templates/_k-rolebinding.tpl
diff --git a/charts/iter8/templates/_k-secret.tpl b/testdata/charts/iter8/templates/_k-secret.tpl
similarity index 100%
rename from charts/iter8/templates/_k-secret.tpl
rename to testdata/charts/iter8/templates/_k-secret.tpl
diff --git a/charts/iter8/templates/_k-serviceacccount.tpl b/testdata/charts/iter8/templates/_k-serviceacccount.tpl
similarity index 100%
rename from charts/iter8/templates/_k-serviceacccount.tpl
rename to testdata/charts/iter8/templates/_k-serviceacccount.tpl
diff --git a/charts/iter8/templates/_payload-github.tpl b/testdata/charts/iter8/templates/_payload-github.tpl
similarity index 100%
rename from charts/iter8/templates/_payload-github.tpl
rename to testdata/charts/iter8/templates/_payload-github.tpl
diff --git a/charts/iter8/templates/_payload-slack.tpl b/testdata/charts/iter8/templates/_payload-slack.tpl
similarity index 98%
rename from charts/iter8/templates/_payload-slack.tpl
rename to testdata/charts/iter8/templates/_payload-slack.tpl
index 79743c91..1a42053f 100644
--- a/charts/iter8/templates/_payload-slack.tpl
+++ b/testdata/charts/iter8/templates/_payload-slack.tpl
@@ -1,3 +1,3 @@
 {
   "text": "Your Iter8 report is ready: {{ regexReplaceAll "\"" (regexReplaceAll "\n" (.Report | toPrettyJson) "\\n") "\\\""}}"
-}
\ No newline at end of file
+}


Test patch:
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index 07c3b39f..7c8d981f 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -50,7 +50,8 @@ jobs:
         done;        
     - name: launch experiment
       run: |
-        iter8 launch --noDownload \
+        iter8 launch --localChart \
+        --chartName testdata/charts/iter8 \
         --set "tasks={http}" \
         --set http.url="http://$HOST_IP/post" \
         --set http.payloadURL="https://httpbin.org/stream/1"
@@ -59,7 +60,8 @@ jobs:
         iter8 assert -c completed -c nofailure
     - name: launch experiment
       run: |
-        iter8 launch --noDownload \
+        iter8 launch --localChart \
+        --chartName testdata/charts/iter8 \
         --set "tasks={http,assess}" \
         --set http.url="http://$HOST_IP/post" \
         --set http.payloadURL="https://httpbin.org/stream/1" \
@@ -92,7 +94,8 @@ jobs:
         kubectl expose deploy httpbin --port=80
     - name: wait for the app to come up
       run: |
-        iter8 launch --noDownload \
+        iter8 launch --localChart \
+        --chartName testdata/charts/iter8 \
         --set "tasks={ready}" \
         --set ready.deploy=httpbin \
         --set ready.service=httpbin \
@@ -102,7 +105,8 @@ jobs:
         kubectl port-forward service/httpbin 8080:80 &
     - name: launch experiment
       run: |
-        iter8 launch --noDownload \
+        iter8 launch --localChart \
+        --chartName testdata/charts/iter8 \
         --set "tasks={ready,http,assess}" \
         --set ready.deploy=httpbin \
         --set ready.service=httpbin \
@@ -135,7 +139,8 @@ jobs:
       run: |
         set -e
         export LOG_LEVEL=trace
-        iter8 launch --noDownload \
+        iter8 launch --localChart \
+          --chartName testdata/charts/iter8 \
           --set "tasks={grpc,assess}" \
           --set grpc.host="127.0.0.1:50051" \
           --set grpc.call="helloworld.Greeter.SayHello" \
@@ -151,7 +156,8 @@ jobs:
       run: |
         set -e
         export LOG_LEVEL=trace
-        iter8 launch --noDownload \
+        iter8 launch --localChart \
+          --chartName testdata/charts/iter8 \
           --set "tasks={grpc,assess}" \
           --set grpc.host="127.0.0.1:50051" \
           --set grpc.call="helloworld.Greeter.SayHello" \
@@ -192,7 +198,8 @@ jobs:
         wait: 300s
     - name: load-test-http in Kubernetes
       run: |
-        iter8 k launch --noDownload \
+        iter8 k launch --localChart \
+        --chartName testdata/charts/iter8 \
         --set "tasks={http,assess}" \
         --set http.url="http://$HOST_IP/get" \
         --set http.duration="3s" \
diff --git a/action/delete_test.go b/action/delete_test.go
index 12fbe1fa..12c46768 100644
--- a/action/delete_test.go
+++ b/action/delete_test.go
@@ -14,9 +14,8 @@ func TestKubeDelete(t *testing.T) {
 
 	// fix lOpts
 	lOpts := NewLaunchOpts(driver.NewFakeKubeDriver(cli.New()))
-	lOpts.ChartsParentDir = base.CompletePath("../", "")
-	lOpts.ChartName = "iter8"
-	lOpts.NoDownload = true
+	lOpts.ChartName = base.CompletePath("../testdata/charts", "iter8")
+	lOpts.LocalChart = true
 	lOpts.Values = []string{"tasks={http}", "http.url=https://iter8.tools", "http.duration=2s"}
 
 	err = lOpts.KubeRun()
diff --git a/action/gen_test.go b/action/gen_test.go
index ef92dc82..e70a297d 100644
--- a/action/gen_test.go
+++ b/action/gen_test.go
@@ -17,8 +17,7 @@ func TestGen(t *testing.T) {
 	// fix gOpts
 	_ = os.Chdir(t.TempDir())
 	gOpts := NewGenOpts()
-	gOpts.ChartsParentDir = base.CompletePath("../", "")
-	gOpts.ChartName = "iter8"
+	gOpts.ChartName = base.CompletePath("../testdata/charts", "iter8")
 	gOpts.Values = []string{"tasks={http}", "http.url=https://httpbin.org/get"}
 	err := gOpts.LocalRun()
 	assert.NoError(t, err)
@@ -40,8 +39,7 @@ func TestGenGRPC(t *testing.T) {
 	// fix gOpts
 	_ = os.Chdir(t.TempDir())
 	gOpts := NewGenOpts()
-	gOpts.ChartsParentDir = base.CompletePath("../", "")
-	gOpts.ChartName = "iter8"
+	gOpts.ChartName = base.CompletePath("../testdata/charts", "iter8")
 	gOpts.Values = []string{"tasks={grpc,assess}", "grpc.host=localhost:50051", "grpc.call=helloworld.Greeter.SayHello", "grpc.proto=helloworld.proto", "grpc.protoset=helloworld.protoset", "grpc.data.name=frodo", "assess.SLOs.upper.grpc/error-rate=0", "assess.SLOs.upper.grpc/latency/mean=150"}
 	err := gOpts.LocalRun()
 	assert.NoError(t, err)
@@ -65,8 +63,7 @@ func TestGenDB(t *testing.T) {
 	// fix gOpts
 	_ = os.Chdir(t.TempDir())
 	gOpts := NewGenOpts()
-	gOpts.ChartsParentDir = base.CompletePath("../", "")
-	gOpts.ChartName = "iter8"
+	gOpts.ChartName = base.CompletePath("../testdata/charts", "iter8")
 	gOpts.Values = []string{"tasks={custommetrics,assess}", "custommetrics.templates.istio-prom=https://raw.githubusercontent.com/iter8-tools/iter8/master/charts/iter8lib/templates/_metrics-istio.tpl", "custommetrics.values.URL=http://prometheus.istio-system:9090/api/v1/query", "custommetrics.values.destinationWorkload=httpbin-v2", "custommetrics.values.destinationWorkloadNamespace=default", `custommetrics.values.startingTime="2020-02-01T09:44:40Z"`, "assess.SLOs.upper.istio/error-rate=0"}
 
 	err := gOpts.LocalRun()
diff --git a/action/hub_test.go b/action/hub_test.go
deleted file mode 100644
index a40f6f96..00000000
--- a/action/hub_test.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package action
-
-import (
-	"os"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func TestHub(t *testing.T) {
-	// fix hOpts
-	hOpts := NewHubOpts()
-	_ = os.Chdir(t.TempDir())
-	hOpts.RemoteFolderURL = "github.com/iter8-tools/iter8.git//charts"
-
-	err := hOpts.LocalRun()
-	assert.NoError(t, err)
-}
diff --git a/action/launch_test.go b/action/launch_test.go
index 7d40abc7..befdb011 100644
--- a/action/launch_test.go
+++ b/action/launch_test.go
@@ -16,12 +16,7 @@ func TestLocalLaunch(t *testing.T) {
 
 	// fix lOpts
 	lOpts := NewLaunchOpts(driver.NewFakeKubeDriver(cli.New()))
-	lOpts.RemoteFolderURL = "github.com/sriumcp/iter8.git?ref=ic//charts"
-	// // TODO: fix git ref
-	// lOpts.RemoteFolderURL = defaultIter8Repo + "?ref=v0.11.0" + "//" + ChartsFolderName
-	lOpts.ChartName = "iter8"
 	lOpts.Values = []string{"tasks={http}", "http.url=https://httpbin.org/get", "http.duration=2s"}
-
 	err := lOpts.LocalRun()
 	assert.NoError(t, err)
 }
@@ -32,10 +27,6 @@ func TestKubeLaunch(t *testing.T) {
 
 	// fix lOpts
 	lOpts := NewLaunchOpts(driver.NewFakeKubeDriver(cli.New()))
-	lOpts.RemoteFolderURL = "github.com/sriumcp/iter8.git?ref=ic//charts"
-	// // TODO: fix git ref
-	// lOpts.RemoteFolderURL = defaultIter8Repo + "?ref=v0.11.0" + "//" + ChartsFolderName
-	lOpts.ChartName = "iter8"
 	lOpts.Values = []string{"tasks={http}", "http.url=https://httpbin.org/get", "http.duration=2s", "runner=job"}
 
 	err = lOpts.KubeRun()
@@ -47,33 +38,29 @@ func TestKubeLaunch(t *testing.T) {
 	assert.NoError(t, err)
 }
 
-func TestLocalLaunchNoDownload(t *testing.T) {
+func TestLocalLaunchLocalChart(t *testing.T) {
 	_ = os.Chdir(t.TempDir())
 	base.SetupWithMock(t)
 
 	// fix lOpts
 	lOpts := NewLaunchOpts(driver.NewFakeKubeDriver(cli.New()))
-	lOpts.ChartsParentDir = base.CompletePath("../", "")
-	lOpts.ChartName = "iter8"
-	lOpts.NoDownload = true
+	lOpts.ChartName = base.CompletePath("../testdata/charts", "iter8")
+	lOpts.LocalChart = true
 	lOpts.Values = []string{"tasks={http}", "http.url=https://httpbin.org/get", "http.duration=2s"}
 
 	err := lOpts.LocalRun()
 	assert.NoError(t, err)
 }
 
-func TestKubeLaunchNoDownload(t *testing.T) {
+func TestKubeLaunchLocalChart(t *testing.T) {
 	var err error
 	_ = os.Chdir(t.TempDir())
 
 	// fix lOpts
 	lOpts := NewLaunchOpts(driver.NewFakeKubeDriver(cli.New()))
-	lOpts.ChartsParentDir = base.CompletePath("../", "")
-	lOpts.ChartName = "iter8"
-	lOpts.NoDownload = true
+	lOpts.ChartName = base.CompletePath("../testdata/charts", "iter8")
+	lOpts.LocalChart = true
 	lOpts.Values = []string{"tasks={http}", "http.url=https://httpbin.org/get", "http.duration=2s", "runner=job"}
-	// // fixing git ref forever
-	// lOpts.RemoteFolderURL = defaultIter8Repo + "?ref=v0.11.0" + "//" + ChartsFolderName
 
 	err = lOpts.KubeRun()
 	assert.NoError(t, err)
diff --git a/action/log_test.go b/action/log_test.go
index cf7aea5b..3263bfd4 100644
--- a/action/log_test.go
+++ b/action/log_test.go
@@ -19,9 +19,8 @@ func TestLog(t *testing.T) {
 
 	// fix lOpts
 	lOpts := NewLaunchOpts(driver.NewFakeKubeDriver(cli.New()))
-	lOpts.ChartsParentDir = base.CompletePath("../", "")
-	lOpts.ChartName = "iter8"
-	lOpts.NoDownload = true
+	lOpts.ChartName = base.CompletePath("../testdata/charts", "iter8")
+	lOpts.LocalChart = true
 	lOpts.Values = []string{"tasks={http}", "http.url=https://httpbin.org/get", "http.duration=2s"}
 
 	err = lOpts.KubeRun()
diff --git a/cmd/gen_test.go b/cmd/gen_test.go
index c0e66b15..0a7ff185 100644
--- a/cmd/gen_test.go
+++ b/cmd/gen_test.go
@@ -14,13 +14,13 @@ func TestGen(t *testing.T) {
 		// gen, with CLI values
 		{
 			name:   "gen with CLI values",
-			cmd:    fmt.Sprintf("gen -c iter8 --chartsParentDir %v --set tasks={http} --set http.url=https://httpbin.org", base.CompletePath("../", "")),
+			cmd:    fmt.Sprintf("gen -c %v --set tasks={http} --set http.url=https://httpbin.org", base.CompletePath("../testdata/charts", "iter8")),
 			golden: base.CompletePath("../testdata", "output/gen-cli-values.txt"),
 		},
 		// gen, values file
 		{
 			name:   "gen with values file",
-			cmd:    fmt.Sprintf("gen -c iter8 --chartsParentDir %v --set tasks={http,assess} --set http.duration=2s -f %v", base.CompletePath("../", ""), base.CompletePath("../testdata", "config.yaml")),
+			cmd:    fmt.Sprintf("gen -c %v --set tasks={http,assess} --set http.duration=2s -f %v", base.CompletePath("../testdata/charts", "iter8"), base.CompletePath("../testdata", "config.yaml")),
 			golden: base.CompletePath("../testdata", "output/gen-values-file.txt"),
 		},
 	}
diff --git a/cmd/hub_test.go b/cmd/hub_test.go
deleted file mode 100644
index beb9189f..00000000
--- a/cmd/hub_test.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package cmd
-
-import (
-	"os"
-	"testing"
-
-	"github.com/iter8-tools/iter8/base"
-)
-
-func TestHub(t *testing.T) {
-	_ = os.Chdir(t.TempDir())
-	tests := []cmdTestCase{
-		// basic hub
-		{
-			name:   "basic hub",
-			cmd:    "hub --remoteFolderURL github.com/iter8-tools/iter8.git//charts",
-			golden: base.CompletePath("../testdata", "output/hub.txt"),
-		},
-	}
-
-	runTestActionCmd(t, tests)
-}
diff --git a/cmd/kassert_test.go b/cmd/kassert_test.go
index e7399688..5bd19cdf 100644
--- a/cmd/kassert_test.go
+++ b/cmd/kassert_test.go
@@ -19,7 +19,7 @@ func TestKAssert(t *testing.T) {
 		// k launch
 		{
 			name:   "k launch",
-			cmd:    fmt.Sprintf("k launch -c iter8 --chartsParentDir %v --noDownload --set tasks={http} --set http.url=https://httpbin.org/get --set http.duration=2s", base.CompletePath("../", "")),
+			cmd:    fmt.Sprintf("k launch -c %v --localChart --set tasks={http} --set http.url=https://httpbin.org/get --set http.duration=2s", base.CompletePath("../testdata/charts", "iter8")),
 			golden: base.CompletePath("../testdata", "output/klaunch.txt"),
 		},
 		// k run
diff --git a/cmd/kdelete_test.go b/cmd/kdelete_test.go
index 5022c6c8..f277acca 100644
--- a/cmd/kdelete_test.go
+++ b/cmd/kdelete_test.go
@@ -15,13 +15,13 @@ func TestKDelete(t *testing.T) {
 		// Launch, base case, values from CLI
 		{
 			name:   "basic k launch",
-			cmd:    fmt.Sprintf("k launch -c iter8 --chartsParentDir %v --noDownload --set tasks={http} --set http.url=https://httpbin.org/get --set http.duration=2s", base.CompletePath("../", "")),
+			cmd:    fmt.Sprintf("k launch -c %v --localChart --set tasks={http} --set http.url=https://httpbin.org/get --set http.duration=2s", base.CompletePath("../testdata/charts", "iter8")),
 			golden: base.CompletePath("../testdata", "output/klaunch.txt"),
 		},
 		// Launch again, values from CLI
 		{
 			name: "launch again",
-			cmd:  fmt.Sprintf("k launch -c iter8 --chartsParentDir %v --noDownload --set tasks={http} --set http.url=https://httpbin.org/get --set http.duration=2s", base.CompletePath("../", "")),
+			cmd:  fmt.Sprintf("k launch -c %v --localChart --set tasks={http} --set http.url=https://httpbin.org/get --set http.duration=2s", base.CompletePath("../testdata/charts", "iter8")),
 		},
 		// Delete
 		{
diff --git a/cmd/klaunch_test.go b/cmd/klaunch_test.go
index a6179c94..bedae4c6 100644
--- a/cmd/klaunch_test.go
+++ b/cmd/klaunch_test.go
@@ -15,7 +15,7 @@ func TestKLaunch(t *testing.T) {
 		// Launch, base case, values from CLI
 		{
 			name:   "basic k launch",
-			cmd:    fmt.Sprintf("k launch -c iter8 --noDownload --chartsParentDir %v --set tasks={http} --set http.url=https://httpbin.org/get --set http.duration=2s", base.CompletePath("../", "")),
+			cmd:    fmt.Sprintf("k launch -c %v --localChart --set tasks={http} --set http.url=https://httpbin.org/get --set http.duration=2s", base.CompletePath("../testdata/charts", "iter8")),
 			golden: base.CompletePath("../testdata", "output/klaunch.txt"),
 		},
 	}
diff --git a/cmd/klog_test.go b/cmd/klog_test.go
index 2747c923..34724940 100644
--- a/cmd/klog_test.go
+++ b/cmd/klog_test.go
@@ -20,7 +20,7 @@ func TestKLog(t *testing.T) {
 		// k launch
 		{
 			name:   "k launch",
-			cmd:    fmt.Sprintf("k launch -c iter8 --noDownload --chartsParentDir %v --set tasks={http} --set http.url=https://httpbin.org/get --set http.duration=2s", base.CompletePath("../", "")),
+			cmd:    fmt.Sprintf("k launch -c %v --localChart --set tasks={http} --set http.url=https://httpbin.org/get --set http.duration=2s", base.CompletePath("../testdata/charts", "iter8")),
 			golden: base.CompletePath("../testdata", "output/klaunch.txt"),
 		},
 		// k assert
diff --git a/cmd/launch_test.go b/cmd/launch_test.go
index 2b931568..7f916f8f 100644
--- a/cmd/launch_test.go
+++ b/cmd/launch_test.go
@@ -11,16 +11,16 @@ import (
 func TestLaunch(t *testing.T) {
 	_ = os.Chdir(t.TempDir())
 	tests := []cmdTestCase{
-		// launch, chartsParentDir, noDownload
+		// launch, localChart
 		{
 			name:   "basic launch",
-			cmd:    fmt.Sprintf("launch -c iter8 --chartsParentDir %v --noDownload --set tasks={http} --set http.url=https://httpbin.org/get --set http.duration=2s", base.CompletePath("../", "")),
+			cmd:    fmt.Sprintf("launch -c %v --localChart --set tasks={http} --set http.url=https://httpbin.org/get --set http.duration=2s", base.CompletePath("../testdata/charts", "iter8")),
 			golden: base.CompletePath("../testdata", "output/launch.txt"),
 		},
 		// launch, values file
 		{
 			name:   "launch with values file",
-			cmd:    fmt.Sprintf("launch -c iter8 --chartsParentDir %v --noDownload --set tasks={http,assess} --set http.duration=2s -f %v", base.CompletePath("../", ""), base.CompletePath("../testdata", "config.yaml")),
+			cmd:    fmt.Sprintf("launch -c %v --localChart --set tasks={http,assess} --set http.duration=2s -f %v", base.CompletePath("../testdata/charts", "iter8"), base.CompletePath("../testdata", "config.yaml")),
 			golden: base.CompletePath("../testdata", "output/launch-with-slos.txt"),
 		},
 	}
diff --git a/driver/kubedriver_test.go b/driver/kubedriver_test.go
index 68d614e2..364c0d73 100644
--- a/driver/kubedriver_test.go
+++ b/driver/kubedriver_test.go
@@ -7,6 +7,7 @@ import (
 
 	"github.com/iter8-tools/iter8/base"
 	"github.com/stretchr/testify/assert"
+	"helm.sh/helm/v3/pkg/action"
 	"helm.sh/helm/v3/pkg/cli"
 	"helm.sh/helm/v3/pkg/cli/values"
 	batchv1 "k8s.io/api/batch/v1"
@@ -24,7 +25,7 @@ func TestKOps(t *testing.T) {
 	assert.NoError(t, err)
 
 	// install
-	err = kd.install(base.CompletePath("../", "charts/iter8"), values.Options{
+	err = kd.install(action.ChartPathOptions{}, base.CompletePath("../", "testdata/charts/iter8"), values.Options{
 		Values: []string{"tasks={http}", "http.url=https://httpbin.org/get", "runner=job"},
 	}, kd.Group, false)
 	assert.NoError(t, err)
@@ -39,7 +40,7 @@ func TestKOps(t *testing.T) {
 	assert.NoError(t, err)
 
 	// upgrade
-	err = kd.upgrade(base.CompletePath("../", "charts/iter8"), values.Options{
+	err = kd.upgrade(action.ChartPathOptions{}, base.CompletePath("../", "testdata/charts/iter8"), values.Options{
 		Values: []string{"tasks={http}", "http.url=https://httpbin.org/get", "runner=job"},
 	}, kd.Group, false)
 	assert.NoError(t, err)
@@ -133,7 +134,7 @@ func TestDryInstall(t *testing.T) {
 	_ = os.Chdir(t.TempDir())
 	kd := NewFakeKubeDriver(cli.New())
 
-	err := kd.Launch(base.CompletePath("../", "charts/iter8"), values.Options{
+	err := kd.Launch(action.ChartPathOptions{}, base.CompletePath("../", "testdata/charts/iter8"), values.Options{
 		ValueFiles:   []string{},
 		StringValues: []string{},
 		Values:       []string{"tasks={http}", "http.url=https://localhost:12345"},
diff --git a/testdata/charts/iter8/README.md b/testdata/charts/iter8/README.md
new file mode 100644
index 00000000..1adae630
--- /dev/null
+++ b/testdata/charts/iter8/README.md
@@ -0,0 +1,3 @@
+# Iter8 experiment chart
+
+This chart enables Iter8 experiments through the Iter8 CLI.